Browse Source

feat(server): Generate ListOptions for nested list fields

Michael Bromley 7 years ago
parent
commit
00c5c514b8

+ 53 - 0
server/src/api/config/generate-list-options.spec.ts

@@ -252,4 +252,57 @@ describe('generateListOptions()', () => {
                    }`),
                    }`),
         );
         );
     });
     });
+
+    it('generates ListOptions for nested list queries', () => {
+        const input = `
+               ${COMMON_TYPES}
+               type Query {
+                   people: PersonList
+               }
+
+               type Person {
+                   id: ID!
+                   orders(options: OrderListOptions): OrderList
+               }
+
+               type OrderList implements PaginatedList {
+                   items: [Order!]!
+                   totalItems: Int!
+               }
+
+               type Order {
+                   id: ID!
+                   code: String!
+               }
+
+               # Generated at runtime
+               input OrderListOptions
+           `;
+
+        const result = generateListOptions(buildSchema(input));
+
+        expect(printType(result.getType('OrderListOptions')!)).toBe(
+            removeLeadingWhitespace(`
+                   input OrderListOptions {
+                     skip: Int
+                     take: Int
+                     sort: OrderSortParameter
+                     filter: OrderFilterParameter
+                   }`),
+        );
+        expect(printType(result.getType('OrderSortParameter')!)).toBe(
+            removeLeadingWhitespace(`
+                   input OrderSortParameter {
+                     id: SortOrder
+                     code: SortOrder
+                   }`),
+        );
+
+        expect(printType(result.getType('OrderFilterParameter')!)).toBe(
+            removeLeadingWhitespace(`
+                   input OrderFilterParameter {
+                     code: StringOperators
+                   }`),
+        );
+    });
 });
 });

+ 44 - 42
server/src/api/config/generate-list-options.ts

@@ -28,59 +28,61 @@ export function generateListOptions(typeDefsOrSchema: string | GraphQLSchema): G
     if (!queryType) {
     if (!queryType) {
         return schema;
         return schema;
     }
     }
-    const queries = queryType.getFields();
-    const { SortOrder, StringOperators, BooleanOperators, NumberOperators, DateOperators } = getCommonTypes(
-        schema,
+    const objectTypes = Object.values(schema.getTypeMap()).filter(isObjectType);
+    const allFields = objectTypes.reduce(
+        (fields, type) => {
+            const typeFields = Object.values(type.getFields()).filter(f => isListQueryType(f.type));
+            return [...fields, ...typeFields];
+        },
+        [] as Array<GraphQLField<any, any>>,
     );
     );
     const generatedTypes: GraphQLNamedType[] = [];
     const generatedTypes: GraphQLNamedType[] = [];
 
 
-    for (const query of Object.values(queries)) {
-        const type = isNonNullType(query.type) ? query.type.ofType : query.type;
-        const isListQuery =
-            isObjectType(type) && !!type.getInterfaces().find(i => i.name === 'PaginatedList');
-
-        if (isListQuery) {
-            const targetTypeName = type.toString().replace(/List$/, '');
-            const targetType = schema.getType(targetTypeName);
-            if (targetType && isObjectType(targetType)) {
-                const sortParameter = createSortParameter(schema, targetType);
-                const filterParameter = createFilterParameter(schema, targetType);
-                const existingListOptions = schema.getType(
-                    `${targetTypeName}ListOptions`,
-                ) as GraphQLInputObjectType | null;
-                const generatedListOptions = new GraphQLInputObjectType({
-                    name: `${targetTypeName}ListOptions`,
-                    fields: {
-                        skip: { type: GraphQLInt },
-                        take: { type: GraphQLInt },
-                        sort: { type: sortParameter },
-                        filter: { type: filterParameter },
-                        ...(existingListOptions ? existingListOptions.getFields() : {}),
-                    },
-                });
-                let listOptionsInput: GraphQLInputObjectType;
-                if (existingListOptions) {
-                    generatedTypes.push(existingListOptions);
-                }
-
-                listOptionsInput = generatedListOptions;
+    for (const query of allFields) {
+        const targetTypeName = unwrapNonNullType(query.type)
+            .toString()
+            .replace(/List$/, '');
+        const targetType = schema.getType(targetTypeName);
+        if (targetType && isObjectType(targetType)) {
+            const sortParameter = createSortParameter(schema, targetType);
+            const filterParameter = createFilterParameter(schema, targetType);
+            const existingListOptions = schema.getType(
+                `${targetTypeName}ListOptions`,
+            ) as GraphQLInputObjectType | null;
+            const generatedListOptions = new GraphQLInputObjectType({
+                name: `${targetTypeName}ListOptions`,
+                fields: {
+                    skip: { type: GraphQLInt },
+                    take: { type: GraphQLInt },
+                    sort: { type: sortParameter },
+                    filter: { type: filterParameter },
+                    ...(existingListOptions ? existingListOptions.getFields() : {}),
+                },
+            });
+            let listOptionsInput: GraphQLInputObjectType;
 
 
-                if (!query.args.find(a => a.type.toString() === `${targetTypeName}ListOptions`)) {
-                    query.args.push({
-                        name: 'options',
-                        type: listOptionsInput,
-                    });
-                }
+            listOptionsInput = generatedListOptions;
 
 
-                generatedTypes.push(filterParameter);
-                generatedTypes.push(sortParameter);
-                generatedTypes.push(listOptionsInput);
+            if (!query.args.find(a => a.type.toString() === `${targetTypeName}ListOptions`)) {
+                query.args.push({
+                    name: 'options',
+                    type: listOptionsInput,
+                });
             }
             }
+
+            generatedTypes.push(filterParameter);
+            generatedTypes.push(sortParameter);
+            generatedTypes.push(listOptionsInput);
         }
         }
     }
     }
     return mergeSchemas({ schemas: [schema, generatedTypes] });
     return mergeSchemas({ schemas: [schema, generatedTypes] });
 }
 }
 
 
+function isListQueryType(type: GraphQLOutputType): type is GraphQLObjectType {
+    const innerType = unwrapNonNullType(type);
+    return isObjectType(innerType) && !!innerType.getInterfaces().find(i => i.name === 'PaginatedList');
+}
+
 function createSortParameter(schema: GraphQLSchema, targetType: GraphQLObjectType) {
 function createSortParameter(schema: GraphQLSchema, targetType: GraphQLObjectType) {
     const fields = Object.values(targetType.getFields());
     const fields = Object.values(targetType.getFields());
     const targetTypeName = targetType.name;
     const targetTypeName = targetType.name;