From 1197ae6225b78f6a127e536372ffd74f4780e291 Mon Sep 17 00:00:00 2001 From: allanhvam Date: Wed, 18 Dec 2024 09:53:23 +0100 Subject: [PATCH] feat: trigger output type --- .eslintrc.cjs | 3 ++- package-lock.json | 13 ++++++++----- package.json | 4 ++-- src/tests/triggers/index.ts | 1 + src/tests/triggers/math.ts | 14 ++++++++++++++ src/tests/workflows.test.ts | 12 ++++++++++++ src/tests/workflows/add-two.ts | 16 ++++++++++++++++ src/triggers/index.ts | 2 +- src/worker/IWorker.ts | 14 +++++++------- src/worker/Worker.ts | 4 ++-- src/worker/WorkflowFunction.ts | 10 ++++------ src/workflows/index.ts | 17 ++++++++++------- 12 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 src/tests/triggers/index.ts create mode 100644 src/tests/triggers/math.ts create mode 100644 src/tests/workflows/add-two.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6504cd2..569aeb8 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -37,6 +37,7 @@ module.exports = { "@typescript-eslint/consistent-type-definitions": "off", "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/no-extraneous-class": "off", - "@typescript-eslint/member-delimiter-style": "off" + "@typescript-eslint/member-delimiter-style": "off", + "@typescript-eslint/explicit-function-return-type": "off" } } diff --git a/package-lock.json b/package-lock.json index 99598b1..e7ee2f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,18 +1,17 @@ { "name": "simple-workflows", - "version": "0.2.2", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "simple-workflows", - "version": "0.2.2", + "version": "0.3.0", "license": "MIT", "dependencies": { "@azure/data-tables": "^13.0.1", "@azure/storage-blob": "^12.10.0", "async-mutex": "^0.4.0", - "ms": "^2.1.3", "nanoid": "^5.0.7" }, "devDependencies": { @@ -24,6 +23,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.5.0", "eslint-plugin-promise": "^6.1.1", + "ms": "^2.1.3", "superjson": "^2.2.1", "typescript": "^5.3.3" }, @@ -2751,7 +2751,9 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/nanoid": { "version": "5.0.7", @@ -5629,7 +5631,8 @@ "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "nanoid": { "version": "5.0.7", diff --git a/package.json b/package.json index 8517ed9..5574f1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "simple-workflows", - "version": "0.2.2", + "version": "0.3.0", "description": "Workflows as code in TypeScript", "main": "lib/index.js", "type": "module", @@ -28,6 +28,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^16.5.0", "eslint-plugin-promise": "^6.1.1", + "ms": "^2.1.3", "superjson": "^2.2.1", "typescript": "^5.3.3" }, @@ -35,7 +36,6 @@ "@azure/data-tables": "^13.0.1", "@azure/storage-blob": "^12.10.0", "async-mutex": "^0.4.0", - "ms": "^2.1.3", "nanoid": "^5.0.7" } } diff --git a/src/tests/triggers/index.ts b/src/tests/triggers/index.ts new file mode 100644 index 0000000..d1884cc --- /dev/null +++ b/src/tests/triggers/index.ts @@ -0,0 +1 @@ +export { math } from "./math.js"; diff --git a/src/tests/triggers/math.ts b/src/tests/triggers/math.ts new file mode 100644 index 0000000..b35a0d0 --- /dev/null +++ b/src/tests/triggers/math.ts @@ -0,0 +1,14 @@ +import { type Trigger } from "../../index.js"; + +export const math = () => { + return { + name: "math", + options: undefined, + start: (workflow: { name: string }, run) => { + console.log(`math: trigger start for ${workflow.name}`); + }, + stop: (workflow: { name: string }) => { + console.log(`math: trigger stop for ${workflow.name}`); + }, + } satisfies Trigger; +}; diff --git a/src/tests/workflows.test.ts b/src/tests/workflows.test.ts index a1f57d2..d109e7a 100644 --- a/src/tests/workflows.test.ts +++ b/src/tests/workflows.test.ts @@ -5,6 +5,7 @@ import { DurableFunctionsWorkflowHistoryStore } from "../stores/index.js"; import { math } from "./workflows/math.js"; import { workflows } from "../workflows/index.js"; import ms from "ms"; +import { addTow } from "./workflows/add-two.js"; test.before(async () => { const worker = Worker.getInstance(); @@ -50,3 +51,14 @@ void test("Workflow", async (t) => { assert.ok(mathInstances.length >= 1); }); + +void test("Workflow add-tow", async (t) => { + // Arrange + const workflow = addTow; + + // Act + const result = await workflow.invoke(2) satisfies number; + + // Assert + assert.equal(result, 4); +}); diff --git a/src/tests/workflows/add-two.ts b/src/tests/workflows/add-two.ts new file mode 100644 index 0000000..464b2bc --- /dev/null +++ b/src/tests/workflows/add-two.ts @@ -0,0 +1,16 @@ +import { workflow } from "../../workflows/index.js"; +import { MathService } from "../services/MathService.js"; +import { math } from "../triggers/index.js"; + +export const addTow = workflow({ + name: "add-tow", + description: "Adds 2 to argument.", + trigger: math(), + services: { + math: new MathService(), + }, + run: (services) => async (input: number) => { + const output = await services.math.add(input, 2); + return output; + }, +}); diff --git a/src/triggers/index.ts b/src/triggers/index.ts index c72d97c..7754380 100644 --- a/src/triggers/index.ts +++ b/src/triggers/index.ts @@ -1,2 +1,2 @@ export { manual } from "./manual.js"; -export { startup } from "./startup.js"; \ No newline at end of file +export { startup } from "./startup.js"; diff --git a/src/worker/IWorker.ts b/src/worker/IWorker.ts index f7e6392..4cdd698 100644 --- a/src/worker/IWorker.ts +++ b/src/worker/IWorker.ts @@ -10,25 +10,25 @@ export declare type WithWorkflowArgs = T & (Param /** * Arguments to pass to the Workflow */ - args?: Parameters + args?: Parameters; }); export declare type WorkflowOptions = { - workflowId?: string + workflowId?: string; /** * @format {@link https://www.npmjs.com/package/ms | ms} formatted string or number of milliseconds */ - workflowExecutionTimeout?: string | number + workflowExecutionTimeout?: string | number; /** * Store for the workflow instance, overwrites the global instance (if set) */ - store?: IWorkflowHistoryStore + store?: IWorkflowHistoryStore; }; export declare type WorkflowStartOptions = WithWorkflowArgs; export interface IWorker { - start: (workflow: T, options?: WorkflowStartOptions) => Promise> - store: IWorkflowHistoryStore - log?: (s: string) => void + start: (workflow: T, options?: WorkflowStartOptions) => Promise>; + store: IWorkflowHistoryStore; + log?: (s: string) => void; } diff --git a/src/worker/Worker.ts b/src/worker/Worker.ts index dc00a44..e8e831f 100644 --- a/src/worker/Worker.ts +++ b/src/worker/Worker.ts @@ -2,7 +2,7 @@ import { AsyncLocalStorage } from "async_hooks"; import { type IWorkflowContext } from "./IWorkflowContext.js"; import { type IWorkflowHistoryStore } from "../stores/IWorkflowHistoryStore.js"; import { MemoryWorkflowHistoryStore } from "../stores/MemoryWorkflowHistoryStore.js"; -import { type WorkflowHandle, type WorkflowFunction, type WorkflowResultType, type WorkflowFunctionReturnType } from "./WorkflowFunction.js"; +import { type WorkflowHandle, type WorkflowFunction, type WorkflowResultType } from "./WorkflowFunction.js"; import msPkg from "ms"; import { Mutex } from "async-mutex"; import { sleep } from "../sleep.js"; @@ -96,7 +96,7 @@ export class Worker implements IWorker { await store?.setInstance(workflowInstance); } - let promise: WorkflowFunctionReturnType = Worker.asyncLocalStorage.run(workflowContext, async () => { + let promise = Worker.asyncLocalStorage.run(workflowContext, async () => { let result: any; let error: any; let isError = false; diff --git a/src/worker/WorkflowFunction.ts b/src/worker/WorkflowFunction.ts index 729fbfb..b36d3fe 100644 --- a/src/worker/WorkflowFunction.ts +++ b/src/worker/WorkflowFunction.ts @@ -1,13 +1,11 @@ import { type IWorkflowHistoryStore } from "../stores/IWorkflowHistoryStore.js"; -export type WorkflowFunctionReturnType = Promise; - -export type WorkflowFunction = (...args: any[]) => WorkflowFunctionReturnType; +export type WorkflowFunction = (...args: any[]) => Promise; export type WorkflowResultType = ReturnType extends Promise ? R : never; export type WorkflowHandle = { - result: () => Promise> - store?: IWorkflowHistoryStore - readonly workflowId: string + result: () => Promise>; + store?: IWorkflowHistoryStore; + readonly workflowId: string; }; diff --git a/src/workflows/index.ts b/src/workflows/index.ts index c1806f4..2fdfd8c 100644 --- a/src/workflows/index.ts +++ b/src/workflows/index.ts @@ -4,32 +4,34 @@ import { type OnlyAsync } from "../types/OnlyAsync.js"; import { nanoid } from "nanoid"; import { type WorkflowHandle } from "../worker/WorkflowFunction.js"; -export type Trigger

= { +// P: Payload +// O: Output +export type Trigger = { name: string, options: any, description?: string, - start: (workflow: any, run: (id: string, triggerData: P) => Promise any> | undefined>) => void | Promise; + start: (workflow: any, run: (id: string, triggerData: P) => Promise Promise> | undefined>) => void | Promise; stop?: (workflow: any) => void | Promise; }; export type Services = { [P in keyof T]: OnlyAsync }; -export type Workflow, P = void> = { +export type Workflow, P = void, O = unknown> = { name: string; description?: string; tags?: Array; disabled?: boolean; trigger: Trigger

; services?: S; - run: (services: Services) => (triggerData: P) => any; + run: (services: Services) => (triggerData: P) => Promise; }; export const workflows = new Map>(); -export const workflow = , P = void>(workflow: Workflow) => { +export const workflow = , P = void, O = unknown>(workflow: Workflow) => { workflows.set(workflow.name, workflow); - const runInternal = async (id: string, services: S | undefined, triggerData: P): Promise any>> => { + const runInternal = async (id: string, services: S | undefined, triggerData: P) => { // Proxy services const proxies = {} as any; if (services) { @@ -39,6 +41,7 @@ export const workflow = , P = void>(workflow: W } const worker = WorkflowWorker.getInstance(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const handle = await worker.start(workflow.run(proxies), { workflowId: `${workflow.name} ${id}`, args: [triggerData], @@ -54,7 +57,7 @@ export const workflow = , P = void>(workflow: W * Start the workflow trigger */ start: async () => { - const run = async (id: string, payload: any) => { + const run = async (id: string, payload: P) => { const worker = WorkflowWorker.getInstance(); if (workflow.disabled) { worker.log?.(`Workflow '${workflow.name}' disabled, skip`);