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

Custom mutation throws: "Cannot read property 'args' of undefined" #1091

Closed
filipproch opened this issue Sep 15, 2021 · 11 comments · Fixed by #1178
Closed

Custom mutation throws: "Cannot read property 'args' of undefined" #1091

filipproch opened this issue Sep 15, 2021 · 11 comments · Fixed by #1178
Assignees
Labels
type: bug 🐛 Something isn't working

Comments

@filipproch
Copy link
Contributor

Describe the bug
Custom mutation throws the following error.

TypeError: Cannot read property 'args' of undefined
[run:server]     at ValidateCustomFieldsInterceptor.getArgumentMap (/app/node_modules/@vendure/core/src/api/middleware/validate-custom-fields-interceptor.ts:133:45)
[run:server]     at ValidateCustomFieldsInterceptor.intercept (/app/node_modules/@vendure/core/src/api/middleware/validate-custom-fields-interceptor.ts:50:45)
[run:server]     at /app/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:23:36
[run:server]     at Object.handle (/app/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:21:56)
[run:server]     at IdInterceptor.intercept (/app/node_modules/@vendure/core/src/api/middleware/id-interceptor.ts:38:21)
[run:server]     at /app/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:23:36
[run:server]     at InterceptorsConsumer.intercept (/app/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:25:24)
[run:server]     at target (/app/node_modules/@nestjs/core/helpers/external-context-creator.js:76:60)
[run:server]     at processTicksAndRejections (internal/process/task_queues.js:95:5)
[run:server]     at /app/node_modules/@nestjs/core/helpers/external-proxy.js:9:24

To Reproduce
Steps to reproduce the behavior:

Define custom API extension (shop api)

extend type Mutation {
  doSomething: ActiveOrderResult!
}
  1. Invoke this mutation in production
    (shopApiDebug: false in vendure config - doesnt seem to be happening locally when developing)
  2. See error

Expected behavior
No error. Seems this started with migration to Vendure 1.2.0 (worked before)

Environment (please complete the following information):

  • @vendure/core version: 1.2.1
  • Nodejs version: 14.17
  • Database (mysql/postgres etc): postgres
@filipproch filipproch added the type: bug 🐛 Something isn't working label Sep 15, 2021
@filipproch
Copy link
Contributor Author

Well actually it seems to be happening locally as well, maybe some cache or whatever before.

@michaelbromley
Copy link
Member

Thanks for the report. So the only thing you changed was to update versions and then you get the error?

  • Do you get the error every time?
  • Can you share a minimal query that creates the error?
  • What version did you update from that was working?

The error originates from this code:

const inputType = mutationType.getFields()[name];
for (const arg of inputType.args) {
map[arg.name] = this.getInputTypeName(arg.type);
}
}

but that particular method has not changed for about 2 years, so I'm not sure what is going wrong here.

@filipproch
Copy link
Contributor Author

I did some changes to my GraphQL schema - so its probably connected to that rather than the version bump. I will try to debug it further and let you know here.

@filipproch
Copy link
Contributor Author

filipproch commented Sep 15, 2021

So I added some logging but it stopped throwing this error afterwards, its really strange.

Is there a possibility that mutationType.getFields() wouldn't be properly populated and thus cause this error?
I will try to watch for it. So that when it happens next time I have more options to debug it.

EDIT: got it
relevant logs:
https://gist.github.com/filipproch/791066578a1959f430408dc21f1c0bd5

I added logging like this

private getArgumentMap(
    operation: OperationDefinitionNode,
    schema: GraphQLSchema,
): { [inputName: string]: string } {
    const mutationType = schema.getMutationType();
    if (!mutationType) {
        return {};
    }
    const map: { [inputName: string]: string } = {};

    for (const selection of operation.selectionSet.selections) {
        if (selection.kind === 'Field') {
            const name = selection.name.value;
            const inputType = mutationType.getFields()[name];
            Logger.debug(`selection: ${JSON.stringify(selection)}`)
            Logger.debug(`fields: ${JSON.stringify(mutationType.getFields())}`)
            Logger.debug(`name: ${name}`)
            for (const arg of inputType.args) {
                map[arg.name] = this.getInputTypeName(arg.type);
            }
        }
    }
    return map;
}

Also I noticed when it starts happening - it happens for every mutation - not only custom mutations (api-extensions). No idea what exactly is triggering it.
@michaelbromley

