Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow clisk konnectors to run server job (SCR-845) #1260

Merged
merged 3 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/libs/Launcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,58 @@
}
}

/**
* Allow client konnector to run a server version of itself. Wait for the end of the execution
* and the returns the result of the execution
*
* @param {Object} args - any arguments we want to pass to server job. Must be serializable as string
* @param {Object} [options] - options object
* @param {Number} [options.timeout] - timeout in ms
* @param {Number} [options.max_exec_count] - maximum errored execution count
* @returns {Promise<Object>}
*/
async runServerJob(args = {}, options = {}) {
const { client, konnector, trigger, account } = this.getStartContext() || {}
const jobArgs = {
...args,
konnector: konnector.slug,
account: account._id,
folder_to_save: trigger.message?.folder_to_save
}
const { data: job } = await client
.collection('io.cozy.jobs')
.create('konnector', jobArgs, options, true)

Check failure on line 494 in src/libs/Launcher.js

View workflow job for this annotation

GitHub Actions / Quality Checks

Property 'create' does not exist on type 'Collection'.
return await client.collection('io.cozy.jobs').waitFor(job.id)

Check failure on line 495 in src/libs/Launcher.js

View workflow job for this annotation

GitHub Actions / Quality Checks

Property 'waitFor' does not exist on type 'Collection'.
paultranvan marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Save data in the current account's data attribute
*
* @param {Object} data - any object serializable with JSON.stringify
* @returns <Promise<import('cozy-client/types/types').IOCozyAccount>>
*/
async saveAccountData(data) {
const { launcherClient: client, account } = this.getStartContext() || {}

if (!account._id) {
throw new Error(
'Launcher: No associated account. Cannot save account data yet'
)
}

const { data: currentAccount } = await client.query(
Q('io.cozy.accounts').getById(account._id)
)
currentAccount.data = data
const { data: newAccount } = await client.save(currentAccount)

this.setStartContext({
...this.getStartContext(),
account: newAccount
})
return newAccount
}

/**
* Calls cozy-konnector-libs' saveFiles function
*
Expand Down
14 changes: 13 additions & 1 deletion src/libs/ReactNativeLauncher.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,15 @@ class ReactNativeLauncher extends Launcher {
'saveIdentity',
'queryAll',
'setUserAgent',
'setIncognito',
'getCredentials',
'saveCredentials',
'getCookiesByDomain',
'saveCookieToKeychain',
'getCookieByDomainAndName',
'getCookieFromKeychainByName'
'getCookieFromKeychainByName',
'saveAccountData',
'runServerJob'
],
listenedEventsNames: ['log']
}),
Expand Down Expand Up @@ -581,6 +584,15 @@ class ReactNativeLauncher extends Launcher {
this.emit('SET_USER_AGENT', userAgent)
}

/**
* Set incognito mode for worker webview
*
* @param {Boolean} incognito
*/
async setIncognito(incognito) {
this.emit('SET_INCOGNITO', incognito)
}

/**
* Run the specified method in the worker and make it fail with WORKER_WILL_RELOAD message
* if the worker page is reloaded
Expand Down
45 changes: 43 additions & 2 deletions src/libs/ReactNativeLauncher.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ describe('ReactNativeLauncher', () => {
const ensureDirectoryExists = jest.fn()
const addReferencesTo = jest.fn()
const get = jest.fn().mockResolvedValue({ data: { _id: 'testfolderid' } })
const create = jest.fn()
const waitFor = jest.fn()
const statByPath = jest
.fn()
.mockResolvedValue({ data: { _id: 'testfolderid' } })
Expand All @@ -93,7 +95,9 @@ describe('ReactNativeLauncher', () => {
get,
statByPath,
statById,
add
add,
create,
waitFor
}),
getInstanceOptions: jest.fn().mockReturnValueOnce({ locale: 'fr' })
}
Expand All @@ -103,10 +107,12 @@ describe('ReactNativeLauncher', () => {
client,
findReferencedBy,
get,
create,
ensureDirectoryExists,
addReferencesTo,
statByPath,
add
add,
waitFor
}
}

Expand Down Expand Up @@ -947,4 +953,39 @@ describe('ReactNativeLauncher', () => {
).rejects.toEqual(ERRORS.SET_WORKER_STATE_TOO_LONG_TO_INIT)
})
})
describe('runServerJob', () => {
it('should create job with proper parameters', async () => {
const { launcher, client, create, waitFor } = setup()
create.mockResolvedValueOnce({ data: { id: 'testjobid' } })
waitFor.mockResolvedValueOnce({ data: { id: 'testjobid' } })
const konnector = {
slug: 'konnectorslug',
clientSide: true
}
launcher.setStartContext({
client,
konnector,
manifest: konnector,
launcherClient: {
setAppMetadata: () => null
},
account: fixtures.account,
trigger: fixtures.trigger
})
const result = await launcher.runServerJob({ test: 'testvalue' })
expect(create).toHaveBeenCalledTimes(1)
expect(create).toHaveBeenCalledWith(
'konnector',
{
test: 'testvalue',
account: 'normal_account_id',
folder_to_save: 'testfolderid',
konnector: 'konnectorslug'
},
{},
true
)
expect(result).toEqual({ data: { id: 'testjobid' } })
})
})
})
5 changes: 5 additions & 0 deletions src/screens/konnectors/LauncherView.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export class LauncherView extends Component {
this.workerWebview = null
this.state = {
userAgent: undefined,
incognito: false,
konnector: null,
workerInnerUrl: null,
worker: {},
Expand Down Expand Up @@ -220,6 +221,9 @@ export class LauncherView extends Component {
this.launcher.on('SET_USER_AGENT', userAgent => {
this.setState({ userAgent })
})
this.launcher.on('SET_INCOGNITO', incognito => {
this.setState({ incognito })
})
this.launcher.on('CREATED_ACCOUNT', this.onCreatedAccount)
this.launcher.on('CREATED_JOB', this.onCreatedJob)
this.launcher.on('STOPPED_JOB', this.onStoppedJob)
Expand Down Expand Up @@ -304,6 +308,7 @@ export class LauncherView extends Component {
javaScriptEnabled={true}
onContentProcessDidTerminate={this.onWorkerWebviewKilled}
onRenderProcessGone={this.onWorkerWebviewKilled}
incognito={this.state.incognito}
userAgent={this.state.userAgent}
source={{
uri: this.state.worker.url
Expand Down
Loading