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

14 скрытие части юзердаты от пользователя #23

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Add visible_in_user_response field to Param table

Revision ID: 5a6490c55c81
Revises: 0932ab8ca14a
Create Date: 2024-09-03 10:54:14.554987

"""

import sqlalchemy as sa
from alembic import op


# revision identifiers, used by Alembic.
revision = '5a6490c55c81'
down_revision = '0932ab8ca14a'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('param', sa.Column('visible_in_user_response', sa.Boolean(), nullable=False))
# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('param', 'visible_in_user_response')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions userdata_api/models/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Param(BaseDbModel):
а параметры эти могут лежать в категории "контакты"
"""

visible_in_user_response: Mapped[bool] = mapped_column(Boolean, default=True)
name: Mapped[str] = mapped_column(String)
category_id: Mapped[int] = mapped_column(Integer, ForeignKey(Category.id))
is_required: Mapped[bool] = mapped_column(Boolean, default=False)
Expand Down
10 changes: 7 additions & 3 deletions userdata_api/routes/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
@user.get("/{id}", response_model=UserInfoGet)
async def get_user_info(
id: int,
additional_data: list[int] = Query(default=[]),
user: dict[str, Any] = Depends(UnionAuth(scopes=[], allow_none=False, auto_error=True)),
) -> UserInfoGet:
"""
Получить информацию о пользователе
\f
:param id: Айди овнера информации(пользователя)
:additional_data: список невидимых по дефолту параметров
:param user: Аутентфикация
:return: Словарь, ключи - категории на которые хватило прав(овнеру не нужны права, он получает всё).
Значения - словари, ключи которых - имена параметров,
Expand All @@ -34,7 +36,8 @@ async def get_user_info(
...
}
"""
return UserInfoGet.model_validate(await get(id, user))

return UserInfoGet.model_validate(await get(id, user, additional_data))


