Skip to content

Commit f122c81

Browse files
authored
fix(error): internal server error when formatting non CDS and GraphQLError 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
1 parent 6026d6d commit f122c81

File tree

11 files changed

+35
-23
lines changed

11 files changed

+35
-23
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Moved registration of `cds.compile.to.gql` and `cds.compile.to.graphql` targets from `@sap/cds` to `@cap-js/graphql`
1717
- Improve merging of custom `graphql` protocol configuration with plugin default configuration
1818
- Errors representing client errors (`4xx` range) are now logged as warnings instead of errors
19-
- Exclude the stack trace of the outer logged error message in multiple error scenarios, as the inner stack trace already
20-
contained the precise initial segment of the outer stack trace.
19+
- 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
2120

2221
### Fixed
2322

2423
- Load custom `errorFormatter` relative to CDS project root
24+
- 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
2525

2626
### Removed
2727

lib/resolvers/error.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,8 @@ const _cdsToGraphQLError = (context, err) => {
3333
return Object.defineProperty(graphQLError, '_cdsError', { value: true, writable: false, enumerable: false })
3434
}
3535

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

39-
const handleCDSError = (context, error) => {
40-
error = _ensureError(error)
41-
_log(error)
42-
return _cdsToGraphQLError(context, error)
43-
}
44-
4538
// TODO: Revise this logging functionality, as it's not specific to protocol adapters.
4639
// This function should be relocated and/or cleaned up when the new abstract/generic
4740
// protocol adapter is designed and implemented.
@@ -73,13 +66,24 @@ const _log = error => {
7366
}
7467
}
7568

69+
const _ensureError = error => (error instanceof Error ? error : new Error(error))
70+
71+
const handleCDSError = (context, error) => {
72+
error = _ensureError(error)
73+
_log(error)
74+
return _cdsToGraphQLError(context, error)
75+
}
76+
7677
const formatError = error => {
78+
// Note: error is not always an instance of GraphQLError
79+
7780
// CDS errors have already been logged and already have a stacktrace in extensions
7881
if (error.originalError?._cdsError) return error
7982

8083
if (LOG_GRAPHQL._error) LOG_GRAPHQL.error(error)
8184

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

8488
return error
8589
}

test/tests/localized.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - queries with localized data', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
test('query with default locale', async () => {

test/tests/logger-dev.test.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ describe('graphql - query logging in development', () => {
1313
let _log
1414

1515
beforeEach(async () => {
16-
await data.reset()
16+
await data.reset()
1717
_log = jest.spyOn(console, 'info')
1818
})
1919

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

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

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

139141
describe('GET requests', () => {
140142
test('Do not log requests with undefined queries', async () => {
141-
await GET('/graphql')
143+
const response = await GET('/graphql')
144+
expect(response.status).toEqual(200)
145+
expect(response.data).toMatch(/html/i) // GraphiQL is returned
142146
expect(_log.mock.calls.length).toEqual(0)
143147
})
144148

test/tests/logger-prod.test.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('graphql - query logging with sanitization in production', () => {
2020
})
2121

2222
beforeEach(async () => {
23-
await data.reset()
23+
await data.reset()
2424
_log = jest.spyOn(console, 'info')
2525
})
2626

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

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

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

148150
describe('GET requests', () => {
149151
test('Do not log requests with undefined queries', async () => {
150-
await GET('/graphql')
152+
const response = await GET('/graphql')
153+
expect(response.status).toEqual(200)
154+
expect(response.data).toMatch(/html/i) // GraphiQL is returned
151155
expect(_log.mock.calls.length).toEqual(0)
152156
})
153157

test/tests/mutations/create.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - create mutations', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
test('create empty entry', async () => {

test/tests/mutations/delete.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - delete mutations', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
test('delete no entries', async () => {

test/tests/mutations/update.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - update mutations', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
test('update no entries', async () => {

test/tests/queries/filter.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - filter', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
// REVISIT: unskip for support of configurable schema flavors

test/tests/queries/queries.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('graphql - queries', () => {
88
axios.defaults.validateStatus = false
99

1010
beforeEach(async () => {
11-
await data.reset()
11+
await data.reset()
1212
})
1313

1414
// REVISIT: unskip for support of configurable schema flavors

0 commit comments

Comments
 (0)