| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196 |
- import {
- buildSchema,
- extendSchema,
- GraphQLEnumType,
- GraphQLField,
- GraphQLInputFieldConfig,
- GraphQLInputFieldConfigMap,
- GraphQLInputObjectType,
- GraphQLInputType,
- GraphQLInt,
- GraphQLNamedType,
- GraphQLObjectType,
- GraphQLOutputType,
- GraphQLSchema,
- isEnumType,
- isListType,
- isNonNullType,
- isObjectType,
- } from 'graphql';
- import { mergeSchemas } from 'graphql-tools';
- /**
- * Generates ListOptions inputs for queries which return PaginatedList types.
- */
- export function generateListOptions(typeDefsOrSchema: string | GraphQLSchema): GraphQLSchema {
- const schema = typeof typeDefsOrSchema === 'string' ? buildSchema(typeDefsOrSchema) : typeDefsOrSchema;
- const queryType = schema.getQueryType();
- if (!queryType) {
- return 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[] = [];
- 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;
- listOptionsInput = generatedListOptions;
- 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] });
- }
- 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) {
- const fields = Object.values(targetType.getFields());
- const targetTypeName = targetType.name;
- const SortOrder = schema.getType('SortOrder') as GraphQLEnumType;
- const sortableTypes = ['ID', 'String', 'Int', 'Float', 'DateTime'];
- return new GraphQLInputObjectType({
- name: `${targetTypeName}SortParameter`,
- fields: fields
- .filter(field => sortableTypes.includes(unwrapNonNullType(field.type).name))
- .reduce(
- (result, field) => {
- const fieldConfig: GraphQLInputFieldConfig = {
- type: SortOrder,
- };
- return {
- ...result,
- [field.name]: fieldConfig,
- };
- },
- {} as GraphQLInputFieldConfigMap,
- ),
- });
- }
- function createFilterParameter(schema: GraphQLSchema, targetType: GraphQLObjectType): GraphQLInputObjectType {
- const fields = Object.values(targetType.getFields());
- const targetTypeName = targetType.name;
- const { StringOperators, BooleanOperators, NumberOperators, DateOperators } = getCommonTypes(schema);
- return new GraphQLInputObjectType({
- name: `${targetTypeName}FilterParameter`,
- fields: fields.reduce(
- (result, field) => {
- const filterType = getFilterType(field);
- if (!filterType) {
- return result;
- }
- const fieldConfig: GraphQLInputFieldConfig = {
- type: filterType,
- };
- return {
- ...result,
- [field.name]: fieldConfig,
- };
- },
- {} as GraphQLInputFieldConfigMap,
- ),
- });
- function getFilterType(field: GraphQLField<any, any>): GraphQLInputType | undefined {
- if (isListType(field.type)) {
- return;
- }
- const innerType = unwrapNonNullType(field.type);
- if (isEnumType(innerType)) {
- return StringOperators;
- }
- switch (innerType.name) {
- case 'String':
- return StringOperators;
- case 'Boolean':
- return BooleanOperators;
- case 'Int':
- case 'Float':
- return NumberOperators;
- case 'DateTime':
- return DateOperators;
- default:
- return;
- }
- }
- }
- function getCommonTypes(schema: GraphQLSchema) {
- const SortOrder = schema.getType('SortOrder') as GraphQLEnumType | null;
- const StringOperators = schema.getType('StringOperators') as GraphQLInputType | null;
- const BooleanOperators = schema.getType('BooleanOperators') as GraphQLInputType | null;
- const NumberRange = schema.getType('NumberRange') as GraphQLInputType | null;
- const NumberOperators = schema.getType('NumberOperators') as GraphQLInputType | null;
- const DateRange = schema.getType('DateRange') as GraphQLInputType | null;
- const DateOperators = schema.getType('DateOperators') as GraphQLInputType | null;
- if (
- !SortOrder ||
- !StringOperators ||
- !BooleanOperators ||
- !NumberRange ||
- !NumberOperators ||
- !DateRange ||
- !DateOperators
- ) {
- throw new Error(`A common type was not defined`);
- }
- return {
- SortOrder,
- StringOperators,
- BooleanOperators,
- NumberOperators,
- DateOperators,
- };
- }
- /**
- * Unwraps the inner type if it is inside a non-nullable type
- */
- function unwrapNonNullType(type: GraphQLOutputType): GraphQLNamedType {
- if (isNonNullType(type)) {
- return type.ofType;
- }
- return type;
- }
|