diff --git a/README.md b/README.md index 56c2d306..ec5d7f80 100644 --- a/README.md +++ b/README.md @@ -1395,6 +1395,17 @@ Return user setting } ``` +#### PATCH /api/setting + +Update user setting. All input fields are optional. + +Input: +- alias_generator (string): uuid or word +- notification (boolean): true or false +- random_alias_default_domain (string): one of the domains returned by `GET /api/setting/domains` + +Output: same as `GET /api/setting` + #### GET /api/setting/domains Return domains that user can use to create random alias diff --git a/app/api/views/setting.py b/app/api/views/setting.py index 6a81b8bf..0873d7d3 100644 --- a/app/api/views/setting.py +++ b/app/api/views/setting.py @@ -1,14 +1,16 @@ -from flask import jsonify, g +from flask import jsonify, g, request from app.api.base import api_bp, require_api_auth -from app.models import User, AliasGeneratorEnum +from app.extensions import db +from app.log import LOG +from app.models import User, AliasGeneratorEnum, SLDomain, CustomDomain def setting_to_dict(user: User): ret = { "notification": user.notification, "alias_generator": "word" - if user.alias_generator == AliasGeneratorEnum.word + if user.alias_generator == AliasGeneratorEnum.word.value else "uuid", "random_alias_default_domain": user.default_random_alias_domain(), } @@ -27,6 +29,61 @@ def get_setting(): return jsonify(setting_to_dict(user)) +@api_bp.route("/setting", methods=["PATCH"]) +@require_api_auth +def update_setting(): + """ + Update user setting + Input: + - notification: bool + - alias_generator: word|uuid + - random_alias_default_domain: str + """ + user = g.user + data = request.get_json() or {} + + if "notification" in data: + user.notification = data["notification"] + + if "alias_generator" in data: + alias_generator = data["alias_generator"] + if alias_generator not in ["word", "uuid"]: + return jsonify(error="Invalid alias_generator"), 400 + + if alias_generator == "word": + user.alias_generator = AliasGeneratorEnum.word.value + else: + user.alias_generator = AliasGeneratorEnum.uuid.value + + if "random_alias_default_domain" in data: + default_domain = data["random_alias_default_domain"] + sl_domain: SLDomain = SLDomain.get_by(domain=default_domain) + if sl_domain: + if sl_domain.premium_only and not user.is_premium(): + return jsonify(error="You cannot use this domain"), 400 + + # make sure only default_random_alias_domain_id or default_random_alias_public_domain_id is set + user.default_random_alias_public_domain_id = sl_domain.id + user.default_random_alias_domain_id = None + else: + custom_domain = CustomDomain.get_by(domain=default_domain) + if not custom_domain: + return jsonify(error="invalid domain"), 400 + + # sanity check + if custom_domain.user_id != user.id or not custom_domain.verified: + LOG.exception("%s cannot use domain %s", user, default_domain) + return jsonify(error="invalid domain"), 400 + else: + # make sure only default_random_alias_domain_id or + # default_random_alias_public_domain_id is set + user.default_random_alias_domain_id = custom_domain.id + user.default_random_alias_public_domain_id = None + + db.session.commit() + return jsonify(setting_to_dict(user)) + + @api_bp.route("/setting/domains") @require_api_auth def get_available_domains_for_random_alias(): diff --git a/tests/api/test_setting.py b/tests/api/test_setting.py index 3b700175..8ccf3c93 100644 --- a/tests/api/test_setting.py +++ b/tests/api/test_setting.py @@ -1,4 +1,6 @@ -from app.models import CustomDomain +import json + +from app.models import CustomDomain, AliasGeneratorEnum from tests.utils import login @@ -8,13 +10,50 @@ def test_get_setting(flask_client): r = flask_client.get("/api/setting") assert r.status_code == 200 assert r.json == { - "alias_generator": "uuid", + "alias_generator": "word", "notification": True, "random_alias_default_domain": "sl.local", } -def test_get_setting(flask_client): +def test_update_settings_notification(flask_client): + user = login(flask_client) + assert user.notification + + r = flask_client.patch("/api/setting", json={"notification": False}) + assert r.status_code == 200 + assert not user.notification + + +def test_update_settings_alias_generator(flask_client): + user = login(flask_client) + assert user.alias_generator == AliasGeneratorEnum.word.value + + r = flask_client.patch("/api/setting", json={"alias_generator": "invalid"}) + assert r.status_code == 400 + + r = flask_client.patch("/api/setting", json={"alias_generator": "uuid"}) + assert r.status_code == 200 + assert user.alias_generator == AliasGeneratorEnum.uuid.value + + +def test_update_settings_random_alias_default_domain(flask_client): + user = login(flask_client) + assert user.default_random_alias_domain() == "sl.local" + + r = flask_client.patch( + "/api/setting", json={"random_alias_default_domain": "invalid"} + ) + assert r.status_code == 400 + + r = flask_client.patch( + "/api/setting", json={"random_alias_default_domain": "d1.test"} + ) + assert r.status_code == 200 + assert user.default_random_alias_domain() == "d1.test" + + +def test_get_setting_domains(flask_client): user = login(flask_client) CustomDomain.create(user_id=user.id, domain="ab.cd", verified=True, commit=True)