Synchronize a workspace
This recipe synchronizes one dossier of your software into Ubikap, then waits for the result. It reuses the client and the makeRpc transport of Getting Started.
1. Log in for the office
Section titled “1. Log in for the office”const { token } = await authenticationService.LogIn({ officeScope: { target: { office: { externalId: officeId } } }, userUuid: SYNCHRO_USER_UUID, userCode: currentTotpCode(),});
const synchronizationService = new SynchronizationServiceClientImpl(makeRpc(token));The token lasts 30 minutes: log in at the start of each run instead of reusing an older token. See Authentication.
2. Post the tasks
Section titled “2. Post the tasks”const source = { credential: { externalId: userId } };
const { synchronizationId } = await synchronizationService.PostAsyncTasks({ tasks: [ { synchronizeOfficeUsers: { target: {}, source, }, }, { synchronizeFullWorkspace: { target: { workspace: { externalId: dossierId } }, source, }, }, ],});userId is the user of your software on whose behalf the data is read, typically the one who changed the dossier. Store synchronizationId: it is the only handle on the synchronization.
synchronizeOfficeUsers brings the office’s users up to date first: the dossier’s accesses name its manager and collaborators, who must exist in Ubikap, or synchronizeWorkspaceAccesses fails. It always runs before the workspace tasks, whatever their order in tasks, and at most once a day per office: when the users were already synchronized today, it ends done at once, so posting it with every synchronization costs nothing.
Several dossiers can go in the same call, one task each. A task type that is not implemented makes the whole call fail with INVALID_ARGUMENT, and nothing is queued: see Task types.
3. Poll the state
Section titled “3. Poll the state”const POLL_INTERVAL_MS = 5_000;const POLL_TIMEOUT_MS = 10 * 60_000;
const waitForSynchronization = async (synchronizationId: string) => { const giveUpAt = Date.now() + POLL_TIMEOUT_MS;
while (Date.now() < giveUpAt) { const { tasks, stats } = await synchronizationService.GetSynchroState({ synchronizationId });
if (stats && stats.pendingCount === 0 && stats.runningCount === 0) { return { tasks, failedCount: stats.failedCount }; }
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS)); }
throw new Error(`Synchronization ${synchronizationId} still running`);};
const { tasks, failedCount } = await waitForSynchronization(synchronizationId);
if (failedCount > 0) { console.warn(tasks.filter((task) => task.state === 'failed').map((task) => task.type));}Over REST, the counters are JSON strings: compare stats.pendingCount === "0", or convert them first.
A synchronization with failed tasks is retried automatically, so failed is the state reached after the retries. Its tasks report which part failed, not why: check that the dossier, its users and the credential exist in your software, then post a new synchronization.
If a synchronization keeps failing, or stays pending or running for longer than your timeout, contact Ubikap support with its synchronizationId, the office’s externalId and the dossier’s externalId: we can see why a task failed, and run the synchronization again.
4. Show the last synchronization
Section titled “4. Show the last synchronization”To display when a dossier was last synchronized, without keeping the ids:
const { lastSuccessful, failed } = await synchronizationService.GetGlobalSynchroState({ filter: { workspace: { externalId: dossierId } },});
const lastSynchronizedAt = lastSuccessful?.date; // When that synchronization was postedconst needsRetry = (failed?.synchronizationIds.length ?? 0) > 0;Checklist
Section titled “Checklist”- A fresh
LogInfor each run, one token per office: a token lasts 30 minutes. synchronizeOfficeUsersposted with the workspace tasks, so that the dossier’s users exist in Ubikap.externalIds are ids of your software, never Ubikap keys.- Poll
GetSynchroStateat a reasonable pace (every few seconds), with an overall timeout. - A
failedtask has already been retried: fix the cause before posting again.