Skip to content

Commit

Permalink
Локализация (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
grigoriev-semyon committed Apr 5, 2024
1 parent 11a7cad commit d08c06a
Show file tree
Hide file tree
Showing 11 changed files with 59 additions and 27 deletions.
1 change: 1 addition & 0 deletions migrations/versions/f8c57101c0f6_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
Create Date: 2023-05-09 12:48:25.550608
"""

import sqlalchemy as sa
from alembic import op

Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
alembic
auth-lib-profcomff[fastapi]
fastapi
fastapi==0.108.0
fastapi-sqlalchemy
gunicorn
logging-profcomff
Expand Down
14 changes: 7 additions & 7 deletions tests/test_routes/test_user_post_then_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def test_create_new(dbsession, client, param, source, admin_source):
dbsession.expire_all()
assert response_upd.status_code == 200
response_get = client.get("/user/0")
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert {"category": param.category.name, "param": param.name, "value": "admin_info"} in list(
response_get.json()["items"]
)
Expand Down Expand Up @@ -63,7 +63,7 @@ def test_delete(dbsession, client, param, admin_source):
dbsession.expire_all()
response_get = client.get("/user/0")
assert response_upd.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert response_get.status_code == 404
dbsession.delete(info1)
dbsession.commit()
Expand Down Expand Up @@ -94,7 +94,7 @@ def test_update(dbsession, client, param, admin_source):
response_get = client.get("/user/0")
assert response_upd.status_code == 200
assert response_get.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert {"category": param.category.name, "param": param.name, "value": "new"} in list(response_get.json()["items"])
assert len(response_get.json()["items"]) == 1
dbsession.delete(info1)
Expand Down Expand Up @@ -156,7 +156,7 @@ def test_update_not_changeable_with_scopes(dbsession, client, param, admin_sourc
response_get = client.get("/user/0")
assert response_get.status_code == 200
assert response_upd.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert {"category": param.category.name, "param": param.name, "value": "new"} in list(response_get.json()["items"])
assert len(response_get.json()["items"]) == 1
dbsession.delete(info1)
Expand All @@ -180,7 +180,7 @@ def test_create_new_no_category(dbsession, client, param, admin_source):
response_get = client.get("/user/0")
assert response_get.status_code == 200
assert response_upd.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert {"category": param.category.name, "param": param.name, "value": "new"} in list(response_get.json()["items"])
assert len(response_get.json()["items"]) == 1
info_new = (
Expand Down Expand Up @@ -211,7 +211,7 @@ def test_update_no_read_scope(dbsession, client, param, admin_source):
response_get = client.get("/user/0")
assert response_upd.status_code == 200
assert response_get.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert response_get.json() == {"items": []}
assert info1.value == "new"
dbsession.delete(info1)
Expand Down Expand Up @@ -244,7 +244,7 @@ def test_update_from_user_source(dbsession, client, param, source):
dbsession.expire_all()
response_get = client.get("/user/0")
assert response_upd.status_code == 200
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded'}
assert response_upd.json() == {'status': 'Success', 'message': 'User patch succeeded', 'ru': 'Изменение успешно'}
assert response_get.status_code == 200
assert {"category": param.category.name, "param": param.name, "value": "new_user_info"} in list(
response_get.json()["items"]
Expand Down
26 changes: 19 additions & 7 deletions userdata_api/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
class ObjectNotFound(Exception):
class UserDataApiError(Exception):
def __init__(self, error_en: str, error_ru: str) -> None:
self.en = error_en
self.ru = error_ru
super().__init__(error_en)


class ObjectNotFound(UserDataApiError):
def __init__(self, obj: type, obj_id_or_name: int | str):
super().__init__(f"Object {obj.__name__} {obj_id_or_name=} not found")
super().__init__(
f"Object {obj.__name__} {obj_id_or_name=} not found",
f"Объект {obj.__name__} с идиентификатором {obj_id_or_name=} не найден",
)


class AlreadyExists(Exception):
class AlreadyExists(UserDataApiError):
def __init__(self, obj: type, obj_id_or_name: int | str):
super().__init__(f"Object {obj.__name__} {obj_id_or_name=} already exists")
super().__init__(
f"Object {obj.__name__} {obj_id_or_name=} already exists",
f"Объект {obj.__name__} с идиентификатором {obj_id_or_name=} уже существует",
)


class Forbidden(Exception):
def __init__(self, msg: str):
super().__init__(msg)
class Forbidden(UserDataApiError):
pass
2 changes: 1 addition & 1 deletion userdata_api/routes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ async def delete_category(
"""
_: Category = Category.get(id, session=db.session)
Category.delete(id, session=db.session)
return StatusResponseModel(status="Success", message="Category deleted")
return StatusResponseModel(status="Success", message="Category deleted", ru="Категория удалена")
12 changes: 9 additions & 3 deletions userdata_api/routes/exc_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@

@app.exception_handler(ObjectNotFound)
async def not_found_handler(req: starlette.requests.Request, exc: ObjectNotFound):
return JSONResponse(content=StatusResponseModel(status="Error", message=f"{exc}").dict(), status_code=404)
return JSONResponse(
content=StatusResponseModel(status="Error", message=exc.en, ru=exc.ru).model_dump(), status_code=404
)


@app.exception_handler(Forbidden)
async def forbidden_handler(req: starlette.requests.Request, exc: Forbidden):
return JSONResponse(content=StatusResponseModel(status="Forbidden", message=f"{exc}").dict(), status_code=403)
return JSONResponse(
content=StatusResponseModel(status="Forbidden", message=exc.en, ru=exc.ru).model_dump(), status_code=403
)


@app.exception_handler(AlreadyExists)
async def already_exists_handler(req: starlette.requests.Request, exc: AlreadyExists):
return JSONResponse(content=StatusResponseModel(status="Already exists", message=f"{exc}").dict(), status_code=409)
return JSONResponse(
content=StatusResponseModel(status="Already exists", message=exc.en, ru=exc.ru).model_dump(), status_code=409
)
2 changes: 1 addition & 1 deletion userdata_api/routes/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,4 @@ async def delete_param(
raise ObjectNotFound(Param, id)
res.is_deleted = True
db.session.commit()
return StatusResponseModel(status="Success", message="Param deleted")
return StatusResponseModel(status="Success", message="Param deleted", ru="Параметр удален")
2 changes: 1 addition & 1 deletion userdata_api/routes/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ async def delete_source(
:return: None
"""
Source.delete(id, session=db.session)
return StatusResponseModel(status="Success", message="Source deleted")
return StatusResponseModel(status="Success", message="Source deleted", ru="Источник удален")
2 changes: 1 addition & 1 deletion userdata_api/routes/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ async def update_user(
:return:
"""
await patch(new_info, id, user)
return StatusResponseModel(status='Success', message='User patch succeeded')
return StatusResponseModel(status='Success', message='User patch succeeded', ru="Изменение успешно")
1 change: 1 addition & 0 deletions userdata_api/schemas/response_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
class StatusResponseModel(Base):
status: str
message: str
ru: str
22 changes: 17 additions & 5 deletions userdata_api/utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,17 @@ async def patch_user_info(new: UserInfoUpdate, user_id: int, user: dict[str, int
"""
scope_names = tuple(scope["name"] for scope in user["session_scopes"])
if new.source == "admin" and "userdata.info.admin" not in scope_names:
raise Forbidden(f"Admin source requires 'userdata.info.admin' scope")
raise Forbidden(
"Admin source requires 'userdata.info.admin' scope",
"Источник 'администратор' требует право 'userdata.info.admin'",
)
if new.source != "admin" and new.source != "user":
raise Forbidden("HTTP protocol applying only 'admin' and 'user' source")
raise Forbidden(
"HTTP protocol applying only 'admin' and 'user' source",
"Данный источник информации не обновляется через HTTP",
)
if new.source == "user" and user["id"] != user_id:
raise Forbidden(f"'user' source requires information own")
raise Forbidden("'user' source requires information own", "Требуется владение информацией")
for item in new.items:
param = (
db.session.query(Param)
Expand All @@ -54,7 +60,10 @@ async def patch_user_info(new: UserInfoUpdate, user_id: int, user: dict[str, int
and not (new.source == "user" and user["id"] == user_id)
):
db.session.rollback()
raise Forbidden(f"Updating category {param.category.name=} requires {param.category.update_scope=} scope")
raise Forbidden(
f"Updating category {param.category.name=} requires {param.category.update_scope=} scope",
f"Обновление категории {param.category.name=} требует {param.category.update_scope=} права",
)
info = (
db.session.query(Info)
.join(Source)
Expand All @@ -80,7 +89,10 @@ async def patch_user_info(new: UserInfoUpdate, user_id: int, user: dict[str, int
if item.value is not None:
if not param.changeable and "userdata.info.update" not in scope_names:
db.session.rollback()
raise Forbidden(f"Param {param.name=} change requires 'userdata.info.update' scope")
raise Forbidden(
f"Param {param.name=} change requires 'userdata.info.update' scope",
f"Изменение {param.name=} параметра требует 'userdata.info.update' права",
)
info.value = item.value
db.session.flush()
continue
Expand Down

0 comments on commit d08c06a

Please sign in to comment.