Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Types used in abstract resolvers are missing after the first E2E-test (code first) #2657

Open
2 tasks done
Lennard-Dietz opened this issue Feb 16, 2023 · 2 comments
Open
2 tasks done

Comments

@Lennard-Dietz
Copy link

Did you read the migration guide?

  • I have read the whole migration guide

Is there an existing issue that is already proposing this?

  • I have searched the existing issues

Potential Commit/PR that introduced the regression

No response

Versions

9.1.1 -> 10.2.0

Describe the regression

After Upgrading the nestjs packages in our repo, we had a bunch of failing tests. Upon further inspection, we found that all of our E2E tests, which are spawning a test nest application to run some gql-queries against it, were failing, if the related resolver was created by inheritance of another abstract resolver.

We are using the following additional packages:

  • jasmine
  • apollo

Minimum reproduction code

import { gql } from "@apollo/client/core";
import { ApolloDriver, ApolloDriverConfig } from "@nestjs/apollo";
import { INestApplication, Module } from "@nestjs/common";
import { Args, Field, GraphQLModule, InputType, Int, Mutation, Query, Resolver } from "@nestjs/graphql";
import { Test, TestingModule } from "@nestjs/testing";
import { ApolloServerBase } from "apollo-server-core";

interface CurrentThisContext {
  module: TestingModule;
  app: INestApplication;
  apolloServer: ApolloServerBase;
}

@InputType()
class MyArgs {
  @Field()
  public myInput: number;
}

@Resolver({ isAbstract: true })
abstract class MyBaseResolver {
  @Query(returns => Int)
  public myQuery(): number {
    return 10;
  }

  @Mutation(returns => Int)
  public myMutation(@Args({ type: () => MyArgs, name: "input" }) args: MyArgs): number {
    return args.myInput;
  }
}

@Resolver()
class MyResolver extends MyBaseResolver {}

@Module({
  imports: [
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: true,
    }),
  ],
  providers: [MyResolver],
})
class MyModule {}

describe("MyIssue", function () {
  beforeEach(async function (this: CurrentThisContext) {
    const moduleRef = await Test.createTestingModule({ imports: [MyModule] });
    this.module = await moduleRef.compile();
    this.app = this.module.createNestApplication();
    await this.app.init();

    // Currently our "way" to run some gql-queries in the specs. There may be a better one, but this should be sufficient for the issue
    const graphqlModule: GraphQLModule = this.module.get<GraphQLModule<ApolloDriver>>(GraphQLModule);
    this.apolloServer = graphqlModule.graphQlAdapter["_apolloServer"];
  });

  afterEach(async function (this: CurrentThisContext) {
    await this.app.close();
  });

  function createMyTest() {
    it("myTest", async function (this: CurrentThisContext) {
      const mutation = gql`
        mutation MyTest {
          myMutation(input: { myInput: 5 })
        }
      `;
      const result = await this.apolloServer.executeOperation({ query: mutation });
      expect(result?.data?.myMutation).toEqual(5);
    });
  }

  // The first test that gets executed is successful, the second one fails with message 'Unknown argument "input" on field "Mutation.myMutation".'
  createMyTest();
  createMyTest();
});

After some deep digging it seems that the type-metadata.storage removes the methodArgs for the second run when compiling the schema. Example:
Mutation metadata in the first run

{
  methodName: 'myMutation',
  schemaName: 'myMutation',
  target: [class MyResolver extends MyBaseResolver],
  typeFn: [Function (anonymous)],
  returnTypeOptions: {},
  description: undefined,
  deprecationReason: undefined,
  complexity: undefined,
  classMetadata: {
    target: [class MyResolver extends MyBaseResolver],
    typeFn: [Function (anonymous)],
    isAbstract: false
  },
  methodArgs: [
    {
      kind: 'arg',
      name: 'input',
      description: undefined,
      target: [class MyBaseResolver],
      methodName: 'myMutation',
      typeFn: [Function (anonymous)],
      index: 0,
      options: [Object]
    }
  ],
  directives: [],
  extensions: {}
}

Mutation metadata in the second run:

{
  methodName: 'myMutation',
  schemaName: 'myMutation',
  target: [class MyResolver extends MyBaseResolver],
  typeFn: [Function (anonymous)],
  returnTypeOptions: {},
  description: undefined,
  deprecationReason: undefined,
  complexity: undefined,
  classMetadata: {
    target: [class MyResolver extends MyBaseResolver],
    typeFn: [Function (anonymous)],
    isAbstract: false
  },
  methodArgs: [],
  directives: [],
  extensions: {}
}

Expected behavior

We expected this to work just like before.

Other

No response

@kamilmysliwiec
Copy link
Member

Please provide a minimum reproduction repository (Git repository/StackBlitz/CodeSandbox project).

@Lennard-Dietz
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants