Repository: FormidableLabs/urql
Pull request #934 (2020-08-06 16:44:16)

(persisted-fetch) - Don't send persisted queries if the hash operation fails
<!--
  Thanks for opening a pull request! We appreciate your dedication and help!
  Before submitting your pull request, please make sure to read our CONTRIBUTING guide.

  The best contribution is always a PR! However, if you're starting to work on a large
  change, it's best to make sure to open an issue first.

  If this PR is already related to an issue, please reference it like so:
  Resolves #123
-->

## Summary

<!-- What's the motivation of this change? What does it solve? -->

In insecure browser environments, the `window.crypto.subtle` api isn't available. The default hashing function in the persisted query exchange will resolve an empty string in this case, and send that empty string as the persisted query, which leads to a `PersistedQueryNotFound` exception. This is fine, because the exchange will follow up with non-persisted query, but we can improve the default behavior by not sending a persisted query at all if the hash fails.

## Set of changes

<!--
  Roughly list the changes you've made and which packages are affected.
  Leave some notes on what may be noteworthy files you've changed.
  And lastly, please let us know if you think this is a breaking change.
-->

Updates the persisted query exchange to only send a persisted query if there's a non-falsey hash value. Let me know if there's a better way of implementing this with Wonka!

changeset-bot[bot]: ###  💥  No Changeset

Latest commit: 4d991ce0b35c0388e11d160f2945d90c0e82b9d2

Merging this PR will not cause any packages to be released. If these changes should not cause updates to packages in this repo, this is fine 🙂

**If these changes should be published to npm, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/lorenries/urql/new/main?filename=.changeset/twenty-bats-occur.md&value=---%0A%22%40urql%2Fexchange-persisted-fetch%22%3A%20patch%0A---%0A%0A(persisted-fetch)%20-%20Don't%20send%20persisted%20queries%20if%20the%20hash%20operation%20fails%0A)



Patch:
diff --git a/.changeset/wet-cows-melt.md b/.changeset/wet-cows-melt.md
new file mode 100644
index 00000000..1ae4525d
--- /dev/null
+++ b/.changeset/wet-cows-melt.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-persisted-fetch': patch
+---
+
+Stops sending a persisted query if the hashing function fails
diff --git a/exchanges/persisted-fetch/src/persistedFetchExchange.ts b/exchanges/persisted-fetch/src/persistedFetchExchange.ts
index 940a39f4..6f6f7b35 100644
--- a/exchanges/persisted-fetch/src/persistedFetchExchange.ts
+++ b/exchanges/persisted-fetch/src/persistedFetchExchange.ts
@@ -71,21 +71,25 @@ export const persistedFetchExchange = (
           // Hash the given GraphQL query
           fromPromise(hashFn(query, operation.query)),
           mergeMap(sha256Hash => {
-            // Attach SHA256 hash and remove query from body
-            body.query = undefined;
-            body.extensions = {
-              persistedQuery: {
-                version: 1,
-                sha256Hash,
-              },
-            };
-
+            // if the hashing operation was successful, add the persisted query extension
+            if (sha256Hash) {
+              // Attach SHA256 hash and remove query from body
+              body.query = undefined;
+              body.extensions = {
+                persistedQuery: {
+                  version: 1,
+                  sha256Hash,
+                },
+              };
+            }
             return makePersistedFetchSource(
               operation,
               body,
               dispatchDebug,
-              !!(options as PersistedFetchExchangeOptions)
-                .preferGetForPersistedQueries
+              !!(
+                (options as PersistedFetchExchangeOptions)
+                  .preferGetForPersistedQueries && sha256Hash
+              )
             );
           }),
           mergeMap(result => {


Test patch:
diff --git a/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts b/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
index 4a18fbb7..abb28b65 100644
--- a/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
+++ b/exchanges/persisted-fetch/src/persistedFetchExchange.test.ts
@@ -235,3 +235,40 @@ it('supports a custom hash function', async () => {
 `;
   expect(hashFn).toBeCalledWith(queryString, queryOperation.query);
 });
+
+it('falls back to a non-persisted query if the hash is falsy', async () => {
+  const expected = {
+    data: {
+      test: true,
+    },
+  };
+
+  fetch.mockResolvedValueOnce({
+    json: () => expected,
+  });
+
+  const hashFn = jest.fn(() => Promise.resolve(''));
+
+  await pipe(
+    fromValue(queryOperation),
+    persistedFetchExchange({ generateHash: hashFn })(exchangeArgs),
+    toPromise
+  );
+
+  expect(fetch).toHaveBeenCalledTimes(1);
+
+  const body = JSON.parse(fetch.mock.calls[0][1].body);
+
+  expect(body).toMatchObject({
+    query:
+      'query getUser($name: String) {\n' +
+      '  user(name: $name) {\n' +
+      '    id\n' +
+      '    firstName\n' +
+      '    lastName\n' +
+      '  }\n' +
+      '}\n',
+    operationName: 'getUser',
+    variables: { name: 'Clara' },
+  });
+});