Skip to content

Commit 84df18a

Browse files
feat: add waitForTaskCompletion
1 parent c24ee48 commit 84df18a

File tree

5 files changed

+192
-9
lines changed

5 files changed

+192
-9
lines changed

packages/sdk/src/lib/dataProtectorCore/IExecDataProtectorCore.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
RevokedAccess,
1818
TransferParams,
1919
TransferResponse,
20+
WaitForTaskCompletionResponse,
21+
WaitForTaskCompletionParams,
2022
} from '../types/index.js';
2123
import { getGrantedAccess } from './getGrantedAccess.js';
2224
import { getProtectedData } from './getProtectedData.js';
@@ -27,6 +29,7 @@ import { protectData } from './protectData.js';
2729
import { revokeAllAccess } from './revokeAllAccess.js';
2830
import { revokeOneAccess } from './revokeOneAccess.js';
2931
import { transferOwnership } from './transferOwnership.js';
32+
import { waitForTaskCompletion } from './waitForTaskCompletion.js';
3033

3134
class IExecDataProtectorCore extends IExecDataProtectorModule {
3235
async protectData(
@@ -99,6 +102,17 @@ class IExecDataProtectorCore extends IExecDataProtectorModule {
99102
return getGrantedAccess({ ...args, iexec: this.iexec });
100103
}
101104

105+
async waitForTaskCompletion(
106+
args: WaitForTaskCompletionParams
107+
): Promise<WaitForTaskCompletionResponse> {
108+
await this.init();
109+
await isValidProvider(this.iexec);
110+
return waitForTaskCompletion({
111+
...args,
112+
iexec: this.iexec,
113+
});
114+
}
115+
102116
async getResultFromCompletedTask(
103117
args: GetResultFromCompletedTaskParams
104118
): Promise<GetResultFromCompletedTaskResponse> {

packages/sdk/src/lib/dataProtectorCore/processProtectedData.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { IExecConsumer, VoucherInfo } from '../types/internalTypes.js';
4040
import { getResultFromCompletedTask } from './getResultFromCompletedTask.js';
4141
import { getWhitelistContract } from './smartContract/getWhitelistContract.js';
4242
import { isAddressInWhitelist } from './smartContract/whitelistContract.read.js';
43+
import { waitForTaskCompletion } from './waitForTaskCompletion.js';
4344

4445
export type ProcessProtectedData = typeof processProtectedData;
4546

@@ -399,15 +400,11 @@ export const processProtectedData = async <
399400
taskId: taskId,
400401
},
401402
});
402-
const taskObservable = await iexec.task.obsTask(taskId, { dealid: dealid });
403-
await new Promise((resolve, reject) => {
404-
taskObservable.subscribe({
405-
next: () => {},
406-
error: (e) => {
407-
reject(e);
408-
},
409-
complete: () => resolve(undefined),
410-
});
403+
404+
await waitForTaskCompletion({
405+
iexec,
406+
dealid,
407+
taskId,
411408
});
412409

413410
vOnStatusUpdate({
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
taskIdSchema,
3+
throwIfMissing,
4+
validateOnStatusUpdateCallback,
5+
} from '../../utils/validators.js';
6+
import {
7+
OnStatusUpdateFn,
8+
WaitForTaskCompletionParams,
9+
WaitForTaskCompletionResponse,
10+
WaitForTaskCompletionStatuses,
11+
} from '../types/index.js';
12+
import { IExecConsumer } from '../types/internalTypes.js';
13+
14+
export const waitForTaskCompletion = async ({
15+
iexec = throwIfMissing(),
16+
dealid,
17+
taskId,
18+
onStatusUpdate = () => {},
19+
}: IExecConsumer &
20+
WaitForTaskCompletionParams): Promise<WaitForTaskCompletionResponse> => {
21+
const vTaskId = taskIdSchema()
22+
.required()
23+
.label('taskId')
24+
.validateSync(taskId);
25+
const vDealId = taskIdSchema()
26+
.required()
27+
.label('dealId')
28+
.validateSync(dealid);
29+
const vOnStatusUpdate =
30+
validateOnStatusUpdateCallback<
31+
OnStatusUpdateFn<WaitForTaskCompletionStatuses>
32+
>(onStatusUpdate);
33+
34+
try {
35+
const taskObservable = await iexec.task.obsTask(vTaskId, {
36+
dealid: vDealId,
37+
});
38+
let status: 'COMPLETED' | 'FAILED' | 'TIMEOUT';
39+
let success: boolean;
40+
await new Promise((resolve, reject) => {
41+
taskObservable.subscribe({
42+
next: (data) => {
43+
const isDone =
44+
data?.task?.statusName === 'COMPLETED' ||
45+
data?.task?.statusName === 'FAILED' ||
46+
data?.task?.statusName === 'TIMEOUT';
47+
if (isDone) {
48+
status = data?.task?.statusName as
49+
| 'COMPLETED'
50+
| 'FAILED'
51+
| 'TIMEOUT';
52+
success = data?.task?.statusName === 'COMPLETED';
53+
}
54+
vOnStatusUpdate({
55+
title: 'TASK_UPDATED',
56+
isDone,
57+
payload: {
58+
taskId: vTaskId,
59+
status: data?.task?.statusName,
60+
},
61+
});
62+
},
63+
error: (e) => {
64+
reject(e);
65+
},
66+
complete: () => resolve(undefined),
67+
});
68+
});
69+
return { status: status, success: success };
70+
} catch (error) {
71+
console.error('Error in waitForTaskCompletion:', error);
72+
throw new Error('Failed to wait for task completion');
73+
}
74+
};

packages/sdk/src/lib/types/coreTypes.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,21 @@ export type GrantedAccessResponse = {
208208
grantedAccess: GrantedAccess[];
209209
};
210210

211+
// ---------------------waitForTaskCompletion Types------------------------------------
212+
213+
export type WaitForTaskCompletionStatuses = 'TASK_UPDATED';
214+
215+
export type WaitForTaskCompletionParams = {
216+
taskId: string;
217+
dealid: string;
218+
onStatusUpdate?: OnStatusUpdateFn<WaitForTaskCompletionStatuses>;
219+
};
220+
221+
export type WaitForTaskCompletionResponse = {
222+
status: 'COMPLETED' | 'FAILED' | 'TIMEOUT';
223+
success: boolean;
224+
};
225+
211226
// ---------------------GetResultFromCompletedTask Types------------------------------------
212227

213228
export type GetResultFromCompletedTaskStatuses =
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { beforeAll, describe, expect, it, jest } from '@jest/globals';
2+
import { Wallet } from 'ethers';
3+
import { IExecDataProtectorCore } from '../../../src/index.js';
4+
import { getTestConfig } from '../../test-utils.js';
5+
6+
describe('dataProtectorCore.waitForTaskCompletion()', () => {
7+
let dataProtectorCore: IExecDataProtectorCore;
8+
9+
beforeAll(async () => {
10+
dataProtectorCore = new IExecDataProtectorCore(
11+
...getTestConfig(Wallet.createRandom().privateKey)
12+
);
13+
});
14+
15+
it('should return when the task is completed', async () => {
16+
// https://explorer.iex.ec/bellecour/task/0xb4655f62bdb841a3b54363b113c4204bf4fee76ab9029f33dc1218ab495970d7
17+
const onStatusUpdate = jest.fn();
18+
const COMPLETED_TASKID =
19+
'0xb4655f62bdb841a3b54363b113c4204bf4fee76ab9029f33dc1218ab495970d7';
20+
const COMPLETED_DEALID =
21+
'0xb5091be0385c80545cdd12e7c678b96dbb6338cf699324f8f2aa94d3f33f6eda';
22+
const res = await dataProtectorCore.waitForTaskCompletion({
23+
dealid: COMPLETED_DEALID,
24+
taskId: COMPLETED_TASKID,
25+
onStatusUpdate,
26+
});
27+
expect(res).toEqual({ status: 'COMPLETED', success: true });
28+
expect(onStatusUpdate).toHaveBeenLastCalledWith({
29+
title: 'TASK_UPDATED',
30+
isDone: true,
31+
payload: {
32+
taskId: COMPLETED_TASKID,
33+
status: 'COMPLETED',
34+
},
35+
});
36+
});
37+
38+
it('should return when the task is failed', async () => {
39+
// https://explorer.iex.ec/bellecour/task/0x000b16d5517e44ca70744ec156e8374ae525c9ab902169fe01d909370e5778e0
40+
const FAILED_TASKID =
41+
'0x000b16d5517e44ca70744ec156e8374ae525c9ab902169fe01d909370e5778e0';
42+
const FAILED_DEALID =
43+
'0xd613b7c6c4a022efe129fd93ce547eba71fc1055e0b42d20b11ad1f3505ad0a5';
44+
const onStatusUpdate = jest.fn();
45+
const res = await dataProtectorCore.waitForTaskCompletion({
46+
dealid: FAILED_DEALID,
47+
taskId: FAILED_TASKID,
48+
onStatusUpdate,
49+
});
50+
expect(res).toEqual({ status: 'FAILED', success: false });
51+
expect(onStatusUpdate).toHaveBeenLastCalledWith({
52+
title: 'TASK_UPDATED',
53+
isDone: true,
54+
payload: {
55+
taskId: FAILED_TASKID,
56+
status: 'FAILED',
57+
},
58+
});
59+
});
60+
61+
it('should return when the task is in timeout', async () => {
62+
// https://explorer.iex.ec/bellecour/task/0x012b3d2f21ea3c8c0cc2a40ce06df028df84d1b53b7dae98d5352e79427b93a6
63+
const TIMEOUT_TASKID =
64+
'0x012b3d2f21ea3c8c0cc2a40ce06df028df84d1b53b7dae98d5352e79427b93a6';
65+
const TIMEOUT_DEALID =
66+
'0xab15a51de7a3829fca1d3666b81b53e9e9ced0aa71bf20e7ebee1be1bdb3ee33';
67+
const onStatusUpdate = jest.fn();
68+
const res = await dataProtectorCore.waitForTaskCompletion({
69+
dealid: TIMEOUT_DEALID,
70+
taskId: TIMEOUT_TASKID,
71+
onStatusUpdate,
72+
});
73+
expect(res).toEqual({ status: 'TIMEOUT', success: false });
74+
expect(onStatusUpdate).toHaveBeenLastCalledWith({
75+
title: 'TASK_UPDATED',
76+
isDone: true,
77+
payload: {
78+
taskId: TIMEOUT_TASKID,
79+
status: 'TIMEOUT',
80+
},
81+
});
82+
});
83+
});

0 commit comments

Comments
 (0)