Repository: FormidableLabs/urql
Pull request #887 (2020-06-30 13:53:52)

(persisted-fetch) - pass document as a second argument to the generateHash option
## Summary

This passes the parsed GQL document as a second parameter so it can be used to get an already calculated hash.

Resolves https://github.com/FormidableLabs/urql/issues/886

## Set of changes

- add Second document parameter to `generateHash`


changeset-bot[bot]: ###  🦋  Changeset is good to go

Latest commit: 787655feae6c5c4e2a9b81f2ee39c1a0d94884b9

**We got this.**

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

  | Name                           | Type  |
| ------------------------------ | ----- |
| @urql/exchange-persisted-fetch | 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/pass-document?filename=.changeset/modern-wolves-argue.md&value=---%0A%22%40urql%2Fexchange-persisted-fetch%22%3A%20patch%0A---%0A%0A(persisted-fetch)%20-%20pass%20document%20as%20a%20second%20argument%20to%20the%20generateHash%20option%0A)



Patch:
diff --git a/.changeset/cuddly-ligers-design.md b/.changeset/cuddly-ligers-design.md
new file mode 100644
index 00000000..c40990b9
--- /dev/null
+++ b/.changeset/cuddly-ligers-design.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-persisted-fetch': minor
+---
+
+Pass the parsed GraphQL-document as a second argument to the `generateHash` option.
diff --git a/exchanges/persisted-fetch/README.md b/exchanges/persisted-fetch/README.md
index 1c524f88..edc1ba92 100644
--- a/exchanges/persisted-fetch/README.md
+++ b/exchanges/persisted-fetch/README.md
@@ -28,7 +28,7 @@ const client = createClient({
     persistedFetchExchange({
       /* optional config */
     }),
-    fetchExchange
+    fetchExchange,
   ],
 });
 ```
@@ -40,3 +40,25 @@ The `persistedQueryExchange` supports two configuration options:
 
 The `persistedFetchExchange` only handles queries, so for mutations we keep the
 `fetchExchange` around alongside of it.
+
+## Avoid hashing during runtime
+
+If you want to generate hashes at build-time you can use a [webpack-loader](https://github.com/leoasis/graphql-persisted-document-loader) to achieve this,
+when using this all you need to do in this exchange is the following:
+
+```js
+import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql';
+import { persistedFetchExchange } from '@urql/exchange-persisted-fetch';
+
+const client = createClient({
+  url: 'http://localhost:1234/graphql',
+  exchanges: [
+    dedupExchange,
+    cacheExchange,
+    persistedFetchExchange({
+      generateHash: (_, document) => document.documentId,
+    }),
+    fetchExchange,
+  ],
+});
+```
diff --git a/exchanges/persisted-fetch/src/persistedFetchExchange.ts b/exchanges/persisted-fetch/src/persistedFetchExchange.ts
index 2191e088..940a39f4 100644
--- a/exchanges/persisted-fetch/src/persistedFetchExchange.ts
+++ b/exchanges/persisted-fetch/src/persistedFetchExchange.ts
@@ -27,12 +27,13 @@ import {
   makeFetchOptions,
   makeFetchSource,
 } from '@urql/core/internal';
+import { DocumentNode } from 'graphql';
 
 import { hash } from './sha256';
 
 interface PersistedFetchExchangeOptions {
   preferGetForPersistedQueries?: boolean;
-  generateHash?: (query: string) => Promise<string>;
+  generateHash?: (query: string, document: DocumentNode) => Promise<string>;
 }
 
 export const persistedFetchExchange = (
@@ -68,7 +69,7 @@ export const persistedFetchExchange = (
 
         return pipe(
           // Hash the given GraphQL query
-          fromPromise(hashFn(query)),
+          fromPromise(hashFn(query, operation.query)),
           mergeMap(sha256Hash => {
             // Attach SHA256 hash and remove query from body
             body.query = undefined;


Test patch:
diff --git a/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts b/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
index 01ae9c0a..4a18fbb7 100644
--- a/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
+++ b/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
@@ -205,7 +205,7 @@ it('supports a custom hash function', async () => {
     json: () => expected,
   });
 
-  const hashFn = () => Promise.resolve('hello');
+  const hashFn = jest.fn(() => Promise.resolve('hello'));
 
   await pipe(
     fromValue(queryOperation),
@@ -225,4 +225,13 @@ it('supports a custom hash function', async () => {
       },
     },
   });
+  const queryString = `query getUser($name: String) {
+  user(name: $name) {
+    id
+    firstName
+    lastName
+  }
+}
+`;
+  expect(hashFn).toBeCalledWith(queryString, queryOperation.query);
 });
diff --git a/packages/core/src/utils/typenames.test.ts b/packages/core/src/utils/typenames.test.ts
index 39833b0e..7d8c75ea 100755
--- a/packages/core/src/utils/typenames.test.ts
+++ b/packages/core/src/utils/typenames.test.ts
@@ -49,6 +49,12 @@ describe('formatTypeNames', () => {
     expect(expectedKey).toBe(actualKey);
   });
 
+  it('preserves custom properties', () => {
+    const doc = parse(`{ id todos { id } }`) as any;
+    doc.documentId = '123';
+    expect((formatDocument(doc) as any).documentId).toBe(doc.documentId);
+  });
+
   it('adds typenames to a query string', () => {
     expect(formatTypeNames(`{ todos { id } }`)).toMatchInlineSnapshot(`
       "{