From a4b66a673771835533bd615e0495e495552c97da Mon Sep 17 00:00:00 2001 From: Sudhanva Nadiger Date: Fri, 23 Feb 2024 18:33:05 +0530 Subject: [PATCH] feat: add endpoint to update price (#202) --- app/api.py | 37 +++++++++++++++++++++++++ app/crud.py | 9 ++++++ app/schemas.py | 9 ++++++ tests/integration/test_api.py | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+) diff --git a/app/api.py b/app/api.py index 3de03afa..44bc80ed 100644 --- a/app/api.py +++ b/app/api.py @@ -392,6 +392,43 @@ def delete_price( return +@app.put( + path="/api/v1/prices/{price_id}", + response_model=schemas.PriceFull, + status_code=status.HTTP_200_OK, + tags=["Prices"], +) +def update_price( + price_id: int, + new_price: schemas.PriceBasicUpdatableFields, + current_user: schemas.UserCreate = Depends(get_current_user), + db: Session = Depends(get_db), +): + """ + Update a price. + + This endpoint requires authentication. + A user can update only owned prices. + """ + # fetch price with id = price_id + db_price = crud.get_price_by_id(db, id=price_id) + + if not db_price: + raise HTTPException( + status_code=404, + detail=f"Price with code {price_id} not found", + ) + # Check if the price belongs to the current user + if db_price.owner != current_user.user_id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="Price does not belong to current user", + ) + + # updated price + return crud.update_price(db, db_price, new_price) + + # Routes: Proofs # ------------------------------------------------------------------------------ @app.get("/api/v1/proofs", response_model=Page[schemas.ProofFull], tags=["Proofs"]) diff --git a/app/crud.py b/app/crud.py index f46cbbbc..dcb1f4c9 100644 --- a/app/crud.py +++ b/app/crud.py @@ -17,6 +17,7 @@ LocationCreate, LocationFilter, LocationFull, + PriceBasicUpdatableFields, PriceCreate, PriceFilter, PriceFull, @@ -331,6 +332,14 @@ def delete_price(db: Session, db_price: PriceFull) -> bool: return True +def update_price(db: Session, price: Price, new_values: PriceBasicUpdatableFields): + for [key, value] in new_values: + setattr(price, key, value) + db.commit() + db.refresh(price) + return price + + # Proofs # ------------------------------------------------------------------------------ def get_proofs_query(filters: ProofFilter | None = None): diff --git a/app/schemas.py b/app/schemas.py index 3934d63a..d1b8e371 100644 --- a/app/schemas.py +++ b/app/schemas.py @@ -363,6 +363,15 @@ def check_price_discount(self): return self +class PriceBasicUpdatableFields(BaseModel): + price: float | None + price_is_discounted: bool | None + price_without_discount: float | None + price_per: PricePerEnum | None + currency: CurrencyEnum | None + date: datetime.date | None + + class PriceFull(PriceCreate): id: int product_id: int | None diff --git a/tests/integration/test_api.py b/tests/integration/test_api.py index 0c627e65..a076c036 100644 --- a/tests/integration/test_api.py +++ b/tests/integration/test_api.py @@ -11,6 +11,7 @@ from app.models import Session as SessionModel from app.schemas import ( LocationCreate, + PriceBasicUpdatableFields, PriceCreate, ProductCreate, ProofFilter, @@ -608,6 +609,57 @@ def test_delete_price(db_session, user_session: SessionModel, clean_prices): assert response.status_code == 204 +def test_update_price(db_session, user_session: SessionModel): + db_price = crud.create_price(db_session, PRICE_1, user_session.user) + # without authentication + response = client.put(f"/api/v1/prices/{db_price.id}") + assert response.status_code == 401 + + # with authentication but not price owner + user_1_session, *_ = crud.create_session(db_session, USER_1.user_id, USER_1.token) + response = client.put( + f"/api/v1/prices/{db_price.id}", + headers={"Authorization": f"Bearer {user_1_session.token}"}, + ) + assert response.status_code == 403 + # with authentication but price unknown + response = client.put( + f"/api/v1/prices/{db_price.id+1}", + headers={"Authorization": f"Bearer {user_session.token}"}, + ) + assert response.status_code == 404 + + # with authentication and price owner + + # if any field which is not is basic updatable field provided + # it should throw an error + price_fields = { + "proof_id": 1, + "new_price": 5.5, + } + + response = client.put( + f"/api/v1/prices/{db_price.id}", + headers={"Authorization": f"Bearer {user_session.token}"}, + json=jsonable_encoder(price_fields), + ) + + assert response.status_code == 422 # Unprocessable Entity + + new_price = 5.5 + price_updatable_fields = PriceBasicUpdatableFields(price=new_price) + + response = client.put( + f"/api/v1/prices/{db_price.id}", + headers={"Authorization": f"Bearer {user_session.token}"}, + json=jsonable_encoder(price_updatable_fields), + ) + + assert response.status_code == 200 + + assert response.json()["price"] == new_price + + # Test proofs # ------------------------------------------------------------------------------ def test_create_proof(user_session: SessionModel, clean_proofs):