aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Gareth Coles <[email protected]>2018-02-11 17:09:58 +0000
committerGravatar GitHub <[email protected]>2018-02-11 17:09:58 +0000
commit64b1108081b6ee9e89acd104454e7a117e28a061 (patch)
tree53ec4f1552ec5d99eaabc9eb46a91a6c74c08343
parentAPIView should inherit RouteView (diff)
Asana integration (#7)
* Event dispatching and webhook sending * snekchek
-rw-r--r--pysite/base_route.py12
-rw-r--r--pysite/constants.py1
-rw-r--r--pysite/views/api/asana.py96
-rw-r--r--requirements.txt1
4 files changed, 108 insertions, 2 deletions
diff --git a/pysite/base_route.py b/pysite/base_route.py
index 04694186..a3e8615b 100644
--- a/pysite/base_route.py
+++ b/pysite/base_route.py
@@ -33,10 +33,18 @@ class APIView(RouteView):
"error_message": "Unknown error"
}
+ http_code = 200
+
if error_code is ErrorCodes.unknown_route:
data["error_message"] = "Unknown API route"
-
- return jsonify(data)
+ http_code = 404
+ elif error_code is ErrorCodes.unauthorized:
+ data["error_message"] = "Unauthorized"
+ http_code = 403
+
+ response = jsonify(data)
+ response.status_code = http_code
+ return response
class ErrorView(BaseView):
diff --git a/pysite/constants.py b/pysite/constants.py
index 8c3d9c1c..e5a283a7 100644
--- a/pysite/constants.py
+++ b/pysite/constants.py
@@ -6,3 +6,4 @@ from enum import IntEnum
class ErrorCodes(IntEnum):
unknown_route = 0
+ unauthorized = 1
diff --git a/pysite/views/api/asana.py b/pysite/views/api/asana.py
new file mode 100644
index 00000000..ca9c5624
--- /dev/null
+++ b/pysite/views/api/asana.py
@@ -0,0 +1,96 @@
+# coding=utf-8
+import json
+import os
+
+from flask import make_response, request
+
+import requests
+
+from pysite.base_route import APIView
+from pysite.constants import ErrorCodes
+
+ASANA_KEY = os.environ.get("ASANA_KEY")
+ASANA_WEBHOOK = os.environ.get("ASANA_WEBHOOK")
+
+COLOUR_RED = 0xFF0000
+COLOUR_GREEN = 0x00FF00
+COLOUR_BLUE = 0x0000FF
+
+
+class IndexView(APIView):
+ path = "/asana/<asana_key>"
+ name = "asana"
+
+ def post(self, asana_key):
+ if asana_key != ASANA_KEY:
+ return self.error(ErrorCodes.unauthorized)
+
+ if "X-Hook-Secret" in request.headers: # Confirm to Asana that we would like to make this hook
+ response = make_response() # type: flask.Response
+ response.headers["X-Hook-Secret"] = request.headers["X-Hook-Secret"]
+ return response
+
+ events = request.get_json()["events"]
+
+ for event in events:
+ func_name = f"asana_{event['type']}_{event['action']}"
+
+ if hasattr(self, func_name):
+ func = getattr(self, func_name)
+ else:
+ func = self.asana_unknown
+
+ try:
+ func(**event)
+ except Exception as e:
+ pretty_event = json.dumps(event, indent=4, sort_key=True)
+
+ try:
+ self.send_webhook(
+ title="Error during webhook",
+ description=f"Failed to handle webhook: {e}\n\n```json\n{pretty_event}\n```",
+ color=COLOUR_RED
+ )
+ except Exception as e:
+ print(f"Fatal error sending webhook: {e}")
+
+ return "", 200 # Empty 200 response
+
+ def send_webhook(self, *, title, description, color=COLOUR_BLUE, url=None, author_name=None, author_icon=None):
+ session = requests.session()
+
+ embed = {
+ "title": title,
+ "description": description,
+ "color": color
+ }
+
+ if url:
+ embed["url"] = url
+
+ if author_name:
+ embed["author"] = {
+ "name": author_name,
+ "icon_url": author_icon
+ }
+
+ session.post(ASANA_WEBHOOK, json={"embeds": [embed]})
+
+ def asana_unknown(self, *, resource, parent, created_at, user, action, _type):
+ pretty_event = json.dumps(
+ {
+ "resource": resource,
+ "parent": parent,
+ "created_at": created_at,
+ "user": user,
+ "action": action,
+ "type": _type
+ },
+ indent=4,
+ sort_key=True
+ )
+
+ self.send_webhook(
+ title="Unknown event",
+ description=f"```json\n{pretty_event}\n```"
+ )
diff --git a/requirements.txt b/requirements.txt
index e6a3877f..101a37f2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
flask==0.12.2
rethinkdb
+requests