ardatan / graphql-mesh

🕸️ GraphQL Federation Framework for any API services such as REST, OpenAPI, Swagger, SOAP, gRPC and more...
https://the-guild.dev/graphql/mesh
MIT License
3.27k stars 339 forks source link

Encapsulate on JSON Schema Handler Breaks API Functionality #1711

Open ddcollins opened 3 years ago

ddcollins commented 3 years ago

Describe the bug After encapsulating a json schema handler source, the "Api" functionality breaks.

I have a Me type with an instructor field which is resolved with TrainingAPI.api.instructor({id: user.crmId})

However, after encapsulating the TrainingAPI source I now get an error from the TrainingAPI.api.instructor({id: user.crmId}) call:

"originalError": {
        "name": "TypeError",
        "message": "Cannot read property 'type' of undefined",
        "stack": [
          "TypeError: Cannot read property 'type' of undefined",
          "    at createProxyInfo (/app/node_modules/@graphql-mesh/runtime/index.cjs.js:24:42)",
          "    at Proxy.<anonymous> (/app/node_modules/@graphql-mesh/runtime/index.cjs.js:162:35)",
          "    at me (/app/src/resolvers/me.js:29:45)",
          "    at processTicksAndRejections (internal/process/task_queues.js:93:5)",
          "    at async Promise.all (index 0)",
          "    at async Promise.all (index 0)"
        ]
      },
- name: TrainingAPI
    transforms:
      - resolversComposition:
          - resolver: 'Query.*'
            composer: ./src/middleware/reformat-rest-response.js
          - resolver: 'Mutation.*'
            composer: ./src/middleware/reformat-rest-response.js
      - encapsulate:
          name: training
          applyTo:
            query: true
            mutation: false
            subscription: false
    handler:
      jsonSchema:
        baseUrl: ${TRAINING_HOST}
...

Environment:

Additional context

In @graphql-mesh/runtime/index.cjs.js the field name the functionality is looking for is not in the field map, as the field map now has training as the query, so the field variable is left undefined.

// LINE 143
let parentType;
let operation;
let field;
for (const operationName in rootTypes) {
    const rootType = rootTypes[operationName];
    console.log(fieldName);
    if (rootType) {
        const fieldMap = rootType.getFields();
        console.log(fieldMap);
        if (fieldName in fieldMap) {
            operation = operationName;
            field = fieldMap[fieldName];
            parentType = rootType;
            // TODO: There might be collision here between the same field names in different root types
            // JYC Fix: collision only in 'all' mode, not in other modes
            if (operation === info.operation.operation) {
                break;
            }
        }
    }
}

Console.log output:

// Field name:
instructor

// Has field map:
[Object: null prototype] {
  training: {
    name: 'training',
    description: undefined,
    type: trainingQuery,
    args: [],
    resolve: undefined,
    subscribe: undefined,
    isDeprecated: false,
    deprecationReason: undefined,
    extensions: undefined,
    astNode: undefined
  }
}
ardatan commented 3 years ago

Source level transforms are reflected to the in context sdk and encapsulation creates another level. Incontext sdk doesn't work well with deeper fields so I don't recommend you to use encapsulate transform in that way. We prefer to use encapsulate when we use Mesh as SDK instead of gateway. Prefix transform would be better choice for you.

angelsvirkov commented 1 year ago

We have a similar problem. As our mesh graph grew we decided to start encapsulating some parts. This however does not work with resolver composition because the nested fields are not present in the field map, as mentioned above. Is there any way encapsulation can work with resolver composition? Or is it planned to have something in that direction in future?

The alternative solution to use prefixes does not scale very well, in my opinion.