Skip to content

Commit

Permalink
fix(error): internal server error when formatting non CDS and GraphQL…
Browse files Browse the repository at this point in the history
…Error errors (#132)

* Test status code of requests with undefined queries

* Fix trying to set stacktrace in extensions when error isn't GraphQLError

* Reorder function declarations

* Prettier format

* Add changelog entry

* Format changelog

* Improve comment

* Destructure response in tests

* Fix expects for GET requests

* Improve expects for POST requests

* Add comments about GraphiQL to expected HTML
  • Loading branch information
schwma authored Nov 16, 2023
1 parent 6026d6d commit f122c81
Show file tree
Hide file tree
Showing 11 changed files with 35 additions and 23 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Moved registration of `cds.compile.to.gql` and `cds.compile.to.graphql` targets from `@sap/cds` to `@cap-js/graphql`
- Improve merging of custom `graphql` protocol configuration with plugin default configuration
- Errors representing client errors (`4xx` range) are now logged as warnings instead of errors
- Exclude the stack trace of the outer logged error message in multiple error scenarios, as the inner stack trace already
contained the precise initial segment of the outer stack trace.
- Exclude the stack trace of the outer logged error message in multiple error scenarios, as the inner stack trace already contained the precise initial segment of the outer stack trace

### Fixed

- Load custom `errorFormatter` relative to CDS project root
- Fix internal server error when formatting errors that aren't CDS errors (thrown by CDS or in custom handlers) or instances of GraphQLError, such as the error caused by requests with undefined `query` property

### Removed

Expand Down
20 changes: 12 additions & 8 deletions lib/resolvers/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,8 @@ const _cdsToGraphQLError = (context, err) => {
return Object.defineProperty(graphQLError, '_cdsError', { value: true, writable: false, enumerable: false })
}

const _ensureError = error => (error instanceof Error ? error : new Error(error))
const _clone = obj => Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj))

const handleCDSError = (context, error) => {
error = _ensureError(error)
_log(error)
return _cdsToGraphQLError(context, error)
}

// TODO: Revise this logging functionality, as it's not specific to protocol adapters.
// This function should be relocated and/or cleaned up when the new abstract/generic
// protocol adapter is designed and implemented.
Expand Down Expand Up @@ -73,13 +66,24 @@ const _log = error => {
}
}

const _ensureError = error => (error instanceof Error ? error : new Error(error))

const handleCDSError = (context, error) => {
error = _ensureError(error)
_log(error)
return _cdsToGraphQLError(context, error)
}

const formatError = error => {
// Note: error is not always an instance of GraphQLError

// CDS errors have already been logged and already have a stacktrace in extensions
if (error.originalError?._cdsError) return error

if (LOG_GRAPHQL._error) LOG_GRAPHQL.error(error)

if (!IS_PRODUCTION) error.extensions.stacktrace = error.stack.split('\n')
// error does not have an extensions property when it is not an instance of GraphQLError
if (!IS_PRODUCTION && error.extensions) error.extensions.stacktrace = error.stack.split('\n')

return error
}
Expand Down
2 changes: 1 addition & 1 deletion test/tests/localized.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - queries with localized data', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

test('query with default locale', async () => {
Expand Down
10 changes: 7 additions & 3 deletions test/tests/logger-dev.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('graphql - query logging in development', () => {
let _log

beforeEach(async () => {
await data.reset()
await data.reset()
_log = jest.spyOn(console, 'info')
})

Expand All @@ -23,7 +23,9 @@ describe('graphql - query logging in development', () => {

describe('POST requests', () => {
test('Do not log requests with undefined queries', async () => {
await POST('/graphql')
const response = await POST('/graphql')
expect(response.status).toEqual(400)
expect(response.data.errors[0]).toEqual({ message: 'Missing query' })
expect(_log.mock.calls.length).toEqual(0)
})

Expand Down Expand Up @@ -138,7 +140,9 @@ describe('graphql - query logging in development', () => {

describe('GET requests', () => {
test('Do not log requests with undefined queries', async () => {
await GET('/graphql')
const response = await GET('/graphql')
expect(response.status).toEqual(200)
expect(response.data).toMatch(/html/i) // GraphiQL is returned
expect(_log.mock.calls.length).toEqual(0)
})

Expand Down
10 changes: 7 additions & 3 deletions test/tests/logger-prod.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('graphql - query logging with sanitization in production', () => {
})

beforeEach(async () => {
await data.reset()
await data.reset()
_log = jest.spyOn(console, 'info')
})

Expand All @@ -30,7 +30,9 @@ describe('graphql - query logging with sanitization in production', () => {

describe('POST requests', () => {
test('Do not log requests with undefined queries', async () => {
await POST('/graphql')
const response = await POST('/graphql')
expect(response.status).toEqual(400)
expect(response.data.errors[0]).toEqual({ message: 'Missing query' })
expect(_log.mock.calls.length).toEqual(0)
})

Expand Down Expand Up @@ -147,7 +149,9 @@ describe('graphql - query logging with sanitization in production', () => {

describe('GET requests', () => {
test('Do not log requests with undefined queries', async () => {
await GET('/graphql')
const response = await GET('/graphql')
expect(response.status).toEqual(200)
expect(response.data).toMatch(/html/i) // GraphiQL is returned
expect(_log.mock.calls.length).toEqual(0)
})

Expand Down
2 changes: 1 addition & 1 deletion test/tests/mutations/create.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - create mutations', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

test('create empty entry', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/tests/mutations/delete.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - delete mutations', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

test('delete no entries', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/tests/mutations/update.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - update mutations', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

test('update no entries', async () => {
Expand Down
2 changes: 1 addition & 1 deletion test/tests/queries/filter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - filter', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

// REVISIT: unskip for support of configurable schema flavors
Expand Down
2 changes: 1 addition & 1 deletion test/tests/queries/queries.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('graphql - queries', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

// REVISIT: unskip for support of configurable schema flavors
Expand Down
2 changes: 1 addition & 1 deletion test/tests/types.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe('graphql - types parsing and validation', () => {
axios.defaults.validateStatus = false

beforeEach(async () => {
await data.reset()
await data.reset()
})

describe('cds.Binary', () => {
Expand Down

0 comments on commit f122c81

Please sign in to comment.