├── lambda_test.json ├── static_website ├── cat.png ├── IMG_0991.jpg ├── error.html ├── main.css ├── formlogic.js └── index.html ├── sms_reminder.py ├── sfn_input.json ├── email_reminder.py ├── step-function-template.json └── api_handler.py /lambda_test.json: -------------------------------------------------------------------------------- 1 | {"body": "{\"waitSeconds\":100, \"preference\":\"sms\", \"phone\":\"+15556667788\", \"message\":\"yo, from Lambda\"}"} 2 | -------------------------------------------------------------------------------- /static_website/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru-Resources/lab-building-a-serverless-application-using-step-functions-api-gateway-lambda-and-s3-in-aws/HEAD/static_website/cat.png -------------------------------------------------------------------------------- /static_website/IMG_0991.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ACloudGuru-Resources/lab-building-a-serverless-application-using-step-functions-api-gateway-lambda-and-s3-in-aws/HEAD/static_website/IMG_0991.jpg -------------------------------------------------------------------------------- /sms_reminder.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | 3 | sns = boto3.client('sns') 4 | 5 | def lambda_handler(event, context): 6 | sns.publish(PhoneNumber=event['phone'], Message=event['message']) 7 | return 'Success!' -------------------------------------------------------------------------------- /static_website/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

This page was not found. Sorry! Did you mean to go to the home page?

