refactor: rename shop to storefront throughout codebase
Rename "shop" to "storefront" as not all platforms sell items - storefront is a more accurate term for the customer-facing interface. Changes: - Rename app/api/v1/shop/ → app/api/v1/storefront/ - Rename app/routes/shop_pages.py → app/routes/storefront_pages.py - Rename app/modules/cms/routes/api/shop.py → storefront.py - Rename tests/integration/api/v1/shop/ → storefront/ - Update API prefix from /api/v1/shop to /api/v1/storefront - Update route tags from shop-* to storefront-* - Rename get_shop_context() → get_storefront_context() - Update architecture rules to reference storefront paths - Update all test API endpoint paths This is Phase 2 of the storefront module restructure plan. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -201,7 +201,7 @@ api_endpoint_rules:
|
|||||||
Use request.state.vendor_id from middleware.
|
Use request.state.vendor_id from middleware.
|
||||||
pattern:
|
pattern:
|
||||||
file_pattern: "app/api/v1/vendor/**/*.py"
|
file_pattern: "app/api/v1/vendor/**/*.py"
|
||||||
file_pattern: "app/api/v1/shop/**/*.py"
|
file_pattern: "app/api/v1/storefront/**/*.py"
|
||||||
discouraged_patterns:
|
discouraged_patterns:
|
||||||
- "db.query(.*).all()"
|
- "db.query(.*).all()"
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ auth_rules:
|
|||||||
1. SHOP ENDPOINTS (public, no authentication required):
|
1. SHOP ENDPOINTS (public, no authentication required):
|
||||||
- Use: vendor: Vendor = Depends(require_vendor_context())
|
- Use: vendor: Vendor = Depends(require_vendor_context())
|
||||||
- Vendor is detected from URL/subdomain/domain
|
- Vendor is detected from URL/subdomain/domain
|
||||||
- File pattern: app/api/v1/shop/**/*.py
|
- File pattern: app/api/v1/storefront/**/*.py
|
||||||
- Mark as public with: # public
|
- Mark as public with: # public
|
||||||
|
|
||||||
2. VENDOR API ENDPOINTS (authenticated):
|
2. VENDOR API ENDPOINTS (authenticated):
|
||||||
@@ -57,7 +57,7 @@ auth_rules:
|
|||||||
file_pattern: "app/api/v1/vendor/**/*.py"
|
file_pattern: "app/api/v1/vendor/**/*.py"
|
||||||
anti_patterns:
|
anti_patterns:
|
||||||
- "require_vendor_context\\(\\)"
|
- "require_vendor_context\\(\\)"
|
||||||
file_pattern: "app/api/v1/shop/**/*.py"
|
file_pattern: "app/api/v1/storefront/**/*.py"
|
||||||
required_patterns:
|
required_patterns:
|
||||||
- "require_vendor_context\\(\\)|# public"
|
- "require_vendor_context\\(\\)|# public"
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ API router configuration for multi-tenant ecommerce platform.
|
|||||||
|
|
||||||
This module provides:
|
This module provides:
|
||||||
- API version 1 route aggregation
|
- API version 1 route aggregation
|
||||||
- Route organization by user type (admin, vendor, shop)
|
- Route organization by user type (admin, vendor, storefront)
|
||||||
- Proper route prefixing and tagging
|
- Proper route prefixing and tagging
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
from app.api.v1 import admin, platform, shop, vendor
|
from app.api.v1 import admin, platform, storefront, vendor
|
||||||
from app.api.v1.shared import language, webhooks
|
from app.api.v1.shared import language, webhooks
|
||||||
|
|
||||||
api_router = APIRouter()
|
api_router = APIRouter()
|
||||||
@@ -30,11 +30,12 @@ api_router.include_router(admin.router, prefix="/v1/admin", tags=["admin"])
|
|||||||
api_router.include_router(vendor.router, prefix="/v1/vendor", tags=["vendor"])
|
api_router.include_router(vendor.router, prefix="/v1/vendor", tags=["vendor"])
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# SHOP ROUTES (Public shop frontend API)
|
# STOREFRONT ROUTES (Public customer-facing API)
|
||||||
# Prefix: /api/v1/shop
|
# Prefix: /api/v1/storefront
|
||||||
|
# Note: Previously /api/v1/shop, renamed as not all platforms sell items
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
api_router.include_router(shop.router, prefix="/v1/shop", tags=["shop"])
|
api_router.include_router(storefront.router, prefix="/v1/storefront", tags=["storefront"])
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# PLATFORM ROUTES (Public marketing and signup)
|
# PLATFORM ROUTES (Public marketing and signup)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# app/api/v1/shop/__init__.py
|
# app/api/v1/storefront/__init__.py
|
||||||
"""
|
"""
|
||||||
Shop API router aggregation.
|
Storefront API router aggregation.
|
||||||
|
|
||||||
This module aggregates all shop-related JSON API endpoints (public facing).
|
This module aggregates all storefront-related JSON API endpoints (public facing).
|
||||||
Uses vendor context from middleware - no vendor_id in URLs.
|
Uses vendor context from middleware - no vendor_id in URLs.
|
||||||
|
|
||||||
Endpoints:
|
Endpoints:
|
||||||
@@ -16,48 +16,50 @@ Authentication:
|
|||||||
- Products, Cart, Content Pages: No auth required
|
- Products, Cart, Content Pages: No auth required
|
||||||
- Orders: Requires customer authentication (get_current_customer_api)
|
- Orders: Requires customer authentication (get_current_customer_api)
|
||||||
- Auth: Public (login, register)
|
- Auth: Public (login, register)
|
||||||
|
|
||||||
|
Note: Previously named "shop", renamed to "storefront" as not all platforms
|
||||||
|
sell items - storefront is a more accurate term for the customer-facing interface.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
|
||||||
# Import shop routers
|
# Import storefront routers
|
||||||
from . import addresses, auth, carts, messages, orders, products, profile
|
from . import addresses, auth, carts, messages, orders, products, profile
|
||||||
|
|
||||||
# CMS module router
|
# CMS module router
|
||||||
from app.modules.cms.routes.api.shop import router as cms_shop_router
|
from app.modules.cms.routes.api.storefront import router as cms_storefront_router
|
||||||
|
|
||||||
# Create shop router
|
# Create storefront router
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# SHOP API ROUTES (All vendor-context aware via middleware)
|
# STOREFRONT API ROUTES (All vendor-context aware via middleware)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# Addresses (authenticated)
|
# Addresses (authenticated)
|
||||||
router.include_router(addresses.router, tags=["shop-addresses"])
|
router.include_router(addresses.router, tags=["storefront-addresses"])
|
||||||
|
|
||||||
# Authentication (public)
|
# Authentication (public)
|
||||||
router.include_router(auth.router, tags=["shop-auth"])
|
router.include_router(auth.router, tags=["storefront-auth"])
|
||||||
|
|
||||||
# Products (public)
|
# Products (public)
|
||||||
router.include_router(products.router, tags=["shop-products"])
|
router.include_router(products.router, tags=["storefront-products"])
|
||||||
|
|
||||||
# Shopping cart (public - session based)
|
# Shopping cart (public - session based)
|
||||||
router.include_router(carts.router, tags=["shop-cart"])
|
router.include_router(carts.router, tags=["storefront-cart"])
|
||||||
|
|
||||||
# Orders (authenticated)
|
# Orders (authenticated)
|
||||||
router.include_router(orders.router, tags=["shop-orders"])
|
router.include_router(orders.router, tags=["storefront-orders"])
|
||||||
|
|
||||||
# Messages (authenticated)
|
# Messages (authenticated)
|
||||||
router.include_router(messages.router, tags=["shop-messages"])
|
router.include_router(messages.router, tags=["storefront-messages"])
|
||||||
|
|
||||||
# Profile (authenticated)
|
# Profile (authenticated)
|
||||||
router.include_router(profile.router, tags=["shop-profile"])
|
router.include_router(profile.router, tags=["storefront-profile"])
|
||||||
|
|
||||||
# CMS module router (self-contained module)
|
# CMS module router (self-contained module)
|
||||||
router.include_router(
|
router.include_router(
|
||||||
cms_shop_router, prefix="/content-pages", tags=["shop-content-pages"]
|
cms_storefront_router, prefix="/content-pages", tags=["storefront-content-pages"]
|
||||||
)
|
)
|
||||||
# Legacy: content_pages.router moved to app.modules.cms.routes.api.shop
|
|
||||||
|
|
||||||
__all__ = ["router"]
|
__all__ = ["router"]
|
||||||
@@ -5,11 +5,11 @@ CMS module API routes.
|
|||||||
Provides REST API endpoints for content page management:
|
Provides REST API endpoints for content page management:
|
||||||
- Admin API: Full CRUD for platform administrators
|
- Admin API: Full CRUD for platform administrators
|
||||||
- Vendor API: Vendor-scoped CRUD with ownership checks
|
- Vendor API: Vendor-scoped CRUD with ownership checks
|
||||||
- Shop API: Public read-only access for storefronts
|
- Storefront API: Public read-only access for storefronts
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from app.modules.cms.routes.api.admin import router as admin_router
|
from app.modules.cms.routes.api.admin import router as admin_router
|
||||||
from app.modules.cms.routes.api.vendor import router as vendor_router
|
from app.modules.cms.routes.api.vendor import router as vendor_router
|
||||||
from app.modules.cms.routes.api.shop import router as shop_router
|
from app.modules.cms.routes.api.storefront import router as storefront_router
|
||||||
|
|
||||||
__all__ = ["admin_router", "vendor_router", "shop_router"]
|
__all__ = ["admin_router", "vendor_router", "storefront_router"]
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# app/modules/cms/routes/api/shop.py
|
# app/modules/cms/routes/api/storefront.py
|
||||||
"""
|
"""
|
||||||
Shop Content Pages API (Public)
|
Storefront Content Pages API (Public)
|
||||||
|
|
||||||
Public endpoints for retrieving content pages in shop frontend.
|
Public endpoints for retrieving content pages in storefront.
|
||||||
No authentication required.
|
No authentication required.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -127,14 +127,14 @@ async def homepage(
|
|||||||
|
|
||||||
if landing_page:
|
if landing_page:
|
||||||
# Render landing page with selected template
|
# Render landing page with selected template
|
||||||
from app.routes.shop_pages import get_shop_context
|
from app.routes.storefront_pages import get_storefront_context
|
||||||
|
|
||||||
template_name = landing_page.template or "default"
|
template_name = landing_page.template or "default"
|
||||||
template_path = f"vendor/landing-{template_name}.html"
|
template_path = f"vendor/landing-{template_name}.html"
|
||||||
|
|
||||||
logger.info(f"[HOMEPAGE] Rendering vendor landing page: {template_path}")
|
logger.info(f"[HOMEPAGE] Rendering vendor landing page: {template_path}")
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
template_path, get_shop_context(request, db=db, page=landing_page)
|
template_path, get_storefront_context(request, db=db, page=landing_page)
|
||||||
)
|
)
|
||||||
|
|
||||||
# No landing page - redirect to shop
|
# No landing page - redirect to shop
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
# app/routes/shop_pages.py
|
# app/routes/storefront_pages.py
|
||||||
"""
|
"""
|
||||||
Shop/Customer HTML page routes using Jinja2 templates.
|
Storefront/Customer HTML page routes using Jinja2 templates.
|
||||||
|
|
||||||
These routes serve the public-facing shop interface for customers.
|
These routes serve the public-facing storefront interface for customers.
|
||||||
Authentication required only for account pages.
|
Authentication required only for account pages.
|
||||||
|
|
||||||
|
Note: Previously named "shop_pages.py", renamed to "storefront" as not all
|
||||||
|
platforms sell items - storefront is a more accurate term.
|
||||||
|
|
||||||
AUTHENTICATION:
|
AUTHENTICATION:
|
||||||
- Public pages (catalog, products): No auth required
|
- Public pages (catalog, products): No auth required
|
||||||
- Account pages (dashboard, orders): Requires customer authentication
|
- Account pages (dashboard, orders): Requires customer authentication
|
||||||
- Customer authentication accepts:
|
- Customer authentication accepts:
|
||||||
* customer_token cookie (path=/shop) - for page navigation
|
* customer_token cookie (path=/storefront) - for page navigation
|
||||||
* Authorization header - for API calls
|
* Authorization header - for API calls
|
||||||
- Customers CANNOT access admin or vendor routes
|
- Customers CANNOT access admin or vendor routes
|
||||||
|
|
||||||
Routes (all mounted at /shop/* or /vendors/{code}/shop/* prefix):
|
Routes (all mounted at /storefront/* or /vendors/{code}/storefront/* prefix):
|
||||||
- GET / → Shop homepage / product catalog
|
- GET / → Shop homepage / product catalog
|
||||||
- GET /products → Product catalog
|
- GET /products → Product catalog
|
||||||
- GET /products/{id} → Product detail page
|
- GET /products/{id} → Product detail page
|
||||||
@@ -86,7 +89,7 @@ def get_resolved_storefront_config(db: Session, vendor) -> dict:
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
|
||||||
def get_shop_context(request: Request, db: Session = None, **extra_context) -> dict:
|
def get_storefront_context(request: Request, db: Session = None, **extra_context) -> dict:
|
||||||
"""
|
"""
|
||||||
Build template context for shop pages.
|
Build template context for shop pages.
|
||||||
|
|
||||||
@@ -103,13 +106,13 @@ def get_shop_context(request: Request, db: Session = None, **extra_context) -> d
|
|||||||
|
|
||||||
Example:
|
Example:
|
||||||
# Simple usage
|
# Simple usage
|
||||||
get_shop_context(request)
|
get_storefront_context(request)
|
||||||
|
|
||||||
# With database session for navigation
|
# With database session for navigation
|
||||||
get_shop_context(request, db=db)
|
get_storefront_context(request, db=db)
|
||||||
|
|
||||||
# With extra data
|
# With extra data
|
||||||
get_shop_context(request, db=db, user=current_user, product_id=123)
|
get_storefront_context(request, db=db, user=current_user, product_id=123)
|
||||||
"""
|
"""
|
||||||
# Extract from middleware state
|
# Extract from middleware state
|
||||||
vendor = getattr(request.state, "vendor", None)
|
vendor = getattr(request.state, "vendor", None)
|
||||||
@@ -235,7 +238,7 @@ async def shop_products_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/products.html", get_shop_context(request, db=db)
|
"shop/products.html", get_storefront_context(request, db=db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -261,7 +264,7 @@ async def shop_product_detail_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/product.html", get_shop_context(request, db=db, product_id=product_id)
|
"shop/product.html", get_storefront_context(request, db=db, product_id=product_id)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -287,7 +290,7 @@ async def shop_category_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/category.html", get_shop_context(request, db=db, category_slug=category_slug)
|
"shop/category.html", get_storefront_context(request, db=db, category_slug=category_slug)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -306,7 +309,7 @@ async def shop_cart_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse("shop/cart.html", get_shop_context(request, db=db))
|
return templates.TemplateResponse("shop/cart.html", get_storefront_context(request, db=db))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/checkout", response_class=HTMLResponse, include_in_schema=False)
|
@router.get("/checkout", response_class=HTMLResponse, include_in_schema=False)
|
||||||
@@ -324,7 +327,7 @@ async def shop_checkout_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse("shop/checkout.html", get_shop_context(request, db=db))
|
return templates.TemplateResponse("shop/checkout.html", get_storefront_context(request, db=db))
|
||||||
|
|
||||||
|
|
||||||
@router.get("/search", response_class=HTMLResponse, include_in_schema=False)
|
@router.get("/search", response_class=HTMLResponse, include_in_schema=False)
|
||||||
@@ -342,7 +345,7 @@ async def shop_search_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse("shop/search.html", get_shop_context(request, db=db))
|
return templates.TemplateResponse("shop/search.html", get_storefront_context(request, db=db))
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -366,7 +369,7 @@ async def shop_register_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/register.html", get_shop_context(request, db=db)
|
"shop/account/register.html", get_storefront_context(request, db=db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -386,7 +389,7 @@ async def shop_login_page(request: Request, db: Session = Depends(get_db)):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/login.html", get_shop_context(request, db=db)
|
"shop/account/login.html", get_storefront_context(request, db=db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -408,7 +411,7 @@ async def shop_forgot_password_page(request: Request, db: Session = Depends(get_
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/forgot-password.html", get_shop_context(request, db=db)
|
"shop/account/forgot-password.html", get_storefront_context(request, db=db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -434,7 +437,7 @@ async def shop_reset_password_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/reset-password.html", get_shop_context(request, db=db)
|
"shop/account/reset-password.html", get_storefront_context(request, db=db)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -500,7 +503,7 @@ async def shop_account_dashboard_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/dashboard.html", get_shop_context(request, user=current_customer)
|
"shop/account/dashboard.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -525,7 +528,7 @@ async def shop_orders_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/orders.html", get_shop_context(request, user=current_customer)
|
"shop/account/orders.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -554,7 +557,7 @@ async def shop_order_detail_page(
|
|||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/order-detail.html",
|
"shop/account/order-detail.html",
|
||||||
get_shop_context(request, user=current_customer, order_id=order_id),
|
get_storefront_context(request, user=current_customer, order_id=order_id),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -579,7 +582,7 @@ async def shop_profile_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/profile.html", get_shop_context(request, user=current_customer)
|
"shop/account/profile.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -604,7 +607,7 @@ async def shop_addresses_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/addresses.html", get_shop_context(request, user=current_customer)
|
"shop/account/addresses.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -629,7 +632,7 @@ async def shop_wishlist_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/wishlist.html", get_shop_context(request, user=current_customer)
|
"shop/account/wishlist.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -654,7 +657,7 @@ async def shop_settings_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/settings.html", get_shop_context(request, user=current_customer)
|
"shop/account/settings.html", get_storefront_context(request, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -679,7 +682,7 @@ async def shop_messages_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/messages.html", get_shop_context(request, db=db, user=current_customer)
|
"shop/account/messages.html", get_storefront_context(request, db=db, user=current_customer)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -711,7 +714,7 @@ async def shop_message_detail_page(
|
|||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/account/messages.html",
|
"shop/account/messages.html",
|
||||||
get_shop_context(
|
get_storefront_context(
|
||||||
request, db=db, user=current_customer, conversation_id=conversation_id
|
request, db=db, user=current_customer, conversation_id=conversation_id
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -787,7 +790,7 @@ async def generic_content_page(
|
|||||||
)
|
)
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
"shop/content-page.html", get_shop_context(request, db=db, page=page)
|
"shop/content-page.html", get_storefront_context(request, db=db, page=page)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
26
main.py
26
main.py
@@ -62,7 +62,7 @@ from app.exceptions import ServiceUnavailableException
|
|||||||
from app.exceptions.handler import setup_exception_handlers
|
from app.exceptions.handler import setup_exception_handlers
|
||||||
|
|
||||||
# Import page routers (legacy routes - will be migrated to modules)
|
# Import page routers (legacy routes - will be migrated to modules)
|
||||||
from app.routes import admin_pages, platform_pages, shop_pages, vendor_pages
|
from app.routes import admin_pages, platform_pages, storefront_pages, vendor_pages
|
||||||
|
|
||||||
# Module route auto-discovery
|
# Module route auto-discovery
|
||||||
from app.modules.routes import discover_module_routes, get_vendor_page_routes
|
from app.modules.routes import discover_module_routes, get_vendor_page_routes
|
||||||
@@ -366,20 +366,20 @@ for route_info in vendor_page_routes:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Customer shop pages - Register at TWO prefixes:
|
# Customer shop pages - Register at TWO prefixes:
|
||||||
# 1. /shop/* (for subdomain/custom domain modes)
|
# 1. /storefront/* (for subdomain/custom domain modes)
|
||||||
# 2. /vendors/{code}/shop/* (for path-based development mode)
|
# 2. /vendors/{code}/storefront/* (for path-based development mode)
|
||||||
logger.info("Registering shop page routes:")
|
logger.info("Registering storefront page routes:")
|
||||||
logger.info(" - /shop/* (subdomain/custom domain mode)")
|
logger.info(" - /storefront/* (subdomain/custom domain mode)")
|
||||||
logger.info(" - /vendors/{code}/shop/* (path-based development mode)")
|
logger.info(" - /vendors/{code}/storefront/* (path-based development mode)")
|
||||||
|
|
||||||
app.include_router(
|
app.include_router(
|
||||||
shop_pages.router, prefix="/shop", tags=["shop-pages"], include_in_schema=False
|
storefront_pages.router, prefix="/storefront", tags=["storefront-pages"], include_in_schema=False
|
||||||
)
|
)
|
||||||
|
|
||||||
app.include_router(
|
app.include_router(
|
||||||
shop_pages.router,
|
storefront_pages.router,
|
||||||
prefix="/vendors/{vendor_code}/shop",
|
prefix="/vendors/{vendor_code}/storefront",
|
||||||
tags=["shop-pages"],
|
tags=["storefront-pages"],
|
||||||
include_in_schema=False,
|
include_in_schema=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -399,7 +399,7 @@ async def vendor_root_path(
|
|||||||
if not vendor:
|
if not vendor:
|
||||||
raise HTTPException(status_code=404, detail=f"Vendor '{vendor_code}' not found")
|
raise HTTPException(status_code=404, detail=f"Vendor '{vendor_code}' not found")
|
||||||
|
|
||||||
from app.routes.shop_pages import get_shop_context
|
from app.routes.storefront_pages import get_storefront_context
|
||||||
from app.modules.cms.services import content_page_service
|
from app.modules.cms.services import content_page_service
|
||||||
|
|
||||||
# Get platform_id (use platform from context or default to 1 for OMS)
|
# Get platform_id (use platform from context or default to 1 for OMS)
|
||||||
@@ -421,10 +421,10 @@ async def vendor_root_path(
|
|||||||
template_path = f"vendor/landing-{template_name}.html"
|
template_path = f"vendor/landing-{template_name}.html"
|
||||||
|
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
template_path, get_shop_context(request, db=db, page=landing_page)
|
template_path, get_storefront_context(request, db=db, page=landing_page)
|
||||||
)
|
)
|
||||||
# No landing page - redirect to shop
|
# No landing page - redirect to shop
|
||||||
return RedirectResponse(url=f"/vendors/{vendor_code}/shop/", status_code=302)
|
return RedirectResponse(url=f"/vendors/{vendor_code}/storefront/", status_code=302)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# tests/integration/api/v1/shop/test_addresses.py
|
# tests/integration/api/v1/storefront/test_addresses.py
|
||||||
"""Integration tests for shop addresses API endpoints.
|
"""Integration tests for shop addresses API endpoints.
|
||||||
|
|
||||||
Tests the /api/v1/shop/addresses/* endpoints.
|
Tests the /api/v1/storefront/addresses/* endpoints.
|
||||||
All endpoints require customer JWT authentication with vendor context.
|
All endpoints require customer JWT authentication with vendor context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -158,13 +158,13 @@ def other_customer_address(db, test_vendor, other_customer):
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressesListAPI:
|
class TestShopAddressesListAPI:
|
||||||
"""Test shop addresses list endpoint at /api/v1/shop/addresses."""
|
"""Test shop addresses list endpoint at /api/v1/storefront/addresses."""
|
||||||
|
|
||||||
def test_list_addresses_requires_authentication(self, client, test_vendor):
|
def test_list_addresses_requires_authentication(self, client, test_vendor):
|
||||||
"""Test that listing addresses requires authentication."""
|
"""Test that listing addresses requires authentication."""
|
||||||
with patch("app.api.v1.shop.addresses.getattr") as mock_getattr:
|
with patch("app.api.v1.shop.addresses.getattr") as mock_getattr:
|
||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
response = client.get("/api/v1/shop/addresses")
|
response = client.get("/api/v1/storefront/addresses")
|
||||||
assert response.status_code in [401, 403]
|
assert response.status_code in [401, 403]
|
||||||
|
|
||||||
def test_list_addresses_success(
|
def test_list_addresses_success(
|
||||||
@@ -181,7 +181,7 @@ class TestShopAddressesListAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ class TestShopAddressesListAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ class TestShopAddressesListAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -242,7 +242,7 @@ class TestShopAddressesListAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressDetailAPI:
|
class TestShopAddressDetailAPI:
|
||||||
"""Test shop address detail endpoint at /api/v1/shop/addresses/{address_id}."""
|
"""Test shop address detail endpoint at /api/v1/storefront/addresses/{address_id}."""
|
||||||
|
|
||||||
def test_get_address_success(
|
def test_get_address_success(
|
||||||
self,
|
self,
|
||||||
@@ -258,7 +258,7 @@ class TestShopAddressDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/addresses/{customer_address.id}",
|
f"/api/v1/storefront/addresses/{customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ class TestShopAddressDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/addresses/99999",
|
"/api/v1/storefront/addresses/99999",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ class TestShopAddressDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/addresses/{other_customer_address.id}",
|
f"/api/v1/storefront/addresses/{other_customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -310,7 +310,7 @@ class TestShopAddressDetailAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressCreateAPI:
|
class TestShopAddressCreateAPI:
|
||||||
"""Test shop address creation at POST /api/v1/shop/addresses."""
|
"""Test shop address creation at POST /api/v1/storefront/addresses."""
|
||||||
|
|
||||||
def test_create_address_success(
|
def test_create_address_success(
|
||||||
self, client, shop_customer_headers, test_vendor, shop_customer
|
self, client, shop_customer_headers, test_vendor, shop_customer
|
||||||
@@ -333,7 +333,7 @@ class TestShopAddressCreateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=address_data,
|
json=address_data,
|
||||||
)
|
)
|
||||||
@@ -367,7 +367,7 @@ class TestShopAddressCreateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=address_data,
|
json=address_data,
|
||||||
)
|
)
|
||||||
@@ -391,7 +391,7 @@ class TestShopAddressCreateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/addresses",
|
"/api/v1/storefront/addresses",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=address_data,
|
json=address_data,
|
||||||
)
|
)
|
||||||
@@ -403,7 +403,7 @@ class TestShopAddressCreateAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressUpdateAPI:
|
class TestShopAddressUpdateAPI:
|
||||||
"""Test shop address update at PUT /api/v1/shop/addresses/{address_id}."""
|
"""Test shop address update at PUT /api/v1/storefront/addresses/{address_id}."""
|
||||||
|
|
||||||
def test_update_address_success(
|
def test_update_address_success(
|
||||||
self,
|
self,
|
||||||
@@ -424,7 +424,7 @@ class TestShopAddressUpdateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/api/v1/shop/addresses/{customer_address.id}",
|
f"/api/v1/storefront/addresses/{customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=update_data,
|
json=update_data,
|
||||||
)
|
)
|
||||||
@@ -445,7 +445,7 @@ class TestShopAddressUpdateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
"/api/v1/shop/addresses/99999",
|
"/api/v1/storefront/addresses/99999",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=update_data,
|
json=update_data,
|
||||||
)
|
)
|
||||||
@@ -468,7 +468,7 @@ class TestShopAddressUpdateAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/api/v1/shop/addresses/{other_customer_address.id}",
|
f"/api/v1/storefront/addresses/{other_customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
json=update_data,
|
json=update_data,
|
||||||
)
|
)
|
||||||
@@ -480,7 +480,7 @@ class TestShopAddressUpdateAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressDeleteAPI:
|
class TestShopAddressDeleteAPI:
|
||||||
"""Test shop address deletion at DELETE /api/v1/shop/addresses/{address_id}."""
|
"""Test shop address deletion at DELETE /api/v1/storefront/addresses/{address_id}."""
|
||||||
|
|
||||||
def test_delete_address_success(
|
def test_delete_address_success(
|
||||||
self,
|
self,
|
||||||
@@ -496,7 +496,7 @@ class TestShopAddressDeleteAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.delete(
|
response = client.delete(
|
||||||
f"/api/v1/shop/addresses/{customer_address.id}",
|
f"/api/v1/storefront/addresses/{customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -511,7 +511,7 @@ class TestShopAddressDeleteAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.delete(
|
response = client.delete(
|
||||||
"/api/v1/shop/addresses/99999",
|
"/api/v1/storefront/addresses/99999",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -531,7 +531,7 @@ class TestShopAddressDeleteAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.delete(
|
response = client.delete(
|
||||||
f"/api/v1/shop/addresses/{other_customer_address.id}",
|
f"/api/v1/storefront/addresses/{other_customer_address.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -542,7 +542,7 @@ class TestShopAddressDeleteAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopAddressSetDefaultAPI:
|
class TestShopAddressSetDefaultAPI:
|
||||||
"""Test set address as default at PUT /api/v1/shop/addresses/{address_id}/default."""
|
"""Test set address as default at PUT /api/v1/storefront/addresses/{address_id}/default."""
|
||||||
|
|
||||||
def test_set_default_success(
|
def test_set_default_success(
|
||||||
self,
|
self,
|
||||||
@@ -577,7 +577,7 @@ class TestShopAddressSetDefaultAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/api/v1/shop/addresses/{second_address.id}/default",
|
f"/api/v1/storefront/addresses/{second_address.id}/default",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -594,7 +594,7 @@ class TestShopAddressSetDefaultAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
"/api/v1/shop/addresses/99999/default",
|
"/api/v1/storefront/addresses/99999/default",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -614,7 +614,7 @@ class TestShopAddressSetDefaultAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.put(
|
response = client.put(
|
||||||
f"/api/v1/shop/addresses/{other_customer_address.id}/default",
|
f"/api/v1/storefront/addresses/{other_customer_address.id}/default",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# tests/integration/api/v1/shop/test_orders.py
|
# tests/integration/api/v1/storefront/test_orders.py
|
||||||
"""Integration tests for shop orders API endpoints.
|
"""Integration tests for shop orders API endpoints.
|
||||||
|
|
||||||
Tests the /api/v1/shop/orders/* endpoints.
|
Tests the /api/v1/storefront/orders/* endpoints.
|
||||||
All endpoints require customer JWT authentication with vendor context.
|
All endpoints require customer JWT authentication with vendor context.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -306,13 +306,13 @@ def other_customer_order(db, test_vendor, other_customer):
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopOrdersListAPI:
|
class TestShopOrdersListAPI:
|
||||||
"""Test shop orders list endpoint at /api/v1/shop/orders."""
|
"""Test shop orders list endpoint at /api/v1/storefront/orders."""
|
||||||
|
|
||||||
def test_list_orders_requires_authentication(self, client, test_vendor):
|
def test_list_orders_requires_authentication(self, client, test_vendor):
|
||||||
"""Test that listing orders requires authentication."""
|
"""Test that listing orders requires authentication."""
|
||||||
with patch("app.api.v1.shop.orders.getattr") as mock_getattr:
|
with patch("app.api.v1.shop.orders.getattr") as mock_getattr:
|
||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
response = client.get("/api/v1/shop/orders")
|
response = client.get("/api/v1/storefront/orders")
|
||||||
# Without token, should get 401 or 403
|
# Without token, should get 401 or 403
|
||||||
assert response.status_code in [401, 403]
|
assert response.status_code in [401, 403]
|
||||||
|
|
||||||
@@ -327,7 +327,7 @@ class TestShopOrdersListAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/orders",
|
"/api/v1/storefront/orders",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ class TestShopOrdersListAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/orders",
|
"/api/v1/storefront/orders",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -358,7 +358,7 @@ class TestShopOrdersListAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopOrderDetailAPI:
|
class TestShopOrderDetailAPI:
|
||||||
"""Test shop order detail endpoint at /api/v1/shop/orders/{order_id}."""
|
"""Test shop order detail endpoint at /api/v1/storefront/orders/{order_id}."""
|
||||||
|
|
||||||
def test_get_order_detail_success(
|
def test_get_order_detail_success(
|
||||||
self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
|
self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
|
||||||
@@ -369,7 +369,7 @@ class TestShopOrderDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/orders/{shop_order.id}",
|
f"/api/v1/storefront/orders/{shop_order.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -390,7 +390,7 @@ class TestShopOrderDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/orders/99999",
|
"/api/v1/storefront/orders/99999",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -410,7 +410,7 @@ class TestShopOrderDetailAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/orders/{other_customer_order.id}",
|
f"/api/v1/storefront/orders/{other_customer_order.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ class TestShopOrderDetailAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestShopOrderInvoiceDownloadAPI:
|
class TestShopOrderInvoiceDownloadAPI:
|
||||||
"""Test shop order invoice download at /api/v1/shop/orders/{order_id}/invoice."""
|
"""Test shop order invoice download at /api/v1/storefront/orders/{order_id}/invoice."""
|
||||||
|
|
||||||
def test_download_invoice_pending_order_rejected(
|
def test_download_invoice_pending_order_rejected(
|
||||||
self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
|
self, client, shop_customer_headers, shop_order, test_vendor, shop_customer
|
||||||
@@ -433,7 +433,7 @@ class TestShopOrderInvoiceDownloadAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/orders/{shop_order.id}/invoice",
|
f"/api/v1/storefront/orders/{shop_order.id}/invoice",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -485,7 +485,7 @@ class TestShopOrderInvoiceDownloadAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/orders/{other_customer_order.id}/invoice",
|
f"/api/v1/storefront/orders/{other_customer_order.id}/invoice",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -501,7 +501,7 @@ class TestShopOrderInvoiceDownloadAPI:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/orders/99999/invoice",
|
"/api/v1/storefront/orders/99999/invoice",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -523,7 +523,7 @@ class TestShopOrderVATFields:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
f"/api/v1/shop/orders/{shop_order.id}",
|
f"/api/v1/storefront/orders/{shop_order.id}",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -544,7 +544,7 @@ class TestShopOrderVATFields:
|
|||||||
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
with patch("app.api.deps._validate_customer_token") as mock_validate:
|
||||||
mock_validate.return_value = shop_customer
|
mock_validate.return_value = shop_customer
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/shop/orders",
|
"/api/v1/storefront/orders",
|
||||||
headers=shop_customer_headers,
|
headers=shop_customer_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# tests/integration/api/v1/shop/test_password_reset.py
|
# tests/integration/api/v1/storefront/test_password_reset.py
|
||||||
"""Integration tests for shop password reset API endpoints.
|
"""Integration tests for shop password reset API endpoints.
|
||||||
|
|
||||||
Tests the /api/v1/shop/auth/forgot-password and /api/v1/shop/auth/reset-password endpoints.
|
Tests the /api/v1/storefront/auth/forgot-password and /api/v1/storefront/auth/reset-password endpoints.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
@@ -104,7 +104,7 @@ def used_reset_token(db, shop_customer):
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestForgotPasswordAPI:
|
class TestForgotPasswordAPI:
|
||||||
"""Test forgot password endpoint at /api/v1/shop/auth/forgot-password."""
|
"""Test forgot password endpoint at /api/v1/storefront/auth/forgot-password."""
|
||||||
|
|
||||||
def test_forgot_password_existing_customer(
|
def test_forgot_password_existing_customer(
|
||||||
self, client, db, test_vendor, shop_customer
|
self, client, db, test_vendor, shop_customer
|
||||||
@@ -119,7 +119,7 @@ class TestForgotPasswordAPI:
|
|||||||
mock_email_service.return_value = mock_instance
|
mock_email_service.return_value = mock_instance
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/forgot-password",
|
"/api/v1/storefront/auth/forgot-password",
|
||||||
params={"email": shop_customer.email},
|
params={"email": shop_customer.email},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -139,7 +139,7 @@ class TestForgotPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/forgot-password",
|
"/api/v1/storefront/auth/forgot-password",
|
||||||
params={"email": "nonexistent@example.com"},
|
params={"email": "nonexistent@example.com"},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ class TestForgotPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/forgot-password",
|
"/api/v1/storefront/auth/forgot-password",
|
||||||
params={"email": inactive_customer.email},
|
params={"email": inactive_customer.email},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ class TestForgotPasswordAPI:
|
|||||||
|
|
||||||
with patch("app.api.v1.shop.auth.EmailService"):
|
with patch("app.api.v1.shop.auth.EmailService"):
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/forgot-password",
|
"/api/v1/storefront/auth/forgot-password",
|
||||||
params={"email": shop_customer.email},
|
params={"email": shop_customer.email},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ class TestForgotPasswordAPI:
|
|||||||
|
|
||||||
with patch("app.api.v1.shop.auth.EmailService"):
|
with patch("app.api.v1.shop.auth.EmailService"):
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/forgot-password",
|
"/api/v1/storefront/auth/forgot-password",
|
||||||
params={"email": shop_customer.email},
|
params={"email": shop_customer.email},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -232,7 +232,7 @@ class TestForgotPasswordAPI:
|
|||||||
@pytest.mark.api
|
@pytest.mark.api
|
||||||
@pytest.mark.shop
|
@pytest.mark.shop
|
||||||
class TestResetPasswordAPI:
|
class TestResetPasswordAPI:
|
||||||
"""Test reset password endpoint at /api/v1/shop/auth/reset-password."""
|
"""Test reset password endpoint at /api/v1/storefront/auth/reset-password."""
|
||||||
|
|
||||||
def test_reset_password_success(
|
def test_reset_password_success(
|
||||||
self, client, db, test_vendor, shop_customer, valid_reset_token
|
self, client, db, test_vendor, shop_customer, valid_reset_token
|
||||||
@@ -243,7 +243,7 @@ class TestResetPasswordAPI:
|
|||||||
|
|
||||||
new_password = "newpassword123"
|
new_password = "newpassword123"
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": valid_reset_token,
|
"reset_token": valid_reset_token,
|
||||||
"new_password": new_password,
|
"new_password": new_password,
|
||||||
@@ -271,7 +271,7 @@ class TestResetPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": valid_reset_token,
|
"reset_token": valid_reset_token,
|
||||||
"new_password": "newpassword123",
|
"new_password": "newpassword123",
|
||||||
@@ -294,7 +294,7 @@ class TestResetPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": "invalid_token_12345",
|
"reset_token": "invalid_token_12345",
|
||||||
"new_password": "newpassword123",
|
"new_password": "newpassword123",
|
||||||
@@ -311,7 +311,7 @@ class TestResetPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": expired_reset_token,
|
"reset_token": expired_reset_token,
|
||||||
"new_password": "newpassword123",
|
"new_password": "newpassword123",
|
||||||
@@ -328,7 +328,7 @@ class TestResetPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": used_reset_token,
|
"reset_token": used_reset_token,
|
||||||
"new_password": "newpassword123",
|
"new_password": "newpassword123",
|
||||||
@@ -345,7 +345,7 @@ class TestResetPasswordAPI:
|
|||||||
mock_getattr.return_value = test_vendor
|
mock_getattr.return_value = test_vendor
|
||||||
|
|
||||||
response = client.post(
|
response = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": valid_reset_token,
|
"reset_token": valid_reset_token,
|
||||||
"new_password": "short", # Less than 8 chars
|
"new_password": "short", # Less than 8 chars
|
||||||
@@ -363,7 +363,7 @@ class TestResetPasswordAPI:
|
|||||||
|
|
||||||
# First reset should succeed
|
# First reset should succeed
|
||||||
response1 = client.post(
|
response1 = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": valid_reset_token,
|
"reset_token": valid_reset_token,
|
||||||
"new_password": "newpassword123",
|
"new_password": "newpassword123",
|
||||||
@@ -373,7 +373,7 @@ class TestResetPasswordAPI:
|
|||||||
|
|
||||||
# Second reset with same token should fail
|
# Second reset with same token should fail
|
||||||
response2 = client.post(
|
response2 = client.post(
|
||||||
"/api/v1/shop/auth/reset-password",
|
"/api/v1/storefront/auth/reset-password",
|
||||||
params={
|
params={
|
||||||
"reset_token": valid_reset_token,
|
"reset_token": valid_reset_token,
|
||||||
"new_password": "anotherpassword123",
|
"new_password": "anotherpassword123",
|
||||||
Reference in New Issue
Block a user