From 254eb40f515f608a549f77c50fd47d207ec74190 Mon Sep 17 00:00:00 2001 From: klarstrup Date: Fri, 24 Apr 2020 05:06:30 +0200 Subject: [PATCH 1/4] Handle empty empty response object in typePatchers --- src/restLink.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/restLink.ts b/src/restLink.ts index e642b47..e4dc4de 100755 --- a/src/restLink.ts +++ b/src/restLink.ts @@ -1183,6 +1183,8 @@ export class RestLink extends ApolloLink { if (typePatcher == null) { this.typePatcher = (result, __typename, _2) => { + if (!Object.keys(result).length) return null; + return { __typename, ...result }; }; } else if ( @@ -1202,6 +1204,8 @@ export class RestLink extends ApolloLink { outerType: string, patchDeeper: RestLink.FunctionalTypePatcher, ) => { + if (!Object.keys(data).length) return null; + const __typename = data.__typename || outerType; if (Array.isArray(data)) { return data.map(d => patchDeeper(d, __typename, patchDeeper)); From c106c637420b361e29f092adf5bc3846c9013622 Mon Sep 17 00:00:00 2001 From: klarstrup Date: Fri, 24 Apr 2020 05:06:51 +0200 Subject: [PATCH 2/4] handle empty selectionSet under @rest --- src/restLink.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/restLink.ts b/src/restLink.ts index e4dc4de..de60fd0 100755 --- a/src/restLink.ts +++ b/src/restLink.ts @@ -392,6 +392,7 @@ function insertNullsForAnyOmittedFields( currentSelectionSet: SelectionSetNode, ): void { if ( + currentSelectionSet == null || null == current || typeof current === 'number' || typeof current === 'boolean' || From ef16a62a864812873525135645cb44249fc7f23f Mon Sep 17 00:00:00 2001 From: klarstrup Date: Fri, 24 Apr 2020 05:07:21 +0200 Subject: [PATCH 3/4] Test handling of empty object responses with and without a typePatcher --- src/__tests__/restLink.ts | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/__tests__/restLink.ts b/src/__tests__/restLink.ts index 9eb44bf..9a5c2b5 100755 --- a/src/__tests__/restLink.ts +++ b/src/__tests__/restLink.ts @@ -2578,6 +2578,65 @@ describe('Mutation', () => { }); }); + it.only('returns null on empty object response without typePatcher', async () => { + // In truth this test is just for show, because the fetch implementation + // used in the tests already returns {} from res.json() for 204 responses + expect.assertions(1); + + const link = new RestLink({ uri: '/api' }); + + const post = { id: '1', title: 'Love apollo' }; + fetchMock.post('/api/posts', { + status: 204, + body: {}, + }); + + const createPostMutation = gql` + mutation publishPost($input: PublishablePostInput!) { + publishedPost(input: $input) @rest(path: "/posts", method: "POST") + } + `; + const response = await toPromise( + execute(link, { + operationName: 'publishPost', + query: createPostMutation, + variables: { input: { title: post.title } }, + }), + ); + + expect(response.data.publishedPost).toEqual(null); + }); + + it.only('returns null on empty object response with typePatcher', async () => { + // In truth this test is just for show, because the fetch implementation + // used in the tests already returns {} from res.json() for 204 responses + expect.assertions(1); + + const link = new RestLink({ + uri: '/api', + typePatcher: { Post: data => data }, + }); + + const post = { id: '1', title: 'Love apollo' }; + + fetchMock.post('/api/posts', { status: 204, body: {} }); + + const createPostMutation = gql` + mutation publishPost($input: PublishablePostInput!) { + publishedPost(input: $input) @rest(path: "/posts", method: "POST") + } + `; + const response = await toPromise( + execute(link, { + operationName: 'publishPost', + query: createPostMutation, + variables: { input: { title: post.title } }, + }), + ); + + expect(response.data.publishedPost).toEqual(null); + }); + it('returns an empty object on successful posts with zero Content-Length', async () => { // In Node.js parsing an empty body doesn't throw an error, so the best test is // to provide body data and ensure the zero length still triggers the empty response From a6623bc2690eebab3639cf1bbbb2a249ef63d8f9 Mon Sep 17 00:00:00 2001 From: klarstrup Date: Fri, 24 Apr 2020 05:17:12 +0200 Subject: [PATCH 4/4] remove irrelevant comments in tests --- src/__tests__/restLink.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/__tests__/restLink.ts b/src/__tests__/restLink.ts index 9a5c2b5..e3356e8 100755 --- a/src/__tests__/restLink.ts +++ b/src/__tests__/restLink.ts @@ -2579,8 +2579,6 @@ describe('Mutation', () => { }); it.only('returns null on empty object response without typePatcher', async () => { - // In truth this test is just for show, because the fetch implementation - // used in the tests already returns {} from res.json() for 204 responses expect.assertions(1); const link = new RestLink({ uri: '/api' }); @@ -2608,8 +2606,6 @@ describe('Mutation', () => { }); it.only('returns null on empty object response with typePatcher', async () => { - // In truth this test is just for show, because the fetch implementation - // used in the tests already returns {} from res.json() for 204 responses expect.assertions(1); const link = new RestLink({