Restructure everything and automate deployment. Fix nasty Javascript.
This commit is contained in:
parent
c718808aee
commit
1de3a78cc2
22 changed files with 2081 additions and 1061 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,5 +27,6 @@ build/Release
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
# Other local things to ignore
|
# Other local things to ignore
|
||||||
cloudformation-helpers.zip
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
/out
|
||||||
|
|
|
@ -1,134 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
apiGateway = Promise.promisifyAll(new AWS.APIGateway());
|
|
||||||
|
|
||||||
// Exposes the SNS.subscribe API method
|
|
||||||
function CreateApi(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
CreateApi.prototype = Object.create(base.Handler.prototype);
|
|
||||||
CreateApi.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
var rootObject = this;
|
|
||||||
return apiGateway.createRestApiAsync({
|
|
||||||
name: p.name,
|
|
||||||
description: p.description
|
|
||||||
})
|
|
||||||
.then(function(apiData) {
|
|
||||||
return rootObject.setReferenceData({ restApiId: apiData.id }) // Set this immediately, in case later calls fail
|
|
||||||
.then(function() {
|
|
||||||
return apiGateway.getResourcesAsync({
|
|
||||||
restApiId: apiData.id
|
|
||||||
})
|
|
||||||
.then(function(resourceData) {
|
|
||||||
return setupEndpoints(p.endpoints, resourceData.items[0].id, apiData.id)
|
|
||||||
.then(function(endpointsData) {
|
|
||||||
return apiGateway.createDeploymentAsync({
|
|
||||||
restApiId: apiData.id,
|
|
||||||
stageName: p.version
|
|
||||||
})
|
|
||||||
.then(function(deploymentData) {
|
|
||||||
// Total hack: there are limits to the number of API Gateway API calls you can make. So this function
|
|
||||||
// will fail if a CloudFormation template includes two or more APIs. Attempting to avoid this by blocking.
|
|
||||||
var until = new Date();
|
|
||||||
until.setSeconds(until.getSeconds() + 60);
|
|
||||||
while (new Date() < until) {
|
|
||||||
// Wait...
|
|
||||||
}
|
|
||||||
// AWS.config.region is a bit of a hack, but I can't figure out how else to dynamically
|
|
||||||
// detect the region of the API - seems to be nothing in API Gateway or AWS Lambda context.
|
|
||||||
// Could possibly get it from the CloudFormation stack, but that seems wrong.
|
|
||||||
return {
|
|
||||||
baseUrl: "https://" + apiData.id + ".execute-api." + AWS.config.region + ".amazonaws.com/" + p.version,
|
|
||||||
restApiId: apiData.id
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
CreateApi.prototype.handleDelete = function(referenceData) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (referenceData && referenceData.restApiId) {
|
|
||||||
// Can simply delete the entire API - don't need to delete each individual component
|
|
||||||
return apiGateway.deleteRestApiAsync({
|
|
||||||
restApiId: referenceData.restApiId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function setupEndpoints(config, parentResourceId, restApiId) {
|
|
||||||
return Promise.map(
|
|
||||||
Object.keys(config),
|
|
||||||
function(key) {
|
|
||||||
switch (key.toUpperCase()) {
|
|
||||||
case 'GET':
|
|
||||||
case 'HEAD':
|
|
||||||
case 'DELETE':
|
|
||||||
case 'OPTIONS':
|
|
||||||
case 'PATCH':
|
|
||||||
case 'POST':
|
|
||||||
case 'PUT':
|
|
||||||
var params = config[key];
|
|
||||||
params["httpMethod"] = key.toUpperCase()
|
|
||||||
params["resourceId"] = parentResourceId
|
|
||||||
params["restApiId"] = restApiId
|
|
||||||
params["apiKeyRequired"] = params["apiKeyRequired"] == "true" // Passing through CloudFormation, booleans become strings :(
|
|
||||||
var integration = params["integration"]
|
|
||||||
delete params.integration
|
|
||||||
return apiGateway.putMethodAsync(params)
|
|
||||||
.then(function() {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (integration) {
|
|
||||||
var contentType = integration["contentType"]
|
|
||||||
if (!contentType) {
|
|
||||||
throw "Integration config must include response contentType."
|
|
||||||
}
|
|
||||||
delete integration.contentType
|
|
||||||
integration["httpMethod"] = key.toUpperCase()
|
|
||||||
integration["resourceId"] = parentResourceId
|
|
||||||
integration["restApiId"] = restApiId
|
|
||||||
return apiGateway.putIntegrationAsync(integration)
|
|
||||||
.then(function(integrationData) {
|
|
||||||
var responseContentTypes = {}
|
|
||||||
responseContentTypes[contentType] = "Empty"
|
|
||||||
return apiGateway.putMethodResponseAsync({
|
|
||||||
httpMethod: key.toUpperCase(),
|
|
||||||
resourceId: parentResourceId,
|
|
||||||
restApiId: restApiId,
|
|
||||||
statusCode: '200',
|
|
||||||
responseModels: responseContentTypes
|
|
||||||
})
|
|
||||||
.then(function(methodResponseData) {
|
|
||||||
responseContentTypes[contentType] = ""
|
|
||||||
return apiGateway.putIntegrationResponseAsync({
|
|
||||||
httpMethod: key.toUpperCase(),
|
|
||||||
resourceId: parentResourceId,
|
|
||||||
restApiId: restApiId,
|
|
||||||
statusCode: '200',
|
|
||||||
responseTemplates: responseContentTypes
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
default:
|
|
||||||
return apiGateway.createResourceAsync({
|
|
||||||
parentId: parentResourceId,
|
|
||||||
pathPart: key,
|
|
||||||
restApiId: restApiId,
|
|
||||||
})
|
|
||||||
.then(function(resourceData) {
|
|
||||||
return setupEndpoints(config[key], resourceData.id, restApiId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
exports.createApi = function(event, context) {
|
|
||||||
handler = new CreateApi(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
helpers = require('lib/helpers'),
|
|
||||||
dynamoDB = Promise.promisifyAll(new AWS.DynamoDB());
|
|
||||||
|
|
||||||
// Exposes the SNS.subscribe API method
|
|
||||||
function PutItems(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
PutItems.prototype = Object.create(base.Handler.prototype);
|
|
||||||
PutItems.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
return dynamoDB.describeTableAsync({
|
|
||||||
TableName: p.TableName
|
|
||||||
})
|
|
||||||
.then(function(tableData) {
|
|
||||||
return Promise
|
|
||||||
.map(
|
|
||||||
p.Items,
|
|
||||||
function(item) {
|
|
||||||
return dynamoDB.putItemAsync({
|
|
||||||
TableName: p.TableName,
|
|
||||||
Item: helpers.formatForDynamo(item, true)
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
var key = {};
|
|
||||||
tableData.Table.KeySchema.forEach(function(keyMember) {
|
|
||||||
key[keyMember.AttributeName] = item[keyMember.AttributeName]
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
TableName: p.TableName,
|
|
||||||
Key: key
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(function(itemsInserted) {
|
|
||||||
return {
|
|
||||||
ItemsInserted: itemsInserted
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
PutItems.prototype.handleDelete = function(referenceData) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (referenceData) {
|
|
||||||
return Promise
|
|
||||||
.map(
|
|
||||||
referenceData.ItemsInserted,
|
|
||||||
function(item) {
|
|
||||||
return dynamoDB
|
|
||||||
.deleteItemAsync({
|
|
||||||
TableName: item.TableName,
|
|
||||||
Key: helpers.formatForDynamo(item.Key, true)
|
|
||||||
})
|
|
||||||
.then(function(data) {
|
|
||||||
return item.Key;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(function(itemsDeleted) {
|
|
||||||
return {
|
|
||||||
ItemsDeleted: itemsDeleted
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.putItems = function(event, context) {
|
|
||||||
handler = new PutItems(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
kinesis = Promise.promisifyAll(new AWS.Kinesis());
|
|
||||||
|
|
||||||
// Exposes the SNS.subscribe API method
|
|
||||||
function CreateStream(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
CreateStream.prototype = Object.create(base.Handler.prototype);
|
|
||||||
CreateStream.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
delete p.ServiceToken;
|
|
||||||
p.ShardCount = parseInt(p.ShardCount);
|
|
||||||
return kinesis.createStreamAsync(p)
|
|
||||||
.then(function() {
|
|
||||||
return waitWhileStatus(p.StreamName, "CREATING");
|
|
||||||
})
|
|
||||||
.then(function(arn) {
|
|
||||||
return {
|
|
||||||
StreamName: p.StreamName,
|
|
||||||
Arn: arn
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
CreateStream.prototype.handleDelete = function(referenceData) {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
return kinesis.deleteStreamAsync({StreamName: p.StreamName})
|
|
||||||
.then(function() {
|
|
||||||
return waitWhileStatus(p.StreamName, "DELETING")
|
|
||||||
})
|
|
||||||
.catch(function(err) {
|
|
||||||
return err;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.createStream = function(event, context) {
|
|
||||||
handler = new CreateStream(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
||||||
// Watch until the given status is no longer the status of the stream.
|
|
||||||
function waitWhileStatus(streamName, status) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
var validStatuses = ["CREATING", "DELETING", "ACTIVE", "UPDATING"]
|
|
||||||
if (validStatuses.indexOf(status) >= 0) {
|
|
||||||
return kinesis.describeStreamAsync({StreamName: streamName})
|
|
||||||
.then(function(data) {
|
|
||||||
console.log("Current status for [" + streamName +"]: " + data.StreamDescription.StreamStatus);
|
|
||||||
if (data.StreamDescription.StreamStatus == status) {
|
|
||||||
return Promise.delay(2000)
|
|
||||||
.then(function() {
|
|
||||||
return waitWhileStatus(streamName, status);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return data.StreamDescription.StreamARN;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw "status [" + status + "] not one of [" + validStatuses.join(", ") + "]";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
80
aws/s3.js
80
aws/s3.js
|
@ -1,80 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
helpers = require('lib/helpers'),
|
|
||||||
s3 = Promise.promisifyAll(new AWS.S3());
|
|
||||||
|
|
||||||
// Exposes the SNS.subscribe API method
|
|
||||||
function PutObject(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
PutObject.prototype = Object.create(base.Handler.prototype);
|
|
||||||
PutObject.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
delete p.ServiceToken;
|
|
||||||
return s3.putObjectAsync(p);
|
|
||||||
}
|
|
||||||
PutObject.prototype.handleDelete = function(referenceData) {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (p.Key.endsWith("/")) {
|
|
||||||
s3.listObjectsAsync({
|
|
||||||
Bucket: p.Bucket,
|
|
||||||
Prefix: p.Key
|
|
||||||
})
|
|
||||||
.then(function(subObjects) {
|
|
||||||
return Promise
|
|
||||||
.map(
|
|
||||||
subObjects.Contents,
|
|
||||||
function(item) {
|
|
||||||
return s3.deleteObjectAsync({
|
|
||||||
Bucket: p.Bucket,
|
|
||||||
Key: item.Key
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(function() {
|
|
||||||
return s3.deleteObjectAsync({
|
|
||||||
Bucket: p.Bucket,
|
|
||||||
Key: p.Key
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.putObject = function(event, context) {
|
|
||||||
handler = new PutObject(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exposes the S3.putBucketPolicy API method
|
|
||||||
function PutBucketPolicy(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
PutBucketPolicy.prototype = Object.create(base.Handler.prototype);
|
|
||||||
PutBucketPolicy.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
delete p.ServiceToken;
|
|
||||||
return s3.putBucketPolicyAsync(p)
|
|
||||||
.then(function() {
|
|
||||||
return {
|
|
||||||
BucketName : p.Bucket
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
PutBucketPolicy.prototype.handleDelete = function(referencedData) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if(referencedData) {
|
|
||||||
return s3.deleteBucketPolicyAsync({
|
|
||||||
Bucket : referencedData.BucketName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.putBucketPolicy = function(event, context) {
|
|
||||||
console.log(JSON.stringify(event));
|
|
||||||
handler = new PutBucketPolicy(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
||||||
|
|
39
aws/ses.js
39
aws/ses.js
|
@ -1,39 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
helpers = require('lib/helpers'),
|
|
||||||
ses = Promise.promisifyAll(new AWS.SES());
|
|
||||||
|
|
||||||
// Exposes the SES.createReceiptRule API method
|
|
||||||
function CreateReceiptRule(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
CreateReceiptRule.prototype = Object.create(base.Handler.prototype);
|
|
||||||
CreateReceiptRule.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
delete p.ServiceToken;
|
|
||||||
p.Rule.Enabled = ("true" === p.Rule.Enabled );
|
|
||||||
p.Rule.ScanEnabled = ("true" === p.Rule.ScanEnabled );
|
|
||||||
return ses.createReceiptRuleAsync(p)
|
|
||||||
.then(function() {
|
|
||||||
return {
|
|
||||||
RuleSetName : p.RuleSetName,
|
|
||||||
RuleName : p.Rule.Name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
CreateReceiptRule.prototype.handleDelete = function(referenceData) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (referenceData) {
|
|
||||||
return ses.deleteReceiptRuleAsync({
|
|
||||||
RuleSetName : referenceData.RuleSetName,
|
|
||||||
RuleName : referenceData.RuleName
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.createReceiptRule = function(event, context) {
|
|
||||||
console.log(JSON.stringify(event));
|
|
||||||
handler = new CreateReceiptRule(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
31
aws/sns.js
31
aws/sns.js
|
@ -1,31 +0,0 @@
|
||||||
var Promise = require('bluebird'),
|
|
||||||
AWS = require('aws-sdk'),
|
|
||||||
base = require('lib/base'),
|
|
||||||
sns = Promise.promisifyAll(new AWS.SNS());
|
|
||||||
|
|
||||||
// Exposes the SNS.subscribe API method
|
|
||||||
function Subscribe(event, context) {
|
|
||||||
base.Handler.call(this, event, context);
|
|
||||||
}
|
|
||||||
Subscribe.prototype = Object.create(base.Handler.prototype);
|
|
||||||
Subscribe.prototype.handleCreate = function() {
|
|
||||||
var p = this.event.ResourceProperties;
|
|
||||||
return sns.subscribeAsync({
|
|
||||||
Endpoint: p.Endpoint,
|
|
||||||
Protocol: p.Protocol,
|
|
||||||
TopicArn: p.TopicArn
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Subscribe.prototype.handleDelete = function(referenceData) {
|
|
||||||
return Promise.try(function() {
|
|
||||||
if (referenceData && referenceData.SubscriptionArn) {
|
|
||||||
return sns.unsubscribeAsync({
|
|
||||||
SubscriptionArn: referenceData.SubscriptionArn
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
exports.subscribe = function(event, context) {
|
|
||||||
handler = new Subscribe(event, context);
|
|
||||||
handler.handle();
|
|
||||||
}
|
|
|
@ -1,533 +0,0 @@
|
||||||
{
|
|
||||||
"AWSTemplateFormatVersion": "2010-09-09",
|
|
||||||
"Resources": {
|
|
||||||
"ReferenceDB": {
|
|
||||||
"Type": "AWS::DynamoDB::Table",
|
|
||||||
"Properties": {
|
|
||||||
"AttributeDefinitions": [
|
|
||||||
{
|
|
||||||
"AttributeName": "key",
|
|
||||||
"AttributeType": "S"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"KeySchema": [
|
|
||||||
{
|
|
||||||
"AttributeName": "key",
|
|
||||||
"KeyType": "HASH"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ProvisionedThroughput": {
|
|
||||||
"ReadCapacityUnits": 1,
|
|
||||||
"WriteCapacityUnits": 1
|
|
||||||
},
|
|
||||||
"TableName": { "Fn::Join": [ "-", [ { "Ref" : "AWS::StackName" }, "reference" ] ] }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"RoleBasePolicy": {
|
|
||||||
"Type": "AWS::IAM::ManagedPolicy",
|
|
||||||
"Properties": {
|
|
||||||
"Description" : { "Fn::Join": [ " ", [ "Base policy for all Lambda function roles in", { "Ref" : "AWS::StackName" }, "." ] ] },
|
|
||||||
"PolicyDocument" : {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"logs:CreateLogGroup",
|
|
||||||
"logs:CreateLogStream",
|
|
||||||
"logs:PutLogEvents"
|
|
||||||
],
|
|
||||||
"Resource": "arn:aws:logs:*:*:*"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"dynamodb:GetItem",
|
|
||||||
"dynamodb:PutItem",
|
|
||||||
"dynamodb:Scan"
|
|
||||||
],
|
|
||||||
"Resource": { "Fn::Join": [ "", [ "arn:aws:dynamodb:", { "Ref": "AWS::Region" }, ":", { "Ref": "AWS::AccountId" } , ":table/", { "Ref": "ReferenceDB" } ] ] }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ApiGatewayCreateApiFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "ApiGatewayWriter",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"apigateway:*"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ApiGatewayCreateApiFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to create a full API in Api Gateway.",
|
|
||||||
"Handler": "aws/apiGateway.createApi",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "ApiGatewayCreateApiFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"ApiGatewayCreateApiFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"CloudWatchLogsPutMetricFilterFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "LogFilterCreator",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"logs:DeleteMetricFilter",
|
|
||||||
"logs:PutMetricFilter"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"CloudWatchLogsPutMetricFilterFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to populate a DynamoDB database from CloudFormation",
|
|
||||||
"Handler": "aws/cloudWatchLogs.putMetricFilter",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "CloudWatchLogsPutMetricFilterFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"CloudWatchLogsPutMetricFilterFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"DynamoDBPutItemsFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "DBWriter",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"dynamodb:DeleteItem",
|
|
||||||
"dynamodb:DescribeTable",
|
|
||||||
"dynamodb:PutItem"
|
|
||||||
],
|
|
||||||
"Resource": { "Fn::Join": [ "", [ "arn:aws:dynamodb:", { "Ref": "AWS::Region" }, ":", { "Ref": "AWS::AccountId" } , ":table/*" ] ] }
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DynamoDBPutItemsFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to populate a DynamoDB database from CloudFormation",
|
|
||||||
"Handler": "aws/dynamo.putItems",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "DynamoDBPutItemsFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"DynamoDBPutItemsFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"KinesisCreateStreamFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "KinesisCreator",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"kinesis:CreateStream",
|
|
||||||
"kinesis:DeleteStream",
|
|
||||||
"kinesis:DescribeStream"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"KinesisCreateStreamFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to create a Kinesis stream",
|
|
||||||
"Handler": "aws/kinesis.createStream",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "KinesisCreateStreamFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 180
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"KinesisCreateStreamFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"S3PutObjectFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "S3Writer",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"s3:DeleteObject",
|
|
||||||
"s3:ListBucket",
|
|
||||||
"s3:PutObject"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"S3PutObjectFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to put objects into S3.",
|
|
||||||
"Handler": "aws/s3.putObject",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "S3PutObjectFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"S3PutObjectFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"S3PutBucketPolicyFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "S3PolicyWriter",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"s3:ListBucket",
|
|
||||||
"s3:PutBucketPolicy",
|
|
||||||
"s3:DeleteBucketPolicy"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"S3PutBucketPolicyFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": "com.gilt.public.backoffice",
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to put S3 bucket policy.",
|
|
||||||
"Handler": "aws/s3.putBucketPolicy",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "S3PutBucketPolicyFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"S3PutBucketPolicyFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"SnsSubscribeFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "SNSSubscriber",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"sns:subscribe",
|
|
||||||
"sns:unsubscribe"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SnsSubscribeFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to subscribe to existing SNS topics.",
|
|
||||||
"Handler": "aws/sns.subscribe",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "SnsSubscribeFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"SnsSubscribeFunctionRole"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"SesCreateReceiptRuleFunctionRole": {
|
|
||||||
"Type": "AWS::IAM::Role",
|
|
||||||
"Properties": {
|
|
||||||
"AssumeRolePolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Principal": {
|
|
||||||
"Service": [ "lambda.amazonaws.com" ]
|
|
||||||
},
|
|
||||||
"Action": [ "sts:AssumeRole" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"ManagedPolicyArns": [
|
|
||||||
{ "Ref": "RoleBasePolicy" }
|
|
||||||
],
|
|
||||||
"Policies": [
|
|
||||||
{
|
|
||||||
"PolicyName": "SESReceiptRuleModifier",
|
|
||||||
"PolicyDocument": {
|
|
||||||
"Version" : "2012-10-17",
|
|
||||||
"Statement": [
|
|
||||||
{
|
|
||||||
"Effect": "Allow",
|
|
||||||
"Action": [
|
|
||||||
"ses:CreateReceiptRule",
|
|
||||||
"ses:DeleteReceiptRule"
|
|
||||||
],
|
|
||||||
"Resource": "*"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SesCreateReceiptRuleFunction": {
|
|
||||||
"Type": "AWS::Lambda::Function",
|
|
||||||
"Properties": {
|
|
||||||
"Code": {
|
|
||||||
"S3Bucket": "com.gilt.public.backoffice",
|
|
||||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
|
||||||
},
|
|
||||||
"Description": "Used to create SES receipt rules.",
|
|
||||||
"Handler": "aws/ses.createReceiptRule",
|
|
||||||
"Role": {"Fn::GetAtt" : [ "SesCreateReceiptRuleFunctionRole", "Arn" ] },
|
|
||||||
"Runtime": "nodejs4.3",
|
|
||||||
"Timeout": 30
|
|
||||||
},
|
|
||||||
"DependsOn": [
|
|
||||||
"SesCreateReceiptRuleFunctionRole"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Outputs": {
|
|
||||||
"ApiGatewayCreateApiFunctionArn": {
|
|
||||||
"Description": "The ARN of the ApiGatewayCreateApiFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["ApiGatewayCreateApiFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"CloudWatchLogsPutMetricFilterFunctionArn": {
|
|
||||||
"Description": "The ARN of the CloudWatchLogsPutMetricFilterFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["CloudWatchLogsPutMetricFilterFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"DynamoDBPutItemsFunctionArn": {
|
|
||||||
"Description": "The ARN of the DynamoDBPutItemsFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["DynamoDBPutItemsFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"KinesisCreateStreamFunctionArn": {
|
|
||||||
"Description": "The ARN of the KinesisCreateStreamFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["KinesisCreateStreamFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"SnsSubscribeFunctionArn": {
|
|
||||||
"Description": "The ARN of the SnsSubscribeFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["SnsSubscribeFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"S3PutObjectFunctionArn": {
|
|
||||||
"Description": "The ARN of the S3PutObjectFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["S3PutObjectFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"S3PutBucketPolicyFunctionArn": {
|
|
||||||
"Description": "The ARN of the S3PutBucketPolicyFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["S3PutBucketPolicyFunction", "Arn"] }
|
|
||||||
},
|
|
||||||
"SesCreateReceiptRuleFunctionArn": {
|
|
||||||
"Description": "The ARN of the SesCreateReceiptRuleFunction, for use in other CloudFormation templates.",
|
|
||||||
"Value": { "Fn::GetAtt" : ["SesCreateReceiptRuleFunction", "Arn"] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
345
create_cloudformation_helper_functions.template.yml
Normal file
345
create_cloudformation_helper_functions.template.yml
Normal file
|
@ -0,0 +1,345 @@
|
||||||
|
AWSTemplateFormatVersion: 2010-09-09
|
||||||
|
Transform: 'AWS::Serverless-2016-10-31'
|
||||||
|
|
||||||
|
Resources:
|
||||||
|
ReferenceDB:
|
||||||
|
Type: 'AWS::DynamoDB::Table'
|
||||||
|
Properties:
|
||||||
|
AttributeDefinitions:
|
||||||
|
- AttributeName: key
|
||||||
|
AttributeType: S
|
||||||
|
KeySchema:
|
||||||
|
- AttributeName: key
|
||||||
|
KeyType: HASH
|
||||||
|
ProvisionedThroughput:
|
||||||
|
ReadCapacityUnits: 1
|
||||||
|
WriteCapacityUnits: 1
|
||||||
|
TableName: !Sub '${AWS::StackName}-reference'
|
||||||
|
RoleBasePolicy:
|
||||||
|
Type: 'AWS::IAM::ManagedPolicy'
|
||||||
|
Properties:
|
||||||
|
Description: !Sub 'Base policy for all Lambda function roles in ${AWS::StackName}.'
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- logs:CreateLogGroup
|
||||||
|
- logs:CreateLogStream
|
||||||
|
- logs:PutLogEvents
|
||||||
|
Resource: 'arn:aws:logs:*:*:*'
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- dynamodb:GetItem
|
||||||
|
- dynamodb:PutItem
|
||||||
|
- dynamodb:Scan
|
||||||
|
Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${ReferenceDB}'
|
||||||
|
ApiGatewayCreateApiFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: ApiGatewayWriter
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'apigateway:*'
|
||||||
|
Resource: '*'
|
||||||
|
ApiGatewayCreateApiFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to create a full API in Api Gateway.
|
||||||
|
Handler: aws/apiGateway.createApi
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'ApiGatewayCreateApiFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- ApiGatewayCreateApiFunctionRole
|
||||||
|
CloudWatchLogsPutMetricFilterFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: LogFilterCreator
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'logs:DeleteMetricFilter'
|
||||||
|
- 'logs:PutMetricFilter'
|
||||||
|
Resource: '*'
|
||||||
|
CloudWatchLogsPutMetricFilterFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to populate a DynamoDB database from CloudFormation
|
||||||
|
Handler: aws/cloudWatchLogs.putMetricFilter
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'CloudWatchLogsPutMetricFilterFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- CloudWatchLogsPutMetricFilterFunctionRole
|
||||||
|
DynamoDBPutItemsFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: DBWriter
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'dynamodb:DeleteItem'
|
||||||
|
- 'dynamodb:DescribeTable'
|
||||||
|
- 'dynamodb:PutItem'
|
||||||
|
Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*'
|
||||||
|
DynamoDBPutItemsFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to populate a DynamoDB database from CloudFormation
|
||||||
|
Handler: aws/dynamo.putItems
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'DynamoDBPutItemsFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- DynamoDBPutItemsFunctionRole
|
||||||
|
KinesisCreateStreamFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: KinesisCreator
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'kinesis:CreateStream'
|
||||||
|
- 'kinesis:DeleteStream'
|
||||||
|
- 'kinesis:DescribeStream'
|
||||||
|
Resource: '*'
|
||||||
|
KinesisCreateStreamFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to create a Kinesis stream
|
||||||
|
Handler: aws/kinesis.createStream
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'KinesisCreateStreamFunctionRole.Arn'
|
||||||
|
Timeout: 180
|
||||||
|
DependsOn:
|
||||||
|
- KinesisCreateStreamFunctionRole
|
||||||
|
S3PutObjectFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: S3Writer
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 's3:DeleteObject'
|
||||||
|
- 's3:ListBucket'
|
||||||
|
- 's3:PutObject'
|
||||||
|
Resource: '*'
|
||||||
|
S3PutObjectFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to put objects into S3.
|
||||||
|
Handler: aws/s3.putObject
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'S3PutObjectFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- S3PutObjectFunctionRole
|
||||||
|
S3PutBucketPolicyFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: S3PolicyWriter
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 's3:ListBucket'
|
||||||
|
- 's3:PutBucketPolicy'
|
||||||
|
- 's3:DeleteBucketPolicy'
|
||||||
|
Resource: '*'
|
||||||
|
S3PutBucketPolicyFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to put S3 bucket policy.
|
||||||
|
Handler: aws/s3.putBucketPolicy
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'S3PutBucketPolicyFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- S3PutBucketPolicyFunctionRole
|
||||||
|
SnsSubscribeFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: SNSSubscriber
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'sns:subscribe'
|
||||||
|
- 'sns:unsubscribe'
|
||||||
|
Resource: '*'
|
||||||
|
SnsSubscribeFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to subscribe to existing SNS topics
|
||||||
|
Handler: aws/sns.subscribe
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'SnsSubscribeFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- SnsSubscribeFunctionRole
|
||||||
|
SesCreateReceiptRuleFunctionRole:
|
||||||
|
Type: 'AWS::IAM::Role'
|
||||||
|
Properties:
|
||||||
|
AssumeRolePolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Principal:
|
||||||
|
Service:
|
||||||
|
- lambda.amazonaws.com
|
||||||
|
Action:
|
||||||
|
- 'sts:AssumeRole'
|
||||||
|
ManagedPolicyArns:
|
||||||
|
- !Ref 'RoleBasePolicy'
|
||||||
|
Policies:
|
||||||
|
- PolicyName: SESReceiptRuleModifier
|
||||||
|
PolicyDocument:
|
||||||
|
Version: 2012-10-17
|
||||||
|
Statement:
|
||||||
|
- Effect: Allow
|
||||||
|
Action:
|
||||||
|
- 'ses:CreateReceiptRule'
|
||||||
|
- 'ses:DeleteReceiptRule'
|
||||||
|
Resource: '*'
|
||||||
|
SesCreateReceiptRuleFunction:
|
||||||
|
Type: 'AWS::Serverless::Function'
|
||||||
|
Properties:
|
||||||
|
CodeUri: out/cloudformation-helpers
|
||||||
|
Description: Used to create SES receipt rules
|
||||||
|
Handler: aws/ses.createReceiptRule
|
||||||
|
Runtime: nodejs12.x
|
||||||
|
Role: !GetAtt 'SesCreateReceiptRuleFunctionRole.Arn'
|
||||||
|
Timeout: 30
|
||||||
|
DependsOn:
|
||||||
|
- SesCreateReceiptRuleFunctionRole
|
||||||
|
|
||||||
|
Outputs:
|
||||||
|
ApiGatewayCreateApiFunctionArn:
|
||||||
|
Description: The ARN of the ApiGatewayCreateApiFunction, for use in other CloudFormation templates
|
||||||
|
Value: !GetAtt 'ApiGatewayCreateApiFunction.Arn'
|
||||||
|
CloudWatchLogsPutMetricFilterFunctionArn:
|
||||||
|
Description: The ARN of the CloudWatchLogsPutMetricFilterFunction, for use in other CloudFormation templates
|
||||||
|
Value: !GetAtt 'CloudWatchLogsPutMetricFilterFunction.Arn'
|
||||||
|
DynamoDBPutItemsFunctionArn:
|
||||||
|
Description: The ARN of the DynamoDBPutItemsFunction, for use in other CloudFormation templates.
|
||||||
|
Value: !GetAtt 'DynamoDBPutItemsFunction.Arn'
|
||||||
|
KinesisCreateStreamFunctionArn:
|
||||||
|
Description: The ARN of the KinesisCreateStreamFunction, for use in other CloudFormation templates
|
||||||
|
Value: !GetAtt 'KinesisCreateStreamFunction.Arn'
|
||||||
|
SnsSubscribeFunctionArn:
|
||||||
|
Description: The ARN of the SnsSubscribeFunction, for use in other CloudFormation templates.
|
||||||
|
Value: !GetAtt 'SnsSubscribeFunction.Arn'
|
||||||
|
S3PutObjectFunctionArn:
|
||||||
|
Description: The ARN of the S3PutObjectFunction, for use in other CloudFormation templates.
|
||||||
|
Value: !GetAtt 'S3PutObjectFunction.Arn'
|
||||||
|
S3PutBucketPolicyFunctionArn:
|
||||||
|
Description: The ARN of the S3PutBucketPolicyFunction, for use in other CloudFormation templates.
|
||||||
|
Value: !GetAtt 'S3PutBucketPolicyFunction.Arn'
|
||||||
|
SesCreateReceiptRuleFunctionArn:
|
||||||
|
Description: The ARN of the SesCreateReceiptRuleFunction, for use in other CloudFormation templates.
|
||||||
|
Value: !GetAtt 'SesCreateReceiptRuleFunction.Arn'
|
20
package.json
20
package.json
|
@ -1,20 +0,0 @@
|
||||||
{
|
|
||||||
"name": "cloudformation-helpers",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"description": "A set of helper methods to fill in the gaps in existing CloudFormation support.",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/gilt/cloudformation-helpers"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"cloudformation"
|
|
||||||
],
|
|
||||||
"author": "Ryan Martin",
|
|
||||||
"license": "Apache 2.0",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/gilt/cloudformation-helpers/issues"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"bluebird": "3.x"
|
|
||||||
}
|
|
||||||
}
|
|
20
src/.eslintrc.js
Normal file
20
src/.eslintrc.js
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module.exports =
|
||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true,
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"handler": "readonly",
|
||||||
|
},
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 2017,
|
||||||
|
"sourceType": "module",
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-undef": "error",
|
||||||
|
"no-unused-vars": "error",
|
||||||
|
"no-var": "error",
|
||||||
|
"semi": ["error", "always" ],
|
||||||
|
}
|
||||||
|
};
|
114
src/aws/apiGateway.js
Normal file
114
src/aws/apiGateway.js
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
apiGateway = utilPromisifyAll(new AWS.APIGateway());
|
||||||
|
|
||||||
|
// Exposes the SNS.subscribe API method
|
||||||
|
function CreateApi(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
CreateApi.prototype = Object.create(base.Handler.prototype);
|
||||||
|
CreateApi.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
const apiData = await apiGateway.createRestApiAsync({
|
||||||
|
name: p.name,
|
||||||
|
description: p.description
|
||||||
|
});
|
||||||
|
await this.setReferenceData({ restApiId: apiData.id }); // Set this immediately, in case later calls fail
|
||||||
|
const resourceData = await apiGateway.getResourcesAsync({
|
||||||
|
restApiId: apiData.id
|
||||||
|
});
|
||||||
|
await setupEndpoints(p.endpoints, resourceData.items[0].id, apiData.id);
|
||||||
|
await apiGateway.createDeploymentAsync({
|
||||||
|
restApiId: apiData.id,
|
||||||
|
stageName: p.version
|
||||||
|
});
|
||||||
|
// Total hack: there are limits to the number of API Gateway API calls you can make. So this function
|
||||||
|
// will fail if a CloudFormation template includes two or more APIs. Attempting to avoid this by blocking.
|
||||||
|
const until = new Date();
|
||||||
|
until.setSeconds(until.getSeconds() + 60);
|
||||||
|
while (new Date() < until) {
|
||||||
|
// Wait...
|
||||||
|
}
|
||||||
|
// AWS.config.region is a bit of a hack, but I can't figure out how else to dynamically
|
||||||
|
// detect the region of the API - seems to be nothing in API Gateway or AWS Lambda context.
|
||||||
|
// Could possibly get it from the CloudFormation stack, but that seems wrong.
|
||||||
|
return {
|
||||||
|
baseUrl: "https://" + apiData.id + ".execute-api." + AWS.config.region + ".amazonaws.com/" + p.version,
|
||||||
|
restApiId: apiData.id
|
||||||
|
};
|
||||||
|
};
|
||||||
|
CreateApi.prototype.handleDelete = async function(referenceData) {
|
||||||
|
if (referenceData && referenceData.restApiId) {
|
||||||
|
// Can simply delete the entire API - don't need to delete each individual component
|
||||||
|
return await apiGateway.deleteRestApiAsync({
|
||||||
|
restApiId: referenceData.restApiId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
async function setupEndpoints(config, parentResourceId, restApiId) {
|
||||||
|
return await Promise.all(
|
||||||
|
Object.keys(config).map(async key => {
|
||||||
|
switch (key.toUpperCase()) {
|
||||||
|
case 'GET':
|
||||||
|
case 'HEAD':
|
||||||
|
case 'DELETE':
|
||||||
|
case 'OPTIONS':
|
||||||
|
case 'PATCH':
|
||||||
|
case 'POST':
|
||||||
|
case 'PUT':
|
||||||
|
const params = config[key];
|
||||||
|
params["httpMethod"] = key.toUpperCase();
|
||||||
|
params["resourceId"] = parentResourceId;
|
||||||
|
params["restApiId"] = restApiId;
|
||||||
|
params["apiKeyRequired"] = params["apiKeyRequired"] == "true"; // Passing through CloudFormation, booleans become strings :/
|
||||||
|
const integration = params["integration"];
|
||||||
|
delete params.integration;
|
||||||
|
await apiGateway.putMethodAsync(params);
|
||||||
|
if (integration) {
|
||||||
|
const contentType = integration["contentType"];
|
||||||
|
if (!contentType) {
|
||||||
|
throw "Integration config must include response contentType.";
|
||||||
|
}
|
||||||
|
delete integration.contentType;
|
||||||
|
integration["httpMethod"] = key.toUpperCase();
|
||||||
|
integration["resourceId"] = parentResourceId;
|
||||||
|
integration["restApiId"] = restApiId;
|
||||||
|
apiGateway.putIntegrationAsync(integration);
|
||||||
|
const responseContentTypes = {};
|
||||||
|
responseContentTypes[contentType] = "Empty";
|
||||||
|
await apiGateway.putMethodResponseAsync({
|
||||||
|
httpMethod: key.toUpperCase(),
|
||||||
|
resourceId: parentResourceId,
|
||||||
|
restApiId: restApiId,
|
||||||
|
statusCode: '200',
|
||||||
|
responseModels: responseContentTypes
|
||||||
|
});
|
||||||
|
responseContentTypes[contentType] = "";
|
||||||
|
return await apiGateway.putIntegrationResponseAsync({
|
||||||
|
httpMethod: key.toUpperCase(),
|
||||||
|
resourceId: parentResourceId,
|
||||||
|
restApiId: restApiId,
|
||||||
|
statusCode: '200',
|
||||||
|
responseTemplates: responseContentTypes
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
const resourceData = await apiGateway.createResourceAsync({
|
||||||
|
parentId: parentResourceId,
|
||||||
|
pathPart: key,
|
||||||
|
restApiId: restApiId,
|
||||||
|
});
|
||||||
|
return await setupEndpoints(config[key], resourceData.id, restApiId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
exports.createApi = function(event, context) {
|
||||||
|
handler = new CreateApi(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
64
src/aws/dynamo.js
Normal file
64
src/aws/dynamo.js
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
helpers = require('lib/helpers'),
|
||||||
|
dynamoDB = utilPromisifyAll(new AWS.DynamoDB());
|
||||||
|
|
||||||
|
// Exposes the SNS.subscribe API method
|
||||||
|
function PutItems(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
PutItems.prototype = Object.create(base.Handler.prototype);
|
||||||
|
PutItems.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
|
||||||
|
const tableData = await dynamoDB.describeTableAsync({
|
||||||
|
TableName: p.TableName
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemsInserted = await Promise.all(
|
||||||
|
p.Items.map(async item => {
|
||||||
|
await dynamoDB.putItemAsync({
|
||||||
|
TableName: p.TableName,
|
||||||
|
Item: helpers.formatForDynamo(item, true)
|
||||||
|
});
|
||||||
|
|
||||||
|
const key = {};
|
||||||
|
tableData.Table.KeySchema.forEach(function(keyMember) {
|
||||||
|
key[keyMember.AttributeName] = item[keyMember.AttributeName];
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
TableName: p.TableName,
|
||||||
|
Key: key
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ItemsInserted: itemsInserted
|
||||||
|
};
|
||||||
|
};
|
||||||
|
PutItems.prototype.handleDelete = async function(referenceData) {
|
||||||
|
if (referenceData) {
|
||||||
|
const itemsDeleted = await Promise.all(
|
||||||
|
referenceData.itemsInserted.map(async item => {
|
||||||
|
await dynamoDB.deleteItemAsync({
|
||||||
|
TableName: item.TableName,
|
||||||
|
Key: helpers.formatForDynamo(item.Key, true)
|
||||||
|
});
|
||||||
|
|
||||||
|
return item.Key;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
ItemsDeleted: itemsDeleted
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.putItems = function(event, context) {
|
||||||
|
handler = new PutItems(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
54
src/aws/kinesis.js
Normal file
54
src/aws/kinesis.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
kinesis = utilPromisifyAll(new AWS.Kinesis());
|
||||||
|
|
||||||
|
// Exposes the SNS.subscribe API method
|
||||||
|
function CreateStream(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
CreateStream.prototype = Object.create(base.Handler.prototype);
|
||||||
|
CreateStream.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
delete p.ServiceToken;
|
||||||
|
p.ShardCount = parseInt(p.ShardCount);
|
||||||
|
await kinesis.createStreamAsync(p);
|
||||||
|
const arn = await waitWhileStatus(p.StreamName, "CREATING");
|
||||||
|
return {
|
||||||
|
StreamName: p.StreamName,
|
||||||
|
Arn: arn
|
||||||
|
};
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
CreateStream.prototype.handleDelete = async function(referenceData) {
|
||||||
|
try {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
await kinesis.deleteStreamAsync({StreamName: p.StreamName});
|
||||||
|
return await waitWhileStatus(p.StreamName, "DELETING");
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.createStream = function(event, context) {
|
||||||
|
handler = new CreateStream(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
||||||
|
// Watch until the given status is no longer the status of the stream.
|
||||||
|
async function waitWhileStatus(streamName, status) {
|
||||||
|
const validStatuses = ["CREATING", "DELETING", "ACTIVE", "UPDATING"];
|
||||||
|
if (validStatuses.indexOf(status) >= 0) {
|
||||||
|
const data = await kinesis.describeStreamAsync({StreamName: streamName});
|
||||||
|
|
||||||
|
console.log("Current status for [" + streamName +"]: " + data.StreamDescription.StreamStatus);
|
||||||
|
if (data.StreamDescription.StreamStatus == status) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||||
|
return await waitWhileStatus(streamName, status);
|
||||||
|
} else {
|
||||||
|
return data.StreamDescription.StreamARN;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw "status [" + status + "] not one of [" + validStatuses.join(", ") + "]";
|
||||||
|
}
|
||||||
|
}
|
72
src/aws/s3.js
Normal file
72
src/aws/s3.js
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
s3 = utilPromisifyAll(new AWS.S3());
|
||||||
|
|
||||||
|
// Exposes the SNS.subscribe API method
|
||||||
|
function PutObject(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
PutObject.prototype = Object.create(base.Handler.prototype);
|
||||||
|
PutObject.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
delete p.ServiceToken;
|
||||||
|
return await s3.putObjectAsync(p);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
PutObject.prototype.handleDelete = async function(referenceData) {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
if (p.Key.endsWith("/")) {
|
||||||
|
const subObjects = await s3.listObjectsAsync({
|
||||||
|
Bucket: p.Bucket,
|
||||||
|
Prefix: p.Key
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
subObjects.Contents.map(async item => {
|
||||||
|
return await s3.deleteObjectAsync({
|
||||||
|
Bucket: p.Bucket,
|
||||||
|
Key: item.Key
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return s3.deleteObjectAsync({
|
||||||
|
Bucket: p.Bucket,
|
||||||
|
Key: p.Key
|
||||||
|
});
|
||||||
|
};
|
||||||
|
exports.putObject = function(event, context) {
|
||||||
|
handler = new PutObject(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exposes the S3.putBucketPolicy API method
|
||||||
|
function PutBucketPolicy(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
PutBucketPolicy.prototype = Object.create(base.Handler.prototype);
|
||||||
|
PutBucketPolicy.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
delete p.ServiceToken;
|
||||||
|
await s3.putBucketPolicyAsync(p);
|
||||||
|
|
||||||
|
return {
|
||||||
|
BucketName : p.Bucket
|
||||||
|
};
|
||||||
|
};
|
||||||
|
PutBucketPolicy.prototype.handleDelete = async function(referencedData) {
|
||||||
|
if(referencedData) {
|
||||||
|
return await s3.deleteBucketPolicyAsync({
|
||||||
|
Bucket : referencedData.BucketName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.putBucketPolicy = function(event, context) {
|
||||||
|
console.log(JSON.stringify(event));
|
||||||
|
handler = new PutBucketPolicy(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
||||||
|
|
36
src/aws/ses.js
Normal file
36
src/aws/ses.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
ses = utilPromisifyAll(new AWS.SES());
|
||||||
|
|
||||||
|
// Exposes the SES.createReceiptRule API method
|
||||||
|
function CreateReceiptRule(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
CreateReceiptRule.prototype = Object.create(base.Handler.prototype);
|
||||||
|
CreateReceiptRule.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
delete p.ServiceToken;
|
||||||
|
p.Rule.Enabled = ("true" === p.Rule.Enabled );
|
||||||
|
p.Rule.ScanEnabled = ("true" === p.Rule.ScanEnabled );
|
||||||
|
await ses.createReceiptRuleAsync(p);
|
||||||
|
|
||||||
|
return {
|
||||||
|
RuleSetName : p.RuleSetName,
|
||||||
|
RuleName : p.Rule.Name
|
||||||
|
};
|
||||||
|
};
|
||||||
|
CreateReceiptRule.prototype.handleDelete = async function(referenceData) {
|
||||||
|
if (referenceData) {
|
||||||
|
return await ses.deleteReceiptRuleAsync({
|
||||||
|
RuleSetName : referenceData.RuleSetName,
|
||||||
|
RuleName : referenceData.RuleName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.createReceiptRule = function(event, context) {
|
||||||
|
console.log(JSON.stringify(event));
|
||||||
|
handler = new CreateReceiptRule(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
30
src/aws/sns.js
Normal file
30
src/aws/sns.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
const
|
||||||
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
|
AWS = require('aws-sdk'),
|
||||||
|
base = require('lib/base'),
|
||||||
|
sns = utilPromisifyAll(new AWS.SNS());
|
||||||
|
|
||||||
|
// Exposes the SNS.subscribe API method
|
||||||
|
function Subscribe(event, context) {
|
||||||
|
base.Handler.call(this, event, context);
|
||||||
|
}
|
||||||
|
Subscribe.prototype = Object.create(base.Handler.prototype);
|
||||||
|
Subscribe.prototype.handleCreate = async function() {
|
||||||
|
const p = this.event.ResourceProperties;
|
||||||
|
return await sns.subscribeAsync({
|
||||||
|
Endpoint: p.Endpoint,
|
||||||
|
Protocol: p.Protocol,
|
||||||
|
TopicArn: p.TopicArn
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Subscribe.prototype.handleDelete = async function(referenceData) {
|
||||||
|
if (referenceData && referenceData.SubscriptionArn) {
|
||||||
|
return await sns.unsubscribeAsync({
|
||||||
|
SubscriptionArn: referenceData.SubscriptionArn
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.subscribe = function(event, context) {
|
||||||
|
handler = new Subscribe(event, context);
|
||||||
|
handler.handle();
|
||||||
|
};
|
|
@ -1,49 +1,45 @@
|
||||||
// Implement this class for every new handler.
|
// Implement this class for every new handler.
|
||||||
|
|
||||||
var Promise = require('bluebird'),
|
const
|
||||||
helpers = require('lib/helpers'),
|
utilPromisifyAll = require('util-promisifyall'),
|
||||||
response = require('lib/cfn-response'),
|
helpers = require('lib/helpers'),
|
||||||
AWS = require('aws-sdk'),
|
response = require('lib/cfn-response'),
|
||||||
dynamoDB = Promise.promisifyAll(new AWS.DynamoDB());
|
AWS = require('aws-sdk'),
|
||||||
|
dynamoDB = utilPromisifyAll(new AWS.DynamoDB());
|
||||||
|
|
||||||
exports.Handler = function(event, context) {
|
exports.Handler = function(event, context) {
|
||||||
this.event = event;
|
this.event = event;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.Handler.prototype.handle = function() {
|
exports.Handler.prototype.handle = async function() {
|
||||||
var outer = this;
|
try {
|
||||||
Promise.try(function() {
|
let data;
|
||||||
switch (outer.event.RequestType) {
|
let referenceData;
|
||||||
|
switch (this.event.RequestType) {
|
||||||
case 'Create':
|
case 'Create':
|
||||||
return outer.handleCreate()
|
const created = await this.handleCreate();
|
||||||
.then(function(data) {
|
await this.setReferenceData(created);
|
||||||
return outer.setReferenceData(data)
|
data = created;
|
||||||
.then(function() {
|
break;
|
||||||
return data;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
case 'Delete':
|
case 'Delete':
|
||||||
return outer.getReferenceData()
|
referenceData = await this.getReferenceData();
|
||||||
.then(function(data) {
|
data = await this.handleDelete(referenceData);
|
||||||
return outer.handleDelete(data);
|
break;
|
||||||
});
|
|
||||||
case 'Update':
|
case 'Update':
|
||||||
return outer.getReferenceData()
|
await this.getReferenceData();
|
||||||
.then(function(data) {
|
data = await this.handleUpdate();
|
||||||
return outer.handleUpdate();
|
break;
|
||||||
});
|
|
||||||
default:
|
default:
|
||||||
throw "Unrecognized RequestType [" + outer.event.RequestType + "]";
|
throw "Unrecognized RequestType [" + this.event.RequestType + "]";
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.then(function(data) {
|
response.send(this.event, this.context, response.SUCCESS, data);
|
||||||
response.send(outer.event, outer.context, response.SUCCESS, data);
|
}
|
||||||
})
|
catch(err) {
|
||||||
.catch(function(err) {
|
this.error(err);
|
||||||
outer.error(err);
|
}
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
When implemented, these should all return a Promise, which will then be completed by the handle()
|
When implemented, these should all return a Promise, which will then be completed by the handle()
|
||||||
|
@ -54,59 +50,56 @@ exports.Handler.prototype.handle = function() {
|
||||||
*/
|
*/
|
||||||
exports.Handler.prototype.handleCreate = function() {
|
exports.Handler.prototype.handleCreate = function() {
|
||||||
throw "create method not implemented";
|
throw "create method not implemented";
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
exports.Handler.prototype.handleDelete = function(referenceData) {
|
exports.Handler.prototype.handleDelete = function(referenceData) {
|
||||||
throw "delete method not implemented";
|
throw "delete method not implemented";
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.Handler.prototype.handleUpdate = function(referenceData) {
|
exports.Handler.prototype.handleUpdate = async function(referenceData) {
|
||||||
var self = this;
|
await this.handleDelete(referenceData);
|
||||||
return this.handleDelete(referenceData)
|
return await this.handleCreate();
|
||||||
.then(function() {
|
};
|
||||||
return self.handleCreate();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.Handler.prototype.error = function(message) {
|
exports.Handler.prototype.error = function(message) {
|
||||||
console.error(message);
|
console.error(message);
|
||||||
response.send(this.event, this.context, response.FAILED, { Error: message });
|
response.send(this.event, this.context, response.FAILED, { Error: message });
|
||||||
throw message;
|
throw message;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.Handler.prototype.getStackName = function() {
|
exports.Handler.prototype.getStackName = function() {
|
||||||
var functionName = this.context.functionName;
|
const functionName = this.context.functionName;
|
||||||
// Assume functionName is: stackName-resourceLogicalId-randomString.
|
// Assume functionName is: stackName-resourceLogicalId-randomString.
|
||||||
// Per http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html,
|
// Per http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html,
|
||||||
// resourceLogicalId cannot include a '-'; randomString seems to also be alphanumeric.
|
// resourceLogicalId cannot include a '-'; randomString seems to also be alphanumeric.
|
||||||
// Thus it seems safe to search for the two dashes in order to find the stackName.
|
// Thus it seems safe to search for the two dashes in order to find the stackName.
|
||||||
var i = functionName.lastIndexOf("-", functionName.lastIndexOf("-") - 1);
|
const i = functionName.lastIndexOf("-", functionName.lastIndexOf("-") - 1);
|
||||||
if (i >= 0)
|
if (i >= 0)
|
||||||
return functionName.substr(0, i);
|
return functionName.substr(0, i);
|
||||||
else
|
else
|
||||||
return functionName;
|
return functionName;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.Handler.prototype.getReferenceData = function() {
|
exports.Handler.prototype.getReferenceData = async function() {
|
||||||
return dynamoDB.getItemAsync(
|
const data = await dynamoDB.getItemAsync(
|
||||||
{
|
{
|
||||||
TableName: this.getStackName() + "-reference",
|
TableName: this.getStackName() + "-reference",
|
||||||
Key: helpers.formatForDynamo({
|
Key: helpers.formatForDynamo({
|
||||||
key: this.event.StackId + this.event.LogicalResourceId
|
key: this.event.StackId + this.event.LogicalResourceId
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
.then(function(data) {
|
|
||||||
data = helpers.formatFromDynamo(data);
|
|
||||||
if (data && data.Item && data.Item.value)
|
|
||||||
return data.Item.value;
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.Handler.prototype.setReferenceData = function(data) {
|
const formattedData = helpers.formatFromDynamo(data);
|
||||||
return dynamoDB.putItemAsync(
|
if (formattedData && formattedData.Item && formattedData.Item.value)
|
||||||
|
return formattedData.Item.value;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.Handler.prototype.setReferenceData = async function(data) {
|
||||||
|
return await dynamoDB.putItemAsync(
|
||||||
{
|
{
|
||||||
TableName: this.getStackName() + "-reference",
|
TableName: this.getStackName() + "-reference",
|
||||||
Item: helpers.formatForDynamo({
|
Item: helpers.formatForDynamo({
|
||||||
|
@ -115,4 +108,4 @@ exports.Handler.prototype.setReferenceData = function(data) {
|
||||||
}, true)
|
}, true)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
};
|
|
@ -10,7 +10,7 @@ exports.FAILED = "FAILED";
|
||||||
|
|
||||||
exports.send = function(event, context, responseStatus, responseData, physicalResourceId) {
|
exports.send = function(event, context, responseStatus, responseData, physicalResourceId) {
|
||||||
|
|
||||||
var responseBody = JSON.stringify({
|
const responseBody = JSON.stringify({
|
||||||
Status: responseStatus,
|
Status: responseStatus,
|
||||||
Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
|
Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
|
||||||
PhysicalResourceId: physicalResourceId || context.logStreamName,
|
PhysicalResourceId: physicalResourceId || context.logStreamName,
|
||||||
|
@ -22,15 +22,15 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
||||||
|
|
||||||
console.log("Response body:\n", responseBody);
|
console.log("Response body:\n", responseBody);
|
||||||
|
|
||||||
var https = require("https");
|
const https = require("https");
|
||||||
var url = require("url");
|
const url = require("url");
|
||||||
|
|
||||||
// This script comes from http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule
|
// This script comes from http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule
|
||||||
// The only change is this 'if' statement around this block of code, so it doesn't
|
// The only change is this 'if' statement around this block of code, so it doesn't
|
||||||
// fail when the ResonseURL is missing (i.e. during manual testing).
|
// fail when the ResonseURL is missing (i.e. during manual testing).
|
||||||
if (event.ResponseURL) {
|
if (event.ResponseURL) {
|
||||||
var parsedUrl = url.parse(event.ResponseURL);
|
const parsedUrl = url.parse(event.ResponseURL);
|
||||||
var options = {
|
const options = {
|
||||||
hostname: parsedUrl.hostname,
|
hostname: parsedUrl.hostname,
|
||||||
port: 443,
|
port: 443,
|
||||||
path: parsedUrl.path,
|
path: parsedUrl.path,
|
||||||
|
@ -41,7 +41,7 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var request = https.request(options, function(response) {
|
const request = https.request(options, function(response) {
|
||||||
console.log("Status code: " + response.statusCode);
|
console.log("Status code: " + response.statusCode);
|
||||||
console.log("Status message: " + response.statusMessage);
|
console.log("Status message: " + response.statusMessage);
|
||||||
context.done();
|
context.done();
|
||||||
|
@ -55,4 +55,4 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
||||||
request.write(responseBody);
|
request.write(responseBody);
|
||||||
request.end();
|
request.end();
|
||||||
}
|
}
|
||||||
}
|
};
|
|
@ -1,5 +1,3 @@
|
||||||
var Promise = require('bluebird');
|
|
||||||
|
|
||||||
// Translates from raw JSON into DynamoDB-formatted JSON. This is more than a
|
// Translates from raw JSON into DynamoDB-formatted JSON. This is more than a
|
||||||
// convenience thing: the original iteration of this accepted DyanamoDB-JSON Items,
|
// convenience thing: the original iteration of this accepted DyanamoDB-JSON Items,
|
||||||
// to avoid complications in translation. But when CloudFormation passes the parameters
|
// to avoid complications in translation. But when CloudFormation passes the parameters
|
||||||
|
@ -8,44 +6,44 @@ var Promise = require('bluebird');
|
||||||
// it is 'true' or 'false') as a 'BOOL' value. So some translation was needed, and
|
// it is 'true' or 'false') as a 'BOOL' value. So some translation was needed, and
|
||||||
// it seemed best to then simplify things for the client by accepting raw JSON.
|
// it seemed best to then simplify things for the client by accepting raw JSON.
|
||||||
exports.formatForDynamo = function(value, topLevel) {
|
exports.formatForDynamo = function(value, topLevel) {
|
||||||
var result = undefined;
|
let result = undefined;
|
||||||
if (value == 'true' || value == 'false') {
|
if (value == 'true' || value == 'false') {
|
||||||
result = {'BOOL': value == 'true'}
|
result = {'BOOL': value == 'true'};
|
||||||
} else if (!isNaN(value) && value.trim() != '') {
|
} else if (!isNaN(value) && value.trim() != '') {
|
||||||
result = {'N': value}
|
result = {'N': value};
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
var arr = [];
|
const arr = [];
|
||||||
for (var i = 0; i < value.length; i++) {
|
for (let i = 0; i < value.length; i++) {
|
||||||
arr.push(exports.formatForDynamo(value[i], false));
|
arr.push(exports.formatForDynamo(value[i], false));
|
||||||
}
|
}
|
||||||
result = {'L': arr};
|
result = {'L': arr};
|
||||||
} else if (typeof value === "object") {
|
} else if (typeof value === "object") {
|
||||||
var map = {};
|
const map = {};
|
||||||
Object.keys(value).forEach(function(key) {
|
Object.keys(value).forEach(function(key) {
|
||||||
map[key] = exports.formatForDynamo(value[key], false)
|
map[key] = exports.formatForDynamo(value[key], false);
|
||||||
});
|
});
|
||||||
if (topLevel) result = map;
|
if (topLevel) result = map;
|
||||||
else result = {'M': map}
|
else result = {'M': map};
|
||||||
} else {
|
} else {
|
||||||
result = {'S': value}
|
result = {'S': value};
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.formatFromDynamo = function(value) {
|
exports.formatFromDynamo = function(value) {
|
||||||
var result = undefined;
|
let result = undefined;
|
||||||
if (typeof value === "string" || typeof value === 'boolean' || typeof value === 'number') {
|
if (typeof value === "string" || typeof value === 'boolean' || typeof value === 'number') {
|
||||||
result = value;
|
result = value;
|
||||||
} else if (Array.isArray(value)) {
|
} else if (Array.isArray(value)) {
|
||||||
var arr = [];
|
const arr = [];
|
||||||
for (var i = 0; i < value.length; i++) {
|
for (let i = 0; i < value.length; i++) {
|
||||||
arr.push(exports.formatFromDynamo(value[i]));
|
arr.push(exports.formatFromDynamo(value[i]));
|
||||||
}
|
}
|
||||||
result = arr;
|
result = arr;
|
||||||
} else if (typeof value === "object") {
|
} else if (typeof value === "object") {
|
||||||
var map = {};
|
const map = {};
|
||||||
Object.keys(value).forEach(function(key) {
|
Object.keys(value).forEach(function(key) {
|
||||||
var v = exports.formatFromDynamo(value[key]);
|
const v = exports.formatFromDynamo(value[key]);
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'B':
|
case 'B':
|
||||||
throw "Unsupported Mongo type [B]";
|
throw "Unsupported Mongo type [B]";
|
||||||
|
@ -65,7 +63,7 @@ exports.formatFromDynamo = function(value) {
|
||||||
break;
|
break;
|
||||||
case 'NS':
|
case 'NS':
|
||||||
result = [];
|
result = [];
|
||||||
for (var i = 0; i < v.length; i++) {
|
for (let i = 0; i < v.length; i++) {
|
||||||
result.push(Number(value[i]));
|
result.push(Number(value[i]));
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
@ -85,16 +83,16 @@ exports.formatFromDynamo = function(value) {
|
||||||
throw "Unrecognized type [" + (typeof value) + "]";
|
throw "Unrecognized type [" + (typeof value) + "]";
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (!String.prototype.endsWith) {
|
if (!String.prototype.endsWith) {
|
||||||
String.prototype.endsWith = function(searchString, position) {
|
String.prototype.endsWith = function(searchString, position) {
|
||||||
var subjectString = this.toString();
|
const subjectString = this.toString();
|
||||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
|
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
|
||||||
position = subjectString.length;
|
position = subjectString.length;
|
||||||
}
|
}
|
||||||
position -= searchString.length;
|
position -= searchString.length;
|
||||||
var lastIndex = subjectString.indexOf(searchString, position);
|
const lastIndex = subjectString.indexOf(searchString, position);
|
||||||
return lastIndex !== -1 && lastIndex === position;
|
return lastIndex !== -1 && lastIndex === position;
|
||||||
};
|
};
|
||||||
}
|
}
|
33
src/package.json
Normal file
33
src/package.json
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"name": "cloudformation-helpers",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "A set of helper methods to fill in the gaps in existing CloudFormation support.",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/empathicqubit/cloudformation-helpers"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"cloudformation"
|
||||||
|
],
|
||||||
|
"author": "Ryan Martin, EmpathicQubit",
|
||||||
|
"license": "Apache 2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/empathicquibt/cloudformation-helpers/issues"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"package": "shx mkdir -p ../out && shx rm -rf ../out/cloudformation-helpers && shx cp -r ../src ../out/cloudformation-helpers && cd ../out/cloudformation-helpers && yarn install --production",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"deploy": "cd .. && cross-env-shell \"aws cloudformation package --s3-bucket $CF_S3_BUCKET --template-file create_cloudformation_helper_functions.template.yml --output-template-file out/create_cloudformation_helper_functions.template.yml\" && cross-env-shell \"aws cloudformation deploy --template-file out/create_cloudformation_helper_functions.template.yml --capabilities CAPABILITY_NAMED_IAM --stack-name $CF_STACK_NAME\"",
|
||||||
|
"publish": "npm-run-all lint package deploy"
|
||||||
|
},
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"util-promisifyall": "^1.0.6"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"cross-env": "^7.0.2",
|
||||||
|
"eslint": "^7.2.0",
|
||||||
|
"npm-run-all": "^4.1.5",
|
||||||
|
"shx": "^0.3.2"
|
||||||
|
}
|
||||||
|
}
|
1231
src/yarn.lock
Normal file
1231
src/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue