@openfn/ws-worker 0.2.4 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # ws-worker
2
2
 
3
+ ## 0.2.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 0fb2d58: correctly handle input_dataclip_id for runs
8
+ - Updated dependencies [c8e9d51]
9
+ - Updated dependencies [7f352d2]
10
+ - @openfn/engine-multi@0.1.10
11
+ - @openfn/runtime@0.1.3
12
+
13
+ ## 0.2.5
14
+
15
+ ### Patch Changes
16
+
17
+ - 36337d7: When calculating exit reasons, exclude non-executed downstream nodes in leaf calculation. (i.e., look at the final executed node in each branch when determining the attempt exit reason.)
18
+
3
19
  ## 0.2.4
4
20
 
5
21
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import Koa from 'koa';
2
2
  import { SanitizePolicies, Logger } from '@openfn/logger';
3
3
  import { Channel as Channel$1 } from 'phoenix';
4
- import { ExecutionPlan } from '@openfn/runtime';
5
4
  import { RuntimeEngine } from '@openfn/engine-multi';
6
5
 
7
6
  type ExitReasonStrings =
@@ -23,6 +22,22 @@ type AttemptOptions = {
23
22
  sanitize?: SanitizePolicies;
24
23
  };
25
24
 
25
+ // Internal server state for each attempt
26
+ type AttemptState = {
27
+ activeRun?: string;
28
+ activeJob?: string;
29
+ plan: ExecutionPlan;
30
+ options: AttemptOptions;
31
+ dataclips: Record<string, any>;
32
+ // For each run, map the input ids
33
+ // TODO better name maybe?
34
+ inputDataclips: Record<string, string>;
35
+ reasons: Record<string, ExitReason>;
36
+
37
+ // final dataclip id
38
+ lastDataclipId?: string;
39
+ };
40
+
26
41
  type ReceiveHook = {
27
42
  receive: (
28
43
  status: 'ok' | 'timeout' | 'error',
@@ -45,15 +60,6 @@ interface Channel extends Channel$1 {
45
60
  // join: () => ReceiveHook;
46
61
  }
47
62
 
48
- declare type AttemptState = {
49
- activeRun?: string;
50
- activeJob?: string;
51
- plan: ExecutionPlan;
52
- options: AttemptOptions;
53
- dataclips: Record<string, any>;
54
- reasons: Record<string, ExitReason>;
55
- lastDataclipId?: string;
56
- };
57
63
  declare type Context = {
58
64
  channel: Channel;
59
65
  state: AttemptState;
package/dist/index.js CHANGED
@@ -215,6 +215,35 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
215
215
  return value;
216
216
  });
217
217
 
218
+ // src/util/create-attempt-state.ts
219
+ var create_attempt_state_default = (plan, options = {}) => {
220
+ const state = {
221
+ plan,
222
+ lastDataclipId: "",
223
+ dataclips: {},
224
+ inputDataclips: {},
225
+ reasons: {},
226
+ options
227
+ };
228
+ if (typeof plan.initialState === "string") {
229
+ let startNode = plan.jobs[0];
230
+ if (plan.start) {
231
+ startNode = plan.jobs.find(({ id }) => id === plan.start);
232
+ }
233
+ const initialRuns = [];
234
+ if (!startNode.expression) {
235
+ initialRuns.push(...Object.keys(startNode.next));
236
+ } else {
237
+ initialRuns.push(startNode.id);
238
+ }
239
+ initialRuns.forEach((id) => {
240
+ state.inputDataclips[id] = plan.initialState;
241
+ });
242
+ } else {
243
+ }
244
+ return state;
245
+ };
246
+
218
247
  // src/api/reasons.ts
219
248
  var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
220
249
  let reason = "success";
@@ -230,9 +259,16 @@ var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
230
259
  }
231
260
  return { reason, error_type, error_message };
232
261
  };
