Skip to content

Commit

Permalink
Merge branch 'master' of github.com:apollographql/apollo-link-rest
Browse files Browse the repository at this point in the history
  • Loading branch information
fbartho committed Apr 23, 2019
2 parents af9c2d6 + df1ec5c commit 1c6df75
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 7 deletions.
85 changes: 85 additions & 0 deletions src/__tests__/restLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3579,6 +3579,91 @@ describe('export directive', () => {
expect(data.post.tag).toEqual({ ...tag, __typename: 'Tag' });
expect(data.post.author).toEqual({ ...author, __typename: 'User' });
});

it('can handle nested exports with deeply structured response data', async () => {
expect.assertions(3);

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

const user = {
id: 'user-a',
posts: [
{
id: 'post-a',
tags: [
{
id: 'tag-a',
},
{
id: 'tag-b',
},
],
},
{
id: 'post-b',
tags: [
{
id: 'tag-c',
},
],
},
],
};
fetchMock.get('/api/user', user);
const postATagA = {
id: 'tag-a-details',
message: 'this is tag details a',
};
fetchMock.get('/api/posts/post-a/tags/tag-a', postATagA);
const postATagB = {
id: 'tag-b-details',
message: 'this is tag details b',
};
fetchMock.get('/api/posts/post-a/tags/tag-b', postATagB);
const postBTagC = {
id: 'tag-c-details',
message: 'this is tag details c',
};
fetchMock.get('/api/posts/post-b/tags/tag-c', postBTagC);

const userPostsWithTagDetails = gql`
query userPostsWithTagDetails {
user @rest(path: "/user") {
id
posts {
id @export(as: "postId")
tags {
id @export(as: "tagId")
details
@rest(
path: "/posts/{exportVariables.postId}/tags/{exportVariables.tagId}"
) {
id
message
}
}
}
}
}
`;

const { data } = await makePromise<Result>(
execute(link, {
operationName: 'userPostsWithTagDetails',
query: userPostsWithTagDetails,
}),
);

expect(data.user.posts[0].tags[0].details.message).toEqual(
'this is tag details a',
);
expect(data.user.posts[0].tags[1].details.message).toEqual(
'this is tag details b',
);
expect(data.user.posts[1].tags[0].details.message).toEqual(
'this is tag details c',
);
});
});

describe('Apollo client integration', () => {
Expand Down
29 changes: 22 additions & 7 deletions src/restLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,8 +791,8 @@ interface RequestContext {
/** Credentials Policy for Fetch */
credentials?: RequestCredentials | null;

/** Exported variables fulfilled in this request, using @export(as:) */
exportVariables: { [key: string]: any };
/** Exported variables fulfilled in this request, using @export(as:). They are stored keyed by node to support deeply nested structures with exports at multiple levels */
exportVariablesByNode: Map<any, { [key: string]: any }>;

endpoints: RestLink.Endpoints;
customFetch: RestLink.CustomFetch;
Expand Down Expand Up @@ -832,7 +832,21 @@ const resolver: Resolver = async (
info: ExecInfo,
) => {
const { directives, isLeaf, resultKey } = info;
const { exportVariables } = context;
const { exportVariablesByNode } = context;

const exportVariables = exportVariablesByNode.get(root) || {};

/** creates a copy of this node's export variables for its child nodes. iterates over array results to provide for each child. returns the passed result. */
const copyExportVariables = <T>(result: T): T => {
if (result instanceof Array) {
result.forEach(copyExportVariables);
} else {
// export variables are stored keyed on the node they are for
exportVariablesByNode.set(result, { ...exportVariables });
}

return result;
};

// Support GraphQL Aliases!
const aliasedNode = (root || {})[resultKey];
Expand Down Expand Up @@ -862,7 +876,7 @@ const resolver: Resolver = async (
if (isNotARestCall) {
// This is not tagged with @rest()
// This might not belong to us so return the aliasNode version preferentially
return aliasedNode || preAliasingNode;
return copyExportVariables(aliasedNode || preAliasingNode);
}
const {
credentials,
Expand Down Expand Up @@ -1078,7 +1092,8 @@ const resolver: Resolver = async (
mainDefinition.selectionSet,
);

return addTypeNameToResult(result, type, typePatcher);
result = addTypeNameToResult(result, type, typePatcher);
return copyExportVariables(result);
};

/**
Expand Down Expand Up @@ -1265,8 +1280,8 @@ export class RestLink extends ApolloLink {
const requestContext: RequestContext = {
headers,
endpoints: this.endpoints,
// Provide an empty hash for this request's exports to be stuffed into
exportVariables: {},
// Provide an empty map for this request's exports to be stuffed into
exportVariablesByNode: new Map(),
credentials,
customFetch: this.customFetch,
operationType,
Expand Down

0 comments on commit 1c6df75

Please sign in to comment.