Add restpublish.py
authorStefan Huber <shuber@sthu.org>
Mon, 19 Dec 2022 07:52:24 +0000 (08:52 +0100)
committerStefan Huber <shuber@sthu.org>
Mon, 19 Dec 2022 07:52:24 +0000 (08:52 +0100)
restpublish.py [new file with mode: 0644]

diff --git a/restpublish.py b/restpublish.py
new file mode 100644 (file)
index 0000000..9310d7a
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+#
+# FLASK_APP=restpublish.py flask run
+
+import datetime
+
+import flask
+from flask_restful import Resource, Api
+
+
+class Element:
+    def __init__(self, key, value):
+        self.key = key
+        self.update(value)
+
+    def update(self, value):
+        self.value = value
+        self.ctime = datetime.datetime.now()
+
+    def is_expired(self, maxage):
+        return datetime.datetime.now() - self.ctime >= maxage
+
+
+class ElementStore:
+    def __init__(self):
+        self.key_dict = dict()
+        self.chronolist = list()
+
+    def insert(self, key, value):
+        if key in self.key_dict:
+            el = self.key_dict[key]
+            el.update(value)
+        else:
+            el = Element(key, value)
+            self.key_dict[key] = el
+            self.chronolist.append(el)
+        return el
+
+    def remove(self, key):
+        del self.key_dict[key]
+        self.chronolist = [el for el in self.chronolist if el.key != key]
+
+    def garbagecollect(self):
+        maxage = datetime.timedelta(7)
+        oldestctime = datetime.datetime.now() - maxage
+        def is_expired(el):
+            return el.ctime < oldestctime
+
+        # Get idx such that self.chronolist[:idx] is expired and
+        # self.chronolist[idx:] is not expired.
+        idx = 0
+        while idx < len(self.chronolist) - 1:
+            if not is_expired(self.chronolist[idx]):
+                break
+            idx += 1
+
+        for el in self.chronolist[:idx]:
+            del self.key_dict[el.key]
+
+        self.chronolist = self.chronolist[idx:]
+
+
+class EntryPoint(Resource):
+    def get(self):
+        return { 'elements': [el.key for el in store.chronolist] }
+
+class ElementPoint(Resource):
+    def get(self, key):
+        if key not in store.key_dict:
+            return { 'message': 'No such element'}, 404
+
+        el = store.key_dict[key]
+        return { 'key': key, 'value': el.value, 'ctime': str(el.ctime) }
+
+    def delete(self, key):
+        if key in store.key_dict:
+            store.remove(key)
+        return { 'message': 'ok'}
+
+    def post(self, key):
+        req = flask.request.get_json(force=True)
+
+        if 'value' not in req:
+            return {'message': 'No value given.'}, 403
+
+        el = store.insert(key, req['value'])
+        store.garbagecollect()
+        return { 'key': key, 'value': el.value, 'ctime': str(el.ctime) }
+
+
+# Map of keys to values
+store = ElementStore()
+
+app = flask.Flask(__name__)
+api = Api(app)
+api.add_resource(EntryPoint, "/")
+api.add_resource(ElementPoint, "/<key>")
+