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

feat: products GET endpoint #122

Merged
merged 2 commits into from
Jan 7, 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
10 changes: 10 additions & 0 deletions app/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,16 @@ def get_user_proofs(
return crud.get_user_proofs(db, user=current_user)


@app.get(
"/api/v1/products", response_model=Page[schemas.ProductBase], tags=["Products"]
)
def get_products(
filters: schemas.ProductFilter = FilterDepends(schemas.ProductFilter),
db: Session = Depends(get_db),
):
return paginate(db, crud.get_products_query(filters=filters))


@app.get(
"/api/v1/products/code/{product_code}",
response_model=schemas.ProductBase,
Expand Down
14 changes: 14 additions & 0 deletions app/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
PriceFilter,
ProductBase,
ProductCreate,
ProductFilter,
UserBase,
)

Expand Down Expand Up @@ -70,6 +71,19 @@ def delete_user(db: Session, user_id: UserBase):

# Products
# ------------------------------------------------------------------------------
def get_products_query(filters: ProductFilter | None = None):
"""Useful for pagination."""
query = select(Product)
if filters:
query = filters.filter(query)
query = filters.sort(query)
return query


def get_products(db: Session, filters: ProductFilter | None = None):
return db.execute(get_products_query(filters=filters)).all()


def get_product_by_id(db: Session, id: int):
return db.query(Product).filter(Product.id == id).first()

Expand Down
15 changes: 14 additions & 1 deletion app/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
)

from app.enums import CurrencyEnum, LocationOSMEnum, ProofTypeEnum
from app.models import Price
from app.models import Price, Product


class UserBase(BaseModel):
Expand Down Expand Up @@ -303,3 +303,16 @@ class PriceFilter(Filter):

class Constants(Filter.Constants):
model = Price


class ProductFilter(Filter):
code: Optional[str] | None = None
source: Optional[Flavor] | None = None
product_name__like: Optional[str] | None = None
brands__like: Optional[str] | None = None
unique_scans_n__gte: Optional[int] | None = None

order_by: Optional[list[str]] | None = None

class Constants(Filter.Constants):
model = Product
95 changes: 89 additions & 6 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,30 @@ def override_get_db():

USER = UserBase(user_id="user1", token="user1__Utoken")
PRODUCT = ProductCreate(code="8001505005592")
PRODUCT_1 = ProductCreate(
code="0022314010025",
product_name="Chestnut spread 500 g",
product_quantity=500,
brands="Clément Faugier",
source="off",
unique_scans_n=20,
)
PRODUCT_2 = ProductCreate(
code="0022314010100",
product_name="Chestnut spread 100 g",
product_quantity=100,
brands="Clément Faugier",
source="off",
unique_scans_n=10,
)
PRODUCT_3 = ProductCreate(
code="3760091721969",
product_name="Crème bio de châtaignes 320 g",
product_quantity=320,
brands="Ethiquable",
source="off",
unique_scans_n=0,
)
LOCATION = LocationCreate(osm_id=3344841823, osm_type="NODE")
PRICE_1 = PriceCreate(
product_code="8001505005707",
Expand Down Expand Up @@ -82,7 +106,13 @@ def clean_prices(db_session):
db_session.commit()


# Tests
@pytest.fixture(scope="function")
def clean_products(db_session):
db_session.query(crud.Product).delete()
db_session.commit()


# Test prices
# ------------------------------------------------------------------------------
def test_create_price(db_session, user, clean_prices):
# without authentication
Expand Down Expand Up @@ -378,6 +408,8 @@ def test_get_prices_orders(db_session, user, clean_prices):
assert (response.json()["items"][0]["date"]) == "2023-10-31"


# Test proofs
# ------------------------------------------------------------------------------
def test_create_proof(user):
# without authentication
response = client.post(
Expand Down Expand Up @@ -427,21 +459,72 @@ def test_get_proofs(user):
assert len(response.json()) == 1


def test_get_product(product):
# Test products
# ------------------------------------------------------------------------------
def test_get_products(db_session, clean_products):
crud.create_product(db_session, PRODUCT_1)
crud.create_product(db_session, PRODUCT_2)
crud.create_product(db_session, PRODUCT_3)

assert len(crud.get_products(db_session)) == 3
response = client.get("/api/v1/products")
assert response.status_code == 200
assert len(response.json()["items"]) == 3


def test_get_products_pagination(clean_products):
response = client.get("/api/v1/products")
assert response.status_code == 200
for key in ["items", "total", "page", "size", "pages"]:
assert key in response.json()


# def test_get_products_filters(db_session, clean_products):
# crud.create_product(db_session, PRODUCT_1)
# crud.create_product(db_session, PRODUCT_2)
# crud.create_product(db_session, PRODUCT_3)

# assert len(crud.get_products(db_session)) == 3

# # 3 prices with the same source
# response = client.get("/api/v1/products?source=off")
# assert response.status_code == 200
# assert len(response.json()["items"]) == 3
# # 1 price with a specific product_name
# response = client.get("/api/v1/products?product_name__like=châtaignes")
# assert response.status_code == 200
# assert len(response.json()["items"]) == 1
# # 2 prices with the same brand
# response = client.get("/api/v1/products?brands__like=Clément Faugier")
# assert response.status_code == 200
# assert len(response.json()["items"]) == 2
# # 2 prices with a positive unique_scans_n
# response = client.get("/api/v1/products?unique_scans_n__gte=1")
# assert response.status_code == 200
# assert len(response.json()["items"]) == 2


def test_get_product(db_session, clean_products):
crud.create_product(db_session, PRODUCT_1)
crud.create_product(db_session, PRODUCT_2)
last_product = crud.create_product(db_session, PRODUCT_3)

# by id: product exists
response = client.get(f"/api/v1/products/{product.id}")
response = client.get(f"/api/v1/products/{last_product.id}")
assert response.status_code == 200
# by id: product does not exist
response = client.get(f"/api/v1/products/{product.id+1}")
response = client.get(f"/api/v1/products/{last_product.id+1}")
assert response.status_code == 404
# by code: product exists
response = client.get(f"/api/v1/products/code/{product.code}")
response = client.get(f"/api/v1/products/code/{last_product.code}")
assert response.status_code == 200
# by code: product does not exist
response = client.get(f"/api/v1/products/code/{product.code+'X'}")
response = client.get(f"/api/v1/products/code/{last_product.code+'X'}")
assert response.status_code == 404


# Test locations
# ------------------------------------------------------------------------------
def test_get_location(location):
# by id: location exists
response = client.get(f"/api/v1/locations/{location.id}")
Expand Down
Loading