Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7274c3d
Adding #10
Wudext Feb 2, 2023
f2fbb3b
Adding #10
Wudext Feb 2, 2023
fb3d11d
Fixing
Wudext Feb 2, 2023
9c687e4
Delete workspace.xml
Wudext Feb 2, 2023
8788f75
Delete .idea directory
Wudext Feb 2, 2023
47f3744
Update button.py
Wudext Feb 2, 2023
b7b06a1
Merge branch 'Adding-#10' of https://github.com/Wudext/services-api i…
Wudext Feb 2, 2023
68a2e3c
Fixing
Wudext Feb 2, 2023
c3fe104
Fixing tests
Wudext Feb 3, 2023
e953dd1
Update .gitignore
Wudext Feb 3, 2023
5309f9a
Update 670f4caac7dd_init.py
Wudext Feb 3, 2023
97fe09a
Update 28d38f9e2e65_.py
Wudext Feb 3, 2023
79ef359
Update conftest.py
Wudext Feb 3, 2023
ecc46ab
Update button.py
Wudext Feb 3, 2023
b52308e
Update category.py
Wudext Feb 3, 2023
e97c2dc
Tests
Wudext Feb 11, 2023
b57f513
Fixing
Wudext Feb 11, 2023
714cada
migrations + drop db fix
parfenovma Feb 11, 2023
ec9f951
Adding tests
Wudext Feb 11, 2023
7a3e37e
Merge branch 'Adding-#10' of https://github.com/Wudext/services-api i…
Wudext Feb 11, 2023
e0f525f
2-step-migrations
parfenovma Feb 11, 2023
da977ae
Tests release
Wudext Feb 11, 2023
91d4070
Merge branch 'Adding-#10' of https://github.com/Wudext/services-api i…
Wudext Feb 11, 2023
d407505
Changing logics
Wudext Feb 12, 2023
59f7247
Fixing
Wudext Feb 12, 2023
a72e1cc
Delete 4ea57f3ba3ed_.py
Wudext Feb 12, 2023
2cce4d1
Tests initialisation
Wudext Feb 12, 2023
5522c9c
Update category.py
Wudext Feb 12, 2023
0ab58bc
Spaces (xd)
Wudext Feb 12, 2023
f55319d
REST routes
Wudext Feb 12, 2023
d03ce1b
Fixing tests
Wudext Feb 13, 2023
08d0084
Update button.py
Wudext Feb 13, 2023
832f3a3
Backend Initialisation
Wudext Feb 14, 2023
50b9832
Update category.py
Wudext Feb 14, 2023
c744675
Delete .idea directory
Wudext Feb 14, 2023
6b3d2ab
Update button.py
Wudext Feb 14, 2023
5a5cffd
Fixing
Wudext Feb 14, 2023
bdff039
Merge branch 'Adding-#10' of https://github.com/Wudext/services-api i…
Wudext Feb 14, 2023
dca9171
Flake8 and Black
Wudext Feb 14, 2023
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ venv.bak/
dmypy.json

# Pyre type checker
.pyre/
.pyre/
29 changes: 17 additions & 12 deletions migrations/versions/670f4caac7dd_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,24 @@


