Skip to content

Commit

Permalink
WIP - Update Node SDK to match recent breaking changes (#159)
Browse files Browse the repository at this point in the history
* chore(version control): init commit for wip pr

* refactor(workflow-node-sdk): reduced API drift and now node sdk resembles browser sdk more closely

* refactor(workflow-core): renamed instances of workflowDefinition to definition

matches latest data model of the workflow service

* refactor(workflow-core): replaced instance of workflowDefinitionType with definitionType

* refactor(workflow-node-sdk): replaced instances of workflowDefinition with definition

matches workflow-core's latest API

* refactor(workflow-node-sdk): replaced instances of workflowDefinitionType with definitionType

* refactor(workflow-browser-sdk): renamed instances of workflowDefinition to match workflow-core's API

* refactor(workflow-browser-sdk): renamed instances of workflowDefinitionType to match workflow-core

* fix(workflow-core): fixed an instance of definition that was supposed to be definitionType

* feat(*): checkpoint

* fix(workflow-core): fixed extensions null breaking workflow-core
  • Loading branch information
Omri-Levy committed Mar 16, 2023
1 parent f0eafe7 commit f072188
Show file tree
Hide file tree
Showing 25 changed files with 294 additions and 230 deletions.
5 changes: 5 additions & 0 deletions .changeset/great-books-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ballerine/workflow-node-sdk': patch
---

reduced API drift and now node sdk resembles the browser sdk more closely
6 changes: 3 additions & 3 deletions packages/workflow-core/src/lib/create-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { TCreateWorkflow } from './types';
import { WorkflowRunner } from './workflow-runner';

export const createWorkflow: TCreateWorkflow = ({
workflowDefinition,
definition,
workflowActions,
workflowContext,
extensions
extensions,
}) =>
new WorkflowRunner({
workflowDefinition,
definition,
workflowActions,
workflowContext,
extensions,
Expand Down
6 changes: 3 additions & 3 deletions packages/workflow-core/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ export interface WorkflowContext {
}

export interface WorkflowOptions {
workflowDefinitionType: 'statechart-json' | 'bpmn-json';
workflowDefinition: MachineConfig<any, any, any>;
definitionType: 'statechart-json' | 'bpmn-json';
definition: MachineConfig<any, any, any>;
workflowActions?: MachineOptions<any, any>['actions'];
workflowContext?: WorkflowContext;
extensions?: WorkflowExtensions;
}

export interface WorkflowRunnerArgs {
workflowDefinition: MachineConfig<any, any, any>;
definition: MachineConfig<any, any, any>;
workflowActions?: MachineOptions<any, any>['actions'];
workflowContext?: WorkflowContext;
extensions?: WorkflowExtensions;
Expand Down
12 changes: 5 additions & 7 deletions packages/workflow-core/src/lib/workflow-runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import type { MachineConfig } from 'xstate';
import { expect, test } from 'vitest';
import { WorkflowRunner } from './workflow-runner';


test('restore workflow test', () => {

const userId = '123456';

const simpleMachine: MachineConfig<any, any, any> = {
Expand All @@ -17,20 +15,20 @@ test('restore workflow test', () => {
};

const service = new WorkflowRunner({
workflowDefinition: simpleMachine,
definition: simpleMachine,
workflowContext: {
machineContext: {
entityId: userId
}
entityId: userId,
},
},
extensions: {
statePlugins: [],
}
},
});

expect(service.state).toBe('inactive');
service.sendEvent({ type: 'TOGGLE' });
expect(service.state).toBe('active');
service.sendEvent({ type: 'TOGGLE' });
expect(service.state).toBe('inactive');
});
});
52 changes: 23 additions & 29 deletions packages/workflow-core/src/lib/workflow-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,70 +31,64 @@ export class WorkflowRunner {
}

constructor(
{ workflowDefinition, workflowActions, workflowContext, extensions }: WorkflowRunnerArgs,
{ definition, workflowActions, workflowContext, extensions }: WorkflowRunnerArgs,
debugMode = true,
) {
// global and state specific extensions
this.#__extensions = extensions ?? {
statePlugins: [],
};
this.#__debugMode = debugMode;

this.#__workflow = this.#__extendedWorkflow({
workflow: workflowDefinition,
definition,
workflowActions,
extensions,
});

// use initial context or provided context
this.#__context =
workflowContext && Object.keys(workflowContext.machineContext).length
workflowContext && Object.keys(workflowContext.machineContext ?? {})?.length
? workflowContext.machineContext
: workflowDefinition.context || {};
: definition.context || {};

