Skip to content

Commit

Permalink
support non-spec errors
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed Oct 9, 2023
1 parent 052d5f4 commit 838cfa9
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
6 changes: 6 additions & 0 deletions .changeset/olive-schools-prove.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@urql/core': patch
---

Support non spec-compliant error bodies, i.e. the Shopify API does return `errors` but as an object. Adding
a check whether we are really dealing with an Array of errors enables this.
146 changes: 146 additions & 0 deletions packages/core/src/internal/__snapshots__/fetchSource.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,152 @@ exports[`on error > returns error data with status 400 and manual redirect mode
}
`;

exports[`on error with non spec-compliant body > handles network errors 1`] = `
{
"data": undefined,
"error": [CombinedError: [Network] Forbidden],
"extensions": undefined,
"hasNext": false,
"operation": {
"context": {
"fetchOptions": {
"method": "POST",
},
"requestPolicy": "cache-first",
"url": "http://localhost:3000/graphql",
},
"key": 2,
"kind": "query",
"query": {
"__key": -2395444236,
"definitions": [
{
"directives": [],
"kind": "OperationDefinition",
"name": {
"kind": "Name",
"value": "getUser",
},
"operation": "query",
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{
"alias": undefined,
"arguments": [
{
"kind": "Argument",
"name": {
"kind": "Name",
"value": "name",
},
"value": {
"kind": "Variable",
"name": {
"kind": "Name",
"value": "name",
},
},
},
],
"directives": [],
"kind": "Field",
"name": {
"kind": "Name",
"value": "user",
},
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{
"alias": undefined,
"arguments": [],
"directives": [],
"kind": "Field",
"name": {
"kind": "Name",
"value": "id",
},
"selectionSet": undefined,
},
{
"alias": undefined,
"arguments": [],
"directives": [],
"kind": "Field",
"name": {
"kind": "Name",
"value": "firstName",
},
"selectionSet": undefined,
},
{
"alias": undefined,
"arguments": [],
"directives": [],
"kind": "Field",
"name": {
"kind": "Name",
"value": "lastName",
},
"selectionSet": undefined,
},
],
},
},
],
},
"variableDefinitions": [
{
"defaultValue": undefined,
"directives": [],
"kind": "VariableDefinition",
"type": {
"kind": "NamedType",
"name": {
"kind": "Name",
"value": "String",
},
},
"variable": {
"kind": "Variable",
"name": {
"kind": "Name",
"value": "name",
},
},
},
],
},
],
"kind": "Document",
"loc": {
"end": 92,
"source": {
"body": "query getUser($name: String) {
user(name: $name) {
id
firstName
lastName
}
}",
"locationOffset": {
"column": 1,
"line": 1,
},
"name": "gql",
},
"start": 0,
},
},
"variables": {
"name": "Clara",
},
},
"stale": false,
}
`;

exports[`on success > returns response data 1`] = `
{
"data": {
Expand Down
21 changes: 21 additions & 0 deletions packages/core/src/internal/fetchSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,27 @@ describe('on unexpected plain text responses', () => {
});
});

describe('on error with non spec-compliant body', () => {
beforeEach(() => {
fetch.mockResolvedValue({
status: 400,
statusText: 'Forbidden',
headers: { get: () => 'application/json' },
text: vi.fn().mockResolvedValue('{"errors":{"detail":"Bad Request"}}'),
});
});

it('handles network errors', async () => {
const data = await pipe(
makeFetchSource(queryOperation, 'https://test.com/graphql', {}),
toPromise
);

expect(data).toMatchSnapshot();
expect(data).toHaveProperty('error.networkError.message', 'Forbidden');
});
});

describe('on teardown', () => {
const fail = () => {
expect(true).toEqual(false);
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/utils/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ export const makeResult = (
result: ExecutionResult,
response?: any
): OperationResult => {
if (!('data' in result) && !('errors' in result)) {
if (
!('data' in result) &&
(!('errors' in result) || !Array.isArray(result.errors))
) {
throw new Error('No Content');
}

Expand Down

0 comments on commit 838cfa9

Please sign in to comment.