From 9d1850b9e6a73af253cceec944b520b942ff927c Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Tue, 29 Aug 2023 18:23:26 +0530 Subject: [PATCH 1/6] feat: add support for binding provider --- src/workflow/types.ts | 5 +++++ src/workflow/utils.ts | 29 ++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/workflow/types.ts b/src/workflow/types.ts index f1f4660..c73d30a 100644 --- a/src/workflow/types.ts +++ b/src/workflow/types.ts @@ -56,6 +56,7 @@ export type WorkflowOptions = { creationTimeBindings?: Record; templateType?: TemplateType; executor?: WorkflowExecutor; + bindingProvider?: WorkflowBindingProvider; }; export type WorkflowOptionsInternal = WorkflowOptions & { @@ -66,3 +67,7 @@ export type WorkflowOptionsInternal = WorkflowOptions & { export interface WorkflowExecutor { execute(engine: WorkflowEngine, input: any): Promise; } + +export interface WorkflowBindingProvider { + provide(name: string): Promise; +} diff --git a/src/workflow/utils.ts b/src/workflow/utils.ts index 8fdfeaf..98af24e 100644 --- a/src/workflow/utils.ts +++ b/src/workflow/utils.ts @@ -8,6 +8,7 @@ import { PathBinding, ValueBinding, Workflow, + WorkflowBindingProvider, WorkflowExecutor, WorkflowOptionsInternal, } from './types'; @@ -55,13 +56,26 @@ export class WorkflowUtils { } } + private static async getModuleExportsFromProvider( + modulePath: string, + provider: WorkflowBindingProvider, + ): Promise { + try { + return await provider.provide(modulePath); + } catch (error: any) { + // Ignoring error + } + } + private static async getModuleExportsFromAllPaths( - rootPath: string, bindingPath: string, + options: WorkflowOptionsInternal, ): Promise { return ( - (await this.getModuleExports(bindingPath)) || - (await this.getModuleExports(path.join(rootPath, bindingPath), true)) + (options.bindingProvider + ? await this.getModuleExportsFromProvider(bindingPath, options.bindingProvider) + : await this.getModuleExports(bindingPath)) ?? + (await this.getModuleExports(path.join(options.rootPath, bindingPath), true)) ); } @@ -74,7 +88,7 @@ export class WorkflowUtils { const bindings = await Promise.all( options.bindingsPaths.map(async (bindingPath) => { - return this.getModuleExportsFromAllPaths(options.rootPath, bindingPath); + return this.getModuleExportsFromAllPaths(bindingPath, options); }), ); return Object.assign({}, ...bindings); @@ -104,8 +118,8 @@ export class WorkflowUtils { const pathBinding = binding as PathBinding; const bindingSource = await this.getModuleExportsFromAllPaths( - options.rootPath, pathBinding.path || 'bindings', + options, ); if (pathBinding.name) { bindingsObj[pathBinding.name] = pathBinding.exportAll @@ -123,10 +137,7 @@ export class WorkflowUtils { options: WorkflowOptionsInternal, ): Promise { if (workflow?.executor?.path) { - let executor = await this.getModuleExportsFromAllPaths( - options.rootPath, - workflow.executor.path, - ); + let executor = await this.getModuleExportsFromAllPaths(workflow.executor.path, options); if ( !executor || From bd73dc32e8001222ac273a22f88dd8f7c514ffd0 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Tue, 29 Aug 2023 22:58:44 +0530 Subject: [PATCH 2/6] feat: add tests --- test/scenarios/bindings_provider/bindings.ts | 1 + test/scenarios/bindings_provider/data.ts | 11 +++++++++++ test/scenarios/bindings_provider/provider.ts | 10 ++++++++++ test/scenarios/bindings_provider/workflow.yaml | 9 +++++++++ 4 files changed, 31 insertions(+) create mode 100644 test/scenarios/bindings_provider/bindings.ts create mode 100644 test/scenarios/bindings_provider/data.ts create mode 100644 test/scenarios/bindings_provider/provider.ts create mode 100644 test/scenarios/bindings_provider/workflow.yaml diff --git a/test/scenarios/bindings_provider/bindings.ts b/test/scenarios/bindings_provider/bindings.ts new file mode 100644 index 0000000..063a72b --- /dev/null +++ b/test/scenarios/bindings_provider/bindings.ts @@ -0,0 +1 @@ +export const anotherMessage = 'Got binding from normal binding.'; diff --git a/test/scenarios/bindings_provider/data.ts b/test/scenarios/bindings_provider/data.ts new file mode 100644 index 0000000..366c7a4 --- /dev/null +++ b/test/scenarios/bindings_provider/data.ts @@ -0,0 +1,11 @@ +import { BindingProvider } from './provider'; +import { Scenario } from '../../types'; + +export const data = [ + { + output: 'Got binding from provider.Got binding from normal binding.', + options: { + bindingProvider: BindingProvider.INSTANCE, + }, + }, +] as Scenario[]; diff --git a/test/scenarios/bindings_provider/provider.ts b/test/scenarios/bindings_provider/provider.ts new file mode 100644 index 0000000..d786a1e --- /dev/null +++ b/test/scenarios/bindings_provider/provider.ts @@ -0,0 +1,10 @@ +import { WorkflowBindingProvider } from '../../../src'; +export class BindingProvider implements WorkflowBindingProvider { + static readonly INSTANCE = new BindingProvider(); + provide(name: string): Promise { + if (name == 'message') { + return Promise.resolve({ message: 'Got binding from provider.' }); + } + return Promise.reject(new Error('Binding not found')); + } +} diff --git a/test/scenarios/bindings_provider/workflow.yaml b/test/scenarios/bindings_provider/workflow.yaml new file mode 100644 index 0000000..9ee955e --- /dev/null +++ b/test/scenarios/bindings_provider/workflow.yaml @@ -0,0 +1,9 @@ +bindings: + # this will be resolved using custom binding provider + - path: message + - name: anotherMessage + +steps: + - name: getMessage + template: | + $message & $anotherMessage From c8307fdeba1da638424206dd1cb300a004a199b4 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Thu, 31 Aug 2023 12:53:34 +0530 Subject: [PATCH 3/6] refactor: error matcher --- src/steps/composed/executors/loop.ts | 9 ++++++--- src/steps/types.ts | 2 ++ test/e2e-custom.test.ts | 1 + test/e2e.test.ts | 6 +++--- test/types.ts | 2 +- test/utils/common.ts | 19 +++++++++++++------ 6 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/steps/composed/executors/loop.ts b/src/steps/composed/executors/loop.ts index c2af6b9..0bf6bf4 100644 --- a/src/steps/composed/executors/loop.ts +++ b/src/steps/composed/executors/loop.ts @@ -2,6 +2,7 @@ import { StepExecutionError } from '../../errors'; import { ExecutionBindings } from '../../../workflow/types'; import { ComposableStepExecutor } from './composable'; import { StepExecutor, StepOutput } from '../../types'; +import { ErrorUtils } from '../../../common'; export class LoopStepExecutor extends ComposableStepExecutor { constructor(nextExecutor: StepExecutor) { @@ -15,11 +16,13 @@ export class LoopStepExecutor extends ComposableStepExecutor { try { return await super.execute(element, executionBindings); } catch (error: any) { + const stepExecutionError = ErrorUtils.createStepExecutionError(error, this.getStepName()); return { error: { - message: error.message, - status: error.status, - originalError: error.originalError, + message: stepExecutionError.message, + status: stepExecutionError.status, + error: stepExecutionError, + originalError: stepExecutionError.originalError, }, }; } diff --git a/src/steps/types.ts b/src/steps/types.ts index 26287ad..063f555 100644 --- a/src/steps/types.ts +++ b/src/steps/types.ts @@ -1,6 +1,7 @@ import { ExecutionBindings, Binding } from '../workflow/types'; import { Executor } from '../common/types'; import { JsonataStepExecutor, JsonTemplateStepExecutor } from './base/simple/executors/template'; +import { StepExecutionError } from './errors'; export interface StepExecutor extends Executor { /** @@ -22,6 +23,7 @@ export type StepOutput = { message: string; status: number; originalError?: Error; + error: StepExecutionError; }; skipped?: boolean; output?: any; diff --git a/test/e2e-custom.test.ts b/test/e2e-custom.test.ts index a0bb62f..625fd82 100644 --- a/test/e2e-custom.test.ts +++ b/test/e2e-custom.test.ts @@ -18,6 +18,7 @@ describe('Custom Scenarios tests', () => { }, }, ]); + expect(result.output[0].error?.error.message).toEqual('some error'); expect(result.output[0].error?.originalError?.message).toEqual('some error'); }); }); diff --git a/test/e2e.test.ts b/test/e2e.test.ts index 42d5094..0bb3c67 100644 --- a/test/e2e.test.ts +++ b/test/e2e.test.ts @@ -32,9 +32,9 @@ describe('Scenarios tests', () => { const result = await ScenarioUtils.executeScenario(workflowEngine, scenario); expect(result.output).toEqual(scenario.output); } catch (error: any) { - expect(error).toMatchObject(CommonUtils.getErrorMatcher(scenario.error)); - if (scenario.errorClass) { - expect(error.error?.constructor.name).toEqual(scenario.errorClass); + CommonUtils.matchError(error, scenario.error); + if (scenario.error?.errorClass) { + expect(error.error?.constructor.name).toEqual(scenario.error.errorClass); } } finally { if (scenario.logLevel !== undefined) { diff --git a/test/types.ts b/test/types.ts index 5b1ca40..2e24d56 100644 --- a/test/types.ts +++ b/test/types.ts @@ -5,6 +5,7 @@ export type ScenarioError = { status?: string; stepName?: string; childStepName?: string; + errorClass?: string; }; export type Scenario = { @@ -15,6 +16,5 @@ export type Scenario = { stepName?: string; output?: any; error?: ScenarioError; - errorClass?: string; logLevel?: LogLevel; }; diff --git a/test/utils/common.ts b/test/utils/common.ts index 46208af..03c419d 100644 --- a/test/utils/common.ts +++ b/test/utils/common.ts @@ -1,16 +1,23 @@ import { ScenarioError } from '../types'; export class CommonUtils { - static getErrorMatcher(error?: ScenarioError) { - if (!error) { + static matchError(actual: any, expected?: ScenarioError) { + if (!expected) { // Ideally shouldn't reach here. // Sending default error so that test case fails. return { message: 'should fail' }; } - let errorMatcher = error; - if (error.message) { - errorMatcher.message = expect.stringContaining(error.message); + if (expected.message) { + expect(actual.message).toEqual(expect.stringContaining(expected.message)); + } + if (expected.status) { + expect(actual.status).toEqual(expected.status); + } + if (expected.stepName) { + expect(actual.stepName).toEqual(expected.stepName); + } + if (expected.childStepName) { + expect(actual.childStepName).toEqual(expected.childStepName); } - return errorMatcher; } } From e1ebf7fa16611a5419de24bca65f97d75577d8f5 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Thu, 31 Aug 2023 13:32:10 +0530 Subject: [PATCH 4/6] refactor: step execution error --- src/steps/errors.ts | 6 +++--- test/e2e-custom.test.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/steps/errors.ts b/src/steps/errors.ts index 2ee760a..8dd6c25 100644 --- a/src/steps/errors.ts +++ b/src/steps/errors.ts @@ -13,8 +13,8 @@ export class StepCreationError extends StatusError { export class StepExecutionError extends StatusError { stepName: string; childStepName?: string; - error?: Error; - originalError?: Error; + error: Error; + originalError: Error; constructor( message: string, status: number, @@ -25,7 +25,7 @@ export class StepExecutionError extends StatusError { super(message, status); this.stepName = stepName; this.childStepName = childStepName; - this.error = error; + this.error = error || this; this.originalError = (error as any)?.originalError || error; } } diff --git a/test/e2e-custom.test.ts b/test/e2e-custom.test.ts index 625fd82..aafa6a6 100644 --- a/test/e2e-custom.test.ts +++ b/test/e2e-custom.test.ts @@ -19,7 +19,7 @@ describe('Custom Scenarios tests', () => { }, ]); expect(result.output[0].error?.error.message).toEqual('some error'); - expect(result.output[0].error?.originalError?.message).toEqual('some error'); + expect(result.output[0].error?.originalError.message).toEqual('some error'); }); }); }); From 0ea85677f3ca7e27951ae754dae656ca08862358 Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Thu, 31 Aug 2023 13:35:02 +0530 Subject: [PATCH 5/6] refactor: step execution error --- src/steps/errors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/steps/errors.ts b/src/steps/errors.ts index 8dd6c25..ce8bea6 100644 --- a/src/steps/errors.ts +++ b/src/steps/errors.ts @@ -26,7 +26,7 @@ export class StepExecutionError extends StatusError { this.stepName = stepName; this.childStepName = childStepName; this.error = error || this; - this.originalError = (error as any)?.originalError || error; + this.originalError = (error as any).originalError || error; } } From 740a4e9875cc2013238c3b9d6d4a8f7a2970d5ad Mon Sep 17 00:00:00 2001 From: Dilip Kola Date: Thu, 31 Aug 2023 13:36:08 +0530 Subject: [PATCH 6/6] refactor: step execution error --- src/steps/errors.ts | 2 +- src/steps/types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/steps/errors.ts b/src/steps/errors.ts index ce8bea6..a5c3808 100644 --- a/src/steps/errors.ts +++ b/src/steps/errors.ts @@ -26,7 +26,7 @@ export class StepExecutionError extends StatusError { this.stepName = stepName; this.childStepName = childStepName; this.error = error || this; - this.originalError = (error as any).originalError || error; + this.originalError = (this.error as any).originalError || error; } } diff --git a/src/steps/types.ts b/src/steps/types.ts index 063f555..0d51ce5 100644 --- a/src/steps/types.ts +++ b/src/steps/types.ts @@ -22,7 +22,7 @@ export type StepOutput = { error?: { message: string; status: number; - originalError?: Error; + originalError: Error; error: StepExecutionError; }; skipped?: boolean;