Skip to content

Latest commit

 

History

History
508 lines (431 loc) · 8.81 KB

readme.md

File metadata and controls

508 lines (431 loc) · 8.81 KB

TS to ASL

Converts typescript code to Amazon State Language. This allows for local development/debugging/testing and deployment as an AWS Step Function.

ASL Lib

Not all ASL constructs can be easily represented in Typescript. A Retry block with specified back-off rate is something that does not exists in Typescript natively. The ASL Lib bridges this gap by providing a Typescript library. The Typescript library can be used for local debugging and testing of the program and can be 1:1 converted to AWS ASL.

ASL Lib code:

ASL.Wait({ Seconds: 2 });
ASL.Task({
  Resource: "arn:aws:123123123:lambda:function:my-func",
  Retry: [{
    ErrorEquals: ["States.Timeout"],
    IntervalSeconds: 3,
    MaxAttempts: 2,
    BackoffRate: 1.5
  }]
});

ASL Lib code can be mixed with Typescript code:

if (pwd !== "password" ) {
  throw new Error("wrong password")
} else {
  ASL.Wait({ Seconds: 2 });
  performWork();
}

The above will be converted to ASL Lib code prior to being deployed to ASL

ASL.Choice({
  Choices: [{
    Variable: "$.password",
    Not: { StringEquals: "pwd" },
    NextInvoke: () => { 
      ASL.Failed({ Error: 'Error', Cause: 'wrong password'  });
  }]
  DefaultInvoke: () => {
    ASL.Wait({ Seconds: 2 });
    ASL.Invoke({ Resource: performWork });
  }
});

Typescript language support

These language constructs translate the ASL Lib code and are implemented as AST transformations. The source code for these transformations can be found in the ./lang-support folder.

The typescript language constructs that are supported are listed below:

Variable Assignment

let abc = 'hello';
let abc = 43;
let abc = { number: 43; text: 'hello' };
ASL Lib Typescript code
let abc = ASL.Pass({ Result: 'hello' });
let abc = ASL.Pass({ Result: 43 });
let abc = ASL.Pass({ Result: { number: 43, text: 'hello' } });
ASL output
    {
      "StartAt": "Assign_abc",
      "States": {
        "Assign_abc": { "Type": "Pass",  "ResultPath": "$.abc", "Result": "hello" },
        "Assign_abc_1": { "Type": "Pass",  "ResultPath": "$.abc", "Result": 43 },
        "Assign_abc_2": { "Type": "Pass", "ResultPath": "$.abc", 
          "Result": {
            "number": 43,
            "text": "hello"
          }
        }
      }
    }
current limitations:
  • only literals can be used, e.g. references to other variables are not supported    

Throw Statement

throw new Error();
throw new Error("bad luck");
throw new SpecialError("bad luck");
ASL Lib Typescript code
ASL.Failed({ Error: 'Error' })
ASL.Failed({ Error: 'Error', Cause: 'bad luck' })
ASL.Failed({ Error: 'SpecialError', Cause: 'bad luck' })
ASL output
 {
    "StartAt": "Failed",
    "States": {
      "Failed": { "Type": "Failed", "Error": "Error" },
      "Failed_1": { "Type": "Failed", "Error": "Error", "Cause": "bad luck" },
      "Failed_2": { "Type": "Failed", "Error": "SpecialError", "Cause": "bad luck" }
    }
  }
   

If Statement

if (password !== 'pwd') throw new Error('wrong password');
if (age < 18) throw new Error('minor') else proceed();
ASL Lib Typescript code
ASL.Choice({
  Choices: [{
      Variable: "$.password",
      Not: { StringEquals: "pwd" },
      NextInvoke: () => { ASL.Failed({ Error: 'Error', Cause: 'wrong password' }) }
    }]
});

ASL.Choice({
  Choices: [{
      Variable: "$.age",
      NumericLessThan: "18",
      NextInvoke: () => { ASL.Failed({ Error: 'Error', Cause: 'minor' }) }
  }],
  DefaultInvoke: () => { 
    ASL.Task({ TypescriptInvoke: proceed }); 
  }
});
ASL output
 {
  "StartAt": "Choice",
  "States": {
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.password",
          "Not": { "StringEquals": "pwd" },
          "Next": "Failed"
        }
      ]
    },
    "Failed": {
      "Type": "Failed",
      "Error": "Error",
      "Cause": "wrong password",
    },
    "Choice_1": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.age",
          "NumericLessThan": "18",
          "Next": "Failed_1"
        }
      ],
      "Default": "Task",
    },
    "Failed_1": {
      "Type": "Failed",
      "Error": "Error",
      "Cause": "minor",
    },
    "Task": {
      "Resource": "typescript:proceed",
      "Type": "Task",
    }
  }
}
current limitations:
  • else is not supported
  • complex boolean expressions are not supported

   

