Skip to content

Commit

Permalink
feat: products GET endpoint (#122)
Browse files Browse the repository at this point in the history
* New endpoint with filters GET /products

* Add tests
  • Loading branch information
raphodn authored Jan 7, 2024
1 parent bd6ff9c commit f80d80b
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 7 deletions.
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

0 comments on commit f80d80b

Please sign in to comment.