Skip to content
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
29 changes: 29 additions & 0 deletions migrations/versions/52230bfc3f86_56_add_is_hidden_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""#56_add_is_hidden_field

Revision ID: 52230bfc3f86
Revises: 1bb798899506
Create Date: 2024-07-29 20:05:34.777852

"""

import sqlalchemy as sa
from alembic import op


# revision identifiers, used by Alembic.
revision = '52230bfc3f86'
down_revision = '1bb798899506'
branch_labels = None
depends_on = None


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


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('button', 'is_hidden')
# ### end Alembic commands ###
1 change: 1 addition & 0 deletions services_backend/models/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Button(Base):
icon: Mapped[str] = mapped_column(String)
link: Mapped[str] = mapped_column(String)
type: Mapped[Type] = mapped_column(DbEnum(Type, native_enum=False), nullable=False)
is_hidden: Mapped[bool] = mapped_column(Boolean, default=False)

_scopes: Mapped[list[Scope]] = relationship("Scope", back_populates="button", lazy='joined', cascade='delete')

Expand Down
23 changes: 16 additions & 7 deletions services_backend/routes/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ class ButtonCreate(Base):
name: str = Field(description='Название кнопки')
link: str = Field(description='Ссылка, на которую перенаправляет кнопка')
type: Type = Field(description='Тип открываемой ссылки (Ссылка приложения/Браузер в приложении/Браузер')
required_scopes: set[str] | None = Field(description='Каким скоупы нужны, чтобы кнопка была доступна', default=None)
optional_scopes: set[str] | None = Field(description='Каким скоупы желательны', default=None)
is_hidden: bool = Field(description='Спрятана ли кнопка от пользователя', default=False)
required_scopes: set[str] | None = Field(description='Какие скоупы нужны, чтобы кнопка была доступна', default=None)
optional_scopes: set[str] | None = Field(description='Какиеп скоупы желательны', default=None)


class ButtonUpdate(Base):
Expand All @@ -35,13 +36,15 @@ class ButtonUpdate(Base):
type: Type | None = Field(
description='Тип открываемой ссылки (Ссылка приложения/Браузер в приложении/Браузер', default=None
)
required_scopes: set[str] | None = Field(description='Каким скоупы нужны, чтобы кнопка была доступна', default=None)
optional_scopes: set[str] | None = Field(description='Каким скоупы желательны', default=None)
is_hidden: bool | None = Field(description='Спрятана ли кнопка от пользователя', default=None)
required_scopes: set[str] | None = Field(description='Какие скоупы нужны, чтобы кнопка была доступна', default=None)
optional_scopes: set[str] | None = Field(description='Какие скоупы желательны', default=None)


class ButtonView(Enum):
ACTIVE = "active"
BLOCKED = "blocked"
HIDDEN = "hidden"


class ButtonGet(Base):
Expand Down Expand Up @@ -120,7 +123,9 @@ def get_buttons(
for button in category.buttons:
view = ButtonView.ACTIVE
scopes = set()
if button.required_scopes - user_scopes:
if button.is_hidden:
view = ButtonView.HIDDEN
elif button.required_scopes - user_scopes:
view = ButtonView.BLOCKED
else:
scopes |= button.required_scopes
Expand Down Expand Up @@ -169,7 +174,9 @@ def get_button(
raise HTTPException(status_code=404, detail="Button is not this category")
view = ButtonView.ACTIVE
scopes = set()
if button.required_scopes - user_scopes:
if button.is_hidden:
view = ButtonView.HIDDEN
elif button.required_scopes - user_scopes:
view = ButtonView.BLOCKED
else:
scopes |= button.required_scopes
Expand Down Expand Up @@ -297,7 +304,9 @@ def get_service(
raise HTTPException(status_code=404, detail="Button does not exist")
view = ButtonView.ACTIVE
scopes = set()
if button.required_scopes - user_scopes:
if button.is_hidden:
view = ButtonView.HIDDEN
elif button.required_scopes - user_scopes:
view = ButtonView.BLOCKED
else:
scopes |= button.required_scopes
Expand Down
8 changes: 5 additions & 3 deletions services_backend/routes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from fastapi_sqlalchemy import db
from pydantic import Field, conint

from services_backend.models.database import Button, Category, Type
from services_backend.models.database import Button, Category
from services_backend.schemas import Base

from .button import ButtonGet, ButtonView
Expand Down Expand Up @@ -109,7 +109,9 @@ def get_categories(
for button in row.buttons:
view = ButtonView.ACTIVE
scopes = set()
if button.required_scopes - user_scopes:
if button.is_hidden:
view = ButtonView.HIDDEN
elif button.required_scopes - user_scopes:
view = ButtonView.BLOCKED
else:
scopes |= button.required_scopes
Expand All @@ -119,7 +121,7 @@ def get_categories(
"id": button.id,
"icon": button.icon,
"name": button.name,
"link": (button.link if view == ButtonView.ACTIVE else None),
"link": button.link if view == ButtonView.ACTIVE else None,
"order": button.order,
"type": button.type,
"view": view.value,
Expand Down
63 changes: 63 additions & 0 deletions tests/api/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,66 @@ def test_type_not_enum(client, dbsession, db_category):
}
res = client.post(f"/category/{db_category.id}/button", data=json.dumps(body))
assert res.status_code == status.HTTP_422_UNPROCESSABLE_ENTITY

Comment thread
grigoriev-semyon marked this conversation as resolved.

def test_post_hidden_success(client, dbsession, db_category):
body = {"icon": "qq", "name": "new", "link": "google.com", "type": "inapp", "is_hidden": True}
res = client.post(f"/category/{db_category.id}/button", data=json.dumps(body))
assert res.status_code == status.HTTP_200_OK
res_body = res.json()
assert res_body["icon"] == body["icon"]
assert res_body["order"] == 1
assert res_body["name"] == body["name"]
assert res_body["link"] == body["link"]
assert res_body["type"] == body["type"]
db_button_created: Button = dbsession.query(Button).filter(Button.id == res_body["id"]).one_or_none()
assert db_button_created
assert db_button_created.icon == body["icon"]
assert db_button_created.name == body["name"]
assert db_button_created.category == db_category
assert db_button_created.link == body["link"]
assert db_button_created.type == body["type"]
assert db_button_created.order == 1
Comment thread
grigoriev-semyon marked this conversation as resolved.
assert db_button_created.is_hidden == True
dbsession.delete(db_button_created)
dbsession.commit()


def test_get_hidden_by_id_success(client, dbsession, db_button, db_category):
db_button.is_hidden = True
Comment thread
grigoriev-semyon marked this conversation as resolved.
dbsession.commit()
res = client.get(f"/category/{db_category.id}/button/{db_button.id}")
assert res.status_code == status.HTTP_200_OK
res_body = res.json()
assert res_body['icon'] == db_button.icon
assert res_body['name'] == db_button.name
assert res_body['order'] == db_button.order
assert res_body['link'] is None
assert res_body['type'] == db_button.type
Comment thread
grigoriev-semyon marked this conversation as resolved.
assert res_body['view'] == "hidden"


def test_patch_to_hide_success(client, db_button, db_category):
body = {"is_hidden": True}
res = client.patch(f"/category/{db_category.id}/button/{db_button.id}", data=json.dumps(body))
assert res.status_code == status.HTTP_200_OK
res_body = res.json()
assert res_body["icon"] == db_button.icon
assert res_body["order"] == db_button.order
assert res_body["name"] == db_button.name
assert res_body["link"] == db_button.link
assert res_body["type"] == db_button.type
assert res_body["is_hidden"] == True
res = client.get(f"/category/{db_category.id}/button/{db_button.id}")
assert res.status_code == status.HTTP_200_OK
assert res.json()["link"] is None
assert res.json()["view"] == "hidden"


def test_delete_hidden_success(client, dbsession, db_button, db_category):
db_button.is_hidden = True
dbsession.commit()
res = client.delete(f"/category/{db_category.id}/button/{db_button.id}")
assert res.status_code == status.HTTP_200_OK
q = dbsession.query(Button).filter(Button.id == db_button.id)
assert not q.one_or_none()
20 changes: 19 additions & 1 deletion tests/api/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,10 @@ def test_delete_order(db_category, client):

res = client.get(f"/category/{res1.json()['id']}")
assert res.json()['order'] == 1
client.delete(f"/category/{res1.json()['id']}")


def test_scopes(client, mocker: MockerFixture):
def test_scopes(client, dbsession, mocker: MockerFixture):
user_mock = mocker.patch('auth_lib.fastapi.UnionAuth.__call__')
user_mock.return_value = {
"session_scopes": [{"id": 0, "name": "string", "comment": "string"}],
Expand Down Expand Up @@ -238,3 +239,20 @@ def test_scopes(client, mocker: MockerFixture):

res8 = client.get(f'/category/{id_}')
assert res8.status_code == status.HTTP_404_NOT_FOUND

category = dbsession.query(Category).filter(Category.id == id_).one_or_none()
dbsession.delete(category)
dbsession.commit()


def test_get_hidden_button_success(client, dbsession, db_button):
db_button.is_hidden = True
dbsession.commit()
res = client.get('/category', params={"info": "buttons"})
assert res.status_code == status.HTTP_200_OK
res_body = res.json()
assert len(res_body) == 1
assert len(res_body[0]["buttons"]) == 1
assert res_body[0]["buttons"][0]["id"] == db_button.id
assert res_body[0]["buttons"][0]["view"] == "hidden"
assert "link" not in res_body[0]["buttons"][0]