confuser / graphql-constraint-directive

Validate GraphQL fields
ISC License
569 stars 74 forks source link

Cannot read properties of undefined (reading 'args') when schema has a custom directive with argument #224

Closed arbejd closed 8 months ago

arbejd commented 8 months ago

Hi! Thank you for a nice package!

I was adding a custom directive with an argument to my apollo server but validation seems mistake this for one of the @constraint directives.

Reproduction files at the bottom. TLDR: Add a QUERY directive with an argument and try to pass that.

{
  "data": {},
  "errors": [
    {
      "message": "Cannot read properties of undefined (reading 'args')",
      "extensions": {
        "code": "INTERNAL_SERVER_ERROR",
        "exception": {
          "stacktrace": [
            "TypeError: Cannot read properties of undefined (reading 'args')",
            "    at QueryValidationVisitor.onArgumentEnter ( (...) /node_modules/graphql-constraint-directive/lib/query-validation-visitor.js:118:46)",
            "    at Object.enter ((...) //node_modules/graphql/utilities/TypeInfo.js:391:27)",
            "    at visit ((...) /node_modules/graphql/language/visitor.js:197:21)",
            "    at validateQuery ((...) /node_modules/graphql-constraint-directive/index.js:193:3)",
            "    at Object.didResolveOperation ((...) /node_modules/graphql-constraint-directive/index.js:207:26)",
            "    at /(...) /node_modules/apollo-server-core/dist/utils/dispatcher.js:12:31",
            "    at Array.map (<anonymous>)",
            "    at Dispatcher.callTargets ((...) /apollo-server-core/dist/utils/dispatcher.js:9:29)",
            "    at Dispatcher.invokeHook ((...) /node_modules/apollo-server-core/dist/utils/dispatcher.js:20:33)",
            "    at processGraphQLRequest ((...) /node_modules/apollo-server-core/dist/requestPipeline.js:116:26)"
          ]
        }
      }
    }
  ]
} 

Reproduction

index.ts

import { ApolloServer } from "apollo-server-express";
import express from "express";
import { makeExecutableSchema } from "@graphql-tools/schema";
import {
    constraintDirectiveTypeDefs,
    createApolloQueryValidationPlugin,
  } from "graphql-constraint-directive";

const typeDefs = `#graphql
directive @component(name: String!) on QUERY
  type Query {
    getName: String!
  }
`;

const schema = makeExecutableSchema({
    typeDefs: [typeDefs, constraintDirectiveTypeDefs],
  });

const resolvers = {
    Query: {
      getName: (a,b,c,d) => {return d.operation.directives[0].arguments[0].value.value},
    },
  };

  const plugins = [createApolloQueryValidationPlugin({ schema })];

const server = new ApolloServer({
    typeDefs: schema,
    resolvers,
    plugins
  });

  const startServer = async () => {
    await server.start();
    const app = express();
    server.applyMiddleware({ app });
    app.listen({ port: 4000 }, () =>
      console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
    );
  };

  startServer();

package.json

{
  "name": "graphql-server-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "compile": "tsc",
    "start": "npm run compile && node ./dist/index.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "@apollo/server": "^4.10.0",
    "@graphql-tools/schema": "^10.0.2",
    "apollo-server-express": "^3.13.0",
    "express": "^4.18.2",
    "graphql": "^16.8.1",
    "graphql-constraint-directive": "^5.4.1"
  },
  "devDependencies": {
    "@types/node": "^20.11.19",
    "typescript": "^5.3.3"
  }
}

simonfaltum commented 8 months ago

This PR solves the problem. https://github.com/confuser/graphql-constraint-directive/pull/223

The problem was that in these cases this.currentrFieldDef would be undefined. As we just return the function anyway if nothing is found, it makes sense to just add the optional chaining operator.