Skip to content
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
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ This project serves as a strong foundation for developers to build upon.
(venv) $ mv fastapi_ecom/config/config.py.example config.py
(venv) $ nano fastapi_ecom/config/config.py
```
Change the variables as per your requirement
`database` = `<DATABASE-NAME>` as mentioned while setting up the database container.
`username` = `<DATABASE-USER>` as mentioned while setting up the database container.
`password` = `<DATABASE-PASSWORD>` as mentioned while setting up the database container.
`dtbsbhost` = `<HOST>` as mentioned while setting up the database container.
`dtbsbport` = `<PORT>` as mentioned while setting up the database container.
`dtbsdriver` = `postgresql+asyncpg` if async database driver to be used or `postgresql+psycopg2` is sync database driver to be used.
`servhost` = `127.0.0.1` if the service is intended to be accessible only on the same device.
`servport` = `8080` if the service is intended to be accessible on the port number `8080` or `[1-65535]` depending on your choice.
`cgreload` = `True` for use in development environments to which automatically reload the unvicorn service.
Change the variables as per your requirement
`database` = `<DATABASE-NAME>` as mentioned while setting up the database container.
`username` = `<DATABASE-USER>` as mentioned while setting up the database container.
`password` = `<DATABASE-PASSWORD>` as mentioned while setting up the database container.
`dtbsbhost` = `<HOST>` as mentioned while setting up the database container.
`dtbsbport` = `<PORT>` as mentioned while setting up the database container.
`dtbsdriver` = `postgresql+asyncpg` if async database driver to be used or `postgresql+psycopg2` is sync database driver to be used.
`servhost` = `127.0.0.1` if the service is intended to be accessible only on the same device.
`servport` = `8080` if the service is intended to be accessible on the port number `8080` or `[1-65535]` depending on your choice.
`cgreload` = `True` for use in development environments to which automatically reload the uvicorn service.
Command
```shell
(venv) $ mv fastapi_ecom/migrations/alembic.ini.example fastapi_ecom/migrations/alembic.ini
Expand Down Expand Up @@ -446,10 +446,10 @@ This project serves as a strong foundation for developers to build upon.
2. Business Route
This route contains endpoints for performing CRUD operations on business entity.
`create`: Endpoint to create a new business account. No authentication is needed for connecting to this endpoint.
`me`: It is a endpoint to fetch the email of the currently authenticated business.
`me`: It is an endpoint to fetch the email of the currently authenticated business.
`search`: Endpoint to fetch a paginated list of businesses from the database. No authentication is needed for connecting to this endpoint.
`delete`: Endpoint for an authenticated business to delete its own record.
_Note:_ This endpoint will be depricated in future update with the implementation of archiving.
_Note:_ This endpoint will be deprecated in future update with the implementation of archiving.
`update`: Endpoint for an authenticated business to update its own record.
![](https://raw.githubusercontent.com/sdglitched/FastAPI-eCom/main/docs/imgs/business_enpt.png)
3. Product Route
Expand All @@ -460,13 +460,13 @@ This project serves as a strong foundation for developers to build upon.
`search/internal`: Endpoint fetches a paginated list of products associated with the authenticated business.
`search/uuid`: Endpoint fetches a specific product by its UUID (Product ID) associated with the authenticated business.
`delete/uuid`: Endpoint to delete a product by its UUID associated for an authenticated business.
_Note:_ This endpoint will be depricated in future update with the implementation of archiving.
_Note:_ This endpoint will be deprecated in future update with the implementation of archiving.
`update/uuid`: Endpoint to update a product by its UUID associated for an authenticated business.
![](https://raw.githubusercontent.com/sdglitched/FastAPI-eCom/main/docs/imgs/product_enpt.png)
4. Customer Route
This route contains endpoints for performing CRUD operations on customer entity.
`create`: Endpoint to create a new customer account. No authentication is needed for connecting to this endpoint.
`me`: It is a endpoint to fetch the email of the currently authenticated customer.
`me`: It is an endpoint to fetch the email of the currently authenticated customer.
`search`: Endpoint to fetch a paginated list of customers from the database. No authentication is needed for connecting to this endpoint.
`delete`: Endpoint for an authenticated customer to delete its own record.
`update`: Endpoint for an authenticated customer to update its own record.
Expand All @@ -476,10 +476,10 @@ This project serves as a strong foundation for developers to build upon.
`create`: Endpoint to place an order by the authenticated customer.
`search`: Endpoint fetches a paginated list of orders and its details associated with the authenticated customer.
`search/internal`: Endpoint fetches a paginated list of orders and its details.
_Note:_ This endpoint is ment to be used by an admin account which will created in future update. Currently no authentication is needed for connecting to this endpoint.
_Note:_ This endpoint is ment to be used by an admin account which will created in future update. Currently, no authentication is needed for connecting to this endpoint.
`search/uuid`: Endpoint fetches a specific order and its details by its UUID associated with the authenticated customer.
`delete/uuid`: Endpoint to delete a order by its UUID associated for an authenticated customer.
_Note:_ This endpoint will be depricated in future update with the implementation of archiving.
`delete/uuid`: Endpoint to delete an order by its UUID associated for an authenticated customer.
_Note:_ This endpoint will be deprecated in future update with the implementation of archiving.
![](https://raw.githubusercontent.com/sdglitched/FastAPI-eCom/main/docs/imgs/order_enpt.png)

## Future Roadmap
Expand Down
2 changes: 1 addition & 1 deletion fastapi_ecom/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
tags_metadata = [
{"name": "business", "description": "Operations on businesses"},
{"name": "product", "description": "Operations on products"},
{"name": "customer", "description": "Operations on customeres"},
{"name": "customer", "description": "Operations on customers"},
{"name": "order", "description": "Operations on orders"}
]

Expand Down
3 changes: 3 additions & 0 deletions fastapi_ecom/database/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
# Base class for ORM models, to be used with SQLAlchemy's declarative system.
baseobjc = declarative_base()

# Path of alembic configuration file
alempath = str(Path(str(Path(str(Path(__file__).parent.resolve().parent.resolve()),"migrations").resolve()),"alembic.ini").resolve())

# Migration path for alembic configuration
migrpath = str(Path(str(Path(__file__).parent.resolve().parent.resolve()),"migrations").resolve())

Expand Down
6 changes: 3 additions & 3 deletions fastapi_ecom/database/db_setup.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from pathlib import Path
from typing import AsyncGenerator

from alembic import command, config
from sqlalchemy.ext.asyncio import AsyncSession

from fastapi_ecom.database import ( #noqa: F401
from fastapi_ecom.database import ( # noqa: F401
alempath,
baseobjc,
get_async_session,
get_database_url,
Expand All @@ -28,7 +28,7 @@ def make_database() -> None:
baseobjc.metadata.create_all(bind=sync_engine)

# Set up Alembic configuration for migration management.
alembic_config = config.Config(str(Path(str(Path(str(Path(__file__).parent.resolve().parent.resolve()),"migrations").resolve()),"alembic.ini").resolve()))
alembic_config = config.Config(alempath)
alembic_config.set_main_option("script_location", migrpath)
alembic_config.set_main_option("sqlalchemy.url", get_database_url().render_as_string(hide_password=False))

Expand Down
6 changes: 5 additions & 1 deletion fastapi_ecom/router/business.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,11 @@ async def delete_business(db: AsyncSession = Depends(get_db), business_auth = De
}

@router.put("/update/me", status_code=status.HTTP_202_ACCEPTED, response_model=BusinessResult, tags=["business"])
async def update_business(business: BusinessUpdate, db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> BusinessResult:
async def update_business(
business: BusinessUpdate,
db: AsyncSession = Depends(get_db),
business_auth = Depends(verify_business_cred)
) -> BusinessResult:
"""
Endpoint for an authenticated business to update its own record.

Expand Down
29 changes: 24 additions & 5 deletions fastapi_ecom/router/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@
router = APIRouter(prefix="/product")

@router.post("/create", status_code=status.HTTP_201_CREATED, response_model=ProductResultInternal, tags=["product"])
async def add_product(product: ProductCreate, db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> ProductResultInternal:
async def add_product(
product: ProductCreate,
db: AsyncSession = Depends(get_db),
business_auth = Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint to add a new product by currently authenticated business.

Expand Down Expand Up @@ -121,7 +125,9 @@ async def get_product_by_text(
:raises HTTPException:
If no matching products exists in the database, it raises 404 Not Found.
"""
query = select(Product).where(or_(Product.name.ilike(f"%{text}%"), Product.description.ilike(f"%{text}%"))).options(selectinload("*")).offset(skip).limit(limit)
query = select(Product).where(
or_(Product.name.ilike(f"%{text}%"), Product.description.ilike(f"%{text}%"))
).options(selectinload("*")).offset(skip).limit(limit)
result = await db.execute(query)
products = result.scalars().all()
if not products:
Expand Down Expand Up @@ -169,7 +175,11 @@ async def get_products_internal(
}

@router.get("/search/uuid/{product_id}", status_code=status.HTTP_200_OK, response_model=ProductResultInternal, tags=["product"])
async def get_product_by_uuid(product_id: str, db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> ProductResultInternal:
async def get_product_by_uuid(
product_id: str,
db: AsyncSession = Depends(get_db),
business_auth = Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint fetches a specific product by its UUID associated with the authenticated business.

Expand Down Expand Up @@ -198,7 +208,11 @@ async def get_product_by_uuid(product_id: str, db: AsyncSession = Depends(get_db
}

@router.delete("/delete/uuid/{product_id}", status_code=status.HTTP_202_ACCEPTED, response_model=ProductResultInternal, tags=["product"])
async def delete_product(product_id: str, db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> ProductResultInternal:
async def delete_product(
product_id: str,
db: AsyncSession = Depends(get_db),
business_auth = Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint to delete a product by its UUID associated for an authenticated business.

Expand Down Expand Up @@ -242,7 +256,12 @@ async def delete_product(product_id: str, db: AsyncSession = Depends(get_db), bu
}

@router.put("/update/uuid/{product_id}", status_code=status.HTTP_202_ACCEPTED, response_model=ProductResultInternal, tags=["product"])
async def update_product(product_id: str, product: ProductUpdate, db: AsyncSession = Depends(get_db), business_auth = Depends(verify_business_cred)) -> ProductResultInternal:
async def update_product(
product_id: str,
product: ProductUpdate,
db: AsyncSession = Depends(get_db),
business_auth = Depends(verify_business_cred)
) -> ProductResultInternal:
"""
Endpoint to update a product by its UUID associated for an authenticated business.

Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ aiosqlite = "^0.20.0"
pytest-mock = "^3.14.0"

[tool.ruff]
line-length = 100
line-length = 150
fix = true

[tool.ruff.lint]
select = ["E", "F", "W", "I", "S", "B", "UP"]

[tool.ruff.lint.per-file-ignores]
"fastapi_ecom/*" = ["E501", "B008"]
"tests/*" = ["S101", "S106", "E501"]
"fastapi_ecom/*" = ["B008"]
"tests/*" = ["S101", "S106",]

[tool.pytest.ini_options]
asyncio_mode = "auto"
Expand Down
10 changes: 8 additions & 2 deletions tests/product/test_product_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
@pytest.mark.parametrize(
"business_id, product_id, present",
[
pytest.param("5c1c48fb", "d5cf6983", True, id="PRODUCT DELETE Endpoint - Deletes the specific product of currently authenticated business"),
pytest.param("5c1c48fb", "d76a11f2", False, id="PRODUCT DELETE Endpoint - Fails to find the specific product of currently authenticated business"),
pytest.param(
"5c1c48fb", "d5cf6983", True,
id="PRODUCT DELETE Endpoint - Deletes the specific product of currently authenticated business"
),
pytest.param(
"5c1c48fb", "d76a11f2", False,
id="PRODUCT DELETE Endpoint - Fails to find the specific product of currently authenticated business"
),
]
)
async def test_delete_product(
Expand Down
10 changes: 8 additions & 2 deletions tests/product/test_product_get.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,14 @@ async def test_get_products_internal(
@pytest.mark.parametrize(
"business_id, product_id, present",
[
pytest.param("d76a11f2", "3250fcbe", True, id="PRODUCT GET Endpoint - Fetch specific product by its UUID which is associated with the authenticated business"),
pytest.param("d76a11f2", "xxxxyyyy", False, id="PRODUCT GET Endpoint - Fail to fetch specific product by its UUID which is associated with the authenticated business")
pytest.param(
"d76a11f2", "3250fcbe", True,
id="PRODUCT GET Endpoint - Fetch specific product by its UUID which is associated with the authenticated business"
),
pytest.param(
"d76a11f2", "xxxxyyyy", False,
id="PRODUCT GET Endpoint - Fail to fetch specific product by its UUID which is associated with the authenticated business"
)
]
)
async def test_get_product_by_uuid(
Expand Down
2 changes: 1 addition & 1 deletion tests/product/test_product_put.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"name": "Updated Test Product",
"description": "Updated Fifth Test Product",
"category": "updated test",
"mfg_date": datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),#"1900-01-01 00:00:00+00:00",
"mfg_date": datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"exp_date": datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=None).isoformat(),
"price": 1950.05
},
Expand Down