diff --git a/daprdocs/content/en/js-sdk-docs/js-client/_index.md b/daprdocs/content/en/js-sdk-docs/js-client/_index.md index 513a721c..a699a19e 100644 --- a/daprdocs/content/en/js-sdk-docs/js-client/_index.md +++ b/daprdocs/content/en/js-sdk-docs/js-client/_index.md @@ -500,6 +500,56 @@ start().catch((e) => { > For a full guide on distributed locks visit [How-To: Use Distributed Locks]({{< ref howto-use-distributed-lock.md >}}). +### Workflow API + +#### Workflow management + +```typescript +import { DaprClient } from "@dapr/dapr"; + +async function start() { + const client = new DaprClient(); + + // Start a new workflow instance + const instanceId = await client.workflow.start("OrderProcessingWorkflow", { + Name: "Paperclips", + TotalCost: 99.95, + Quantity: 4, + }); + console.log(`Started workflow instance ${instanceId}`); + + // Get a workflow instance + const workflow = await client.workflow.get(instanceId); + console.log( + `Workflow ${workflow.workflowName}, created at ${workflow.createdAt.toUTCString()}, has status ${ + workflow.runtimeStatus + }`, + ); + console.log(`Additional properties: ${JSON.stringify(workflow.properties)}`); + + // Pause a workflow instance + await client.workflow.pause(instanceId); + console.log(`Paused workflow instance ${instanceId}`); + + // Resume a workflow instance + await client.workflow.resume(instanceId); + console.log(`Resumed workflow instance ${instanceId}`); + + // Terminate a workflow instance + await client.workflow.terminate(instanceId); + console.log(`Terminated workflow instance ${instanceId}`); + + // Purge a workflow instance + await client.workflow.purge(instanceId); + console.log(`Purged workflow instance ${instanceId}`); +} + +start().catch((e) => { + console.error(e); + process.exit(1); +}); +``` + ## Related links - [JavaScript SDK examples](https://github.com/dapr/js-sdk/tree/master/examples) diff --git a/examples/workflow/package-lock.json b/examples/workflow/package-lock.json new file mode 100644 index 00000000..60d31df1 --- /dev/null +++ b/examples/workflow/package-lock.json @@ -0,0 +1,417 @@ +{ + "name": "dapr-example-workflow", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dapr-example-workflow", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@dapr/dapr": "file:../../build", + "@types/node": "^18.16.3" + }, + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + } + }, + "../../build": { + "name": "@dapr/dapr", + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "@grpc/grpc-js": "^1.3.7", + "@js-temporal/polyfill": "^0.3.0", + "@types/google-protobuf": "^3.15.5", + "@types/node-fetch": "^2.6.2", + "body-parser": "^1.19.0", + "express": "^4.18.2", + "google-protobuf": "^3.18.0", + "http-terminator": "^3.0.4", + "node-fetch": "^2.6.7" + }, + "devDependencies": { + "@types/body-parser": "^1.19.1", + "@types/express": "^4.17.15", + "@types/jest": "^27.0.1", + "@types/node": "^16.9.1", + "@types/uuid": "^8.3.1", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", + "eslint": "^8.1.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-prettier": "^4.2.1", + "grpc_tools_node_protoc_ts": "^5.3.2", + "husky": "^8.0.1", + "jest": "^27.2.0", + "nodemon": "^2.0.20", + "prettier": "^2.4.0", + "pretty-quick": "^3.1.3", + "ts-jest": "^27.0.5", + "typescript": "^4.5.5" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dapr/dapr": { + "resolved": "../../build", + "link": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.16.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.3.tgz", + "integrity": "sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==" + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + }, + "dependencies": { + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@dapr/dapr": { + "version": "file:../../build", + "requires": { + "@grpc/grpc-js": "^1.3.7", + "@js-temporal/polyfill": "^0.3.0", + "@types/body-parser": "^1.19.1", + "@types/express": "^4.17.15", + "@types/google-protobuf": "^3.15.5", + "@types/jest": "^27.0.1", + "@types/node": "^16.9.1", + "@types/node-fetch": "^2.6.2", + "@types/uuid": "^8.3.1", + "@typescript-eslint/eslint-plugin": "^5.1.0", + "@typescript-eslint/parser": "^5.1.0", + "body-parser": "^1.19.0", + "eslint": "^8.1.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-header": "^3.1.1", + "eslint-plugin-prettier": "^4.2.1", + "express": "^4.18.2", + "google-protobuf": "^3.18.0", + "grpc_tools_node_protoc_ts": "^5.3.2", + "http-terminator": "^3.0.4", + "husky": "^8.0.1", + "jest": "^27.2.0", + "node-fetch": "^2.6.7", + "nodemon": "^2.0.20", + "prettier": "^2.4.0", + "pretty-quick": "^3.1.3", + "ts-jest": "^27.0.5", + "typescript": "^4.5.5" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "dev": true + }, + "@types/node": { + "version": "18.16.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.3.tgz", + "integrity": "sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q==" + }, + "acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + } + }, + "typescript": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "dev": true + }, + "v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true + } + } +} diff --git a/examples/workflow/package.json b/examples/workflow/package.json new file mode 100644 index 00000000..46fc5e1c --- /dev/null +++ b/examples/workflow/package.json @@ -0,0 +1,23 @@ +{ + "name": "dapr-example-workflow", + "version": "1.0.0", + "description": "An example utilizing the Dapr JS-SDK to manage workflow", + "main": "dist/index.js", + "private": "true", + "scripts": { + "build": "rimraf ./dist && tsc", + "start": "npm run build && node dist/index.js", + "start:dapr-grpc": "dapr run --app-id example-workflow --app-port 50051 --app-protocol grpc --components-path ./components npm run start", + "start:dapr-http": "dapr run --app-id example-workflow --app-port 50051 --app-protocol http --components-path ./components npm run start" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "dependencies": { + "@dapr/dapr": "file:../../build", + "@types/node": "^18.16.3" + } +} diff --git a/examples/workflow/src/index.ts b/examples/workflow/src/index.ts new file mode 100644 index 00000000..c8774884 --- /dev/null +++ b/examples/workflow/src/index.ts @@ -0,0 +1,68 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { DaprClient } from "@dapr/dapr"; + +async function printWorkflowStatus(client: DaprClient, instanceId: string) { + const workflow = await client.workflow.get(instanceId); + console.log( + `Workflow ${workflow.workflowName}, created at ${workflow.createdAt.toUTCString()}, has status ${ + workflow.runtimeStatus + }`, + ); + console.log(`Additional properties: ${JSON.stringify(workflow.properties)}`); + console.log("--------------------------------------------------\n\n"); +} + +async function start() { + const client = new DaprClient(); + + // Start a new workflow instance + const instanceId = await client.workflow.start("OrderProcessingWorkflow", { + Name: "Paperclips", + TotalCost: 99.95, + Quantity: 4, + }); + console.log(`Started workflow instance ${instanceId}`); + await printWorkflowStatus(client, instanceId); + + // Pause a workflow instance + await client.workflow.pause(instanceId); + console.log(`Paused workflow instance ${instanceId}`); + await printWorkflowStatus(client, instanceId); + + // Resume a workflow instance + await client.workflow.resume(instanceId); + console.log(`Resumed workflow instance ${instanceId}`); + await printWorkflowStatus(client, instanceId); + + // Terminate a workflow instance + // await client.workflow.terminate(instanceId); + // console.log(`Terminated workflow instance ${instanceId}`); + // await printWorkflowStatus(client, instanceId); + + // Wait for the workflow to complete, 30 seconds! + await new Promise((resolve) => setTimeout(resolve, 30000)); + await printWorkflowStatus(client, instanceId); + + // Purge a workflow instance + await client.workflow.purge(instanceId); + console.log(`Purged workflow instance ${instanceId}`); + // This will throw an error because the workflow instance no longer exists. + await printWorkflowStatus(client, instanceId); +} + +start().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/examples/workflow/tsconfig.json b/examples/workflow/tsconfig.json new file mode 100644 index 00000000..4c8d3f94 --- /dev/null +++ b/examples/workflow/tsconfig.json @@ -0,0 +1,71 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, + "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist" /* Redirect output structure to the directory. */, + "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true /* Enable all strict type-checking options. */, + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true /* Skip type checking of declaration files. */, + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/src/implementation/Client/DaprClient.ts b/src/implementation/Client/DaprClient.ts index 3561f031..8954ff36 100644 --- a/src/implementation/Client/DaprClient.ts +++ b/src/implementation/Client/DaprClient.ts @@ -11,49 +11,52 @@ See the License for the specific language governing permissions and limitations under the License. */ +import IClient from "../../interfaces/Client/IClient"; +import IClientActorBuilder from "../../interfaces/Client/IClientActorBuilder"; import IClientBinding from "../../interfaces/Client/IClientBinding"; -import IClientPubSub from "../../interfaces/Client/IClientPubSub"; -import IClientState from "../../interfaces/Client/IClientState"; -import IClientInvoker from "../../interfaces/Client/IClientInvoker"; -import IClientSecret from "../../interfaces/Client/IClientSecret"; +import IClientConfiguration from "../../interfaces/Client/IClientConfiguration"; +import IClientCrypto from "../../interfaces/Client/IClientCrypto"; import IClientHealth from "../../interfaces/Client/IClientHealth"; +import IClientInvoker from "../../interfaces/Client/IClientInvoker"; +import IClientLock from "../../interfaces/Client/IClientLock"; import IClientMetadata from "../../interfaces/Client/IClientMetadata"; -import IClientSidecar from "../../interfaces/Client/IClientSidecar"; -import IClientConfiguration from "../../interfaces/Client/IClientConfiguration"; import IClientProxy from "../../interfaces/Client/IClientProxy"; -import IClientLock from "../../interfaces/Client/IClientLock"; -import IClientCrypto from "../../interfaces/Client/IClientCrypto"; -import IClientActorBuilder from "../../interfaces/Client/IClientActorBuilder"; -import IClient from "../../interfaces/Client/IClient"; +import IClientPubSub from "../../interfaces/Client/IClientPubSub"; +import IClientSecret from "../../interfaces/Client/IClientSecret"; +import IClientSidecar from "../../interfaces/Client/IClientSidecar"; +import IClientState from "../../interfaces/Client/IClientState"; +import IClientWorkflow from "../../interfaces/Client/IClientWorkflow"; +import GRPCClient from "./GRPCClient/GRPCClient"; +import GRPCClientActor from "./GRPCClient/actor"; import GRPCClientBinding from "./GRPCClient/binding"; -import GRPCClientPubSub from "./GRPCClient/pubsub"; -import GRPCClientState from "./GRPCClient/state"; -import GRPCClientInvoker from "./GRPCClient/invoker"; -import GRPCClientSecret from "./GRPCClient/secret"; +import GRPCClientConfiguration from "./GRPCClient/configuration"; +import GRPCClientCrypto from "./GRPCClient/crypto"; import GRPCClientHealth from "./GRPCClient/health"; +import GRPCClientInvoker from "./GRPCClient/invoker"; +import GRPCClientLock from "./GRPCClient/lock"; import GRPCClientMetadata from "./GRPCClient/metadata"; +import GRPCClientPubSub from "./GRPCClient/pubsub"; +import GRPCClientSecret from "./GRPCClient/secret"; import GRPCClientSidecar from "./GRPCClient/sidecar"; -import GRPCClientConfiguration from "./GRPCClient/configuration"; -import GRPCClientLock from "./GRPCClient/lock"; -import GRPCClientCrypto from "./GRPCClient/crypto"; -import GRPCClientActor from "./GRPCClient/actor"; -import GRPCClient from "./GRPCClient/GRPCClient"; +import GRPCClientState from "./GRPCClient/state"; +import GRPCClientWorkflow from "./GRPCClient/workflow"; +import HTTPClient from "./HTTPClient/HTTPClient"; +import HTTPClientActor from "./HTTPClient/actor"; import HTTPClientBinding from "./HTTPClient/binding"; -import HTTPClientPubSub from "./HTTPClient/pubsub"; -import HTTPClientState from "./HTTPClient/state"; -import HTTPClientInvoker from "./HTTPClient/invoker"; -import HTTPClientSecret from "./HTTPClient/secret"; +import HTTPClientConfiguration from "./HTTPClient/configuration"; +import HTTPClientCrypto from "./HTTPClient/crypto"; import HTTPClientHealth from "./HTTPClient/health"; +import HTTPClientInvoker from "./HTTPClient/invoker"; +import HTTPClientLock from "./HTTPClient/lock"; import HTTPClientMetadata from "./HTTPClient/metadata"; -import HTTPClientSidecar from "./HTTPClient/sidecar"; -import HTTPClientConfiguration from "./HTTPClient/configuration"; import HTTPClientProxy from "./HTTPClient/proxy"; -import HTTPClientLock from "./HTTPClient/lock"; -import HTTPClientCrypto from "./HTTPClient/crypto"; -import HTTPClientActor from "./HTTPClient/actor"; -import HTTPClient from "./HTTPClient/HTTPClient"; +import HTTPClientPubSub from "./HTTPClient/pubsub"; +import HTTPClientSecret from "./HTTPClient/secret"; +import HTTPClientSidecar from "./HTTPClient/sidecar"; +import HTTPClientState from "./HTTPClient/state"; +import HTTPClientWorkflow from "./HTTPClient/workflow"; import CommunicationProtocolEnum from "../../enum/CommunicationProtocol.enum"; import { DaprClientOptions } from "../../types/DaprClientOptions"; @@ -66,19 +69,20 @@ import { getClientOptions } from "../../utils/Client.util"; export default class DaprClient { readonly options: DaprClientOptions; readonly daprClient: IClient; - readonly pubsub: IClientPubSub; - readonly state: IClientState; + readonly actor: IClientActorBuilder; readonly binding: IClientBinding; - readonly invoker: IClientInvoker; - readonly secret: IClientSecret; + readonly configuration: IClientConfiguration; + readonly crypto: IClientCrypto; readonly health: IClientHealth; + readonly invoker: IClientInvoker; + readonly lock: IClientLock; readonly metadata: IClientMetadata; - readonly sidecar: IClientSidecar; - readonly configuration: IClientConfiguration; readonly proxy: IClientProxy; - readonly lock: IClientLock; - readonly crypto: IClientCrypto; - readonly actor: IClientActorBuilder; + readonly pubsub: IClientPubSub; + readonly secret: IClientSecret; + readonly sidecar: IClientSidecar; + readonly state: IClientState; + readonly workflow: IClientWorkflow; private readonly logger: Logger; @@ -110,6 +114,7 @@ export default class DaprClient { this.lock = new GRPCClientLock(client); this.crypto = new GRPCClientCrypto(client); this.actor = new GRPCClientActor(client); // we use an abstractor here since we interface through a builder with the Actor Runtime + this.workflow = new GRPCClientWorkflow(client); break; } case CommunicationProtocolEnum.HTTP: @@ -117,19 +122,20 @@ export default class DaprClient { const client = new HTTPClient(this.options); this.daprClient = client; - this.state = new HTTPClientState(client); - this.pubsub = new HTTPClientPubSub(client); + this.actor = new HTTPClientActor(client); // we use an abstractor here since we interface through a builder with the Actor Runtime this.binding = new HTTPClientBinding(client); - this.invoker = new HTTPClientInvoker(client); - this.secret = new HTTPClientSecret(client); + this.configuration = new HTTPClientConfiguration(client); + this.crypto = new HTTPClientCrypto(client); this.health = new HTTPClientHealth(client); + this.invoker = new HTTPClientInvoker(client); + this.lock = new HTTPClientLock(client); this.metadata = new HTTPClientMetadata(client); - this.sidecar = new HTTPClientSidecar(client); - this.configuration = new HTTPClientConfiguration(client); this.proxy = new HTTPClientProxy(client); - this.lock = new HTTPClientLock(client); - this.crypto = new HTTPClientCrypto(client); - this.actor = new HTTPClientActor(client); // we use an abstractor here since we interface through a builder with the Actor Runtime + this.pubsub = new HTTPClientPubSub(client); + this.secret = new HTTPClientSecret(client); + this.sidecar = new HTTPClientSidecar(client); + this.state = new HTTPClientState(client); + this.workflow = new HTTPClientWorkflow(client); break; } } diff --git a/src/implementation/Client/GRPCClient/workflow.ts b/src/implementation/Client/GRPCClient/workflow.ts new file mode 100644 index 00000000..54f38251 --- /dev/null +++ b/src/implementation/Client/GRPCClient/workflow.ts @@ -0,0 +1,57 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import GRPCClient from "./GRPCClient"; +import IClientWorkflow from "../../../interfaces/Client/IClientWorkflow"; +import { WorkflowGetResponseType } from "../../../types/workflow/WorkflowGetResponse.type"; + +export default class GRPCClientWorkflow implements IClientWorkflow { + client: GRPCClient; + + constructor(client: GRPCClient) { + this.client = client; + } + + get(_instanceId: string, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } + + start( + _workflowName: string, + _input?: any, + _instanceId?: string | undefined, + _workflowComponent?: string | undefined, + ): Promise { + throw new Error("Method not implemented."); + } + + terminate(_instanceId: string, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } + + pause(_instanceId: string, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } + + resume(_instanceId: string, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } + + purge(_instanceId: string, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } + + raise(_instanceId: string, _eventName: string, _input?: any, _workflowComponent?: string | undefined): Promise { + throw new Error("Method not implemented."); + } +} diff --git a/src/implementation/Client/HTTPClient/workflow.ts b/src/implementation/Client/HTTPClient/workflow.ts new file mode 100644 index 00000000..8f95eea8 --- /dev/null +++ b/src/implementation/Client/HTTPClient/workflow.ts @@ -0,0 +1,193 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import HTTPClient from "./HTTPClient"; +import IClientWorkflow from "../../../interfaces/Client/IClientWorkflow"; +import { WorkflowGetResponseType } from "../../../types/workflow/WorkflowGetResponse.type"; +import { Logger } from "../../../logger/Logger"; +import { KeyValueType } from "../../../types/KeyValue.type"; +import { WorkflowStartOptions } from "../../../types/workflow/WorkflowStartOptions.type"; +import { randomUUID } from "crypto"; +import { createHTTPQueryParam } from "../../../utils/Client.util"; +import { WorkflowRaiseOptions } from "../../../types/workflow/WorkflowRaiseOptions.type"; + +export default class HTTPClientWorkflow implements IClientWorkflow { + private readonly client: HTTPClient; + private readonly logger: Logger; + + private static readonly DEFAULT_WORKFLOW_COMPONENT = "dapr"; + + constructor(client: HTTPClient) { + this.client = client; + this.logger = new Logger("HTTPClient", "Workflow", client.options.logger); + } + + async get(instanceID: string, workflowComponent?: string): Promise { + if (!instanceID) { + throw new Error("instanceID is required"); + } + + workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; + + try { + const result = await this.client.executeWithApiVersion( + "v1.0-alpha1", + `/workflows/${workflowComponent}/${instanceID}`, + { method: "GET" }, + ); + + if (typeof result === "string") { + throw new Error(`Error getting workflow instance: ${result}`); + } + + const resultJson = result as KeyValueType; + + const response: WorkflowGetResponseType = { + instanceID: resultJson["instanceID"], + workflowName: resultJson["workflowName"], + createdAt: resultJson["createdAt"] ? new Date(resultJson["createdAt"]) : new Date(), + lastUpdatedAt: resultJson["lastUpdatedAt"] ? new Date(resultJson["lastUpdatedAt"]) : new Date(), + runtimeStatus: resultJson["runtimeStatus"], + properties: resultJson["properties"], + }; + + return response; + } catch (e: any) { + this.logger.error(`Error getting workflow instance: ${e.message}`); + throw e; + } + } + + async start( + workflowName: string, + input?: any, + instanceId?: string | undefined, + workflowComponent?: string | undefined, + options: WorkflowStartOptions = {}, + ): Promise { + if (!workflowName) { + throw new Error("workflowName is required"); + } + + if (!instanceId) { + instanceId = randomUUID(); + } + + workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; + + const queryParams = createHTTPQueryParam({ data: { instanceID: instanceId } }); + + // Set content type if provided. + // If not, HTTPClient will infer it from the data. + const headers: KeyValueType = {}; + if (options.contentType) { + headers["Content-Type"] = options.contentType; + } + + try { + await this.client.executeWithApiVersion( + "v1.0-alpha1", + `/workflows/${workflowComponent}/${workflowName}/start?${queryParams}`, + { + method: "POST", + body: input, + headers, + }, + ); + + return instanceId; + } catch (e: any) { + this.logger.error(`Error starting workflow instance: ${e.message}`); + throw e; + } + } + + async raise( + instanceId: string, + eventName: string, + eventData?: any, + workflowComponent?: string | undefined, + options: WorkflowRaiseOptions = {}, + ): Promise { + if (!instanceId) { + throw new Error("instanceID is required"); + } + + if (!eventName) { + throw new Error("eventName is required"); + } + + workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; + + // Set content type if provided. + // If not, HTTPClient will infer it from the data. + const headers: KeyValueType = {}; + if (options.eventContentType) { + headers["Content-Type"] = options.eventContentType; + } + + try { + await this.client.executeWithApiVersion( + "v1.0-alpha1", + `/workflows/${workflowComponent}/${instanceId}/raiseEvent/${eventName}`, + { + method: "POST", + body: eventData, + headers, + }, + ); + } catch (e: any) { + this.logger.error(`Error raising event on workflow instance: ${e.message}`); + throw e; + } + } + + async terminate(instanceId: string, workflowComponent?: string | undefined): Promise { + await this._invokeMethod(instanceId, "terminate", workflowComponent); + } + + async pause(instanceId: string, workflowComponent?: string | undefined): Promise { + await this._invokeMethod(instanceId, "pause", workflowComponent); + } + + async resume(instanceId: string, workflowComponent?: string | undefined): Promise { + await this._invokeMethod(instanceId, "resume", workflowComponent); + } + + async purge(instanceId: string, workflowComponent?: string | undefined): Promise { + await this._invokeMethod(instanceId, "purge", workflowComponent); + } + + async _invokeMethod(instanceId: string, method: string, workflowComponent?: string | undefined): Promise { + if (!instanceId) { + throw new Error("instanceID is required"); + } + + if (!method) { + throw new Error("method is required"); + } + + workflowComponent = workflowComponent ?? HTTPClientWorkflow.DEFAULT_WORKFLOW_COMPONENT; + + try { + await this.client.executeWithApiVersion( + "v1.0-alpha1", + `/workflows/${workflowComponent}/${instanceId}/${method}`, + { method: "POST" }, + ); + } catch (e: any) { + this.logger.error(`Error invoking ${method} on workflow instance: ${e.message}`); + throw e; + } + } +} diff --git a/src/interfaces/Client/IClientWorkflow.ts b/src/interfaces/Client/IClientWorkflow.ts new file mode 100644 index 00000000..9378c3a7 --- /dev/null +++ b/src/interfaces/Client/IClientWorkflow.ts @@ -0,0 +1,69 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { WorkflowGetResponseType } from "../../types/workflow/WorkflowGetResponse.type"; + +export default interface IClientWorkflow { + /** + * Get information about a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + get(instanceId: string, workflowComponent?: string): Promise; + + /** + * Starts a new workflow instance. + * @param workflowName The name of the workflow to start. + * @param input The input to pass to the workflow, should be JSON serializable. + * @param instanceId The unique identifier for the workflow instance, if not provided one will be generated. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + start(workflowName: string, input?: any, instanceId?: string, workflowComponent?: string): Promise; + + /** + * Terminates a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + terminate(instanceId: string, workflowComponent?: string): Promise; + + /** + * Pauses a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + pause(instanceId: string, workflowComponent?: string): Promise; + + /** + * Resumes a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + resume(instanceId: string, workflowComponent?: string): Promise; + + /** + * Purge a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + purge(instanceId: string, workflowComponent?: string): Promise; + + /** + * Raise an event to a workflow instance. + * @param instanceId The unique identifier for the workflow instance. + * @param eventName The name of the event to raise. + * @param eventData The data associated with the event, should be JSON serializable. + * @param workflowComponent The name of the workflow component to interface with, if not provided the default "dapr" will be used. + */ + raise(instanceId: string, eventName: string, eventData?: any, workflowComponent?: string): Promise; +} diff --git a/src/types/workflow/WorkflowGetResponse.type.ts b/src/types/workflow/WorkflowGetResponse.type.ts new file mode 100644 index 00000000..9cf30f10 --- /dev/null +++ b/src/types/workflow/WorkflowGetResponse.type.ts @@ -0,0 +1,51 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { KeyValueType } from "../KeyValue.type"; +import { WorkflowRuntimeStatus } from "./WorkflowRuntimeStatus.type"; + +/** + * WorkflowGetResponseType defines the response from a get request for a workflow. + */ +export type WorkflowGetResponseType = { + /** + * instanceID is the unique identifier for the workflow instance. + */ + instanceID: string; + + /** + * workflowName is the name of the workflow. + * This is the name of the workflow as defined in the workflow definition. + */ + workflowName: string; + + /** + * createdAt is the time the workflow instance was created. + */ + createdAt: Date; + + /** + * lastUpdatedAt is the time the workflow instance was last updated. + */ + lastUpdatedAt: Date; + + /** + * runtimeStatus is the current status of the workflow instance. + */ + runtimeStatus: WorkflowRuntimeStatus; + + /** + * properties is a collection of key/value pairs that are associated with the workflow instance. + */ + properties: KeyValueType; +}; diff --git a/src/types/workflow/WorkflowRaiseOptions.type.ts b/src/types/workflow/WorkflowRaiseOptions.type.ts new file mode 100644 index 00000000..863760cd --- /dev/null +++ b/src/types/workflow/WorkflowRaiseOptions.type.ts @@ -0,0 +1,20 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +export type WorkflowRaiseOptions = { + /** + * The content type of the event. + * This is optional and will be inferred from the payload if not provided. + */ + eventContentType?: string; +}; diff --git a/src/types/workflow/WorkflowRuntimeStatus.type.ts b/src/types/workflow/WorkflowRuntimeStatus.type.ts new file mode 100644 index 00000000..136606f5 --- /dev/null +++ b/src/types/workflow/WorkflowRuntimeStatus.type.ts @@ -0,0 +1,25 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * WorkflowRuntimeStatus is the status of a workflow instance. + */ +export enum WorkflowRuntimeStatus { + Unknown = "Unknown", + Running = "Running", + Completed = "Completed", + Failed = "Failed", + Terminated = "Terminated", + Pending = "Pending", + Suspended = "Suspended", +} diff --git a/src/types/workflow/WorkflowStartOptions.type.ts b/src/types/workflow/WorkflowStartOptions.type.ts new file mode 100644 index 00000000..48fec096 --- /dev/null +++ b/src/types/workflow/WorkflowStartOptions.type.ts @@ -0,0 +1,28 @@ +/* +Copyright 2023 The Dapr Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { KeyValueType } from "../KeyValue.type"; + +export type WorkflowStartOptions = { + /** + * The content type of the message. + * This is optional and will be inferred from the payload if not provided. + */ + contentType?: string; + + /** + * Options to be passed to the workflow. + * Only applicable for gRPC. + */ + workflowOptions?: KeyValueType; +};