Skip to content

Commit

Permalink
Add utils and items endpoints to API router
Browse files Browse the repository at this point in the history
  • Loading branch information
AndPuQing committed Jan 15, 2024
1 parent 87b30e6 commit 60f4441
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 41 deletions.
2 changes: 2 additions & 0 deletions bemore/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class Settings(BaseSettings):
# Current environment
environment: str = "dev"

USERS_OPEN_REGISTRATION: bool = False

log_level: LogLevel = LogLevel.INFO

SQLALCHEMY_DATABASE_URI: str = "sqlite:///./bemore.db"
Expand Down
49 changes: 31 additions & 18 deletions bemore/crud/crud_item.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
from typing import List

from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from sqlmodel import col

from bemore.crud.base import CRUDBase
from bemore.models import Item
from bemore.schemas.item import ItemCreate, ItemUpdate


class CRUDItem(CRUDBase[Item, ItemCreate, ItemUpdate]):
def create_with_owner(
self, db: Session, *, obj_in: ItemCreate, owner_id: int
) -> Item:
obj_in_data = jsonable_encoder(obj_in)
db_obj = self.model(**obj_in_data, owner_id=owner_id)
def create(self, db: Session, *, obj_in: ItemCreate) -> Item:
db_obj = Item(
title=obj_in.title,
description=obj_in.description,
keywords=obj_in.keywords,
raw_url=obj_in.raw_url,
)
db.add(db_obj)
db.commit()
db.refresh(db_obj)
return db_obj

def get_multi_by_owner(
self, db: Session, *, owner_id: int, skip: int = 0, limit: int = 100
) -> List[Item]:
return (
db.query(self.model)
.filter(Item.owner_id == owner_id)
.offset(skip)
.limit(limit)
.all()
)
def update(self, db: Session, *, db_obj: Item, obj_in: ItemUpdate) -> Item:
if obj_in.title:
db_obj.title = obj_in.title
if obj_in.description:
db_obj.description = obj_in.description
if obj_in.keywords:
db_obj.keywords = obj_in.keywords
if obj_in.raw_url:
db_obj.raw_url = obj_in.raw_url
return super().update(db, db_obj=db_obj, obj_in=obj_in)

def get_by_id(self, db: Session, *, id: int) -> Item:
return db.query(Item).filter(Item.id == id).first()

def get_by_fuzzy_title(self, db: Session, *, title: str) -> list[Item]:
return db.query(Item).filter(col("title").ilike(f"%{title}%")).all()

def create_bulk(self, db: Session, *, objs_in: list[ItemCreate]) -> list[Item]:
objs = [Item(**obj_in.model_dump()) for obj_in in objs_in]
db.add_all(objs)
db.commit()
db.refresh(objs)
return objs


item = CRUDItem(Item)
52 changes: 37 additions & 15 deletions bemore/models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# Contents of JWT token
from typing import Union
from typing import List, Optional, Union

from pydantic import BaseModel, EmailStr
from sqlmodel import Field, Relationship, SQLModel, AutoString
from pydantic import BaseModel, EmailStr, HttpUrl
from sqlmodel import Field, Relationship, SQLModel, AutoString, JSON, Column

# Shared properties
class UserBase(SQLModel):
email: EmailStr = Field(unique=True, index=True, sa_type=AutoString)
is_active: bool = True
is_superuser: bool = False
full_name: Union[str, None] = None
subscription: Union[list[str], None] = Field(default=None, sa_column=Column(JSON))


# Properties to receive via API on creation
class UserCreate(UserBase):
password: str


class UserCreateOpen(SQLModel):
email: EmailStr
password: str
full_name: Union[str, None] = None


# Properties to receive via API on update, all are optional
class UserUpdate(UserBase):
email: Union[EmailStr, None] = None
password: Union[str, None] = None


class UserUpdateMe(BaseModel):
password: Union[str, None] = None
full_name: Union[str, None] = None
email: Union[EmailStr, None] = None
subscription: Union[List[str], None] = None


# Database model, database table inferred from class name
Expand All @@ -19,11 +44,6 @@ class User(UserBase, table=True):
items: list["Item"] = Relationship(back_populates="owner")


# Properties to receive via API on creation
class UserCreate(UserBase):
password: str