@filipproch
Copy link
Contributor Author

filipproch commented Sep 15, 2021

In a good case when it stops happening, the selection variable looks like this
Bad case is in the gist above

Click to expand!
{
[run:server]   kind: 'Field',
[run:server]   alias: undefined,
[run:server]   name: {
[run:server]     kind: 'Name',
[run:server]     value: 'sendContactForm',
[run:server]     loc: { start: 13, end: 28 }
[run:server]   },
[run:server]   arguments: [
[run:server]     {
[run:server]       kind: 'Argument',
[run:server]       name: [Object],
[run:server]       value: [Object],
[run:server]       loc: [Object]
[run:server]     },
[run:server]     {
[run:server]       kind: 'Argument',
[run:server]       name: [Object],
[run:server]       value: [Object],
[run:server]       loc: [Object]
[run:server]     },
[run:server]     {
[run:server]       kind: 'Argument',
[run:server]       name: [Object],
[run:server]       value: [Object],
[run:server]       loc: [Object]
[run:server]     },
[run:server]     {
[run:server]       kind: 'Argument',
[run:server]       name: [Object],
[run:server]       value: [Object],
[run:server]       loc: [Object]
[run:server]     }
[run:server]   ],
[run:server]   directives: [],
[run:server]   selectionSet: {
[run:server]     kind: 'SelectionSet',
[run:server]     selections: [ [Object] ],
[run:server]     loc: { start: 120, end: 140 }
[run:server]   },
[run:server]   loc: { start: 13, end: 140 }
[run:server] }

@filipproch
Copy link
Contributor Author

In both cases, its the same Vendure version (1.2.1) - only I restarted the server and it started to work. Seems like a race condition to me.

@filipproch
Copy link
Contributor Author

filipproch commented Sep 15, 2021

Also looking at the operation object above - seems that in the crash case - the first object in the "selections" is "__typename"
see below the full OperationDefinition object

