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

feat: override operation field types via directives #8071

Conversation

izumin5210
Copy link
Contributor

@izumin5210 izumin5210 commented Jul 9, 2022

Description

Add directiveFieldMappings option to typescript-operations plugin to overriding operation field types via directives.

Related #7356

I fixed @jenseng's implementation to follow master and pass tests.

Example

# codegen.yml
plugins:
  config:
    # You cannot control optional(`?`) with `directiveFieldMapping`.
    # Setting `avoidOptionals` is recommended when defining directives that change nullability, such as `@nonNull` or `@required`.
    avoidOptionals:
      field: true  #
    directiveFieldMappings:
      asString: AsStringTransform
      nonNull: NonNullable
      nonNullEntries:
        type: NonNullable
        entries: true
# GraphQL Schema
directive @asString on FIELD
directive @nonNull on FIELD
directive @nonNullEntries on FIELD
                                         
type User {
  id: Int
  username: String
  emails: [String]
}
                                         
...
# GrpahQL operation document
query {
  me {
    id @asString @nonNull
    username @nonNull
    emails @nonNullEntries
  }
}
// generated type
{
  me: {
    id: AsStringTransform<number>,
    username: NonNullable<string | null>,
    emails: Array<NonNullable<string | null>> | null,
  }  
}

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • I have followed the CONTRIBUTING doc and the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Further comments

  • directive argument support is not included in the first release
    • e.g. @required(entries: true), @as(type: "boolean")
    • Because:
      • the need to discuss the optimal configuration file design
      • even if arguments are not supported, the minimum use case can be satisfied
      • it can be implement later without breaking changes

Similar to DirectiveArgumentAndInputFieldMappings, add the ability to override
the final operation types/nullability via field-level directives.

The rationale behind this change is to allow clients to adjust the generated
types/nullability within queries/fragments to match a corresponding server-side
transformation (e.g. we have a @nonnull FIELD directive that causes our server
to treat the field as required, this way we can ensure the types match)

Previous discussion: dotansimha#5676
Further reading: https://github.com/graphql/graphql-wg/blob/main/rfcs/ClientControlledNullability.md#a-nonnull-custom-directive

Co-authored-by: Jon Jensen <[email protected]>
@changeset-bot
Copy link

changeset-bot bot commented Jul 9, 2022

🦋 Changeset detected

Latest commit: eea74b2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 39 packages
Name Type
@graphql-codegen/visitor-plugin-common Minor
@graphql-codegen/typescript-operations Minor
@graphql-codegen/flow Patch
@graphql-codegen/flow-operations Patch
@graphql-codegen/flow-resolvers Patch
@graphql-codegen/java-apollo-android Patch
@graphql-codegen/java-common Patch
@graphql-codegen/java Patch
@graphql-codegen/kotlin Patch
@graphql-codegen/java-resolvers Patch
@graphql-codegen/typescript-apollo-angular Patch
@graphql-codegen/typescript-apollo-client-helpers Patch
@graphql-codegen/typescript-document-nodes Patch
@graphql-codegen/typescript-generic-sdk Patch
@graphql-codegen/gql-tag-operations Patch
@graphql-codegen/typescript-graphql-request Patch
@graphql-codegen/typescript-jit-sdk Patch
@graphql-codegen/typescript-mongodb Patch
@graphql-codegen/typescript-msw Patch
@graphql-codegen/typescript-oclif Patch
@graphql-codegen/typescript-react-offix Patch
@graphql-codegen/typescript-react-apollo Patch
@graphql-codegen/typescript-react-query Patch
@graphql-codegen/typescript-resolvers Patch
@graphql-codegen/typescript-rtk-query Patch
@graphql-codegen/typescript-stencil-apollo Patch
@graphql-codegen/typescript-type-graphql Patch
@graphql-codegen/typed-document-node Patch
@graphql-codegen/typescript Patch
@graphql-codegen/typescript-urql-graphcache Patch
@graphql-codegen/urql-svelte-operations-store Patch
@graphql-codegen/typescript-urql Patch
@graphql-codegen/typescript-vue-apollo-smart-ops Patch
@graphql-codegen/typescript-vue-apollo Patch
@graphql-codegen/typescript-vue-urql Patch
@graphql-codegen/jsdoc Patch
@graphql-codegen/graphql-modules-preset Patch
@graphql-codegen/import-types-preset Patch
@graphql-codegen/near-operation-file-preset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Jul 9, 2022

@izumin5210 is attempting to deploy a commit to the The Guild Team on Vercel.

A member of the Team first needs to authorize it.

@n1ru4l
Copy link
Collaborator

n1ru4l commented Jul 12, 2022

I wonder whether it instead would make sense that the field types are wrapped within a generic.
That would allow more flexibility as well as allow chain ability of directives.

plugins:
  config:
    directiveFieldMappings:
      asString: type AsStringTransform<_TIn> = string
      nonNull: type NonNullTransform<TIn> = Exclude<TIn, null | undefined>
      asBoolean: type AsBooleanTransform<_TIn> = boolean
query MeQuery {
	me {
		id @asBoolean @asString
	}
}
type AsBooleanTransform<_TIn> = boolean

type Me = {
	__typename?: 'User';
	id: AsString<AsBooleanTransform<boolean>>;
};

@izumin5210
Copy link
Contributor Author

Sorry for the delay in replying 🙇

I wonder whether it instead would make sense that the field types are wrapped within a generic.
That would allow more flexibility as well as allow chain ability of directives.

I too think that is a great idea! I will update this pull request or create a new one!

@izumin5210 izumin5210 marked this pull request as draft July 31, 2022 07:21
@izumin5210 izumin5210 changed the title feat: override operation field types/nullability via directives feat: override operation field types via directives Jul 31, 2022
@izumin5210 izumin5210 marked this pull request as ready for review July 31, 2022 12:22
@izumin5210
Copy link
Contributor Author

izumin5210 commented Jul 31, 2022

I updated the codes and PR description! could you review them? 🙏

@charlypoly charlypoly requested a review from saihaj August 1, 2022 12:22
@charlypoly charlypoly self-assigned this Aug 4, 2022
@charlypoly charlypoly removed their assignment Nov 22, 2022
@charlypoly charlypoly removed their request for review November 22, 2022 10:05
@n1ru4l
Copy link
Collaborator

n1ru4l commented Mar 16, 2023

We talked about this a lot and came to the conclusion that this is an anti-pattern. If you want special handling for a field you should use a custom scalar instead. See #1532

I am going to close this.

@n1ru4l n1ru4l closed this Mar 16, 2023
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.

3 participants