2017-03-28 23:00:23 +01:00
|
|
|
# This demo requires aioredis and environmental variables established in ENV_VARS
|
2017-03-27 23:42:13 +01:00
|
|
|
import json
|
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
|
2017-03-28 23:00:23 +01:00
|
|
|
from datetime import datetime
|
|
|
|
|
2017-03-27 23:42:13 +01:00
|
|
|
import aioredis
|
|
|
|
|
|
|
|
import sanic
|
|
|
|
from sanic import Sanic
|
|
|
|
|
|
|
|
|
|
|
|
ENV_VARS = ["REDIS_HOST", "REDIS_PORT",
|
|
|
|
"REDIS_MINPOOL", "REDIS_MAXPOOL",
|
|
|
|
"REDIS_PASS", "APP_LOGFILE"]
|
|
|
|
|
|
|
|
app = Sanic(name=__name__)
|
|
|
|
|
|
|
|
logger = None
|
|
|
|
|
|
|
|
|
2017-03-28 09:22:36 +01:00
|
|
|
@app.middleware("request")
|
|
|
|
async def log_uri(request):
|
|
|
|
# Simple middleware to log the URI endpoint that was called
|
|
|
|
logger.info("URI called: {0}".format(request.url))
|
|
|
|
|
|
|
|
|
2017-03-28 23:00:23 +01:00
|
|
|
@app.listener('before_server_start')
|
|
|
|
async def before_server_start(app, loop):
|
|
|
|
logger.info("Starting redis pool")
|
|
|
|
app.redis_pool = await aioredis.create_pool(
|
|
|
|
(app.config.REDIS_HOST, int(app.config.REDIS_PORT)),
|
|
|
|
minsize=int(app.config.REDIS_MINPOOL),
|
|
|
|
maxsize=int(app.config.REDIS_MAXPOOL),
|
|
|
|
password=app.config.REDIS_PASS)
|
|
|
|
|
|
|
|
|
|
|
|
@app.listener('after_server_stop')
|
|
|
|
async def after_server_stop(app, loop):
|
|
|
|
logger.info("Closing redis pool")
|
|
|
|
app.redis_pool.close()
|
|
|
|
await app.redis_pool.wait_closed()
|
|
|
|
|
|
|
|
|
2017-03-27 23:42:13 +01:00
|
|
|
@app.middleware("request")
|
|
|
|
async def attach_db_connectors(request):
|
|
|
|
# Just put the db objects in the request for easier access
|
2017-03-28 23:00:23 +01:00
|
|
|
logger.info("Passing redis pool to request object")
|
|
|
|
request["redis"] = request.app.redis_pool
|
2017-03-27 23:42:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
@app.route("/state/<user_id>", methods=["GET"])
|
|
|
|
async def access_state(request, user_id):
|
|
|
|
try:
|
|
|
|
# Check to see if the value is in cache, if so lets return that
|
|
|
|
with await request["redis"] as redis_conn:
|
|
|
|
state = await redis_conn.get(user_id, encoding="utf-8")
|
|
|
|
if state:
|
2017-03-28 23:00:23 +01:00
|
|
|
return sanic.response.json({"msg": "Success",
|
|
|
|
"status": 200,
|
|
|
|
"success": True,
|
|
|
|
"data": json.loads(state),
|
|
|
|
"finished_at": datetime.now().isoformat()})
|
2017-03-27 23:42:13 +01:00
|
|
|
# Then state object is not in redis
|
|
|
|
logger.critical("Unable to find user_data in cache.")
|
2017-03-28 23:00:23 +01:00
|
|
|
return sanic.response.HTTPResponse({"msg": "User state not found",
|
|
|
|
"success": False,
|
|
|
|
"status": 404,
|
|
|
|
"finished_at": datetime.now().isoformat()}, status=404)
|
2017-03-27 23:42:13 +01:00
|
|
|
except aioredis.ProtocolError:
|
|
|
|
logger.critical("Unable to connect to state cache")
|
2017-03-28 23:00:23 +01:00
|
|
|
return sanic.response.HTTPResponse({"msg": "Internal Server Error",
|
|
|
|
"status": 500,
|
|
|
|
"success": False,
|
|
|
|
"finished_at": datetime.now().isoformat()}, status=500)
|
2017-03-27 23:42:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
@app.route("/state/<user_id>/push", methods=["POST"])
|
|
|
|
async def set_state(request, user_id):
|
|
|
|
try:
|
2017-03-28 09:22:36 +01:00
|
|
|
# Pull a connection from the pool
|
2017-03-27 23:42:13 +01:00
|
|
|
with await request["redis"] as redis_conn:
|
|
|
|
# Set the value in cache to your new value
|
|
|
|
await redis_conn.set(user_id, json.dumps(request.json), expire=1800)
|
2017-03-28 23:00:23 +01:00
|
|
|
logger.info("Successfully pushed state to cache")
|
|
|
|
return sanic.response.HTTPResponse({"msg": "Successfully pushed state to cache",
|
|
|
|
"success": True,
|
|
|
|
"status": 200,
|
|
|
|
"finished_at": datetime.now().isoformat()})
|
2017-03-27 23:42:13 +01:00
|
|
|
except aioredis.ProtocolError:
|
2017-03-28 09:22:36 +01:00
|
|
|
logger.critical("Unable to connect to state cache")
|
2017-03-28 23:00:23 +01:00
|
|
|
return sanic.response.HTTPResponse({"msg": "Internal Server Error",
|
|
|
|
"status": 500,
|
|
|
|
"success": False,
|
|
|
|
"finished_at": datetime.now().isoformat()}, status=500)
|
2017-03-27 23:42:13 +01:00
|
|
|
|
|
|
|
|
|
|
|
def configure():
|
|
|
|
# Setup environment variables
|
|
|
|
env_vars = [os.environ.get(v, None) for v in ENV_VARS]
|
|
|
|
if not all(env_vars):
|
|
|
|
# Send back environment variables that were not set
|
2017-03-28 09:22:36 +01:00
|
|
|
return False, ", ".join([ENV_VARS[i] for i, flag in env_vars if not flag])
|
2017-03-27 23:42:13 +01:00
|
|
|
else:
|
2017-03-28 09:22:36 +01:00
|
|
|
# Add all the env vars to our app config
|
2017-03-27 23:42:13 +01:00
|
|
|
app.config.update({k: v for k, v in zip(ENV_VARS, env_vars)})
|
|
|
|
setup_logging()
|
2017-03-28 09:22:36 +01:00
|
|
|
return True, None
|
|
|
|
|
2017-03-27 23:42:13 +01:00
|
|
|
|
|
|
|
def setup_logging():
|
|
|
|
logging_format = "[%(asctime)s] %(process)d-%(levelname)s "
|
|
|
|
logging_format += "%(module)s::%(funcName)s():l%(lineno)d: "
|
|
|
|
logging_format += "%(message)s"
|
|
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
filename=app.config.APP_LOGFILE,
|
|
|
|
format=logging_format,
|
2017-03-28 09:22:36 +01:00
|
|
|
level=logging.DEBUG)
|
2017-03-27 23:42:13 +01:00
|
|
|
|
2017-03-28 09:22:36 +01:00
|
|
|
|
|
|
|
def main(result, missing):
|
2017-03-27 23:42:13 +01:00
|
|
|
if result:
|
|
|
|
try:
|
|
|
|
app.run(host="0.0.0.0", port=8080, debug=True)
|
|
|
|
except:
|
2017-03-28 09:22:36 +01:00
|
|
|
logging.critical("User killed server. Closing")
|
2017-03-27 23:42:13 +01:00
|
|
|
else:
|
2017-03-28 09:22:36 +01:00
|
|
|
logging.critical("Unable to start. Missing environment variables [{0}]".format(missing))
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
result, missing = configure()
|
|
|
|
logger = logging.getLogger()
|
|
|
|
main(result, missing)
|