Click to expand!
{
  "kind": "OperationDefinition",
  "operation": "mutation",
  "variableDefinitions": [
    {
      "kind": "VariableDefinition",
      "variable": {
        "kind": "Variable",
        "name": {
          "kind": "Name",
          "value": "fullName",
          "loc": {
            "start": 11,
            "end": 19
          }
        },
        "loc": {
          "start": 10,
          "end": 19
        }
      },
      "type": {
        "kind": "NonNullType",
        "type": {
          "kind": "NamedType",
          "name": {
            "kind": "Name",
            "value": "String",
            "loc": {
              "start": 21,
              "end": 27
            }
          },
          "loc": {
            "start": 21,
            "end": 27
          }
        },
        "loc": {
          "start": 21,
          "end": 28
        }
      },
      "directives": [],
      "loc": {
        "start": 10,
        "end": 28
      }
    },
    {
      "kind": "VariableDefinition",
      "variable": {
        "kind": "Variable",
        "name": {
          "kind": "Name",
          "value": "email",
          "loc": {
            "start": 31,
            "end": 36
          }
        },
        "loc": {
          "start": 30,
          "end": 36
        }
      },
      "type": {
        "kind": "NonNullType",
        "type": {
          "kind": "NamedType",
          "name": {
            "kind": "Name",
            "value": "String",
            "loc": {
              "start": 38,
              "end": 44
            }
          },
          "loc": {
            "start": 38,
            "end": 44
          }
        },
        "loc": {
          "start": 38,
          "end": 45
        }
      },
      "directives": [],
      "loc": {
        "start": 30,
        "end": 45
      }
    },
    {
      "kind": "VariableDefinition",
      "variable": {
        "kind": "Variable",
        "name": {
          "kind": "Name",
          "value": "topic",
          "loc": {
            "start": 48,
            "end": 53
          }
        },
        "loc": {
          "start": 47,
          "end": 53
        }
      },
      "type": {
        "kind": "NonNullType",
        "type": {
          "kind": "NamedType",
          "name": {
            "kind": "Name",
            "value": "String",
            "loc": {
              "start": 55,
              "end": 61
            }
          },
          "loc": {
            "start": 55,
            "end": 61
          }
        },
        "loc": {
          "start": 55,
          "end": 62
        }
      },
      "directives": [],
      "loc": {
        "start": 47,
        "end": 62
      }
    },
    {
      "kind": "VariableDefinition",
      "variable": {
        "kind": "Variable",
        "name": {
          "kind": "Name",
          "value": "message",
          "loc": {
            "start": 65,
            "end": 72
          }
        },
        "loc": {
          "start": 64,
          "end": 72
        }
      },
      "type": {
        "kind": "NonNullType",
        "type": {
          "kind": "NamedType",
          "name": {
            "kind": "Name",
            "value": "String",
            "loc": {
              "start": 74,
              "end": 80
            }
          },
          "loc": {
            "start": 74,
            "end": 80
          }
        },
        "loc": {
          "start": 74,
          "end": 81
        }
      },
      "directives": [],
      "loc": {
        "start": 64,
        "end": 81
      }
    }
  ],
  "directives": [],
  "selectionSet": {
    "kind": "SelectionSet",
    "selections": [
      {
        "kind": "Field",
        "name": {
          "kind": "Name",
          "value": "__typename",
          "loc": {
            "start": 87,
            "end": 97
          }
        },
        "arguments": [],
        "directives": [],
        "loc": {
          "start": 87,
          "end": 97
        }
      },
      {
        "kind": "Field",
        "alias": {
          "kind": "Name",
          "value": "store_sendContactForm",
          "loc": {
            "start": 100,
            "end": 121
          }
        },
        "name": {
          "kind": "Name",
          "value": "sendContactForm",
          "loc": {
            "start": 123,
            "end": 138
          }
        },
        "arguments": [
          {
            "kind": "Argument",
            "name": {
              "kind": "Name",
              "value": "fullName",
              "loc": {
                "start": 144,
                "end": 152
              }
            },
            "value": {
              "kind": "Variable",
              "name": {
                "kind": "Name",
                "value": "fullName",
                "loc": {
                  "start": 155,
                  "end": 163
                }
              },
              "loc": {
                "start": 154,
                "end": 163
              }
            },
            "loc": {
              "start": 144,
              "end": 163
            }
          },
          {
            "kind": "Argument",
            "name": {
              "kind": "Name",
              "value": "emailAddress",
              "loc": {
                "start": 168,
                "end": 180
              }
            },
            "value": {
              "kind": "Variable",
              "name": {
                "kind": "Name",
                "value": "email",
                "loc": {
                  "start": 183,
                  "end": 188
                }
              },
              "loc": {
                "start": 182,
                "end": 188
              }
            },
            "loc": {
              "start": 168,
              "end": 188
            }
          },
          {
            "kind": "Argument",
            "name": {
              "kind": "Name",
              "value": "topic",
              "loc": {
                "start": 193,
                "end": 198
              }
            },
            "value": {
              "kind": "Variable",
              "name": {
                "kind": "Name",
                "value": "topic",
                "loc": {
                  "start": 201,
                  "end": 206
                }
              },
              "loc": {
                "start": 200,
                "end": 206
              }
            },
            "loc": {
              "start": 193,
              "end": 206
            }
          },
          {
            "kind": "Argument",
            "name": {
              "kind": "Name",
              "value": "message",
              "loc": {
                "start": 211,
                "end": 218
              }
            },
            "value": {
              "kind": "Variable",
              "name": {
                "kind": "Name",
                "value": "message",
                "loc": {
                  "start": 221,
                  "end": 228
                }
              },
              "loc": {
                "start": 220,
                "end": 228
              }
            },
            "loc": {
              "start": 211,
              "end": 228
            }
          }
        ],
        "directives": [],
        "selectionSet": {
          "kind": "SelectionSet",
          "selections": [
            {
              "kind": "Field",
              "name": {
                "kind": "Name",
                "value": "__typename",
                "loc": {
                  "start": 239,
                  "end": 249
                }
              },
              "arguments": [],
              "directives": [],
              "loc": {
                "start": 239,
                "end": 249
              }
            },
            {
              "kind": "InlineFragment",
              "typeCondition": {
                "kind": "NamedType",
                "name": {
                  "kind": "Name",
                  "value": "SendContactFormError",
                  "loc": {
                    "start": 261,
                    "end": 281
                  }
                },
                "loc": {
                  "start": 261,
                  "end": 281
                }
              },
              "directives": [],
              "selectionSet": {
                "kind": "SelectionSet",
                "selections": [
                  {
                    "kind": "Field",
                    "name": {
                      "kind": "Name",
                      "value": "errorCode",
                      "loc": {
                        "start": 290,
                        "end": 299
                      }
                    },
                    "arguments": [],
                    "directives": [],
                    "loc": {
                      "start": 290,
                      "end": 299
                    }
                  },
                  {
                    "kind": "Field",
                    "name": {
                      "kind": "Name",
                      "value": "message",
                      "loc": {
                        "start": 306,
                        "end": 313
                      }
                    },
                    "arguments": [],
                    "directives": [],
                    "loc": {
                      "start": 306,
                      "end": 313
                    }
                  },
                  {
                    "kind": "Field",
                    "name": {
                      "kind": "Name",
                      "value": "__typename",
                      "loc": {
                        "start": 320,
                        "end": 330
                      }
                    },
                    "arguments": [],
                    "directives": [],
                    "loc": {
                      "start": 320,
                      "end": 330
                    }
                  }
                ],
                "loc": {
                  "start": 282,
                  "end": 336
                }
              },
              "loc": {
                "start": 254,
                "end": 336
              }
            },
            {
              "kind": "InlineFragment",
              "typeCondition": {
                "kind": "NamedType",
                "name": {
                  "kind": "Name",
                  "value": "SendContactFormSuccess",
                  "loc": {
                    "start": 348,
                    "end": 370
                  }
                },
                "loc": {
                  "start": 348,
                  "end": 370
                }
              },
              "directives": [],
              "selectionSet": {
                "kind": "SelectionSet",
                "selections": [
                  {
                    "kind": "Field",
                    "name": {
                      "kind": "Name",
                      "value": "sent",
                      "loc": {
                        "start": 379,
                        "end": 383
                      }
                    },
                    "arguments": [],
                    "directives": [],
                    "loc": {
                      "start": 379,
                      "end": 383
                    }
                  },
                  {
                    "kind": "Field",
                    "name": {
                      "kind": "Name",
                      "value": "__typename",
                      "loc": {
                        "start": 390,
                        "end": 400
                      }
                    },
                    "arguments": [],
                    "directives": [],
                    "loc": {
                      "start": 390,
                      "end": 400
                    }
                  }
                ],
                "loc": {
                  "start": 371,
                  "end": 406
                }
              },
              "loc": {
                "start": 341,
                "end": 406
              }
            },
            {
              "kind": "Field",
              "name": {
                "kind": "Name",
                "value": "__typename",
                "loc": {
                  "start": 411,
                  "end": 421
                }
              },
              "arguments": [],
              "directives": [],
              "loc": {
                "start": 411,
                "end": 421
              }
            }
          ],
          "loc": {
            "start": 233,
            "end": 425
          }
        },
        "loc": {
          "start": 100,
          "end": 425
        }
      }
    ],
    "loc": {
      "start": 83,
      "end": 427
    }
  },
  "loc": {
    "start": 0,
    "end": 427
  }
}

