import { EnumTypeComposer , SchemaComposer , ObjectTypeComposer , InterfaceTypeComposer , ObjectTypeComposerRelationOpts , ObjectTypeComposerFieldConfigDefinition , ObjectTypeComposerFieldConfigMapDefinition , ObjectTypeComposerFieldConfigAsObjectDefinition , graphqlVersion , } from 'graphql-compose' ; import type { Model } from 'mongoose' ; import { composeWithMongoose , ComposeWithMongooseOpts } from '../composeWithMongoose' ; import { composeChildTC } from './composeChildTC' ; import { mergeCustomizationOptions } from './utils/mergeCustomizationOptions' ; import { prepareBaseResolvers } from './prepareBaseResolvers' ; import { reorderFields } from './utils/reorderFields' ; import { GraphQLFieldConfig , GraphQLObjectType } from 'graphql-compose/lib/graphql' ; export interface ComposeWithMongooseDiscriminatorsOpts < TContext > extends ComposeWithMongooseOpts < TContext > { reorderFields ? : boolean | string [ ] ; } type Discriminators = { [ DName ] : any ; } ; function setDKeyETCValues ( discriminators ) { const values = { } ; for ( const DName in discriminators ) { if ( discriminators . hasOwnProperty ( DName ) ) { values [ DName ] = { value : DName , } ; } } return values ; } function createAndSetDKeyETC ( dTC , discriminators ) { const DKeyETC = dTC . schemaComposer . createEnumTC ( { name : `EnumDKey${ dTC . getTypeName ( ) }${ dTC . getDKey ( ) [ 0 ] . toUpperCase ( ) + dTC . getDKey ( ) . substr ( 1 ) }` , values : setDKeyETCValues ( discriminators ) , } ) ; dTC . extendField ( dTC . getDKey ( ) , { type : ( ) => DKeyETC , } ) ; dTC . getInputTypeComposer ( ) . extendField ( dTC . getDKey ( ) , { type : ( ) => DKeyETC , } ) ; return DKeyETC ; } export class DiscriminatorTypeComposer < TSource , TContext > extends ObjectTypeComposer < TSource , TContext > { discriminatorKey : string = '' ; opts : ComposeWithMongooseDiscriminatorsOpts < TContext > = { } ; childTCs : ObjectTypeComposer < any , TContext > [ ] = [ ] ; DInterface ? : InterfaceTypeComposer < TSource , TContext > ; DKeyETC ? : EnumTypeComposer < TContext > ; constructor ( gqType , schemaComposer ) { super ( gqType , schemaComposer ) ; return this ; } static createFromModel < TSrc = any , TCtx = any > ( baseModel , schemaComposer , opts ? ) { if ( ! baseModel || ! baseModel . discriminators ) { throw Error ( 'Discriminator Key not Set, have you already registered discriminators on your base model? ' + 'Otherwise, use composeWithMongoose for Normal Collections' ) ; } if ( ! ( schemaComposer instanceof SchemaComposer ) ) { throw Error ( 'DiscriminatorTC.createFromModel() should receive SchemaComposer in second argument' ) ; } opts = { reorderFields : true , schemaComposer , ... opts , } ; const baseTC = composeWithMongoose ( baseModel , opts ) ; const baseDTC = new DiscriminatorTypeComposer ( baseTC . getType ( ) , schemaComposer ) ; baseTC . clone ( baseDTC as ObjectTypeComposer < any , any > ) ; baseDTC . _gqcInputTypeComposer = ( baseTC as any ) . _gqcInputTypeComposer ; baseDTC . opts = opts ; baseDTC . childTCs = [ ] ; baseDTC . discriminatorKey = baseModel . schema . get ( 'discriminatorKey' ) || '__t' ; baseDTC . DInterface = baseDTC . _createDInterface ( baseDTC ) ; baseDTC . setInterfaces ( [ baseDTC . DInterface ] ) ; baseDTC . DKeyETC = createAndSetDKeyETC ( baseDTC , baseModel . discriminators ) ; reorderFields ( baseDTC , baseDTC . opts . reorderFields , baseDTC . discriminatorKey ) ; baseDTC . schemaComposer . addSchemaMustHaveType ( baseDTC as any ) ; prepareBaseResolvers ( baseDTC ) ; return baseDTC as any ; } _createDInterface ( baseTC ) { const baseFields = baseTC . getFieldNames ( ) ; const interfaceFields = { } as Record < string , GraphQLFieldConfig < any , any > > ; for ( const field of baseFields ) { interfaceFields [ field ] = baseTC . getFieldConfig ( field ) ; } return this . schemaComposer . createInterfaceTC ( { name : `${ baseTC . getTypeName ( ) }Interface` , resolveType : ( value ) => { const childDName = value [ baseTC . getDKey ( ) ] ; if ( childDName ) { if ( graphqlVersion >= 16 ) { return childDName ; } else { return baseTC . schemaComposer . getOTC ( childDName ) . getType ( ) ; } } if ( graphqlVersion >= 16 ) { return baseTC . getTypeName ( ) ; } else { return baseTC . schemaComposer . getOTC ( baseTC . getTypeName ( ) ) . getType ( ) ; } } , fields : interfaceFields as any , } ) ; } getDKey ( ) { return this . discriminatorKey ; } getDKeyETC ( ) { return this . DKeyETC as any ; } getDInterface ( ) { return this . DInterface as any ; } hasChildTC ( DName ) { return ! ! this . childTCs . find ( ( ch ) => ch . getTypeName ( ) === DName ) ; } discriminator < TSrc = Model < any > > ( childModel , opts ? ) { const customizationOpts = mergeCustomizationOptions ( this . opts , opts ) ; let childTC = composeWithMongoose ( childModel , customizationOpts ) ; childTC = composeChildTC ( this , childTC , this . opts ) ; this . schemaComposer . addSchemaMustHaveType ( childTC ) ; this . childTCs . push ( childTC ) ; return childTC as any ; } setFields ( fields ) { const oldFieldNames = super . getFieldNames ( ) ; super . setFields ( fields ) ; this . getDInterface ( ) . setFields ( fields ) ; for ( const childTC of this . childTCs ) { childTC . removeField ( oldFieldNames ) ; childTC . addFields ( fields ) ; reorderFields ( childTC , this . opts . reorderFields , this . getDKey ( ) , super . getFieldNames ( ) ) ; } return this ; } setField ( fieldName , fieldConfig ) { super . setField ( fieldName , fieldConfig ) ; this . getDInterface ( ) . setField ( fieldName , fieldConfig ) ; for ( const childTC of this . childTCs ) { childTC . setField ( fieldName , fieldConfig ) ; } return this ; } addFields ( newFields ) { super . addFields ( newFields ) ; this . getDInterface ( ) . addFields ( newFields ) ; for ( const childTC of this . childTCs ) { childTC . addFields ( newFields ) ; } return this ; } addNestedFields ( newFields ) { super . addNestedFields ( newFields ) ; this . getDInterface ( ) . setFields ( this . getFields ( ) ) ; for ( const childTC of this . childTCs ) { childTC . addNestedFields ( newFields ) ; } return this ; } removeField ( fieldNameOrArray ) { super . removeField ( fieldNameOrArray ) ; this . getDInterface ( ) . removeField ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { childTC . removeField ( fieldNameOrArray ) ; } return this ; } removeOtherFields ( fieldNameOrArray ) { const oldFieldNames = super . getFieldNames ( ) ; super . removeOtherFields ( fieldNameOrArray ) ; this . getDInterface ( ) . removeOtherFields ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { const specificFields = childTC . getFieldNames ( ) . filter ( ( childFieldName ) => ! oldFieldNames . find ( ( oldBaseFieldName ) => oldBaseFieldName === childFieldName ) ) ; childTC . removeOtherFields ( super . getFieldNames ( ) . concat ( specificFields ) ) ; reorderFields ( childTC , this . opts . reorderFields , this . getDKey ( ) , super . getFieldNames ( ) ) ; } return this ; } reorderFields ( names ) { super . reorderFields ( names ) ; this . getDInterface ( ) . reorderFields ( names ) ; for ( const childTC of this . childTCs ) { childTC . reorderFields ( names ) ; } return this ; } extendField ( fieldName , partialFieldConfig ) { super . extendField ( fieldName , partialFieldConfig ) ; this . getDInterface ( ) . extendField ( fieldName , partialFieldConfig ) ; for ( const childTC of this . childTCs ) { childTC . extendField ( fieldName , partialFieldConfig ) ; } return this ; } makeFieldNonNull ( fieldNameOrArray ) { super . makeFieldNonNull ( fieldNameOrArray ) ; this . getDInterface ( ) . makeFieldNonNull ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { childTC . makeFieldNonNull ( fieldNameOrArray ) ; } return this ; } makeFieldNullable ( fieldNameOrArray ) { super . makeFieldNullable ( fieldNameOrArray ) ; this . getDInterface ( ) . makeFieldNullable ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { childTC . makeFieldNullable ( fieldNameOrArray ) ; } return this ; } makeFieldPlural ( fieldNameOrArray ) { super . makeFieldPlural ( fieldNameOrArray ) ; this . getDInterface ( ) . makeFieldPlural ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { childTC . makeFieldPlural ( fieldNameOrArray ) ; } return this ; } makeFieldNonPlural ( fieldNameOrArray ) { super . makeFieldNonPlural ( fieldNameOrArray ) ; this . getDInterface ( ) . makeFieldNonPlural ( fieldNameOrArray ) ; for ( const childTC of this . childTCs ) { childTC . makeFieldNonPlural ( fieldNameOrArray ) ; } return this ; } deprecateFields ( fields ) { super . deprecateFields ( fields ) ; this . getDInterface ( ) . deprecateFields ( fields ) ; for ( const childTC of this . childTCs ) { childTC . deprecateFields ( fields ) ; } return this ; } addRelation ( fieldName , relationOpts ) { super . addRelation ( fieldName , relationOpts ) ; this . getDInterface ( ) . setField ( fieldName , this . getField ( fieldName ) ) ; for ( const childTC of this . childTCs ) { childTC . addRelation ( fieldName , relationOpts ) ; } return this ; } }