# Properties to return via API, id is always required
class UserOut(UserBase):
id: int
Expand All @@ -32,7 +52,9 @@ class UserOut(UserBase):
# Shared properties
class ItemBase(SQLModel):
title: str
description: Union[str, None] = None
description: str
keywords: Union[list[str], None] = Field(default=None, sa_column=Column(JSON))
raw_url: Optional[HttpUrl] = Field(default=None, sa_type=AutoString)


# Properties to receive on item creation
Expand All @@ -42,17 +64,17 @@ class ItemCreate(ItemBase):

# Properties to receive on item update
class ItemUpdate(ItemBase):
title: Union[str, None] = None
title: Optional[str] = None
description: Optional[str] = None
keywords: Optional[list[str]] = None
raw_url: Optional[HttpUrl] = None
is_hidden: Optional[bool] = None


# Database model, database table inferred from class name
class Item(ItemBase, table=True):
id: Union[int, None] = Field(default=None, primary_key=True)
title: str
owner_id: Union[int, None] = Field(
default=None, foreign_key="user.id", nullable=False
)
owner: Union[User, None] = Relationship(back_populates="items")
is_hidden: bool = False


# Properties to return via API, id is always required
Expand Down
7 changes: 5 additions & 2 deletions bemore/schemas/item.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from typing import Optional
from typing import Optional, Union

from pydantic import BaseModel


# Shared properties
class ItemBase(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
description: str
keywords: Union[list[str], None]
raw_url: Optional[str] = None


# Properties to receive on item creation
Expand All @@ -24,6 +26,7 @@ class ItemInDBBase(ItemBase):
id: int
title: str
owner_id: int
is_hidden: bool = False

class Config:
from_attributes = True
Expand Down
91 changes: 91 additions & 0 deletions bemore/web/api/endpoints/items.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from typing import Any

from fastapi import APIRouter, HTTPException
from sqlmodel import select

from bemore.crud.crud_item import item as crud
from bemore.web.api.deps import CurrentUser, SessionDep
from bemore.models import Item, ItemCreate, ItemOut, ItemUpdate

router = APIRouter()


@router.get("/", response_model=list[ItemOut])
def read_items(
session: SessionDep, current_user: CurrentUser, skip: int = 0, limit: int = 100
) -> list[ItemOut]:
"""
Retrieve items.
"""
if current_user.is_superuser:
raise HTTPException(status_code=400, detail="Not enough permissions")
statement = select(Item).offset(skip).limit(limit)
return session.exec(statement).all()


@router.get("/{id}", response_model=ItemOut)
def read_item(session: SessionDep, id: int) -> Any:
"""
Get item by ID.
"""
item = session.get(Item, id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
return item


@router.get("/fuzzy/{title}", response_model=list[ItemOut])
def read_item_fuzzy(session: SessionDep, title: str) -> Any:
"""
Get item by fuzzy title.
"""
statement = crud.get_by_fuzzy_title(session, title=title)
return statement


@router.post("/", response_model=ItemOut)
def create_item(*, session: SessionDep, item_in: ItemCreate) -> Any:
"""
Create new item.
"""
item = crud.create(session, obj_in=item_in)
return item


@router.post("/bulk", response_model=list[ItemOut])
def create_items(*, session: SessionDep, items_in: list[ItemCreate]) -> Any:
"""
Create new items.
"""
items = crud.create_bulk(session, objs_in=items_in)
return items


@router.put("/{id}", response_model=ItemOut)
def update_item(
*, session: SessionDep, current_user: CurrentUser, id: int, item_in: ItemUpdate
) -> Any:
"""
Update an item.
"""
if not current_user.is_superuser:
raise HTTPException(status_code=400, detail="Not enough permissions")
item = crud.get(session, id=id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
item = crud.update(session, db_obj=item, obj_in=item_in)
return item


@router.delete("/{id}", response_model=ItemOut)
def delete_item(session: SessionDep, current_user: CurrentUser, id: int) -> ItemOut:
"""
Delete an item.
"""
item = crud.get(session, id=id)
if not item:
raise HTTPException(status_code=404, detail="Item not found")
if not current_user.is_superuser:
raise HTTPException(status_code=400, detail="Not enough permissions")
item = crud.remove(session, id=id)
return item
Loading

0 comments on commit 60f4441

Please sign in to comment.