From 6276ad441955a0a4aa28f27a336e9c370d113a21 Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Wed, 3 May 2023 10:15:47 +0200 Subject: [PATCH] Stats endpoint (#1716) * update some dependencies: newrelic, gevent that isn't compatible with python 3.11 on mac * update package-lock using npm 9.6.4 and node 20.0 * Add GET /api/stats * update pytest --------- Co-authored-by: Son Nguyen Kim --- app/api/views/user_info.py | 21 ++++++++++++++ docs/api.md | 17 ++++++++++++ poetry.lock | 55 +++++++++++++++---------------------- tests/api/test_user_info.py | 9 ++++++ 4 files changed, 69 insertions(+), 33 deletions(-) diff --git a/app/api/views/user_info.py b/app/api/views/user_info.py index 98368e93..44542a00 100644 --- a/app/api/views/user_info.py +++ b/app/api/views/user_info.py @@ -1,4 +1,5 @@ import base64 +import dataclasses from io import BytesIO from typing import Optional @@ -7,6 +8,7 @@ from flask import jsonify, g, request, make_response from app import s3, config from app.api.base import api_bp, require_api_auth from app.config import SESSION_COOKIE_NAME +from app.dashboard.views.index import get_stats from app.db import Session from app.models import ApiKey, File, PartnerUser, User from app.proton.utils import get_proton_partner @@ -136,3 +138,22 @@ def logout(): response.delete_cookie(SESSION_COOKIE_NAME) return response + + +@api_bp.route("/stats") +@require_api_auth +def user_stats(): + """ + Return stats + + Output as json + - nb_alias + - nb_forward + - nb_reply + - nb_block + + """ + user = g.user + stats = get_stats(user) + + return jsonify(dataclasses.asdict(stats)) diff --git a/docs/api.md b/docs/api.md index 9f9011f8..a4bfac4f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -15,6 +15,7 @@ - [GET /api/user/cookie_token](#get-apiusercookie_token): Get a one time use token to exchange it for a valid cookie - [PATCH /api/user_info](#patch-apiuser_info): Update user's information. - [POST /api/api_key](#post-apiapi_key): Create a new API key. +- [GET /api/stats](#get-apistats): Get user's stats. - [GET /api/logout](#get-apilogout): Log out. [Alias endpoints](#alias-endpoints) @@ -226,6 +227,22 @@ Input: Output: same as GET /api/user_info +#### GET /api/stats + +Given the API Key, return stats about the number of aliases, number of emails forwarded/replied/blocked + +Input: + +- `Authentication` header that contains the api key + +Output: if api key is correct, return a json with the following fields: + +```json +{"nb_alias": 1, "nb_block": 0, "nb_forward": 0, "nb_reply": 0} +``` + +If api key is incorrect, return 401. + #### PATCH /api/sudo Enable sudo mode diff --git a/poetry.lock b/poetry.lock index 8151c483..ccda6b6c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -265,18 +265,6 @@ files = [ {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, ] -[[package]] -name = "atomicwrites" -version = "1.4.0" -description = "Atomic file writes." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] - [[package]] name = "atpublic" version = "2.0" @@ -900,6 +888,21 @@ files = [ dnspython = ">=1.15.0" idna = ">=2.0.0" +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "facebook-sdk" version = "3.1.0" @@ -2381,18 +2384,6 @@ files = [ {file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"}, ] -[[package]] -name = "py" -version = "1.10.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, - {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, -] - [[package]] name = "pyasn1" version = "0.4.8" @@ -2633,29 +2624,27 @@ files = [ [[package]] name = "pytest" -version = "7.0.0" +version = "7.3.1" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "pytest-7.0.0-py3-none-any.whl", hash = "sha256:42901e6bd4bd4a0e533358a86e848427a49005a3256f657c5c8f8dd35ef137a9"}, - {file = "pytest-7.0.0.tar.gz", hash = "sha256:dad48ffda394e5ad9aa3b7d7ddf339ed502e5e365b1350e0af65f4a602344b11"}, + {file = "pytest-7.3.1-py3-none-any.whl", hash = "sha256:3799fa815351fea3a5e96ac7e503a96fa51cc9942c3753cda7651b93c1cfa362"}, + {file = "pytest-7.3.1.tar.gz", hash = "sha256:434afafd78b1d78ed0addf160ad2b77a30d35d4bdf8af234fe621919d9ed15e3"}, ] [package.dependencies] -atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" +tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] [[package]] name = "pytest-cov" diff --git a/tests/api/test_user_info.py b/tests/api/test_user_info.py index 7b67f297..db01d84e 100644 --- a/tests/api/test_user_info.py +++ b/tests/api/test_user_info.py @@ -129,3 +129,12 @@ def test_change_name(flask_client): assert r.json["name"] == "new name" assert user.name == "new name" + + +def test_stats(flask_client): + login(flask_client) + + r = flask_client.get("/api/stats") + + assert r.status_code == 200 + assert r.json == {"nb_alias": 1, "nb_block": 0, "nb_forward": 0, "nb_reply": 0}