Skip to content

Commit 6f1e85e

Browse files
committed
fix(filterOperators): field operators in filter arg now do not create nested types without fields
1 parent 4c7de01 commit 6f1e85e

File tree

2 files changed

+81
-22
lines changed

2 files changed

+81
-22
lines changed

src/resolvers/helpers/__tests__/filterOperators-test.ts

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import mongoose from 'mongoose';
2-
import { schemaComposer, InputTypeComposer } from 'graphql-compose';
2+
import { schemaComposer, InputTypeComposer, SchemaComposer, graphql } from 'graphql-compose';
33
import { composeWithMongoose } from '../../../composeWithMongoose';
4+
import { composeMongoose } from '../../../composeMongoose';
45

56
import {
67
_createOperatorsField,
@@ -271,4 +272,68 @@ describe('Resolver helper `filter` ->', () => {
271272
});
272273
});
273274
});
275+
276+
describe('integration tests', () => {
277+
it('should not throw error: must define one or more fields', async () => {
278+
const OrderDetailsSchema = new mongoose.Schema(
279+
{
280+
productID: Number,
281+
unitPrice: Number,
282+
quantity: Number,
283+
discount: Number,
284+
},
285+
{
286+
_id: false,
287+
}
288+
);
289+
290+
const OrderSchema = new mongoose.Schema(
291+
{
292+
orderID: {
293+
type: Number,
294+
description: 'Order unique ID',
295+
unique: true,
296+
},
297+
customerID: String,
298+
employeeID: Number,
299+
orderDate: Date,
300+
requiredDate: Date,
301+
shippedDate: Date,
302+
shipVia: Number,
303+
freight: Number,
304+
shipName: String,
305+
details: {
306+
type: [OrderDetailsSchema],
307+
index: true,
308+
description: 'List of ordered products',
309+
},
310+
},
311+
{
312+
collection: 'northwind_orders',
313+
}
314+
);
315+
316+
const OrderModel = mongoose.model<any>('Order', OrderSchema);
317+
318+
const OrderTC = composeMongoose(OrderModel);
319+
320+
const orderFindOneResolver = OrderTC.mongooseResolvers.findOne();
321+
322+
const sc = new SchemaComposer();
323+
sc.Query.addFields({
324+
order: orderFindOneResolver,
325+
});
326+
327+
const schema = sc.buildSchema();
328+
329+
const res = await graphql.graphql({
330+
schema,
331+
source: `{ __typename }`,
332+
});
333+
334+
expect(res?.errors?.[0]?.message).not.toBe(
335+
'Input Object type FilterFindOneOrderDetailsOperatorsInput must define one or more fields.'
336+
);
337+
});
338+
});
274339
});

src/resolvers/helpers/filterOperators.ts

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import {
2-
getNamedType,
3-
GraphQLInputObjectType,
4-
GraphQLScalarType,
5-
GraphQLEnumType,
6-
GraphQLString,
7-
} from 'graphql-compose/lib/graphql';
81
import type { Model } from 'mongoose';
9-
import { InputTypeComposer, inspect } from 'graphql-compose';
2+
import { EnumTypeComposer, InputTypeComposer, inspect, ScalarTypeComposer } from 'graphql-compose';
103
import type { InputTypeComposerFieldConfigAsObjectDefinition } from 'graphql-compose';
114

125
import { upperFirst, getIndexesFromModel } from '../../utils';
@@ -89,24 +82,23 @@ export function _availableOperatorsFields(
8982
: availableOperators;
9083

9184
operators.forEach((operatorName: string) => {
92-
// unwrap from GraphQLNonNull and GraphQLList, if present
93-
const fieldType = getNamedType(itc.getFieldType(fieldName));
85+
const fieldTC = itc.getFieldTC(fieldName);
9486

95-
if (fieldType) {
87+
if (fieldTC) {
9688
if (['in', 'nin', 'in[]', 'nin[]'].includes(operatorName)) {
9789
// wrap with GraphQLList, if operator required this with `[]`
9890
const newName = operatorName.slice(-2) === '[]' ? operatorName.slice(0, -2) : operatorName;
99-
fields[newName] = { type: [fieldType] as any };
91+
fields[newName] = { type: [fieldTC] };
10092
} else {
10193
if (operatorName === 'exists') {
10294
fields[operatorName] = { type: 'Boolean' };
10395
} else if (operatorName === 'regex') {
10496
// Only for fields with type String allow regex operator
105-
if (fieldType === GraphQLString) {
97+
if (fieldTC.getTypeName() === 'String') {
10698
fields[operatorName] = { type: GraphQLRegExpAsString };
10799
}
108100
} else {
109-
fields[operatorName] = { type: fieldType as any };
101+
fields[operatorName] = { type: fieldTC };
110102
}
111103
}
112104
}
@@ -148,15 +140,14 @@ export function _recurseSchema(
148140
}
149141

150142
const fieldTC = sourceITC.getFieldTC(fieldName);
151-
const fieldType = fieldTC.getType();
152143

153144
// prevent infinite recursion
154-
if (sourceITC.getType() === fieldType) return;
145+
if (sourceITC === fieldTC) return;
155146

156147
const baseTypeName = `${opts.baseTypeName}${upperFirst(fieldName)}`;
157148
const inputFieldTypeName = `${opts.prefix || ''}${baseTypeName}${opts.suffix || ''}`;
158149

159-
if (fieldType instanceof GraphQLScalarType || fieldType instanceof GraphQLEnumType) {
150+
if (fieldTC instanceof ScalarTypeComposer || fieldTC instanceof EnumTypeComposer) {
160151
if (
161152
fieldOperatorsConfig &&
162153
!Array.isArray(fieldOperatorsConfig) &&
@@ -181,7 +172,7 @@ export function _recurseSchema(
181172
[fieldName]: fieldOperatorsITC,
182173
});
183174
}
184-
} else if (fieldType instanceof GraphQLInputObjectType) {
175+
} else if (fieldTC instanceof InputTypeComposer) {
185176
const fieldOperatorsITC = schemaComposer.createInputTC(inputFieldTypeName);
186177
_recurseSchema(
187178
fieldOperatorsITC,
@@ -194,9 +185,12 @@ export function _recurseSchema(
194185
indexedFields,
195186
fieldPath
196187
);
197-
inputITC.addFields({
198-
[fieldName]: fieldOperatorsITC,
199-
});
188+
189+
if (fieldOperatorsITC.getFieldNames().length > 0) {
190+
inputITC.addFields({
191+
[fieldName]: fieldOperatorsITC,
192+
});
193+
}
200194
}
201195
});
202196
}

0 commit comments

Comments
 (0)