├── .gitignore ├── Front ├── dashboard │ ├── error.html │ ├── index.html │ ├── narrow.css │ ├── refresh.js │ └── vote.css └── votes │ ├── controller.js │ ├── index.css │ └── index.html ├── Lambdas ├── aggregate-votes │ ├── app.js │ └── serverless.yml └── receive-vote │ ├── app.js │ ├── package-lock.json │ ├── package.json │ └── serverless.yml ├── README.md ├── Terraform ├── dynamoDBAgregateVotes.tf ├── dynamoDBVotes.tf ├── outputs.tf ├── s3StaticWebsite.tf ├── state.tf └── vars.tf └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | node_modules/* 4 | jspm_packages 5 | 6 | # Serverless directories 7 | .serverless 8 | 9 | #terraform 10 | *.tfstate* 11 | *.tfvars 12 | *.terraform/ 13 | 14 | *Icon* -------------------------------------------------------------------------------- /Front/dashboard/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Error Page 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 |
36 |
37 |

My Vote App

38 |
39 |
40 | 41 |
42 |

Oops.

43 |

We're sorry, something went wrong.

44 |
45 | 46 |
47 |
48 |

Demo created for AWS Startup Collection on Medium.

49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Front/dashboard/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Vote App 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
31 |

VoteApp

32 |

Text Your Vote For RED, GREEN, or BLUE to

33 |

(123) 456-7890

34 |

This is a simple demonstration of Lambda, a compute service from AWS that runs your code in response to events.

35 |
36 | 37 |
38 |
39 |
40 | 41 |
42 |
43 |

Lambda monitors a DynamoDB table, which is populated with votes generated by texting RED, BLUE, or GREEN to a phone number provided by our friends at Twilio. 44 | When the table is updated, Lambda invokes code to query the table and aggregate the votes, which are used to create the graph above. Read more at https://medium.com/aws-activate-startup-blog.

45 |
46 |
47 | 48 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Front/dashboard/narrow.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | html, 3 | body { 4 | background-color: #333; 5 | color: #fff; 6 | text-align: center; 7 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 8 | padding-top: 20px; 9 | padding-bottom: 20px; 10 | } 11 | 12 | /* Everything but the jumbotron gets side spacing for mobile first views */ 13 | .header, 14 | .marketing, 15 | .footer { 16 | padding-right: 15px; 17 | padding-left: 15px; 18 | } 19 | 20 | /* Custom page header */ 21 | .header { 22 | border-bottom: 1px solid #e5e5e5; 23 | } 24 | /* Make the masthead heading the same height as the navigation */ 25 | .header h3 { 26 | padding-bottom: 19px; 27 | margin-top: 0; 28 | margin-bottom: 0; 29 | line-height: 40px; 30 | } 31 | 32 | /* Custom page footer */ 33 | .footer { 34 | padding-top: 19px; 35 | color: #777; 36 | border-top: 1px solid #e5e5e5; 37 | } 38 | 39 | /* Customize container */ 40 | @media (min-width: 768px) { 41 | .container { 42 | max-width: 730px; 43 | } 44 | } 45 | .container-narrow > hr { 46 | margin: 30px 0; 47 | } 48 | 49 | /* Main marketing message and sign up button */ 50 | .jumbotron { 51 | text-align: center; 52 | border-bottom: 1px solid #e5e5e5; 53 | } 54 | .jumbotron .btn { 55 | padding: 14px 24px; 56 | font-size: 21px; 57 | } 58 | 59 | /* Supporting marketing content */ 60 | .marketing { 61 | margin: 40px 0; 62 | } 63 | .marketing p + h4 { 64 | margin-top: 28px; 65 | } 66 | 67 | /* Responsive: Portrait tablets and up */ 68 | @media screen and (min-width: 768px) { 69 | /* Remove the padding we set earlier */ 70 | .header, 71 | .marketing, 72 | .footer { 73 | padding-right: 0; 74 | padding-left: 0; 75 | } 76 | /* Space out the masthead */ 77 | .header { 78 | margin-bottom: 30px; 79 | } 80 | /* Remove the bottom border on the jumbotron for visual effect */ 81 | .jumbotron { 82 | border-bottom: 0; 83 | } 84 | } -------------------------------------------------------------------------------- /Front/dashboard/refresh.js: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use 4 | this file except in compliance with the License. A copy of the License is 5 | located at 6 | 7 | http://aws.amazon.com/apache2.0/ 8 | 9 | or in the "license" file accompanying this file. This file is distributed on an 10 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 11 | implied. See the License for the specific language governing permissions and 12 | limitations under the License. */ 13 | 14 | // Region and IdentityPoolId should be set to your own values 15 | AWS.config.region = 'us-east-1'; // Region 16 | // AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 17 | // IdentityPoolId: 'arn:aws:cognito-idp:us-east-1:885643748287:userpool/us-east-1_h5Oz0Cs5h', 18 | // }); 19 | AWS.config.update({ 20 | accessKeyId: "", 21 | secretAccessKey: "", 22 | "region": "us-east-1" 23 | }); 24 | 25 | var dynamodb = new AWS.DynamoDB(); 26 | var params = { TableName: 'VoteAppAggregates' }; 27 | 28 | /* Create the context for applying the chart to the HTML canvas */ 29 | var ctx = $("#graph").get(0).getContext("2d"); 30 | 31 | /* Set the options for our chart */ 32 | var options = { segmentShowStroke : false, 33 | animateScale: true, 34 | percentageInnerCutout : 50, 35 | showToolTips: true, 36 | tooltipEvents: ["mousemove", "touchstart", "touchmove"], 37 | tooltipFontColor: "#fff", 38 | animationEasing : 'easeOutCirc' 39 | } 40 | 41 | /* Set the initial data */ 42 | var init = [ 43 | { 44 | value: 1, 45 | color: "#e74c3c", 46 | highlight: "#c0392b", 47 | label: "Red" 48 | }, 49 | { 50 | value: 1, 51 | color: "#2ecc71", 52 | highlight: "#27ae60", 53 | label: "Green" 54 | }, 55 | { 56 | value: 1, 57 | color: "#3498db", 58 | highlight: "#2980b9", 59 | label: "Blue" 60 | } 61 | ]; 62 | 63 | graph = new Chart(ctx).Doughnut(init, options); 64 | 65 | $(function() { 66 | getData(); 67 | $.ajaxSetup({ cache: false }); 68 | /* Get the data every 3 seconds */ 69 | setInterval(getData, 3000); 70 | }); 71 | 72 | /* Makes a scan of the DynamoDB table to set a data object for the chart */ 73 | function getData() { 74 | dynamodb.scan(params, function(err, data) { 75 | if (err) { 76 | console.log(err); 77 | return null; 78 | } else { 79 | var redCount = 0; 80 | var greenCount = 0; 81 | var blueCount = 0; 82 | 83 | for (var i in data['Items']) { 84 | if (data['Items'][i]['VotedFor']['S'] == "RED") { 85 | redCount = parseInt(data['Items'][i]['Vote']['N']); 86 | } 87 | if (data['Items'][i]['VotedFor']['S'] == "GREEN") { 88 | greenCount = parseInt(data['Items'][i]['Vote']['N']); 89 | } 90 | if (data['Items'][i]['VotedFor']['S'] == "BLUE") { 91 | blueCount = parseInt(data['Items'][i]['Vote']['N']); 92 | } 93 | } 94 | 95 | var data = [ 96 | { 97 | value: redCount, 98 | color:"#e74c3c", 99 | highlight: "#c0392b", 100 | label: "Red" 101 | }, 102 | { 103 | value: greenCount, 104 | color: "#2ecc71", 105 | highlight: "#27ae60", 106 | label: "Green" 107 | }, 108 | { 109 | value: blueCount, 110 | color: "#3498db", 111 | highlight: "#2980b9", 112 | label: "Blue" 113 | } 114 | ]; 115 | 116 | /* Only update if we have new values (preserves tooltips) */ 117 | if ( graph.segments[0].value != data[0].value || 118 | graph.segments[1].value != data[1].value || 119 | graph.segments[2].value != data[2].value 120 | ) 121 | { 122 | graph.segments[0].value = data[0].value; 123 | graph.segments[1].value = data[1].value; 124 | graph.segments[2].value = data[2].value; 125 | graph.update(); 126 | } 127 | 128 | } 129 | }); 130 | } 131 | -------------------------------------------------------------------------------- /Front/dashboard/vote.css: -------------------------------------------------------------------------------- 1 | #graph { 2 | margin-bottom: 20px; 3 | } 4 | 5 | a { 6 | color: #ff9900; 7 | } 8 | 9 | .lead { 10 | color: #c6c6c6; 11 | } 12 | -------------------------------------------------------------------------------- /Front/votes/controller.js: -------------------------------------------------------------------------------- 1 | // url: "https://bv36gqslc9.execute-api.us-east-1.amazonaws.com/dev/receivevote", 2 | 3 | // $(document).ready(function(){ 4 | $('#red-button').click(function(){ 5 | $.ajax({ 6 | type: "POST", 7 | crossDomain: true, 8 | url: "https://bv36gqslc9.execute-api.us-east-1.amazonaws.com/dev/receivevote", 9 | data: '{"Body": "RED"}', 10 | dataType: 'json', 11 | success: function(responseData, textStatus, jqXHR) { 12 | console.log(responseData); 13 | console.log(textStatus); 14 | swal("Obrigado pela Votação!", "Você escolheu vermelho!", "success"); 15 | }, 16 | error: function (responseData, textStatus, errorThrown) { 17 | alert('POST failed.'); 18 | } 19 | }); 20 | }); 21 | 22 | $('#green-button').click(function(){ 23 | $.ajax({ 24 | type: "POST", 25 | crossDomain: true, 26 | url: "https://bv36gqslc9.execute-api.us-east-1.amazonaws.com/dev/receivevote", 27 | data: '{"Body": "GREEN"}', 28 | dataType: 'json', 29 | success: function(responseData, textStatus, jqXHR) { 30 | console.log(responseData); 31 | console.log(textStatus); 32 | swal("Obrigado pela Votação!", "Você escolheu verde!", "success"); 33 | }, 34 | error: function (responseData, textStatus, errorThrown) { 35 | console.log(errorThrown) 36 | console.log(textStatus) 37 | console.log(responseData) 38 | alert('POST failed.'); 39 | } 40 | }); 41 | }); 42 | 43 | $('#blue-button').click(function(){ 44 | $.ajax({ 45 | type: "POST", 46 | crossDomain: true, 47 | url: "https://bv36gqslc9.execute-api.us-east-1.amazonaws.com/dev/receivevote", 48 | data: '{"Body": "BLUE"}', 49 | dataType: 'json', 50 | success: function(responseData, textStatus, jqXHR) { 51 | console.log(responseData); 52 | console.log(textStatus); 53 | swal("Obrigado pela Votação!", "Você escolheu azul!", "success"); 54 | }, 55 | error: function (responseData, textStatus, errorThrown) { 56 | alert('POST failed.'); 57 | } 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /Front/votes/index.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | html, 3 | body { 4 | background-color: #333; 5 | color: #fff; 6 | text-align: center; 7 | text-shadow: 0 1px 3px rgba(0,0,0,.5); 8 | padding-bottom: 20px; 9 | padding-top: 10px; 10 | } 11 | 12 | #red-button { 13 | background-color: #e74c3c; 14 | } 15 | #blue-button { 16 | background-color: #3498db; 17 | } 18 | #green-button { 19 | background-color: #2ecc71; 20 | } 21 | 22 | .swal-modal { 23 | width: 80%; 24 | } 25 | .swal-footer { 26 | text-align: center; 27 | } 28 | .swal-title { 29 | color: black; 30 | } 31 | .swal-text { 32 | color: black; 33 | } 34 | /* Custom, iPhone Retina */ 35 | @media only screen and (min-width : 320px) { 36 | h1 { 37 | margin-bottom: 13%; 38 | } 39 | .col-style { 40 | padding: 30px; 41 | } 42 | .button-style { 43 | width: 75%; 44 | height: auto; 45 | border: none; 46 | padding: 8%; 47 | } 48 | } 49 | 50 | /* Extra Small Devices, Phones */ 51 | @media only screen and (min-width : 480px) { 52 | 53 | } 54 | 55 | /* Small Devices, Tablets */ 56 | @media only screen and (min-width : 768px) { 57 | 58 | } 59 | 60 | /* Medium Devices, Desktops */ 61 | @media only screen and (min-width : 992px) { 62 | 63 | } 64 | 65 | /* Large Devices, Wide Screens */ 66 | @media only screen and (min-width : 1200px) { 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Front/votes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Vote App 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | 26 | 27 | 28 |
29 |
30 |

Votação

31 |
32 |
33 |
34 |
35 |
36 | 37 |
38 |
39 |
40 |
41 | 42 |
43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /Lambdas/aggregate-votes/app.js: -------------------------------------------------------------------------------- 1 | console.log('Loading event'); 2 | var AWS = require('aws-sdk'); 3 | var dynamodb = new AWS.DynamoDB(); 4 | 5 | exports.handler = function(event, context) { 6 | 7 | var totalRed = 0; 8 | var totalGreen = 0; 9 | var totalBlue = 0; 10 | 11 | event.Records.forEach(function(record) { 12 | 13 | var votedForHash = record.dynamodb['NewImage']['VotedFor']['S']; 14 | var numVotes = record.dynamodb['NewImage']['Votes']['N']; 15 | 16 | // Determine the color on which to add the vote 17 | if (votedForHash.indexOf("RED") > -1) { 18 | votedFor = "RED"; 19 | totalRed += parseInt(numVotes); 20 | } else if (votedForHash.indexOf("GREEN") > -1) { 21 | votedFor = "GREEN"; 22 | totalGreen += parseInt(numVotes); 23 | } else if (votedForHash.indexOf("BLUE") > -1) { 24 | votedFor = "BLUE"; 25 | totalBlue += parseInt(numVotes); 26 | } else { 27 | console.log("Invalid vote: ", votedForHash); 28 | } 29 | }); 30 | 31 | // Update the aggregation table with the total of RED, GREEN, and BLUE 32 | // votes received from this series of updates 33 | 34 | var aggregatesTable = 'VoteAppAggregates'; 35 | if (totalRed > 0) updateAggregateForColor("RED", totalRed); 36 | if (totalBlue > 0) updateAggregateForColor("BLUE", totalBlue); 37 | if (totalGreen > 0) updateAggregateForColor("GREEN", totalGreen); 38 | 39 | console.log('Updating Aggregates Table', totalRed); 40 | 41 | function updateAggregateForColor(votedFor, numVotes) { 42 | console.log("Updating Aggregate Color ", votedFor); 43 | console.log("For NumVotes: ", numVotes); 44 | 45 | dynamodb.updateItem({ 46 | 'TableName': aggregatesTable, 47 | 'Key': { 'VotedFor' : { 'S': votedFor }}, 48 | 'UpdateExpression': 'add #vote :x', 49 | 'ExpressionAttributeNames': {'#vote' : 'Vote'}, 50 | 'ExpressionAttributeValues': { ':x' : { "N" : numVotes.toString() } } 51 | }, function(err, data) { 52 | if (err) { 53 | console.log(err); 54 | context.fail("Error updating Aggregates table: ", err) 55 | } else { 56 | console.log("Vote received for %s", votedFor); 57 | context.succeed("Successfully processed " + event.Records.length + " records."); 58 | } 59 | }); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /Lambdas/aggregate-votes/serverless.yml: -------------------------------------------------------------------------------- 1 | service: aggregate-vote 2 | 3 | provider: 4 | name: aws 5 | runtime: nodejs10.x 6 | iamRoleStatements: # permissions for all of your functions can be set here 7 | - Effect: Allow 8 | Action: # Gives permission to DynamoDB tables in a specific region 9 | - dynamodb:DescribeTable 10 | - dynamodb:Query 11 | - dynamodb:Scan 12 | - dynamodb:GetItem 13 | - dynamodb:PutItem 14 | - dynamodb:UpdateItem 15 | - dynamodb:DeleteItem 16 | Resource: "arn:aws:dynamodb:us-east-1:*:*" 17 | 18 | functions: 19 | aggregate-vote: 20 | handler: app.handler 21 | memorySize: 128 22 | timeout: 10 23 | events: 24 | - stream: 25 | arn: arn:aws:dynamodb:us-east-1:867061838226:table/VoteApp/stream/2020-03-28T11:20:04.055 26 | batchSize: 1 27 | -------------------------------------------------------------------------------- /Lambdas/receive-vote/app.js: -------------------------------------------------------------------------------- 1 | console.log('Loading event'); 2 | var AWS = require('aws-sdk'); 3 | var dynamodb = new AWS.DynamoDB(); 4 | 5 | exports.handler = function(event, context, callback) { 6 | var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10', region: 'us-east-1'}); 7 | 8 | /* Make sure we have a valid vote (one of [RED, GREEN, BLUE]) */ 9 | var votedFor = JSON.parse(event['body'])['Body']//.toUpperCase().trim(); 10 | if (['RED', 'GREEN', 'BLUE'].indexOf(votedFor) >= 0) { 11 | /* Add randomness to our value to help spread across partitions */ 12 | votedForHash = votedFor + "." + Math.floor((Math.random() * 10) + 1).toString(); 13 | /* ...updateItem into our DynamoDB database */ 14 | var tableName = 'VoteApp'; 15 | dynamodb.updateItem({ 16 | 'TableName': tableName, 17 | 'Key': { 'VotedFor' : { 'S': votedForHash }}, 18 | 'UpdateExpression': 'add #vote :x', 19 | 'ExpressionAttributeNames': {'#vote' : 'Votes'}, 20 | 'ExpressionAttributeValues': { ':x' : { "N" : "1" } } 21 | }, function(err, data) { 22 | if (err) { 23 | console.log(err); 24 | context.fail(err); 25 | } else { 26 | // var resp = new twilio.TwimlResponse(); 27 | // resp.message("Thank you for casting a vote for " + votedFor); 28 | // context.done(null, [resp.toString()]); 29 | console.log("Vote received for %s", votedFor); 30 | } 31 | }); 32 | } else { 33 | console.log("Invalid vote received (%s)", votedFor); 34 | context.fail("Invalid vote received"); 35 | } 36 | const result = { 37 | "isBase64Encoded": false, 38 | "statusCode": 200, 39 | "body":event['body'], 40 | "headers": { 41 | "Content-Type": "application/json", 42 | "X-Requested-With": "*", 43 | "Access-Control-Allow-Origin": "*", 44 | "Access-Control-Allow-Methods": "GET,HEAD,OPTIONS,POST,PUT,PATCH,DELETE", 45 | "Access-Control-Allow-Headers": "Access-Control-Allow-Origin,Access-Control-Allow-Headers, Origin,Accept,Authorization, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers" 46 | } 47 | }; 48 | callback(null, result); 49 | } 50 | -------------------------------------------------------------------------------- /Lambdas/receive-vote/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VoteApp", 3 | "version": "0.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "ajv": { 8 | "version": "5.5.2", 9 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 10 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 11 | "requires": { 12 | "co": "4.6.0", 13 | "fast-deep-equal": "1.0.0", 14 | "fast-json-stable-stringify": "2.0.0", 15 | "json-schema-traverse": "0.3.1" 16 | } 17 | }, 18 | "asap": { 19 | "version": "2.0.6", 20 | "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", 21 | "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" 22 | }, 23 | "asn1": { 24 | "version": "0.2.3", 25 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 26 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" 27 | }, 28 | "assert-plus": { 29 | "version": "1.0.0", 30 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 31 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" 32 | }, 33 | "asynckit": { 34 | "version": "0.4.0", 35 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 36 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" 37 | }, 38 | "aws-sign2": { 39 | "version": "0.7.0", 40 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 41 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" 42 | }, 43 | "aws4": { 44 | "version": "1.6.0", 45 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", 46 | "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" 47 | }, 48 | "base64url": { 49 | "version": "2.0.0", 50 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", 51 | "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" 52 | }, 53 | "bcrypt-pbkdf": { 54 | "version": "1.0.1", 55 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", 56 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", 57 | "optional": true, 58 | "requires": { 59 | "tweetnacl": "0.14.5" 60 | } 61 | }, 62 | "boom": { 63 | "version": "4.3.1", 64 | "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", 65 | "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", 66 | "requires": { 67 | "hoek": "4.2.1" 68 | } 69 | }, 70 | "buffer-equal-constant-time": { 71 | "version": "1.0.1", 72 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 73 | "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" 74 | }, 75 | "caseless": { 76 | "version": "0.12.0", 77 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 78 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" 79 | }, 80 | "co": { 81 | "version": "4.6.0", 82 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 83 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 84 | }, 85 | "combined-stream": { 86 | "version": "1.0.6", 87 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", 88 | "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", 89 | "requires": { 90 | "delayed-stream": "1.0.0" 91 | } 92 | }, 93 | "core-util-is": { 94 | "version": "1.0.2", 95 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 96 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" 97 | }, 98 | "cryptiles": { 99 | "version": "3.1.2", 100 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", 101 | "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", 102 | "requires": { 103 | "boom": "5.2.0" 104 | }, 105 | "dependencies": { 106 | "boom": { 107 | "version": "5.2.0", 108 | "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", 109 | "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", 110 | "requires": { 111 | "hoek": "4.2.1" 112 | } 113 | } 114 | } 115 | }, 116 | "dashdash": { 117 | "version": "1.14.1", 118 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 119 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 120 | "requires": { 121 | "assert-plus": "1.0.0" 122 | } 123 | }, 124 | "delayed-stream": { 125 | "version": "1.0.0", 126 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 127 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" 128 | }, 129 | "deprecate": { 130 | "version": "1.0.0", 131 | "resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.0.0.tgz", 132 | "integrity": "sha1-ZhSQ7SQokWpsiIPYg05WRvTkpKg=" 133 | }, 134 | "ecc-jsbn": { 135 | "version": "0.1.1", 136 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 137 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 138 | "optional": true, 139 | "requires": { 140 | "jsbn": "0.1.1" 141 | } 142 | }, 143 | "ecdsa-sig-formatter": { 144 | "version": "1.0.9", 145 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", 146 | "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", 147 | "requires": { 148 | "base64url": "2.0.0", 149 | "safe-buffer": "5.1.1" 150 | } 151 | }, 152 | "extend": { 153 | "version": "3.0.1", 154 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 155 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" 156 | }, 157 | "extsprintf": { 158 | "version": "1.3.0", 159 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 160 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" 161 | }, 162 | "fast-deep-equal": { 163 | "version": "1.0.0", 164 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", 165 | "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" 166 | }, 167 | "fast-json-stable-stringify": { 168 | "version": "2.0.0", 169 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 170 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" 171 | }, 172 | "forever-agent": { 173 | "version": "0.6.1", 174 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 175 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" 176 | }, 177 | "form-data": { 178 | "version": "2.3.2", 179 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", 180 | "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", 181 | "requires": { 182 | "asynckit": "0.4.0", 183 | "combined-stream": "1.0.6", 184 | "mime-types": "2.1.18" 185 | } 186 | }, 187 | "getpass": { 188 | "version": "0.1.7", 189 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 190 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 191 | "requires": { 192 | "assert-plus": "1.0.0" 193 | } 194 | }, 195 | "har-schema": { 196 | "version": "2.0.0", 197 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 198 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" 199 | }, 200 | "har-validator": { 201 | "version": "5.0.3", 202 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", 203 | "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", 204 | "requires": { 205 | "ajv": "5.5.2", 206 | "har-schema": "2.0.0" 207 | } 208 | }, 209 | "hawk": { 210 | "version": "6.0.2", 211 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", 212 | "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", 213 | "requires": { 214 | "boom": "4.3.1", 215 | "cryptiles": "3.1.2", 216 | "hoek": "4.2.1", 217 | "sntp": "2.1.0" 218 | } 219 | }, 220 | "hoek": { 221 | "version": "4.2.1", 222 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", 223 | "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" 224 | }, 225 | "http-signature": { 226 | "version": "1.2.0", 227 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 228 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 229 | "requires": { 230 | "assert-plus": "1.0.0", 231 | "jsprim": "1.4.1", 232 | "sshpk": "1.13.1" 233 | } 234 | }, 235 | "is-typedarray": { 236 | "version": "1.0.0", 237 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 238 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 239 | }, 240 | "isstream": { 241 | "version": "0.1.2", 242 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 243 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" 244 | }, 245 | "jsbn": { 246 | "version": "0.1.1", 247 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 248 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 249 | "optional": true 250 | }, 251 | "json-schema": { 252 | "version": "0.2.3", 253 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 254 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" 255 | }, 256 | "json-schema-traverse": { 257 | "version": "0.3.1", 258 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 259 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" 260 | }, 261 | "json-stringify-safe": { 262 | "version": "5.0.1", 263 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 264 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 265 | }, 266 | "jsonwebtoken": { 267 | "version": "8.1.1", 268 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.1.1.tgz", 269 | "integrity": "sha512-+ijVOtfLMlCII8LJkvabaKX3+8tGrGjiCTfzoed2D1b/ebKTO1hIYBQUJHbd9dJ9Fa4kH+dhYEd1qDwyzDLUUw==", 270 | "requires": { 271 | "jws": "3.1.4", 272 | "lodash.includes": "4.3.0", 273 | "lodash.isboolean": "3.0.3", 274 | "lodash.isinteger": "4.0.4", 275 | "lodash.isnumber": "3.0.3", 276 | "lodash.isplainobject": "4.0.6", 277 | "lodash.isstring": "4.0.1", 278 | "lodash.once": "4.1.1", 279 | "ms": "2.1.1", 280 | "xtend": "4.0.1" 281 | } 282 | }, 283 | "jsprim": { 284 | "version": "1.4.1", 285 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 286 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 287 | "requires": { 288 | "assert-plus": "1.0.0", 289 | "extsprintf": "1.3.0", 290 | "json-schema": "0.2.3", 291 | "verror": "1.10.0" 292 | } 293 | }, 294 | "jwa": { 295 | "version": "1.1.5", 296 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", 297 | "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", 298 | "requires": { 299 | "base64url": "2.0.0", 300 | "buffer-equal-constant-time": "1.0.1", 301 | "ecdsa-sig-formatter": "1.0.9", 302 | "safe-buffer": "5.1.1" 303 | } 304 | }, 305 | "jws": { 306 | "version": "3.1.4", 307 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", 308 | "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", 309 | "requires": { 310 | "base64url": "2.0.0", 311 | "jwa": "1.1.5", 312 | "safe-buffer": "5.1.1" 313 | } 314 | }, 315 | "lodash": { 316 | "version": "4.0.0", 317 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.0.0.tgz", 318 | "integrity": "sha1-msQ4RMWV4o0wEIt7pYNwM5WSLfw=" 319 | }, 320 | "lodash.includes": { 321 | "version": "4.3.0", 322 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", 323 | "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" 324 | }, 325 | "lodash.isboolean": { 326 | "version": "3.0.3", 327 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", 328 | "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" 329 | }, 330 | "lodash.isinteger": { 331 | "version": "4.0.4", 332 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", 333 | "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" 334 | }, 335 | "lodash.isnumber": { 336 | "version": "3.0.3", 337 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", 338 | "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" 339 | }, 340 | "lodash.isplainobject": { 341 | "version": "4.0.6", 342 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", 343 | "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" 344 | }, 345 | "lodash.isstring": { 346 | "version": "4.0.1", 347 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", 348 | "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" 349 | }, 350 | "lodash.once": { 351 | "version": "4.1.1", 352 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", 353 | "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" 354 | }, 355 | "mime-db": { 356 | "version": "1.33.0", 357 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 358 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 359 | }, 360 | "mime-types": { 361 | "version": "2.1.18", 362 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 363 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 364 | "requires": { 365 | "mime-db": "1.33.0" 366 | } 367 | }, 368 | "moment": { 369 | "version": "2.19.3", 370 | "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.3.tgz", 371 | "integrity": "sha1-vbmdJw1tf9p4zA+6zoVeJ/59pp8=" 372 | }, 373 | "ms": { 374 | "version": "2.1.1", 375 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 376 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 377 | }, 378 | "oauth-sign": { 379 | "version": "0.8.2", 380 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 381 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" 382 | }, 383 | "performance-now": { 384 | "version": "2.1.0", 385 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 386 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" 387 | }, 388 | "pop-iterate": { 389 | "version": "1.0.1", 390 | "resolved": "https://registry.npmjs.org/pop-iterate/-/pop-iterate-1.0.1.tgz", 391 | "integrity": "sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=" 392 | }, 393 | "punycode": { 394 | "version": "1.4.1", 395 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 396 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" 397 | }, 398 | "q": { 399 | "version": "2.0.3", 400 | "resolved": "https://registry.npmjs.org/q/-/q-2.0.3.tgz", 401 | "integrity": "sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=", 402 | "requires": { 403 | "asap": "2.0.6", 404 | "pop-iterate": "1.0.1", 405 | "weak-map": "1.0.5" 406 | } 407 | }, 408 | "qs": { 409 | "version": "6.5.1", 410 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 411 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" 412 | }, 413 | "request": { 414 | "version": "2.83.0", 415 | "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", 416 | "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", 417 | "requires": { 418 | "aws-sign2": "0.7.0", 419 | "aws4": "1.6.0", 420 | "caseless": "0.12.0", 421 | "combined-stream": "1.0.6", 422 | "extend": "3.0.1", 423 | "forever-agent": "0.6.1", 424 | "form-data": "2.3.2", 425 | "har-validator": "5.0.3", 426 | "hawk": "6.0.2", 427 | "http-signature": "1.2.0", 428 | "is-typedarray": "1.0.0", 429 | "isstream": "0.1.2", 430 | "json-stringify-safe": "5.0.1", 431 | "mime-types": "2.1.18", 432 | "oauth-sign": "0.8.2", 433 | "performance-now": "2.1.0", 434 | "qs": "6.5.1", 435 | "safe-buffer": "5.1.1", 436 | "stringstream": "0.0.5", 437 | "tough-cookie": "2.3.3", 438 | "tunnel-agent": "0.6.0", 439 | "uuid": "3.2.1" 440 | } 441 | }, 442 | "rootpath": { 443 | "version": "0.1.2", 444 | "resolved": "https://registry.npmjs.org/rootpath/-/rootpath-0.1.2.tgz", 445 | "integrity": "sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=" 446 | }, 447 | "safe-buffer": { 448 | "version": "5.1.1", 449 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 450 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" 451 | }, 452 | "scmp": { 453 | "version": "0.0.3", 454 | "resolved": "https://registry.npmjs.org/scmp/-/scmp-0.0.3.tgz", 455 | "integrity": "sha1-NkjfLXKUZB5/eGc//CloHZutkHM=" 456 | }, 457 | "sntp": { 458 | "version": "2.1.0", 459 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", 460 | "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", 461 | "requires": { 462 | "hoek": "4.2.1" 463 | } 464 | }, 465 | "sshpk": { 466 | "version": "1.13.1", 467 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", 468 | "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", 469 | "requires": { 470 | "asn1": "0.2.3", 471 | "assert-plus": "1.0.0", 472 | "bcrypt-pbkdf": "1.0.1", 473 | "dashdash": "1.14.1", 474 | "ecc-jsbn": "0.1.1", 475 | "getpass": "0.1.7", 476 | "jsbn": "0.1.1", 477 | "tweetnacl": "0.14.5" 478 | } 479 | }, 480 | "stringstream": { 481 | "version": "0.0.5", 482 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", 483 | "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" 484 | }, 485 | "tough-cookie": { 486 | "version": "2.3.3", 487 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", 488 | "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", 489 | "requires": { 490 | "punycode": "1.4.1" 491 | } 492 | }, 493 | "tunnel-agent": { 494 | "version": "0.6.0", 495 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 496 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 497 | "requires": { 498 | "safe-buffer": "5.1.1" 499 | } 500 | }, 501 | "tweetnacl": { 502 | "version": "0.14.5", 503 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 504 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 505 | "optional": true 506 | }, 507 | "twilio": { 508 | "version": "3.11.3", 509 | "resolved": "https://registry.npmjs.org/twilio/-/twilio-3.11.3.tgz", 510 | "integrity": "sha512-xGUH+SW8lBsPmTB9nNuawB8AajhevjIktD6LvESbIJ5HVeAgE44Y327AziCAT6lQr90PYB5joO5IUagH6YwzDA==", 511 | "requires": { 512 | "deprecate": "1.0.0", 513 | "jsonwebtoken": "8.1.1", 514 | "lodash": "4.0.0", 515 | "moment": "2.19.3", 516 | "q": "2.0.3", 517 | "request": "2.83.0", 518 | "rootpath": "0.1.2", 519 | "scmp": "0.0.3", 520 | "xmlbuilder": "9.0.1" 521 | } 522 | }, 523 | "uuid": { 524 | "version": "3.2.1", 525 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", 526 | "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" 527 | }, 528 | "verror": { 529 | "version": "1.10.0", 530 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 531 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 532 | "requires": { 533 | "assert-plus": "1.0.0", 534 | "core-util-is": "1.0.2", 535 | "extsprintf": "1.3.0" 536 | } 537 | }, 538 | "weak-map": { 539 | "version": "1.0.5", 540 | "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", 541 | "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" 542 | }, 543 | "xmlbuilder": { 544 | "version": "9.0.1", 545 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.1.tgz", 546 | "integrity": "sha1-kc1wiXdVNj66V8Et3uq0o0GmH2U=" 547 | }, 548 | "xtend": { 549 | "version": "4.0.1", 550 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", 551 | "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" 552 | } 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /Lambdas/receive-vote/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "VoteApp", 3 | "version": "0.0.0", 4 | "description": "VoteApp Demo using API Gateway and Lambda", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "jeffnunn@amazon.com", 10 | "dependencies": { 11 | "twilio": ">= 2.0.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Lambdas/receive-vote/serverless.yml: -------------------------------------------------------------------------------- 1 | 2 | service: receive-vote 3 | stage: dev 4 | 5 | provider: 6 | name: aws 7 | runtime: nodejs10.x 8 | iamRoleStatements: # permissions for all of your functions can be set here 9 | - Effect: Allow 10 | Action: # Gives permission to DynamoDB tables in a specific region 11 | - dynamodb:DescribeTable 12 | - dynamodb:Query 13 | - dynamodb:Scan 14 | - dynamodb:GetItem 15 | - dynamodb:PutItem 16 | - dynamodb:UpdateItem 17 | - dynamodb:DeleteItem 18 | Resource: "arn:aws:dynamodb:us-east-1:*:*" 19 | 20 | functions: 21 | receive-vote: 22 | handler: app.handler 23 | memorySize: 128 24 | timeout: 10 25 | events: 26 | - http: 27 | path: receivevote 28 | method: post 29 | cors: true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serverless-talk-demo 2 | -------------------------------------------------------------------------------- /Terraform/dynamoDBAgregateVotes.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "VoteAppAggregates" { 2 | name = "VoteAppAggregates" 3 | read_capacity = 5 4 | write_capacity = 5 5 | hash_key = "VotedFor" 6 | 7 | attribute { 8 | name = "VotedFor" 9 | type = "S" 10 | } 11 | 12 | tags = { 13 | Name = "VoteAppAggregates" 14 | Environment = "palestra" 15 | } 16 | } -------------------------------------------------------------------------------- /Terraform/dynamoDBVotes.tf: -------------------------------------------------------------------------------- 1 | resource "aws_dynamodb_table" "VoteApp" { 2 | name = "VoteApp" 3 | read_capacity = 5 4 | write_capacity = 5 5 | hash_key = "VotedFor" 6 | stream_enabled = true 7 | stream_view_type = "NEW_IMAGE" 8 | 9 | attribute { 10 | name = "VotedFor" 11 | type = "S" 12 | } 13 | 14 | tags = { 15 | Name = "VoteApp" 16 | Environment = "palestra" 17 | } 18 | } -------------------------------------------------------------------------------- /Terraform/outputs.tf: -------------------------------------------------------------------------------- 1 | output "dynamo_votes_arn" { 2 | value = "${aws_dynamodb_table.VoteApp.arn}" 3 | } 4 | 5 | output "dynamo_votes_stream_arn" { 6 | value = "${aws_dynamodb_table.VoteApp.stream_arn}" 7 | } -------------------------------------------------------------------------------- /Terraform/s3StaticWebsite.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = "${var.region}" 3 | } 4 | 5 | 6 | resource "aws_s3_bucket" "dashboard" { 7 | bucket = "${var.bucket_dashboard}" 8 | acl = "public-read" 9 | policy = <