|
@@ -0,0 +1,109 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+import orjson
|
|
|
+import falcon.asgi
|
|
|
+from falcon import media
|
|
|
+from helpers import load_template, FortuneTuple, generate_ids, sanitize
|
|
|
+from operator import attrgetter
|
|
|
+from random import randint
|
|
|
+from tortoise import Tortoise
|
|
|
+from tortoise_models import World, Fortune
|
|
|
+from tortoise.transactions import in_transaction, atomic
|
|
|
+from tortoise.exceptions import OperationalError
|
|
|
+
|
|
|
+
|
|
|
+# Middleware
|
|
|
+class TortoiseInit:
|
|
|
+ async def process_startup(self, scope, event):
|
|
|
+ await Tortoise.init(
|
|
|
+ db_url="postgres://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world",
|
|
|
+ modules={"models": ["tortoise_models"]}
|
|
|
+ )
|
|
|
+ await Tortoise.generate_schemas(safe=True)
|
|
|
+
|
|
|
+ async def process_shutdown(self, scopre, event):
|
|
|
+ await Tortoise.close_connections()
|
|
|
+
|
|
|
+
|
|
|
+tortoise_init = TortoiseInit()
|
|
|
+
|
|
|
+
|
|
|
+# resource endpoints
|
|
|
+class JSONResource(object):
|
|
|
+ async def on_get(self, request, response):
|
|
|
+ response.media = {'message': "Hello, world!"}
|
|
|
+
|
|
|
+
|
|
|
+class SingleQuery(object):
|
|
|
+ # Note: There's much improvement when we decorate
|
|
|
+ # the query, even just for retreiving data
|
|
|
+ @atomic()
|
|
|
+ async def on_get(self, request, response):
|
|
|
+ resp = await World.get(id=randint(1, 10000))
|
|
|
+ response.media = resp.to_dict()
|
|
|
+
|
|
|
+
|
|
|
+class MultipleQueries(object):
|
|
|
+ # Note: Not much different between using atomic or
|
|
|
+ # in_transaction decorator here.
|
|
|
+ @atomic()
|
|
|
+ async def on_get(self, request, response, num):
|
|
|
+ num = sanitize(num)
|
|
|
+ worlds = []
|
|
|
+ for ids in generate_ids(num):
|
|
|
+ data = await World.get(id=ids)
|
|
|
+ worlds.append(data.to_dict())
|
|
|
+ response.media = worlds
|
|
|
+
|
|
|
+
|
|
|
+class UpdateQueries(object):
|
|
|
+ async def on_get(self, request, response, num):
|
|
|
+ try:
|
|
|
+ async with in_transaction():
|
|
|
+ num = sanitize(num)
|
|
|
+ items = await World.filter(id__in=generate_ids(num))
|
|
|
+ worlds = []
|
|
|
+ for ids in items:
|
|
|
+ ids.randomNumber = randint(1, 10000)
|
|
|
+ await ids.save()
|
|
|
+ worlds.append({'id': ids.id, 'randomNumber': ids.randomNumber})
|
|
|
+ response.media = worlds
|
|
|
+ except OperationalError:
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class Fortunes(object):
|
|
|
+ _template = load_template()
|
|
|
+
|
|
|
+ async def on_get(self, request, response):
|
|
|
+ fortunes = [FortuneTuple(id=f.id, message=f.message) for f in await Fortune.all()]
|
|
|
+ fortunes.append(FortuneTuple(id=0, message="Additional fortune added at request time."))
|
|
|
+ fortunes.sort(key=attrgetter("message"))
|
|
|
+ content = self._template.render(fortunes=fortunes)
|
|
|
+ response.content_type = falcon.MEDIA_HTML
|
|
|
+ response.text = content
|
|
|
+
|
|
|
+
|
|
|
+class PlaintextResource(object):
|
|
|
+ async def on_get(self, request, response):
|
|
|
+ response.content_type = falcon.MEDIA_TEXT
|
|
|
+ response.text = 'Hello, world!'
|
|
|
+
|
|
|
+
|
|
|
+asgi = falcon.asgi.App(middleware=[tortoise_init])
|
|
|
+
|
|
|
+# Change JSON handler to orjson
|
|
|
+JSONHandler = media.JSONHandler(dumps=orjson.dumps, loads=orjson.loads)
|
|
|
+extra_handlers = {
|
|
|
+ "application/json": JSONHandler,
|
|
|
+ "application/json; charset=UTF-8": JSONHandler
|
|
|
+}
|
|
|
+asgi.req_options.media_handlers.update(extra_handlers)
|
|
|
+asgi.resp_options.media_handlers.update(extra_handlers)
|
|
|
+
|
|
|
+# register resources
|
|
|
+asgi.add_route("/json", JSONResource())
|
|
|
+asgi.add_route("/db", SingleQuery())
|
|
|
+asgi.add_route("/queries/{num}", MultipleQueries())
|
|
|
+asgi.add_route("/updates/{num}", UpdateQueries())
|
|
|
+asgi.add_route("/fortunes", Fortunes())
|
|
|
+asgi.add_route("/plaintext", PlaintextResource())
|