Product Catalog Service ‐ API Integration Guide (Actual Implementation) - Wiz-DevTech/prettygirllz GitHub Wiki
The Product Catalog Service provides REST APIs for managing products with specialized endpoints for Candy and Fashion categories. The service is currently unsecured and all endpoints are publicly accessible.
-
Main API:
http://localhost:8080/api
-
Swagger UI:
http://localhost:8080/api/swagger-ui.html
-
API Documentation:
http://localhost:8080/api/api-docs
-
Content-Type:
application/json
for all requests - No authentication required (all endpoints are public)
-
Method:
GET
-
URL:
/api/products
- Description: Retrieves all products
- Authentication: None
- Response Example:
[
{
"id": 3,
"name": "Fashion Item",
"description": "A trendy fashion item",
"category": "FASHION",
"active": true,
"createdDate": "2025-05-10T04:58:17.889678",
"lastModifiedDate": "2025-05-10T04:58:17.889678",
"createdBy": "system",
"lastModifiedBy": "system",
"skus": []
}
]
-
Method:
GET
-
URL:
/api/products/{id}
- Description: Retrieves a specific product
-
Path Parameters:
-
id
(required): Product ID
-
-
Response Codes:
-
200 OK
: Product found -
404 Not Found
: Product doesn't exist
-
-
Method:
GET
-
URL:
/api/products/with-skus/{id}
- Description: Retrieves product with all associated SKUs
-
Implementation: Uses
JOIN FETCH
for performance - Response: Product with populated SKUs array
-
Method:
GET
-
URL:
/api/products/category/{category}
- Description: Filters products by category
-
Path Parameters:
-
category
: Category name (e.g., "CANDY", "FASHION")
-
-
Example:
/api/products/category/CANDY
-
Method:
GET
-
URL:
/api/products/search
- Description: Case-insensitive name search
-
Query Parameters:
-
name
(required): Search term
-
-
Example:
/api/products/search?name=sweet
-
Method:
GET
-
URL:
/api/products/available
- Description: Returns products with available SKU inventory
-
Filter Logic:
availableQuantity > 0 AND active = true
-
Method:
POST
-
URL:
/api/products
- Request Body:
{
"name": "New Product",
"description": "Product description",
"category": "CANDY"
}
-
Validation:
- SKU codes must match pattern
[A-Z]{2}[0-9]{6}
- Throws
InvalidSKUException
for invalid formats
- SKU codes must match pattern
-
Method:
PUT
-
URL:
/api/products/{id}
- Description: Updates entire product
- Validation: Same as create + existence check
-
Error:
RuntimeException
if product not found
-
Method:
DELETE
-
URL:
/api/products/{id}
- Description: Permanently removes product and related SKUs
- Cascade: Deletes associated SKUs and color variants
-
Method:
PATCH
-
URL:
/api/products/{id}/deactivate
- Description: Soft delete (sets active=false)
-
Response:
204 No Content
-
Method:
GET
-
URL:
/api/candy
-
Filter:
category = "CANDY"
-
Method:
GET
-
URL:
/api/candy/{id}
- Validation: Verifies product category is CANDY
-
Response:
404
if not candy product
-
Method:
POST
-
URL:
/api/candy
-
Auto-sets:
category = "CANDY"
- Request Body:
{
"name": "Gummy Bears",
"description": "Sweet gummy candy"
}
-
Method:
PUT
-
URL:
/api/candy/{id}
- Validation: Ensures product remains CANDY category
-
Enforces:
category = "CANDY"
-
Method:
DELETE
-
URL:
/api/candy/{id}
- Validation: Only deletes if category is CANDY
- Same patterns as Candy API
- Base URL:
/api/fashion
- Category filter:
"FASHION"
- All operations mirror candy endpoints
{
"id": "Long (generated)",
"name": "String (required)",
"description": "String (optional)",
"category": "String (required)",
"active": "boolean (default: true)",
"createdDate": "LocalDateTime",
"lastModifiedDate": "LocalDateTime",
"createdBy": "String (default: 'system')",
"lastModifiedBy": "String (default: 'system')",
"skus": ["Array of SKU objects"]
}
{
"id": "Long (generated)",
"skuCode": "String (unique, pattern: [A-Z]{2}[0-9]{6})",
"productId": "Long (foreign key)",
"price": "BigDecimal (required, > 0)",
"availableQuantity": "Integer (default: 0, >= 0)",
"active": "boolean (default: true)",
"createdDate": "LocalDateTime",
"lastModifiedDate": "LocalDateTime",
"createdBy": "String",
"lastModifiedBy": "String"
}
{
"id": "Long (generated)",
"skuId": "Long (foreign key)",
"colorName": "String (required)",
"colorCode": "String (optional)",
"imageUrl": "String (optional)",
"createdDate": "LocalDateTime",
"lastModifiedDate": "LocalDateTime",
"createdBy": "String",
"lastModifiedBy": "String"
}
{
"status": "BAD_REQUEST",
"timestamp": "2025-05-16T07:04:37.206",
"message": "Invalid SKU format: XX1234567",
"path": null
}
{
"status": "INTERNAL_SERVER_ERROR",
"timestamp": "2025-05-16T07:04:37.206",
"message": "An unexpected error occurred: Product not found with id: 999",
"path": null
}
{
"fieldName": "Error message from messages.properties"
}
-
Pattern:
[A-Z]{2}\\d{6}
- Example Valid: "AB123456"
- Example Invalid: "ab123456", "ABC12345", "AB12345X"
- Products: name NOT NULL, category NOT NULL
- SKUs: price > 0, availableQuantity >= 0
- SKU code must be unique
class ProductService {
private baseUrl = 'http://localhost:8080/api';
async getAllProducts(): Promise<Product[]> {
const response = await fetch(`${this.baseUrl}/products`);
if (!response.ok) {
throw new Error(`Failed to fetch: ${response.status}`);
}
return response.json();
}
async createCandyProduct(product: Partial<Product>): Promise<Product> {
const response = await fetch(`${this.baseUrl}/candy`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(product),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Failed to create product');
}
return response.json();
}
async searchProducts(name: string): Promise<Product[]> {
const response = await fetch(
`${this.baseUrl}/products/search?name=${encodeURIComponent(name)}`
);
return response.json();
}
}
import requests
from typing import List, Dict, Optional
class ProductService:
def __init__(self, base_url: str = "http://localhost:8080/api"):
self.base_url = base_url
def get_all_products(self) -> List[Dict]:
response = requests.get(f"{self.base_url}/products")
response.raise_for_status()
return response.json()
def create_product(self, product: Dict) -> Dict:
response = requests.post(
f"{self.base_url}/products",
json=product
)
response.raise_for_status()
return response.json()
def get_fashion_products(self) -> List[Dict]:
response = requests.get(f"{self.base_url}/fashion")
response.raise_for_status()
return response.json()
# Get all products
curl http://localhost:8080/api/products
# Create a candy product
curl -X POST http://localhost:8080/api/candy \
-H "Content-Type: application/json" \
-d '{
"name": "Chocolate Bar",
"description": "Rich dark chocolate"
}'
# Search products
curl "http://localhost:8080/api/products/search?name=sweet"
# Get product with SKUs
curl http://localhost:8080/api/products/with-skus/3
# Deactivate product
curl -X PATCH http://localhost:8080/api/products/3/deactivate
- All endpoints are publicly accessible
- No rate limiting implemented
- No CORS configuration
- All list endpoints return complete datasets
- Could be problematic with large datasets
- No sorting options available
- All service methods use
@Transactional
- Read-only transactions for queries
- Proper cascade deletes configured
- All entities have created/modified timestamps
- Created/modified by fields (default: "system")
- JPA auditing enabled with system-wide auditor
- SQL queries are logged (debug level)
- N+1 problem avoided with JOIN FETCH queries
- No query optimization implemented
- No caching implemented
- Direct database queries for all requests
- Connection pooling uses Hikari defaults
- Open
http://localhost:8080/api/swagger-ui.html
- Expand endpoint sections
- Click "Try it out" buttons
- Fill parameters and execute
- View responses and status codes
- Products with IDs: 3, 4, 5, 6, 7
- Categories: FASHION, CANDY
- All created by "system" user
- Created around 2025-05-10
- No authentication required
- No input sanitization beyond SKU validation
- Database credentials in plain text
- No HTTPS enforcement
- No request rate limiting
- Add authentication (JWT/OAuth2)
- Implement HTTPS
- Add input validation annotations
- Encrypt configuration properties
- Add CORS headers for web clients
- Available at
/api/api-docs
- Version: 1.0
- Format: OAS 3.0
- Includes all endpoints with parameters
- Schema definitions for all models
- Interactive API testing
- Request/response examples
- Model schema visualization
- Operation grouping by controller
- Try-it-out functionality
- 404 Not Found: Resource doesn't exist
- 400 Bad Request: Invalid SKU format
- 500 Internal Error: Unexpected exceptions
// Example error handling
try {
const product = await productService.getProduct(id);
} catch (error) {
if (error.status === 404) {
console.log('Product not found');
} else if (error.status === 400) {
console.log('Invalid request:', error.message);
} else {
console.log('Unexpected error:', error.message);
}
}
- Authentication/Authorization system
- Input validation with Bean Validation
- Pagination and sorting
- Unit and integration tests
- Health check endpoints
- Response caching
- Rate limiting
- Better error messages
- API versioning
- Batch operations
- Full-text search
- Image upload for products
- WebSocket notifications
- Export/import functionality
- Admin dashboard
- Service: Product Catalog Service
- Version: 0.0.1-SNAPSHOT
- Port: 8080
- Context Path: /api
- Documentation: Swagger UI available
- Database: PostgreSQL with Flyway migrations