Skip to content

Commit

Permalink
Handle empty responses on 204 and zero Content-Length
Browse files Browse the repository at this point in the history
  • Loading branch information
marnusw committed May 22, 2018
1 parent fe3b0e9 commit 0aac8a8
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 3 deletions.
86 changes: 86 additions & 0 deletions src/__tests__/restLink.ts
Expand Up @@ -1717,6 +1717,92 @@ describe('Mutation', () => {
});
});

describe('empty response bodies', () => {
afterEach(() => {
fetchMock.restore();
});
it('returns an empty object on 204 status', 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: post,
});

const createPostMutation = gql`
fragment PublishablePostInput on REST {
title: String
}
mutation publishPost($input: PublishablePostInput!) {
publishedPost(input: $input)
@rest(type: "Post", path: "/posts", method: "POST") {
id
title
}
}
`;
const response = await makePromise<Result>(
execute(link, {
operationName: 'publishPost',
query: createPostMutation,
variables: { input: { title: post.title } },
}),
);

expect(response.data.publishedPost).toEqual({
__typename: 'Post',
id: null,
title: null,
});
});
it('returns an empty object on 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
expect.assertions(1);

const link = new RestLink({ uri: '/api' });

const post = { id: '1', title: 'Love apollo' };
fetchMock.post('/api/posts', {
headers: { 'Content-Length': 0 },
body: post,
});

const createPostMutation = gql`
fragment PublishablePostInput on REST {
title: String
}
mutation publishPost($input: PublishablePostInput!) {
publishedPost(input: $input)
@rest(type: "Post", path: "/posts", method: "POST") {
id
title
}
}
`;
const response = await makePromise<Result>(
execute(link, {
operationName: 'publishPost',
query: createPostMutation,
variables: { input: { title: post.title } },
}),
);

expect(response.data.publishedPost).toEqual({
__typename: 'Post',
id: null,
title: null,
});
});
});

describe('fieldNameDenormalizer', () => {
afterEach(() => {
fetchMock.restore();
Expand Down
8 changes: 5 additions & 3 deletions src/restLink.ts
Expand Up @@ -93,7 +93,7 @@ export namespace RestLink {

/**
* A function that takes the response field name and converts it into a GraphQL compliant name
*
*
* @note This is called *before* @see typePatcher so that it happens after
* optional-field-null-insertion.
*/
Expand All @@ -113,7 +113,7 @@ export namespace RestLink {
*
* @note: This is called *after* @see fieldNameNormalizer because that happens
* after optional-nulls insertion, and those would clobber normalized names.
*
*
* @warning: We're not thrilled with this API, and would love a better alternative before we get to 1.0.0
* Please see proposals considered in https://github.com/apollographql/apollo-link-rest/issues/48
* And consider submitting alternate solutions to the problem!
Expand Down Expand Up @@ -732,7 +732,9 @@ const resolver: Resolver = async (
})
.then(res => {
context.responses.push(res);
return res.json();
return res.status === 204 || res.headers.get('Content-Length') === '0'
? {}
: res.json()
})
.then(
result =>
Expand Down

0 comments on commit 0aac8a8

Please sign in to comment.