From 4aba700cd2f77054206fb8636bc5d70ab6119e06 Mon Sep 17 00:00:00 2001 From: Ldoppea Date: Tue, 17 Dec 2024 17:02:49 +0100 Subject: [PATCH] feat: Allow to enforce Stack link on request chain In previous commits we implemented supports for offline mode through CozyPouchLink and FlagshipLink Since this feature, the StackLink is able to check for internet reachability by using the `isOnline()` method. When no internet is detected, then the request is forwarded to the next link (i.e. the CozyPouchLink) In some scenario we may want to prevent that behavior. This is the case when we are working on a feature that cannot work without internet access, or when the query's result is expected to contain data that is injected by the cozy-stack (and so that is not available in the local Pouch database) To make this possible we introduce the `forceStack` parameter that can be set in the query options When set to `true` then the StackLink will not attempt to forward the request when offline, instead it will still attempt to do the request and throw We chose to attempt the request instead of directly throwing an error in order to prevent hypothetical scenario where `isOnline()` method returns false even if internet is reachable --- docs/link-authoring.md | 8 ++++---- packages/cozy-client/src/CozyClient.js | 12 ++++++------ packages/cozy-client/src/CozyLink.js | 10 +++++----- packages/cozy-client/src/StackLink.js | 16 ++++++++-------- packages/cozy-client/src/WebFlagshipLink.js | 4 ++-- packages/cozy-pouch-link/src/CozyPouchLink.js | 16 ++++++++-------- 6 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/link-authoring.md b/docs/link-authoring.md index 1119c3a118..2da202f7df 100644 --- a/docs/link-authoring.md +++ b/docs/link-authoring.md @@ -20,9 +20,9 @@ In the chain example pictured above, the Dedup link would avoid re-fetching the There are two ways of creating a new link. First, you can instantiate a `CozyLink` and pass a request handling function to its constructor: ```js -const logLink = new CozyLink((operation, result, forward) => { +const logLink = new CozyLink((operation, options, result, forward) => { console.log(JSON.stringify(operation)) - return forward(operation, result) + return forward(operation, options, result) }) ``` @@ -30,9 +30,9 @@ Or you can subclass `CozyLink`: ```js class LogLink extends CozyLink { - request(operation, result, forward) { + request(operation, options, result, forward) { console.log(JSON.stringify(operation)) - return forward(operation, result) + return forward(operation, options, result) } } ``` diff --git a/packages/cozy-client/src/CozyClient.js b/packages/cozy-client/src/CozyClient.js index 68148d411b..c3f3efa985 100644 --- a/packages/cozy-client/src/CozyClient.js +++ b/packages/cozy-client/src/CozyClient.js @@ -976,7 +976,7 @@ client.query(Q('io.cozy.bills'))`) Promise.resolve( executeQueryFromState(this.store.getState(), queryDefinition) ) - : () => this.requestQuery(queryDefinition) + : () => this.requestQuery(queryDefinition, options) const response = await this._promiseCache.exec(requestFn, () => stringify(queryDefinition) ) @@ -1085,7 +1085,7 @@ client.query(Q('io.cozy.bills'))`) options.as || this.queryIdGenerator.generateId(mutationDefinition) this.dispatch(initMutation(mutationId, mutationDefinition)) try { - const response = await this.requestMutation(mutationDefinition) + const response = await this.requestMutation(mutationDefinition, options) this.dispatch( receiveMutationResult( mutationId, @@ -1111,8 +1111,8 @@ client.query(Q('io.cozy.bills'))`) * @param {QueryDefinition} definition QueryDefinition to be executed * @returns {Promise} */ - async requestQuery(definition) { - const mainResponse = await this.chain.request(definition) + async requestQuery(definition, options) { + const mainResponse = await this.chain.request(definition, options) await this.persistVirtualDocuments(definition, mainResponse.data) @@ -1262,7 +1262,7 @@ client.query(Q('io.cozy.bills'))`) } } - async requestMutation(definition) { + async requestMutation(definition, options) { if (Array.isArray(definition)) { const [first, ...rest] = definition const firstResponse = await this.requestMutation(first) @@ -1275,7 +1275,7 @@ client.query(Q('io.cozy.bills'))`) ) return firstResponse } - return this.chain.request(definition) + return this.chain.request(definition, options) } getIncludesRelationships(queryDefinition) { diff --git a/packages/cozy-client/src/CozyLink.js b/packages/cozy-client/src/CozyLink.js index 055b61b1e8..c24aeb4313 100644 --- a/packages/cozy-client/src/CozyLink.js +++ b/packages/cozy-client/src/CozyLink.js @@ -17,7 +17,7 @@ export default class CozyLink { * @param {any} forward - The next request of the chain * @returns {Promise} */ - async request(operation, result, forward) { + async request(operation, options, result, forward) { throw new Error('request is not implemented') } @@ -67,11 +67,11 @@ export const chain = links => [...links, defaultLinkHandler].map(toLink).reduce(concat) const concat = (firstLink, nextLink) => { - const requestHandler = (operation, result, forward) => { - const nextForward = (op, res) => { - return nextLink.request(op, res, forward) + const requestHandler = (operation, options, result, forward) => { + const nextForward = (op, options, res) => { + return nextLink.request(op, options, res, forward) } - return firstLink.request(operation, result, nextForward) + return firstLink.request(operation, options, result, nextForward) } const persistHandler = (data, forward) => { diff --git a/packages/cozy-client/src/StackLink.js b/packages/cozy-client/src/StackLink.js index 8f8cf2a9f1..6e51777b11 100644 --- a/packages/cozy-client/src/StackLink.js +++ b/packages/cozy-client/src/StackLink.js @@ -89,19 +89,19 @@ export default class StackLink extends CozyLink { this.stackClient = null } - async request(operation, result, forward) { - if (this.isOnline && !(await this.isOnline())) { - return forward(operation) + async request(operation, options, result, forward) { + if (this.isOnline && !(await this.isOnline()) && !options?.forceStack) { + return forward(operation, options) } try { if (operation.mutationType) { - return await this.executeMutation(operation, result, forward) + return await this.executeMutation(operation, options, result, forward) } return await this.executeQuery(operation) } catch (err) { - if (isReactNativeOfflineError(err)) { - return forward(operation) + if (isReactNativeOfflineError(err) && !options?.forceStack) { + return forward(operation, options) } throw err } @@ -138,7 +138,7 @@ export default class StackLink extends CozyLink { } } - async executeMutation(mutation, result, forward) { + async executeMutation(mutation, options, result, forward) { const { mutationType, document: doc, documents: docs, ...props } = mutation switch (mutationType) { case MutationTypes.CREATE_DOCUMENT: @@ -182,7 +182,7 @@ export default class StackLink extends CozyLink { .collection(DOCTYPE_FILES) .upload(props.file, props.dirPath) default: - return forward(mutation, result) + return forward(mutation, options, result) } } } diff --git a/packages/cozy-client/src/WebFlagshipLink.js b/packages/cozy-client/src/WebFlagshipLink.js index cc0e664225..765566195c 100644 --- a/packages/cozy-client/src/WebFlagshipLink.js +++ b/packages/cozy-client/src/WebFlagshipLink.js @@ -18,8 +18,8 @@ export default class WebFlagshipLink extends CozyLink { // does nothing, we don't need any client for this kind of link } - async request(operation, result, forward) { - return this.webviewIntent.call('flagshipLinkRequest', operation) + async request(operation, options, result, forward) { + return this.webviewIntent.call('flagshipLinkRequest', operation, options) } async persistCozyData(data, forward) { diff --git a/packages/cozy-pouch-link/src/CozyPouchLink.js b/packages/cozy-pouch-link/src/CozyPouchLink.js index 263ae1fb99..c1fc7d9ad7 100644 --- a/packages/cozy-pouch-link/src/CozyPouchLink.js +++ b/packages/cozy-pouch-link/src/CozyPouchLink.js @@ -410,7 +410,7 @@ class PouchLink extends CozyLink { return !!this.getPouch(impactedDoctype) } - async request(operation, result = null, forward = doNothing) { + async request(operation, options, result = null, forward = doNothing) { const doctype = getDoctypeFromOperation(operation) if (!this.pouches) { @@ -420,7 +420,7 @@ class PouchLink extends CozyLink { ) } - return forward(operation) + return forward(operation, options) } if (this.pouches.getSyncStatus(doctype) === 'not_synced') { @@ -429,7 +429,7 @@ class PouchLink extends CozyLink { `Tried to access local ${doctype} but Cozy Pouch is not synced yet. Forwarding the operation to next link` ) } - return forward(operation) + return forward(operation, options) } if (await this.needsToWaitWarmup(doctype)) { @@ -438,7 +438,7 @@ class PouchLink extends CozyLink { `Tried to access local ${doctype} but not warmuped yet. Forwarding the operation to next link` ) } - return forward(operation) + return forward(operation, options) } // Forwards if doctype not supported @@ -448,11 +448,11 @@ class PouchLink extends CozyLink { `The doctype '${doctype}' is not supported. Forwarding the operation to next link` ) } - return forward(operation) + return forward(operation, options) } if (operation.mutationType) { - return this.executeMutation(operation, result, forward) + return this.executeMutation(operation, options, result, forward) } else { return this.executeQuery(operation) } @@ -749,7 +749,7 @@ class PouchLink extends CozyLink { return jsonResult } - async executeMutation(mutation, result, forward) { + async executeMutation(mutation, options, result, forward) { const markName = this.performancesApi.mark('executeMutation') let pouchRes switch (mutation.mutationType) { @@ -769,7 +769,7 @@ class PouchLink extends CozyLink { pouchRes = await this.addReferencesTo(mutation) break default: - return forward(mutation, result) + return forward(mutation, options, result) } const jsonResult = jsonapi.fromPouchResult({