Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1132 lines (1132 sloc) 34.1 KB
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"StopWords": {
"Type": "String",
"Description": "A regular expression that is used to match the message as a 'stop' message."
},
"StageWhitelist": {
"Type": "String",
"Description": "A comma-delimited list (no spaces) of phone numbers to whitelist in the stage environment."
}
},
"Resources": {
"DB": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"AttributeDefinitions": [
{
"AttributeName": "Id",
"AttributeType": "S"
},
{
"AttributeName": "Type",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "Id",
"KeyType": "HASH"
},
{
"AttributeName": "Type",
"KeyType": "RANGE"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"TableName": {
"Ref": "AWS::StackName"
}
}
},
"StageDB": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"AttributeDefinitions": [
{
"AttributeName": "Id",
"AttributeType": "S"
},
{
"AttributeName": "Type",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "Id",
"KeyType": "HASH"
},
{
"AttributeName": "Type",
"KeyType": "RANGE"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"TableName": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"stage"
]
]
}
}
},
"NotificationQueue": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"add",
"notification"
]
]
}
}
},
"StageNotificationQueue": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"add",
"notification",
"stage"
]
]
}
}
},
"LambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "LogWriter",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
},
{
"PolicyName": "DBWriter",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Resource": [
{
"Fn::Join": [
"",
[
"arn:aws:dynamodb:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":table/",
{
"Ref": "DB"
}
]
]
},
{
"Fn::Join": [
"",
[
"arn:aws:dynamodb:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":table/",
{
"Ref": "DB"
},
"-stage"
]
]
}
]
}
]
}
},
{
"PolicyName": "NotificationPublisher",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": [
{
"Ref": "NotificationQueue"
},
{
"Ref": "StageNotificationQueue"
}
]
}
]
}
}
]
},
"DependsOn": [
"DB"
]
},
"Api": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": {
"Ref": "AWS::StackName"
},
"Description": {
"Fn::Join": [
" ",
[
"API endpoints for",
{
"Ref": "AWS::StackName"
}
]
]
}
}
},
"MoMessageResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"Api",
"RootResourceId"
]
},
"PathPart": "mo_message",
"RestApiId": {
"Ref": "Api"
}
},
"DependsOn": [
"Api"
]
},
"MoMessagePost": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"ResourceId": {
"Ref": "MoMessageResource"
},
"RestApiId": {
"Ref": "Api"
},
"HttpMethod": "POST",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/arn:aws:lambda:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":function:",
{
"Ref": "MoMessageFunction"
},
"/invocations"
]
]
}
}
},
"DependsOn": [
"Api",
"MoMessageFunction",
"MoMessageResource"
]
},
"NotificationTypeResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"Api",
"RootResourceId"
]
},
"PathPart": "{notification_type}",
"RestApiId": {
"Ref": "Api"
}
},
"DependsOn": [
"Api"
]
},
"BlacklistResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Ref": "NotificationTypeResource"
},
"PathPart": "{blacklist_id}",
"RestApiId": {
"Ref": "Api"
}
},
"DependsOn": [
"NotificationTypeResource"
]
},
"BlacklistDelete": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"ResourceId": {
"Ref": "BlacklistResource"
},
"RestApiId": {
"Ref": "Api"
},
"HttpMethod": "DELETE",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/arn:aws:lambda:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":function:",
{
"Ref": "DeleteFunction"
},
"/invocations"
]
]
}
}
},
"DependsOn": [
"Api",
"DeleteFunction",
"BlacklistResource"
]
},
"BlacklistGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"ResourceId": {
"Ref": "BlacklistResource"
},
"RestApiId": {
"Ref": "Api"
},
"HttpMethod": "GET",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/arn:aws:lambda:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":function:",
{
"Ref": "GetFunction"
},
"/invocations"
]
]
}
}
},
"DependsOn": [
"Api",
"GetFunction",
"BlacklistResource"
]
},
"BlacklistPut": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"ResourceId": {
"Ref": "BlacklistResource"
},
"RestApiId": {
"Ref": "Api"
},
"HttpMethod": "PUT",
"AuthorizationType": "NONE",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Join": [
"",
[
"arn:aws:apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/arn:aws:lambda:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":function:",
{
"Ref": "PutFunction"
},
"/invocations"
]
]
}
}
},
"DependsOn": [
"Api",
"PutFunction",
"BlacklistResource"
]
},
"Deployment": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "Api"
}
},
"DependsOn": [
"BlacklistDelete",
"BlacklistGet",
"BlacklistPut",
"MoMessagePost"
]
},
"ProdStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "Deployment"
},
"Description": {
"Fn::Join": [
" ",
[
"Production version of the",
{
"Ref": "AWS::StackName"
},
"API"
]
]
},
"RestApiId": {
"Ref": "Api"
},
"StageName": "prod",
"Variables": {
"NOTIFICATION_QUEUE": {
"Ref": "NotificationQueue"
},
"TABLE_NAME": {
"Ref": "DB"
}
}
},
"DependsOn": [
"Api",
"DB",
"Deployment"
]
},
"StageStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "Deployment"
},
"Description": {
"Fn::Join": [
" ",
[
"Production version of the",
{
"Ref": "AWS::StackName"
},
"API"
]
]
},
"RestApiId": {
"Ref": "Api"
},
"StageName": "stage",
"Variables": {
"NOTIFICATION_QUEUE": {
"Ref": "StageNotificationQueue"
},
"TABLE_NAME": {
"Ref": "StageDB"
},
"WHITELIST": {
"Ref": "StageWhitelist"
}
}
},
"DependsOn": [
"Api",
"StageDB",
"Deployment"
]
},
"DeleteFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"const AWS = require('aws-sdk'),",
" dynamo = new AWS.DynamoDB();",
"",
"exports.handler = (event, context, callback) => {",
" console.info(toMessageString(event));",
" const blacklistId = sanitizeNumber(event.pathParameters.blacklist_id);",
" withSupportedType(event, context, callback, function(notificationType) {",
" dynamo.updateItem({",
" TableName: event.stageVariables.TABLE_NAME,",
" Key: { Id: { S: blacklistId }, Type: { S: notificationType } },",
" ExpressionAttributeNames: { '#l': 'Log' },",
" ExpressionAttributeValues: {",
" ':d': { S: (new Date()).toISOString() },",
" ':m': { SS: [ toMessageString(event) ] }",
" },",
" UpdateExpression: 'SET DeletedAt=:d, UpdatedAt=:d ADD #l :m'",
" }, function(err, data) {",
" if (err) return callback(err);",
" callback(null, { statusCode: 200, body: JSON.stringify({ id: blacklistId }) });",
" })",
" });",
"}",
"",
"function withSupportedType(event, context, lambdaCallback, callback) {",
" const supportedTypes = ['sms'];",
" if (supportedTypes.indexOf(event.pathParameters.notification_type.toLowerCase()) >= 0) {",
" callback(event.pathParameters.notification_type.toLowerCase());",
" } else {",
" lambdaCallback(null, { statusCode: 400, body: JSON.stringify({ message: 'Notification type [' + event.pathParameters.notification_type + '] not supported.' }) });",
" }",
"}",
"",
"function sanitizeNumber(raw) {",
" var numbers = raw.replace(/[^\\d]+/g, '');",
" if (numbers.match(/^1\\d{10}$/)) numbers = numbers.substring(1, 11);",
" return numbers;",
"}",
"",
"function toMessageString(event) {",
" return JSON.stringify({",
" httpMethod: event.httpMethod,",
" pathParameters: event.pathParameters,",
" queryStringParameters: event.queryStringParameters",
" })",
"}",
""
]
]
}
},
"Environment": {
"Variables": {}
},
"Runtime": "nodejs6.10",
"Timeout": "30"
},
"DependsOn": [
"LambdaRole"
]
},
"DeleteFunctionPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"DeleteFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "Api"
},
"/*/*/*"
]
]
}
},
"DependsOn": [
"DeleteFunction",
"Api"
]
},
"GetFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"const AWS = require('aws-sdk'),",
" dynamo = new AWS.DynamoDB();",
"",
"exports.handler = (event, context, callback) => {",
" console.info(toMessageString(event));",
" const blacklistId = sanitizeNumber(event.pathParameters.blacklist_id);",
" withSupportedType(event, context, callback, function(notificationType) {",
" dynamo.getItem({",
" TableName: event.stageVariables.TABLE_NAME,",
" Key: { Id: { S: blacklistId }, Type: { S: notificationType } }",
" }, function(err, data) {",
" if (err) return callback(err);",
" if ((data && data.Item && afterNow(data, \"DeletedAt\") && matchesFilter(event, data.Item)) || !onWhitelist(blacklistId, event.stageVariables.WHITELIST)) {",
" callback(null, { statusCode: 200, body: JSON.stringify({ id: blacklistId }) });",
" } else {",
" callback(null, { statusCode: 404, body: JSON.stringify({ message: \"Entry not blacklisted\" }) });",
" }",
" })",
" });",
"}",
"",
"function afterNow(data, propertyName) {",
" if (data && data.Item && data.Item[propertyName] && data.Item[propertyName].S) {",
" return Date.parse(data.Item[propertyName].S) >= new Date();",
" } else {",
" return true;",
" }",
"}",
"",
"function matchesFilter(event, item) {",
" const queryStringParams = event.queryStringParameters || {};",
" return queryStringParams.length == 0 || Object.keys(queryStringParams).every(function(key) {",
" const k = sanitizeString(key),",
" values = item['MetaData.' + k] || { SS: [] };",
" return !values || !values.SS || values.SS.includes(queryStringParams[key]);",
" });",
"}",
"",
"// Set the whitelist in staging to only allow certain entries.",
"function onWhitelist(blacklistId, whitelist) {",
" if (whitelist && whitelist.trim() != '') {",
" const whitelisted = whitelist.split(',');",
" return whitelisted.findIndex(function(item) { return blacklistId == item.trim(); }) >= 0;",
" } else {",
" return true;",
" }",
"}",
"",
"function withSupportedType(event, context, lambdaCallback, callback) {",
" const supportedTypes = ['sms'];",
" if (supportedTypes.indexOf(event.pathParameters.notification_type.toLowerCase()) >= 0) {",
" callback(event.pathParameters.notification_type.toLowerCase());",
" } else {",
" lambdaCallback(null, { statusCode: 400, body: JSON.stringify({ message: 'Notification type [' + event.pathParameters.notification_type + '] not supported.' }) });",
" }",
"}",
"",
"function sanitizeNumber(raw) {",
" var numbers = raw.replace(/[^\\d]+/g, '');",
" if (numbers.match(/^1\\d{10}$/)) numbers = numbers.substring(1, 11);",
" return numbers;",
"}",
"",
"function sanitizeString(raw) {",
" return raw.replace(/\\W+/g, '_').toLowerCase();",
"}",
"",
"function toMessageString(event) {",
" return JSON.stringify({",
" httpMethod: event.httpMethod,",
" pathParameters: event.pathParameters,",
" queryStringParameters: event.queryStringParameters",
" })",
"}",
""
]
]
}
},
"Environment": {
"Variables": {}
},
"Runtime": "nodejs6.10",
"Timeout": "30"
},
"DependsOn": [
"LambdaRole"
]
},
"GetFunctionPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"GetFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "Api"
},
"/*/*/*"
]
]
}
},
"DependsOn": [
"GetFunction",
"Api"
]
},
"MoMessageFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"const AWS = require('aws-sdk'),",
" dynamo = new AWS.DynamoDB(),",
" sns = new AWS.SNS();",
"",
"exports.handler = (event, context, callback) => {",
" const moMessageXml = event.body;",
" console.info(moMessageXml);",
" if (messageMatch = moMessageXml.match(/<message>(.*)<\\/message>/)) {",
" if (messageMatch[1].toLowerCase().match(process.env.STOP_WORDS)) { // STOP_WORDS should be a Regex",
" if (originNumberMatch = moMessageXml.match(/<\\s*source\\s+.*?address\\s*=\\s*[\"'](.*?)[\"']/)) {",
" const originNumber = sanitizeNumber(originNumberMatch[1]),",
" shortCodeMatch = moMessageXml.match(/<\\s*destination\\s+.*?address\\s*=\\s*[\"'](.*?)[\"']/),",
" shortCode = (shortCodeMatch ? shortCodeMatch[1] : '');",
" withQueryStringAsMetaData(",
" event.queryStringParameters,",
" { '#l': 'Log', '#short_code': 'MetaData.short_code' },",
" {",
" ':d': { S: (new Date()).toISOString() },",
" ':l': { SS: [ toMessageString(event) ] },",
" ':short_code': { SS: [ shortCode ] }",
" },",
" '#l :l, #short_code :short_code',",
" function(attrNames, attrValues, addExpr) {",
" dynamo.updateItem({",
" TableName: event.stageVariables.TABLE_NAME,",
" Key: { Id: { S: originNumber }, Type: { S: 'sms' } },",
" ExpressionAttributeNames: attrNames,",
" ExpressionAttributeValues: attrValues,",
" UpdateExpression: 'SET UpdatedAt=:d ADD ' + addExpr + ' REMOVE DeletedAt'",
" }, function(err, data) {",
" if (err) return callback(err);",
" sns.publish({",
" Message: JSON.stringify({",
" phone_number: originNumber",
" }),",
" TopicArn: event.stageVariables.NOTIFICATION_QUEUE",
" }, function(err, data) {",
" if (err) return callback(err);",
" callback(null, { statusCode: 200, body: JSON.stringify({ id: originNumber }) });",
" })",
" });",
" }",
" );",
" } else {",
" callback(null, { statusCode: 400, body: JSON.stringify({ message: 'Missing source address' }) });",
" }",
" } else {",
" callback(null, { statusCode: 200, body: JSON.stringify({ id: '' }) });",
" }",
" } else {",
" callback(null, { statusCode: 400, body: JSON.stringify({ message: 'Invalid message xml' }) });",
" }",
"}",
"",
"function sanitizeNumber(raw) {",
" var numbers = raw.replace(/[^\\d]+/g, '');",
" if (numbers.match(/^1\\d{10}$/)) numbers = numbers.substring(1, 11);",
" return numbers;",
"}",
"",
"function sanitizeString(raw) {",
" return raw.replace(/\\W+/g, '_').toLowerCase();",
"}",
"",
"function withQueryStringAsMetaData(queryStringParams, attrNames, attrValues, addExpr, callback) {",
" queryStringParams = queryStringParams || {};",
" Object.keys(queryStringParams).forEach(function(key) {",
" const k = sanitizeString(key);",
" attrNames['#' + k] = 'MetaData.' + k;",
" attrValues[':' + k] = { SS: [ queryStringParams[key] ]};",
" if (addExpr.trim() != '') addExpr += ', ';",
" addExpr += '#' + k + ' :' + k;",
" });",
" callback(attrNames, attrValues, addExpr);",
"}",
"",
"function toMessageString(event) {",
" return JSON.stringify({",
" body: event.body,",
" queryStringParameters: event.queryStringParameters",
" })",
"}"
]
]
}
},
"Environment": {
"Variables": {
"STOP_WORDS": {
"Ref": "StopWords"
}
}
},
"Runtime": "nodejs6.10",
"Timeout": "30"
},
"DependsOn": [
"LambdaRole"
]
},
"MoMessageFunctionPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MoMessageFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "Api"
},
"/*/*/*"
]
]
}
},
"DependsOn": [
"MoMessageFunction",
"Api"
]
},
"PutFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Handler": "index.handler",
"Role": {
"Fn::GetAtt": [
"LambdaRole",
"Arn"
]
},
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"const AWS = require('aws-sdk'),",
" dynamo = new AWS.DynamoDB();",
"",
"exports.handler = (event, context, callback) => {",
" console.info(toMessageString(event));",
" const blacklistId = sanitizeNumber(event.pathParameters.blacklist_id);",
" withSupportedType(event, context, callback, function(notificationType) {",
" withQueryStringAsMetaData(",
" event.queryStringParameters,",
" { '#l': 'Log' },",
" {",
" ':d': { S: (new Date()).toISOString() },",
" ':l': { SS: [ toMessageString(event) ] }",
" },",
" '#l :l',",
" function(attrNames, attrValues, addExpr) {",
" dynamo.updateItem({",
" TableName: event.stageVariables.TABLE_NAME,",
" Key: { Id: { S: blacklistId }, Type: { S: notificationType } },",
" ExpressionAttributeNames: attrNames,",
" ExpressionAttributeValues: attrValues,",
" UpdateExpression: 'SET UpdatedAt=:d ADD ' + addExpr + ' REMOVE DeletedAt'",
" }, function(err, data) {",
" if (err) return callback(err);",
" callback(null, { statusCode: 200, body: JSON.stringify({ id: blacklistId }) });",
" })",
" }",
" );",
" });",
"}",
"",
"function withQueryStringAsMetaData(queryStringParams, attrNames, attrValues, addExpr, callback) {",
" queryStringParams = queryStringParams || {};",
" Object.keys(queryStringParams).forEach(function(key) {",
" const k = sanitizeString(key);",
" attrNames['#' + k] = 'MetaData.' + k;",
" attrValues[':' + k] = { SS: [ queryStringParams[key] ]};",
" if (addExpr.trim() != '') addExpr += ', ';",
" addExpr += '#' + k + ' :' + k;",
" });",
" callback(attrNames, attrValues, addExpr);",
"}",
"",
"function withSupportedType(event, context, lambdaCallback, callback) {",
" const supportedTypes = ['sms'];",
" if (supportedTypes.indexOf(event.pathParameters.notification_type.toLowerCase()) >= 0) {",
" callback(event.pathParameters.notification_type.toLowerCase());",
" } else {",
" lambdaCallback(null, { statusCode: 400, body: JSON.stringify({ message: 'Notification type [' + event.pathParameters.notification_type + '] not supported.' }) });",
" }",
"}",
"",
"function sanitizeNumber(raw) {",
" var numbers = raw.replace(/[^\\d]+/g, '');",
" if (numbers.match(/^1\\d{10}$/)) numbers = numbers.substring(1, 11);",
" return numbers;",
"}",
"",
"function sanitizeString(raw) {",
" return raw.replace(/\\W+/g, '_').toLowerCase();",
"}",
"",
"function toMessageString(event) {",
" return JSON.stringify({",
" httpMethod: event.httpMethod,",
" pathParameters: event.pathParameters,",
" queryStringParameters: event.queryStringParameters",
" })",
"}",
""
]
]
}
},
"Environment": {
"Variables": {}
},
"Runtime": "nodejs6.10",
"Timeout": "30"
},
"DependsOn": [
"LambdaRole"
]
},
"PutFunctionPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"PutFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:aws:execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "Api"
},
"/*/*/*"
]
]
}
},
"DependsOn": [
"PutFunction",
"Api"
]
}
},
"Outputs": {}
}