Skip to content
This repository has been archived by the owner on Dec 8, 2021. It is now read-only.

[WIP] Support enums #244

Merged
merged 22 commits into from
Nov 7, 2018
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT.

import type { GraphQLResolveInfo } from 'graphql'
import type { Context } from '../types'
import type { Post } from '../types'
import type { User } from '../types'
import type { Context } from '../types'

// Types for Query
export const Query_defaultResolvers = {}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
/* @flow */
// @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 type { Resolvers } from '../graphqlgen'

import { Query } from './Query'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Code generated by github.com/prisma/graphqlgen, DO NOT EDIT.

import { GraphQLResolveInfo } from 'graphql'
import { Context } from '../types'
import { Post } from '../types'
import { User } from '../types'
import { Context } from '../types'
Weakky marked this conversation as resolved.
Show resolved Hide resolved

export namespace QueryResolvers {
export const defaultResolvers = {}
Expand Down Expand Up @@ -141,8 +141,7 @@ export namespace PostResolvers {
export namespace UserResolvers {
export const defaultResolvers = {
id: (parent: User) => parent.id,
name: (parent: User) =>
parent.name === undefined || parent.name === null ? null : parent.name,
name: (parent: User) => (parent.name === undefined ? null : parent.name),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why () is needed here?

Copy link
Contributor Author

@Weakky Weakky Nov 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parenthesis aren't in the code generator. They must be added by prettier somehow 😕

}

export type IdResolver = (
Expand Down
2 changes: 1 addition & 1 deletion packages/graphqlgen/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "graphqlgen",
"version": "0.2.15",
"version": "0.3.0-beta1",
"description": "Generate resolver types based on a GraphQL Schema",
"main": "dist/index.js",
"files": [
Expand Down
128 changes: 100 additions & 28 deletions packages/graphqlgen/src/generators/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ import {
GraphQLTypeObject,
GraphQLType,
GraphQLTypeField,
getGraphQLEnumValues,
} from '../source-helper'
import { Model, ModelMap, ContextDefinition } from '../types'
import { ModelField } from '../introspection/ts-ast'
import { ModelMap, ContextDefinition, GenerateArgs } from '../types'
import { flatten, uniq } from '../utils'
import {
TypeDefinition,
FieldDefinition,
InterfaceDefinition,
TypeAliasDefinition,
AnonymousInterfaceAnnotation,
} from '../introspection/types'
import {
isFieldDefinitionEnumOrLiteral,
getEnumValues,
} from '../introspection/utils'

type SpecificGraphQLScalarType = 'boolean' | 'number' | 'string'

Expand All @@ -19,28 +30,56 @@ export interface TypeToInputTypeAssociation {
[objectTypeName: string]: string[]
}

export function fieldsFromModelDefinition(
modelDef: TypeDefinition,
): FieldDefinition[] {
// If model is of type `interface InterfaceName { ... }`
if (modelDef.kind === 'InterfaceDefinition') {
const interfaceDef = modelDef as InterfaceDefinition

return interfaceDef.fields
}
// If model is of type `type TypeName = { ... }`
if (
modelDef.kind === 'TypeAliasDefinition' &&
(modelDef as TypeAliasDefinition).getType().kind ===
'AnonymousInterfaceAnnotation'
) {
const interfaceDef = (modelDef as TypeAliasDefinition).getType() as AnonymousInterfaceAnnotation

return interfaceDef.fields
}

return []
}

export function renderDefaultResolvers(
type: GraphQLTypeObject,
modelMap: ModelMap,
extractFieldsFromModel: (model: Model) => ModelField[],
graphQLTypeObject: GraphQLTypeObject,
args: GenerateArgs,
variableName: string,
): string {
const model = modelMap[type.name]
const model = args.modelMap[graphQLTypeObject.name]

if (model === undefined) {
return `export const ${variableName} = {}`
}

const modelFields = extractFieldsFromModel(model)
const modelDef = model.definition

return `export const ${variableName} = {
${modelFields
.filter(modelField => shouldRenderDefaultResolver(type, modelField))
${fieldsFromModelDefinition(modelDef)
.filter(modelField => {
const graphQLField = graphQLTypeObject.fields.find(
field => field.name === modelField.name,
)

return shouldRenderDefaultResolver(graphQLField, modelField, args)
})
.map(modelField =>
renderDefaultResolver(
modelField.fieldName,
modelField.fieldOptional,
model.modelTypeName,
modelField.name,
modelField.optional,
model.definition.name,
),
)
.join(os.EOL)}
Expand Down Expand Up @@ -90,37 +129,60 @@ export function getModelName(type: GraphQLType, modelMap: ModelMap): string {
return '{}'
}

return model.modelTypeName
return model.definition.name
}

function shouldRenderDefaultResolver(
type: GraphQLTypeObject,
modelField: ModelField,
function isModelEnumSubsetOfGraphQLEnum(
graphQLEnumValues: string[],
modelEnumValues: string[],
) {
const graphQLField = type.fields.find(
field => field.name === modelField.fieldName,
return modelEnumValues.every(enumValue =>
graphQLEnumValues.includes(enumValue),
)
}

function shouldRenderDefaultResolver(
graphQLField: GraphQLTypeField | undefined,
modelField: FieldDefinition | undefined,
args: GenerateArgs,
) {
if (graphQLField === undefined) {
return false
}

if (!graphQLField) {
if (modelField === undefined) {
return false
}

return !(modelField.fieldOptional && graphQLField.type.isRequired)
const modelFieldType = modelField.getType()

// If both types are enums, and model definition enum is a subset of the graphql enum
// Then render as defaultResolver
// eg: given GraphQLEnum = 'A' | 'B' | 'C'
// render when FieldDefinition = ('A') | ('A' | 'B') | ('A | 'B' | 'C')
if (
graphQLField.type.isEnum &&
isFieldDefinitionEnumOrLiteral(modelFieldType)
) {
return isModelEnumSubsetOfGraphQLEnum(
getGraphQLEnumValues(graphQLField, args.enums),
getEnumValues(modelFieldType),
)
}

return !(modelField.optional && graphQLField.type.isRequired)
}

export function shouldScaffoldFieldResolver(
graphQLField: GraphQLTypeField,
modelFields: ModelField[],
modelFields: FieldDefinition[],
args: GenerateArgs,
): boolean {
const modelField = modelFields.find(
modelField => modelField.fieldName === graphQLField.name,
modelField => modelField.name === graphQLField.name,
)

if (!modelField) {
return true
}

return modelField.fieldOptional && graphQLField.type.isRequired
return !shouldRenderDefaultResolver(graphQLField, modelField, args)
}

export function printFieldLikeType(
Expand All @@ -133,7 +195,7 @@ export function printFieldLikeType(
}${!field.type.isRequired ? '| null' : ''}`
}

if (field.type.isInput) {
if (field.type.isInput || field.type.isEnum) {
return `${field.type.name}${field.type.isArray ? '[]' : ''}${
!field.type.isRequired ? '| null' : ''
}`
Expand Down Expand Up @@ -189,3 +251,13 @@ export function getDistinctInputTypes(
.reduce(flatten, [])
.filter(uniq)
}

export function renderEnums(args: GenerateArgs): string {
return args.enums
.map(enumObject => {
return `type ${enumObject.name} = ${enumObject.values
.map(value => `'${value}'`)
.join(' | ')}`
})
.join(os.EOL)
}
43 changes: 17 additions & 26 deletions packages/graphqlgen/src/generators/flow-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import * as prettier from 'prettier'

import { GenerateArgs, ModelMap, ContextDefinition } from '../types'
import { GraphQLTypeField, GraphQLTypeObject } from '../source-helper'
import { extractFieldsFromFlowType } from '../introspection/flow-ast'
import { upperFirst } from '../utils'
import {
renderDefaultResolvers,
getContextName,
getDistinctInputTypes,
getModelName,
TypeToInputTypeAssociation,
InputTypesMap,
printFieldLikeType,
getDistinctInputTypes,
renderDefaultResolvers,
renderEnums,
TypeToInputTypeAssociation,
} from './common'

export function format(code: string, options: prettier.Options = {}) {
Expand Down Expand Up @@ -67,6 +67,8 @@ export function generate(args: GenerateArgs): string {
return `\
${renderHeader(args)}

${renderEnums(args)}

${renderNamespaces(args, typeToInputTypeAssociation, inputTypesMap)}

${renderResolvers(args)}
Expand All @@ -79,7 +81,7 @@ function renderHeader(args: GenerateArgs): string {
const modelImports = modelArray
.map(
m =>
`import type { ${m.modelTypeName} } from '${
`import type { ${m.definition.name} } from '${
m.importPathRelativeToOutput
}'`,
)
Expand All @@ -90,7 +92,6 @@ function renderHeader(args: GenerateArgs): string {

import type { GraphQLResolveInfo } from 'graphql'
${modelImports}

${renderContext(args.context)}
`
}
Expand All @@ -113,13 +114,7 @@ function renderNamespaces(
return args.types
.filter(type => type.type.isObject)
.map(type =>
renderNamespace(
type,
typeToInputTypeAssociation,
inputTypesMap,
args.modelMap,
args.context,
),
renderNamespace(type, typeToInputTypeAssociation, inputTypesMap, args),
)
.join(os.EOL)
}
Expand All @@ -128,32 +123,26 @@ function renderNamespace(
type: GraphQLTypeObject,
typeToInputTypeAssociation: TypeToInputTypeAssociation,
inputTypesMap: InputTypesMap,
modelMap: ModelMap,
context?: ContextDefinition,
args: GenerateArgs,
): string {
const typeName = upperFirst(type.name)

return `\
// Types for ${typeName}
${renderDefaultResolvers(
type,
modelMap,
extractFieldsFromFlowType,
`${typeName}_defaultResolvers`,
)}
${renderDefaultResolvers(type, args, `${typeName}_defaultResolvers`)}

${renderInputTypeInterfaces(
type,
modelMap,
args.modelMap,
typeToInputTypeAssociation,
inputTypesMap,
)}

${renderInputArgInterfaces(type, modelMap)}
${renderInputArgInterfaces(type, args.modelMap)}

${renderResolverFunctionInterfaces(type, modelMap, context)}
${renderResolverFunctionInterfaces(type, args.modelMap, args.context)}

${renderResolverTypeInterface(type, modelMap, context)}
${renderResolverTypeInterface(type, args.modelMap, args.context)}

${/* TODO renderResolverClass(type, modelMap) */ ''}
`
Expand All @@ -171,7 +160,9 @@ function renderInputTypeInterfaces(

return getDistinctInputTypes(type, typeToInputTypeAssociation, inputTypesMap)
.map(typeAssociation => {
return `export interface ${inputTypesMap[typeAssociation].name} {
return `export interface ${upperFirst(type.name)}_${upperFirst(
inputTypesMap[typeAssociation].name,
)} {
${inputTypesMap[typeAssociation].fields.map(
field => `${field.name}: ${printFieldLikeType(field, modelMap)}`,
)}
Expand Down
Loading