Skip to content
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
6 changes: 6 additions & 0 deletions packages/ws-worker/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# ws-worker

## 1.23.6

### Patch Changes

- Harden empty state detection in final_state processing

## 1.23.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/ws-worker/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openfn/ws-worker",
"version": "1.23.5",
"version": "1.23.6",
"description": "A Websocket Worker to connect Lightning to a Runtime Engine",
"main": "dist/index.js",
"type": "module",
Expand Down
30 changes: 24 additions & 6 deletions packages/ws-worker/src/events/run-complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,34 @@ import { timeInMicroseconds } from '../util';
import { sendEvent } from '../util/send-event';

const isEmptyState = (obj: any) => {
if (Object.keys(obj).length == 0) {
return true;
}
if (
Object.keys(obj).length == 1 &&
'data' in obj &&
!Object.keys(obj.data).length
typeof obj === 'string' ||
typeof obj === 'number' ||
typeof obj === 'boolean' ||
obj === null
) {
return false;
}

if (typeof obj === 'undefined') {
// ignore undefined
return true;
}

try {
if (Object.keys(obj).length == 0) {
return true;
}
if (
Object.keys(obj).length == 1 &&
'data' in obj &&
!Object.keys(obj.data).length
) {
return true;
}
} catch (e) {
// do nothing
}
return false;
};

Expand Down
67 changes: 61 additions & 6 deletions packages/ws-worker/test/events/run-complete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,17 @@ test('should send final_state when there are multiple leaves', async (t) => {
});

test('should ignore empty leaf state in final_state', async (t) => {
const result = {};
const plan = createPlan();

const state = createRunState(plan);
state.leafDataclipIds = ['clip-1', 'clip-2'];
state.dataclips = {
const result = {
// two different structures of empty
a: {},
b: { data: {} },
};

const plan = createPlan();

const state = createRunState(plan);
state.leafDataclipIds = ['a', 'b'];

const channel = mockChannel({
[RUN_LOG]: () => true,
[RUN_COMPLETE]: (evt) => {
Expand All @@ -80,6 +80,61 @@ test('should ignore empty leaf state in final_state', async (t) => {
await handleRunComplete(context, event);
});

test('should handle falsy leaves in final_state', async (t) => {
const result = {
a: null,
b: 0,
c: '',
};
const plan = createPlan();

const state = createRunState(plan);
state.leafDataclipIds = ['a', 'b', 'c'];

const channel = mockChannel({
[RUN_LOG]: () => true,
[RUN_COMPLETE]: (evt) => {
t.deepEqual(evt.final_state, {
a: null,
b: 0,
c: '',
});
t.falsy(evt.final_dataclip_id);
},
});

const event: any = { state: result };

const context: any = { channel, state, onFinish: () => {} };
await handleRunComplete(context, event);
});

test('should treat undefined leaves as empty state', async (t) => {
const result = {
a: undefined,
b: { x: 1 },
};
const plan = createPlan();

const state = createRunState(plan);
state.leafDataclipIds = ['a', 'b', 'c', 'd'];

const channel = mockChannel({
[RUN_LOG]: () => true,
[RUN_COMPLETE]: (evt) => {
t.deepEqual(evt.final_state, {
b: { x: 1 },
});
t.falsy(evt.final_dataclip_id);
},
});

const event: any = { state: result };

const context: any = { channel, state, onFinish: () => {} };
await handleRunComplete(context, event);
});

test('should include a timestamp', async (t) => {
const plan = createPlan();

Expand Down