Skip to content

wrapSchema transforms the response when error path is not provided  #3880

Open
@Grmiade

Description

@Grmiade

Describe the bug

The wrapSchema function transforms the response when error path is not provided.
Currently, @apollo/gateway doesn't provide this top-level path, so when we wrap a federated graph, responses with errors are not handled correctly.

See apollographql/federation#354

To Reproduce

Here a test suite to reproduce the issue:

import { makeExecutableSchema } from '@graphql-tools/schema'
import { wrapSchema } from '@graphql-tools/wrap'
import { graphql } from 'graphql'

function generateResponse(withPath = false): Record<string, unknown> {
  return {
    errors: [
      {
        message: 'Country not found',
        path: withPath ? ['companies', 1, 'country', 'name'] : undefined,
      },
    ],
    data: {
      companies: [
        {
          country: null,
        },
        {
          country: {
            name: null,
          },
        },
        {
          country: {
            name: 'France',
          },
        },
      ],
    },
  }
}

const typeDefs = `
  type Country {
    code: String!
    name: String
  }

  type Company {
    country: Country
  }

  type Query {
    companies: [Company!]!
  }
`

const resolvers = {
  Country: {
    name(parent: { code: string }) {
      switch (parent.code) {
        case 'FR':
          return 'France'
        case 'US':
          return 'United States'
        default:
          throw new Error('Country not found')
      }
    },
  },
  Query: {
    companies() {
      return [{ country: null }, { country: { code: 'BE' } }, { country: { code: 'FR' } }]
    },
  },
}

const companySchema = makeExecutableSchema({ resolvers, typeDefs })

it('should success with companySchema', async () => {
  const response = await graphql(
    companySchema,
    `
      {
        companies {
          country {
            name
          }
        }
      }
    `,
  )

  expect(response.data?.companies[0].country).toBe(null)
  expect(response.data?.companies[1].country).toEqual({ name: null })
  expect(response.errors).toBeDefined()
})

it('should success by wrapping companySchema when path is provided', async () => {
  const wrappedCompanySchema = wrapSchema({
    executor() {
      return generateResponse(true)
    },
    schema: companySchema,
  })

  const response = await graphql(
    wrappedCompanySchema,
    `
      {
        companies {
          country {
            name
          }
        }
      }
    `,
  )

  expect(response.data?.companies[0].country).toBe(null)
  expect(response.data?.companies[1].country).toEqual({ name: null })
  expect(response.errors).toBeDefined()
})

it('should success by wrapping companySchema when path is not provided', async () => {
  const wrappedCompanySchema = wrapSchema({
    executor() {
      return generateResponse(false)
    },
    schema: companySchema,
  })

  const response = await graphql(
    wrappedCompanySchema,
    `
      {
        companies {
          country {
            name
          }
        }
      }
    `,
  )

  console.log(JSON.stringify(response, null, 2))

  expect(response.data?.companies[0].country).toBe(null)
  expect(response.data?.companies[1].country).toEqual({ name: null })
  expect(response.errors).toBeDefined()
})

Current behavior

When the error doesn't provide the path, we receive:

{
  "data": {
    "companies": [
      {
        "country": {
          "name": null
        }
      },
      {
        "country": {
          "name": null
        }
      },
      {
        "country": {
          "name": "France"
        }
      }
    ]
  }
}

Is this behavior is wanted?

Expected behavior

We should probably receive:

{
  "errors": [{ "message": "Country not found" }],
  "data": {
    "companies": [
      {
        "country": null
      },
      {
        "country": {
          "name": null
        }
      },
      {
        "country": {
          "name": "France"
        }
      }
    ]
  }
}

Environment

  • @graphql-tools/schema to 8.3.1
  • @graphql-tools/wrap to 8.3.2
  • graphql to 15.7.2

Thanks in advance 🙏 Let me know if you need more details.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions