Skip to content

Commit 1a54aaf

Browse files
committed
feat(resolvers): now resolvers have typechecks for args param (it checks names and presence of required arguments)
1 parent d4f6cee commit 1a54aaf

38 files changed

+262
-179
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"graphql-compose-pagination": "^7.0.0"
3232
},
3333
"peerDependencies": {
34-
"graphql-compose": "^7.21.0",
34+
"graphql-compose": "^7.21.1",
3535
"mongoose": "^5.0.0 || ^4.4.0"
3636
},
3737
"devDependencies": {
@@ -46,12 +46,12 @@
4646
"eslint-plugin-import": "2.22.0",
4747
"eslint-plugin-prettier": "3.1.4",
4848
"graphql": "15.3.0",
49-
"graphql-compose": "7.21.0",
49+
"graphql-compose": "7.21.1",
5050
"graphql-compose-connection": "^7.0.0",
5151
"graphql-compose-pagination": "^7.0.0",
5252
"jest": "26.4.2",
5353
"mongodb-memory-server": "6.7.4",
54-
"mongoose": "5.10.4",
54+
"mongoose": "5.10.5",
5555
"prettier": "2.1.1",
5656
"request": "2.88.2",
5757
"rimraf": "3.0.2",

src/composeMongoose.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import type { Model, Document } from 'mongoose';
44
import { convertModelToGraphQL } from './fieldsConverter';
55
import { allResolvers } from './resolvers';
66
import MongoID from './types/MongoID';
7-
import { ArgsMap } from './resolvers/helpers';
87
import {
98
prepareFields,
109
createInputType,
@@ -27,13 +26,18 @@ export type ComposeMongooseOpts<TContext> = {
2726
};
2827

2928
export type GenerateResolverType<TDoc extends Document, TContext = any> = {
30-
// get all available resolver generators, then leave only 3rd arg – opts
29+
// Get all available resolver generators, then leave only 3rd arg – opts
3130
// because first two args will be attached via bind() method at runtime:
3231
// count = count.bind(undefined, model, tc);
33-
// TODO: explain infer
3432
[resolver in keyof typeof allResolvers]: <TSource = any>(
3533
opts?: Parameters<typeof allResolvers[resolver]>[2]
36-
) => typeof allResolvers[resolver] extends (...args: any) => Resolver<any, any, infer TArgs, any>
34+
) => // Also we should patch generics of the returned Resolver
35+
// attach TContext TDoc from the code which will bind at runtime
36+
// and allow user to attach TSource via generic at call
37+
// For this case we are using `extends infer` construction
38+
// it helps to extract any Generic from existed method
39+
// and then construct new combined return type
40+
typeof allResolvers[resolver] extends (...args: any) => Resolver<any, any, infer TArgs, any>
3741
? Resolver<TSource, TContext, TArgs, TDoc>
3842
: any;
3943
};

src/resolvers/__tests__/createMany-test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,22 @@ describe('createMany() ->', () => {
5959
});
6060

6161
it('should rejected with Error if args.records is empty', async () => {
62-
const result = createMany(UserModel, UserTC).resolve({ args: {} });
62+
const result = createMany(UserModel, UserTC).resolve({
63+
// @ts-expect-error
64+
args: {},
65+
});
6366
await expect(result).rejects.toThrow(
6467
'User.createMany resolver requires args.records to be an Array and must contain at least one record'
6568
);
6669
});
6770

6871
it('should rejected with Error if args.records is not array', async () => {
69-
const result = createMany(UserModel, UserTC).resolve({ args: { records: {} } });
72+
const result = createMany(UserModel, UserTC).resolve({
73+
args: {
74+
// @ts-expect-error
75+
records: {},
76+
},
77+
});
7078
await expect(result).rejects.toThrow(
7179
'ser.createMany resolver requires args.records to be an Array and must contain at least one record'
7280
);

src/resolvers/__tests__/createOne-test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ describe('createOne() ->', () => {
5151
});
5252

5353
it('should rejected with Error if args.record is empty', async () => {
54-
const result = createOne(UserModel, UserTC).resolve({ args: {} });
54+
const result = createOne(UserModel, UserTC).resolve({
55+
// @ts-expect-error
56+
args: {},
57+
});
5558
await expect(result).rejects.toThrow(
5659
'User.createOne resolver requires at least one value in args.record'
5760
);

src/resolvers/__tests__/dataLoaderMany-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('dataLoaderMany() ->', () => {
6262

6363
it('should return Error if args.ids contains incorrect ObjectId', async () => {
6464
// For more info see https://github.com/graphql/dataloader#loadmanykeys
65-
// var [ a, b, c ] = await myLoader.loadMany([ 'a', 'b', 'badkey' ]);
65+
// var [ a, b, c ] = await myLoader.loadMany([ 'a', 'b', 'badKey' ]);
6666
// // c instanceof Error
6767
const result = await dataLoaderMany(UserModel, UserTC).resolve({
6868
args: { _ids: [1] },

src/resolvers/__tests__/dataLoaderManyLean-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ describe('dataLoaderManyLean() ->', () => {
6262

6363
it('should return Error if args.ids contains incorrect ObjectId', async () => {
6464
// For more info see https://github.com/graphql/dataloader#loadmanykeys
65-
// var [ a, b, c ] = await myLoader.loadMany([ 'a', 'b', 'badkey' ]);
65+
// var [ a, b, c ] = await myLoader.loadMany([ 'a', 'b', 'badKey' ]);
6666
// // c instanceof Error
6767
const result = await dataLoaderManyLean(UserModel, UserTC).resolve({
6868
args: { _ids: [1] },

src/resolvers/__tests__/findOne-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('findOne() ->', () => {
9898

9999
it('should return document if provided existed id', async () => {
100100
const result = await findOne(UserModel, UserTC).resolve({
101-
args: { id: user1._id },
101+
args: { filter: { _id: user1._id } },
102102
});
103103
expect(result.name).toBe(user1.name);
104104
});
@@ -122,14 +122,14 @@ describe('findOne() ->', () => {
122122

123123
it('should return mongoose document', async () => {
124124
const result = await findOne(UserModel, UserTC).resolve({
125-
args: { _id: user1._id },
125+
args: { filter: { _id: user1._id } },
126126
});
127127
expect(result).toBeInstanceOf(UserModel);
128128
});
129129

130130
it('should call `beforeQuery` method with non-executed `query` as arg', async () => {
131131
const result = await findOne(UserModel, UserTC).resolve({
132-
args: { _id: user1._id },
132+
args: { filter: { _id: user1._id } },
133133
beforeQuery(query: any, rp: ExtendedResolveParams) {
134134
expect(rp.model).toBe(UserModel);
135135
expect(rp.query).toHaveProperty('exec');

src/resolvers/__tests__/findOneLean-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ describe('findOneLean() ->', () => {
9898

9999
it('should return document if provided existed id', async () => {
100100
const result = await findOneLean(UserModel, UserTC).resolve({
101-
args: { id: user1._id },
101+
args: { filter: { _id: user1._id } },
102102
});
103103
expect(result.name).toBe(user1.name);
104104
});
@@ -122,7 +122,7 @@ describe('findOneLean() ->', () => {
122122

123123
it('should return mongoose document', async () => {
124124
const result = await findOneLean(UserModel, UserTC).resolve({
125-
args: { _id: user1._id },
125+
args: { filter: { _id: user1._id } },
126126
});
127127
expect(result).not.toBeInstanceOf(UserModel);
128128
// should translate aliases fields
@@ -131,7 +131,7 @@ describe('findOneLean() ->', () => {
131131

132132
it('should call `beforeQuery` method with non-executed `query` as arg', async () => {
133133
const result = await findOneLean(UserModel, UserTC).resolve({
134-
args: { _id: user1._id },
134+
args: { filter: { _id: user1._id } },
135135
beforeQuery(query: any, rp: ExtendedResolveParams) {
136136
expect(rp.model).toBe(UserModel);
137137
expect(rp.query).toHaveProperty('lean');

src/resolvers/__tests__/removeById-test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ describe('removeById() ->', () => {
6060
});
6161

6262
it('should rejected with Error if args._id is empty', async () => {
63-
const result = removeById(UserModel, UserTC).resolve({ args: {} });
63+
const result = removeById(UserModel, UserTC).resolve({
64+
// @ts-expect-error
65+
args: {},
66+
});
6467
await expect(result).rejects.toThrow('User.removeById resolver requires args._id value');
6568
});
6669

src/resolvers/__tests__/removeMany-test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ describe('removeMany() ->', () => {
7878
});
7979

8080
it('should rejected with Error if args.filter is empty', async () => {
81-
const result = removeMany(UserModel, UserTC).resolve({ args: {} });
81+
const result = removeMany(UserModel, UserTC).resolve({
82+
// @ts-expect-error
83+
args: {},
84+
});
8285
await expect(result).rejects.toThrow(
8386
'User.removeMany resolver requires at least one value in args.filter'
8487
);

0 commit comments

Comments
 (0)