Skip to content
This repository was archived by the owner on Aug 1, 2022. It is now read-only.

Commit 235d0dd

Browse files
Merge pull request #37 from sam-n-johnston/feat/add-catchers
[2] Feat/add catchers
2 parents 10a681e + 1a882a8 commit 235d0dd

File tree

8 files changed

+118
-20
lines changed

8 files changed

+118
-20
lines changed

src/stateTasks/executors/TaskExecutor.ts

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { StateInfoHandler } from '../../StateInfoHandler';
99
import { StateProcessor } from '../../StateProcessor';
1010
import { Context } from '../../Context/Context';
1111
import { Retriers } from '../../types/Retriers';
12+
import { Catchers } from '../../types/Catchers';
13+
import { StatesErrors } from '../../types/StatesErrors';
1214

1315
export class TaskExecutor extends StateTypeExecutor {
1416
public async execute(
@@ -31,11 +33,17 @@ export class TaskExecutor extends StateTypeExecutor {
3133

3234
// eslint-disable-next-line @typescript-eslint/ban-types
3335
let output: any;
34-
if (stateDefinition.Retry) {
35-
const retrier = Retriers.create(stateDefinition.Retry);
36-
output = await retrier.retry(functionLambda[stateInfo.handlerName].bind(this, input, context));
37-
} else {
38-
output = await functionLambda[stateInfo.handlerName](input, context);
36+
try {
37+
if (stateDefinition.Retry) {
38+
const retrier = Retriers.create(stateDefinition.Retry);
39+
output = await retrier.retry(() => functionLambda[stateInfo.handlerName](input, context));
40+
} else {
41+
output = await functionLambda[stateInfo.handlerName](input, context);
42+
}
43+
} catch (error) {
44+
this.removeEnvVarsLambdaSpecific(stateInfo.environment);
45+
46+
return this.dealWithError(stateDefinition, error, input);
3947
}
4048

4149
this.removeEnvVarsLambdaSpecific(stateInfo.environment);
@@ -56,7 +64,33 @@ export class TaskExecutor extends StateTypeExecutor {
5664
return false;
5765
}
5866

59-
private processInput(json: string | undefined, stateDefinition: TaskStateDefinition, context: Context): any {
67+
private dealWithError(stateDefinition: TaskStateDefinition, error: Error, input: Record<string, unknown>) {
68+
if (!stateDefinition.Catch) {
69+
throw error;
70+
}
71+
72+
const catchers = Catchers.create(stateDefinition.Catch);
73+
const catcher = catchers.getCatcherBasedOn([StatesErrors.TaskFailed, StatesErrors.All]);
74+
75+
if (!catcher) {
76+
throw error;
77+
}
78+
79+
const output = { message: error.message, stack: error.stack };
80+
const outputJson = StateProcessor.processResultPath(input, output, catcher.ResultPath);
81+
82+
return {
83+
Next: catcher.Next,
84+
End: stateDefinition.End,
85+
json: outputJson,
86+
};
87+
}
88+
89+
private processInput(
90+
json: string | undefined,
91+
stateDefinition: TaskStateDefinition,
92+
context: Context,
93+
): StateExecutorOutput {
6094
const proccessedInputJson = StateProcessor.processInputPath(json, stateDefinition.InputPath);
6195

6296
let output = proccessedInputJson;

src/types/Catcher.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { TaskCatchRule } from './State';
2+
import { StatesErrors } from './StatesErrors';
3+
4+
export class Catcher {
5+
private constructor(
6+
private readonly _ErrorEquals: StatesErrors[],
7+
private readonly _Next: string,
8+
private readonly _ResultPath?: string,
9+
) {}
10+
11+
public static create(taskCatchRule: TaskCatchRule): Catcher {
12+
return new Catcher(taskCatchRule.ErrorEquals, taskCatchRule.Next, taskCatchRule.ResultPath);
13+
}
14+
15+
get Next(): string {
16+
return this._Next;
17+
}
18+
19+
get ErrorEquals(): StatesErrors[] {
20+
return this._ErrorEquals;
21+
}
22+
23+
get ResultPath(): string | undefined {
24+
return this._ResultPath;
25+
}
26+
27+
public includesSomeOf(statesErrors: StatesErrors[]): boolean {
28+
return statesErrors.some((statesError) => this.ErrorEquals.includes(statesError));
29+
}
30+
}

src/types/Catchers.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Catcher } from './Catcher';
2+
import { TaskCatchRule } from './State';
3+
import { StatesErrors } from './StatesErrors';
4+
5+
export class Catchers {
6+
private constructor(private readonly _catchers: Catcher[]) {}
7+
8+
public static create(taskCatchRules: TaskCatchRule[]): Catchers {
9+
const catchers = taskCatchRules.map((taskCatchRule) => {
10+
return Catcher.create(taskCatchRule);
11+
});
12+
return new Catchers(catchers);
13+
}
14+
15+
public getCatcherBasedOn(statesErrors: StatesErrors[]): Catcher | undefined {
16+
const catcher = this._catchers.find((catcher) => {
17+
return catcher.includesSomeOf(statesErrors);
18+
});
19+
if (!catcher) {
20+
return;
21+
}
22+
return catcher;
23+
}
24+
}

src/types/Retrier.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import { TaskRetryRule } from './State';
2-
3-
export enum StatesErrors {
4-
DataLimitExceeded = 'States.DataLimitExceeded',
5-
Runtime = 'States.Runtime',
6-
Timeout = 'States.Timeout',
7-
TaskFailed = 'States.TaskFailed',
8-
Permissions = 'States.Permissions',
9-
}
2+
import { StatesErrors } from './StatesErrors';
103

114
export class Retrier {
125
private currentNumberOfRetries = 0;
@@ -31,15 +24,22 @@ export class Retrier {
3124
}
3225

3326
async retry(fn: () => any): Promise<any> {
27+
let output: unknown;
3428
try {
35-
return await fn();
29+
output = await fn();
30+
return output;
3631
} catch (error) {
3732
if (this.currentNumberOfRetries < this._MaxAttempts) {
3833
this.currentNumberOfRetries++;
39-
return await new Promise((resolve) => {
34+
return await new Promise((resolve, reject) => {
4035
setTimeout(async () => {
4136
this.currentIntervalSeconds = this.currentIntervalSeconds * this._BackoffRate;
42-
return resolve(await this.retry(fn));
37+
try {
38+
const output = await this.retry(fn);
39+
return resolve(output);
40+
} catch (error) {
41+
return reject(error);
42+
}
4343
}, this.currentIntervalSeconds * 1000);
4444
});
4545
} else {

src/types/Retriers.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { Retrier, StatesErrors } from './Retrier';
1+
import { Retrier } from './Retrier';
22
import { TaskRetryRule } from './State';
3+
import { StatesErrors } from './StatesErrors';
34

45
export class Retriers {
56
private constructor(private readonly _retriers: Retrier[]) {}

src/types/State.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { StateMachineDefinition } from './StateMachine';
22
import { StateType } from '../stateTasks/StateType';
3-
import { StatesErrors } from './Retrier';
3+
import { StatesErrors } from './StatesErrors';
44

55
export type StateInfo = {
66
handlerPath: string;

src/types/StatesErrors.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export enum StatesErrors {
2+
All = 'States.ALL',
3+
DataLimitExceeded = 'States.DataLimitExceeded',
4+
Runtime = 'States.Runtime',
5+
Timeout = 'States.Timeout',
6+
TaskFailed = 'States.TaskFailed',
7+
Permissions = 'States.Permissions',
8+
}

tests/src/Retrier.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { Retrier, StatesErrors } from '../../src/types/Retrier';
1+
import { Retrier } from '../../src/types/Retrier';
2+
import { StatesErrors } from '../../src/types/StatesErrors';
23

34
describe('Retrier', () => {
45
describe('when the function succeeds', () => {

0 commit comments

Comments
 (0)