Skip to content

Commit

Permalink
feat: Wait for startLaucher resolution
Browse files Browse the repository at this point in the history
To put the connection flow back to IDLE when the user abort the clisk
connection

konnectorPolicy.onLauch is not blocking because the signal is only sent
when the execution is aborted. If the execution continues after login, a
job is created and the rest of the process will work using the job test
and realtime
  • Loading branch information
doubleface authored and doubleface committed Mar 17, 2023
1 parent ac43bba commit 5f322b7
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 15 deletions.
15 changes: 10 additions & 5 deletions packages/cozy-harvest-lib/src/models/ConnectionFlow.js
Expand Up @@ -627,11 +627,16 @@ export class ConnectionFlow {
this.account = await prepareTriggerAccount(this.client, this.trigger)
}
if (konnectorPolicy.onLaunch) {
konnectorPolicy.onLaunch({
konnector: this.konnector,
trigger: this.trigger,
account: this.account
})
konnectorPolicy
.onLaunch({
konnector: this.konnector,
trigger: this.trigger,
account: this.account
})
.then(() => this.setState({ status: IDLE }))
.catch(err =>
logger.error(`Error while launching policy : ${err.message}`)
)
}
if (!konnectorPolicy.needsTriggerLaunch) {
return
Expand Down
56 changes: 48 additions & 8 deletions packages/cozy-harvest-lib/src/models/ConnectionFlow.spec.js
@@ -1,5 +1,15 @@
import EventEmitter from 'events'

import { waitFor } from '@testing-library/react'

import { Q } from 'cozy-client'

import ConnectionFlow from './ConnectionFlow'
import { ERRORED, EXPECTING_TRIGGER_LAUNCH } from './flowEvents'
import KonnectorJobWatcher, {
watchKonnectorJob
} from './konnector/KonnectorJobWatcher'
import fixtures from '../../test/fixtures'
import { saveAccount } from '../connections/accounts'
import {
createTrigger,
Expand All @@ -8,15 +18,9 @@ import {
prepareTriggerAccount,
launchTrigger
} from '../connections/triggers'
import KonnectorJobWatcher, {
watchKonnectorJob
} from './konnector/KonnectorJobWatcher'
import { KonnectorJobError } from '../helpers/konnectors'
import { konnectorPolicy as biKonnectorPolicy } from '../policies/budget-insight'
import fixtures from '../../test/fixtures'
import sentryHub from '../sentry'
import { Q } from 'cozy-client'
import { KonnectorJobError } from '../helpers/konnectors'
import { ERRORED, EXPECTING_TRIGGER_LAUNCH } from './flowEvents'

jest.mock('./konnector/KonnectorJobWatcher')
jest.mock('../sentry', () => {
Expand Down Expand Up @@ -185,7 +189,7 @@ describe('ConnectionFlow', () => {
)
})

it('should watch watch future jobs for a trigger if new fetched trigger is running', async () => {
it('should watch future jobs for a trigger if new fetched trigger is running', async () => {
const { flow } = setup({ trigger: fixtures.createdTrigger })
expect(fetchTrigger).not.toHaveBeenCalled()
fetchTrigger.mockResolvedValueOnce(fixtures.runningTrigger)
Expand Down Expand Up @@ -379,6 +383,42 @@ describe('ConnectionFlow', () => {
delete window.cozy
delete window.ReactNativeWebView
})

it('should set the flow status to idle if the launcher aborts', async () => {
prepareTriggerAccount.mockImplementation(
async () => fixtures.existingAccount
)
const { client } = setup()
const flow = new ConnectionFlow(
client,
fixtures.existingTrigger,
fixtures.clientKonnector
)
window.cozy = {
ClientKonnectorLauncher: 'react-native'
}
window.ReactNativeWebView = {
postMessage: jest.fn()
}

await flow.launch()
await waitFor(() =>
expect(flow.getState().status).toStrictEqual('PENDING')
)

window.postMessage(
JSON.stringify({
type: 'Clisk',
message: 'launchResult',
param: { message: 'abort' }
}),
'*'
)
await waitFor(() => expect(flow.getState().status).toStrictEqual('IDLE'))

delete window.cozy
delete window.ReactNativeWebView
})
})

describe('handleFormSubmit', () => {
Expand Down
61 changes: 59 additions & 2 deletions packages/cozy-harvest-lib/src/policies/clisk.js
Expand Up @@ -32,15 +32,56 @@ function isRunnable() {
*
* @param {Object} options - options object
* @param {import('cozy-client/types/types').KonnectorsDoctype} options.konnector - konnector object
* @param {import('cozy-client/types/types').AccountsDoctype} options.account - account object
* @param {import('cozy-client/types/types').TriggersDoctype} options.trigger - trigger object
* @returns {Promise<StartLauncherResult> | void}
*/
function onLaunch({ konnector, account, trigger }) {
async function onLaunch({ konnector, account, trigger }) {
const launcher = getLauncher()
if (launcher) {
logger.debug('Found a launcher', launcher)
} else {
logger.warn('Found no launcher')
}
if (launcher === 'react-native') {
return startLauncher({ konnector, account, trigger })
}
return
}

/**
* @typedef StartLauncherResult
* @property {string} message - startLauncher result message
*/

/**
* Run the current clisk launcher with postMessage. Will wait for a return message the be sent to
* resolve the promise
*
* @param {Object} options - options object
* @param {import('cozy-client/types/types').KonnectorsDoctype} options.konnector - konnector object
* @param {import('cozy-client/types/types').AccountsDoctype} options.account - account object
* @param {import('cozy-client/types/types').TriggersDoctype} options.trigger - trigger object
* @returns {Promise<StartLauncherResult>}
*/
function startLauncher({ konnector, account, trigger }) {
return new Promise(resolve => {
addMessageListener(rawData => {
if (typeof rawData !== 'string') {
return
}

const dataPayload = JSON.parse(rawData)

if (
dataPayload.type !== 'Clisk' ||
dataPayload.message !== 'launchResult'
) {
return
}

resolve(dataPayload.param)
})
// @ts-ignore ReactNativeWebview is injected by react-native launcher
window.ReactNativeWebView.postMessage(
JSON.stringify({
Expand All @@ -53,7 +94,23 @@ function onLaunch({ konnector, account, trigger }) {
}
})
)
}
})
}

/**
* Listens to postMessage event. Compatible with android and ios webviews
*
* @param {Function} callback - callback function
*/
function addMessageListener(callback) {
// Android's WebView messaging interface
window.document?.addEventListener?.('message', function (e) {
callback(e.data)
})
// iOS's WebView messaging interface
window?.addEventListener?.('message', function (e) {
callback(e.data)
})
}

export const konnectorPolicy = {
Expand Down

0 comments on commit 5f322b7

Please sign in to comment.