Repository: FormidableLabs/urql
Pull request #1311 (2021-01-14 21:10:18)

(graphcache) - Relay backwards pagination
Resolves https://github.com/FormidableLabs/urql/issues/1297

## Summary

When we'd have two backwards overlapping pages the results would start mixing up.

## Set of changes

- Reverse the backwards pagination node - edge concatenation


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: 5c469af85b490bfb9a947be4e08e4deb61486a26

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                      | Type  |
| ------------------------- | ----- |
| @urql/exchange-graphcache | Minor |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/FormidableLabs/urql/new/relay-backwards-pagination?filename=.changeset/lucky-birds-remain.md&value=---%0A%22%40urql%2Fexchange-graphcache%22%3A%20patch%0A---%0A%0A(graphcache)%20-%20Relay%20backwards%20pagination%0A)



Patch:
diff --git a/.changeset/strange-scissors-swim.md b/.changeset/strange-scissors-swim.md
new file mode 100644
index 00000000..65fbb71f
--- /dev/null
+++ b/.changeset/strange-scissors-swim.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+Fix a Relay Pagination edge case where overlapping ends of pages queried using the `last` argument would be in reverse order.
diff --git a/exchanges/graphcache/src/extras/relayPagination.ts b/exchanges/graphcache/src/extras/relayPagination.ts
index 534223e5..db3a728c 100644
--- a/exchanges/graphcache/src/extras/relayPagination.ts
+++ b/exchanges/graphcache/src/extras/relayPagination.ts
@@ -240,8 +240,8 @@ export const relayPagination = (params: PaginationParams = {}): Resolver => {
         pageInfo.startCursor = page.pageInfo.startCursor;
         pageInfo.hasPreviousPage = page.pageInfo.hasPreviousPage;
       } else if (typeof args.last === 'number') {
-        endEdges = concatEdges(cache, endEdges, page.edges);
-        endNodes = concatNodes(endNodes, page.nodes);
+        endEdges = concatEdges(cache, page.edges, endEdges);
+        endNodes = concatNodes(page.nodes, endNodes);
         pageInfo = page.pageInfo;
       } else {
         startEdges = concatEdges(cache, startEdges, page.edges);


Test patch:
diff --git a/exchanges/graphcache/src/extras/relayPagination.test.ts b/exchanges/graphcache/src/extras/relayPagination.test.ts
index 466638be..2583ba1e 100644
--- a/exchanges/graphcache/src/extras/relayPagination.test.ts
+++ b/exchanges/graphcache/src/extras/relayPagination.test.ts
@@ -1156,3 +1156,166 @@ it('Works with nodes absent from query', () => {
   expect(res.partial).toBe(false);
   expect(res.data).toEqual(results);
 });
+
+it('handles subsequent queries with larger last values', () => {
+  const Pagination = gql`
+    query($last: Int!) {
+      __typename
+      items(last: $last) {
+        __typename
+        edges {
+          __typename
+          node {
+            __typename
+            id
+          }
+        }
+        nodes {
+          __typename
+          id
+        }
+        pageInfo {
+          __typename
+          hasPreviousPage
+          startCursor
+        }
+      }
+    }
+  `;
+
+  const store = new Store({
+    resolvers: {
+      Query: {
+        items: relayPagination(),
+      },
+    },
+  });
+
+  const pageOne = {
+    __typename: 'Query',
+    items: {
+      __typename: 'ItemsConnection',
+      edges: [itemEdge(2)],
+      nodes: [itemNode(2)],
+      pageInfo: {
+        __typename: 'PageInfo',
+        hasPreviousPage: true,
+        startCursor: '2',
+      },
+    },
+  };
+
+  const pageTwo = {
+    __typename: 'Query',
+    items: {
+      __typename: 'ItemsConnection',
+      edges: [itemEdge(1), itemEdge(2)],
+      nodes: [itemNode(1), itemNode(2)],
+      pageInfo: {
+        __typename: 'PageInfo',
+        hasPreviousPage: false,
+        startCursor: '1',
+      },
+    },
+  };
+
+  const pageThree = {
+    __typename: 'Query',
+    items: {
+      __typename: 'ItemsConnection',
+      edges: [itemEdge(0), itemEdge(1), itemEdge(2)],
+      nodes: [itemNode(0), itemNode(1), itemNode(2)],
+      pageInfo: {
+        __typename: 'PageInfo',
+        hasPreviousPage: false,
+        startCursor: '0',
+      },
+    },
+  };
+
+  write(store, { query: Pagination, variables: { last: 1 } }, pageOne);
+  write(store, { query: Pagination, variables: { last: 2 } }, pageTwo);
+
+  let res = query(store, { query: Pagination, variables: { last: 2 } });
+
+  expect(res.partial).toBe(false);
+  expect(res.data).toEqual(pageTwo);
+
+  write(store, { query: Pagination, variables: { last: 3 } }, pageThree);
+
+  res = query(store, { query: Pagination, variables: { last: 3 } });
+
+  expect(res.partial).toBe(false);
+  expect(res.data).toEqual(pageThree);
+});
+
+it('handles subsequent queries with larger first values', () => {
+  const Pagination = gql`
+    query($first: Int!) {
+      __typename
+      items(first: $first) {
+        __typename
+        edges {
+          __typename
+          node {
+            __typename
+            id
+          }
+        }
+        nodes {
+          __typename
+          id
+        }
+        pageInfo {
+          __typename
+          hasNextPage
+          endCursor
+        }
+      }
+    }
+  `;
+
+  const store = new Store({
+    resolvers: {
+      Query: {
+        items: relayPagination(),
+      },
+    },
+  });
+
+  const pageOne = {
+    __typename: 'Query',
+    items: {
+      __typename: 'ItemsConnection',
+      edges: [itemEdge(1)],
+      nodes: [itemNode(1)],
+      pageInfo: {
+        __typename: 'PageInfo',
+        hasNextPage: true,
+        endCursor: '1',
+      },
+    },
+  };
+
+  const pageTwo = {
+    __typename: 'Query',
+    items: {
+      __typename: 'ItemsConnection',
+      edges: [itemEdge(1), itemEdge(2)],
+      nodes: [itemNode(1), itemNode(2)],
+      pageInfo: {
+        __typename: 'PageInfo',
+        hasNextPage: false,
+        endCursor: '2',
+      },
+    },
+  };
+
+  write(store, { query: Pagination, variables: { first: 1 } }, pageOne);
+  write(store, { query: Pagination, variables: { first: 2 } }, pageTwo);
+
+  const res = query(store, { query: Pagination, variables: { first: 2 } });
+
+  expect(res.partial).toBe(false);
+  expect(res.data).toEqual(pageTwo);
+});