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

Transforms for normalizing stitched subschema deprecations #1925

Merged
merged 30 commits into from Aug 24, 2020

Conversation

gmac
Copy link
Contributor

@gmac gmac commented Aug 19, 2020

Adds package support and a documented approach for handling Federation-style stitching patterns that result in field oddities outside of the gateway context. While deprecation is an imperfect solution, it seems pretty reasonable as an approach. Full explanation in updated docs:

One disadvantage of this pattern is that we end up with clutter—buyerId/sellerId are extra fields, and buyer/seller fields have gateway dependencies. To tidy things up, we can aggressively deprecate these fields in subschemas and then remove/normalize their behavior in the gateway using available transforms:

import { RemoveFieldsWithDirective, RemoveFieldDirectives } from '@graphql-tools/stitch';

const listingsSchema = makeExecutableSchema({
  typeDefs: `
    type Listing {
      id: ID!
      description: String!
      price: Float!
      sellerId: ID! @deprecated(reason: "stitching use only")
      buyerId: ID  @deprecated(reason: "stitching use only")
    }
  `
});

const usersSchema = makeExecutableSchema({
  typeDefs: `
    type User {
      id: ID!
      email: String!
    }
    type Listing {
      seller: User! @deprecated(reason: "gateway access only")
      buyer: User @deprecated(reason: "gateway access only")
    }
  `
});

const gatewaySchema = stitchSchemas({
  subschemas: [
    {
      schema: listingsSchema,
      transforms: [new RemoveFieldsWithDirective('deprecated', { reason: 'stitching use only' })],
      merge: { ... }
    },
    {
      schema: usersSchema,
      transforms: [new RemoveFieldDirectives('deprecated', { reason: 'gateway access only' })],
      merge: { ... }
    },
  ],
});

@yaacovCR

TODO:

  • If this PR is a new feature, reference an issue where a consensus about the design was reached (not necessary for small changes)
  • Make sure all of the significant new logic is covered by tests
  • Rebase your changes on master so that they can be merged easily
  • Make sure all tests and linter rules pass

@gmac gmac changed the title [deploy_website] add transforms for normalizing subschema deprecations. Transforms for normalizing stitched subschema deprecations Aug 19, 2020
@gmac
Copy link
Contributor Author

gmac commented Aug 19, 2020

Canary is still failing... otherwise, I think this is ready for another look. I just went fully generic throughout the entire transformation chain.

@gmac
Copy link
Contributor Author

gmac commented Aug 20, 2020

Oh, question: is stitch the best package for these or is there a strong case for keep them generic in wrap? I’m on the fence. The remove-only behavior feels extremely specific to this stitching usecase. But, wrap is certainly more conventional.

@gmac gmac requested a review from yaacovCR August 20, 2020 12:28
Copy link
Collaborator

@yaacovCR yaacovCR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great!

Currently it does a strict equal comparison which will fail for input object types...

I do wonder if it would be better if the match directive function took the schema so that it could properly parse even custom scalars or whatever types that the directive arguments throw at it.

This could be easily implemented with getDirectives

https://github.com/ardatan/graphql-tools/blob/master/packages/utils/src/get-directives.ts#L57

Maybe two versions a la valueFromASTUntyped and valueFromAST from graphql-js?

Thoughts?

@gmac
Copy link
Contributor Author

gmac commented Aug 20, 2020

One note: these new transforms don’t touch input object fields. I can't think of a practical scenario where inputs should be groomed in this manner as they’re not a direct concern of the readable schema; also they don't support deprecation. Does that seem reasonable?

@gmac
Copy link
Contributor Author

gmac commented Aug 21, 2020

Ooooooohh... I get it now. The AST is all strings, so getDirectives casts them per the typeDef. Thanks! I'm learning a lot about GraphQL internals on this journey.

@gmac gmac requested a review from yaacovCR August 21, 2020 03:10
Copy link
Collaborator

@yaacovCR yaacovCR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests for the transforms would also be helpful.

packages/utils/src/matchDirectiveValue.ts Outdated Show resolved Hide resolved
packages/wrap/src/transforms/FilterFieldDirectives.ts Outdated Show resolved Hide resolved
* handle nested input object fields in directive arguments

* handle repeatable directives

* needs tests for transforms
@yaacovCR
Copy link
Collaborator

This is what I had in mind:

gmac#1

@gmac
Copy link
Contributor Author

gmac commented Aug 22, 2020

lots more tests are in.

packages/wrap/src/transforms/FilterFieldDirectives.ts Outdated Show resolved Hide resolved
packages/stitch/src/index.ts Outdated Show resolved Hide resolved
@gmac
Copy link
Contributor Author

gmac commented Aug 23, 2020

OKAY – another big retooling. There are now 5 new transforms in the wrap package...

  • FilterFieldDirectives
  • RemoveFieldDirectives
  • RemoveFieldsWithDirective
  • RemoveFieldDeprecations
  • RemoveFieldsWithDeprecation

Only the Deprecation transform flavors mess around with deprecationReason now (in addition to also filtering out @deprecated attributes). Seems like that covers the bases? Also, I've updated all of these transforms to accept string|RegExp inputs so that directive names/args can be pattern matched.

@gmac gmac requested a review from yaacovCR August 23, 2020 18:20
@yaacovCR
Copy link
Collaborator

Wow!

I guess the only last nit is that these transforms only work on object fields and not input object fields, so maybe they should have ObjectField in the name like FilterObjectFieldDirectives, etc

Pretty long name, but descriptive, what do you think?

And DS Store ended up in diff...

Really looks great!!!!

@gmac
Copy link
Contributor Author

gmac commented Aug 24, 2020

.DS_Store is the worst. Fixed, along with naming.

@yaacovCR yaacovCR merged commit 5ba1fc2 into ardatan:master Aug 24, 2020
@theguild-bot
Copy link
Collaborator

The latest changes of this PR are available as alpha in npm: 6.0.19-alpha-5ba1fc22.0

Quickly update your package.json by running:

npx match-version @graphql-tools 6.0.19-alpha-5ba1fc22.0

@gmac gmac deleted the gm-stitch-deprecations branch January 30, 2021 03:21
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

Successfully merging this pull request may close these issues.

None yet

3 participants