From 9a03ca16cfc81fd12471f51ed73b3fb3da732649 Mon Sep 17 00:00:00 2001 From: Koen Punt Date: Sun, 11 Nov 2018 16:42:20 +0100 Subject: [PATCH 01/19] generate resolvers for interface and union types --- packages/graphqlgen/src/generators/common.ts | 44 +- .../src/generators/flow-generator.ts | 135 ++++- .../src/generators/flow-scaffolder.ts | 32 +- .../graphqlgen/src/generators/ts-generator.ts | 240 +++++++- .../src/generators/ts-scaffolder.ts | 28 +- packages/graphqlgen/src/source-helper.ts | 50 +- .../tests/fixtures/interface/schema.graphql | 25 + .../src/tests/fixtures/interface/types.ts | 19 + .../src/tests/fixtures/union/schema.graphql | 5 +- .../flow/__snapshots__/basic.test.ts.snap | 164 +++--- .../__snapshots__/basic.test.ts.snap | 524 +++++++++++++++--- .../__snapshots__/large-schema.test.ts.snap | 6 +- .../src/tests/typescript/basic.test.ts | 15 + packages/graphqlgen/src/types.ts | 2 + 14 files changed, 1113 insertions(+), 176 deletions(-) create mode 100644 packages/graphqlgen/src/tests/fixtures/interface/schema.graphql create mode 100644 packages/graphqlgen/src/tests/fixtures/interface/types.ts diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index 194cceb8..5fed846a 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -2,9 +2,9 @@ import * as os from 'os' import { GraphQLTypeObject, - GraphQLType, GraphQLTypeField, getGraphQLEnumValues, + GraphQLTypeDefinition, } from '../source-helper' import { ModelMap, ContextDefinition, GenerateArgs, Model } from '../types' import { flatten, uniq } from '../utils' @@ -30,6 +30,14 @@ export interface TypeToInputTypeAssociation { [objectTypeName: string]: string[] } +export interface InterfacesMap { + [interfaceName: string]: GraphQLTypeDefinition[] +} + +export interface UnionsMap { + [unionName: string]: GraphQLTypeDefinition[] +} + export function fieldsFromModelDefinition( modelDef: TypeDefinition, ): FieldDefinition[] { @@ -116,7 +124,7 @@ export function getContextName(context?: ContextDefinition) { } export function getModelName( - type: GraphQLType, + type: GraphQLTypeDefinition, modelMap: ModelMap, emptyType: string = '{}', ): string { @@ -192,6 +200,8 @@ export function shouldScaffoldFieldResolver( export function printFieldLikeType( field: GraphQLTypeField, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, ) { if (field.type.isScalar) { return `${getTypeFromGraphQLType(field.type.name)}${ @@ -205,6 +215,28 @@ export function printFieldLikeType( }` } + if (field.type.isInterface) { + let types = interfacesMap[field.type.name] + .map(type => getModelName(type, modelMap)) + .filter(uniq) + .join(' | ') + if (field.type.isArray) { + types = `Array<${types}>` + } + return `${types}${!field.type.isRequired ? '| null' : ''}` + } + + if (field.type.isUnion) { + let types = unionsMap[field.type.name] + .map(type => getModelName(type, modelMap)) + .filter(uniq) + .join(' | ') + if (field.type.isArray) { + types = `Array<${types}>` + } + return `${types}${!field.type.isRequired ? '| null' : ''}` + } + return `${getModelName(field.type, modelMap)}${ field.type.isArray ? '[]' : '' }${!field.type.isRequired ? '| null' : ''}` @@ -274,11 +306,13 @@ export function isParentType(name: string) { export function groupModelsNameByImportPath(models: Model[]) { return models.reduce<{ [importPath: string]: string[] }>((acc, model) => { - if (acc[model.importPathRelativeToOutput] === undefined) { - acc[model.importPathRelativeToOutput] = [] + const fileModels = acc[model.importPathRelativeToOutput] || [] + + if (fileModels.indexOf(model.definition.name) === -1) { + fileModels.push(model.definition.name) } - acc[model.importPathRelativeToOutput].push(model.definition.name) + acc[model.importPathRelativeToOutput] = fileModels return acc }, {}) diff --git a/packages/graphqlgen/src/generators/flow-generator.ts b/packages/graphqlgen/src/generators/flow-generator.ts index eafdfcee..558e9258 100644 --- a/packages/graphqlgen/src/generators/flow-generator.ts +++ b/packages/graphqlgen/src/generators/flow-generator.ts @@ -14,6 +14,8 @@ import { renderDefaultResolvers, renderEnums, TypeToInputTypeAssociation, + InterfacesMap, + UnionsMap, } from './common' export function format(code: string, options: prettier.Options = {}) { @@ -65,12 +67,35 @@ export function generate(args: GenerateArgs): string { } }, {}) + const interfacesMap: InterfacesMap = args.interfaces.reduce( + (interfaces, int) => { + return { + ...interfaces, + [int.name]: int.types, + } + }, + {}, + ) + + const unionsMap: InterfacesMap = args.unions.reduce((interfaces, int) => { + return { + ...interfaces, + [int.name]: int.types, + } + }, {}) + return `\ ${renderHeader(args)} ${renderEnums(args)} - ${renderNamespaces(args, typeToInputTypeAssociation, inputTypesMap)} + ${renderNamespaces( + args, + interfacesMap, + unionsMap, + typeToInputTypeAssociation, + inputTypesMap, + )} ${renderResolvers(args)} @@ -111,19 +136,30 @@ function renderContext(context?: ContextDefinition) { function renderNamespaces( args: GenerateArgs, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, ): string { return args.types .filter(type => type.type.isObject) .map(type => - renderNamespace(type, typeToInputTypeAssociation, inputTypesMap, args), + renderNamespace( + type, + interfacesMap, + unionsMap, + typeToInputTypeAssociation, + inputTypesMap, + args, + ), ) .join(os.EOL) } function renderNamespace( type: GraphQLTypeObject, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, args: GenerateArgs, @@ -137,15 +173,29 @@ function renderNamespace( ${renderInputTypeInterfaces( type, args.modelMap, + interfacesMap, + unionsMap, typeToInputTypeAssociation, inputTypesMap, )} - ${renderInputArgInterfaces(type, args.modelMap)} + ${renderInputArgInterfaces(type, args.modelMap, interfacesMap, unionsMap)} - ${renderResolverFunctionInterfaces(type, args.modelMap, args.context)} + ${renderResolverFunctionInterfaces( + type, + args.modelMap, + interfacesMap, + unionsMap, + args.context, + )} - ${renderResolverTypeInterface(type, args.modelMap, args.context)} + ${renderResolverTypeInterface( + type, + args.modelMap, + interfacesMap, + unionsMap, + args.context, + )} ${/* TODO renderResolverClass(type, modelMap) */ ''} ` @@ -154,6 +204,8 @@ function renderNamespace( function renderInputTypeInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, ) { @@ -167,7 +219,13 @@ function renderInputTypeInterfaces( inputTypesMap[typeAssociation].name, )} { ${inputTypesMap[typeAssociation].fields.map( - field => `${field.name}: ${printFieldLikeType(field, modelMap)}`, + field => + `${field.name}: ${printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + )}`, )} }` }) @@ -177,9 +235,13 @@ function renderInputTypeInterfaces( function renderInputArgInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, ): string { return type.fields - .map(field => renderInputArgInterface(type, field, modelMap)) + .map(field => + renderInputArgInterface(type, field, modelMap, interfacesMap, unionsMap), + ) .join(os.EOL) } @@ -187,6 +249,8 @@ function renderInputArgInterface( type: GraphQLTypeObject, field: GraphQLTypeField, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, ): string { if (field.arguments.length === 0) { return '' @@ -200,6 +264,8 @@ function renderInputArgInterface( `${arg.name}: ${printFieldLikeType( arg as GraphQLTypeField, modelMap, + interfacesMap, + unionsMap, )}`, ) .join(',' + os.EOL)} @@ -210,11 +276,20 @@ function renderInputArgInterface( function renderResolverFunctionInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { return type.fields .map(field => - renderResolverFunctionInterface(field, type, modelMap, context), + renderResolverFunctionInterface( + field, + type, + modelMap, + interfacesMap, + unionsMap, + context, + ), ) .join(os.EOL) } @@ -223,6 +298,8 @@ function renderResolverFunctionInterface( field: GraphQLTypeField, type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { const resolverName = `${upperFirst(type.name)}_${upperFirst( @@ -236,7 +313,12 @@ function renderResolverFunctionInterface( info: GraphQLResolveInfo, ) ` - const returnType = printFieldLikeType(field, modelMap) + const returnType = printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + ) if (type.name === 'Subscription') { return ` @@ -255,13 +337,22 @@ function renderResolverFunctionInterface( function renderResolverTypeInterface( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { return ` export interface ${upperFirst(type.name)}_Resolvers { ${type.fields .map(field => - renderResolverTypeInterfaceFunction(field, type, modelMap, context), + renderResolverTypeInterfaceFunction( + field, + type, + modelMap, + interfacesMap, + unionsMap, + context, + ), ) .join(os.EOL)} } @@ -272,6 +363,8 @@ function renderResolverTypeInterfaceFunction( field: GraphQLTypeField, type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { const resolverDefinition = ` @@ -281,7 +374,12 @@ function renderResolverTypeInterfaceFunction( ctx: ${getContextName(context)}, info: GraphQLResolveInfo, )` - const returnType = printFieldLikeType(field, modelMap) + const returnType = printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + ) if (type.name === 'Subscription') { return ` @@ -301,10 +399,17 @@ function renderResolverTypeInterfaceFunction( function renderResolvers(args: GenerateArgs): string { return ` export interface Resolvers { - ${args.types - .filter(type => type.type.isObject) - .map(type => `${type.name}: ${upperFirst(type.name)}_Resolvers`) - .join(',' + os.EOL)} + ${[ + ...args.types + .filter(type => type.type.isObject) + .map(type => `${type.name}: ${upperFirst(type.name)}_Resolvers`), + ...args.interfaces.map( + type => `${type.name}?: ${upperFirst(type.name)}_Resolvers`, + ), + ...args.unions.map( + type => `${type.name}?: ${upperFirst(type.name)}_Resolvers`, + ), + ].join(`,${os.EOL}`)} } ` } diff --git a/packages/graphqlgen/src/generators/flow-scaffolder.ts b/packages/graphqlgen/src/generators/flow-scaffolder.ts index 9b075567..46a833eb 100644 --- a/packages/graphqlgen/src/generators/flow-scaffolder.ts +++ b/packages/graphqlgen/src/generators/flow-scaffolder.ts @@ -1,6 +1,10 @@ import { GenerateArgs, CodeFileLike } from '../types' import { upperFirst } from '../utils' -import { GraphQLTypeObject } from '../source-helper' +import { + GraphQLTypeObject, + GraphQLInterfaceObject, + GraphQLUnionObject, +} from '../source-helper' import { fieldsFromModelDefinition, shouldScaffoldFieldResolver, @@ -59,6 +63,27 @@ function renderExports(types: GraphQLTypeObject[]): string { .join(',')} }` } + +function renderPolyResolvers( + type: GraphQLInterfaceObject | GraphQLUnionObject, +): CodeFileLike { + const upperTypeName = upperFirst(type.name) + + const code = `\ + // @flow + // This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. + // Please do not import this file directly but copy & paste to your application code. + + import { ${upperTypeName}_Resolvers } from '[TEMPLATE-INTERFACES-PATH]' + + export const ${type.name}: ${upperTypeName}_Resolvers = { + __resolveType: (parent, ctx, info) => { + throw new Error('Resolver not implemented') + } + }` + return { path: `${type.name}.ts`, force: false, code } +} + function renderResolvers( type: GraphQLTypeObject, args: GenerateArgs, @@ -98,6 +123,11 @@ export function generate(args: GenerateArgs): CodeFileLike[] { .filter(type => !isParentType(type.name)) .map(type => renderResolvers(type, args)) + files = files.concat( + args.interfaces.map(type => renderPolyResolvers(type)), + args.unions.map(type => renderPolyResolvers(type)), + ) + files = files.concat( args.types .filter(type => isParentType(type.name)) diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index 4eef63d5..b4eefadd 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -2,7 +2,13 @@ import * as os from 'os' import * as prettier from 'prettier' import { GenerateArgs, ModelMap, ContextDefinition } from '../types' -import { GraphQLTypeField, GraphQLTypeObject } from '../source-helper' +import { + GraphQLTypeField, + GraphQLTypeObject, + GraphQLInterfaceObject, + GraphQLTypeDefinition, + GraphQLUnionObject, +} from '../source-helper' import { renderDefaultResolvers, getContextName, @@ -13,9 +19,11 @@ import { getDistinctInputTypes, renderEnums, groupModelsNameByImportPath, + InterfacesMap, + UnionsMap, } from './common' import { TypeAliasDefinition } from '../introspection/types' -import { upperFirst } from '../utils' +import { upperFirst, flatten } from '../utils' export function format(code: string, options: prettier.Options = {}) { try { @@ -66,12 +74,35 @@ export function generate(args: GenerateArgs): string { } }, {}) + const interfacesMap: InterfacesMap = args.interfaces.reduce( + (interfaces, int) => { + return { + ...interfaces, + [int.name]: int.types, + } + }, + {}, + ) + + const unionsMap: InterfacesMap = args.unions.reduce((interfaces, int) => { + return { + ...interfaces, + [int.name]: int.types, + } + }, {}) + return `\ ${renderHeader(args)} ${renderEnums(args)} - ${renderNamespaces(args, typeToInputTypeAssociation, inputTypesMap)} + ${renderNamespaces( + args, + interfacesMap, + unionsMap, + typeToInputTypeAssociation, + inputTypesMap, + )} ${renderResolvers(args)} @@ -103,7 +134,7 @@ function renderHeader(args: GenerateArgs): string { return ` // Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from 'graphql' +import { GraphQLResolveInfo, GraphQLTypeResolver, GraphQLIsTypeOfFn } from 'graphql' ${modelImports} ${renderContext(args.context)} ` @@ -119,19 +150,92 @@ function renderContext(context?: ContextDefinition) { function renderNamespaces( args: GenerateArgs, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, + typeToInputTypeAssociation: TypeToInputTypeAssociation, + inputTypesMap: InputTypesMap, +): string { + return `\ + ${renderObjectNamespaces( + args, + interfacesMap, + unionsMap, + typeToInputTypeAssociation, + inputTypesMap, + )} + + ${renderInterfaceNamespaces(args)} + + ${renderUnionNamespaces(args)} + ` +} + +function renderObjectNamespaces( + args: GenerateArgs, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, ): string { return args.types .filter(type => type.type.isObject) .map(type => - renderNamespace(type, typeToInputTypeAssociation, inputTypesMap, args), + renderNamespace( + type, + interfacesMap, + unionsMap, + typeToInputTypeAssociation, + inputTypesMap, + args, + ), ) .join(os.EOL) } +function renderInterfaceNamespaces(args: GenerateArgs): string { + return args.interfaces + .map(type => renderInterfaceNamespace(type, args)) + .join(os.EOL) +} + +function renderUnionNamespaces(args: GenerateArgs): string { + return args.unions.map(type => renderUnionNamespace(type, args)).join(os.EOL) +} + +function renderInterfaceNamespace( + graphQLTypeObject: GraphQLInterfaceObject, + args: GenerateArgs, +): string { + return `\ + export namespace ${graphQLTypeObject.name}Resolvers { + export interface Type { + __resolveType: GraphQLTypeResolver<${graphQLTypeObject.types + .map(interfaceType => getModelName(interfaceType, args.modelMap)) + .join(' | ')}, ${getContextName(args.context)}>; + } + } + ` +} + +function renderUnionNamespace( + graphQLTypeObject: GraphQLUnionObject, + args: GenerateArgs, +): string { + return `\ + export namespace ${graphQLTypeObject.name}Resolvers { + export interface Type { + __resolveType?: GraphQLTypeResolver<${graphQLTypeObject.types + .map(interfaceType => getModelName(interfaceType, args.modelMap)) + .join(' | ')}, ${getContextName(args.context)}>; + } + } + ` +} + function renderNamespace( graphQLTypeObject: GraphQLTypeObject, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, args: GenerateArgs, @@ -144,21 +248,32 @@ function renderNamespace( ${renderInputTypeInterfaces( graphQLTypeObject, args.modelMap, + interfacesMap, + unionsMap, typeToInputTypeAssociation, inputTypesMap, )} - ${renderInputArgInterfaces(graphQLTypeObject, args.modelMap)} + ${renderInputArgInterfaces( + graphQLTypeObject, + args.modelMap, + interfacesMap, + unionsMap, + )} ${renderResolverFunctionInterfaces( graphQLTypeObject, args.modelMap, + interfacesMap, + unionsMap, args.context, )} ${renderResolverTypeInterface( graphQLTypeObject, args.modelMap, + interfacesMap, + unionsMap, args.context, )} @@ -167,9 +282,44 @@ function renderNamespace( ` } +function renderIsTypeOfFunctionInterface( + type: GraphQLTypeObject, + modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, + context?: ContextDefinition, +) { + let possibleTypes: GraphQLTypeDefinition[] = [] + + if (type.interfaces) { + possibleTypes = type.interfaces.reduce( + (obj: GraphQLTypeDefinition[], interfaceName) => { + return flatten(obj, interfacesMap[interfaceName]) + }, + [], + ) + } + + for (let unionName in unionsMap) { + if (unionsMap[unionName].find(unionType => unionType.name === type.name)) { + possibleTypes = unionsMap[unionName] + } + } + + if (possibleTypes.length === 0) { + return '' + } + return `\ + __isTypeOf?: GraphQLIsTypeOfFn<${possibleTypes + .map(possibleType => getModelName(possibleType, modelMap)) + .join(' | ')}, ${getContextName(context)}>;` +} + function renderInputTypeInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, typeToInputTypeAssociation: TypeToInputTypeAssociation, inputTypesMap: InputTypesMap, ) { @@ -181,7 +331,13 @@ function renderInputTypeInterfaces( .map(typeAssociation => { return `export interface ${inputTypesMap[typeAssociation].name} { ${inputTypesMap[typeAssociation].fields.map( - field => `${field.name}: ${printFieldLikeType(field, modelMap)}`, + field => + `${field.name}: ${printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + )}`, )} }` }) @@ -191,15 +347,21 @@ function renderInputTypeInterfaces( function renderInputArgInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, ): string { return type.fields - .map(field => renderInputArgInterface(field, modelMap)) + .map(field => + renderInputArgInterface(field, modelMap, interfacesMap, unionsMap), + ) .join(os.EOL) } function renderInputArgInterface( field: GraphQLTypeField, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, ): string { if (field.arguments.length === 0) { return '' @@ -213,6 +375,8 @@ function renderInputArgInterface( `${arg.name}: ${printFieldLikeType( arg as GraphQLTypeField, modelMap, + interfacesMap, + unionsMap, )}`, ) .join(os.EOL)} @@ -223,11 +387,20 @@ function renderInputArgInterface( function renderResolverFunctionInterfaces( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { return type.fields .map(field => - renderResolverFunctionInterface(field, type, modelMap, context), + renderResolverFunctionInterface( + field, + type, + modelMap, + interfacesMap, + unionsMap, + context, + ), ) .join(os.EOL) } @@ -236,6 +409,8 @@ function renderResolverFunctionInterface( field: GraphQLTypeField, type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { const resolverName = `${upperFirst(field.name)}Resolver` @@ -249,7 +424,12 @@ function renderResolverFunctionInterface( info: GraphQLResolveInfo, ) ` - const returnType = printFieldLikeType(field, modelMap) + const returnType = printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + ) if (type.name === 'Subscription') { return ` @@ -268,15 +448,31 @@ function renderResolverFunctionInterface( function renderResolverTypeInterface( type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { return ` export interface Type { ${type.fields .map(field => - renderResolverTypeInterfaceFunction(field, type, modelMap, context), + renderResolverTypeInterfaceFunction( + field, + type, + modelMap, + interfacesMap, + unionsMap, + context, + ), ) .join(os.EOL)} + ${renderIsTypeOfFunctionInterface( + type, + modelMap, + interfacesMap, + unionsMap, + context, + )} } ` } @@ -285,6 +481,8 @@ function renderResolverTypeInterfaceFunction( field: GraphQLTypeField, type: GraphQLTypeObject, modelMap: ModelMap, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, context?: ContextDefinition, ): string { const resolverDefinition = ` @@ -297,7 +495,12 @@ function renderResolverTypeInterfaceFunction( info: GraphQLResolveInfo, ) ` - const returnType = printFieldLikeType(field, modelMap) + const returnType = printFieldLikeType( + field, + modelMap, + interfacesMap, + unionsMap, + ) if (type.name === 'Subscription') { return ` @@ -316,12 +519,15 @@ function renderResolverTypeInterfaceFunction( } function renderResolvers(args: GenerateArgs): string { - return ` + return `\ export interface Resolvers { - ${args.types - .filter(type => type.type.isObject) - .map(type => `${type.name}: ${type.name}Resolvers.Type`) - .join(os.EOL)} + ${[ + ...args.types + .filter(obj => obj.type.isObject) + .map(type => `${type.name}: ${type.name}Resolvers.Type`), + ...args.interfaces.map(type => `${type.name}?: ${type.name}Resolvers.Type`), + ...args.unions.map(type => `${type.name}?: ${type.name}Resolvers.Type`), + ].join(os.EOL)} } ` } diff --git a/packages/graphqlgen/src/generators/ts-scaffolder.ts b/packages/graphqlgen/src/generators/ts-scaffolder.ts index f7b6621d..c11c325a 100644 --- a/packages/graphqlgen/src/generators/ts-scaffolder.ts +++ b/packages/graphqlgen/src/generators/ts-scaffolder.ts @@ -1,5 +1,9 @@ import { GenerateArgs, CodeFileLike } from '../types' -import { GraphQLTypeObject } from '../source-helper' +import { + GraphQLTypeObject, + GraphQLInterfaceObject, + GraphQLUnionObject, +} from '../source-helper' import { fieldsFromModelDefinition, shouldScaffoldFieldResolver, @@ -36,6 +40,23 @@ function renderResolvers( return { path: `${type.name}.ts`, force: false, code } } +function renderPolyResolvers( + type: GraphQLInterfaceObject | GraphQLUnionObject, +): CodeFileLike { + const code = `\ + // This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. + // Please do not import this file directly but copy & paste to your application code. + + import { ${type.name}Resolvers } from '[TEMPLATE-INTERFACES-PATH]' + + export const ${type.name}: ${type.name}Resolvers.Type = { + __resolveType: (parent, ctx) => { + throw new Error('Resolver not implemented') + } + }` + return { path: `${type.name}.ts`, force: false, code } +} + function renderParentResolvers(type: GraphQLTypeObject): CodeFileLike { const code = `\ // This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. @@ -96,6 +117,11 @@ export function generate(args: GenerateArgs): CodeFileLike[] { .filter(type => !isParentType(type.name)) .map(type => renderResolvers(type, args)) + files = files.concat( + args.interfaces.map(type => renderPolyResolvers(type)), + args.unions.map(type => renderPolyResolvers(type)), + ) + files = files.concat( args.types .filter(type => type.type.isObject) diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 502e379d..eb2b542a 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -23,6 +23,7 @@ export type GraphQLTypes = { types: GraphQLTypeObject[] unions: GraphQLUnionObject[] enums: GraphQLEnumObject[] + interfaces: GraphQLInterfaceObject[] } /** Converts typeDefs, e.g. the raw SDL string, into our `GraphQLTypes`. */ @@ -31,10 +32,11 @@ export function extractTypes(typeDefs: string): GraphQLTypes { const types = extractGraphQLTypes(schema) const unions = extractGraphQLUnions(schema) const enums = extractGraphQLEnums(schema) - return { types, enums, unions } + const interfaces = extractGraphQLInterfaces(schema) + return { types, enums, unions, interfaces } } -type GraphQLTypeDefinition = { +export type GraphQLTypeDefinition = { name: string isScalar: boolean isEnum: boolean @@ -64,6 +66,7 @@ export type GraphQLTypeObject = { name: string type: GraphQLTypeDefinition fields: GraphQLTypeField[] + interfaces?: string[] } export type GraphQLEnumObject = { @@ -78,6 +81,13 @@ export type GraphQLUnionObject = { types: GraphQLTypeDefinition[] } +export type GraphQLInterfaceObject = { + name: string + type: GraphQLTypeDefinition + fields: any // TODO + types: GraphQLTypeDefinition[] +} + interface FinalType { isRequired: boolean isArray: boolean @@ -181,7 +191,7 @@ function extractTypeLike( function extractTypeFieldsFromObjectType( schema: GraphQLSchema, - node: GraphQLObjectType, + node: GraphQLObjectType | GraphQLInterfaceType, ) { const fields: GraphQLTypeField[] = [] Object.values(node.getFields()).forEach( @@ -219,7 +229,7 @@ function extractTypeFieldsFromInputType( return fields } -function extractGraphQLTypes(schema: GraphQLSchema) { +function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { const types: GraphQLTypeObject[] = [] Object.values(schema.getTypeMap()).forEach((node: GraphQLNamedType) => { // Ignore meta types like __Schema and __TypeKind @@ -253,6 +263,9 @@ function extractGraphQLTypes(schema: GraphQLSchema) { isInterface: false, }, fields: extractTypeFieldsFromObjectType(schema, node), + interfaces: node + .getInterfaces() + .map(interfaceType => interfaceType.name), }) } else if (node instanceof GraphQLInputObjectType) { types.push({ @@ -325,13 +338,40 @@ function extractGraphQLUnions(schema: GraphQLSchema) { return types } +function extractGraphQLInterfaces(schema: GraphQLSchema) { + const types: GraphQLInterfaceObject[] = [] + Object.values(schema.getTypeMap()).forEach((node: GraphQLNamedType) => { + if (node instanceof GraphQLInterfaceType) { + const allTypes = extractGraphQLTypes(schema) + types.push({ + name: node.name, + type: { + name: node.name, + isObject: false, + isInput: false, + isEnum: false, + isUnion: false, + isScalar: false, + isInterface: true, + }, + types: allTypes + .filter(type => !!type.interfaces) + .filter(type => type.interfaces!.includes(node.name)) + .map(type => type.type), + fields: extractTypeFieldsFromObjectType(schema, node), + }) + } + }) + return types +} + const graphqlToTypescriptFlow: { [key: string]: string } = { String: 'string', Boolean: 'boolean', ID: 'string', Int: 'number', Float: 'number', - DateTime: 'string' + DateTime: 'string', } export function graphQLToTypecriptFlowType(type: GraphQLType): string { diff --git a/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql b/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql new file mode 100644 index 00000000..a7a251d6 --- /dev/null +++ b/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql @@ -0,0 +1,25 @@ +type Query { + media(first: Int): [Media] +} + +interface Media { + id: ID! + url: String! +} + +type Dimensions { + width: Int! + height: Int! +} + +type Image implements Media { + id: ID! + url: String! + dimensions: Dimensions! +} + +type Video implements Media { + id: ID! + url: String! + duration: Int! +} diff --git a/packages/graphqlgen/src/tests/fixtures/interface/types.ts b/packages/graphqlgen/src/tests/fixtures/interface/types.ts new file mode 100644 index 00000000..3bd11d5c --- /dev/null +++ b/packages/graphqlgen/src/tests/fixtures/interface/types.ts @@ -0,0 +1,19 @@ +// export interface Context {} + +export interface Media { + id: string + url: string +} + +export interface Dimensions { + width: number + height: number +} + +export interface Image extends Media { + dimensions: Dimensions +} + +export interface Video extends Media { + dimensions: number +} diff --git a/packages/graphqlgen/src/tests/fixtures/union/schema.graphql b/packages/graphqlgen/src/tests/fixtures/union/schema.graphql index 5a675c5f..be7f67b4 100644 --- a/packages/graphqlgen/src/tests/fixtures/union/schema.graphql +++ b/packages/graphqlgen/src/tests/fixtures/union/schema.graphql @@ -1,7 +1,10 @@ +type Query { + users(first: Int): [UserType!]! +} + type User { id: ID! name: String! - type: UserType! } union UserType = Student | Professor diff --git a/packages/graphqlgen/src/tests/flow/__snapshots__/basic.test.ts.snap b/packages/graphqlgen/src/tests/flow/__snapshots__/basic.test.ts.snap index 124e009d..0147593d 100644 --- a/packages/graphqlgen/src/tests/flow/__snapshots__/basic.test.ts.snap +++ b/packages/graphqlgen/src/tests/flow/__snapshots__/basic.test.ts.snap @@ -615,60 +615,33 @@ exports[`basic union 1`] = ` import type { GraphQLResolveInfo } from \\"graphql\\"; import type { - User, Student, - Professor + Professor, + User } from \\"../../../fixtures/union/flow-types\\"; type Context = any; -// Types for User -export const User_defaultResolvers = { - id: (parent: User) => parent.id, - name: (parent: User) => parent.name -}; - -export type User_Id_Resolver = ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo -) => string | Promise; +// Types for Query +export const Query_defaultResolvers = {}; -export type User_Name_Resolver = ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo -) => string | Promise; +export interface Query_Args_Users { + first: number | null; +} -export type User_Type_Resolver = ( - parent: User, - args: {}, +export type Query_Users_Resolver = ( + parent: {}, + args: Query_Args_Users, ctx: Context, info: GraphQLResolveInfo -) => {} | Promise<{}>; - -export interface User_Resolvers { - id: ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; - - name: ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; +) => Array | Promise>; - type: ( - parent: User, - args: {}, +export interface Query_Resolvers { + users: ( + parent: {}, + args: Query_Args_Users, ctx: Context, info: GraphQLResolveInfo - ) => {} | Promise<{}>; + ) => Array | Promise>; } // Types for Student @@ -713,10 +686,48 @@ export interface Professor_Resolvers { ) => string | null | Promise; } +// Types for User +export const User_defaultResolvers = { + id: (parent: User) => parent.id, + name: (parent: User) => parent.name +}; + +export type User_Id_Resolver = ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo +) => string | Promise; + +export type User_Name_Resolver = ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo +) => string | Promise; + +export interface User_Resolvers { + id: ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + name: ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; +} + export interface Resolvers { - User: User_Resolvers; + Query: Query_Resolvers; Student: Student_Resolvers; Professor: Professor_Resolvers; + User: User_Resolvers; + UserType?: UserType_Resolvers; } " `; @@ -725,22 +736,6 @@ exports[`basic union 2`] = ` Array [ Object { "code": "/* @flow */ -import { User_defaultResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; -import type { User_Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; - -export const User: User_Resolvers = { - ...User_defaultResolvers, - - type: (parent, args, ctx, info) => { - throw new Error(\\"Resolver not implemented\\"); - } -}; -", - "force": false, - "path": "User.js", - }, - Object { - "code": "/* @flow */ import { Student_defaultResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; import type { Student_Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; @@ -763,6 +758,47 @@ export const Professor: Professor_Resolvers = { "force": false, "path": "Professor.js", }, + Object { + "code": "/* @flow */ +import { User_defaultResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; +import type { User_Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const User: User_Resolvers = { + ...User_defaultResolvers +}; +", + "force": false, + "path": "User.js", + }, + Object { + "code": "// @flow +// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { UserType_Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const UserType: UserType_Resolvers = { + __resolveType: (parent, ctx, info) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "UserType.ts", + }, + Object { + "code": "/* @flow */ +import type { Query_Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Query: Query_Resolvers = { + users: (parent, args, ctx, info) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "Query.js", + }, Object { "code": "// @flow // This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. @@ -770,14 +806,16 @@ export const Professor: Professor_Resolvers = { import type { Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; -import { User } from \\"./User\\"; +import { Query } from \\"./Query\\"; import { Student } from \\"./Student\\"; import { Professor } from \\"./Professor\\"; +import { User } from \\"./User\\"; export const resolvers: Resolvers = { - User, + Query, Student, - Professor + Professor, + User }; ", "force": false, diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap index 58d267cd..a5478192 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap @@ -3,7 +3,11 @@ exports[`basic enum 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { User } from \\"../../../fixtures/enum/types\\"; type Context = any; @@ -166,7 +170,11 @@ export const resolvers: Resolvers = { exports[`basic input 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { AddMemberPayload } from \\"../../../fixtures/input/types\\"; type Context = any; @@ -333,10 +341,317 @@ export const resolvers: Resolvers = { ] `; +exports[`basic interface 1`] = ` +"// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. + +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; +import { Dimensions, Image, Video } from \\"../../../fixtures/interface/types\\"; +type Context = any; + +export namespace QueryResolvers { + export const defaultResolvers = {}; + + export interface ArgsMedia { + first: number | null; + } + + export type MediaResolver = ( + parent: undefined, + args: ArgsMedia, + ctx: Context, + info: GraphQLResolveInfo + ) => Array | null | Promise | null>; + + export interface Type { + media: ( + parent: undefined, + args: ArgsMedia, + ctx: Context, + info: GraphQLResolveInfo + ) => Array | null | Promise | null>; + } +} + +export namespace DimensionsResolvers { + export const defaultResolvers = { + width: (parent: Dimensions) => parent.width, + height: (parent: Dimensions) => parent.height + }; + + export type WidthResolver = ( + parent: Dimensions, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + + export type HeightResolver = ( + parent: Dimensions, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + + export interface Type { + width: ( + parent: Dimensions, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + + height: ( + parent: Dimensions, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + } +} + +export namespace ImageResolvers { + export const defaultResolvers = { + dimensions: (parent: Image) => parent.dimensions + }; + + export type IdResolver = ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export type UrlResolver = ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export type DimensionsResolver = ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => Dimensions | Promise; + + export interface Type { + id: ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + url: ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + dimensions: ( + parent: Image, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => Dimensions | Promise; + + __isTypeOf?: GraphQLIsTypeOfFn; + } +} + +export namespace VideoResolvers { + export const defaultResolvers = {}; + + export type IdResolver = ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export type UrlResolver = ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export type DurationResolver = ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + + export interface Type { + id: ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + url: ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + duration: ( + parent: Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => number | Promise; + + __isTypeOf?: GraphQLIsTypeOfFn; + } +} + +export namespace MediaResolvers { + export interface Type { + __resolveType: GraphQLTypeResolver; + } +} + +export interface Resolvers { + Query: QueryResolvers.Type; + Dimensions: DimensionsResolvers.Type; + Image: ImageResolvers.Type; + Video: VideoResolvers.Type; + Media?: MediaResolvers.Type; +} +" +`; + +exports[`basic interface 2`] = ` +Array [ + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { DimensionsResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Dimensions: DimensionsResolvers.Type = { + ...DimensionsResolvers.defaultResolvers +}; +", + "force": false, + "path": "Dimensions.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { ImageResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Image: ImageResolvers.Type = { + ...ImageResolvers.defaultResolvers, + + id: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + }, + url: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "Image.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { VideoResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Video: VideoResolvers.Type = { + ...VideoResolvers.defaultResolvers, + + id: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + }, + url: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + }, + duration: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "Video.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { MediaResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Media: MediaResolvers.Type = { + __resolveType: (parent, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "Media.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { QueryResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Query: QueryResolvers.Type = { + ...QueryResolvers.defaultResolvers, + media: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } +}; +", + "force": false, + "path": "Query.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +import { Query } from \\"./Query\\"; +import { Dimensions } from \\"./Dimensions\\"; +import { Image } from \\"./Image\\"; +import { Video } from \\"./Video\\"; + +export const resolvers: Resolvers = { + Query, + Dimensions, + Image, + Video +}; +", + "force": false, + "path": "index.ts", + }, +] +`; + exports[`basic scalar 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { AddMemberPayload } from \\"../../../fixtures/scalar/types\\"; type Context = any; @@ -452,7 +767,11 @@ export const resolvers: Resolvers = { exports[`basic schema 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { Number } from \\"../../../fixtures/basic\\"; type Context = any; @@ -794,58 +1113,35 @@ export const resolvers: Resolvers = { exports[`basic union 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; -import { User, Student, Professor } from \\"../../../fixtures/union/types\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; +import { Student, Professor, User } from \\"../../../fixtures/union/types\\"; type Context = any; -export namespace UserResolvers { - export const defaultResolvers = { - id: (parent: User) => parent.id, - name: (parent: User) => parent.name - }; - - export type IdResolver = ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; +export namespace QueryResolvers { + export const defaultResolvers = {}; - export type NameResolver = ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; + export interface ArgsUsers { + first: number | null; + } - export type TypeResolver = ( - parent: User, - args: {}, + export type UsersResolver = ( + parent: undefined, + args: ArgsUsers, ctx: Context, info: GraphQLResolveInfo - ) => {} | Promise<{}>; + ) => Array | Promise>; export interface Type { - id: ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; - - name: ( - parent: User, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; - - type: ( - parent: User, - args: {}, + users: ( + parent: undefined, + args: ArgsUsers, ctx: Context, info: GraphQLResolveInfo - ) => {} | Promise<{}>; + ) => Array | Promise>; } } @@ -868,6 +1164,8 @@ export namespace StudentResolvers { ctx: Context, info: GraphQLResolveInfo ) => number | Promise; + + __isTypeOf?: GraphQLIsTypeOfFn; } } @@ -890,13 +1188,60 @@ export namespace ProfessorResolvers { ctx: Context, info: GraphQLResolveInfo ) => string | null | Promise; + + __isTypeOf?: GraphQLIsTypeOfFn; + } +} + +export namespace UserResolvers { + export const defaultResolvers = { + id: (parent: User) => parent.id, + name: (parent: User) => parent.name + }; + + export type IdResolver = ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export type NameResolver = ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + export interface Type { + id: ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + name: ( + parent: User, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + } +} + +export namespace UserTypeResolvers { + export interface Type { + __resolveType?: GraphQLTypeResolver; } } export interface Resolvers { - User: UserResolvers.Type; + Query: QueryResolvers.Type; Student: StudentResolvers.Type; Professor: ProfessorResolvers.Type; + User: UserResolvers.Type; + UserType?: UserTypeResolvers.Type; } " `; @@ -907,14 +1252,36 @@ Array [ "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. // Please do not import this file directly but copy & paste to your application code. +import { StudentResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Student: StudentResolvers.Type = { + ...StudentResolvers.defaultResolvers +}; +", + "force": false, + "path": "Student.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + +import { ProfessorResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; + +export const Professor: ProfessorResolvers.Type = { + ...ProfessorResolvers.defaultResolvers +}; +", + "force": false, + "path": "Professor.ts", + }, + Object { + "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. +// Please do not import this file directly but copy & paste to your application code. + import { UserResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; export const User: UserResolvers.Type = { - ...UserResolvers.defaultResolvers, - - type: (parent, args, ctx) => { - throw new Error(\\"Resolver not implemented\\"); - } + ...UserResolvers.defaultResolvers }; ", "force": false, @@ -924,27 +1291,32 @@ export const User: UserResolvers.Type = { "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. // Please do not import this file directly but copy & paste to your application code. -import { StudentResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; +import { UserTypeResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; -export const Student: StudentResolvers.Type = { - ...StudentResolvers.defaultResolvers +export const UserType: UserTypeResolvers.Type = { + __resolveType: (parent, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } }; ", "force": false, - "path": "Student.ts", + "path": "UserType.ts", }, Object { "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. // Please do not import this file directly but copy & paste to your application code. -import { ProfessorResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; +import { QueryResolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; -export const Professor: ProfessorResolvers.Type = { - ...ProfessorResolvers.defaultResolvers +export const Query: QueryResolvers.Type = { + ...QueryResolvers.defaultResolvers, + users: (parent, args, ctx) => { + throw new Error(\\"Resolver not implemented\\"); + } }; ", "force": false, - "path": "Professor.ts", + "path": "Query.ts", }, Object { "code": "// This resolver file was scaffolded by github.com/prisma/graphqlgen, DO NOT EDIT. @@ -952,14 +1324,16 @@ export const Professor: ProfessorResolvers.Type = { import { Resolvers } from \\"[TEMPLATE-INTERFACES-PATH]\\"; -import { User } from \\"./User\\"; +import { Query } from \\"./Query\\"; import { Student } from \\"./Student\\"; import { Professor } from \\"./Professor\\"; +import { User } from \\"./User\\"; export const resolvers: Resolvers = { - User, + Query, Student, - Professor + Professor, + User }; ", "force": false, @@ -971,7 +1345,11 @@ export const resolvers: Resolvers = { exports[`context 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { User } from \\"../../../fixtures/context/types\\"; import { Context } from \\"../../../fixtures/context/types\\"; @@ -1078,7 +1456,11 @@ export const resolvers: Resolvers = { exports[`defaultName 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { NumberNode } from \\"../../../fixtures/defaultName\\"; type Context = any; @@ -1420,7 +1802,11 @@ export const resolvers: Resolvers = { exports[`override model 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { Number } from \\"../../../fixtures/basic\\"; type Context = any; @@ -1762,7 +2148,11 @@ export const resolvers: Resolvers = { exports[`subscription 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { User } from \\"../../../fixtures/subscription/types\\"; type Context = any; diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap index a217cfa8..725a4d21 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap @@ -3,7 +3,11 @@ exports[`large schema tests large schema 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo } from \\"graphql\\"; +import { + GraphQLResolveInfo, + GraphQLTypeResolver, + GraphQLIsTypeOfFn +} from \\"graphql\\"; import { Experience, ExperienceCategory, diff --git a/packages/graphqlgen/src/tests/typescript/basic.test.ts b/packages/graphqlgen/src/tests/typescript/basic.test.ts index 0b5eb31e..b3bbd2c7 100644 --- a/packages/graphqlgen/src/tests/typescript/basic.test.ts +++ b/packages/graphqlgen/src/tests/typescript/basic.test.ts @@ -146,3 +146,18 @@ test('override model', async () => { }, }) }) + +test('basic interface', async () => { + testGeneration({ + language, + schema: relative('../fixtures/interface/schema.graphql'), + models: { + files: [relative('../fixtures/interface/types.ts')], + }, + output: relative('./generated/interface/graphqlgen.ts'), + ['resolver-scaffolding']: { + output: relative('./tmp/interface/'), + layout: 'file-per-type', + }, + }) +}) diff --git a/packages/graphqlgen/src/types.ts b/packages/graphqlgen/src/types.ts index 2168a907..97863af2 100644 --- a/packages/graphqlgen/src/types.ts +++ b/packages/graphqlgen/src/types.ts @@ -3,10 +3,12 @@ import { GraphQLTypeObject, GraphQLEnumObject, GraphQLUnionObject, + GraphQLInterfaceObject, } from './source-helper' import { TypeDefinition } from './introspection/types' export interface GenerateArgs { + interfaces: GraphQLInterfaceObject[] types: GraphQLTypeObject[] enums: GraphQLEnumObject[] unions: GraphQLUnionObject[] From 64b654702ee87e9dba9f4a6e02fce6951bd37821 Mon Sep 17 00:00:00 2001 From: Koen Punt Date: Sun, 11 Nov 2018 18:49:00 +0100 Subject: [PATCH 02/19] generate interfaces for fields of graphql interfaces --- .../graphqlgen/src/generators/ts-generator.ts | 52 +++++++++++++++++-- .../__snapshots__/basic.test.ts.snap | 20 ++++++- 2 files changed, 65 insertions(+), 7 deletions(-) diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index b4eefadd..60fd58c2 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -164,7 +164,7 @@ function renderNamespaces( inputTypesMap, )} - ${renderInterfaceNamespaces(args)} + ${renderInterfaceNamespaces(args, interfacesMap, unionsMap)} ${renderUnionNamespaces(args)} ` @@ -192,9 +192,13 @@ function renderObjectNamespaces( .join(os.EOL) } -function renderInterfaceNamespaces(args: GenerateArgs): string { +function renderInterfaceNamespaces( + args: GenerateArgs, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, +): string { return args.interfaces - .map(type => renderInterfaceNamespace(type, args)) + .map(type => renderInterfaceNamespace(type, interfacesMap, unionsMap, args)) .join(os.EOL) } @@ -204,10 +208,28 @@ function renderUnionNamespaces(args: GenerateArgs): string { function renderInterfaceNamespace( graphQLTypeObject: GraphQLInterfaceObject, + interfacesMap: InterfacesMap, + unionsMap: UnionsMap, args: GenerateArgs, ): string { return `\ export namespace ${graphQLTypeObject.name}Resolvers { + ${renderInputArgInterfaces( + graphQLTypeObject, + args.modelMap, + interfacesMap, + unionsMap, + )} + + ${renderResolverTypeInterface( + graphQLTypeObject, + args.modelMap, + interfacesMap, + unionsMap, + args.context, + 'InterfaceType', + )} + export interface Type { __resolveType: GraphQLTypeResolver<${graphQLTypeObject.types .map(interfaceType => getModelName(interfaceType, args.modelMap)) @@ -451,9 +473,17 @@ function renderResolverTypeInterface( interfacesMap: InterfacesMap, unionsMap: UnionsMap, context?: ContextDefinition, + interfaceName: string = 'Type', ): string { + const extend = + type.interfaces && type.interfaces.length + ? `extends ${type.interfaces + .map(typeInterface => `${typeInterface}Resolvers.InterfaceType`) + .join(',')}` + : '' + return ` - export interface Type { + export interface ${interfaceName} ${extend} { ${type.fields .map(field => renderResolverTypeInterfaceFunction( @@ -485,9 +515,21 @@ function renderResolverTypeInterfaceFunction( unionsMap: UnionsMap, context?: ContextDefinition, ): string { + let parent: string + + if (type.type.isInterface) { + const implementingTypes = interfacesMap[type.name] + + parent = implementingTypes + .map(implType => getModelName(implType, modelMap, 'undefined')) + .join(' | ') + } else { + parent = getModelName(type.type as any, modelMap, 'undefined') + } + const resolverDefinition = ` ( - parent: ${getModelName(type.type as any, modelMap, 'undefined')}, + parent: ${parent}, args: ${ field.arguments.length > 0 ? `Args${upperFirst(field.name)}` : '{}' }, diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap index a5478192..082cc674 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap @@ -439,7 +439,7 @@ export namespace ImageResolvers { info: GraphQLResolveInfo ) => Dimensions | Promise; - export interface Type { + export interface Type extends MediaResolvers.InterfaceType { id: ( parent: Image, args: {}, @@ -489,7 +489,7 @@ export namespace VideoResolvers { info: GraphQLResolveInfo ) => number | Promise; - export interface Type { + export interface Type extends MediaResolvers.InterfaceType { id: ( parent: Video, args: {}, @@ -516,6 +516,22 @@ export namespace VideoResolvers { } export namespace MediaResolvers { + export interface InterfaceType { + id: ( + parent: Image | Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + + url: ( + parent: Image | Video, + args: {}, + ctx: Context, + info: GraphQLResolveInfo + ) => string | Promise; + } + export interface Type { __resolveType: GraphQLTypeResolver; } From c2d464a20bff7432d8432baee0701083d5ac47d8 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Thu, 17 Jan 2019 20:45:51 -0500 Subject: [PATCH 03/19] refactor confusing idents --- packages/graphqlgen/src/generators/common.ts | 8 ++---- .../src/generators/flow-generator.ts | 25 +++++++++---------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index 6b504172..ee2b3b1f 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -28,13 +28,9 @@ export interface TypeToInputTypeAssociation { [objectTypeName: string]: string[] } -export interface InterfacesMap { - [interfaceName: string]: GraphQLTypeDefinition[] -} +export type InterfacesMap = Record -export interface UnionsMap { - [unionName: string]: GraphQLTypeDefinition[] -} +export type UnionsMap = Record export function fieldsFromModelDefinition( modelDef: TypeDefinition, diff --git a/packages/graphqlgen/src/generators/flow-generator.ts b/packages/graphqlgen/src/generators/flow-generator.ts index 47bf37b5..163a9778 100644 --- a/packages/graphqlgen/src/generators/flow-generator.ts +++ b/packages/graphqlgen/src/generators/flow-generator.ts @@ -71,22 +71,21 @@ export function generate(args: GenerateArgs): string { } }, {}) - const interfacesMap: InterfacesMap = args.interfaces.reduce( - (interfaces, int) => { - return { - ...interfaces, - [int.name]: int.types, - } + const interfacesMap = args.interfaces.reduce( + (interfaces, inter) => { + interfaces[inter.name] = inter.types + return interfaces }, - {}, + {} as InterfacesMap, ) - const unionsMap: InterfacesMap = args.unions.reduce((interfaces, int) => { - return { - ...interfaces, - [int.name]: int.types, - } - }, {}) + const unionsMap = args.unions.reduce( + (unions, union) => { + unions[union.name] = union.types + return unions + }, + {} as UnionsMap, + ) return `\ ${renderHeader(args)} From b59896368a482764e02559673ec004fb7e16587f Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 20:24:35 -0500 Subject: [PATCH 04/19] refactor extractGraphQLInterfaces --- .../src/generators/flow-generator.ts | 2 +- .../graphqlgen/src/generators/ts-generator.ts | 4 +- packages/graphqlgen/src/source-helper.ts | 61 +++++++++++-------- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/packages/graphqlgen/src/generators/flow-generator.ts b/packages/graphqlgen/src/generators/flow-generator.ts index 163a9778..5a3fa984 100644 --- a/packages/graphqlgen/src/generators/flow-generator.ts +++ b/packages/graphqlgen/src/generators/flow-generator.ts @@ -73,7 +73,7 @@ export function generate(args: GenerateArgs): string { const interfacesMap = args.interfaces.reduce( (interfaces, inter) => { - interfaces[inter.name] = inter.types + interfaces[inter.name] = inter.implementors return interfaces }, {} as InterfacesMap, diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index b74c3539..a6555c57 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -78,7 +78,7 @@ export function generate(args: GenerateArgs): string { (interfaces, int) => { return { ...interfaces, - [int.name]: int.types, + [int.name]: int.implementors, } }, {}, @@ -245,7 +245,7 @@ function renderInterfaceNamespace( )} export interface Type { - __resolveType: GraphQLTypeResolver<${graphQLTypeObject.types + __resolveType: GraphQLTypeResolver<${graphQLTypeObject.implementors .map(interfaceType => getModelName(interfaceType, args.modelMap)) .join(' | ')}, ${getContextName(args.context)}>; } diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 1983c782..fb90c787 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -88,7 +88,7 @@ export type GraphQLInterfaceObject = { name: string type: GraphQLTypeDefinition fields: any // TODO - types: GraphQLTypeDefinition[] + implementors: GraphQLTypeDefinition[] } interface FinalType { @@ -359,31 +359,42 @@ function extractGraphQLUnions(schema: GraphQLSchema) { return types } -function extractGraphQLInterfaces(schema: GraphQLSchema) { - const types: GraphQLInterfaceObject[] = [] - Object.values(schema.getTypeMap()).forEach((node: GraphQLNamedType) => { - if (node instanceof GraphQLInterfaceType) { - const allTypes = extractGraphQLTypes(schema) - types.push({ - name: node.name, - type: { - name: node.name, - isObject: false, - isInput: false, - isEnum: false, - isUnion: false, - isScalar: false, - isInterface: true, - }, - types: allTypes - .filter(type => !!type.interfaces) +function extractGraphQLInterfaces( + schema: GraphQLSchema, +): GraphQLInterfaceObject[] { + const typesUsingInterfaces = extractGraphQLTypes(schema).filter( + type => type.interfaces !== undefined, + ) + + return Object.values(schema.getTypeMap()) + .filter(node => node instanceof GraphQLInterfaceType) + .reduce( + (interfaces, node) => { + node = node as GraphQLInterfaceType + + const implementorTypes = typesUsingInterfaces .filter(type => type.interfaces!.includes(node.name)) - .map(type => type.type), - fields: extractTypeFieldsFromObjectType(schema, node), - }) - } - }) - return types + .map(type => type.type) + + interfaces.push({ + name: node.name, + type: { + name: node.name, + isObject: false, + isInput: false, + isEnum: false, + isUnion: false, + isScalar: false, + isInterface: true, + }, + implementors: implementorTypes, + fields: extractTypeFieldsFromObjectType(schema, node), + }) + + return interfaces + }, + [] as GraphQLInterfaceObject[], + ) } const graphqlToTypescriptFlow: { [key: string]: string } = { From 4843b2e3ab39602a0c3158f56340932567726cc2 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 20:36:41 -0500 Subject: [PATCH 05/19] optimize extractGraphQLInterfaces --- packages/graphqlgen/src/source-helper.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index fb90c787..0eca5520 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -32,7 +32,7 @@ export function extractTypes(typeDefs: string): GraphQLTypes { const types = extractGraphQLTypes(schema) const unions = extractGraphQLUnions(schema) const enums = extractGraphQLEnums(schema) - const interfaces = extractGraphQLInterfaces(schema) + const interfaces = extractGraphQLInterfaces(schema, types) return { types, enums, unions, interfaces } } @@ -361,8 +361,9 @@ function extractGraphQLUnions(schema: GraphQLSchema) { function extractGraphQLInterfaces( schema: GraphQLSchema, + types: GraphQLTypeObject[], ): GraphQLInterfaceObject[] { - const typesUsingInterfaces = extractGraphQLTypes(schema).filter( + const typesUsingInterfaces = types.filter( type => type.interfaces !== undefined, ) From 73c7fa0a63d697c1a2f003b627e6bca03935a70e Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 20:40:45 -0500 Subject: [PATCH 06/19] refactor: explicit null --- packages/graphqlgen/src/source-helper.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 0eca5520..356a6a6e 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -69,7 +69,7 @@ export type GraphQLTypeObject = { name: string type: GraphQLTypeDefinition fields: GraphQLTypeField[] - interfaces?: string[] + interfaces: null | string[] } export type GraphQLEnumObject = { @@ -270,6 +270,7 @@ function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { isInterface: false, }, fields: [], // extractTypeFields(schema, node), + interfaces: null, }) } else if (node instanceof GraphQLObjectType) { types.push({ @@ -301,6 +302,7 @@ function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { isInterface: false, }, fields: extractTypeFieldsFromInputType(schema, node), + interfaces: null, }) } }) @@ -363,9 +365,7 @@ function extractGraphQLInterfaces( schema: GraphQLSchema, types: GraphQLTypeObject[], ): GraphQLInterfaceObject[] { - const typesUsingInterfaces = types.filter( - type => type.interfaces !== undefined, - ) + const interfaceUsingTypes = types.filter(type => type.interfaces !== null) return Object.values(schema.getTypeMap()) .filter(node => node instanceof GraphQLInterfaceType) @@ -373,7 +373,7 @@ function extractGraphQLInterfaces( (interfaces, node) => { node = node as GraphQLInterfaceType - const implementorTypes = typesUsingInterfaces + const implementorTypes = interfaceUsingTypes .filter(type => type.interfaces!.includes(node.name)) .map(type => type.type) From 18bc848e3f377ebaec82d35b47ad072292ca59db Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 20:49:49 -0500 Subject: [PATCH 07/19] chore: check types script --- packages/graphqlgen/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/graphqlgen/package.json b/packages/graphqlgen/package.json index 1c454123..1a122f99 100644 --- a/packages/graphqlgen/package.json +++ b/packages/graphqlgen/package.json @@ -20,6 +20,7 @@ "watch": "tsc -w", "lint": "tslint --project tsconfig.json {src,test}/**/*.ts", "test": "jest", + "check:types": "yarn tsc --noEmit", "test:watch": "jest --watch", "test:ci": "npm run lint && jest --maxWorkers 4", "gen": "ts-node --files src/index.ts" From f1e36c6b8df139c86b51867a0b9cb5143eb56184 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 22:04:02 -0500 Subject: [PATCH 08/19] fix type errors --- .../graphqlgen/src/generators/common.spec.ts | 5 ++ .../graphqlgen/src/generators/ts-generator.ts | 46 ++++++++++++------- packages/graphqlgen/src/source-helper.ts | 2 +- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.spec.ts b/packages/graphqlgen/src/generators/common.spec.ts index 7935b95e..c98a8a71 100644 --- a/packages/graphqlgen/src/generators/common.spec.ts +++ b/packages/graphqlgen/src/generators/common.spec.ts @@ -13,11 +13,13 @@ it('getDistinctInputTypes', () => { isUnion: false, }, fields: [], + interfaces: null, } const typeMap: Common.InputTypesMap = { A: { name: 'A', + interfaces: null, type: { name: 'A', isInput: true, @@ -64,6 +66,7 @@ it('getDistinctInputTypes', () => { }, B: { name: 'B', + interfaces: null, type: { name: 'B', isInput: true, @@ -94,6 +97,7 @@ it('getDistinctInputTypes', () => { }, C: { name: 'C', + interfaces: null, type: { name: 'C', isInput: true, @@ -124,6 +128,7 @@ it('getDistinctInputTypes', () => { }, D: { name: 'D', + interfaces: null, type: { name: 'D', isInput: true, diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index a6555c57..e8bba253 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -319,7 +319,7 @@ function renderNamespace( } function renderIsTypeOfFunctionInterface( - type: GraphQLTypeObject, + type: GraphQLTypeObject | GraphQLInterfaceObject, modelMap: ModelMap, interfacesMap: InterfacesMap, unionsMap: UnionsMap, @@ -327,13 +327,17 @@ function renderIsTypeOfFunctionInterface( ) { let possibleTypes: GraphQLTypeDefinition[] = [] - if (type.interfaces) { - possibleTypes = type.interfaces.reduce( - (obj: GraphQLTypeDefinition[], interfaceName) => { - return [...obj, ...interfacesMap[interfaceName]] - }, - [], - ) + // TODO Refactor once type is a proper discriminated union + if (type.type.isInterface) { + type = type as GraphQLTypeObject + if (type.interfaces) { + possibleTypes = type.interfaces.reduce( + (obj: GraphQLTypeDefinition[], interfaceName) => { + return [...obj, ...interfacesMap[interfaceName]] + }, + [], + ) + } } for (let unionName in unionsMap) { @@ -375,7 +379,7 @@ function renderInputTypeInterfaces( } function renderInputArgInterfaces( - type: GraphQLTypeObject, + type: GraphQLTypeObject | GraphQLInterfaceObject, modelMap: ModelMap, interfacesMap: InterfacesMap, unionsMap: UnionsMap, @@ -477,19 +481,27 @@ function renderResolverFunctionInterface( } function renderResolverTypeInterface( - type: GraphQLTypeObject, + type: GraphQLTypeObject | GraphQLInterfaceObject, modelMap: ModelMap, interfacesMap: InterfacesMap, unionsMap: UnionsMap, context?: ContextDefinition, interfaceName: string = 'Type', ): string { - const extend = - type.interfaces && type.interfaces.length - ? `extends ${type.interfaces - .map(typeInterface => `${typeInterface}Resolvers.InterfaceType`) - .join(',')}` - : '' + // TODO: Refactor this with proper union type branching once the + // type modelling is refactored itself to support that. + let extend = '' + + if (!type.type.isInterface) { + type = type as GraphQLTypeObject + + extend = + type.interfaces && type.interfaces.length + ? `extends ${type.interfaces + .map(typeInterface => `${typeInterface}Resolvers.InterfaceType`) + .join(',')}` + : '' + } return ` export interface ${interfaceName} ${extend} { @@ -518,7 +530,7 @@ function renderResolverTypeInterface( function renderResolverTypeInterfaceFunction( field: GraphQLTypeField, - type: GraphQLTypeObject, + type: GraphQLTypeObject | GraphQLInterfaceObject, modelMap: ModelMap, interfacesMap: InterfacesMap, unionsMap: UnionsMap, diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 356a6a6e..66b5b97e 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -87,7 +87,7 @@ export type GraphQLUnionObject = { export type GraphQLInterfaceObject = { name: string type: GraphQLTypeDefinition - fields: any // TODO + fields: GraphQLTypeField[] implementors: GraphQLTypeDefinition[] } From 77ea2588f143dc0e6772d9e06f7af46c28e5a689 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 22:07:43 -0500 Subject: [PATCH 09/19] refactor: rename field to carry semantic meaning --- packages/graphqlgen/src/generators/common.spec.ts | 13 +++++++------ packages/graphqlgen/src/generators/ts-generator.ts | 8 ++++---- packages/graphqlgen/src/source-helper.ts | 12 ++++++------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.spec.ts b/packages/graphqlgen/src/generators/common.spec.ts index c98a8a71..3c95a7a0 100644 --- a/packages/graphqlgen/src/generators/common.spec.ts +++ b/packages/graphqlgen/src/generators/common.spec.ts @@ -1,7 +1,8 @@ +import * as Source from '../source-helper' import * as Common from './common' it('getDistinctInputTypes', () => { - const Z = { + const Z: Source.GraphQLTypeObject = { name: 'Z', type: { name: 'Z', @@ -13,13 +14,13 @@ it('getDistinctInputTypes', () => { isUnion: false, }, fields: [], - interfaces: null, + implements: null, } const typeMap: Common.InputTypesMap = { A: { name: 'A', - interfaces: null, + implements: null, type: { name: 'A', isInput: true, @@ -66,7 +67,7 @@ it('getDistinctInputTypes', () => { }, B: { name: 'B', - interfaces: null, + implements: null, type: { name: 'B', isInput: true, @@ -97,7 +98,7 @@ it('getDistinctInputTypes', () => { }, C: { name: 'C', - interfaces: null, + implements: null, type: { name: 'C', isInput: true, @@ -128,7 +129,7 @@ it('getDistinctInputTypes', () => { }, D: { name: 'D', - interfaces: null, + implements: null, type: { name: 'D', isInput: true, diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index e8bba253..d46252ab 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -330,8 +330,8 @@ function renderIsTypeOfFunctionInterface( // TODO Refactor once type is a proper discriminated union if (type.type.isInterface) { type = type as GraphQLTypeObject - if (type.interfaces) { - possibleTypes = type.interfaces.reduce( + if (type.implements) { + possibleTypes = type.implements.reduce( (obj: GraphQLTypeDefinition[], interfaceName) => { return [...obj, ...interfacesMap[interfaceName]] }, @@ -496,8 +496,8 @@ function renderResolverTypeInterface( type = type as GraphQLTypeObject extend = - type.interfaces && type.interfaces.length - ? `extends ${type.interfaces + type.implements && type.implements.length + ? `extends ${type.implements .map(typeInterface => `${typeInterface}Resolvers.InterfaceType`) .join(',')}` : '' diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 66b5b97e..9c4397be 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -69,7 +69,7 @@ export type GraphQLTypeObject = { name: string type: GraphQLTypeDefinition fields: GraphQLTypeField[] - interfaces: null | string[] + implements: null | string[] } export type GraphQLEnumObject = { @@ -270,7 +270,7 @@ function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { isInterface: false, }, fields: [], // extractTypeFields(schema, node), - interfaces: null, + implements: null, }) } else if (node instanceof GraphQLObjectType) { types.push({ @@ -285,7 +285,7 @@ function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { isInterface: false, }, fields: extractTypeFieldsFromObjectType(schema, node), - interfaces: node + implements: node .getInterfaces() .map(interfaceType => interfaceType.name), }) @@ -302,7 +302,7 @@ function extractGraphQLTypes(schema: GraphQLSchema): GraphQLTypeObject[] { isInterface: false, }, fields: extractTypeFieldsFromInputType(schema, node), - interfaces: null, + implements: null, }) } }) @@ -365,7 +365,7 @@ function extractGraphQLInterfaces( schema: GraphQLSchema, types: GraphQLTypeObject[], ): GraphQLInterfaceObject[] { - const interfaceUsingTypes = types.filter(type => type.interfaces !== null) + const interfaceUsingTypes = types.filter(type => type.implements !== null) return Object.values(schema.getTypeMap()) .filter(node => node instanceof GraphQLInterfaceType) @@ -374,7 +374,7 @@ function extractGraphQLInterfaces( node = node as GraphQLInterfaceType const implementorTypes = interfaceUsingTypes - .filter(type => type.interfaces!.includes(node.name)) + .filter(type => type.implements!.includes(node.name)) .map(type => type.type) interfaces.push({ From 6c4120bb379cf43c8597ef9f1f9b1bfbbca87ccb Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 22:24:08 -0500 Subject: [PATCH 10/19] reduce diff --- packages/graphqlgen/src/generators/common.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index ee2b3b1f..f59f30d0 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -4,7 +4,7 @@ import { GraphQLTypeObject, GraphQLTypeField, getGraphQLEnumValues, - GraphQLTypeDefinition, + GraphQLType, } from '../source-helper' import { ModelMap, ContextDefinition, GenerateArgs, Model } from '../types' import { @@ -28,9 +28,9 @@ export interface TypeToInputTypeAssociation { [objectTypeName: string]: string[] } -export type InterfacesMap = Record +export type InterfacesMap = Record -export type UnionsMap = Record +export type UnionsMap = Record export function fieldsFromModelDefinition( modelDef: TypeDefinition, @@ -118,7 +118,7 @@ export function getContextName(context?: ContextDefinition) { } export function getModelName( - type: GraphQLTypeDefinition, + type: GraphQLType, modelMap: ModelMap, emptyType: string = '{}', ): string { @@ -383,7 +383,7 @@ export function groupModelsNameByImportPath(models: Model[]) { return models.reduce<{ [importPath: string]: string[] }>((acc, model) => { const fileModels = acc[model.importPathRelativeToOutput] || [] - if (fileModels.indexOf(model.definition.name) === -1) { + if (!fileModels.includes(model.definition.name)) { fileModels.push(model.definition.name) } From 7f8cfaeb2e122bee7f5a0cfeb1c1d72aa1c95dc2 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Fri, 18 Jan 2019 22:25:18 -0500 Subject: [PATCH 11/19] reduce diff --- packages/graphqlgen/src/generators/common.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index f59f30d0..04533226 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -2,9 +2,9 @@ import * as os from 'os' import { GraphQLTypeObject, + GraphQLType, GraphQLTypeField, getGraphQLEnumValues, - GraphQLType, } from '../source-helper' import { ModelMap, ContextDefinition, GenerateArgs, Model } from '../types' import { From 67b3cef9ab9f753fd19cb8fa18665c367e223803 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 15:58:08 -0500 Subject: [PATCH 12/19] fix type errors --- packages/graphqlgen/src/generators/common.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index 04533226..59ef3743 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -2,7 +2,7 @@ import * as os from 'os' import { GraphQLTypeObject, - GraphQLType, + GraphQLTypeDefinition, GraphQLTypeField, getGraphQLEnumValues, } from '../source-helper' @@ -28,9 +28,9 @@ export interface TypeToInputTypeAssociation { [objectTypeName: string]: string[] } -export type InterfacesMap = Record +export type InterfacesMap = Record -export type UnionsMap = Record +export type UnionsMap = Record export function fieldsFromModelDefinition( modelDef: TypeDefinition, @@ -118,7 +118,7 @@ export function getContextName(context?: ContextDefinition) { } export function getModelName( - type: GraphQLType, + type: GraphQLTypeDefinition, modelMap: ModelMap, emptyType: string = '{}', ): string { From 362b432cba9e1de5c9013a4b55e6c8de416686c8 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 16:21:48 -0500 Subject: [PATCH 13/19] fix --- packages/graphqlgen/src/generators/ts-generator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index d46252ab..3395206a 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -328,7 +328,7 @@ function renderIsTypeOfFunctionInterface( let possibleTypes: GraphQLTypeDefinition[] = [] // TODO Refactor once type is a proper discriminated union - if (type.type.isInterface) { + if (!type.type.isInterface) { type = type as GraphQLTypeObject if (type.implements) { possibleTypes = type.implements.reduce( From 8f510f8c0f1e5f2485fa96c4068a04e7174bd289 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 16:25:12 -0500 Subject: [PATCH 14/19] refactor: dry up unions/interfaces map creations --- packages/graphqlgen/src/generators/common.ts | 16 +++++++++++++++ .../src/generators/flow-generator.ts | 19 ++++-------------- .../graphqlgen/src/generators/ts-generator.ts | 20 ++++--------------- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/packages/graphqlgen/src/generators/common.ts b/packages/graphqlgen/src/generators/common.ts index 59ef3743..cd73e6ba 100644 --- a/packages/graphqlgen/src/generators/common.ts +++ b/packages/graphqlgen/src/generators/common.ts @@ -5,6 +5,8 @@ import { GraphQLTypeDefinition, GraphQLTypeField, getGraphQLEnumValues, + GraphQLInterfaceObject, + GraphQLUnionObject, } from '../source-helper' import { ModelMap, ContextDefinition, GenerateArgs, Model } from '../types' import { @@ -30,8 +32,22 @@ export interface TypeToInputTypeAssociation { export type InterfacesMap = Record +export const createInterfacesMap = ( + interfaces: GraphQLInterfaceObject[], +): InterfacesMap => + interfaces.reduce((interfacesMap, inter) => { + interfacesMap[inter.name] = inter.implementors + return interfacesMap + }, {}) + export type UnionsMap = Record +export const createUnionsMap = (unions: GraphQLUnionObject[]): UnionsMap => + unions.reduce((unionsMap, union) => { + unionsMap[union.name] = union.types + return unionsMap + }, {}) + export function fieldsFromModelDefinition( modelDef: TypeDefinition, ): FieldDefinition[] { diff --git a/packages/graphqlgen/src/generators/flow-generator.ts b/packages/graphqlgen/src/generators/flow-generator.ts index 5a3fa984..6ce99dec 100644 --- a/packages/graphqlgen/src/generators/flow-generator.ts +++ b/packages/graphqlgen/src/generators/flow-generator.ts @@ -20,6 +20,8 @@ import { TypeToInputTypeAssociation, InterfacesMap, UnionsMap, + createInterfacesMap, + createUnionsMap, } from './common' export function format(code: string, options: prettier.Options = {}) { @@ -71,21 +73,8 @@ export function generate(args: GenerateArgs): string { } }, {}) - const interfacesMap = args.interfaces.reduce( - (interfaces, inter) => { - interfaces[inter.name] = inter.implementors - return interfaces - }, - {} as InterfacesMap, - ) - - const unionsMap = args.unions.reduce( - (unions, union) => { - unions[union.name] = union.types - return unions - }, - {} as UnionsMap, - ) + const interfacesMap = createInterfacesMap(args.interfaces) + const unionsMap = createUnionsMap(args.unions) return `\ ${renderHeader(args)} diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index 3395206a..94e757ce 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -21,6 +21,8 @@ import { groupModelsNameByImportPath, InterfacesMap, UnionsMap, + createInterfacesMap, + createUnionsMap, } from './common' import { TypeAliasDefinition } from '../introspection/types' import { upperFirst } from '../utils' @@ -74,22 +76,8 @@ export function generate(args: GenerateArgs): string { } }, {}) - const interfacesMap: InterfacesMap = args.interfaces.reduce( - (interfaces, int) => { - return { - ...interfaces, - [int.name]: int.implementors, - } - }, - {}, - ) - - const unionsMap: InterfacesMap = args.unions.reduce((interfaces, int) => { - return { - ...interfaces, - [int.name]: int.types, - } - }, {}) + const interfacesMap = createInterfacesMap(args.interfaces) + const unionsMap = createUnionsMap(args.unions) return `\ ${renderHeader(args)} From 60fa23e50d41583a4b9a0816285619059b120075 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 20:15:57 -0500 Subject: [PATCH 15/19] remove unused fixture code --- packages/graphqlgen/src/tests/fixtures/interface/types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/graphqlgen/src/tests/fixtures/interface/types.ts b/packages/graphqlgen/src/tests/fixtures/interface/types.ts index 3bd11d5c..dfc6ce74 100644 --- a/packages/graphqlgen/src/tests/fixtures/interface/types.ts +++ b/packages/graphqlgen/src/tests/fixtures/interface/types.ts @@ -1,5 +1,3 @@ -// export interface Context {} - export interface Media { id: string url: string From be26c4624264d585a5925d21b32279e7ccd74c02 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 20:36:21 -0500 Subject: [PATCH 16/19] fix benchmark type errors --- packages/graphqlgen/benchmarks/micro/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/graphqlgen/benchmarks/micro/index.ts b/packages/graphqlgen/benchmarks/micro/index.ts index cae5647f..ec7253e2 100644 --- a/packages/graphqlgen/benchmarks/micro/index.ts +++ b/packages/graphqlgen/benchmarks/micro/index.ts @@ -3,6 +3,7 @@ import * as Benchmark from '../lib/benchmark' const type = { name: 'Z', + implements: null, type: { name: 'Z', isInput: false, @@ -18,6 +19,7 @@ const type = { const typeMap: Core.InputTypesMap = { A: { name: 'A', + implements: null, type: { name: 'A', isInput: true, @@ -41,6 +43,7 @@ const typeMap: Core.InputTypesMap = { isUnion: false, isRequired: false, isArray: false, + isArrayRequired: false, }, }, { @@ -56,12 +59,14 @@ const typeMap: Core.InputTypesMap = { isUnion: false, isRequired: false, isArray: false, + isArrayRequired: false, }, }, ], }, B: { name: 'B', + implements: null, type: { name: 'B', isInput: true, @@ -85,12 +90,14 @@ const typeMap: Core.InputTypesMap = { isUnion: false, isRequired: false, isArray: false, + isArrayRequired: false, }, }, ], }, C: { name: 'C', + implements: null, type: { name: 'C', isInput: true, @@ -114,12 +121,14 @@ const typeMap: Core.InputTypesMap = { isUnion: false, isRequired: false, isArray: false, + isArrayRequired: false, }, }, ], }, D: { name: 'D', + implements: null, type: { name: 'D', isInput: true, @@ -143,6 +152,7 @@ const typeMap: Core.InputTypesMap = { isUnion: false, isRequired: false, isArray: false, + isArrayRequired: false, }, }, ], From bae08335ef5423ce25d0c45b7783d819762b501d Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sat, 19 Jan 2019 20:44:55 -0500 Subject: [PATCH 17/19] if an interface is unused it should not emit types --- packages/graphqlgen/src/source-helper.ts | 28 ++++++++++--------- .../tests/fixtures/interface/schema.graphql | 4 +++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/graphqlgen/src/source-helper.ts b/packages/graphqlgen/src/source-helper.ts index 9c4397be..6efa0c11 100644 --- a/packages/graphqlgen/src/source-helper.ts +++ b/packages/graphqlgen/src/source-helper.ts @@ -377,20 +377,22 @@ function extractGraphQLInterfaces( .filter(type => type.implements!.includes(node.name)) .map(type => type.type) - interfaces.push({ - name: node.name, - type: { + if (implementorTypes.length) { + interfaces.push({ name: node.name, - isObject: false, - isInput: false, - isEnum: false, - isUnion: false, - isScalar: false, - isInterface: true, - }, - implementors: implementorTypes, - fields: extractTypeFieldsFromObjectType(schema, node), - }) + type: { + name: node.name, + isObject: false, + isInput: false, + isEnum: false, + isUnion: false, + isScalar: false, + isInterface: true, + }, + implementors: implementorTypes, + fields: extractTypeFieldsFromObjectType(schema, node), + }) + } return interfaces }, diff --git a/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql b/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql index a7a251d6..faf187e7 100644 --- a/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql +++ b/packages/graphqlgen/src/tests/fixtures/interface/schema.graphql @@ -2,6 +2,10 @@ type Query { media(first: Int): [Media] } +interface Unused { + foo: Int +} + interface Media { id: ID! url: String! From cfb013349c06597d865bd91a8f4aa600f13508c6 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 20 Jan 2019 15:16:31 -0500 Subject: [PATCH 18/19] do not generate gql object resolver extends interface --- .../graphqlgen/src/generators/ts-generator.ts | 26 +------------------ .../__snapshots__/basic.test.ts.snap | 20 ++------------ 2 files changed, 3 insertions(+), 43 deletions(-) diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index 94e757ce..058486aa 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -223,15 +223,6 @@ function renderInterfaceNamespace( unionsMap, )} - ${renderResolverTypeInterface( - graphQLTypeObject, - args.modelMap, - interfacesMap, - unionsMap, - args.context, - 'InterfaceType', - )} - export interface Type { __resolveType: GraphQLTypeResolver<${graphQLTypeObject.implementors .map(interfaceType => getModelName(interfaceType, args.modelMap)) @@ -476,23 +467,8 @@ function renderResolverTypeInterface( context?: ContextDefinition, interfaceName: string = 'Type', ): string { - // TODO: Refactor this with proper union type branching once the - // type modelling is refactored itself to support that. - let extend = '' - - if (!type.type.isInterface) { - type = type as GraphQLTypeObject - - extend = - type.implements && type.implements.length - ? `extends ${type.implements - .map(typeInterface => `${typeInterface}Resolvers.InterfaceType`) - .join(',')}` - : '' - } - return ` - export interface ${interfaceName} ${extend} { + export interface ${interfaceName} { ${type.fields .map(field => renderResolverTypeInterfaceFunction( diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap index 8eab170e..f47f6b27 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap @@ -448,7 +448,7 @@ export namespace ImageResolvers { info: GraphQLResolveInfo ) => Dimensions | Promise; - export interface Type extends MediaResolvers.InterfaceType { + export interface Type { id: ( parent: Image, args: {}, @@ -498,7 +498,7 @@ export namespace VideoResolvers { info: GraphQLResolveInfo ) => number | Promise; - export interface Type extends MediaResolvers.InterfaceType { + export interface Type { id: ( parent: Video, args: {}, @@ -525,22 +525,6 @@ export namespace VideoResolvers { } export namespace MediaResolvers { - export interface InterfaceType { - id: ( - parent: Image | Video, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; - - url: ( - parent: Image | Video, - args: {}, - ctx: Context, - info: GraphQLResolveInfo - ) => string | Promise; - } - export interface Type { __resolveType: GraphQLTypeResolver; } From fffef276d6812a199c9ef2eb6811adce0524579d Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Sun, 20 Jan 2019 15:34:53 -0500 Subject: [PATCH 19/19] if unions/interfaces not used do not import respective field resolution types --- .../graphqlgen/src/generators/ts-generator.ts | 19 ++++++-- .../__snapshots__/basic.test.ts.snap | 48 ++++--------------- .../__snapshots__/large-schema.test.ts.snap | 6 +-- 3 files changed, 25 insertions(+), 48 deletions(-) diff --git a/packages/graphqlgen/src/generators/ts-generator.ts b/packages/graphqlgen/src/generators/ts-generator.ts index 058486aa..8c5a0f42 100644 --- a/packages/graphqlgen/src/generators/ts-generator.ts +++ b/packages/graphqlgen/src/generators/ts-generator.ts @@ -78,9 +78,11 @@ export function generate(args: GenerateArgs): string { const interfacesMap = createInterfacesMap(args.interfaces) const unionsMap = createUnionsMap(args.unions) + const hasPolymorphicObjects = + Object.keys(interfacesMap).length > 0 || Object.keys(unionsMap).length > 0 return `\ - ${renderHeader(args)} + ${renderHeader(args, { hasPolymorphicObjects })} ${renderEnums(args)} @@ -97,11 +99,22 @@ export function generate(args: GenerateArgs): string { ` } -function renderHeader(args: GenerateArgs): string { +type HeaderOptions = { + hasPolymorphicObjects?: boolean +} + +function renderHeader( + args: GenerateArgs, + { hasPolymorphicObjects = false }: HeaderOptions = {}, +): string { + const imports = hasPolymorphicObjects + ? ['GraphQLResolveInfo', 'GraphQLTypeResolver', 'GraphQLIsTypeOfFn'] + : ['GraphQLResolveInfo'] + return ` // Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { GraphQLResolveInfo, GraphQLTypeResolver, GraphQLIsTypeOfFn } from 'graphql' +import { ${imports.join(', ')} } from 'graphql' ${renderImports(args)} ` } diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap index f47f6b27..94d078c1 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/basic.test.ts.snap @@ -3,11 +3,7 @@ exports[`basic enum 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { User } from \\"../../fixtures/enum/types\\"; type Context = any; @@ -170,11 +166,7 @@ export const resolvers: Resolvers = { exports[`basic input 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { AddMemberPayload } from \\"../../fixtures/input/types\\"; type Context = any; @@ -656,11 +648,7 @@ export const resolvers: Resolvers = { exports[`basic scalar 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { AddMemberPayload } from \\"../../fixtures/scalar/types\\"; type Context = any; @@ -776,11 +764,7 @@ export const resolvers: Resolvers = { exports[`basic schema 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { Number } from \\"../../fixtures/basic\\"; type Context = any; @@ -1354,11 +1338,7 @@ export const resolvers: Resolvers = { exports[`context 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { User, Context } from \\"../../fixtures/context/types\\"; export namespace QueryResolvers { @@ -1464,11 +1444,7 @@ export const resolvers: Resolvers = { exports[`defaultName 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { NumberNode } from \\"../../fixtures/defaultName\\"; type Context = any; @@ -1816,11 +1792,7 @@ export const resolvers: Resolvers = { exports[`override model 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { Number } from \\"../../../fixtures/basic\\"; type Context = any; @@ -2162,11 +2134,7 @@ export const resolvers: Resolvers = { exports[`subscription 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { User } from \\"../../fixtures/subscription/types\\"; type Context = any; diff --git a/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap b/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap index 632b8058..1e11565f 100644 --- a/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap +++ b/packages/graphqlgen/src/tests/typescript/__snapshots__/large-schema.test.ts.snap @@ -3,11 +3,7 @@ exports[`large schema tests large schema 1`] = ` "// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT. -import { - GraphQLResolveInfo, - GraphQLTypeResolver, - GraphQLIsTypeOfFn -} from \\"graphql\\"; +import { GraphQLResolveInfo } from \\"graphql\\"; import { Experience, ExperienceCategory,