262
+ var isLeafNode = (state, job) => {
263
+ if (!job.next || Object.keys(job.next).length == 0) {
264
+ return true;
265
+ }
266
+ const hasDownstream = Object.keys(job.next).find((id) => state.reasons[id]);
267
+ return !hasDownstream;
268
+ };
233
269
  var calculateAttemptExitReason = (state) => {
234
270
  if (state.plan && state.reasons) {
235
- const leafJobReasons = state.plan.jobs.filter(({ next }) => !next || Object.keys(next).length == 0).map(({ id }) => state.reasons[id]);
271
+ const leafJobReasons = state.plan.jobs.filter((job) => isLeafNode(state, job)).map(({ id }) => state.reasons[id]);
236
272
  const fail = leafJobReasons.find((r) => r && r.reason === "fail");
237
273
  if (fail) {
238
274
  return fail;
@@ -250,17 +286,10 @@ var eventMap = {
250
286
  "workflow-log": ATTEMPT_LOG,
251
287
  "workflow-complete": ATTEMPT_COMPLETE
252
288
  };
253
- var createAttemptState = (plan, options = {}) => ({
254
- plan,
255
- lastDataclipId: plan.initialState,
256
- dataclips: {},
257
- reasons: {},
258
- options
259
- });
260
289
  function execute(channel, engine, logger, plan, options = {}, onComplete = (_result) => {
261
290
  }) {
262
- logger.info("execute...");
263
- const state = createAttemptState(plan, options);
291
+ logger.info("executing ", plan.id);
292
+ const state = create_attempt_state_default(plan, options);
264
293
  const context = { channel, state, logger, onComplete };
265
294
  const addEvent = (eventName, handler) => {
266
295
  const wrappedFn = async (event) => {
@@ -305,7 +334,11 @@ function execute(channel, engine, logger, plan, options = {}, onComplete = (_res
305
334
  try {
306
335
  engine.execute(plan, { resolvers, ...options });
307
336
  } catch (e) {
308
- onWorkflowError(context, { workflowId: plan.id, message: e.message });
337
+ onWorkflowError(context, {
338
+ workflowId: plan.id,
339
+ message: e.message,
340
+ type: e.type
341
+ });
309
342
  }
310
343
  });
311
344
  return context;
@@ -316,10 +349,11 @@ var sendEvent = (channel, event, payload) => new Promise((resolve, reject) => {
316
349
  function onJobStart({ channel, state }, event) {
317
350
  state.activeRun = crypto2.randomUUID();
318
351
  state.activeJob = event.jobId;
352
+ const input_dataclip_id = state.inputDataclips[event.jobId];
319
353
  return sendEvent(channel, RUN_START, {
320
354
  run_id: state.activeRun,
321
355
  job_id: state.activeJob,
322
- input_dataclip_id: state.lastDataclipId
356
+ input_dataclip_id
323
357
  });
324
358
  }
325
359
  function onJobError(context, event) {
@@ -339,27 +373,27 @@ function onJobComplete({ channel, state }, event, error) {
339
373
  }
340
374
  state.dataclips[dataclipId] = event.state;
341
375
  state.lastDataclipId = dataclipId;
376
+ event.next?.forEach((nextJobId) => {
377
+ state.inputDataclips[nextJobId] = dataclipId;
378
+ });
342
379
  delete state.activeRun;
343
380
  delete state.activeJob;
344
- try {
345
- const { reason, error_message, error_type } = calculateJobExitReason(
346
- job_id,
347
- event.state,
348
- error
349
- );
350
- state.reasons[job_id] = { reason, error_message, error_type };
351
- return sendEvent(channel, RUN_COMPLETE, {
352
- run_id,
353
- job_id,
354
- output_dataclip_id: dataclipId,
355
- output_dataclip: stringify_default(event.state),
356
- reason,
357
- error_message,
358
- error_type
359
- });
360
- } catch (e) {
361
- console.log(e);
362
- }
381
+ const { reason, error_message, error_type } = calculateJobExitReason(
382
+ job_id,
383
+ event.state,
384
+ error
385
+ );
386
+ state.reasons[job_id] = { reason, error_message, error_type };
387
+ const evt = {
388
+ run_id,
389
+ job_id,
390
+ output_dataclip_id: dataclipId,
391
+ output_dataclip: stringify_default(event.state),
392
+ reason,
393
+ error_message,
394
+ error_type
395
+ };
396
+ return sendEvent(channel, RUN_COMPLETE, evt);
363
397
  }
364
398
  function onWorkflowStart({ channel }, _event) {
365
399
  return sendEvent(channel, ATTEMPT_START);
package/dist/start.js CHANGED
@@ -4939,7 +4939,13 @@ async function createMock() {
4939
4939
  nextState = initialState;
4940
4940
  }
4941
4941
  }
4942
- dispatch("job-complete", { workflowId, jobId, state: nextState, runId });
4942
+ dispatch("job-complete", {
4943
+ workflowId,
4944
+ jobId,
4945
+ state: nextState,
4946
+ runId,
4947
+ next: []
4948
+ });
4943
4949
  return nextState;
4944
4950
  };
4945
4951
  const execute2 = (xplan, options = {
@@ -5197,6 +5203,35 @@ var stringify_default = (obj) => stringify(obj, (_key, value) => {
5197
5203
  return value;
5198
5204
  });
5199
5205
 
5206
+ // src/util/create-attempt-state.ts
5207
+ var create_attempt_state_default = (plan, options = {}) => {
5208
+ const state = {
5209
+ plan,
5210
+ lastDataclipId: "",
5211
+ dataclips: {},
5212
+ inputDataclips: {},
5213
+ reasons: {},
5214
+ options
5215
+ };
5216
+ if (typeof plan.initialState === "string") {
5217
+ let startNode = plan.jobs[0];
5218
+ if (plan.start) {
5219
+ startNode = plan.jobs.find(({ id }) => id === plan.start);
5220
+ }
5221
+ const initialRuns = [];
5222
+ if (!startNode.expression) {
5223
+ initialRuns.push(...Object.keys(startNode.next));
5224
+ } else {
5225
+ initialRuns.push(startNode.id);
5226
+ }
5227
+ initialRuns.forEach((id) => {
5228
+ state.inputDataclips[id] = plan.initialState;
5229
+ });
5230
+ } else {
5231
+ }
5232
+ return state;
5233
+ };
5234
+
5200
5235
  // src/api/reasons.ts
5201
5236
  var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
5202
5237
  let reason = "success";
@@ -5212,9 +5247,16 @@ var calculateJobExitReason = (jobId, state = { data: {} }, error) => {
5212
5247
  }
5213
5248
  return { reason, error_type, error_message };
5214
5249
  };
5250
+ var isLeafNode = (state, job) => {
5251
+ if (!job.next || Object.keys(job.next).length == 0) {
5252
+ return true;
5253
+ }
5254
+ const hasDownstream = Object.keys(job.next).find((id) => state.reasons[id]);
5255
+ return !hasDownstream;
5256
+ };
5215
5257
  var calculateAttemptExitReason = (state) => {
5216
5258
  if (state.plan && state.reasons) {
5217
- const leafJobReasons = state.plan.jobs.filter(({ next }) => !next || Object.keys(next).length == 0).map(({ id }) => state.reasons[id]);
5259
+ const leafJobReasons = state.plan.jobs.filter((job) => isLeafNode(state, job)).map(({ id }) => state.reasons[id]);
5218
5260
  const fail = leafJobReasons.find((r) => r && r.reason === "fail");
5219
5261
  if (fail) {
5220
5262
  return fail;
@@ -5232,17 +5274,10 @@ var eventMap = {
5232
5274
  "workflow-log": ATTEMPT_LOG,
5233
5275
  "workflow-complete": ATTEMPT_COMPLETE
5234
5276
  };
5235
- var createAttemptState = (plan, options = {}) => ({
5236
- plan,
5237
- lastDataclipId: plan.initialState,
5238
- dataclips: {},
5239
- reasons: {},
5240
- options
5241
- });
5242
5277
  function execute(channel, engine, logger2, plan, options = {}, onComplete = (_result) => {
5243
5278
  }) {
5244
- logger2.info("execute...");
5245
- const state = createAttemptState(plan, options);
5279
+ logger2.info("executing ", plan.id);
5280
+ const state = create_attempt_state_default(plan, options);
5246
5281
  const context = { channel, state, logger: logger2, onComplete };
5247
5282
  const addEvent = (eventName, handler) => {
5248
5283
  const wrappedFn = async (event) => {
@@ -5287,7 +5322,11 @@ function execute(channel, engine, logger2, plan, options = {}, onComplete = (_re
5287
5322
  try {
5288
5323
  engine.execute(plan, { resolvers, ...options });
5289
5324
  } catch (e) {
5290
- onWorkflowError(context, { workflowId: plan.id, message: e.message });
5325
+ onWorkflowError(context, {
5326
+ workflowId: plan.id,
5327
+ message: e.message,
5328
+ type: e.type
5329
+ });
5291
5330
  }
5292
5331
  });
5293
5332
  return context;
@@ -5298,10 +5337,11 @@ var sendEvent = (channel, event, payload) => new Promise((resolve5, reject) => {
5298
5337
  function onJobStart({ channel, state }, event) {
5299
5338
  state.activeRun = crypto3.randomUUID();
5300
5339
  state.activeJob = event.jobId;
5340
+ const input_dataclip_id = state.inputDataclips[event.jobId];
5301
5341
  return sendEvent(channel, RUN_START, {
5302
5342
  run_id: state.activeRun,
5303
5343
  job_id: state.activeJob,
5304
- input_dataclip_id: state.lastDataclipId
5344
+ input_dataclip_id
5305
5345
  });
5306
5346
  }
5307
5347
  function onJobError(context, event) {
@@ -5321,27 +5361,27 @@ function onJobComplete({ channel, state }, event, error) {
5321
5361
  }
5322
5362
  state.dataclips[dataclipId] = event.state;
5323
5363
  state.lastDataclipId = dataclipId;
5364
+ event.next?.forEach((nextJobId) => {
5365
+ state.inputDataclips[nextJobId] = dataclipId;
5366
+ });
5324
5367
  delete state.activeRun;
5325
5368
  delete state.activeJob;
5326
- try {
5327
- const { reason, error_message, error_type } = calculateJobExitReason(
5328
- job_id,
5329
- event.state,
5330
- error
5331
- );
5332
- state.reasons[job_id] = { reason, error_message, error_type };
5333
- return sendEvent(channel, RUN_COMPLETE, {
5334
- run_id,
5335
- job_id,
5336
- output_dataclip_id: dataclipId,
5337
- output_dataclip: stringify_default(event.state),
5338
- reason,
5339
- error_message,
5340
- error_type
5341
- });
5342
- } catch (e) {
5343
- console.log(e);
5344
- }
5369
+ const { reason, error_message, error_type } = calculateJobExitReason(
5370
+ job_id,
5371
+ event.state,
5372
+ error
5373
+ );
5374
+ state.reasons[job_id] = { reason, error_message, error_type };
5375
+ const evt = {
5376
+ run_id,
5377
+ job_id,
5378
+ output_dataclip_id: dataclipId,
5379
+ output_dataclip: stringify_default(event.state),
5380
+ reason,
5381
+ error_message,
5382
+ error_type
5383
+ };
5384
+ return sendEvent(channel, RUN_COMPLETE, evt);
5345
5385
  }
5346
5386
  function onWorkflowStart({ channel }, _event) {
5347
5387
  return sendEvent(channel, ATTEMPT_START);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openfn/ws-worker",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "A Websocket Worker to connect Lightning to a Runtime Engine",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -21,9 +21,9 @@
21
21
  "koa-logger": "^3.2.1",
22
22
  "phoenix": "^1.7.7",
23
23
  "ws": "^8.14.1",
24
- "@openfn/engine-multi": "0.1.9",
24
+ "@openfn/engine-multi": "0.1.10",
25
25
  "@openfn/logger": "0.0.19",
26
- "@openfn/runtime": "0.1.2"
26
+ "@openfn/runtime": "0.1.3"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/koa": "^2.13.5",
@@ -40,7 +40,7 @@
40
40
  "tsup": "^6.2.3",
41
41
  "typescript": "^4.6.4",
42
42
  "yargs": "^17.6.2",
43
- "@openfn/lightning-mock": "1.0.10"
43
+ "@openfn/lightning-mock": "1.0.11"
44
44
  },
45
45
  "files": [
46
46
  "dist",