Skip to content

Commit ae1a5b5

Browse files
authored
Merge pull request #41 from nibble-4bits/refactor-6
Refactor 6
2 parents f914c71 + e967933 commit ae1a5b5

23 files changed

+105
-45
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AWS Local Step Functions
22

3-
A Node.js implementation of the [Amazon States Language specification](https://states-language.net/spec.html).
3+
A TypeScript implementation of the [Amazon States Language specification](https://states-language.net/spec.html).
44

55
This package lets you run AWS Step Functions locally on your machine!
66

examples/README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Examples
2+
3+
Here you can find some useful examples demonstrating specific usages of AWS Local Step Functions.
4+
5+
- [abort-execution-without-throwing](./abort-execution-without-throwing.js): Abort a running execution without throwing an error.
6+
- [abort-execution](./abort-execution.js): Abort a running execution.
7+
- [check-for-execution-failure](./check-for-execution-failure.js): Check if execution failed and catch error.
8+
- [disable-state-machine-validation](./disable-state-machine-validation.js): Disable ARN and/or JSONPath validations when instantiating a new `StateMachine` object.
9+
- [task-state-local-override](./task-state-local-override.js): Override the default action for a `Task` state, so that instead of invoking the Lambda specified in the `Resource` field, it runs a local function. This allows running state machines completely locally.
10+
- [wait-state-local-override](./wait-state-local-override.js): Override the wait duration of a `Wait` state so that instead of waiting the duration specified in the `Seconds`, `Timestamp`, `SecondsPath`, `TimestampPath` fields, it waits for a specified number of milliseconds.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { StateMachine, ExecutionError } from 'aws-local-stepfunctions';
2+
3+
const machineDefinition = {
4+
StartAt: 'MapState',
5+
States: {
6+
MapState: {
7+
Type: 'Map',
8+
Iterator: {
9+
StartAt: 'Success',
10+
States: {
11+
Success: {
12+
Type: 'Succeed',
13+
},
14+
},
15+
},
16+
End: true,
17+
},
18+
},
19+
};
20+
21+
const stateMachine = new StateMachine(machineDefinition);
22+
const myInput = 'this is not an array';
23+
const execution = stateMachine.run(myInput);
24+
25+
try {
26+
// Execution fails because Map state expects an array as input
27+
const result = await execution.result;
28+
console.log(result);
29+
} catch (error) {
30+
// When execution fails, type of error is `ExecutionError`
31+
if (error instanceof ExecutionError) {
32+
console.error('The execution has failed:', error);
33+
}
34+
}

src/stateMachine/InputOutputProcessing.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { PayloadTemplate } from '../typings/InputOutputProcessing';
22
import type { JSONValue } from '../typings/JSONValue';
3+
import type { Context } from '../typings/Context';
34
import { isPlainObj } from '../util';
45
import { jsonPathQuery } from './JsonPath';
56
import { StatesResultPathMatchFailureError } from '../error/predefined/StatesResultPathMatchFailureError';
@@ -13,7 +14,7 @@ import set from 'lodash/set.js';
1314
* * If `InputPath` is `null`, returns an empty object (`{}`).
1415
* * If `InputPath` is a string, it's considered a JSONPath and the selected portion of the current input is returned.
1516
*/
16-
function processInputPath(path: string | null | undefined, input: JSONValue, context?: JSONValue): JSONValue {
17+
function processInputPath(path: string | null | undefined, input: JSONValue, context?: Context): JSONValue {
1718
if (path === undefined) {
1819
return input;
1920
}
@@ -32,11 +33,7 @@ function processInputPath(path: string | null | undefined, input: JSONValue, con
3233
* @param context The context object to evaluate, if the path expression starts with `$$`.
3334
* @returns The processed payload template.
3435
*/
35-
function processPayloadTemplate(
36-
payloadTemplate: PayloadTemplate,
37-
json: JSONValue,
38-
context?: JSONValue
39-
): PayloadTemplate {
36+
function processPayloadTemplate(payloadTemplate: PayloadTemplate, json: JSONValue, context?: Context): PayloadTemplate {
4037
const resolvedProperties = Object.entries(payloadTemplate).map(([key, value]) => {
4138
let sanitizedKey = key;
4239
let resolvedValue = value;
@@ -78,7 +75,7 @@ function processResultPath(path: string | null | undefined, rawInput: JSONValue,
7875
const sanitizedPath = path.replace('$.', '');
7976

8077
if (isPlainObj(rawInput)) {
81-
const clonedRawInput = cloneDeep(rawInput) as object;
78+
const clonedRawInput = cloneDeep(rawInput);
8279
return set(clonedRawInput, sanitizedPath, result);
8380
}
8481

@@ -92,7 +89,7 @@ function processResultPath(path: string | null | undefined, rawInput: JSONValue,
9289
* * If `OutputPath` is `null`, returns an empty object (`{}`).
9390
* * If `OutputPath` is a string, it's considered a JSONPath and the selected portion of the current result is returned.
9491
*/
95-
function processOutputPath(path: string | null | undefined, result: JSONValue, context?: JSONValue): JSONValue {
92+
function processOutputPath(path: string | null | undefined, result: JSONValue, context?: Context): JSONValue {
9693
if (path === undefined) {
9794
return result;
9895
}

src/stateMachine/JsonPath.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { JSONValue } from '../typings/JSONValue';
2+
import type { Context } from '../typings/Context';
23
import { JSONPath as jp } from 'jsonpath-plus';
34

45
/**
@@ -8,7 +9,7 @@ import { JSONPath as jp } from 'jsonpath-plus';
89
* @param context The context object to evaluate, if the path expression starts with `$$`.
910
* @returns The value of the property that was queried for, if found. Otherwise returns `undefined`.
1011
*/
11-
function jsonPathQuery<T>(pathExpression: string, json: JSONValue, context?: JSONValue): T {
12+
function jsonPathQuery<T>(pathExpression: string, json: JSONValue, context?: Context): T {
1213
// If the expression starts with double `$$`, evaluate the path in the context object.
1314
if (pathExpression.startsWith('$$')) {
1415
return jp({ path: pathExpression.slice(1), json: context ?? null, wrap: false });

src/stateMachine/StateExecutor.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type { ChoiceState } from '../typings/ChoiceState';
1212
import type { FailState } from '../typings/FailState';
1313
import type { SucceedState } from '../typings/SucceedState';
1414
import type { RuntimeError } from '../error/RuntimeError';
15+
import type { Context } from '../typings/Context';
1516
import {
1617
processInputPath,
1718
processOutputPath,
@@ -101,7 +102,7 @@ export class StateExecutor {
101102
/**
102103
* Execute the current state.
103104
*/
104-
async execute(input: JSONValue, context: Record<string, unknown>, options: ExecuteOptions): Promise<ExecutionResult> {
105+
async execute(input: JSONValue, context: Context, options: ExecuteOptions): Promise<ExecutionResult> {
105106
const rawInput = cloneDeep(input);
106107

107108
try {
@@ -127,7 +128,7 @@ export class StateExecutor {
127128
// Handle `Retry` logic
128129
const { shouldRetry, waitTimeBeforeRetry } = this.shouldRetry(error as RuntimeError);
129130
if (shouldRetry && waitTimeBeforeRetry) {
130-
await sleep(waitTimeBeforeRetry);
131+
await sleep(waitTimeBeforeRetry, options.abortSignal);
131132
return this.execute(input, context, options);
132133
}
133134

@@ -144,7 +145,7 @@ export class StateExecutor {
144145
/**
145146
* Process the current input according to the `InputPath` and `Parameters` fields.
146147
*/
147-
private processInput(input: JSONValue, context: Record<string, unknown>): JSONValue {
148+
private processInput(input: JSONValue, context: Context): JSONValue {
148149
let processedInput = input;
149150

150151
if ('InputPath' in this.stateDefinition) {
@@ -163,7 +164,7 @@ export class StateExecutor {
163164
/**
164165
* Process the current result according to the `ResultSelector`, `ResultPath` and `OutputPath` fields.
165166
*/
166-
private processResult(result: JSONValue, rawInput: JSONValue, context: Record<string, unknown>): JSONValue {
167+
private processResult(result: JSONValue, rawInput: JSONValue, context: Context): JSONValue {
167168
let processedResult = result;
168169

169170
if ('ResultSelector' in this.stateDefinition) {
@@ -265,7 +266,7 @@ export class StateExecutor {
265266
private async executeTaskState(
266267
stateDefinition: TaskState,
267268
input: JSONValue,
268-
context: Record<string, unknown>,
269+
context: Context,
269270
stateName: string,
270271
options: ExecuteOptions
271272
): Promise<ExecutionResult> {
@@ -287,7 +288,7 @@ export class StateExecutor {
287288
private async executeMapState(
288289
stateDefinition: MapState,
289290
input: JSONValue,
290-
context: Record<string, unknown>,
291+
context: Context,
291292
stateName: string,
292293
options: ExecuteOptions
293294
): Promise<ExecutionResult> {
@@ -309,7 +310,7 @@ export class StateExecutor {
309310
private async executePassState(
310311
stateDefinition: PassState,
311312
input: JSONValue,
312-
context: Record<string, unknown>,
313+
context: Context,
313314
// eslint-disable-next-line @typescript-eslint/no-unused-vars
314315
stateName: string,
315316
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -330,7 +331,7 @@ export class StateExecutor {
330331
private async executeWaitState(
331332
stateDefinition: WaitState,
332333
input: JSONValue,
333-
context: Record<string, unknown>,
334+
context: Context,
334335
stateName: string,
335336
options: ExecuteOptions
336337
): Promise<ExecutionResult> {
@@ -363,7 +364,7 @@ export class StateExecutor {
363364
private async executeChoiceState(
364365
stateDefinition: ChoiceState,
365366
input: JSONValue,
366-
context: Record<string, unknown>,
367+
context: Context,
367368
// eslint-disable-next-line @typescript-eslint/no-unused-vars
368369
stateName: string,
369370
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -383,7 +384,7 @@ export class StateExecutor {
383384
private async executeSucceedState(
384385
stateDefinition: SucceedState,
385386
input: JSONValue,
386-
context: Record<string, unknown>,
387+
context: Context,
387388
// eslint-disable-next-line @typescript-eslint/no-unused-vars
388389
stateName: string,
389390
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -403,7 +404,7 @@ export class StateExecutor {
403404
private async executeFailState(
404405
stateDefinition: FailState,
405406
input: JSONValue,
406-
context: Record<string, unknown>,
407+
context: Context,
407408
// eslint-disable-next-line @typescript-eslint/no-unused-vars
408409
stateName: string,
409410
// eslint-disable-next-line @typescript-eslint/no-unused-vars

src/stateMachine/StateMachine.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { StateMachineDefinition } from '../typings/StateMachineDefinition';
22
import type { JSONValue } from '../typings/JSONValue';
33
import type { ExecuteOptions, RunOptions, ValidationOptions } from '../typings/StateMachineImplementation';
4+
import type { Context } from '../typings/Context';
45
import { ExecutionAbortedError } from '../error/ExecutionAbortedError';
56
import { ExecutionError } from '../error/ExecutionError';
67
import { StateExecutor } from './StateExecutor';
@@ -87,7 +88,7 @@ export class StateMachine {
8788
let nextState = '';
8889
let isEndState = false;
8990
// eslint-disable-next-line prefer-const
90-
let context: Record<string, unknown> = {};
91+
let context: Context = {};
9192

9293
try {
9394
do {

src/stateMachine/stateActions/BaseStateAction.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { TerminalState } from '../../typings/TerminalState';
33
import type { JSONValue } from '../../typings/JSONValue';
44
import type { BaseState } from '../../typings/BaseState';
55
import type { ExecutionResult } from '../../typings/StateActions';
6+
import type { Context } from '../../typings/Context';
67

78
abstract class BaseStateAction<T extends BaseState | IntermediateState | TerminalState> {
89
protected stateDefinition: T;
@@ -25,11 +26,7 @@ abstract class BaseStateAction<T extends BaseState | IntermediateState | Termina
2526
return executionResult;
2627
}
2728

28-
abstract execute(
29-
input: JSONValue,
30-
context: Record<string, unknown>,
31-
options?: Record<string, unknown>
32-
): Promise<ExecutionResult>;
29+
abstract execute(input: JSONValue, context: Context, options?: Record<string, unknown>): Promise<ExecutionResult>;
3330
}
3431

3532
export { BaseStateAction };

src/stateMachine/stateActions/ChoiceStateAction.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { JSONValue } from '../../typings/JSONValue';
22
import type { ChoiceState, ChoiceRuleWithoutNext } from '../../typings/ChoiceState';
33
import type { ChoiceStateActionOptions, ExecutionResult } from '../../typings/StateActions';
4+
import type { Context } from '../../typings/Context';
45
import { BaseStateAction } from './BaseStateAction';
56
import { jsonPathQuery } from '../JsonPath';
67
import { StatesNoChoiceMatchedError } from '../../error/predefined/StatesNoChoiceMatchedError';
@@ -253,7 +254,7 @@ class ChoiceStateAction extends BaseStateAction<ChoiceState> {
253254
override async execute(
254255
input: JSONValue,
255256
// eslint-disable-next-line @typescript-eslint/no-unused-vars
256-
context: Record<string, unknown>,
257+
context: Context,
257258
// eslint-disable-next-line @typescript-eslint/no-unused-vars
258259
options?: ChoiceStateActionOptions
259260
): Promise<ExecutionResult> {

src/stateMachine/stateActions/FailStateAction.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { JSONValue } from '../../typings/JSONValue';
22
import type { FailState } from '../../typings/FailState';
33
import type { ExecutionResult, FailStateActionOptions } from '../../typings/StateActions';
4+
import type { Context } from '../../typings/Context';
45
import { BaseStateAction } from './BaseStateAction';
56
import { FailStateError } from '../../error/FailStateError';
67

@@ -13,7 +14,7 @@ class FailStateAction extends BaseStateAction<FailState> {
1314
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1415
input: JSONValue,
1516
// eslint-disable-next-line @typescript-eslint/no-unused-vars
16-
context: Record<string, unknown>,
17+
context: Context,
1718
// eslint-disable-next-line @typescript-eslint/no-unused-vars
1819
options?: FailStateActionOptions
1920
): Promise<ExecutionResult> {

0 commit comments

Comments
 (0)