Skip to content

Commit e18cf8f

Browse files
authored
feat(TypeScript): add typings for DiscriminatorTypeComposer, add helpers for resolver args, get by ${ResolverName}Args (#126)
* chore: add tslint prettier rules plugins, apply styles to all `.ts` files * add `tslint-config-prettier` and `tslint-plugin-prettier` packages * chore: update `graphql-compose` to v5.1.1 this is to get all the latest changes that were made with TypeComposer * feat(TypeScript): add typings for `DiscriminatorTypeComposer` fix(TypeScript): adjust composeMongoose types to support new TypeComposer typings * chore: update `graphql-compose` to v5.2.0 as it contains `TArgs`, `TArgsMap` changes * feat(TypeScript): add `TArgs` and `TArgsMap` to `DiscriminatorTypeComposer` * feat(TypeScript): add helpers for resolver args, get by `${ResolverName}Args`
1 parent 230c2b1 commit e18cf8f

40 files changed

+834
-136
lines changed

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"eslint-plugin-prettier": "^2.6.2",
6262
"flow-bin": "^0.80.0",
6363
"graphql": "14.0.0",
64-
"graphql-compose": "^5.0.2",
64+
"graphql-compose": "^5.2.0",
6565
"graphql-compose-connection": ">=4.0.0",
6666
"graphql-compose-pagination": ">=4.0.0",
6767
"jest": "^23.5.0",
@@ -72,6 +72,8 @@
7272
"rimraf": "^2.6.2",
7373
"semantic-release": "^15.9.12",
7474
"tslint": "^5.11.0",
75+
"tslint-config-prettier": "^1.15.0",
76+
"tslint-plugin-prettier": "^1.3.0",
7577
"typescript": "^3.0.3"
7678
},
7779
"config": {
@@ -97,7 +99,7 @@
9799
"coverage": "jest --coverage --maxWorkers 4",
98100
"lint": "npm run eslint && npm run tslint",
99101
"eslint": "eslint --ext .js ./src",
100-
"tslint": "tslint -p . \"src/**/*.d.ts\"",
102+
"tslint": "tslint -p .",
101103
"tscheck": "tsc",
102104
"flow": "./node_modules/.bin/flow",
103105
"test": "npm run coverage && npm run lint && npm run flow && npm run tscheck",
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { model, Schema } from 'mongoose';
2+
import { composeWithMongoose } from '../../composeWithMongoose';
3+
import { FindManyArgs } from '../..';
4+
import { Context, IPost, IUser, IUserModel } from './mock-typedefs';
5+
6+
const UserModel = model<IUser, IUserModel>('User', new Schema({}));
7+
const PostModel = model('Post', new Schema({}));
8+
9+
const UserTC = composeWithMongoose<IUser, Context>(UserModel);
10+
11+
// if one doesn't pass `any` as source, then we will eventually get `Document`
12+
// as source which to an extend is not as open as `any`.
13+
// the `Document` type was set internally by mongoose typedefs.
14+
const PostTC = composeWithMongoose<any>(PostModel);
15+
16+
UserTC.addFields({
17+
newField: {
18+
// test Thunk
19+
type: 'Int',
20+
resolve: (source, args, context) => {
21+
source.name = 'GQC';
22+
// source.name = 44;
23+
context.auth = 'auth';
24+
// context.auth = 44;
25+
},
26+
},
27+
});
28+
29+
PostTC.addFields({
30+
newField: {
31+
// test Thunk
32+
type: 'Int',
33+
resolve: (source, args, context) => {
34+
source.name = 'GQC';
35+
source.name = 44;
36+
context.auth = 'auth';
37+
context.auth = 44;
38+
},
39+
},
40+
});
41+
42+
PostTC.getResolver('findMany').wrapResolve<any, FindManyArgs<IPost>>(
43+
next => rp => {
44+
if (rp.source && rp.args) {
45+
rp.args.limit = 50;
46+
// fix this to display only Post fields.
47+
// Avoid Document fields
48+
rp.args.filter.title = 'New Title';
49+
// rp.args.filter.title = 5;
50+
// rp.args.limit = 'limit';
51+
}
52+
},
53+
);
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import { model, Schema } from 'mongoose';
2+
import { composeWithMongooseDiscriminators } from '../../composeWithMongooseDiscriminators';
3+
import {
4+
Context,
5+
EnumCharacterType,
6+
ICharacter,
7+
ICharacterModel,
8+
IDroid,
9+
IPerson,
10+
} from './mock-typedefs';
11+
12+
const CharacterModel = model<ICharacter, ICharacterModel>(
13+
'Character',
14+
new Schema({}),
15+
);
16+
17+
const PersonModel = CharacterModel.discriminator<IPerson>(
18+
EnumCharacterType.Person,
19+
new Schema({}),
20+
);
21+
const DroidModel = CharacterModel.discriminator<IDroid>(
22+
EnumCharacterType.Droid,
23+
new Schema({}),
24+
);
25+
26+
const CharacterTC = composeWithMongooseDiscriminators<ICharacter, Context>(
27+
CharacterModel,
28+
{
29+
reorderFields: true,
30+
},
31+
);
32+
33+
const PersonTC = CharacterTC.discriminator(PersonModel);
34+
const DroidTC = CharacterTC.discriminator(DroidModel);
35+
36+
CharacterTC.getResolver<ICharacter, { arg1: string; arg2: number }>(
37+
'findOne',
38+
).wrapResolve(next => rp => {
39+
if (rp.source && rp.args) {
40+
rp.args.arg1 = 'string';
41+
rp.args.arg2 = 888;
42+
}
43+
});
44+
45+
CharacterTC.addFields({
46+
newField: {
47+
// test Thunk
48+
type: 'Int',
49+
resolve: (source, args, context) => {
50+
source.name = 'GQC';
51+
// source.name = 44;
52+
context.auth = 'auth';
53+
// context.auth = 44;
54+
},
55+
},
56+
});
57+
58+
PersonTC.addFields({
59+
newField: {
60+
// test Thunk
61+
type: 'Int',
62+
resolve: (source, args, context) => {
63+
source.name = 'GQC';
64+
// source.name = 44;
65+
source.dob = 51225545;
66+
// source.dob = 'string';
67+
context.auth = 'auth';
68+
// context.auth = 44;
69+
},
70+
},
71+
});
72+
73+
DroidTC.addFields({
74+
newField: {
75+
// test Thunk
76+
type: 'Int',
77+
resolve: (source, args, context) => {
78+
source.name = 'GQC';
79+
// source.name = 44;
80+
source.makeDate = new Date();
81+
// source.makeDate = 55566156;
82+
context.auth = 'auth';
83+
// context.auth = 44;
84+
},
85+
},
86+
});
87+
88+
// Testing a scenario where we use no types
89+
const Char = model('Char', new Schema({}));
90+
const Per = Char.discriminator('Per', new Schema({}));
91+
92+
// by default, from mongoose types, sources are `Document`.
93+
// it is good to explicitly override `Document` to `any`
94+
// then since we did not pass a context, our context results to `any`
95+
const CharTC = composeWithMongooseDiscriminators<any>(Char);
96+
const PerTC = CharTC.discriminator<any>(Per);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { Document, Model } from 'mongoose';
2+
3+
export interface Context {
4+
uid: string;
5+
profileUrl: string;
6+
auth: string;
7+
}
8+
9+
export interface IUser extends Document {
10+
name: string;
11+
age: number;
12+
gender: 'male' | 'female' | 'ladyboy';
13+
skills: string[];
14+
}
15+
16+
export interface IUserModel extends Model<IUser> {
17+
statics1: string;
18+
}
19+
20+
export interface IPost extends Document {
21+
title: string;
22+
body: string;
23+
comments: Array<{ by: string; text: string }>;
24+
}
25+
26+
export enum EnumCharacterType {
27+
Person = 'Person',
28+
Droid = 'Droid',
29+
}
30+
31+
export interface ICharacter extends Document {
32+
name: string;
33+
type: EnumCharacterType;
34+
kind: string;
35+
friends: string[];
36+
appearsIn: string[];
37+
}
38+
39+
// if static functions exist
40+
export interface ICharacterModel extends Model<ICharacter> {
41+
getCharactersFriends(id: string): ICharacter[];
42+
}
43+
44+
export interface IPerson extends ICharacter {
45+
dob: number;
46+
starShips: string[];
47+
totalCredits: number;
48+
}
49+
50+
export interface IDroid extends ICharacter {
51+
makeDate: Date;
52+
modelNumber: number;
53+
primaryFunction: string[];
54+
}

src/composeWithMongoose.d.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import {
1313
} from './resolvers/helpers';
1414
import { PaginationResolverOpts } from './resolvers/pagination';
1515

16-
export type TypeConverterOpts = {
17-
schemaComposer?: SchemaComposer<any>;
16+
export type TypeConverterOpts<TContext = any> = {
17+
schemaComposer?: SchemaComposer<TContext>;
1818
name?: string;
1919
description?: string;
2020
fields?: {
@@ -111,10 +111,13 @@ export type TypeConverterResolversOpts = {
111111
pagination?: PaginationResolverOpts | false;
112112
};
113113

114-
export function composeWithMongoose(
115-
model: Model<any>,
116-
opts?: TypeConverterOpts,
117-
): TypeComposer<any>;
114+
export function composeWithMongoose<
115+
TModel extends Document = any,
116+
TContext = any
117+
>(
118+
model: Model<TModel>,
119+
opts?: TypeConverterOpts<TContext>,
120+
): TypeComposer<TModel, TContext>;
118121

119122
export function prepareFields(
120123
tc: TypeComposer<any>,
Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { TypeComposerClass } from 'graphql-compose';
2-
import { Model } from 'mongoose';
1+
import { Document, Model } from 'mongoose';
2+
import {
3+
DiscriminatorOptions,
4+
DiscriminatorTypeComposer,
5+
} from './discriminators';
36

4-
export function composeWithMongooseDiscriminators(
5-
baseModel: Model<any>,
6-
opts?: { [opts: string]: any }): TypeComposerClass<any>;
7+
export function composeWithMongooseDiscriminators<
8+
TBaseModel extends Document = any,
9+
TContext = any
10+
>(
11+
baseModel: Model<TBaseModel>,
12+
opts?: DiscriminatorOptions<TContext>,
13+
): DiscriminatorTypeComposer<TBaseModel, TContext>;

0 commit comments

Comments
 (0)