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

Avoid optionals on fields influences variables #4788

Open
Tracked by #8296 ...
simPod opened this issue Sep 24, 2020 · 7 comments
Open
Tracked by #8296 ...

Avoid optionals on fields influences variables #4788

simPod opened this issue Sep 24, 2020 · 7 comments
Labels
core Related to codegen core/cli plugins stage/1-reproduction A reproduction exists

Comments

@simPod
Copy link
Contributor

simPod commented Sep 24, 2020

Describe the bug

Even though avoidOptionals.inputValue is false, variable types are generated without optionals.

To Reproduce

  1. My GraphQL schema:
type Query {
    a(filter: [A!]): String
}
  1. My GraphQL operations:
query a($filter: [A!]) {
    a(filter: $filter)
}
  1. My codegen.yml config file:
avoidOptionals:
  field: true
  object: true
export type AQueryVariables = Exact<{
  filter: Maybe<Array<A>>;
}>;

https://codesandbox.io/s/stupefied-paper-vjrm1?file=/types.ts

Expected behavior

It's still part of my InputType so this is expected

export type AQueryVariables = Exact<{
  filter?: Maybe<Array<A>>;
}>;

Environment:

  • OS:
    "@graphql-codegen/add": "^2.0.1",
    "@graphql-codegen/cli": "^1.17.8",
    "@graphql-codegen/fragment-matcher": "^1.17.8",
    "@graphql-codegen/typescript": "^1.17.9",
    "@graphql-codegen/typescript-operations": "^1.17.8",
    "@graphql-codegen/typescript-react-apollo": "^2.0.6",
  • NodeJS: 14
@simPod
Copy link
Contributor Author

simPod commented Feb 9, 2021

@dotansimha where this could be possible fixed? Can look at it but have no idea to start honestly.

@Rio6
Copy link

Rio6 commented Feb 9, 2021

Looks like it's the problem with typescript-resolvers plugin. It has this function:

    formatRootResolver(schemaTypeName, resolverType, declarationKind) {
        return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`;
    }

Which assumes avoidOptionals to be boolean type only. I made this simple patch to test it

diff --git a/node_modules/@graphql-codegen/typescript-resolvers/index.cjs.js b/node_modules/@graphql-codegen/typescript-resolvers/index.cjs.js
index a08c96d..60fb56c 100644
--- a/node_modules/@graphql-codegen/typescript-resolvers/index.cjs.js
+++ b/node_modules/@graphql-codegen/typescript-resolvers/index.cjs.js
@@ -38,7 +38,7 @@ class TypeScriptResolversVisitor extends visitorPluginCommon.BaseResolversVisito
         return `ParentType extends ${parentType} = ${parentType}`;
     }
     formatRootResolver(schemaTypeName, resolverType, declarationKind) {
-        return `${schemaTypeName}${this.config.avoidOptionals ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`;
+        return `${schemaTypeName}${this.config.avoidOptionals && this.config.avoidOptionals.resolver ? '' : '?'}: ${resolverType}${this.getPunctuation(declarationKind)}`;
     }
     clearOptional(str) {
         if (str.startsWith('Maybe')) {

And it looks like that was it.

EDIT: nevermind, I was having a different issue than yours.

@simPod
Copy link
Contributor Author

simPod commented Apr 19, 2021

So the code that controls the behaviour starts here:

const hasDefaultValue = variable.defaultValue != null && typeof variable.defaultValue !== 'undefined';
const isNonNullType = variable.type.kind === Kind.NON_NULL_TYPE;
const formattedFieldString = this.formatFieldString(fieldName, isNonNullType, hasDefaultValue);

Not sure if it needs some change but adding a variable default value helps in my case

query a($filter: [A!] = null) {
    a(filter: $filter)
}

https://codesandbox.io/s/pensive-shaw-fw7tf?file=/types.ts

@Urigo Urigo added the stage/1-reproduction A reproduction exists label Apr 20, 2021
@Urigo
Copy link
Collaborator

Urigo commented Apr 20, 2021

I'm not adding a lot here but I'm just labeling it according to our new Contribution Guide and issue flow.

It looks like you already made it into stage 1 thanks to your reproduction, thank you for that!

Would be great if someone could help progress the issues through the stages

In order to move it to the next step, do you think you could create a failing test with the same configuration?
Also check why there isn't already one.
Probably check the tests here: https://github.com/dotansimha/graphql-code-generator/blob/f0b5ea53af313e95d59cf2b0e01ee6b048fc141a/packages/plugins/typescript/operations/tests/ts-documents.spec.ts

Thank you and sorry that this comment is not a complete solution (yet)

@simPod
Copy link
Contributor Author

simPod commented Apr 20, 2021

@Urigo thanks. I'might have the test in my local history.

But I wonder if it's something that should be fixed. Like I don't have such knowledge of GQL spec so I can tell.

I can achieve the proper behaviour by adding a default value. Should input fields be marked as optional if they have no default value but are nullable? If so, I'll provide the test.

@neiker
Copy link

neiker commented Aug 13, 2021

In my case the solution was to set object to false.

  avoidOptionals: 
    field: true 
    object: false
    inputValue: false
    defaultValue: false

@charlypoly charlypoly added the core Related to codegen core/cli label Nov 3, 2022
@laurisvan
Copy link

I think we've got plenty of data here, but just wanted to confirm that input types incorrectly get their optionality removed, even with the following settings:

      avoidOptionals:
        field: true
        inputValue: false
        object: false
        defaultValue: false
 export type AcceptFeedback = {
   __typename?: 'AcceptFeedback';
-  note?: Maybe<Scalars['String']>;
-  npsScore?: Maybe<Scalars['Int']>;
+  note: Maybe<Scalars['String']>;
+  npsScore: Maybe<Scalars['Int']>;
 };
 
 export type AcceptFeedbackInput = {
@@ -56,16 +56,16 @@ export type AcceptFeedbackInput = {
 export type Account = {
   __typename?: 'Account';
   createdAt: Scalars['DateTime'];
-  deletedAt?: Maybe<Scalars['DateTime']>;
+  deletedAt: Maybe<Scalars['DateTime']>;
   id: Scalars['ID'];
   /** Login username */
   login: Scalars['String'];
   /** Phone number for sms login */
-  phone?: Maybe<Scalars['String']>;
+  phone: Maybe<Scalars['String']>;
   /** Generated, long-lived JWT access token for refreshing the actual token */
-  refreshToken?: Maybe<Scalars['String']>;
+  refreshToken: Maybe<Scalars['String']>;
   /** Generated JWT access token - not part of the data model */
-  token?: Maybe<Scalars['String']>;
+  token: Maybe<Scalars['String']>;
   updatedAt: Scalars['DateTime'];
   user: User;
 };

This feels like an issue that I would like to try fixing. I have not contributed to this project yet, so @dotansimha @charlypoly please hint me if this is an issue that is beyond a "simple first issue" to work on for a person that does know JavaScript and large projects fairly well but does not have a clue on the internals of this project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Related to codegen core/cli plugins stage/1-reproduction A reproduction exists
Projects
None yet
Development

No branches or pull requests

7 participants