Skip to content

Commit 6f20e1d

Browse files
committed
feat: required fields from mongoose schema became required in GraphQL
closes #224
1 parent df6afdb commit 6f20e1d

18 files changed

+88
-13
lines changed

src/__mocks__/userModel.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,17 @@ const UserSchema: SchemaType<any> = new Schema(
2828

2929
name: {
3030
type: String,
31+
required: true,
3132
description: 'Person name',
3233
},
3334

3435
age: {
3536
type: Number,
3637
description: 'Full years',
38+
required() {
39+
// in graphql this field should be Nullable
40+
return this.name === 'Something special';
41+
},
3742
},
3843

3944
gender: {
@@ -120,7 +125,7 @@ UserSchema.set('autoIndex', false);
120125
UserSchema.index({ name: 1, age: -1 });
121126

122127
// eslint-disable-next-line
123-
UserSchema.virtual('nameVirtual').get(function() {
128+
UserSchema.virtual('nameVirtual').get(function () {
124129
return `VirtualFieldValue${this._id}`;
125130
});
126131

src/__tests__/composeWithMongoose-test.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,8 +208,13 @@ describe('composeWithMongoose ->', () => {
208208
},
209209
},
210210
});
211-
const filterArgInFindOne = typeComposer.getResolver('findOne').getArgITC('filter');
212-
expect(filterArgInFindOne.isFieldNonNull('age')).toBe(true);
211+
212+
const ac = typeComposer.getResolver('createOne').getArgITC('record');
213+
expect(ac.isFieldNonNull('age')).toBe(true);
214+
215+
// but should be nullable in filters
216+
const ac2 = typeComposer.getResolver('findOne').getArgITC('filter');
217+
expect(ac2.isFieldNonNull('age')).toBe(false);
213218
});
214219

215220
it('should use cached type to avoid maximum call stack size exceeded', () => {

src/__tests__/composeWithMongooseDiscriminators-test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,8 @@ describe('composeWithMongooseDiscriminators ->', () => {
3939
},
4040
},
4141
});
42-
const filterArgInFindOne: any = typeComposer.getResolver('findOne').getArg('filter');
43-
const inputComposer = schemaComposer.createInputTC(filterArgInFindOne.type);
44-
expect(inputComposer.isFieldNonNull('kind')).toBe(true);
42+
const ac: any = typeComposer.getResolver('createOne').getArgTC('record');
43+
expect(ac.isFieldNonNull('kind')).toBe(true);
4544
});
4645

4746
it('should proceed customizationOptions.inputType.fields.required', () => {

src/__tests__/fieldConverter-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ describe('fieldConverter', () => {
275275
const tc = convertModelToGraphQL(UserModel, 'User', sc);
276276

277277
it('should work with String', () => {
278-
expect(tc.getFieldTypeName('name')).toBe('String');
278+
expect(tc.getFieldTypeName('name')).toBe('String!');
279279
expect(tc.getFieldTypeName('skills')).toBe('[String]');
280280
});
281281

src/fieldsConverter.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,19 @@ export function convertModelToGraphQL<TSource, TContext>(
140140

141141
const mongooseFields = getFieldsFromModel(model);
142142
const graphqlFields = {};
143+
const requiredFields = [];
143144

144145
Object.keys(mongooseFields).forEach((fieldName) => {
145146
const mongooseField: MongooseFieldT = mongooseFields[fieldName];
146147

148+
if (
149+
(mongooseField: any).isRequired &&
150+
// conditional required field in mongoose cannot be NonNulable in GraphQL
151+
typeof (mongooseField: any).originalRequiredValue !== 'function'
152+
) {
153+
requiredFields.push(fieldName);
154+
}
155+
147156
graphqlFields[fieldName] = {
148157
type: convertFieldToGraphQL(mongooseField, typeName, sc),
149158
description: _getFieldDescription(mongooseField),
@@ -165,6 +174,7 @@ export function convertModelToGraphQL<TSource, TContext>(
165174
});
166175

167176
typeComposer.addFields(graphqlFields);
177+
typeComposer.makeFieldNonNull(requiredFields);
168178
return typeComposer;
169179
}
170180

src/resolvers/__tests__/createMany-test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,10 @@ describe('createMany() ->', () => {
187187

188188
describe('Resolver.getType()', () => {
189189
it('should have correct output type name', () => {
190-
const outputType: any = createMany(UserModel, UserTC).getType();
191-
expect(outputType.name).toBe(`CreateMany${UserTC.getTypeName()}Payload`);
190+
const resolver: any = createMany(UserModel, UserTC);
191+
expect(resolver.getTypeName()).toBe(`CreateMany${UserTC.getTypeName()}Payload`);
192+
expect(resolver.getArgITC('records').getFieldTypeName('name')).toBe('String!');
193+
expect(resolver.getArgITC('records').getFieldTypeName('age')).toBe('Float');
192194
});
193195

194196
it('should have recordIds field, NonNull List', () => {

src/resolvers/__tests__/createOne-test.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,10 @@ describe('createOne() ->', () => {
114114

115115
describe('Resolver.getType()', () => {
116116
it('should have correct output type name', () => {
117-
const outputType: any = createOne(UserModel, UserTC).getType();
118-
expect(outputType.name).toBe(`CreateOne${UserTC.getTypeName()}Payload`);
117+
const resolver = createOne(UserModel, UserTC);
118+
expect(resolver.getTypeName()).toBe(`CreateOne${UserTC.getTypeName()}Payload`);
119+
expect(resolver.getArgITC('record').getFieldTypeName('name')).toBe('String!');
120+
expect(resolver.getArgITC('record').getFieldTypeName('age')).toBe('Float');
119121
});
120122

121123
it('should have recordId field', () => {

src/resolvers/__tests__/findMany-test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,12 @@ describe('findMany() ->', () => {
124124
expect(result).toEqual([{ overridden: true }]);
125125
});
126126
});
127+
128+
describe('Resolver.getType()', () => {
129+
it('should have all fields optional in filter', () => {
130+
const resolver = findMany(UserModel, UserTC);
131+
expect(resolver.getArgITC('filter').getFieldTypeName('name')).toBe('String');
132+
expect(resolver.getArgITC('filter').getFieldTypeName('age')).toBe('Float');
133+
});
134+
});
127135
});

src/resolvers/__tests__/findOne-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,5 +138,11 @@ describe('findOne() ->', () => {
138138
const outputType = findOne(UserModel, UserTC).getType();
139139
expect(outputType).toBe(UserTC.getType());
140140
});
141+
142+
it('should have all fields optional in filter', () => {
143+
const resolver = findOne(UserModel, UserTC);
144+
expect(resolver.getArgITC('filter').getFieldTypeName('name')).toBe('String');
145+
expect(resolver.getArgITC('filter').getFieldTypeName('age')).toBe('Float');
146+
});
141147
});
142148
});

src/resolvers/__tests__/removeMany-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,5 +142,11 @@ describe('removeMany() ->', () => {
142142
const outputType = removeMany(UserModel, UserTC).getType();
143143
expect(outputType).toBe(existedType.getType());
144144
});
145+
146+
it('should have all fields optional in filter', () => {
147+
const resolver = removeMany(UserModel, UserTC);
148+
expect(resolver.getArgITC('filter').getFieldTypeName('name')).toBe('String');
149+
expect(resolver.getArgITC('filter').getFieldTypeName('age')).toBe('Float');
150+
});
145151
});
146152
});

0 commit comments

Comments
 (0)