in Lab6-StepFunction/cdk-deployment/lib/lab6-stack.ts [14:179]
constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const stack = cdk.Stack.of(this);
// create SNS topic
const topic = new sns.Topic(this, 'Lottery-Notification', {
displayName: `Lottery`
});
const notifyEmail = this.node.tryGetContext('notifyEmail');
if (notifyEmail) {
topic.addSubscription(new sns_sub.EmailSubscription(notifyEmail));
}
// create Dynamodb Tables
const lotteryWinnersTable = new dynamodb.Table(this, 'Lottery-Winners', {
tableName: 'Lottery-Winners',
partitionKey: { name: 'employee_id', type: dynamodb.AttributeType.STRING },
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
const lotteryEmployeeTable = new dynamodb.Table(this, 'Lottery-Employee', {
tableName: 'Lottery-Employee',
partitionKey: { name: 'employee_id', type: dynamodb.AttributeType.STRING },
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
lotteryEmployeeTable.addGlobalSecondaryIndex({
indexName: 'lottery_serial-index',
partitionKey: { name: 'lottery_serial', type: dynamodb.AttributeType.NUMBER },
});
const employeesJson = new s3_assets.Asset(this, 'EmployeeMockData', {
path: path.join(__dirname, '../../request-items.json'),
});
const loadMockDataHandler = new lambda_python.PythonFunction(this, 'DynamoDBLoadMockData', {
entry: path.join(__dirname, '../dynamodb-load-data'),
index: 'index.py',
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'handler',
timeout: cdk.Duration.minutes(3),
});
// workaround for new synthesis toolkit
loadMockDataHandler.addToRolePolicy(new iam.PolicyStatement({
actions: ['kms:Decrypt', 'kms:DescribeKey'],
effect: iam.Effect.ALLOW,
resources: ['*'],
}));
employeesJson.grantRead(loadMockDataHandler);
lotteryEmployeeTable.grantWriteData(loadMockDataHandler);
const customDynamoDBLoading = new cdk.CustomResource(this, 'CustomResource', {
serviceToken: loadMockDataHandler.functionArn,
resourceType: 'Custom::DynamodbLoadData',
properties: {
BucketName: employeesJson.s3BucketName,
ObjectKey: employeesJson.s3ObjectKey,
},
});
customDynamoDBLoading.node.addDependency(lotteryEmployeeTable);
// create step Lambda functions
const lambdaCodeAsset = lambda.Code.fromAsset(path.join(__dirname, '../../'), {
exclude: [ 'cdk-deployment', 'docs', '*.json', '*.md' ],
});
const lotteryInputWinners = new lambda.Function(this, 'Lottery-InputWinners', {
code: lambdaCodeAsset,
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'Lottery-InputWinners.lambda_handler',
timeout: cdk.Duration.seconds(10),
});
const lotteryRandomSelectWinners = new lambda.Function(this, 'Lottery-RandomSelectWinners', {
code: lambdaCodeAsset,
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'Lottery-RandomSelectWinners.lambda_handler',
timeout: cdk.Duration.minutes(2),
});
lotteryEmployeeTable.grantReadData(lotteryRandomSelectWinners);
const lotteryValidateWinners = new lambda.Function(this, 'Lottery-ValidateWinners', {
code: lambdaCodeAsset,
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'Lottery-ValidateWinners.lambda_handler',
timeout: cdk.Duration.minutes(2),
});
lotteryWinnersTable.grantReadData(lotteryValidateWinners);
const lotteryRecordWinners = new lambda.Function(this, 'Lottery-RecordWinners', {
code: lambdaCodeAsset,
runtime: lambda.Runtime.PYTHON_3_8,
handler: 'Lottery-RecordWinners.lambda_handler',
timeout: cdk.Duration.minutes(2),
});
lotteryWinnersTable.grantWriteData(lotteryRecordWinners);
// create state of step function
const failure = new sfn.Fail(this, 'Fail', {
});
const inputLotteryWinners = new tasks.LambdaInvoke(this, 'Input Lottery Winners', {
lambdaFunction: lotteryInputWinners,
resultPath: '$',
payloadResponseOnly: true,
}).addCatch(failure, {
errors: [ 'CustomError' ]
}).addCatch(failure, {
errors: [ 'States.ALL' ]
});
const randomSelectWinners = new tasks.LambdaInvoke(this, 'Random Select Winners', {
lambdaFunction: lotteryRandomSelectWinners,
inputPath: '$.body',
payloadResponseOnly: true,
}).addRetry({
maxAttempts: 2,
interval: cdk.Duration.seconds(1),
errors: [ 'States.ALL' ]
}).addCatch(failure, {
errors: [ 'States.ALL' ]
});
const validateWinners = new tasks.LambdaInvoke(this, 'Validate Winners', {
lambdaFunction: lotteryValidateWinners,
inputPath: '$.body',
payloadResponseOnly: true,
}).addRetry({
maxAttempts: 2,
interval: cdk.Duration.seconds(1),
errors: [ 'States.ALL' ]
}).addCatch(failure, {
errors: [ 'States.ALL' ]
});
const notifyWinners = new tasks.SnsPublish(this, 'Notify Winners', {
topic,
message: sfn.TaskInput.fromDataAt('$.sns'),
});
const recordWinners = new tasks.LambdaInvoke(this, 'Record Winner Queue', {
lambdaFunction: lotteryRecordWinners,
inputPath: '$.body',
payloadResponseOnly: true,
timeout: cdk.Duration.seconds(300),
});
const notifyAndRecordParrallel = new sfn.Parallel(this, 'Send SNS and Record In Dynamodb')
.branch(notifyWinners)
.branch(recordWinners)
.addRetry({
maxAttempts: 2,
interval: cdk.Duration.seconds(1),
errors: [ 'States.ALL' ]
}).addCatch(failure, {
errors: [ 'States.ALL' ]
});
const winnerChoice = new sfn.Choice(this, 'Is Winner In Past Draw');
winnerChoice.when(sfn.Condition.numberEquals('$.status', 0), notifyAndRecordParrallel);
winnerChoice.when(sfn.Condition.numberEquals('$.status', 1), randomSelectWinners);
const definition = inputLotteryWinners
.next(randomSelectWinners)
.next(validateWinners)
.next(winnerChoice);
new sfn.StateMachine(this, 'Lottery', {
definition,
});
}