def upgrade():
op.create_table('category',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('type', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id')
op.create_table(
'category',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('type', sa.String(), nullable=True),
sa.PrimaryKeyConstraint('id'),
)
op.create_table('button',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('category_id', sa.Integer(), nullable=True),
sa.Column('icon', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['category_id'], ['category.id'], ),
sa.PrimaryKeyConstraint('id')
op.create_table(
'button',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(), nullable=True),
sa.Column('category_id', sa.Integer(), nullable=True),
sa.Column('icon', sa.String(), nullable=True),
sa.ForeignKeyConstraint(
['category_id'],
['category.id'],
),
sa.PrimaryKeyConstraint('id'),
)


Expand Down
42 changes: 42 additions & 0 deletions migrations/versions/6a486347af93_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""order

Revision ID: 6a486347af93
Revises: 670f4caac7dd
Create Date: 2023-02-11 10:18:11.179485

"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '6a486347af93'
down_revision = '670f4caac7dd'
branch_labels = None
depends_on = None


def upgrade():
op.add_column('button', sa.Column('order', sa.Integer(), nullable=False))
op.add_column('button', sa.Column('link', sa.String(), nullable=False))
op.add_column('button', sa.Column('type', sa.String(), nullable=False))
op.alter_column('button', 'name', existing_type=sa.VARCHAR(), nullable=False)
op.alter_column('button', 'category_id', existing_type=sa.INTEGER(), nullable=False)
op.alter_column('button', 'icon', existing_type=sa.VARCHAR(), nullable=False)
op.add_column('category', sa.Column('order', sa.Integer(), nullable=False))
op.alter_column('category', 'name', existing_type=sa.VARCHAR(), nullable=False)
op.alter_column('category', 'type', existing_type=sa.VARCHAR(), nullable=False)


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('category', 'type', existing_type=sa.VARCHAR(), nullable=True)
op.alter_column('category', 'name', existing_type=sa.VARCHAR(), nullable=True)
op.drop_column('category', 'order')
op.alter_column('button', 'icon', existing_type=sa.VARCHAR(), nullable=True)
op.alter_column('button', 'category_id', existing_type=sa.INTEGER(), nullable=True)
op.alter_column('button', 'name', existing_type=sa.VARCHAR(), nullable=True)
op.drop_column('button', 'type')
op.drop_column('button', 'link')
op.drop_column('button', 'order')
# ### end Alembic commands ###
27 changes: 16 additions & 11 deletions services_backend/models/database.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from __future__ import annotations
from sqlalchemy import Integer, String, ForeignKey
from sqlalchemy.orm import relationship, Mapped, mapped_column
from .base import Base


class Category(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
type = Column(String)
buttons = relationship("Button", back_populates="category", foreign_keys="Button.category_id")
id: Mapped[int] = mapped_column(Integer, primary_key=True)
order: Mapped[int] = mapped_column(Integer, default=1)
name: Mapped[str] = mapped_column(String)
type: Mapped[str] = mapped_column(String)
buttons: Mapped[list[Button]] = relationship("Button", back_populates="category", foreign_keys="Button.category_id")


class Button(Base):
id = Column(Integer, primary_key=True)
name = Column(String)
category_id = Column(Integer, ForeignKey(Category.id))
category = relationship("Category", back_populates="buttons", foreign_keys=[category_id])
icon = Column(String)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String)
order: Mapped[int] = mapped_column(Integer, default=1)
category_id: Mapped[int] = mapped_column(Integer, ForeignKey(Category.id))
category: Mapped[Category] = relationship("Category", back_populates="buttons", foreign_keys=[category_id])
icon: Mapped[str] = mapped_column(String)
link: Mapped[str] = mapped_column(String)
type: Mapped[str] = mapped_column(String)
6 changes: 2 additions & 4 deletions services_backend/routes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
app = FastAPI()


app.add_middleware(
DBSessionMiddleware, db_url=settings.DB_DSN, session_args={"autocommit": True}, engine_args={"pool_pre_ping": True}
)
app.add_middleware(DBSessionMiddleware, db_url=settings.DB_DSN, engine_args={"pool_pre_ping": True})

app.add_middleware(
CORSMiddleware,
Expand All @@ -22,5 +20,5 @@
allow_headers=settings.CORS_ALLOW_HEADERS,
)

app.include_router(button, prefix='/button', tags=["Button"])
app.include_router(button, prefix='/category/{category_id}/button', tags=["Button"])
app.include_router(category, prefix='/category', tags=["Category"])
80 changes: 60 additions & 20 deletions services_backend/routes/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,94 @@
from fastapi_sqlalchemy import db

from .models.button import ButtonCreate, ButtonUpdate, ButtonGet
from .models.category import CategoryGet
from ..models.database import Button, Category

button = APIRouter()


@button.post("/", response_model=ButtonGet)
def create_button(button_inp: ButtonCreate):
category = db.session.query(Category).filter(Category.id == button_inp.category_id).one_or_none()
def create_button(button_inp: ButtonCreate, category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
button = Button(**button_inp.dict())
last_button = db.session.query(Button).order_by(Button.order.desc()).first()
button = Button(**button_inp.dict(exclude_none=True))
button.category_id = category_id
if last_button:
button.order = last_button.order + 1
db.session.add(button)
db.session.flush()
db.session.commit()
return button


@button.get("/", response_model=list[ButtonGet])
def get_buttons(offset: int = 0, limit: int = 100):
return db.session.query(Button).offset(offset).limit(limit).all()
@button.get("/", response_model=CategoryGet)
def get_buttons(category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
return category


@button.get("/{button_id}", response_model=ButtonGet)
def get_button(button_id: int):
def get_button(button_id: int, category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
button = db.session.query(Button).filter(Button.id == button_id).one_or_none()
if not button:
raise HTTPException(status_code=404, detail="Button does not exist")
if button.category_id != category_id:
raise HTTPException(status_code=404, detail="Button is not this category")
return button


@button.delete("/{button_id}", response_model=None)
def remove_button(button_id: int):
def remove_button(button_id: int, category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
button = db.session.query(Button).filter(Button.id == button_id).one_or_none()
if not button:
raise HTTPException(status_code=404, detail="Button does not exist")
if button.category_id != category_id:
raise HTTPException(status_code=404, detail="Button is not in this category")
db.session.delete(button)
db.session.flush()
db.session.query(Button).filter(Button.order > button.order).update({"order": Button.order - 1})
db.session.commit()


@button.patch("/{button_id}", response_model=ButtonGet)
def update_button(button_inp: ButtonUpdate, button_id: int):
button = db.session.query(Button).filter(Button.id == button_id)
if not button.one_or_none():
@button.patch("/{button_id}", response_model=ButtonUpdate)
def update_button(button_inp: ButtonUpdate, button_id: int, category_id: int):
query = db.session.query(Button).filter(Button.id == button_id)
button = query.one_or_none()
last_button = (
db.session.query(Button).filter(Button.category_id == category_id).order_by(Button.order.desc()).first()
)
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()

if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
if not button:
raise HTTPException(status_code=404, detail="Button does not exist")
if not any(button_inp.dict().values()):
Comment thread
Wudext marked this conversation as resolved.
raise HTTPException(status_code=400, detail="Empty schema")
button.update(
button_inp.dict(exclude_unset=True)
)
db.session.flush()
patched = button.one()
return patched
if button.category_id != category_id:
raise HTTPException(status_code=404, detail="Button is not this category")

if button_inp.order:
if last_button and (button_inp.order > last_button.order + 1):
raise HTTPException(
status_code=400,
detail=f"Can`t create button with order {button_inp.order}. " f"Last category is {last_button.order}",
)
if button_inp.order < 1:
raise HTTPException(status_code=400, detail="Order can`t be less than 1")
if button.order > button_inp.order:
db.session.query(Button).filter(Button.order < button.order).update({"order": Button.order + 1})
elif button.order < button_inp.order:
db.session.query(Button).filter(Button.order > button.order).update({"order": Button.order - 1})

query.update(button_inp.dict(exclude_unset=True, exclude_none=True))
db.session.commit()
return button
65 changes: 47 additions & 18 deletions services_backend/routes/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,77 @@

@category.post("/", response_model=CategoryGet)
def create_category(category_inp: CategoryCreate):
category = Category(**category_inp.dict())
last_category = db.session.query(Category).order_by(Category.order.desc()).first()
category = Category(**category_inp.dict(exclude_none=True))
if last_category:
category.order = last_category.order + 1
db.session.add(category)
db.session.flush()
db.session.commit()
return category


@category.get("/", response_model=list[CategoryGet])
@category.get("/", response_model=list[CategoryGet], response_model_exclude_none=True)
def get_categories(offset: int = 0, limit: int = 100):
return db.session.query(Category).offset(offset).limit(limit).all()
if (offset < 0) or (limit < 0):
raise HTTPException(400, detail="Offset or limit cant be negative")
return [
CategoryGet.from_orm(row).dict(exclude={"buttons"})
for row in db.session.query(Category).order_by(Category.order).offset(offset).limit(limit).all()
]


@category.get("/{category_id}", response_model=CategoryGet)
@category.get("/{category_id}", response_model=CategoryGet, response_model_exclude_none=True)
def get_category(category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
return category
return {
"id": category_id,
"order": category.order,
"name": category.name,
"type": category.type,
}


@category.delete("/{category_id}", response_model=None)
def remove_category(category_id: int):
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
if category is None:
if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
delete = db.session.query(Category).filter(Category.id == category_id).one_or_none()
for button in db.session.query(Button).filter(Button.category_id == category_id).all():
db.session.delete(button)
db.session.flush()
db.session.delete(delete)
db.session.flush()
db.session.query(Category).filter(Category.order > category.order).update({"order": Category.order - 1})
db.session.delete(category)
db.session.commit()


@category.patch("/{category_id}", response_model=CategoryUpdate)
def update_category(category_inp: CategoryUpdate, category_id: int):
category = db.session.query(Category).filter(Category.id == category_id)
if not category.one_or_none():
category = db.session.query(Category).filter(Category.id == category_id).one_or_none()
last_category = db.session.query(Category).order_by(Category.order.desc()).first()

if not category:
raise HTTPException(status_code=404, detail="Category does not exist")
if not any(category_inp.dict().values()):
raise HTTPException(status_code=400, detail="Empty schema")
category.update(
category_inp.dict(exclude_unset=True)
)
db.session.flush()
patched = category.one()
return patched

if category_inp.order:
if category_inp.order < 1:
raise HTTPException(status_code=400, detail="Order can`t be less than 1")
if last_category and (category_inp.order > last_category.order):
raise HTTPException(
status_code=400,
detail=f"Can`t create category with order {category_inp.order}. "
f"Last category is {last_category.order}",
)

if category.order > category_inp.order:
db.session.query(Category).filter(Category.order < category.order).update({"order": Category.order + 1})
elif category.order < category_inp.order:
db.session.query(Category).filter(Category.order > category.order).update({"order": Category.order - 1})

query = db.session.query(Category).filter(Category.id == category_id)
query.update(category_inp.dict(exclude_unset=True, exclude_none=True))
db.session.commit()
return category
10 changes: 8 additions & 2 deletions services_backend/routes/models/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@


class ButtonCreate(Base):
category_id: int
icon: str | None
name: str | None
link: str | None
type: str | None


class ButtonUpdate(Base):
category_id: int | None
icon: str | None
name: str | None
order: int | None
link: str | None
type: str | None


class ButtonGet(Base):
id: int
category_id: int
order: int
icon: str | None
name: str | None
link: str | None
type: str | None
4 changes: 3 additions & 1 deletion services_backend/routes/models/category.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ class CategoryCreate(Base):


class CategoryUpdate(Base):
order: int | None
type: str | None
name: str | None


class CategoryGet(Base):
id: int
order: int
type: str | None
name: str | None
buttons: list[ButtonGet]
buttons: list[ButtonGet] | None
Loading