Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Локализация #24

Merged
merged 2 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading