From 4f5d994fa0a7e551df28c338221bf9223f7431c4 Mon Sep 17 00:00:00 2001 From: Stephen Coady Date: Tue, 21 May 2019 14:51:35 +0100 Subject: [PATCH 1/5] fix: expose update function --- packages/sync/src/cache/createMutationOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sync/src/cache/createMutationOptions.ts b/packages/sync/src/cache/createMutationOptions.ts index ae28876f..6a42ace6 100644 --- a/packages/sync/src/cache/createMutationOptions.ts +++ b/packages/sync/src/cache/createMutationOptions.ts @@ -40,7 +40,7 @@ export const createMutationOptions = (options: MutationHelperOptions): MutationO // returns the update function used to update the cache by the client // ignores the scenario where the cache operation is an update as this is handled automatically // from Apollo client 2.5 onwards. -const getUpdateFunction = ( +export const getUpdateFunction = ( operation: string, idField: string, updateQuery: Query, From 760f0e23ac3ea117a6ab18a87f4061131201a61b Mon Sep 17 00:00:00 2001 From: Stephen Coady Date: Tue, 21 May 2019 16:38:24 +0100 Subject: [PATCH 2/5] fix: added api docs --- packages/sync/src/cache/createMutationOptions.ts | 16 +++++++++++++--- .../sync/src/cache/createSubscriptionOptions.ts | 15 +++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/sync/src/cache/createMutationOptions.ts b/packages/sync/src/cache/createMutationOptions.ts index 6a42ace6..d9c75505 100644 --- a/packages/sync/src/cache/createMutationOptions.ts +++ b/packages/sync/src/cache/createMutationOptions.ts @@ -14,6 +14,10 @@ export interface MutationHelperOptions { idField?: string; } +/** + * Returns an Apollo Client mutate compatible set of options. + * @param options see `MutationHelperOptions` + */ export const createMutationOptions = (options: MutationHelperOptions): MutationOptions => { const { mutation, @@ -37,9 +41,15 @@ export const createMutationOptions = (options: MutationHelperOptions): MutationO return { mutation, variables, optimisticResponse, update }; }; -// returns the update function used to update the cache by the client -// ignores the scenario where the cache operation is an update as this is handled automatically -// from Apollo client 2.5 onwards. +/** + * Generate the update function to update the cache for a given operation and query. + * Ignores the scenario where the cache operation is an update as this is handled automatically + * from Apollo Client 2.5 onwards. + * @param operation The title of the operation being performed + * @param idField The id field the item keys off + * @param updateQuery The Query to update in the cache + * @param opType The type of operation being performed + */ export const getUpdateFunction = ( operation: string, idField: string, diff --git a/packages/sync/src/cache/createSubscriptionOptions.ts b/packages/sync/src/cache/createSubscriptionOptions.ts index f0618dff..82c4bda9 100644 --- a/packages/sync/src/cache/createSubscriptionOptions.ts +++ b/packages/sync/src/cache/createSubscriptionOptions.ts @@ -11,6 +11,11 @@ export interface SubscriptionHelperOptions { idField?: string; } +/** + * Helper function which can be used to call subscribeToMore for multiple SubscriptionHelperOptions + * @param observableQuery the query which you would like to call subscribeToMore on. + * @param arrayOfHelperOptions the array of `SubscriptionHelperOptions` + */ export const subscribeToMoreHelper = (observableQuery: ObservableQuery, arrayOfHelperOptions: SubscriptionHelperOptions[]) => { for (const option of arrayOfHelperOptions) { @@ -18,6 +23,11 @@ export const subscribeToMoreHelper = (observableQuery: ObservableQuery, } }; +/** + * Creates a SubscribeToMoreOptions object which can be used with Apollo Client's subscribeToMore function + * on an observable query. + * @param options see `SubscriptionHelperOptions` + */ export const createSubscriptionOptions = (options: SubscriptionHelperOptions): SubscribeToMoreOptions => { const { subscriptionQuery, @@ -53,6 +63,11 @@ export const createSubscriptionOptions = (options: SubscriptionHelperOptions): S }; }; +/** + * Generate the standard update function to update the cache for a given operation type and query. + * @param opType The type of operation being performed + * @param idField The id field the item keys off + */ export const getUpdateQueryFunction = (opType: CacheOperation, idField = "id"): UpdateFunction => { let updateFunction: UpdateFunction; From 1e0793a9567e819ecdf104da6d5250daf1af12fc Mon Sep 17 00:00:00 2001 From: Stephen Coady Date: Thu, 23 May 2019 14:31:21 +0100 Subject: [PATCH 3/5] fix: added ability to have multiple update queries --- packages/sync/package.json | 3 ++- .../sync/src/cache/createMutationOptions.ts | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/sync/package.json b/packages/sync/package.json index 8d1caa52..24245089 100644 --- a/packages/sync/package.json +++ b/packages/sync/package.json @@ -62,6 +62,7 @@ "graphql-tag": "2.10.1", "graphql-tools": "4.0.4", "idb-localstorage": "0.2.0", - "subscriptions-transport-ws": "0.9.16" + "subscriptions-transport-ws": "0.9.16", + "util": "^0.12.0" } } diff --git a/packages/sync/src/cache/createMutationOptions.ts b/packages/sync/src/cache/createMutationOptions.ts index d9c75505..ed6b1f41 100644 --- a/packages/sync/src/cache/createMutationOptions.ts +++ b/packages/sync/src/cache/createMutationOptions.ts @@ -4,20 +4,17 @@ import { CacheOperation } from "./CacheOperation"; import { createOptimisticResponse } from "./createOptimisticResponse"; import { Query } from "./CacheUpdates"; import { getOperationFieldName, deconstructQuery } from "../utils/helpers"; +import { isArray } from "util"; export interface MutationHelperOptions { mutation: DocumentNode; variables: OperationVariables; - updateQuery: Query; + updateQuery: Query | Query[]; typeName: string; operationType?: CacheOperation; idField?: string; } -/** - * Returns an Apollo Client mutate compatible set of options. - * @param options see `MutationHelperOptions` - */ export const createMutationOptions = (options: MutationHelperOptions): MutationOptions => { const { mutation, @@ -37,7 +34,16 @@ export const createMutationOptions = (options: MutationHelperOptions): MutationO typeName }); - const update = getUpdateFunction(operationName, idField, updateQuery, operationType); + const update: MutationUpdaterFn = (cache, { data }) => { + if (isArray(updateQuery)) { + updateQuery.forEach(query => { + getUpdateFunction(operationName, idField, query, operationType)(cache, { data }); + }); + } else { + getUpdateFunction(operationName, idField, updateQuery, operationType)(cache, { data }); + } + }; + return { mutation, variables, optimisticResponse, update }; }; From 09f05a990a8b3f9ec8f18ce1735fe8a42ec15d6e Mon Sep 17 00:00:00 2001 From: Stephen Coady Date: Fri, 24 May 2019 15:15:05 +0100 Subject: [PATCH 4/5] fix: add test for multiple queries --- .../sync/src/cache/createMutationOptions.ts | 4 +- packages/sync/test/CacheHelpersTest.ts | 71 ++++++++++++++++++- packages/sync/test/mock/mutations.ts | 34 +++++++++ 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/packages/sync/src/cache/createMutationOptions.ts b/packages/sync/src/cache/createMutationOptions.ts index ed6b1f41..c924e3f5 100644 --- a/packages/sync/src/cache/createMutationOptions.ts +++ b/packages/sync/src/cache/createMutationOptions.ts @@ -36,9 +36,9 @@ export const createMutationOptions = (options: MutationHelperOptions): MutationO const update: MutationUpdaterFn = (cache, { data }) => { if (isArray(updateQuery)) { - updateQuery.forEach(query => { + for (const query of updateQuery) { getUpdateFunction(operationName, idField, query, operationType)(cache, { data }); - }); + } } else { getUpdateFunction(operationName, idField, updateQuery, operationType)(cache, { data }); } diff --git a/packages/sync/test/CacheHelpersTest.ts b/packages/sync/test/CacheHelpersTest.ts index cb0f7690..4c39c55e 100644 --- a/packages/sync/test/CacheHelpersTest.ts +++ b/packages/sync/test/CacheHelpersTest.ts @@ -1,12 +1,16 @@ import { expect, should } from "chai"; -import { MutationHelperOptions, CacheOperation, createMutationOptions, createSubscriptionOptions } from "../src/cache"; +import { CacheOperation, createMutationOptions, createSubscriptionOptions } from "../src/cache"; import { CREATE_ITEM, GET_ITEMS, + GET_LISTS, DELETE_ITEM, ITEM_CREATED_SUB, ITEM_DELETED_SUB, - ITEM_UPDATED_SUB + ITEM_UPDATED_SUB, + CREATE_LIST, + DOESNT_EXIST, + GET_NON_EXISTENT } from "./mock/mutations"; import { NormalizedCacheObject } from "apollo-cache-inmemory"; import { OfflineClient } from "../src"; @@ -39,6 +43,26 @@ describe("CacheHelpers", () => { operationType: CacheOperation.DELETE, idField: "id" }); + const builtOptionsWithArray = createMutationOptions({ + mutation: CREATE_LIST, + variables: { + "title": "new list" + }, + updateQuery: [{ query: GET_ITEMS, variables: {} }, { query: GET_LISTS, variables: {} }], + typeName: "List", + operationType: CacheOperation.ADD, + idField: "id" + }); + const builtNonExistent = createMutationOptions({ + mutation: DOESNT_EXIST, + variables: { + "title": "new list" + }, + updateQuery: { query: GET_NON_EXISTENT, variables: {} }, + typeName: "Something", + operationType: CacheOperation.ADD, + idField: "id" + }); const builtSubCreateOptions = createSubscriptionOptions({ subscriptionQuery: ITEM_CREATED_SUB, cacheUpdateQuery: GET_ITEMS, @@ -70,6 +94,34 @@ describe("CacheHelpers", () => { } }; + const createList = (id: string) => { + if (builtOptionsWithArray && builtOptionsWithArray.update) { + builtOptionsWithArray.update(client.cache, { + data: { + "createList": { + "id": id, + "title": "new list" + id, + "__typename": "Item" + } + } + }); + } + }; + + const createNonExistent = (id: string) => { + if (builtNonExistent && builtNonExistent.update) { + builtNonExistent.update(client.cache, { + data: { + "somethingFake": { + "id": id, + "title": "new list" + id, + "__typename": "Item" + } + } + }); + } + }; + const remove = (id: string) => { if (builtDeleteOptions && builtDeleteOptions.update) { builtDeleteOptions.update(client.cache, { @@ -96,6 +148,12 @@ describe("CacheHelpers", () => { "allItems": [] } }); + client.writeQuery({ + query: GET_LISTS, + data: { + "allLists": [] + } + }); }); it("ensures built mutation options include a function", () => { @@ -109,6 +167,13 @@ describe("CacheHelpers", () => { expect(client.readQuery({ query: GET_ITEMS }).allItems).to.have.length(1); }); + it("add item to cache with multiple", () => { + expect(client.readQuery({ query: GET_LISTS }).allLists).to.have.length(0); + createList("1"); + expect(client.readQuery({ query: GET_LISTS }).allLists).to.have.length(1); + expect(client.readQuery({ query: GET_ITEMS }).allItems).to.have.length(1); + }); + it("delete item from cache", async () => { expect(client.readQuery({ query: GET_ITEMS }).allItems).to.have.length(0); create("1"); @@ -232,7 +297,7 @@ describe("CacheHelpers", () => { { subscriptionData: { data: { - itemUpdated: { } + itemUpdated: {} } } }); diff --git a/packages/sync/test/mock/mutations.ts b/packages/sync/test/mock/mutations.ts index 6b3f5b5e..20f7cc62 100644 --- a/packages/sync/test/mock/mutations.ts +++ b/packages/sync/test/mock/mutations.ts @@ -8,6 +8,14 @@ mutation createItem($title: String!){ } `; +export const CREATE_LIST = gql` +mutation createList($title: String!){ + createList(title: $title){ + title + } + } +`; + export const DELETE_ITEM = gql` mutation deleteItem($id: ID!){ deleteItem(id: $id){ @@ -16,6 +24,14 @@ mutation deleteItem($id: ID!){ } `; +export const DOESNT_EXIST = gql` +mutation somethingFake($id: ID!){ + somethingFake(id: $id){ + title + } + } +`; + export const GET_ITEMS = gql` query allItems($first: Int) { allItems(first: $first) { @@ -25,6 +41,24 @@ export const GET_ITEMS = gql` } `; +export const GET_LISTS = gql` + query allLists($first: Int) { + allLists(first: $first) { + id + title + } +} +`; + +export const GET_NON_EXISTENT = gql` + query somethingFake($first: Int) { + somethingFake(first: $first) { + id + title + } +} +`; + export const ITEM_CREATED_SUB = gql` subscription itemCreated { itemCreated { From 892eb2b46554c3b452d495803b58bf3987c6e1b0 Mon Sep 17 00:00:00 2001 From: Stephen Coady Date: Fri, 24 May 2019 17:26:42 +0100 Subject: [PATCH 5/5] fix: feedback changes --- packages/sync/src/cache/createMutationOptions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/sync/src/cache/createMutationOptions.ts b/packages/sync/src/cache/createMutationOptions.ts index c924e3f5..85f63674 100644 --- a/packages/sync/src/cache/createMutationOptions.ts +++ b/packages/sync/src/cache/createMutationOptions.ts @@ -37,10 +37,12 @@ export const createMutationOptions = (options: MutationHelperOptions): MutationO const update: MutationUpdaterFn = (cache, { data }) => { if (isArray(updateQuery)) { for (const query of updateQuery) { - getUpdateFunction(operationName, idField, query, operationType)(cache, { data }); + const updateFunction = getUpdateFunction(operationName, idField, query, operationType); + updateFunction(cache, { data }); } } else { - getUpdateFunction(operationName, idField, updateQuery, operationType)(cache, { data }); + const updateFunction = getUpdateFunction(operationName, idField, updateQuery, operationType); + updateFunction(cache, { data }); } };