graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Different enum treatment in Federation Module

Open Boshen opened this issue 5 years ago • 8 comments

I'm submitting a...


[ ] Regression 
[x] Bug report
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

repro: https://github.com/Boshen/nestjs-playground

This is going to be hard one ... but here we go,

Current behavior

With GraphQLFederationModule, https://github.com/Boshen/nestjs-playground/blob/master/src/app.module.ts#L18-L20

   GraphQLFederationModule.forRoot({
      autoSchemaFile: true,
    }),

and this reduced introspection query (reduced from when you try to get the schema):

query IntrospectionQuery {
  __schema {
    types {
      name
      inputFields {
        name
        defaultValue # <-- this guy
      }
    }
  }
}

the enum type gets treated differently in the defaultValue field of an input field https://github.com/Boshen/nestjs-playground/blob/master/src/author.resolver.ts

enum AuthorType {
  XXX, 
  YYY, 
}

registerEnumType(AuthorType, {
  name: 'AuthorType',
})

@InputType()
class AuthorInput {
  @Field()
  name!: string

  @Field(() => AuthorType)
  authorType: AuthorType = AuthorType.XXX // <---- this guy here
}

By comparing the output from the following error in graphql source code:

"message": "Enum \"AuthorType\" cannot represent value: \"XXX\"",
                    "stacktrace": [
                        "GraphQLError: Enum \"AuthorType\" cannot represent value: \"XXX\"",
                        "    at GraphQLEnumType.serialize (/Users/boshen/github/nestjs-playground/node_modules/graphql/type/definition.js:1006:13)",

node_modules/graphql/type/definition.js:1006 source:

  _proto5.serialize = function serialize(outputValue) {
    var enumValue = this._valueLookup.get(outputValue);

    if (enumValue === undefined) {
      throw new _GraphQLError.GraphQLError("Enum \"".concat(this.name, "\" cannot represent value: ").concat((0, _inspect.default)(outputValue)));
    }

    return enumValue.name;
  };

GraphQLFederationModule outputs the _valueLookup Map as

Map(2) {
  0 => { 
    name: 'XXX',
    description: undefined,
    value: 0,
    isDeprecated: false,
    deprecationReason: undefined,
    extensions: undefined,
    astNode: undefined
  },
  1 => {
    name: 'YYY',
    description: undefined,
    value: 1,
    isDeprecated: false,
    deprecationReason: undefined,
    extensions: undefined,
    astNode: undefined
  }

But in the normal graphql module, it outputs 'XXX', 'YYY' as the Map keys.

I think this has something to do with GraphQLFederationModule parsing the values during request, but normal GraphQLModule parses during application start time?

The easy fix is obviously:

enum AuthorType {
  XXX = 'XXX',
  YYY = 'YYY'
}

But two hours wasted cracking this ;-)

Boshen avatar Jan 19 '21 10:01 Boshen

Would you like to create a PR for this issue?

kamilmysliwiec avatar Feb 09 '21 13:02 kamilmysliwiec

Would you like to create a PR for this issue?

This is way too complicated for me, I couldn't find the root cause ...

Boshen avatar Feb 15 '21 03:02 Boshen

i just encountered this with federation as well. this error does not present itself if you are not using federation

stringbeans avatar Aug 22 '22 21:08 stringbeans

Hi there,

I noticed this discussion about the error handling issues with GraphQL in NestJS, and I'm facing the same problem. I've tried some of the suggestions mentioned here, such as using a middleware or the "debug" option, but I still can't seem to get meaningful error messages when a GraphQL query fails.

I'm wondering if anyone has found a solution or workaround for this issue since this discussion was last updated. Any insights or suggestions would be greatly appreciated.

Thanks in advance!

maltyxx avatar May 11 '23 00:05 maltyxx

I dug into this issue and found that it's due to the federated schema factory using printSubgraphSchema from @apollo/subraph, while the default schema factory uses makeExecutableSchema from graphql-tools. makeExecutableSchema takes a resolvers parameter which means it can make the correct graphql enum default value, while printSubgraphSchema does not take a resolvers param so it doesn't behave correctly here. To fix the problem, Apollo will need to add a resolvers parameter to their printSubgraphSchema command, or nestjs will have to do a band-aid on the output of that function to correct the enum values.

kbrooks avatar Sep 13 '23 19:09 kbrooks

Hey @kbrooks - it sounds like you might have a better grasp on the issue than I do. I'm trying to find the discrepancy but not sure I'm looking in the right place. My attempt to reproduce shows the same result (for the original introspection query) w.r.t. the field on the input type and its defaultValue (vs. a schema build using buildSchema from graphql-js). https://github.com/apollographql/federation/compare/trevor/print-input-field-default-issue?expand=1

What can I add to this test case to demonstrate the issue? Do I need resolvers in order to resolve the MyEnum.A value to something that isn't "A" perhaps?

trevor-scheer avatar Sep 14 '23 00:09 trevor-scheer

Here is a patch you can apply which creates two test cases you can use to debug the issue repro.patch

The reason I believe it is related to resolvers is that the resolvers contain the type information to map the graphql enum value to the TypeScript one. So if your nest.js ts enum looks like this

enum SpiceLevel {
  ZERO
  ONE
  TWO
  THREE
}

Then the underlying values are going to be 0,1,2,3 and not "ZERO", "ONE" etc.

kbrooks avatar Sep 15 '23 17:09 kbrooks

@kbrooks got it. Opened an issue on our repo for it. I'm not sure when we we'll be able to address this, but happy to review a PR if you're feeling motivated or in a hurry to get this resolved.

trevor-scheer avatar Sep 15 '23 21:09 trevor-scheer