Skip to content

Commit

Permalink
feat(aws-codepipeline): support notifications on the ManualApprovalAc…
Browse files Browse the repository at this point in the history
…tion.

Fixes aws#1222
  • Loading branch information
skinny85 committed Dec 19, 2018
1 parent 478a714 commit c8c3079
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 2 deletions.
49 changes: 48 additions & 1 deletion packages/@aws-cdk/aws-codepipeline/lib/manual-approval-action.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,68 @@
import actions = require('@aws-cdk/aws-codepipeline-api');
import sns = require('@aws-cdk/aws-sns');
import cdk = require('@aws-cdk/cdk');

// tslint:disable-next-line:no-empty-interface
/**
* Construction properties of the {@link ManualApprovalAction}.
*/
export interface ManualApprovalActionProps extends actions.CommonActionProps,
actions.CommonActionConstructProps {
/**
* Optional SNS topic to send notifications to when an approval is pending.
*/
notificationTopic?: sns.TopicRef;

/**
* A list of email addresses to subscribe to notifications when this Action is pending approval.
* If this has been provided, but not `notificationTopic`,
* a new Topic will be created.
*/
notifyEmails?: string[];

/**
* Any additional information that you want to include in the notification email message.
*/
additionalInformation?: string;
}

/**
* Manual approval action.
*/
export class ManualApprovalAction extends actions.Action {
private _notificationTopic?: sns.TopicRef;

constructor(parent: cdk.Construct, name: string, props: ManualApprovalActionProps) {
super(parent, name, {
category: actions.ActionCategory.Approval,
provider: 'Manual',
artifactBounds: { minInputs: 0, maxInputs: 0, minOutputs: 0, maxOutputs: 0 },
configuration: new cdk.Token(() => this.actionConfiguration(props)),
...props,
});

const anyEmailsProvided = (props.notifyEmails || []).length > 0;
this._notificationTopic = props.notificationTopic
? props.notificationTopic
: anyEmailsProvided ? new sns.Topic(this, 'TopicResource') : undefined;

if (this.notificationTopic) {
this.notificationTopic.grantPublish(props.stage.pipeline.role);
for (const notifyEmail of props.notifyEmails || []) {
this.notificationTopic.subscribeEmail(`Subscription-${notifyEmail}`, notifyEmail);
}
}
}

public get notificationTopic(): sns.TopicRef | undefined {
return this._notificationTopic;
}

private actionConfiguration(props: ManualApprovalActionProps): any {
return this.notificationTopic
? {
NotificationArn: this.notificationTopic.topicArn,
CustomData: props.additionalInformation,
}
: undefined;
}
}
3 changes: 2 additions & 1 deletion packages/@aws-cdk/aws-codepipeline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
"@aws-cdk/aws-codedeploy": "^0.20.0",
"@aws-cdk/aws-ecr": "^0.20.0",
"@aws-cdk/aws-lambda": "^0.20.0",
"@aws-cdk/aws-sns": "^0.20.0",
"cdk-build-tools": "^0.20.0",
"cdk-integ-tools": "^0.20.0",
"cfn2ts": "^0.20.0",
Expand All @@ -76,6 +75,7 @@
"@aws-cdk/aws-events": "^0.20.0",
"@aws-cdk/aws-iam": "^0.20.0",
"@aws-cdk/aws-s3": "^0.20.0",
"@aws-cdk/aws-sns": "^0.20.0",
"@aws-cdk/cdk": "^0.20.0"
},
"homepage": "https://github.com/awslabs/aws-cdk",
Expand All @@ -84,6 +84,7 @@
"@aws-cdk/aws-events": "^0.20.0",
"@aws-cdk/aws-iam": "^0.20.0",
"@aws-cdk/aws-s3": "^0.20.0",
"@aws-cdk/aws-sns": "^0.20.0",
"@aws-cdk/cdk": "^0.20.0"
},
"engines": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
{
"Resources": {
"Bucket83908E77": {
"Type": "AWS::S3::Bucket",
"DeletionPolicy": "Retain"
},
"PipelineRoleD68726F7": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Action": "sts:AssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "codepipeline.amazonaws.com"
}
}
],
"Version": "2012-10-17"
}
}
},
"PipelineRoleDefaultPolicyC7A05455": {
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyDocument": {
"Statement": [
{
"Action": [
"s3:GetObject*",
"s3:GetBucket*",
"s3:List*",
"s3:DeleteObject*",
"s3:PutObject*",
"s3:Abort*"
],
"Effect": "Allow",
"Resource": [
{
"Fn::GetAtt": [
"Bucket83908E77",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"Bucket83908E77",
"Arn"
]
},
"/*"
]
]
}
]
},
{
"Action": [
"s3:GetObject*",
"s3:GetBucket*",
"s3:List*"
],
"Effect": "Allow",
"Resource": [
{
"Fn::GetAtt": [
"Bucket83908E77",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"Bucket83908E77",
"Arn"
]
},
"/*"
]
]
}
]
},
{
"Action": "sns:Publish",
"Effect": "Allow",
"Resource": {
"Ref": "ManualApprovalTopicResource300641E2"
}
}
],
"Version": "2012-10-17"
},
"PolicyName": "PipelineRoleDefaultPolicyC7A05455",
"Roles": [
{
"Ref": "PipelineRoleD68726F7"
}
]
}
},
"PipelineC660917D": {
"Type": "AWS::CodePipeline::Pipeline",
"Properties": {
"RoleArn": {
"Fn::GetAtt": [
"PipelineRoleD68726F7",
"Arn"
]
},
"Stages": [
{
"Actions": [
{
"ActionTypeId": {
"Category": "Source",
"Owner": "AWS",
"Provider": "S3",
"Version": "1"
},
"Configuration": {
"S3Bucket": {
"Ref": "Bucket83908E77"
},
"S3ObjectKey": "file.zip",
"PollForSourceChanges": true
},
"InputArtifacts": [],
"Name": "S3",
"OutputArtifacts": [
{
"Name": "Artifact_awscdkcodepipelinemanualapprovalBucketS39750AFE7"
}
],
"RunOrder": 1
}
],
"Name": "Source"
},
{
"Actions": [
{
"ActionTypeId": {
"Category": "Approval",
"Owner": "AWS",
"Provider": "Manual",
"Version": "1"
},
"Configuration": {
"NotificationArn": {
"Ref": "ManualApprovalTopicResource300641E2"
}
},
"InputArtifacts": [],
"Name": "ManualApproval",
"OutputArtifacts": [],
"RunOrder": 1
}
],
"Name": "Approve"
}
],
"ArtifactStore": {
"Location": {
"Ref": "Bucket83908E77"
},
"Type": "S3"
}
},
"DependsOn": [
"PipelineRoleD68726F7",
"PipelineRoleDefaultPolicyC7A05455"
]
},
"ManualApprovalTopicResource300641E2": {
"Type": "AWS::SNS::Topic"
},
"ManualApprovalTopicResourceSubscriptionadamruka85gmailcomBACEE98E": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Endpoint": "adamruka85@gmail.com",
"Protocol": "email",
"TopicArn": {
"Ref": "ManualApprovalTopicResource300641E2"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import s3 = require('@aws-cdk/aws-s3');
import cdk = require('@aws-cdk/cdk');
import codepipeline = require('../lib');

const app = new cdk.App();

const stack = new cdk.Stack(app, 'aws-cdk-codepipeline-manual-approval');

const bucket = new s3.Bucket(stack, 'Bucket');

const pipeline = new codepipeline.Pipeline(stack, 'Pipeline', {
artifactBucket: bucket,
});

const sourceStage = pipeline.addStage('Source');
bucket.addToPipeline(sourceStage, 'S3', {
bucketKey: 'file.zip',
});

const approveStage = pipeline.addStage('Approve');
new codepipeline.ManualApprovalAction(stack, 'ManualApproval', {
stage: approveStage,
notifyEmails: ['adamruka85@gmail.com']
});

app.run();
15 changes: 15 additions & 0 deletions packages/@aws-cdk/aws-codepipeline/test/test.pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ export = {
test.done();
},

'manual approval Action': {
'allows passing an SNS Topic when constructing it'(test: Test) {
const stack = new cdk.Stack();
const topic = new sns.Topic(stack, 'Topic');
const manualApprovalAction = new codepipeline.ManualApprovalAction(stack, 'Approve', {
stage: stageForTesting(stack),
notificationTopic: topic,
});

test.equal(manualApprovalAction.notificationTopic, topic);

test.done();
},
},

'PipelineProject': {
'with a custom Project Name': {
'sets the source and artifacts to CodePipeline'(test: Test) {
Expand Down

0 comments on commit c8c3079

Please sign in to comment.