import { GraphQLJSONObject } from 'graphql-type-json' ; import { GraphQLNonInputType , TargetTypeMetadata , TargetTypesMap } from './common' ; import { AnyClass , UUID } from '@boostercloud/framework-types' ; import { GraphQLFieldConfigMap , GraphQLList , GraphQLObjectType , GraphQLBoolean , GraphQLFloat , GraphQLID , GraphQLString , GraphQLInputType , GraphQLScalarType , GraphQLInputObjectType , GraphQLOutputType , GraphQLNonNull , GraphQLEnumType , GraphQLInterfaceType , } from 'graphql' ; import { GraphQLFieldMap , GraphQLInputFieldConfigMap } from 'graphql/type/definition' ; import { PropertyMetadata } from 'metadata-booster' ; import { getPropertiesMetadata } from './../../decorators/metadata' ; export class GraphQLTypeInformer { private graphQLTypesByName : Record < string , GraphQLNonInputType > = { } ; private graphQLInputTypesByName : Record < string , GraphQLInputType > = { } ; public constructor ( private readonly typesByName ) { for ( const name in this . typesByName ) { this . generateGraphQLTypeFromMetadata ( this . typesByName [ name ] ) ; } } public generateGraphQLTypeFromMetadata ( typeMetadata ) { const name = typeMetadata . class . name ; if ( ! this . graphQLTypesByName [ name ] ) { this . graphQLTypesByName [ name ] = new GraphQLObjectType ( { name : typeMetadata . class . name , fields : ( ) => this . metadataPropertiesToGraphQLFields ( typeMetadata . properties ) , } ) ; } } private metadataPropertiesToGraphQLFields ( properties ) { const fields = { } ; for ( const prop of properties ) { const primitiveType = this . getOriginalAncestor ( prop . typeInfo . type ) ; if ( primitiveType === Array ) { const param = prop . typeInfo . parameters [ 0 ] ; const graphQLPropType = this . getGraphQLTypeFor ( param . type ) ; if ( ! this . isGraphQLScalarType ( graphQLPropType ) && param . type ) { const properties = getPropertiesMetadata ( param . type ) ; this . generateGraphQLTypeFromMetadata ( { class : param . type , properties } ) ; } fields [ prop . name ] = { type : GraphQLList ( new GraphQLNonNull ( graphQLPropType ) ) , } ; } else { fields [ prop . name ] = { type : this . getGraphQLTypeFor ( prop . typeInfo . type ) } ; } } return fields ; } public isGraphQLScalarType ( graphQLType ) { return graphQLType instanceof GraphQLScalarType && graphQLType != GraphQLJSONObject ; } public getOriginalAncestor ( type ) { if ( ! type ) return type ; const typePrototype = Object . getPrototypeOf ( type ) ; if ( typePrototype === Function . prototype ) { return type ; } return this . getOriginalAncestor ( typePrototype ) ; } public getGraphQLTypeFor ( type ) { if ( type === UUID ) return GraphQLID ; const primitiveType = this . getOriginalAncestor ( type ) ; switch ( primitiveType ) { case String : return GraphQLString ; case Number : return GraphQLFloat ; case Boolean : return GraphQLBoolean ; case undefined : return GraphQLJSONObject ; default : if ( this . graphQLTypesByName [ type . name ] ) { return this . graphQLTypesByName [ type . name ] ; } if ( this . typesByName [ type . name ] ) { this . generateGraphQLTypeFromMetadata ( this . typesByName [ type . name ] ) ; return this . graphQLTypesByName [ type . name ] ; } return GraphQLJSONObject ; } } public getGraphQLInputTypeFor ( type ) { return this . toInputType ( this . getGraphQLTypeFor ( type ) ) ; } public toInputType ( graphQLType ) { if ( graphQLType instanceof GraphQLScalarType || graphQLType instanceof GraphQLEnumType ) { return graphQLType ; } if ( graphQLType instanceof GraphQLList ) { return new GraphQLList ( this . toInputType ( graphQLType . ofType ) ) ; } if ( graphQLType instanceof GraphQLNonNull ) { return new GraphQLNonNull ( this . toInputType ( graphQLType . ofType ) ) ; } if ( graphQLType instanceof GraphQLObjectType ) { const name = `${ graphQLType . name }Input` ; if ( ! this . graphQLInputTypesByName [ name ] ) { this . graphQLInputTypesByName [ name ] = new GraphQLInputObjectType ( { name , fields : ( ) => this . toInputFields ( graphQLType . getFields ( ) ) , } ) ; } return this . graphQLInputTypesByName [ name ] ; } throw new Error ( `Types '${ GraphQLEnumType . name }' and '${ GraphQLInterfaceType }' are not allowed as input type, ` + `and '${ graphQLType . name }' was found` ) ; } private toInputFields ( fields ) { const inputFields = { } ; for ( const fieldName in fields ) { inputFields [ fieldName ] = { type : this . toInputType ( fields [ fieldName ] . type ) , } ; } return inputFields ; } }