diff --git a/typescript/stepfunction-external-definition/.gitignore b/typescript/stepfunction-external-definition/.gitignore new file mode 100644 index 0000000000..f60797b6a9 --- /dev/null +++ b/typescript/stepfunction-external-definition/.gitignore @@ -0,0 +1,8 @@ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/stepfunction-external-definition/.npmignore b/typescript/stepfunction-external-definition/.npmignore new file mode 100644 index 0000000000..c1d6d45dcf --- /dev/null +++ b/typescript/stepfunction-external-definition/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/typescript/stepfunction-external-definition/README.md b/typescript/stepfunction-external-definition/README.md new file mode 100644 index 0000000000..3c7b0da83f --- /dev/null +++ b/typescript/stepfunction-external-definition/README.md @@ -0,0 +1,14 @@ +# Welcome to your CDK TypeScript project + +This is a Tyepscript CDK project which enables implementing stepfunctions with external asl(Amazon States Language) file. This can be useful in usecases where it is desired to leverage local development flow of the stepfunction statemachines,e.g. AWS toolkit for VScode, and directly import the asl file into your project without rewriting it in CDK. + +The `cdk.json` file tells the CDK Toolkit how to execute your app. + +## Useful commands + +* `npm run build` compile typescript to js +* `npm run watch` watch for changes and compile +* `npm run test` perform the jest unit tests +* `npx cdk deploy` deploy this stack to your default AWS account/region +* `npx cdk diff` compare deployed stack with current state +* `npx cdk synth` emits the synthesized CloudFormation template diff --git a/typescript/stepfunction-external-definition/bin/stepfunction-external-definition.ts b/typescript/stepfunction-external-definition/bin/stepfunction-external-definition.ts new file mode 100644 index 0000000000..8620751e2c --- /dev/null +++ b/typescript/stepfunction-external-definition/bin/stepfunction-external-definition.ts @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { StepfunctionExternalDefinitionStack } from '../lib/stepfunction-external-definition-stack'; + +const app = new cdk.App(); +new StepfunctionExternalDefinitionStack(app, 'StepfunctionExternalDefinitionStack', { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ + + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + // env: { account: '123456789012', region: 'us-east-1' }, + + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ +}); \ No newline at end of file diff --git a/typescript/stepfunction-external-definition/cdk.json b/typescript/stepfunction-external-definition/cdk.json new file mode 100644 index 0000000000..257e669bc6 --- /dev/null +++ b/typescript/stepfunction-external-definition/cdk.json @@ -0,0 +1,78 @@ +{ + "app": "npx ts-node --prefer-ts-exts bin/stepfunction-external-definition.ts", + "watch": { + "include": [ + "**" + ], + "exclude": [ + "README.md", + "cdk*.json", + "**/*.d.ts", + "**/*.js", + "tsconfig.json", + "package*.json", + "yarn.lock", + "node_modules", + "test" + ] + }, + "context": { + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": [ + "aws", + "aws-cn" + ], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true + } +} diff --git a/typescript/stepfunction-external-definition/lib/stepfunction-external-definition-stack.ts b/typescript/stepfunction-external-definition/lib/stepfunction-external-definition-stack.ts new file mode 100644 index 0000000000..8daf0da0dc --- /dev/null +++ b/typescript/stepfunction-external-definition/lib/stepfunction-external-definition-stack.ts @@ -0,0 +1,38 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as apigateway from "aws-cdk-lib/aws-apigateway"; +import * as stepfunctions from "aws-cdk-lib/aws-stepfunctions"; +import * as logs from 'aws-cdk-lib/aws-logs'; +import * as iam from "aws-cdk-lib/aws-iam"; +import * as fs from "fs"; + +export class StepfunctionExternalDefinitionStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + const logGroup = new logs.LogGroup(this, 'MyStateMachineLogGroup', { + logGroupName: '/aws/vendedlogs/states/MyStateMachine', + retention: logs.RetentionDays.ONE_WEEK, + }); + + const workflow = fs.readFileSync('./workflow/stepfunction.json.asl', 'utf8'); + const stateMachine= new stepfunctions.StateMachine(this, 'MyStateMachine', { + stateMachineType: stepfunctions.StateMachineType.EXPRESS, + definitionBody: stepfunctions.DefinitionBody.fromString(workflow.toString()), + logs: { + destination: logGroup, + level: stepfunctions.LogLevel.ALL, + includeExecutionData: true, + }, + }); + logGroup.grantWrite(stateMachine.role); + + const api = new apigateway.RestApi(this, "StepFuncApi", { + restApiName: "StepFuncApi", + description: "StepFuncApi", + endpointTypes: [apigateway.EndpointType.REGIONAL] + }); + const resource = api.root.addResource("orders"); + resource.addMethod("GET", apigateway.StepFunctionsIntegration.startExecution(stateMachine)); + } +} diff --git a/typescript/stepfunction-external-definition/package.json b/typescript/stepfunction-external-definition/package.json new file mode 100644 index 0000000000..8bffbf265a --- /dev/null +++ b/typescript/stepfunction-external-definition/package.json @@ -0,0 +1,23 @@ +{ + "name": "stepfunction-external-definition", + "version": "0.1.0", + "bin": { + "stepfunction-external-definition": "bin/stepfunction-external-definition.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "cdk": "cdk" + }, + "devDependencies": { + "@types/node": "22.5.4", + "aws-cdk": "2.162.1", + "ts-node": "^10.9.2", + "typescript": "~5.6.2" + }, + "dependencies": { + "aws-cdk-lib": "2.162.1", + "constructs": "^10.0.0", + "source-map-support": "^0.5.21" + } +} diff --git a/typescript/stepfunction-external-definition/tsconfig.json b/typescript/stepfunction-external-definition/tsconfig.json new file mode 100644 index 0000000000..aaa7dc510f --- /dev/null +++ b/typescript/stepfunction-external-definition/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "commonjs", + "lib": [ + "es2020", + "dom" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/typescript/stepfunction-external-definition/workflow/stepfunction.json.asl b/typescript/stepfunction-external-definition/workflow/stepfunction.json.asl new file mode 100644 index 0000000000..29fd3b5aa7 --- /dev/null +++ b/typescript/stepfunction-external-definition/workflow/stepfunction.json.asl @@ -0,0 +1,83 @@ +{ + "Comment": "smiple workflow for the car company", + "StartAt": "Order Received", + "States": { + "Order Received": { + "Comment": "Order revieved.", + "Type": "Pass", + "Next": "RiskyCustomer" + }, + "RiskyCustomer": { + "Comment": "No risky business!", + "Type": "Choice", + "Choices": [ + { + "Variable": "$.risk", + "BooleanEquals": true, + "Next": "Yes" + }, + { + "Variable": "$.risk", + "BooleanEquals": false, + "Next": "No" + } + ], + "Default": "Yes" + }, + "Yes": { + "Type": "Pass", + "Next": "organize pickup" + }, + "No": { + "Type": "Fail", + "Cause": "Not Hello World" + }, + "organize pickup": { + "Comment": "wait for client to pickup", + "Type": "Wait", + "Seconds": 3, + "Next": "wait for 6 months" + }, + "wait for 6 months": { + "Comment": "wait for 6 months", + "Type": "Wait", + "Seconds": 3, + "Next": "organize return" + }, + "organize return": { + "Comment": "wait for client to return", + "Type": "Wait", + "Seconds": 3, + "Next": "Parallel State" + }, + "Parallel State": { + "Comment": "A Parallel state can be used to create parallel branches of execution in your state machine.", + "Type": "Parallel", + "Next": "return", + "Branches": [ + { + "StartAt": "book", + "States": { + "book": { + "Type": "Pass", + "End": true + } + } + }, + { + "StartAt": "calculate damage", + "States": { + "calculate damage": { + "Type": "Pass", + "End": true + } + } + } + ] + }, + "return": { + "Type": "Pass", + "End": true + } + } + } \ No newline at end of file