├── .gitignore
├── Pipfile
├── zappa_settings.json
├── .env.example
├── README.md
└── app.py
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | zappa_settings.json
3 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.python.org/simple"
3 | verify_ssl = true
4 |
5 | [dev-packages]
6 | awscli = "*"
7 |
8 | [packages]
9 | zappa = "*"
10 | flask = "*"
11 | requests = "*"
12 | dotenv = "*"
13 | pymysql = "*"
14 |
--------------------------------------------------------------------------------
/zappa_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "dev": {
3 | "app_function": "app.app",
4 | "aws_region": "us-west-2",
5 | "profile_name": "personal",
6 | "s3_bucket": "lambda-tutorial-01",
7 | "runtime": "python2.7"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | DB_HOST=
2 | DB_USERNAME=
3 | DB_PASSWORD=
4 | DB_NAME=
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Lambda Tutorial
2 |
3 | I built this tutorial on lambda because of my original struggles getting lambda set up and working. this covers:
4 |
5 | * set up with zappa
6 | * vpc to connect to a mysql RDS instance on amazon
7 | * RDS persistence and querying through the lambda function
8 | * CORs setup
9 | * simple security
10 |
11 | The tutorial should be done by checking out these branches and going through the readmes in this order
12 |
13 | * hello-world
14 | * routing
15 | * rds-integration
16 | * database-requests
17 |
18 | If you just want to run the app, adjust your .env to have the values for the
19 | config vars in .env.example and run:
20 |
21 | ```bash
22 | pipenv install
23 |
24 | ```
25 |
26 | then,
27 |
28 | ```
29 | zappa deploy
30 | ```
31 |
32 | if you get stuck here I'd recommend at least running through the readme in the [rds-integration](https://github.com/ipbrennan90/lambda-tutorial/tree/rds-integration) branch
33 |
34 |
35 |
36 | I have done my best to keep this simple and short, please open any issues if you have trouble with an explanation or get stuck on a step and I will work hard to solve those.
37 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 | from flask import Flask, Response, json, request
4 | from dotenv import Dotenv
5 | from uuid import uuid5, uuid1
6 | import pymysql
7 | # Of course, replace by your correct path
8 | dotenv = Dotenv(os.path.join(os.path.dirname(__file__), ".env"))
9 | os.environ.update(dotenv)
10 |
11 | app = Flask(__name__)
12 | RDS_HOST = os.environ.get("DB_HOST")
13 | RDS_PORT = int(os.environ.get("DB_PORT", 3306))
14 | NAME = os.environ.get("DB_USERNAME")
15 | PASSWORD = os.environ.get("DB_PASSWORD")
16 | DB_NAME = os.environ.get("DB_NAME")
17 |
18 | logger = logging.getLogger()
19 | logger.setLevel(logging.INFO)
20 |
21 | def connect():
22 | try:
23 | cursor = pymysql.cursors.DictCursor
24 | conn = pymysql.connect(RDS_HOST, user=NAME, passwd=PASSWORD, db=DB_NAME, port=RDS_PORT, cursorclass=cursor, connect_timeout=5)
25 | logger.info("SUCCESS: connection to RDS successful")
26 | return conn
27 | except Exception as e:
28 | logger.exception("Database Connection Error")
29 |
30 | def validate(data):
31 | error_fields = []
32 | not_null = [
33 | "first_name",
34 | "last_name",
35 | "email"
36 | ]
37 |
38 | for x in not_null:
39 | if x not in data or len(data[x]) == 0:
40 | error_fields.append(x)
41 | return (len(error_fields) == 0, error_fields)
42 |
43 | def insert(data):
44 | uniq_id = str(uuid5(uuid1(), str(uuid1())))
45 | query = """insert into User (ID, FirstName, LastName, Email)
46 | values(%s, %s, %s, %s)
47 | """
48 | return (query, (uniq_id, data["first_name"], data["last_name"], data["email"]))
49 |
50 | def build_response(resp_dict, status_code):
51 | response = Response(json.dumps(resp_dict), status_code)
52 | response.headers["Access-Control-Allow-Origin"] = "*"
53 | response.headers["Access-Control-Allow-Headers"] = "Content-Type"
54 | return response
55 |
56 | @app.route('/')
57 | def index():
58 | return build_response({"message": "Welcome to my lambda app!"}, 200)
59 |
60 | @app.route('/user', methods=["GET", "POST"])
61 | def user():
62 | if request.method == "OPTIONS":
63 | return build_response({"status": "success"}, 200)
64 |
65 | conn = connect()
66 | if request.method == "GET":
67 | items = []
68 | try:
69 | with conn.cursor() as cur:
70 | cur.execute("select * from User")
71 | for row in cur:
72 | items.append(row)
73 | conn.commit()
74 | except Exception as e:
75 | logger.info(e)
76 | response = build_response({"status": "error", "message": "error getting users"}, 500)
77 | return response
78 | finally:
79 | conn.close()
80 | response = build_response({"rows": items, "status": "success"}, 200)
81 | return response
82 | if request.method == "POST":
83 | data = {
84 | "first_name": request.form.get("first_name", ""),
85 | "last_name": request.form.get("last_name", ""),
86 | "email": request.form.get("email", "")
87 | }
88 | valid, fields = validate(data)
89 | if not valid:
90 | error_fields = ', '.join(fields)
91 | error_message = "Data missing from these fields: %s" %error_fields
92 | return build_response({"status": "error", "message": error_message}, 400)
93 | query, vals = insert(data)
94 | try:
95 | with conn.cursor() as cur:
96 | cur.execute(query, vals)
97 | conn.commit()
98 | except Exception as e:
99 | logger.exception("insert error")
100 | return build_response({"status": "error", "message": "insert error"}, 500)
101 | finally:
102 | conn.close()
103 | cur.close()
104 | return build_response({"status": "success"}, 200)
105 |
106 | if __name__ == '__main__':
107 | app.run()
108 |
--------------------------------------------------------------------------------