@filipproch
Copy link
Contributor Author

filipproch commented Sep 15, 2021

@michaelbromley
I think I finally understood what is causing this. Its the proxy GraphQL which I am using (which is stitching two schemas together to create a single API- using @graphql-tools/stitch) a __typename everywhere.

Why it appeared now? I updated to the latest @graphql-tools version.
Why I thought it appears randomly? I was testing it sometimes on the website, sometimes directly against the endpoint.

The solution could be to add check in the for loop, together with selection.kind === 'Field' to filter __typename out.

Should I submit a PR for this?

PS: sorry for spamming this thread - wanted to share the progress

@michaelbromley
Copy link
Member

Thanks for the updates. Not spamming at all! It is good to document the process, it helps me follow your line of investigation.

Yes, a PR for this would be welcome. If you are able to add an e2e test for this (would go here) which fails before your fix and passes afterwards, that would be amazing. I'm happy to provide guidance on this part if you have any difficulty.

@filipproch
Copy link
Contributor Author

@michaelbromley Thanks, will look into it and submit PR this week

filipproch added a commit to Coconut-Works/vendure that referenced this issue Oct 20, 2021
filipproch added a commit to Coconut-Works/vendure that referenced this issue Oct 20, 2021
@filipproch filipproch mentioned this issue Oct 20, 2021
@filipproch
Copy link
Contributor Author

@michaelbromley sorry that took longer to get to it, submitted PR with a fix and a test case
looking forward to get it merged and released 🤩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug 🐛 Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants