test updates to take into account exception management

This commit is contained in:
2025-09-27 13:47:36 +02:00
parent 3e720212d9
commit 6b9817f179
38 changed files with 2951 additions and 871 deletions

View File

@@ -1,27 +1,20 @@
# auth.py - Keep security-critical validation
import re
from datetime import datetime
from typing import List, Optional
from typing import Optional
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
# User Authentication Models
class UserRegister(BaseModel):
email: EmailStr = Field(..., description="Valid email address")
username: str = Field(
..., min_length=3, max_length=50, description="Username (3-50 characters)"
)
password: str = Field(
..., min_length=6, description="Password (minimum 6 characters)"
)
username: str = Field(..., description="Username")
password: str = Field(..., description="Password")
# Keep security validation in Pydantic for auth
@field_validator("username")
@classmethod
def validate_username(cls, v):
if not re.match(r"^[a-zA-Z0-9_]+$", v):
raise ValueError(
"Username must contain only letters, numbers, or underscores"
)
raise ValueError("Username must contain only letters, numbers, or underscores")
return v.lower().strip()
@field_validator("password")
@@ -31,7 +24,6 @@ class UserRegister(BaseModel):
raise ValueError("Password must be at least 6 characters long")
return v
class UserLogin(BaseModel):
username: str = Field(..., description="Username")
password: str = Field(..., description="Password")
@@ -41,10 +33,8 @@ class UserLogin(BaseModel):
def validate_username(cls, v):
return v.strip()
class UserResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
email: str
username: str
@@ -54,7 +44,6 @@ class UserResponse(BaseModel):
created_at: datetime
updated_at: datetime
class LoginResponse(BaseModel):
access_token: str
token_type: str = "bearer"

View File

@@ -1,58 +1,34 @@
import re
# marketplace.py - Keep URL validation, remove business constraints
from datetime import datetime
from typing import List, Optional
from typing import Optional
from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
# Marketplace Import Models
class MarketplaceImportRequest(BaseModel):
url: str = Field(..., description="URL to CSV file from marketplace")
marketplace: str = Field(
default="Letzshop",
description="Name of the marketplace (e.g., Letzshop, Amazon, eBay)",
)
marketplace: str = Field(default="Letzshop", description="Marketplace name")
shop_code: str = Field(..., description="Shop code to associate products with")
batch_size: Optional[int] = Field(
1000, gt=0, le=10000, description="Batch size for processing"
)
batch_size: Optional[int] = Field(1000, description="Processing batch size")
# Removed: gt=0, le=10000 constraints - let service handle
@field_validator("url")
@classmethod
def validate_url(cls, v):
# Keep URL format validation for security
if not v.startswith(("http://", "https://")):
raise ValueError("URL must start with http:// or https://")
return v
@field_validator("marketplace")
@field_validator("marketplace", "shop_code")
@classmethod
def validate_marketplace(cls, v):
# You can add validation for supported marketplaces here
supported_marketplaces = [
"Letzshop",
"Amazon",
"eBay",
"Etsy",
"Shopify",
"Other",
]
if v not in supported_marketplaces:
# For now, allow any marketplace but log it
pass
def validate_strings(cls, v):
return v.strip()
@field_validator("shop_code")
@classmethod
def validate_shop_code(cls, v):
return v.upper().strip()
class MarketplaceImportJobResponse(BaseModel):
job_id: int
status: str
marketplace: str
shop_id: int
shop_code: Optional[str] = None # Will be populated from shop relationship
shop_code: Optional[str] = None
shop_name: str
message: Optional[str] = None
imported: Optional[int] = 0

View File

@@ -1,12 +1,9 @@
import re
# product.py - Simplified validation
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field
from models.schemas.stock import StockSummaryResponse
class ProductBase(BaseModel):
product_id: Optional[str] = None
title: Optional[str] = None
@@ -45,42 +42,30 @@ class ProductBase(BaseModel):
identifier_exists: Optional[str] = None
shipping: Optional[str] = None
currency: Optional[str] = None
# New marketplace fields
marketplace: Optional[str] = None
shop_name: Optional[str] = None
class ProductCreate(ProductBase):
product_id: str = Field(..., min_length=1, description="Product ID is required")
title: str = Field(..., min_length=1, description="Title is required")
@field_validator("product_id", "title")
@classmethod
def validate_required_fields(cls, v):
if not v or not v.strip():
raise ValueError("Field cannot be empty")
return v.strip()
product_id: str = Field(..., description="Product identifier")
title: str = Field(..., description="Product title")
# Removed: min_length constraints and custom validators
# Service will handle empty string validation with proper domain exceptions
class ProductUpdate(ProductBase):
pass
class ProductResponse(ProductBase):
model_config = ConfigDict(from_attributes=True)
id: int
created_at: datetime
updated_at: datetime
class ProductListResponse(BaseModel):
products: List[ProductResponse]
total: int
skip: int
limit: int
class ProductDetailResponse(BaseModel):
product: ProductResponse
stock_info: Optional[StockSummaryResponse] = None

View File

@@ -1,51 +1,32 @@
# shop.py - Keep basic format validation, remove business logic
import re
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
from pydantic import BaseModel, ConfigDict, Field, field_validator
from models.schemas.product import ProductResponse
class ShopCreate(BaseModel):
shop_code: str = Field(
...,
min_length=3,
max_length=50,
description="Unique shop code (e.g., TECHSTORE)",
)
shop_name: str = Field(
..., min_length=1, max_length=200, description="Display name of the shop"
)
description: Optional[str] = Field(
None, max_length=2000, description="Shop description"
)
shop_code: str = Field(..., description="Unique shop identifier")
shop_name: str = Field(..., description="Display name of the shop")
description: Optional[str] = Field(None, description="Shop description")
contact_email: Optional[str] = None
contact_phone: Optional[str] = None
website: Optional[str] = None
business_address: Optional[str] = None
tax_number: Optional[str] = None
@field_validator("shop_code")
def validate_shop_code(cls, v):
# Convert to uppercase and check format
v = v.upper().strip()
if not v.replace("_", "").replace("-", "").isalnum():
raise ValueError(
"Shop code must be alphanumeric (underscores and hyphens allowed)"
)
return v
# Removed: min_length, max_length constraints - let service handle
@field_validator("contact_email")
@classmethod
def validate_contact_email(cls, v):
# Keep basic format validation for data integrity
if v and ("@" not in v or "." not in v):
raise ValueError("Invalid email format")
return v.lower() if v else v
class ShopUpdate(BaseModel):
shop_name: Optional[str] = Field(None, min_length=1, max_length=200)
description: Optional[str] = Field(None, max_length=2000)
shop_name: Optional[str] = None
description: Optional[str] = None
contact_email: Optional[str] = None
contact_phone: Optional[str] = None
website: Optional[str] = None
@@ -53,15 +34,14 @@ class ShopUpdate(BaseModel):
tax_number: Optional[str] = None
@field_validator("contact_email")
@classmethod
def validate_contact_email(cls, v):
if v and ("@" not in v or "." not in v):
raise ValueError("Invalid email format")
return v.lower() if v else v
class ShopResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
shop_code: str
shop_name: str
@@ -77,40 +57,27 @@ class ShopResponse(BaseModel):
created_at: datetime
updated_at: datetime
class ShopListResponse(BaseModel):
shops: List[ShopResponse]
total: int
skip: int
limit: int
class ShopProductCreate(BaseModel):
product_id: str = Field(..., description="Product ID to add to shop")
shop_product_id: Optional[str] = Field(
None, description="Shop's internal product ID"
)
shop_price: Optional[float] = Field(
None, ge=0, description="Shop-specific price override"
)
shop_sale_price: Optional[float] = Field(
None, ge=0, description="Shop-specific sale price"
)
shop_currency: Optional[str] = Field(None, description="Shop-specific currency")
shop_availability: Optional[str] = Field(
None, description="Shop-specific availability"
)
shop_condition: Optional[str] = Field(None, description="Shop-specific condition")
shop_product_id: Optional[str] = None
shop_price: Optional[float] = None # Removed: ge=0 constraint
shop_sale_price: Optional[float] = None # Removed: ge=0 constraint
shop_currency: Optional[str] = None
shop_availability: Optional[str] = None
shop_condition: Optional[str] = None
is_featured: bool = Field(False, description="Featured product flag")
min_quantity: int = Field(1, ge=1, description="Minimum order quantity")
max_quantity: Optional[int] = Field(
None, ge=1, description="Maximum order quantity"
)
min_quantity: int = Field(1, description="Minimum order quantity")
max_quantity: Optional[int] = None # Removed: ge=1 constraint
# Service will validate price ranges and quantity logic
class ShopProductResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
shop_id: int
product: ProductResponse

View File

@@ -1,31 +1,26 @@
import re
# stock.py - Remove business logic validation constraints
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator
# Stock Models
class StockBase(BaseModel):
gtin: str = Field(..., min_length=1, description="GTIN is required")
location: str = Field(..., min_length=1, description="Location is required")
gtin: str = Field(..., description="GTIN identifier")
location: str = Field(..., description="Storage location")
class StockCreate(StockBase):
quantity: int = Field(ge=0, description="Quantity must be non-negative")
quantity: int = Field(..., description="Initial stock quantity")
# Removed: ge=0 constraint - let service handle negative validation
class StockAdd(StockBase):
quantity: int = Field(gt=0, description="Quantity to add must be positive")
quantity: int = Field(..., description="Quantity to add/remove")
# Removed: gt=0 constraint - let service handle zero/negative validation
class StockUpdate(BaseModel):
quantity: int = Field(ge=0, description="Quantity must be non-negative")
quantity: int = Field(..., description="New stock quantity")
# Removed: ge=0 constraint - let service handle negative validation
class StockResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
gtin: str
location: str
@@ -33,12 +28,10 @@ class StockResponse(BaseModel):
created_at: datetime
updated_at: datetime
class StockLocationResponse(BaseModel):
location: str
quantity: int
class StockSummaryResponse(BaseModel):
gtin: str
total_quantity: int