From 1a5b00e92f97dc057d65842074a3fbb6c3ff1193 Mon Sep 17 00:00:00 2001 From: doubleface Date: Wed, 20 Dec 2023 16:51:24 +0100 Subject: [PATCH] feat: Send existing files index to pilot fetch This index is sent only if the konnector has a permission on io.cozy.files, to avoid unneccessary requests. This existing files index is needed to have `shouldReplaceFile` functionnal in konnectors. But shouldReplaceFile won't throw if it is not present. --- src/app/domain/manifest/permissions.ts | 11 ++++++ src/libs/ReactNativeLauncher.js | 8 +++++ src/libs/ReactNativeLauncher.spec.js | 46 ++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/app/domain/manifest/permissions.ts diff --git a/src/app/domain/manifest/permissions.ts b/src/app/domain/manifest/permissions.ts new file mode 100644 index 000000000..9febfcc52 --- /dev/null +++ b/src/app/domain/manifest/permissions.ts @@ -0,0 +1,11 @@ +import { KonnectorsDocument } from 'cozy-client/types/types' + +export const hasPermission = ( + konnector: KonnectorsDocument, + permission: string +): boolean => { + return Object.values(konnector.permissions ?? []).some( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + perm => perm?.type === permission // Unsafe member access .type on an `any` value + ) +} diff --git a/src/libs/ReactNativeLauncher.js b/src/libs/ReactNativeLauncher.js index 2974c0a8f..293326733 100644 --- a/src/libs/ReactNativeLauncher.js +++ b/src/libs/ReactNativeLauncher.js @@ -22,6 +22,7 @@ import { activateKeepAwake, deactivateKeepAwake } from '/app/domain/sleep/services/sleep' +import { hasPermission } from '/app/domain/manifest/permissions' const log = Minilog('ReactNativeLauncher') @@ -346,6 +347,13 @@ class ReactNativeLauncher extends Launcher { job, sourceAccountIdentifier } + + if (hasPermission(manifest, 'io.cozy.files')) { + const existingFilesIndex = await this.getExistingFilesIndex() + const serializableExistingFilesIndex = + Object.fromEntries(existingFilesIndex) + pilotContext.existingFilesIndex = serializableExistingFilesIndex + } await this.pilot.call('fetch', pilotContext) await this.stop() } catch (err) { diff --git a/src/libs/ReactNativeLauncher.spec.js b/src/libs/ReactNativeLauncher.spec.js index 1fe10f0a2..508d0170f 100644 --- a/src/libs/ReactNativeLauncher.spec.js +++ b/src/libs/ReactNativeLauncher.spec.js @@ -73,6 +73,7 @@ describe('ReactNativeLauncher', () => { save: jest.fn(), create: jest.fn(), query: jest.fn(), + queryAll: jest.fn(), destroy: jest.fn(), getStackClient: () => ({ uri: 'http://cozy.localhost:8080' @@ -310,12 +311,57 @@ describe('ReactNativeLauncher', () => { errorMessage: `getUserDataFromWebsite did not return any sourceAccountIdentifier. Cannot continue the execution.` }) }) + it('should send existingFilesIndex to the konnector if it has files permission', async () => { + const { launcher, client, launch } = setup() + launcher.setStartContext({ + client, + account: fixtures.account, + trigger: fixtures.trigger, + manifest: { permissions: { files: { type: 'io.cozy.files' } } }, + konnector: { + slug: 'testkonnector', + name: 'Test Konnector' + }, + launcherClient: { + setAppMetadata: () => null + } + }) + client.query.mockResolvedValue({ data: fixtures.account }) + client.queryAll.mockResolvedValueOnce([ + { + name: 'file1.txt', + metadata: { fileIdAttributes: 'sourceaccountidentifier1' } + } + ]) + client.save.mockImplementation(async doc => ({ data: doc })) + launch.mockResolvedValue({ data: fixtures.job }) + launcher.pilot.call + .mockResolvedValueOnce(true) + .mockResolvedValueOnce(true) + .mockResolvedValueOnce({ + sourceAccountIdentifier: 'testsourceaccountidentifier' + }) + .mockResolvedValueOnce(true) // fetch + await launcher.start() + expect(launcher.pilot.call).toHaveBeenLastCalledWith( + 'fetch', + expect.objectContaining({ + existingFilesIndex: { + sourceaccountidentifier1: { + name: 'file1.txt', + metadata: { fileIdAttributes: 'sourceaccountidentifier1' } + } + } + }) + ) + }) it('should update job with error message on error', async () => { const { launcher, client, launch } = setup() launcher.setStartContext({ client, account: fixtures.account, trigger: fixtures.trigger, + manifest: {}, konnector: { slug: 'testkonnector', name: 'Test Konnector'