Skip to content

Custom directive not working #4027

Open
@longlee218

Description

@longlee218

Custom directive for input is not working in Apollo version 3

Description
I'm following a tutorial at graphql-tools for Enforcing value restrictions to build a directive to validate length of string input.

 input AddCycleWorkInput {
        name: String! @length(max: 10)
    }

Expected
If the length's name input exceeds the specified, i hope throwing any ValidateError or Error

My Environment:

  • OS: Windowns 10
  • @graphql-tools/utils: 8.1.1
  • apollo-server-core: ^3.3.0
  • typescript: ^4.4.3
  • @graphql-tools/utils: 8.1.1,

Schema build

private static buildSchema(accountsGraphQL: any): GraphQLSchema {
        // List directives.
        const { lengthDirectiveTypeDefs, lengthDirectiveTransformer } = lengthDirective('length');

        // Build schema.
        let schema = makeExecutableSchema({
            typeDefs: mergeTypeDefs([
                lengthDirectiveTypeDefs,        // This is problem
                accountsGraphQL.typeDefs,
                typeDefs
            ]),
            resolvers: mergeResolvers([accountsGraphQL.resolvers, resolvers]),
            schemaDirectives: {
                ...accountsGraphQL.schemaDirectives,
            },
        });
        // needed to validate input fields!
        schema = lengthDirectiveTransformer(schema);
        return schema;
    }

Constructor of Apollo Server

   const server = new ApolloServer({
            schema: this.buildSchema(accountsGraphQL),         // Schema build
            context: accountsGraphQL.context,
            uploads: false,
            formatError: (error) => {
                if (error) {
                    return {
                        message: error.message,
                        status: codeToStatus[error.extensions.code.toUpperCase()] || 400,
                        path: error.path
                    };
                }
            }
        });

Length directive, to short you can watch this link

export default function lengthDirective(directiveName: string) {
    class LimitedLengthType extends GraphQLScalarType {
        constructor(type: GraphQLScalarType, maxLength: number) {
            super({
                name: `${type.name}WithLengthAtMost${maxLength.toString()}`,
                serialize(value: string) {
                    console.log("value1", value);
                    const newValue: string = type.serialize(value)
                    if (newValue.length > maxLength) {
                        throw new Error(`expected ${newValue.length.toString(10)} to be at most ${maxLength.toString(10)}`)
                    }
                    return newValue
                },

                parseValue(value: string) {
                    console.log("value2", value);
                    return type.parseValue(value)
                },

                parseLiteral(ast) {
                    return type.parseLiteral(ast, {})
                }
            })
        }
    }

    const limitedLengthTypes: Record<string, Record<number, GraphQLScalarType>> = {}

    function getLimitedLengthType(type: GraphQLScalarType, maxLength: number): GraphQLScalarType {
        const limitedLengthTypesByTypeName = limitedLengthTypes[type.name]
        if (!limitedLengthTypesByTypeName) {
            const newType = new LimitedLengthType(type, maxLength)
            limitedLengthTypes[type.name] = {}
            limitedLengthTypes[type.name][maxLength] = newType
            return newType
        }

        const limitedLengthType = limitedLengthTypesByTypeName[maxLength]
        if (!limitedLengthType) {
            const newType = new LimitedLengthType(type, maxLength)
            limitedLengthTypesByTypeName[maxLength] = newType
            return newType
        }

        return limitedLengthType
    }

    function wrapType<F extends GraphQLFieldConfig<any, any> | GraphQLInputFieldConfig>(
        fieldConfig: F,
        directiveArgumentMap: Record<string, any>
    ): void {
        if (isNonNullType(fieldConfig.type) && isScalarType(fieldConfig.type.ofType)) {
            fieldConfig.type = getLimitedLengthType(fieldConfig.type.ofType, directiveArgumentMap['max'])
        } else if (isScalarType(fieldConfig.type)) {
            fieldConfig.type = getLimitedLengthType(fieldConfig.type, directiveArgumentMap['max'])
        } else {
            throw new Error(`Not a scalar type: ${fieldConfig.type.toString()}`)
        }
    }
    return {
        lengthDirectiveTypeDefs: `directive @${directiveName}(max: Int) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION`,
        lengthDirectiveTransformer: (schema: GraphQLSchema) =>
            mapSchema(schema, {
                [MapperKind.FIELD]: fieldConfig => {
                    const lengthDirective = getDirective(schema, fieldConfig, directiveName)?.[0]
                    if (lengthDirective) {
                        wrapType(fieldConfig, lengthDirective)
                        return fieldConfig
                    }
                },
            })
    }
}

I know this is a bug of version 3 ApolloServer, but can other way to fix it. Please help me. Thanks a lot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions