diff --git a/the-state-machine/README.md b/the-state-machine/README.md
index 8bdcd7df..ed53d00e 100644
--- a/the-state-machine/README.md
+++ b/the-state-machine/README.md
@@ -21,3 +21,5 @@ If you pass in pineapple or hawaiian you should see the step function flow fail
* [TypeScript](typescript/)
* [Python](python/)
+ * [CSharp](csharp/)
+ * [Java](java/)
diff --git a/the-state-machine/csharp/.gitignore b/the-state-machine/csharp/.gitignore
new file mode 100644
index 00000000..f555633e
--- /dev/null
+++ b/the-state-machine/csharp/.gitignore
@@ -0,0 +1,342 @@
+# CDK asset staging directory
+.cdk.staging
+cdk.out
+
+# Created by https://www.gitignore.io/api/csharp
+
+### Csharp ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+
+# End of https://www.gitignore.io/api/csharp
\ No newline at end of file
diff --git a/the-state-machine/csharp/README.md b/the-state-machine/csharp/README.md
new file mode 100644
index 00000000..eb3acafa
--- /dev/null
+++ b/the-state-machine/csharp/README.md
@@ -0,0 +1,59 @@
+# The State Machine
+
+This is an example CDK stack to deploy The State Machine stack described by Jeremy Daly here - https://www.jeremydaly.com/serverless-microservice-patterns-for-aws/#statemachine
+
+You would use this pattern if you can do your processing asynchronously and you need to have different flows in your logic.
+
+![Architecture](../img/the-state-machine-arch.png)
+
+### Stepfunction Logic
+![Architecture](../img/statemachine.png)
+
+
+### Testing It Out
+
+After deployment you should have a proxy api gateway where any url hits a lambda which triggers a step function. You can pass in a queryparameter like '?flavour=pepperoni' or '?flavour=pineapple'.
+
+If you pass in pineapple or hawaiian you should see the step function flow fail when you check it via the console.
+
+
+# Useful commands
+
+* `dotnet build src` compile this app
+* `cdk deploy` deploy this stack to your default AWS account/region
+* `cdk diff` compare deployed stack with current state
+* `cdk synth` emits the synthesized CloudFormation template
+
+## Deploy with AWS Cloud9
+
+* Create an **Ubuntu** AWS Cloud9 EC2 development environment
+* Add the Microsoft repository
+ ```
+ wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
+ ```
+ ```
+ sudo dpkg -i packages-microsoft-prod.deb
+ ```
+* Install the .NET Core SDK
+ ```
+ sudo apt-get update; \
+ sudo apt-get install -y apt-transport-https && \
+ sudo apt-get update && \
+ sudo apt-get install -y dotnet-sdk-3.1
+ ```
+* Clone the CDK Patterns repo
+ ```
+ git clone https://github.com/cdk-patterns/serverless.git
+ ```
+* Change directory
+ ```
+ cd serverless/the-scalable-webhook/csharp
+ ```
+* Build the project to see if .NET Core has been setup correctly (optional)
+ ```
+ dotnet build src
+ ```
+* Deploy the stack
+ ```
+ cdk deploy
+ ```
diff --git a/the-state-machine/csharp/cdk.json b/the-state-machine/csharp/cdk.json
new file mode 100644
index 00000000..b5656549
--- /dev/null
+++ b/the-state-machine/csharp/cdk.json
@@ -0,0 +1,11 @@
+{
+ "app": "dotnet run -p src/TheStateMachine/TheStateMachine.csproj",
+ "context": {
+ "@aws-cdk/core:enableStackNameDuplicates": "true",
+ "aws-cdk:enableDiffNoFail": "true",
+ "@aws-cdk/core:stackRelativeExports": "true",
+ "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
+ "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
+ "@aws-cdk/aws-kms:defaultKeyPolicies": true
+ }
+}
diff --git a/the-state-machine/csharp/lambda_fns/orderPizza.js b/the-state-machine/csharp/lambda_fns/orderPizza.js
new file mode 100644
index 00000000..7dcaae3d
--- /dev/null
+++ b/the-state-machine/csharp/lambda_fns/orderPizza.js
@@ -0,0 +1,10 @@
+"use strict";
+exports.handler = async function (flavour) {
+ console.log("Requested Pizza :", JSON.stringify(flavour, undefined, 2));
+ let containsPineapple = false;
+ if (flavour == 'pineapple' || flavour == 'hawaiian') {
+ containsPineapple = true;
+ }
+ return { 'containsPineapple': containsPineapple };
+};
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXJQaXp6YS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm9yZGVyUGl6emEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sQ0FBQyxPQUFPLEdBQUcsS0FBSyxXQUFVLE9BQVc7SUFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUV4RSxJQUFJLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUU5QixJQUFHLE9BQU8sSUFBSSxXQUFXLElBQUksT0FBTyxJQUFHLFVBQVUsRUFBQztRQUM5QyxpQkFBaUIsR0FBRyxJQUFJLENBQUM7S0FDNUI7SUFFRCxPQUFPLEVBQUMsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUMsQ0FBQTtBQUNuRCxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnRzLmhhbmRsZXIgPSBhc3luYyBmdW5jdGlvbihmbGF2b3VyOmFueSkge1xyXG4gICAgY29uc29sZS5sb2coXCJSZXF1ZXN0ZWQgUGl6emEgOlwiLCBKU09OLnN0cmluZ2lmeShmbGF2b3VyLCB1bmRlZmluZWQsIDIpKTtcclxuICAgIFxyXG4gICAgbGV0IGNvbnRhaW5zUGluZWFwcGxlID0gZmFsc2U7XHJcbiAgICBcclxuICAgIGlmKGZsYXZvdXIgPT0gJ3BpbmVhcHBsZScgfHwgZmxhdm91ciA9PSdoYXdhaWlhbicpe1xyXG4gICAgICAgIGNvbnRhaW5zUGluZWFwcGxlID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4geydjb250YWluc1BpbmVhcHBsZSc6IGNvbnRhaW5zUGluZWFwcGxlfVxyXG59Il19
\ No newline at end of file
diff --git a/the-state-machine/csharp/lambda_fns/stateMachineLambda.js b/the-state-machine/csharp/lambda_fns/stateMachineLambda.js
new file mode 100644
index 00000000..dc7dfac3
--- /dev/null
+++ b/the-state-machine/csharp/lambda_fns/stateMachineLambda.js
@@ -0,0 +1,40 @@
+"use strict";
+const AWS = require('aws-sdk');
+const stepFunctions = new AWS.StepFunctions({
+ region: 'us-east-1'
+});
+module.exports.handler = (event, context, callback) => {
+ let pizzaType = 'pepperoni';
+ if (null != event.queryStringParameters) {
+ if (typeof event.queryStringParameters.flavour != 'undefined') {
+ pizzaType = event.queryStringParameters.flavour;
+ }
+ }
+ const params = {
+ stateMachineArn: process.env.statemachine_arn,
+ input: JSON.stringify({ flavour: pizzaType })
+ };
+ stepFunctions.startExecution(params, (err, data) => {
+ if (err) {
+ console.log(err);
+ const response = {
+ statusCode: 500,
+ body: JSON.stringify({
+ message: 'There was an error'
+ })
+ };
+ callback(null, response);
+ }
+ else {
+ console.log(data);
+ const response = {
+ statusCode: 200,
+ body: JSON.stringify({
+ message: 'The Pizzeria is processing your order'
+ })
+ };
+ callback(null, response);
+ }
+ });
+};
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGVNYWNoaW5lTGFtYmRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic3RhdGVNYWNoaW5lTGFtYmRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFL0IsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsYUFBYSxDQUFDO0lBQzVDLE1BQU0sRUFBRSxXQUFXO0NBQ2xCLENBQUMsQ0FBQztBQUVILE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLENBQUMsS0FBUyxFQUFFLE9BQVcsRUFBRSxRQUFZLEVBQUUsRUFBRTtJQUM5RCxJQUFJLFNBQVMsR0FBRyxXQUFXLENBQUM7SUFFNUIsSUFBRyxJQUFJLElBQUksS0FBSyxDQUFDLHFCQUFxQixFQUFDO1FBQ25DLElBQUcsT0FBTyxLQUFLLENBQUMscUJBQXFCLENBQUMsT0FBTyxJQUFJLFdBQVcsRUFBRTtZQUMxRCxTQUFTLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQztTQUNuRDtLQUNKO0lBRUQsTUFBTSxNQUFNLEdBQUc7UUFDWCxlQUFlLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0I7UUFDN0MsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxPQUFPLEVBQUMsU0FBUyxFQUFDLENBQUM7S0FDN0MsQ0FBQztJQUVGLGFBQWEsQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBTyxFQUFFLElBQVEsRUFBRSxFQUFFO1FBQ3ZELElBQUksR0FBRyxFQUFFO1lBQ1QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNqQixNQUFNLFFBQVEsR0FBRztnQkFDYixVQUFVLEVBQUUsR0FBRztnQkFDZixJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztvQkFDckIsT0FBTyxFQUFFLG9CQUFvQjtpQkFDNUIsQ0FBQzthQUNMLENBQUM7WUFDRixRQUFRLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1NBQ3hCO2FBQU07WUFDUCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2xCLE1BQU0sUUFBUSxHQUFHO2dCQUNiLFVBQVUsRUFBRSxHQUFHO2dCQUNmLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO29CQUNyQixPQUFPLEVBQUUsdUNBQXVDO2lCQUMvQyxDQUFDO2FBQ0wsQ0FBQztZQUNGLFFBQVEsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7U0FDeEI7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IEFXUyA9IHJlcXVpcmUoJ2F3cy1zZGsnKTtcclxuXHJcbmNvbnN0IHN0ZXBGdW5jdGlvbnMgPSBuZXcgQVdTLlN0ZXBGdW5jdGlvbnMoe1xyXG5yZWdpb246ICd1cy1lYXN0LTEnXHJcbn0pO1xyXG5cclxubW9kdWxlLmV4cG9ydHMuaGFuZGxlciA9IChldmVudDphbnksIGNvbnRleHQ6YW55LCBjYWxsYmFjazphbnkpID0+IHtcclxuICAgIGxldCBwaXp6YVR5cGUgPSAncGVwcGVyb25pJztcclxuICAgIFxyXG4gICAgaWYobnVsbCAhPSBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMpe1xyXG4gICAgICAgIGlmKHR5cGVvZiBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMuZmxhdm91ciAhPSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgICAgICBwaXp6YVR5cGUgPSBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMuZmxhdm91cjtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGNvbnN0IHBhcmFtcyA9IHtcclxuICAgICAgICBzdGF0ZU1hY2hpbmVBcm46IHByb2Nlc3MuZW52LnN0YXRlbWFjaGluZV9hcm4sXHJcbiAgICAgICAgaW5wdXQ6IEpTT04uc3RyaW5naWZ5KHtmbGF2b3VyOnBpenphVHlwZX0pXHJcbiAgICB9O1xyXG4gICAgXHJcbiAgICBzdGVwRnVuY3Rpb25zLnN0YXJ0RXhlY3V0aW9uKHBhcmFtcywgKGVycjphbnksIGRhdGE6YW55KSA9PiB7XHJcbiAgICAgICAgaWYgKGVycikge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKGVycik7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IDUwMCxcclxuICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICBtZXNzYWdlOiAnVGhlcmUgd2FzIGFuIGVycm9yJ1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH07XHJcbiAgICAgICAgY2FsbGJhY2sobnVsbCwgcmVzcG9uc2UpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coZGF0YSk7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICBtZXNzYWdlOiAnVGhlIFBpenplcmlhIGlzIHByb2Nlc3NpbmcgeW91ciBvcmRlcidcclxuICAgICAgICAgICAgfSlcclxuICAgICAgICB9O1xyXG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHJlc3BvbnNlKTtcclxuICAgICAgICB9XHJcbiAgICB9KTtcclxufTsiXX0=
\ No newline at end of file
diff --git a/the-state-machine/csharp/src/TheStateMachine.sln b/the-state-machine/csharp/src/TheStateMachine.sln
new file mode 100644
index 00000000..b1aea04a
--- /dev/null
+++ b/the-state-machine/csharp/src/TheStateMachine.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26124.0
+MinimumVisualStudioVersion = 15.0.26124.0
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TheStateMachine", "TheStateMachine\TheStateMachine.csproj", "{1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|x64.Build.0 = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Debug|x86.Build.0 = Debug|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|x64.ActiveCfg = Release|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|x64.Build.0 = Release|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|x86.ActiveCfg = Release|Any CPU
+ {1E7C2FF2-7B2D-4AD8-BC69-91F0BB4EA161}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/the-state-machine/csharp/src/TheStateMachine/GlobalSuppressions.cs b/the-state-machine/csharp/src/TheStateMachine/GlobalSuppressions.cs
new file mode 100644
index 00000000..26233fcb
--- /dev/null
+++ b/the-state-machine/csharp/src/TheStateMachine/GlobalSuppressions.cs
@@ -0,0 +1 @@
+[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Potential Code Quality Issues", "RECS0026:Possible unassigned object created by 'new'", Justification = "Constructs add themselves to the scope in which they are created")]
diff --git a/the-state-machine/csharp/src/TheStateMachine/Program.cs b/the-state-machine/csharp/src/TheStateMachine/Program.cs
new file mode 100644
index 00000000..5a7a8d3a
--- /dev/null
+++ b/the-state-machine/csharp/src/TheStateMachine/Program.cs
@@ -0,0 +1,18 @@
+using Amazon.CDK;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace TheStateMachine
+{
+ sealed class Program
+ {
+
+ public static void Main(string[] args)
+ {
+ var app = new App();
+ new TheStateMachineStack(app, "TheStateMachineStack");
+ app.Synth();
+ }
+ }
+}
diff --git a/the-state-machine/csharp/src/TheStateMachine/TheStateMachine.csproj b/the-state-machine/csharp/src/TheStateMachine/TheStateMachine.csproj
new file mode 100644
index 00000000..41af1592
--- /dev/null
+++ b/the-state-machine/csharp/src/TheStateMachine/TheStateMachine.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+ Major
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/the-state-machine/csharp/src/TheStateMachine/TheStateMachineStack.cs b/the-state-machine/csharp/src/TheStateMachine/TheStateMachineStack.cs
new file mode 100644
index 00000000..7cf24070
--- /dev/null
+++ b/the-state-machine/csharp/src/TheStateMachine/TheStateMachineStack.cs
@@ -0,0 +1,108 @@
+using Amazon.CDK;
+using Lambda = Amazon.CDK.AWS.Lambda;
+using SQS = Amazon.CDK.AWS.SQS;
+using APIGateway = Amazon.CDK.AWS.APIGateway;
+using StepFunction = Amazon.CDK.AWS.StepFunctions;
+using StepFuncionTasks = Amazon.CDK.AWS.StepFunctions.Tasks;
+using System.Collections.Generic;
+
+namespace TheStateMachine
+{
+ public class TheStateMachineStack : Stack
+ {
+
+ readonly private Lambda.Function _pineppaleCheckHandler;
+ readonly private Lambda.Function _stateMachineHandler;
+ readonly private StepFuncionTasks.LambdaInvoke _orderPizzaTask;
+ readonly private StepFunction.Fail _jobFailed;
+ readonly private StepFunction.Pass _cookPizza;
+ readonly private StepFunction.Chain _chainDefinition;
+ readonly private StepFunction.StateMachine _stateMachine;
+ readonly private SQS.Queue _deadeLetterQueue;
+
+ internal TheStateMachineStack(Construct scope, string id, IStackProps props = null) : base(scope, id, props)
+ {
+
+ // Step Function Starts Here
+
+ // The first thing we need to do is see if they are asking for pineapple on a pizza
+ _pineppaleCheckHandler = new Lambda.Function(this, "pineappleCheckLambdaHandler", new Lambda.FunctionProps
+ {
+ Runtime = Lambda.Runtime.NODEJS_12_X,
+ Code = Lambda.Code.FromAsset("lambda_fns"),
+ Handler = "orderPizza.handler"
+ });
+
+ /*
+ * Step functions are built up of steps, we need to define our first step
+ * This step was refactored due to Deprecated function
+ */
+ _orderPizzaTask = new StepFuncionTasks.LambdaInvoke(this, "Order Pizza Job", new StepFuncionTasks.LambdaInvokeProps
+ {
+ LambdaFunction = _pineppaleCheckHandler,
+ InputPath = "$.flavour",
+ ResultPath = "$.pineappleAnalysis",
+ PayloadResponseOnly = true
+ });
+
+ // Pizza Order failure step defined
+ _jobFailed = new StepFunction.Fail(this, "Sorry, We Dont add Pineapple", new StepFunction.FailProps
+ {
+ Cause = "Failed To Make Pizza",
+ Error = "They asked for Pineapple"
+ });
+
+ // If they didnt ask for pineapple let's cook the pizza
+ _cookPizza = new StepFunction.Pass(this, "Lets make your pizza");
+
+ // If they ask for a pizza with pineapple, fail. Otherwise cook the pizza
+ _chainDefinition = StepFunction.Chain
+ .Start(_orderPizzaTask)
+ .Next(new StepFunction.Choice(this, "With Pineapple?") // Logical choice added to flow
+ .When(StepFunction.Condition.BooleanEquals("$.pineappleAnalysis.containsPineapple", true), _jobFailed)
+ .Otherwise(_cookPizza));
+
+ // Building the state machine
+ _stateMachine = new StepFunction.StateMachine(this, "StateMachine", new StepFunction.StateMachineProps
+ {
+ Definition = _chainDefinition,
+ Timeout = Duration.Minutes(5)
+ });
+
+ /**
+ * Dead Letter Queue Setup
+ * SQS creation
+ * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html
+ */
+ _deadeLetterQueue = new SQS.Queue(this, "stateMachineLambdaDLQ", new SQS.QueueProps
+ {
+ VisibilityTimeout = Duration.Seconds(300)
+ });
+
+ // defines an AWS Lambda resource to connect to our API Gateway
+ _stateMachineHandler = new Lambda.Function(this, "stateMachineLambdaHandler", new Lambda.FunctionProps
+ {
+ Runtime = Lambda.Runtime.NODEJS_12_X,
+ Code = Lambda.Code.FromAsset("lambda_fns"),
+ Handler = "stateMachineLambda.handler",
+ DeadLetterQueue = _deadeLetterQueue,
+ Environment = new Dictionary
+ {
+ { "statemachine_arn", _stateMachine.StateMachineArn }
+ }
+ });
+
+ // Grants to state machine execution
+ _stateMachine.GrantStartExecution(_stateMachineHandler);
+
+ /*
+ * Simple API Gateway proxy integration
+ */
+ // defines an API Gateway REST API resource backed by our "sqs_publish_lambda" function.
+ new APIGateway.LambdaRestApi(this, "Endpoint", new APIGateway.LambdaRestApiProps
+ {
+ Handler = _stateMachineHandler
+ });
+ }
+ }
+}
diff --git a/the-state-machine/java/.gitignore b/the-state-machine/java/.gitignore
new file mode 100644
index 00000000..1db21f16
--- /dev/null
+++ b/the-state-machine/java/.gitignore
@@ -0,0 +1,13 @@
+.classpath.txt
+target
+.classpath
+.project
+.idea
+.settings
+.vscode
+*.iml
+
+# CDK asset staging directory
+.cdk.staging
+cdk.out
+
diff --git a/the-state-machine/java/README.md b/the-state-machine/java/README.md
new file mode 100644
index 00000000..2785b2bc
--- /dev/null
+++ b/the-state-machine/java/README.md
@@ -0,0 +1,28 @@
+# The State Machine
+
+This is an example CDK stack to deploy The State Machine stack described by Jeremy Daly here - https://www.jeremydaly.com/serverless-microservice-patterns-for-aws/#statemachine
+
+You would use this pattern if you can do your processing asynchronously and you need to have different flows in your logic.
+
+![Architecture](../img/the-state-machine-arch.png)
+
+### Stepfunction Logic
+![Architecture](../img/statemachine.png)
+
+
+### Testing It Out
+
+After deployment you should have a proxy api gateway where any url hits a lambda which triggers a step function. You can pass in a queryparameter like '?flavour=pepperoni' or '?flavour=pineapple'.
+
+If you pass in pineapple or hawaiian you should see the step function flow fail when you check it via the console.
+
+
+
+## Useful commands
+
+ * `mvn package` compile and run tests
+ * `cdk ls` list all stacks in the app
+ * `cdk synth` emits the synthesized CloudFormation template
+ * `cdk deploy` deploy this stack to your default AWS account/region
+ * `cdk diff` compare deployed stack with current state
+ * `cdk docs` open CDK documentation
\ No newline at end of file
diff --git a/the-state-machine/java/cdk.json b/the-state-machine/java/cdk.json
new file mode 100644
index 00000000..3b9f54cc
--- /dev/null
+++ b/the-state-machine/java/cdk.json
@@ -0,0 +1,11 @@
+{
+ "app": "mvn -e -q compile exec:java",
+ "context": {
+ "@aws-cdk/core:enableStackNameDuplicates": "true",
+ "aws-cdk:enableDiffNoFail": "true",
+ "@aws-cdk/core:stackRelativeExports": "true",
+ "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true,
+ "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true,
+ "@aws-cdk/aws-kms:defaultKeyPolicies": true
+ }
+}
diff --git a/the-state-machine/java/lambda_fns/orderPizza.js b/the-state-machine/java/lambda_fns/orderPizza.js
new file mode 100644
index 00000000..7dcaae3d
--- /dev/null
+++ b/the-state-machine/java/lambda_fns/orderPizza.js
@@ -0,0 +1,10 @@
+"use strict";
+exports.handler = async function (flavour) {
+ console.log("Requested Pizza :", JSON.stringify(flavour, undefined, 2));
+ let containsPineapple = false;
+ if (flavour == 'pineapple' || flavour == 'hawaiian') {
+ containsPineapple = true;
+ }
+ return { 'containsPineapple': containsPineapple };
+};
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JkZXJQaXp6YS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIm9yZGVyUGl6emEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sQ0FBQyxPQUFPLEdBQUcsS0FBSyxXQUFVLE9BQVc7SUFDeEMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUV4RSxJQUFJLGlCQUFpQixHQUFHLEtBQUssQ0FBQztJQUU5QixJQUFHLE9BQU8sSUFBSSxXQUFXLElBQUksT0FBTyxJQUFHLFVBQVUsRUFBQztRQUM5QyxpQkFBaUIsR0FBRyxJQUFJLENBQUM7S0FDNUI7SUFFRCxPQUFPLEVBQUMsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUMsQ0FBQTtBQUNuRCxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJleHBvcnRzLmhhbmRsZXIgPSBhc3luYyBmdW5jdGlvbihmbGF2b3VyOmFueSkge1xyXG4gICAgY29uc29sZS5sb2coXCJSZXF1ZXN0ZWQgUGl6emEgOlwiLCBKU09OLnN0cmluZ2lmeShmbGF2b3VyLCB1bmRlZmluZWQsIDIpKTtcclxuICAgIFxyXG4gICAgbGV0IGNvbnRhaW5zUGluZWFwcGxlID0gZmFsc2U7XHJcbiAgICBcclxuICAgIGlmKGZsYXZvdXIgPT0gJ3BpbmVhcHBsZScgfHwgZmxhdm91ciA9PSdoYXdhaWlhbicpe1xyXG4gICAgICAgIGNvbnRhaW5zUGluZWFwcGxlID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICByZXR1cm4geydjb250YWluc1BpbmVhcHBsZSc6IGNvbnRhaW5zUGluZWFwcGxlfVxyXG59Il19
\ No newline at end of file
diff --git a/the-state-machine/java/lambda_fns/stateMachineLambda.js b/the-state-machine/java/lambda_fns/stateMachineLambda.js
new file mode 100644
index 00000000..dc7dfac3
--- /dev/null
+++ b/the-state-machine/java/lambda_fns/stateMachineLambda.js
@@ -0,0 +1,40 @@
+"use strict";
+const AWS = require('aws-sdk');
+const stepFunctions = new AWS.StepFunctions({
+ region: 'us-east-1'
+});
+module.exports.handler = (event, context, callback) => {
+ let pizzaType = 'pepperoni';
+ if (null != event.queryStringParameters) {
+ if (typeof event.queryStringParameters.flavour != 'undefined') {
+ pizzaType = event.queryStringParameters.flavour;
+ }
+ }
+ const params = {
+ stateMachineArn: process.env.statemachine_arn,
+ input: JSON.stringify({ flavour: pizzaType })
+ };
+ stepFunctions.startExecution(params, (err, data) => {
+ if (err) {
+ console.log(err);
+ const response = {
+ statusCode: 500,
+ body: JSON.stringify({
+ message: 'There was an error'
+ })
+ };
+ callback(null, response);
+ }
+ else {
+ console.log(data);
+ const response = {
+ statusCode: 200,
+ body: JSON.stringify({
+ message: 'The Pizzeria is processing your order'
+ })
+ };
+ callback(null, response);
+ }
+ });
+};
+//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdGVNYWNoaW5lTGFtYmRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsic3RhdGVNYWNoaW5lTGFtYmRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxNQUFNLEdBQUcsR0FBRyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUM7QUFFL0IsTUFBTSxhQUFhLEdBQUcsSUFBSSxHQUFHLENBQUMsYUFBYSxDQUFDO0lBQzVDLE1BQU0sRUFBRSxXQUFXO0NBQ2xCLENBQUMsQ0FBQztBQUVILE1BQU0sQ0FBQyxPQUFPLENBQUMsT0FBTyxHQUFHLENBQUMsS0FBUyxFQUFFLE9BQVcsRUFBRSxRQUFZLEVBQUUsRUFBRTtJQUM5RCxJQUFJLFNBQVMsR0FBRyxXQUFXLENBQUM7SUFFNUIsSUFBRyxJQUFJLElBQUksS0FBSyxDQUFDLHFCQUFxQixFQUFDO1FBQ25DLElBQUcsT0FBTyxLQUFLLENBQUMscUJBQXFCLENBQUMsT0FBTyxJQUFJLFdBQVcsRUFBRTtZQUMxRCxTQUFTLEdBQUcsS0FBSyxDQUFDLHFCQUFxQixDQUFDLE9BQU8sQ0FBQztTQUNuRDtLQUNKO0lBRUQsTUFBTSxNQUFNLEdBQUc7UUFDWCxlQUFlLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0I7UUFDN0MsS0FBSyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxPQUFPLEVBQUMsU0FBUyxFQUFDLENBQUM7S0FDN0MsQ0FBQztJQUVGLGFBQWEsQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBTyxFQUFFLElBQVEsRUFBRSxFQUFFO1FBQ3ZELElBQUksR0FBRyxFQUFFO1lBQ1QsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztZQUNqQixNQUFNLFFBQVEsR0FBRztnQkFDYixVQUFVLEVBQUUsR0FBRztnQkFDZixJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztvQkFDckIsT0FBTyxFQUFFLG9CQUFvQjtpQkFDNUIsQ0FBQzthQUNMLENBQUM7WUFDRixRQUFRLENBQUMsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO1NBQ3hCO2FBQU07WUFDUCxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ2xCLE1BQU0sUUFBUSxHQUFHO2dCQUNiLFVBQVUsRUFBRSxHQUFHO2dCQUNmLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO29CQUNyQixPQUFPLEVBQUUsdUNBQXVDO2lCQUMvQyxDQUFDO2FBQ0wsQ0FBQztZQUNGLFFBQVEsQ0FBQyxJQUFJLEVBQUUsUUFBUSxDQUFDLENBQUM7U0FDeEI7SUFDTCxDQUFDLENBQUMsQ0FBQztBQUNQLENBQUMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IEFXUyA9IHJlcXVpcmUoJ2F3cy1zZGsnKTtcclxuXHJcbmNvbnN0IHN0ZXBGdW5jdGlvbnMgPSBuZXcgQVdTLlN0ZXBGdW5jdGlvbnMoe1xyXG5yZWdpb246ICd1cy1lYXN0LTEnXHJcbn0pO1xyXG5cclxubW9kdWxlLmV4cG9ydHMuaGFuZGxlciA9IChldmVudDphbnksIGNvbnRleHQ6YW55LCBjYWxsYmFjazphbnkpID0+IHtcclxuICAgIGxldCBwaXp6YVR5cGUgPSAncGVwcGVyb25pJztcclxuICAgIFxyXG4gICAgaWYobnVsbCAhPSBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMpe1xyXG4gICAgICAgIGlmKHR5cGVvZiBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMuZmxhdm91ciAhPSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgICAgICBwaXp6YVR5cGUgPSBldmVudC5xdWVyeVN0cmluZ1BhcmFtZXRlcnMuZmxhdm91cjtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGNvbnN0IHBhcmFtcyA9IHtcclxuICAgICAgICBzdGF0ZU1hY2hpbmVBcm46IHByb2Nlc3MuZW52LnN0YXRlbWFjaGluZV9hcm4sXHJcbiAgICAgICAgaW5wdXQ6IEpTT04uc3RyaW5naWZ5KHtmbGF2b3VyOnBpenphVHlwZX0pXHJcbiAgICB9O1xyXG4gICAgXHJcbiAgICBzdGVwRnVuY3Rpb25zLnN0YXJ0RXhlY3V0aW9uKHBhcmFtcywgKGVycjphbnksIGRhdGE6YW55KSA9PiB7XHJcbiAgICAgICAgaWYgKGVycikge1xyXG4gICAgICAgIGNvbnNvbGUubG9nKGVycik7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IDUwMCxcclxuICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICBtZXNzYWdlOiAnVGhlcmUgd2FzIGFuIGVycm9yJ1xyXG4gICAgICAgICAgICB9KVxyXG4gICAgICAgIH07XHJcbiAgICAgICAgY2FsbGJhY2sobnVsbCwgcmVzcG9uc2UpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgY29uc29sZS5sb2coZGF0YSk7XHJcbiAgICAgICAgY29uc3QgcmVzcG9uc2UgPSB7XHJcbiAgICAgICAgICAgIHN0YXR1c0NvZGU6IDIwMCxcclxuICAgICAgICAgICAgYm9keTogSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICBtZXNzYWdlOiAnVGhlIFBpenplcmlhIGlzIHByb2Nlc3NpbmcgeW91ciBvcmRlcidcclxuICAgICAgICAgICAgfSlcclxuICAgICAgICB9O1xyXG4gICAgICAgIGNhbGxiYWNrKG51bGwsIHJlc3BvbnNlKTtcclxuICAgICAgICB9XHJcbiAgICB9KTtcclxufTsiXX0=
\ No newline at end of file
diff --git a/the-state-machine/java/pom.xml b/the-state-machine/java/pom.xml
new file mode 100644
index 00000000..a67ff5da
--- /dev/null
+++ b/the-state-machine/java/pom.xml
@@ -0,0 +1,91 @@
+
+
+ 4.0.0
+
+ com.cdkpatterns
+ the-state-machine
+ 0.1
+
+
+ UTF-8
+ 1.82.0
+ 5.7.0
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ 1.8
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.0.0
+
+ com.cdkpatterns.TheStateMachineApp
+
+
+
+
+
+
+
+
+ software.amazon.awscdk
+ core
+ ${cdk.version}
+
+
+ software.amazon.awscdk
+ lambda
+ ${cdk.version}
+
+
+ software.amazon.awscdk
+ sqs
+ ${cdk.version}
+
+
+ software.amazon.awscdk
+ apigateway
+ ${cdk.version}
+
+
+ software.amazon.awscdk
+ stepfunctions
+ ${cdk.version}
+
+
+ software.amazon.awscdk
+ stepfunctions-tasks
+ ${cdk.version}
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.18.1
+ test
+
+
+
diff --git a/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineApp.java b/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineApp.java
new file mode 100644
index 00000000..25688292
--- /dev/null
+++ b/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineApp.java
@@ -0,0 +1,15 @@
+package com.cdkpatterns;
+
+import software.amazon.awscdk.core.App;
+
+import java.util.Arrays;
+
+public class TheStateMachineApp {
+ public static void main(final String[] args) {
+ App app = new App();
+
+ new TheStateMachineStack(app, "TheStateMachineStack");
+
+ app.synth();
+ }
+}
diff --git a/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineStack.java b/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineStack.java
new file mode 100644
index 00000000..27559b01
--- /dev/null
+++ b/the-state-machine/java/src/main/java/com/cdkpatterns/TheStateMachineStack.java
@@ -0,0 +1,108 @@
+package com.cdkpatterns;
+
+import java.util.Map;
+
+import software.amazon.awscdk.core.Construct;
+import software.amazon.awscdk.core.Duration;
+import software.amazon.awscdk.core.Stack;
+import software.amazon.awscdk.core.StackProps;
+import software.amazon.awscdk.services.lambda.Code;
+import software.amazon.awscdk.services.lambda.Function;
+import software.amazon.awscdk.services.lambda.Runtime;
+import software.amazon.awscdk.services.stepfunctions.tasks.LambdaInvoke;
+import software.amazon.awscdk.services.stepfunctions.Fail;
+import software.amazon.awscdk.services.stepfunctions.Pass;
+import software.amazon.awscdk.services.stepfunctions.Chain;
+import software.amazon.awscdk.services.stepfunctions.Choice;
+import software.amazon.awscdk.services.stepfunctions.Condition;
+import software.amazon.awscdk.services.stepfunctions.StateMachine;
+import software.amazon.awscdk.services.sqs.Queue;
+import software.amazon.awscdk.services.apigateway.LambdaRestApi;
+
+public class TheStateMachineStack extends Stack {
+ public TheStateMachineStack(final Construct scope, final String id) {
+ this(scope, id, null);
+ }
+
+ public TheStateMachineStack(final Construct scope, final String id, final StackProps props) {
+ super(scope, id, props);
+
+ // Step Function Starts Here
+
+ // The first thing we need to do is see if they are asking for pineapple on a pizza
+ Function pineappleCheckHandler = Function.Builder.create(this, "pineappleCheckLambdaHandler")
+ .runtime(Runtime.NODEJS_12_X) // execution environment
+ .code(Code.fromAsset("lambda_fns")) // code loaded from the "lambda_fns" directory
+ .handler("orderPizza.handler") // file is "orderPizza", function is "handler"
+ .build();
+
+ /*
+ * Step functions are built up of steps, we need to define our first step
+ * This step was refactored due to Deprecated function
+ */
+ LambdaInvoke orderPizzaTask = LambdaInvoke.Builder.create(this, "Order Pizza Job")
+ .lambdaFunction(pineappleCheckHandler)
+ .inputPath("$.flavour")
+ .resultPath("$.pineappleAnalysis")
+ .payloadResponseOnly(true)
+ .build();
+
+ // Pizza Order failure step defined
+ Fail jobFailed = Fail.Builder.create(this, "Sorry, We Dont add Pineapple")
+ .cause("Failed To Make Pizza")
+ .error("They asked for Pineapple")
+ .build();
+
+ // If they didn't ask for pineapple let's cook the pizza
+ Pass cookPizza = Pass.Builder.create(this, "Lets make your pizza")
+ .build();
+
+
+ // If they ask for a pizza with pineapple, fail. Otherwise cook the pizza
+ Chain chainDefinition = Chain
+ .start(orderPizzaTask)
+ .next(Choice.Builder.create(this, "With Pineapple?") // Logical choice added to flow
+ .build()
+ // Look at the "status" field
+ .when(Condition.booleanEquals("$.pineappleAnalysis.containsPineapple", true), jobFailed) // Fail for pineapple
+ .otherwise(cookPizza));
+
+ // Building the state machine
+ StateMachine stateMachine = StateMachine.Builder.create(this, "StateMachine")
+ .definition(chainDefinition)
+ .timeout(Duration.minutes(5))
+ .build();
+
+ /*
+ * Dead Letter Queue Setup
+ * SQS creation
+ * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html
+ */
+ Queue deadLetterQueue = Queue.Builder.create(this, "stateMachineLambdaDLQ")
+ .visibilityTimeout(Duration.seconds(300))
+ .build();
+
+ // defines an AWS Lambda resource to connect to our API Gateway
+ Function stateMachineHandler = Function.Builder.create(this, "stateMachineLambdaHandler")
+ .runtime(Runtime.NODEJS_12_X) // execution environment
+ .code(Code.fromAsset("lambda_fns")) // code loaded from the "lambda_fns" directory
+ .handler("stateMachineLambda.handler") // file is "stateMachineLambda", function is "handler
+ .deadLetterQueue(deadLetterQueue)
+ .environment(Map.of(
+ "statemachine_arn", stateMachine.getStateMachineArn()
+ ))
+ .build();
+
+
+ // Grants to state machine execution
+ stateMachine.grantStartExecution(stateMachineHandler);
+
+ /*
+ * Simple API Gateway proxy integration
+ */
+ // defines an API Gateway REST API resource backed by our "sqs_publish_lambda" function.
+ LambdaRestApi.Builder.create(this, "Endpoint")
+ .handler(stateMachineHandler)
+ .build();
+ }
+}
diff --git a/the-state-machine/python/the_state_machine/the_state_machine_stack.py b/the-state-machine/python/the_state_machine/the_state_machine_stack.py
index 95d803f6..22ce5285 100644
--- a/the-state-machine/python/the_state_machine/the_state_machine_stack.py
+++ b/the-state-machine/python/the_state_machine/the_state_machine_stack.py
@@ -23,11 +23,11 @@ def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
)
# Step functions are built up of steps, we need to define our first step
- order_pizza = step_fn.Task(self, 'Order Pizza Job',
- task=step_fn_tasks.InvokeFunction(pineapple_check_lambda),
- input_path='$.flavour',
- result_path='$.pineappleAnalysis'
- )
+ order_pizza = step_fn_tasks.LambdaInvoke(self, 'Order Pizza Job',
+ lambda_function=pineapple_check_lambda,
+ input_path='$.flavour',
+ result_path='$.pineappleAnalysis',
+ payload_response_only=True)
# Pizza Order failure step defined
job_failed = step_fn.Fail(self, 'Sorry, We Dont add Pineapple',
@@ -47,6 +47,7 @@ def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
state_machine = step_fn.StateMachine(self, 'StateMachine', definition=definition, timeout=core.Duration.minutes(5))
# Dead Letter Queue Setup
+ # https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html
dlq = sqs.Queue(self, 'stateMachineLambdaDLQ', visibility_timeout=core.Duration.seconds(300))
# defines an AWS Lambda resource to connect to our API Gateway
@@ -61,7 +62,8 @@ def __init__(self, scope: core.Construct, id: str, **kwargs) -> None:
state_machine.grant_start_execution(state_machine_lambda)
- # defines an API Gateway REST API resource backed by our "sqs_publish_lambda" function.
+ # Simple API Gateway proxy integration
+ # defines an API Gateway REST API resource backed by our "state_machine_lambda" function.
api_gw.LambdaRestApi(self, 'Endpoint',
handler=state_machine_lambda
- )
\ No newline at end of file
+ )
diff --git a/the-state-machine/typescript/lib/the-state-machine-stack.ts b/the-state-machine/typescript/lib/the-state-machine-stack.ts
index 2cfa616b..57765991 100644
--- a/the-state-machine/typescript/lib/the-state-machine-stack.ts
+++ b/the-state-machine/typescript/lib/the-state-machine-stack.ts
@@ -15,17 +15,18 @@ export class TheStateMachineStack extends cdk.Stack {
//The first thing we need to do is see if they are asking for pineapple on a pizza
let pineappleCheckLambda = new lambda.Function(this, 'pineappleCheckLambdaHandler', {
- runtime: lambda.Runtime.NODEJS_12_X, // execution environment
- code: lambda.Code.fromAsset('lambda-fns'), // code loaded from the "lambda" directory
- handler: 'orderPizza.handler' // file is "orderPizza", function is "handler"
+ runtime: lambda.Runtime.NODEJS_12_X,
+ code: lambda.Code.fromAsset('lambda-fns'),
+ handler: 'orderPizza.handler'
});
// Step functions are built up of steps, we need to define our first step
- const orderPizza = new sfn.Task(this, 'Order Pizza Job', {
- task: new tasks.InvokeFunction(pineappleCheckLambda),
+ const orderPizza = new tasks.LambdaInvoke(this, "Order Pizza Job", {
+ lambdaFunction: pineappleCheckLambda,
inputPath: '$.flavour',
resultPath: '$.pineappleAnalysis',
- });
+ payloadResponseOnly: true
+ })
// Pizza Order failure step defined
const jobFailed = new sfn.Fail(this, 'Sorry, We Dont add Pineapple', {
@@ -52,6 +53,7 @@ export class TheStateMachineStack extends cdk.Stack {
/**
* Dead Letter Queue Setup
* SQS creation
+ * https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-dead-letter-queues.html
*/
const dlq = new sqs.Queue(this, 'stateMachineLambdaDLQ', {
visibilityTimeout: cdk.Duration.seconds(300)
@@ -59,9 +61,9 @@ export class TheStateMachineStack extends cdk.Stack {
// defines an AWS Lambda resource to connect to our API Gateway
const stateMachineLambda = new lambda.Function(this, 'stateMachineLambdaHandler', {
- runtime: lambda.Runtime.NODEJS_12_X, // execution environment
- code: lambda.Code.fromAsset('lambda-fns'), // code loaded from the "lambda" directory
- handler: 'stateMachineLambda.handler', // file is "lambda", function is "handler
+ runtime: lambda.Runtime.NODEJS_12_X,
+ code: lambda.Code.fromAsset('lambda-fns'),
+ handler: 'stateMachineLambda.handler',
deadLetterQueue:dlq,
environment: {
statemachine_arn: stateMachine.stateMachineArn
diff --git a/the-state-machine/typescript/test/the-state-machine.test.ts b/the-state-machine/typescript/test/the-state-machine.test.ts
index 8969f52a..e4910ca7 100644
--- a/the-state-machine/typescript/test/the-state-machine.test.ts
+++ b/the-state-machine/typescript/test/the-state-machine.test.ts
@@ -13,6 +13,7 @@ test('API Gateway Proxy Created', () => {
));
});
+
test('State Machine Created', () => {
const app = new cdk.App();
// WHEN
@@ -23,10 +24,10 @@ test('State Machine Created', () => {
"Fn::Join": [
"",
[
- "{\"StartAt\":\"Order Pizza Job\",\"States\":{\"Order Pizza Job\":{\"Next\":\"With Pineapple?\",\"InputPath\":\"$.flavour\",\"Type\":\"Task\",\"Resource\":\"",
+ "{\"StartAt\":\"Order Pizza Job\",\"States\":{\"Order Pizza Job\":{\"Next\":\"With Pineapple?\",\"Retry\":[{\"ErrorEquals\":[\"Lambda.ServiceException\",\"Lambda.AWSLambdaException\",\"Lambda.SdkClientException\"],\"IntervalSeconds\":2,\"MaxAttempts\":6,\"BackoffRate\":2}],\"Type\":\"Task\",\"InputPath\":\"$.flavour\",\"ResultPath\":\"$.pineappleAnalysis\",\"Resource\":\"",
{
},
- "\",\"ResultPath\":\"$.pineappleAnalysis\"},\"With Pineapple?\":{\"Type\":\"Choice\",\"Choices\":[{\"Variable\":\"$.pineappleAnalysis.containsPineapple\",\"BooleanEquals\":true,\"Next\":\"Sorry, We Dont add Pineapple\"}],\"Default\":\"Lets make your pizza\"},\"Lets make your pizza\":{\"Type\":\"Pass\",\"End\":true},\"Sorry, We Dont add Pineapple\":{\"Type\":\"Fail\",\"Error\":\"They asked for Pineapple\",\"Cause\":\"Failed To Make Pizza\"}},\"TimeoutSeconds\":300}"
+ "\"},\"With Pineapple?\":{\"Type\":\"Choice\",\"Choices\":[{\"Variable\":\"$.pineappleAnalysis.containsPineapple\",\"BooleanEquals\":true,\"Next\":\"Sorry, We Dont add Pineapple\"}],\"Default\":\"Lets make your pizza\"},\"Lets make your pizza\":{\"Type\":\"Pass\",\"End\":true},\"Sorry, We Dont add Pineapple\":{\"Type\":\"Fail\",\"Error\":\"They asked for Pineapple\",\"Cause\":\"Failed To Make Pizza\"}},\"TimeoutSeconds\":300}"
]
]
}