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

PruneSchema does not prune unused implementations of a returned interface #4819

Closed
2 of 4 tasks
Tracked by #5201 ...
NullScope opened this issue Nov 3, 2022 · 9 comments
Closed
2 of 4 tasks
Tracked by #5201 ...

Comments

@NullScope
Copy link
Contributor

NullScope commented Nov 3, 2022

Issue workflow progress

Progress of the issue based on the Contributor Workflow

  • 1. The issue provides a reproduction available on Github, Stackblitz or CodeSandbox

    Make sure to fork this template and run yarn generate in the terminal.

    Please make sure the GraphQL Tools package versions under package.json matches yours.

  • 2. A failing test has been provided
  • 3. A local solution has been provided
  • 4. A pull request is pending review

Describe the bug

The PruneSchema utility function, and consequently, the PruneSchema transform, does not prune unused types that implement interfaces that are returned from an operation or used as field types.

To Reproduce
Steps to reproduce the behavior:

Here is a unit test for prune.test.ts:

test('removes unused implementations of a returned interface', () => {
  const schema = buildSchema(/* GraphQL */ `
    type Query {
      operation: SomeType
      anotherOperation: SomeInterface
    }

    interface SomeInterface {
      field: String
    }

    type SomeType implements SomeInterface {
      field: String
    }

    type ShouldPrune implements SomeInterface {
      field: String
    }
  `);

  const result = pruneSchema(schema);

  expect(result.getType('ShouldPrune')).toBeUndefined();
});

Expected behavior

Unused types should be pruned.

Environment:

  • OS: macOS 12.5.1
  • @graphql-tools/utils: 9.0.1
  • NodeJS: v16.15.0

Additional context
This is very important for this project, as the schema that is being transformed and then pruned has literally thousands of types, most of which (around 60%) is unused

@NullScope
Copy link
Contributor Author

As for a solution, that's a tough one. The culprit of this issue is this line in prune.ts: https://github.com/ardatan/graphql-tools/blob/master/packages/utils/src/prune.ts#L139

This was introduced in #4442, however, I am not sure why that line exists at all. Could someone please provide an explanation for it? Can't really understand if from the comment alone.

@ardatan
Copy link
Owner

ardatan commented Nov 3, 2022

What if anotherOperation returns ShouldPrune, then your app would break. I don't think this is a bug.

@NullScope
Copy link
Contributor Author

NullScope commented Nov 3, 2022

Maybe its harder to understand as a simple example like this, but in our real world usage, the type is indeed never used, but does not get pruned, let me find a better example perhaps

@NullScope
Copy link
Contributor Author

NullScope commented Nov 3, 2022

Let's imagine the following original schema:

type Query {
  getCart: Cart
  getExtensions: [Extension!]!
}

type Cart implements Versioned {
  price: Int!
  id: String!
  version: Int!
  created_at: String!
  last_modified_at: String!
  created_by: ID!
  last_modified_by: ID!
}

type Extension implements Versioned {
  name: String!
  id: String!
  version: Int!
  created_at: String!
  last_modified_at: String!
  created_by: ID!
  last_modified_by: ID!
}

interface Versioned {
  id: String!
  version: Int!
  created_at: String!
  last_modified_at: String!
  created_by: ID!
  last_modified_by: ID!
}

now lets imagine that I only want the query getCart from the schema and filter any other unrelated query, and want to prune it to remove any dangling types that result from that, but because of that line it will check who implements Versioned and keep the type Extension because it implements it.

I now understand that in some cases, it could lead to breaking the app, thank you, but in this case it would never happen and it is not desirable to filter the unwanted types as there are literally thousands of them, maybe its not a bug and maybe a feature request? Hopefully you can understand, thank you.

@NullScope
Copy link
Contributor Author

NullScope commented Nov 3, 2022

maybe one way of doing this would be to have an option called something like skipImplementedTypes which adds another condition to the revisit[namedType.name] = true; section? This would be a feature request if so

@ardatan
Copy link
Owner

ardatan commented Nov 3, 2022

This is not the same. If a field returns an interface, that means it can return any type implementing that interface so if we prune a type that is not referenced anywhere else, this would break.

@NullScope
Copy link
Contributor Author

NullScope commented Nov 3, 2022

Ah got it, that makes sense thank you, would it then be possible to achieve the example above using something else or do we have to filter these unwanted types manually?

@ardatan
Copy link
Owner

ardatan commented Mar 29, 2023

@NullScope Sorry for the late response but my answer is yes. Unwanted types should be filtered manually.

@ardatan ardatan closed this as completed Mar 29, 2023
@NullScope
Copy link
Contributor Author

No problem, thank you for the reply

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

No branches or pull requests

2 participants