8 | 9 | -------------------------------------------------------------------------------- /sfn_input.json: -------------------------------------------------------------------------------- 1 | { 2 | "waitSeconds": 10, 3 | "preference": "both", 4 | "message": "Hello! this works, but remember your cat needs something", 5 | "email": "adrian.cantrill@linuxacademy.com", 6 | "phone": "+15555555555" 7 | } 8 | -------------------------------------------------------------------------------- /email_reminder.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | 3 | VERIFIED_EMAIL = 'YOUR_SES_VERIFIED_EMAIL' 4 | 5 | ses = boto3.client('ses') 6 | 7 | def lambda_handler(event, context): 8 | ses.send_email( 9 | Source=VERIFIED_EMAIL, 10 | Destination={ 11 | 'ToAddresses': [event['email']] # Also a verified email 12 | }, 13 | Message={ 14 | 'Subject': {'Data': 'A reminder from your reminder service!'}, 15 | 'Body': {'Text': {'Data': event['message']}} 16 | } 17 | ) 18 | return 'Success!' -------------------------------------------------------------------------------- /step-function-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "Comment": "An example of the Amazon States Language using a choice state.", 3 | "StartAt": "SendReminder", 4 | "States": { 5 | "SendReminder": { 6 | "Type": "Wait", 7 | "SecondsPath": "$.waitSeconds", 8 | "Next": "ChoiceState" 9 | }, 10 | "ChoiceState": { 11 | "Type" : "Choice", 12 | "Choices": [ 13 | { 14 | "Variable": "$.preference", 15 | "StringEquals": "email", 16 | "Next": "EmailReminder" 17 | } 18 | ], 19 | "Default": "DefaultState" 20 | }, 21 | 22 | "EmailReminder": { 23 | "Type" : "Task", 24 | "Resource": "EMAIL_REMINDER_ARN", 25 | "Next": "NextState" 26 | }, 27 | 28 | "DefaultState": { 29 | "Type": "Fail", 30 | "Error": "DefaultStateError", 31 | "Cause": "No Matches!" 32 | }, 33 | 34 | "NextState": { 35 | "Type": "Pass", 36 | "End": true 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /static_website/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 40px; 3 | padding-bottom: 40px; 4 | background-color: #eee; 5 | } 6 | 7 | hr { 8 | border-top: solid black; 9 | } 10 | 11 | div #error-message { 12 | color: red; 13 | font-size: 15px; 14 | font-weight: bold; 15 | } 16 | 17 | div #success-message, #results-message { 18 | color: green; 19 | font-size: 15px; 20 | font-weight: bold; 21 | } 22 | 23 | .form-signin { 24 | max-width: 530px; 25 | padding: 15px; 26 | margin: 0 auto; 27 | } 28 | .form-signin .form-signin-heading, 29 | .form-signin .checkbox { 30 | margin-bottom: 10px; 31 | } 32 | .form-signin .checkbox { 33 | font-weight: normal; 34 | } 35 | .form-signin .form-control { 36 | position: relative; 37 | height: auto; 38 | -webkit-box-sizing: border-box; 39 | box-sizing: border-box; 40 | padding: 10px; 41 | font-size: 16px; 42 | } 43 | .form-signin .form-control:focus { 44 | z-index: 2; 45 | } 46 | .form-signin input[type="Artist"] { 47 | margin-bottom: -1px; 48 | border-bottom-right-radius: 0; 49 | border-bottom-left-radius: 0; 50 | } 51 | .form-signin input[type="bottom"] { 52 | margin-bottom: 10px; 53 | border-top-left-radius: 0; 54 | border-top-right-radius: 0; 55 | } 56 | -------------------------------------------------------------------------------- /api_handler.py: -------------------------------------------------------------------------------- 1 | import boto3 2 | import json 3 | import os 4 | import decimal 5 | 6 | SFN_ARN = 'STEP_FUNCTION_ARN' 7 | 8 | sfn = boto3.client('stepfunctions') 9 | 10 | def lambda_handler(event, context): 11 | print('EVENT:') 12 | print(event) 13 | data = json.loads(event['body']) 14 | data['waitSeconds'] = int(data['waitSeconds']) 15 | 16 | # Validation Checks 17 | checks = [] 18 | checks.append('waitSeconds' in data) 19 | checks.append(type(data['waitSeconds']) == int) 20 | checks.append('preference' in data) 21 | checks.append('message' in data) 22 | if data.get('preference') == 'email': 23 | checks.append('email' in data) 24 | 25 | # Check for any errors in validation checks 26 | if False in checks: 27 | response = { 28 | "statusCode": 400, 29 | "headers": {"Access-Control-Allow-Origin":"*"}, 30 | "body": json.dumps( 31 | { 32 | "Status": "Success", 33 | "Reason": "Input failed validation" 34 | }, 35 | cls=DecimalEncoder 36 | ) 37 | } 38 | # If none, run the state machine and return a 200 code saying this is fine :) 39 | else: 40 | sfn.start_execution( 41 | stateMachineArn=SFN_ARN, 42 | input=json.dumps(data, cls=DecimalEncoder) 43 | ) 44 | response = { 45 | "statusCode": 200, 46 | "headers": {"Access-Control-Allow-Origin":"*"}, 47 | "body": json.dumps( 48 | {"Status": "Success"}, 49 | cls=DecimalEncoder 50 | ) 51 | } 52 | return response 53 | 54 | # This is a workaround for: http://bugs.python.org/issue16535 55 | class DecimalEncoder(json.JSONEncoder): 56 | def default(self, obj): 57 | if isinstance(obj, decimal.Decimal): 58 | return int(obj) 59 | return super(DecimalEncoder, self).default(obj) 60 | 61 | -------------------------------------------------------------------------------- /static_website/formlogic.js: -------------------------------------------------------------------------------- 1 | // Replace the YOUR_API_ENDPOINT_URL with yours 2 | // It should look something like this: 3 | // https://example1a2s3d.execute-api.us-east-1.amazonaws.com/prod/reminders 4 | 5 | var API_ENDPOINT = 'UPDATETOYOURINVOKEURLENDPOINT/reminders'; 6 | 7 | // Setup divs that will be used to display interactive messages 8 | var errorDiv = document.getElementById('error-message') 9 | var successDiv = document.getElementById('success-message') 10 | var resultsDiv = document.getElementById('results-message') 11 | 12 | // Setup easy way to reference values of the input boxes 13 | function waitSecondsValue() { return document.getElementById('waitSeconds').value } 14 | function messageValue() { return document.getElementById('message').value } 15 | function emailValue() { return document.getElementById('email').value } 16 | 17 | 18 | function clearNotifications() { 19 | // Clear any exisiting notifications in the browser notifications divs 20 | errorDiv.textContent = ''; 21 | resultsDiv.textContent = ''; 22 | successDiv.textContent = ''; 23 | } 24 | 25 | document.getElementById('emailButton').addEventListener('click', function(e) { 26 | sendData(e, 'email'); 27 | }); 28 | 29 | function sendData (e, pref) { 30 | // Prevent the page reloading and clear exisiting notifications 31 | e.preventDefault() 32 | clearNotifications() 33 | // Prepare the appropriate HTTP request to the API with fetch 34 | // create uses the root /prometheon endpoint and requires a JSON payload 35 | fetch(API_ENDPOINT, { 36 | headers:{ 37 | "Content-type": "application/json" 38 | }, 39 | method: 'POST', 40 | body: JSON.stringify({ 41 | waitSeconds: waitSecondsValue(), 42 | preference: pref, 43 | message: messageValue(), 44 | email: emailValue(), 45 | 46 | }), 47 | mode: 'cors' 48 | }) 49 | .then((resp) => resp.json()) 50 | .then(function(data) { 51 | console.log(data) 52 | successDiv.textContent = 'Looks ok. But check the result below!'; 53 | resultsDiv.textContent = JSON.stringify(data); 54 | }) 55 | .catch(function(err) { 56 | errorDiv.textContent = 'Yikes! There was an error:\n' + err.toString(); 57 | console.log(err) 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /static_website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | FembotIT 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 | the boss 28 |
29 |
30 |
31 |
32 | 33 |

Required sections:

34 | 35 | 36 | 37 |
38 |

Email address required for email reminders:

39 | 40 | 41 |
42 |

Reminder Type:

43 | 44 | 45 | 46 | 47 |
48 |
49 | 50 |
51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | --------------------------------------------------------------------------------