// use initial state or provided state
this.#__currentState = workflowContext?.state
? workflowContext.state
: workflowDefinition.initial;

// global and state specific extensions
this.#__extensions = extensions || { statePlugins: [] };
this.#__debugMode = debugMode;
this.#__currentState = workflowContext?.state ? workflowContext.state : definition.initial;
}

#__extendedWorkflow({
workflow,
definition,
workflowActions,
extensions = {
statePlugins: [],
},
}: {
workflow: any;
definition: any;
workflowActions?: WorkflowRunnerArgs['workflowActions'];
extensions?: WorkflowExtensions;
}) {
const extended = workflow;
const onEnter: string[] = [];
const onExit: string[] = [];
const stateActions: Record<string, ActionFunction<any, any>> = {};

for (const state in extended.states) {
extended.states[state].entry = uniqueArray([
...(workflow.states[state].entry ?? []),
for (const state in definition.states) {
definition.states[state].entry = uniqueArray([
...(definition.states[state].entry ?? []),
...onEnter,
]);

extended.states[state].exit = uniqueArray([
...(workflow.states[state].exit ?? []),
definition.states[state].exit = uniqueArray([
...(definition.states[state].exit ?? []),
...onExit,
]);
}

for (const statePlugin of extensions.statePlugins) {
for (const statePlugin of this.#__extensions.statePlugins) {
const when = statePlugin.when === 'pre' ? 'entry' : 'exit';

for (const stateName of statePlugin.stateNames) {
if (!extended.states[stateName]) {
if (!definition.states[stateName]) {
throw new Error(`${stateName} is not defined within the workflow definition's states`);
}

// E.g { state: { entry: [...,plugin.name] } }
extended.states[stateName][when] = uniqueArray([
...(extended.states[stateName][when] ?? []),
definition.states[stateName][when] = uniqueArray([
...(definition.states[stateName][when] ?? []),
statePlugin.name,
]);

Expand Down Expand Up @@ -172,7 +166,7 @@ export class WorkflowRunner {
},
};

return createMachine({ predictableActionArguments: false, ...extended }, { actions, guards });
return createMachine({ predictableActionArguments: false, ...definition }, { actions, guards });
}

async sendEvent(event: WorkflowEventWithoutState) {
Expand Down
2 changes: 1 addition & 1 deletion sdks/workflow-browser-sdk/src/lib/tests/smoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('smoke', () => {
method: backendOptions.endpoints.persist.method,
endpoint: backendOptions.endpoints.persist.endpoint.replace(
':workflowId',
shortWorkflow.workflowDefinition.id ?? '',
shortWorkflow.definition.id ?? '',
),
headers: lowerCaseDefaultHeaders,
});
Expand Down
4 changes: 2 additions & 2 deletions sdks/workflow-browser-sdk/src/lib/tests/subscribe.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ describe('subscribe', () => {

it('should subscribe to user defined events', () => {
workflowService = new WorkflowBrowserSDK({
workflowDefinitionType: 'statechart-json',
workflowDefinition: {
definitionType: 'statechart-json',
definition: {
id: 'test',
initial: 'first',
states: {
Expand Down
12 changes: 6 additions & 6 deletions sdks/workflow-browser-sdk/src/lib/tests/workflow-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { WorkflowBrowserSDKParams } from '../types';

// Specifies six states to have enough steps for USER_NEXT_STEP and USER_PREV_STEP tests.
export const workflowOptions: WorkflowBrowserSDKParams = {
workflowDefinitionType: 'statechart-json',
workflowDefinition: {
definitionType: 'statechart-json',
definition: {
id: 'test',
initial: 'first',
context: {
Expand Down Expand Up @@ -63,8 +63,8 @@ export const errorWorkflow: WorkflowBrowserSDKParams = {
backend: {
baseUrl: 'http://bad-url.fail',
},
workflowDefinitionType: 'statechart-json',
workflowDefinition: {
definitionType: 'statechart-json',
definition: {
id: 'test',
initial: 'first',
states: {
Expand Down Expand Up @@ -96,8 +96,8 @@ export const errorWorkflow: WorkflowBrowserSDKParams = {
};

export const shortWorkflow: WorkflowBrowserSDKParams = {
workflowDefinitionType: 'statechart-json',
workflowDefinition: {
definitionType: 'statechart-json',
definition: {
id: 'test',
initial: 'first',
states: {
Expand Down
10 changes: 5 additions & 5 deletions sdks/workflow-browser-sdk/src/lib/workflow-browser-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ export class WorkflowBrowserSDK {
this.#__mergeBackendOptions(backend);

// Actions defined within the machine's `states` object.
const states = this.#__injectUserStepActionsToStates(options?.workflowDefinition?.states ?? {});
const states = this.#__injectUserStepActionsToStates(options?.definition?.states ?? {});
const statePlugins = this.#__handlePersistStatePlugins({
states,
persistStates: options?.persistStates,
submitStates: options?.submitStates,
workflowId: options?.workflowDefinition.id ?? '',
workflowId: options?.definition.id ?? '',
});
const assignContext = assign<Record<PropertyKey, any>, IUserStepEvent>((context, event) => {
context = {
Expand All @@ -58,8 +58,8 @@ export class WorkflowBrowserSDK {
extensions: {
statePlugins,
},
workflowDefinition: {
...options?.workflowDefinition,
definition: {
...options?.definition,
states,
},
workflowActions: {
Expand All @@ -83,7 +83,7 @@ export class WorkflowBrowserSDK {
submitStates,
workflowId,
}: {
states: WorkflowOptionsBrowser['workflowDefinition']['states'];
states: WorkflowOptionsBrowser['definition']['states'];
persistStates: WorkflowOptionsBrowser['persistStates'];
submitStates: WorkflowOptionsBrowser['submitStates'];
workflowId: string;
Expand Down
4 changes: 2 additions & 2 deletions sdks/workflow-node-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"scripts": {
"build": "rollup --config rollup.config.js",
"watch": "concurrently --kill-others \"rollup --config rollup.config.js -w\" \"tsc -b --watch\"",
"test": "vitest"
"test": "vitest run"
},
"engines": {
"node": ">=12"
Expand Down Expand Up @@ -59,4 +59,4 @@
"vite": "^4.1.1",
"vitest": "^0.28.5"
}
}
}
9 changes: 2 additions & 7 deletions sdks/workflow-node-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
import { createNodeWorkflow, NodeWorkflowOptions, NodeWorkflow } from "./lib/node-workflow";

type TInitNodeWorkflow = (options: NodeWorkflowOptions) => NodeWorkflow;

export const initNodeWorkflow: TInitNodeWorkflow = (options) => {
return createNodeWorkflow(options);
}
export { createWorkflow, WorkflowNodeSDK } from './lib';
export type { WorkflowOptionsNode } from './lib';
1 change: 1 addition & 0 deletions sdks/workflow-node-sdk/src/lib/adapters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MemoryStore } from './memory-store';
61 changes: 28 additions & 33 deletions sdks/workflow-node-sdk/src/lib/adapters/memory-store.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,38 @@
import { StoreAdapter, TPutWorkflow, TRemoveWorkflow, WorkflowData } from "./adapter";
import { StoreAdapter, WorkflowData } from './types';

export class MemoryStore implements StoreAdapter {


#__store: Record<PropertyKey, Record<PropertyKey, WorkflowData>>;

constructor() {
this.#__store = {}
}



// Get all active workflows for entity
find(entityId: string) {
const entityWorkflows: string[] = [];
for (const workflowId in this.#__store) {
if (this.#__store[workflowId]![entityId]) {
entityWorkflows.push(workflowId);
}
}

return entityWorkflows;
#__store: Record<PropertyKey, Record<PropertyKey, WorkflowData>>;

constructor() {
this.#__store = {};
}

// Get all active workflows for entity
find(entityId: string) {
const entityWorkflows: string[] = [];
for (const workflowId in this.#__store) {
if (this.#__store[workflowId]![entityId]) {
entityWorkflows.push(workflowId);
}
}

get(workflowId: string, entityId: string): WorkflowData|undefined {
return this.#__store[workflowId]?.[entityId];
}
return entityWorkflows;
}

put( workflowId:string, entityId: string, data: WorkflowData ) {
get(workflowId: string, entityId: string): WorkflowData | undefined {
return this.#__store[workflowId]?.[entityId];
}

if (this.#__store[workflowId] === undefined) {
this.#__store[workflowId] = {};
}
this.#__store[workflowId]![entityId] = data;
put(workflowId: string, entityId: string, data: WorkflowData) {
if (this.#__store[workflowId] === undefined) {
this.#__store[workflowId] = {};
}
this.#__store[workflowId]![entityId] = data;
}

remove( workflowId: string, entityId: string) {
if (this.#__store[workflowId] !== undefined) {
delete this.#__store[workflowId]![entityId]
}
remove(workflowId: string, entityId: string) {
if (this.#__store[workflowId] !== undefined) {
delete this.#__store[workflowId]![entityId];
}
}
}
Loading

0 comments on commit f072188

Please sign in to comment.