Switch Statement

switch (color) {
  case "red":
    doRed();
    break;

  default:
    somethingElse();
    break;
}
ASL Lib Typescript code
ASL.Choice({ 
  Choices: [{ 
    Variable: "$.color", 
    StringEquals: "red", 
    NextInvoke: () => {
      ASL.Task({ TypescriptInvoke: doRed });
    } 
  }], 
  DefaultInvoke: () => {
    ASL.Task({ TypescriptInvoke: somethingElse });
  } 
});
"
ASL output
{
  "StartAt": "Choice",
  "States": {
    "Choice": {
      "Type": "Choice",
      "Choices": [
        {
          "Variable": "$.color",
          "StringEquals": "red",
          "Next": "Task"
        }
      ],
      "Default": "Task_1"
    },
    "Task_1": {
      "Type": "Task",
      "Resource": "typescript:somethingElse"
    },
    "Task": {
      "Type": "Task",
      "Resource": "typescript:doRed"
    }
  }
}
   

Call Statement

sayHello(arg);
await sayHello();
const z = await sayHello();
ASL Lib Typescript code
ASL.Task({
  TypescriptInvoke: sayHello,
  InputPath: \\"$.arg\\"
});
await ASL.Task({
  TypescriptInvoke: sayHello
});
const z = await ASL.Task({
  TypescriptInvoke: sayHello
});
ASL output
{
  "StartAt": "Task",
  "States": {
    "Task": {
      "Type": "Task",
      "Resource": "typescript:sayHello",
      "InputPath": "$.arg"
    },
    "Task_1": {
      "Type": "Task",
      "Resource": "typescript:sayHello"
    },
    "Assign_z": {
      "Type": "Task",
      "Resource": "typescript:sayHello",
      "ResultPath": "$.z"
    }
  }
}
   

Promise.all Statement

await Promise.all([
  () => { spinLeft(); },
  () => { spinRight(); }
]);
ASL Lib Typescript code
await ASL.Parallel({
  Branches: [{
    BlockInvoke: () => { 
      ASL.Task({ TypescriptInvoke: spinLeft }); }
    }, {
    BlockInvoke: () => { 
        ASL.Task({ TypescriptInvoke: spinRight }); }
    }]
});
ASL output
 {
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "Task",
          "States": {
            "Task": {
              "Type": "Task",
              "Resource": "typescript:spinLeft"
            }
          }
        },
        {
          "StartAt": "Task",
          "States": {
            "Task": {
              "Type": "Task",
              "Resource": "typescript:spinRight"
            }
          }
        }
      ]
    }
  }
}
   

Try Statement

try { doWork(); } catch { revert(); }
ASL Lib Typescript code
ASL.Parallel({
  Branches: [{
    BlockInvoke: () => { 
      ASL.Task({ TypescriptInvoke: doWork }); 
    }
  }],
  Catch: [{
    ErrorEquals: ["States.All"],
    NextInvoke: () => { ASL.Task({
        TypescriptInvoke: revert
    }); 
  }
  }]
});
ASL output
 {
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "Task",
          "States": {
            "Task": {
              "Type": "Task",
              "Resource": "typescript:doWork"
            }
          }
        }
      ],
      "Catch": [
        {
          "ErrorEquals": ["States.All"],
          "Next": "Task"
        }
      ]
    },
    "Task": {
      "Type": "Task",
      "Resource": "typescript:revert"
    }
  }
}
   

Return Statement

return;
ASL Lib Typescript code
ASL.Succeed();
ASL output
 {
  "StartAt": "Succeed",
  "States": {
    "Succeed": {
      "Type": "Succeed"
    }
  }
}