@user.post("/{id}", response_model=StatusResponseModel)
Expand All @@ -57,7 +60,7 @@ async def update_user(
Чтобы обновить от имени админиа, надо иметь скоуп `userdata.info.admin`
Чтобы обновить неизменяемую информацию надо обладать скоупом `userdata.info.update`
Для обновления своей информации(источник `user`) не нужны скоупы на обновление соответствующих категорий
Для обновления чужой информации от имени админа(источник `admin`)
Для обновления чужой информации от имени админа(источник `admin`)
нужны скоупы на обновление всех указанных в теле запроса категорий пользовательских данных данных
\f
:param request: Запрос из fastapi
Expand All @@ -75,11 +78,12 @@ async def get_users_info(
users: list[int] = Query(),
categories: list[int] = Query(),
user: dict[str, Any] = Depends(UnionAuth(scopes=["userdata.info.admin"], allow_none=False, auto_error=True)),
additional_data: list[int] = Query(default=[]),
) -> UsersInfoGet:
"""
Получить информацию о пользователях.
:param users: список id юзеров, про которых нужно вернуть информацию
:param categories: список id категорий, параметры которых нужно вернуть
:return: список данных о пользователях и данных категориях
"""
return UsersInfoGet.model_validate(await get_users(users, categories, user))
return UsersInfoGet.model_validate(await get_users(users, categories, user, additional_data))
2 changes: 2 additions & 0 deletions userdata_api/schemas/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@


class ParamPost(Base):
visible_in_user_response: bool = True
gitfresnel marked this conversation as resolved.
Show resolved Hide resolved
name: constr(min_length=1)
is_required: bool
changeable: bool
Expand All @@ -14,6 +15,7 @@ class ParamPost(Base):


class ParamPatch(Base):
visible_in_user_response: bool = True
gitfresnel marked this conversation as resolved.
Show resolved Hide resolved
name: constr(min_length=1) | None = None
is_required: bool | None = None
changeable: bool | None = None
Expand Down
7 changes: 2 additions & 5 deletions userdata_api/schemas/types/scope.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import string
from typing import Any
from typing import Any, Callable

from pydantic._internal import _schema_generation_shared
from pydantic.json_schema import JsonSchemaValue
Expand All @@ -16,10 +16,7 @@ class Scope:
"""

@classmethod
def __get_pydantic_core_schema__(
cls,
source: type[Any],
) -> core_schema.CoreSchema:
def __get_pydantic_core_schema__(cls, source: type[Any], handler: Callable) -> core_schema.CoreSchema:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from pydantic import GetCoreSchemaHandler, TypeAdapter


class Username(str):
    @classmethod
    def __get_pydantic_core_schema__(
        cls, source_type: Any, handler: GetCoreSchemaHandler
    ) -> CoreSchema:
        return core_schema.no_info_after_validator_function(cls, handler(str))

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нужно использовать тип GetCoreSchemaHandler а не Callable

return core_schema.general_after_validator_function(cls._validate, core_schema.str_schema())

@classmethod
Expand Down
26 changes: 20 additions & 6 deletions userdata_api/utils/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from re import search

from fastapi_sqlalchemy import db
from sqlalchemy import not_
from sqlalchemy import String, cast, func, not_, or_

from userdata_api.exceptions import Forbidden, InvalidValidation, ObjectNotFound
from userdata_api.models.db import Category, Info, Param, Source, ViewType
Expand Down Expand Up @@ -110,7 +110,10 @@ async def patch_user_info(new: UserInfoUpdate, user_id: int, user: dict[str, int


async def get_users_info(
user_ids: list[int], category_ids: list[int] | None, user: dict[str, int | list[dict[str, str | int]]]
user_ids: list[int],
category_ids: list[int] | None,
user: dict[str, int | list[dict[str, str | int]]],
additional_data: list[int],
) -> list[dict[str, str | None]]:
""".
Возвращает информацию о данных пользователей в указанных категориях
Expand All @@ -132,6 +135,10 @@ async def get_users_info(
not_(Param.is_deleted),
not_(Category.is_deleted),
not_(Info.is_deleted),
or_(
Param.visible_in_user_response,
Param.id.in_(additional_data),
),
)
)
if not is_single_user:
Expand Down Expand Up @@ -205,7 +212,10 @@ async def get_users_info(


async def get_users_info_batch(
user_ids: list[int], category_ids: list[int], user: dict[str, int | list[dict[str, str | int]]]
user_ids: list[int],
category_ids: list[int],
user: dict[str, int | list[dict[str, str | int]]],
additional_data: list[int],
) -> UsersInfoGet:
""".
Возвращает информацию о данных пользователей в указанных категориях
Expand All @@ -215,10 +225,13 @@ async def get_users_info_batch(
:param user: Сессия выполняющего запрос данных
:return: Список словарей содержащих id пользователя, категорию, параметр категории и значение этого параметра у пользователя
"""
return UsersInfoGet(items=await get_users_info(user_ids, category_ids, user))

return UsersInfoGet(items=await get_users_info(user_ids, category_ids, user, additional_data))

async def get_user_info(user_id: int, user: dict[str, int | list[dict[str, str | int]]]) -> UserInfoGet:

async def get_user_info(
user_id: int, user: dict[str, int | list[dict[str, str | int]]], additional_data: list[int]
) -> UserInfoGet:
"""Возвращает информауию о пользователе в соотетствии с переданным токеном.

Пользователь может прочитать любую информацию о себе
Expand All @@ -229,7 +242,8 @@ async def get_user_info(user_id: int, user: dict[str, int | list[dict[str, str |
:param user: Сессия выполняющего запрос данных
:return: Список словарей содержащих категорию, параметр категории и значение этого параметра у пользователя
"""
result = await get_users_info([user_id], None, user)

result = await get_users_info([user_id], None, user, additional_data)
for value in result:
del value["user_id"]
return UserInfoGet(items=result)
Loading