diff --git a/README.md b/README.md index 5ac0a32a..a9cc3f78 100644 --- a/README.md +++ b/README.md @@ -1219,6 +1219,34 @@ If success, 200. } ``` +### Notification endpoints +#### GET /api/notifications + +Get notifications + +Input: +- `Authentication` in header: the api key +- page in url: the page number, starts at 0 + +Output: +List of notification, each notification has: +- id +- message: the message in html +- read: whether the user has read the notification +- created_at: when the notification is created + +#### POST /api/notifications/:notification_id + +Mark a notification as read + +Input: +- `Authentication` in header: the api key +- notification_id in url: the page number, starts at 0 + +Output: +200 if success + + ### Misc endpoints #### POST /api/apple/process_payment diff --git a/app/api/__init__.py b/app/api/__init__.py index 71c76a3c..44c541ed 100644 --- a/app/api/__init__.py +++ b/app/api/__init__.py @@ -8,4 +8,5 @@ from .views import ( alias, apple, mailbox, + notification, ) diff --git a/app/api/views/notification.py b/app/api/views/notification.py new file mode 100644 index 00000000..dc386b89 --- /dev/null +++ b/app/api/views/notification.py @@ -0,0 +1,81 @@ +from time import sleep + +from flask import g +from flask import jsonify +from flask import request +from flask_cors import cross_origin + +from app.api.base import api_bp, require_api_auth +from app.config import PAGE_LIMIT +from app.extensions import db +from app.models import Notification + + +@api_bp.route("/notifications", methods=["GET"]) +@cross_origin() +@require_api_auth +def get_notifications(): + """ + Get notifications + + Input: + - page: in url. Starts at 0 + + Output: list of notifications. Each notification has the following field: + - id + - message + - read + - created_at + """ + user = g.user + try: + page = int(request.args.get("page")) + except (ValueError, TypeError): + return jsonify(error="page must be provided in request query"), 400 + + notifications = ( + Notification.query.filter_by(user_id=user.id) + .order_by(Notification.read, Notification.created_at.desc()) + .limit(PAGE_LIMIT) + .offset(page * PAGE_LIMIT) + .all() + ) + + return ( + jsonify( + [ + { + "id": notification.id, + "message": notification.message, + "read": notification.read, + "created_at": notification.created_at.humanize(), + } + for notification in notifications + ] + ), + 200, + ) + + +@api_bp.route("/notifications//read", methods=["POST"]) +@cross_origin() +@require_api_auth +def mark_as_read(notification_id): + """ + Mark a notification as read + Input: + notification_id: in url + Output: + 200 if updated successfully + + """ + user = g.user + notification = Notification.get(notification_id) + + if not notification or notification.user_id != user.id: + return jsonify(error="Forbidden"), 403 + + notification.read = True + db.session.commit() + + return jsonify(done=True), 200 diff --git a/tests/api/test_notification.py b/tests/api/test_notification.py new file mode 100644 index 00000000..00a27167 --- /dev/null +++ b/tests/api/test_notification.py @@ -0,0 +1,63 @@ +from flask import url_for + +from app.extensions import db +from app.models import User, ApiKey, Notification + + +def test_get_notifications(flask_client): + user = User.create( + email="a@b.c", password="password", name="Test User", activated=True + ) + db.session.commit() + + # create api_key + api_key = ApiKey.create(user.id, "for test") + db.session.commit() + + # create some notifications + Notification.create(user_id=user.id, message="Test message 1") + Notification.create(user_id=user.id, message="Test message 2") + db.session.commit() + + r = flask_client.get( + url_for("api.get_notifications", page=0), + headers={"Authentication": api_key.code}, + ) + + assert r.status_code == 200 + assert len(r.json) == 2 + for n in r.json: + assert n["id"] > 0 + assert n["message"] + assert n["read"] is False + assert n["created_at"] + + # no more post at the next page + r = flask_client.get( + url_for("api.get_notifications", page=1), + headers={"Authentication": api_key.code}, + ) + assert len(r.json) == 0 + + +def test_mark_notification_as_read(flask_client): + user = User.create( + email="a@b.c", password="password", name="Test User", activated=True + ) + db.session.commit() + + # create api_key + api_key = ApiKey.create(user.id, "for test") + db.session.commit() + + Notification.create(id=1, user_id=user.id, message="Test message 1") + db.session.commit() + + r = flask_client.post( + url_for("api.mark_as_read", notification_id=1), + headers={"Authentication": api_key.code}, + ) + + assert r.status_code == 200 + notification = Notification.get(1) + assert notification.read