Add user delete process
This commit is contained in:
326
scripts/functional_test_deletion.sh
Executable file
326
scripts/functional_test_deletion.sh
Executable file
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Functional Test: Tenant Deletion System
|
||||
# ============================================================================
|
||||
# Tests the complete tenant deletion workflow with service tokens
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/functional_test_deletion.sh <tenant_id>
|
||||
#
|
||||
# Example:
|
||||
# ./scripts/functional_test_deletion.sh dbc2128a-7539-470c-94b9-c1e37031bd77
|
||||
#
|
||||
# ============================================================================
|
||||
|
||||
set -e # Exit on error
|
||||
|
||||
# Require bash 4+ for associative arrays
|
||||
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
|
||||
echo "Error: This script requires bash 4.0 or higher"
|
||||
echo "Current version: ${BASH_VERSION}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
TENANT_ID="${1:-dbc2128a-7539-470c-94b9-c1e37031bd77}"
|
||||
SERVICE_TOKEN="${SERVICE_TOKEN:-eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZW5hbnQtZGVsZXRpb24tb3JjaGVzdHJhdG9yIiwidXNlcl9pZCI6InRlbmFudC1kZWxldGlvbi1vcmNoZXN0cmF0b3IiLCJzZXJ2aWNlIjoidGVuYW50LWRlbGV0aW9uLW9yY2hlc3RyYXRvciIsInR5cGUiOiJzZXJ2aWNlIiwiaXNfc2VydmljZSI6dHJ1ZSwicm9sZSI6ImFkbWluIiwiZW1haWwiOiJ0ZW5hbnQtZGVsZXRpb24tb3JjaGVzdHJhdG9yQGludGVybmFsLnNlcnZpY2UiLCJleHAiOjE3OTM0NDIwMzAsImlhdCI6MTc2MTkwNjAzMCwiaXNzIjoiYmFrZXJ5LWF1dGgifQ.I6mWLpkRim2fJ1v9WH24g4YT3-ZGbuFXxCorZxhPp6c}"
|
||||
|
||||
# Test mode (preview or delete)
|
||||
TEST_MODE="${2:-preview}" # preview or delete
|
||||
|
||||
# Service list with their endpoints
|
||||
declare -A SERVICES=(
|
||||
["orders"]="orders-service:8000"
|
||||
["inventory"]="inventory-service:8000"
|
||||
["recipes"]="recipes-service:8000"
|
||||
["sales"]="sales-service:8000"
|
||||
["production"]="production-service:8000"
|
||||
["suppliers"]="suppliers-service:8000"
|
||||
["pos"]="pos-service:8000"
|
||||
["external"]="city-service:8000"
|
||||
["forecasting"]="forecasting-service:8000"
|
||||
["training"]="training-service:8000"
|
||||
["alert-processor"]="alert-processor-service:8000"
|
||||
["notification"]="notification-service:8000"
|
||||
)
|
||||
|
||||
# Results tracking
|
||||
TOTAL_SERVICES=12
|
||||
SUCCESSFUL_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
declare -a FAILED_SERVICES
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
print_header() {
|
||||
echo -e "${BLUE}============================================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}============================================================================${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Test Functions
|
||||
# ============================================================================
|
||||
|
||||
test_service_preview() {
|
||||
local service_name=$1
|
||||
local service_host=$2
|
||||
local endpoint_path=$3
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Testing ${service_name}...${NC}"
|
||||
|
||||
# Get running pod
|
||||
local pod=$(kubectl get pods -n bakery-ia -l app=${service_name}-service 2>/dev/null | grep Running | head -1 | awk '{print $1}')
|
||||
|
||||
if [ -z "$pod" ]; then
|
||||
print_error "No running pod found for ${service_name}"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_info "Pod: ${pod}"
|
||||
|
||||
# Execute request inside pod
|
||||
local response=$(kubectl exec -n bakery-ia "$pod" -- curl -s -w "\n%{http_code}" \
|
||||
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
|
||||
"http://localhost:8000${endpoint_path}/tenant/${TENANT_ID}/deletion-preview" 2>&1)
|
||||
|
||||
local http_code=$(echo "$response" | tail -1)
|
||||
local body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "Preview successful (HTTP ${http_code})"
|
||||
|
||||
# Parse and display counts
|
||||
local total_records=$(echo "$body" | grep -o '"total_records":[0-9]*' | cut -d':' -f2 || echo "0")
|
||||
print_info "Records to delete: ${total_records}"
|
||||
|
||||
# Show breakdown if available
|
||||
echo "$body" | python3 -m json.tool 2>/dev/null | grep -A50 "breakdown" | head -20 || echo ""
|
||||
|
||||
SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1))
|
||||
return 0
|
||||
elif [ "$http_code" = "401" ]; then
|
||||
print_error "Authentication failed (HTTP ${http_code})"
|
||||
print_warning "Service token may be invalid or expired"
|
||||
echo "$body"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
elif [ "$http_code" = "403" ]; then
|
||||
print_error "Authorization failed (HTTP ${http_code})"
|
||||
print_warning "Service token not recognized as service"
|
||||
echo "$body"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
elif [ "$http_code" = "404" ]; then
|
||||
print_error "Endpoint not found (HTTP ${http_code})"
|
||||
print_warning "Deletion endpoint may not be implemented"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
elif [ "$http_code" = "500" ]; then
|
||||
print_error "Server error (HTTP ${http_code})"
|
||||
echo "$body" | head -5
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
else
|
||||
print_error "Unexpected response (HTTP ${http_code})"
|
||||
echo "$body" | head -5
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
test_service_deletion() {
|
||||
local service_name=$1
|
||||
local service_host=$2
|
||||
local endpoint_path=$3
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Deleting data in ${service_name}...${NC}"
|
||||
|
||||
# Get running pod
|
||||
local pod=$(kubectl get pods -n bakery-ia -l app=${service_name}-service 2>/dev/null | grep Running | head -1 | awk '{print $1}')
|
||||
|
||||
if [ -z "$pod" ]; then
|
||||
print_error "No running pod found for ${service_name}"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Execute deletion request inside pod
|
||||
local response=$(kubectl exec -n bakery-ia "$pod" -- curl -s -w "\n%{http_code}" \
|
||||
-X DELETE \
|
||||
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
|
||||
"http://localhost:8000${endpoint_path}/tenant/${TENANT_ID}" 2>&1)
|
||||
|
||||
local http_code=$(echo "$response" | tail -1)
|
||||
local body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "Deletion successful (HTTP ${http_code})"
|
||||
|
||||
# Parse and display deletion summary
|
||||
local total_deleted=$(echo "$body" | grep -o '"total_records_deleted":[0-9]*' | cut -d':' -f2 || echo "0")
|
||||
print_info "Records deleted: ${total_deleted}"
|
||||
|
||||
SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1))
|
||||
return 0
|
||||
else
|
||||
print_error "Deletion failed (HTTP ${http_code})"
|
||||
echo "$body" | head -5
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("${service_name}")
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Test Execution
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
print_header "Tenant Deletion System - Functional Test"
|
||||
|
||||
echo ""
|
||||
print_info "Tenant ID: ${TENANT_ID}"
|
||||
print_info "Test Mode: ${TEST_MODE}"
|
||||
print_info "Services to test: ${TOTAL_SERVICES}"
|
||||
echo ""
|
||||
|
||||
# Verify service token
|
||||
print_info "Verifying service token..."
|
||||
if python scripts/generate_service_token.py --verify "${SERVICE_TOKEN}" > /dev/null 2>&1; then
|
||||
print_success "Service token is valid"
|
||||
else
|
||||
print_error "Service token is invalid or expired"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_header "Phase 1: Testing Service Previews"
|
||||
|
||||
# Test each service preview
|
||||
test_service_preview "orders" "orders-service:8000" "/api/v1/orders"
|
||||
test_service_preview "inventory" "inventory-service:8000" "/api/v1/inventory"
|
||||
test_service_preview "recipes" "recipes-service:8000" "/api/v1/recipes"
|
||||
test_service_preview "sales" "sales-service:8000" "/api/v1/sales"
|
||||
test_service_preview "production" "production-service:8000" "/api/v1/production"
|
||||
test_service_preview "suppliers" "suppliers-service:8000" "/api/v1/suppliers"
|
||||
test_service_preview "pos" "pos-service:8000" "/api/v1/pos"
|
||||
test_service_preview "external" "city-service:8000" "/api/v1/nominatim"
|
||||
test_service_preview "forecasting" "forecasting-service:8000" "/api/v1/forecasting"
|
||||
test_service_preview "training" "training-service:8000" "/api/v1/training"
|
||||
test_service_preview "alert-processor" "alert-processor-service:8000" "/api/v1/analytics"
|
||||
test_service_preview "notification" "notification-service:8000" "/api/v1/notifications"
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
print_header "Preview Test Results"
|
||||
echo -e "Total Services: ${TOTAL_SERVICES}"
|
||||
echo -e "${GREEN}Successful:${NC} ${SUCCESSFUL_TESTS}/${TOTAL_SERVICES}"
|
||||
echo -e "${RED}Failed:${NC} ${FAILED_TESTS}/${TOTAL_SERVICES}"
|
||||
|
||||
if [ ${FAILED_TESTS} -gt 0 ]; then
|
||||
echo ""
|
||||
print_warning "Failed Services:"
|
||||
for service in "${FAILED_SERVICES[@]}"; do
|
||||
echo " - ${service}"
|
||||
done
|
||||
fi
|
||||
|
||||
# Ask for confirmation before actual deletion
|
||||
if [ "$TEST_MODE" = "delete" ]; then
|
||||
echo ""
|
||||
print_header "Phase 2: Actual Deletion"
|
||||
print_warning "This will PERMANENTLY delete data for tenant ${TENANT_ID}"
|
||||
print_warning "This operation is IRREVERSIBLE"
|
||||
echo ""
|
||||
read -p "Are you sure you want to proceed? (yes/no): " confirm
|
||||
|
||||
if [ "$confirm" != "yes" ]; then
|
||||
print_info "Deletion cancelled by user"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Reset counters
|
||||
SUCCESSFUL_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
FAILED_SERVICES=()
|
||||
|
||||
# Execute deletions
|
||||
test_service_deletion "orders" "orders-service:8000" "/api/v1/orders"
|
||||
test_service_deletion "inventory" "inventory-service:8000" "/api/v1/inventory"
|
||||
test_service_deletion "recipes" "recipes-service:8000" "/api/v1/recipes"
|
||||
test_service_deletion "sales" "sales-service:8000" "/api/v1/sales"
|
||||
test_service_deletion "production" "production-service:8000" "/api/v1/production"
|
||||
test_service_deletion "suppliers" "suppliers-service:8000" "/api/v1/suppliers"
|
||||
test_service_deletion "pos" "pos-service:8000" "/api/v1/pos"
|
||||
test_service_deletion "external" "city-service:8000" "/api/v1/nominatim"
|
||||
test_service_deletion "forecasting" "forecasting-service:8000" "/api/v1/forecasting"
|
||||
test_service_deletion "training" "training-service:8000" "/api/v1/training"
|
||||
test_service_deletion "alert-processor" "alert-processor-service:8000" "/api/v1/analytics"
|
||||
test_service_deletion "notification" "notification-service:8000" "/api/v1/notifications"
|
||||
|
||||
# Deletion summary
|
||||
echo ""
|
||||
print_header "Deletion Test Results"
|
||||
echo -e "Total Services: ${TOTAL_SERVICES}"
|
||||
echo -e "${GREEN}Successful:${NC} ${SUCCESSFUL_TESTS}/${TOTAL_SERVICES}"
|
||||
echo -e "${RED}Failed:${NC} ${FAILED_TESTS}/${TOTAL_SERVICES}"
|
||||
|
||||
if [ ${FAILED_TESTS} -gt 0 ]; then
|
||||
echo ""
|
||||
print_warning "Failed Services:"
|
||||
for service in "${FAILED_SERVICES[@]}"; do
|
||||
echo " - ${service}"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
print_header "Test Complete"
|
||||
|
||||
if [ ${FAILED_TESTS} -eq 0 ]; then
|
||||
print_success "All tests passed successfully!"
|
||||
exit 0
|
||||
else
|
||||
print_error "Some tests failed. See details above."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
137
scripts/functional_test_deletion_simple.sh
Executable file
137
scripts/functional_test_deletion_simple.sh
Executable file
@@ -0,0 +1,137 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# Functional Test: Tenant Deletion System (Simple Version)
|
||||
# ============================================================================
|
||||
|
||||
set +e # Don't exit on error
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
TENANT_ID="${1:-dbc2128a-7539-470c-94b9-c1e37031bd77}"
|
||||
SERVICE_TOKEN="${SERVICE_TOKEN}"
|
||||
|
||||
# Results
|
||||
TOTAL_SERVICES=12
|
||||
SUCCESSFUL_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}================================================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}================================================================================${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
# Test function
|
||||
test_service() {
|
||||
local service_name=$1
|
||||
local endpoint_path=$2
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Testing ${service_name}...${NC}"
|
||||
|
||||
# Find running pod
|
||||
local pod=$(kubectl get pods -n bakery-ia 2>/dev/null | grep "${service_name}" | grep "Running" | grep "1/1" | head -1 | awk '{print $1}')
|
||||
|
||||
if [ -z "$pod" ]; then
|
||||
print_error "No running pod found"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_info "Pod: ${pod}"
|
||||
|
||||
# Execute request
|
||||
local result=$(kubectl exec -n bakery-ia "$pod" -- curl -s -w "\nHTTP_CODE:%{http_code}" \
|
||||
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
|
||||
"http://localhost:8000${endpoint_path}/tenant/${TENANT_ID}/deletion-preview" 2>&1)
|
||||
|
||||
local http_code=$(echo "$result" | grep "HTTP_CODE" | cut -d':' -f2)
|
||||
local body=$(echo "$result" | sed '/HTTP_CODE/d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
print_success "Preview successful (HTTP ${http_code})"
|
||||
local total=$(echo "$body" | grep -o '"total_records":[0-9]*' | cut -d':' -f2 | head -1)
|
||||
if [ -n "$total" ]; then
|
||||
print_info "Records to delete: ${total}"
|
||||
fi
|
||||
SUCCESSFUL_TESTS=$((SUCCESSFUL_TESTS + 1))
|
||||
return 0
|
||||
elif [ "$http_code" = "401" ]; then
|
||||
print_error "Authentication failed (HTTP ${http_code})"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
elif [ "$http_code" = "403" ]; then
|
||||
print_error "Authorization failed (HTTP ${http_code})"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
elif [ "$http_code" = "404" ]; then
|
||||
print_error "Endpoint not found (HTTP ${http_code})"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
elif [ "$http_code" = "500" ]; then
|
||||
print_error "Server error (HTTP ${http_code})"
|
||||
echo "$body" | head -3
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
else
|
||||
print_error "Unexpected response (HTTP ${http_code})"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Main
|
||||
print_header "Tenant Deletion System - Functional Test"
|
||||
echo ""
|
||||
print_info "Tenant ID: ${TENANT_ID}"
|
||||
print_info "Services to test: ${TOTAL_SERVICES}"
|
||||
echo ""
|
||||
|
||||
# Test all services
|
||||
test_service "orders-service" "/api/v1/orders"
|
||||
test_service "inventory-service" "/api/v1/inventory"
|
||||
test_service "recipes-service" "/api/v1/recipes"
|
||||
test_service "sales-service" "/api/v1/sales"
|
||||
test_service "production-service" "/api/v1/production"
|
||||
test_service "suppliers-service" "/api/v1/suppliers"
|
||||
test_service "pos-service" "/api/v1/pos"
|
||||
test_service "city-service" "/api/v1/nominatim"
|
||||
test_service "forecasting-service" "/api/v1/forecasting"
|
||||
test_service "training-service" "/api/v1/training"
|
||||
test_service "alert-processor-service" "/api/v1/analytics"
|
||||
test_service "notification-service" "/api/v1/notifications"
|
||||
|
||||
# Summary
|
||||
echo ""
|
||||
print_header "Test Results"
|
||||
echo "Total Services: ${TOTAL_SERVICES}"
|
||||
echo -e "${GREEN}Successful:${NC} ${SUCCESSFUL_TESTS}/${TOTAL_SERVICES}"
|
||||
echo -e "${RED}Failed:${NC} ${FAILED_TESTS}/${TOTAL_SERVICES}"
|
||||
echo ""
|
||||
|
||||
if [ ${FAILED_TESTS} -eq 0 ]; then
|
||||
print_success "All tests passed!"
|
||||
exit 0
|
||||
else
|
||||
print_error "Some tests failed"
|
||||
exit 1
|
||||
fi
|
||||
270
scripts/generate_deletion_service.py
Normal file
270
scripts/generate_deletion_service.py
Normal file
@@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Quick script to generate deletion service boilerplate
|
||||
Usage: python generate_deletion_service.py <service_name> <model1,model2,model3>
|
||||
Example: python generate_deletion_service.py pos POSConfiguration,POSTransaction,POSSession
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def generate_deletion_service(service_name: str, models: list[str]):
|
||||
"""Generate deletion service file from template"""
|
||||
|
||||
service_class = f"{service_name.title().replace('_', '')}TenantDeletionService"
|
||||
model_imports = ", ".join(models)
|
||||
|
||||
# Build preview section
|
||||
preview_code = []
|
||||
delete_code = []
|
||||
|
||||
for i, model in enumerate(models):
|
||||
model_lower = model.lower().replace('_', ' ')
|
||||
model_plural = f"{model_lower}s" if not model_lower.endswith('s') else model_lower
|
||||
|
||||
preview_code.append(f"""
|
||||
# Count {model_plural}
|
||||
try:
|
||||
{model.lower()}_count = await self.db.scalar(
|
||||
select(func.count({model}.id)).where({model}.tenant_id == tenant_id)
|
||||
)
|
||||
preview["{model_plural}"] = {model.lower()}_count or 0
|
||||
except Exception:
|
||||
preview["{model_plural}"] = 0 # Table might not exist
|
||||
""")
|
||||
|
||||
delete_code.append(f"""
|
||||
# Delete {model_plural}
|
||||
try:
|
||||
{model.lower()}_delete = await self.db.execute(
|
||||
delete({model}).where({model}.tenant_id == tenant_id)
|
||||
)
|
||||
result.add_deleted_items("{model_plural}", {model.lower()}_delete.rowcount)
|
||||
|
||||
logger.info("Deleted {model_plural} for tenant",
|
||||
tenant_id=tenant_id,
|
||||
count={model.lower()}_delete.rowcount)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error deleting {model_plural}",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
result.add_error(f"{model} deletion: {{str(e)}}")
|
||||
""")
|
||||
|
||||
template = f'''"""
|
||||
{service_name.title()} Service - Tenant Data Deletion
|
||||
Handles deletion of all {service_name}-related data for a tenant
|
||||
"""
|
||||
from typing import Dict
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy import select, delete, func
|
||||
import structlog
|
||||
|
||||
from shared.services.tenant_deletion import BaseTenantDataDeletionService, TenantDataDeletionResult
|
||||
|
||||
logger = structlog.get_logger()
|
||||
|
||||
|
||||
class {service_class}(BaseTenantDataDeletionService):
|
||||
"""Service for deleting all {service_name}-related data for a tenant"""
|
||||
|
||||
def __init__(self, db_session: AsyncSession):
|
||||
super().__init__("{service_name}-service")
|
||||
self.db = db_session
|
||||
|
||||
async def get_tenant_data_preview(self, tenant_id: str) -> Dict[str, int]:
|
||||
"""Get counts of what would be deleted"""
|
||||
|
||||
try:
|
||||
preview = {{}}
|
||||
|
||||
# Import models here to avoid circular imports
|
||||
from app.models import {model_imports}
|
||||
{"".join(preview_code)}
|
||||
return preview
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Error getting deletion preview",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
return {{}}
|
||||
|
||||
async def delete_tenant_data(self, tenant_id: str) -> TenantDataDeletionResult:
|
||||
"""Delete all data for a tenant"""
|
||||
|
||||
result = TenantDataDeletionResult(tenant_id, self.service_name)
|
||||
|
||||
try:
|
||||
# Import models here to avoid circular imports
|
||||
from app.models import {model_imports}
|
||||
{"".join(delete_code)}
|
||||
# Commit all deletions
|
||||
await self.db.commit()
|
||||
|
||||
logger.info("Tenant data deletion completed",
|
||||
tenant_id=tenant_id,
|
||||
deleted_counts=result.deleted_counts)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Fatal error during tenant data deletion",
|
||||
tenant_id=tenant_id,
|
||||
error=str(e))
|
||||
await self.db.rollback()
|
||||
result.add_error(f"Fatal error: {{str(e)}}")
|
||||
|
||||
return result
|
||||
'''
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def generate_api_endpoints(service_name: str):
|
||||
"""Generate API endpoint code"""
|
||||
|
||||
service_class = f"{service_name.title().replace('_', '')}TenantDeletionService"
|
||||
|
||||
template = f'''
|
||||
# ===== Tenant Data Deletion Endpoints =====
|
||||
|
||||
@router.delete("/tenant/{{tenant_id}}")
|
||||
async def delete_tenant_data(
|
||||
tenant_id: str,
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Delete all {service_name}-related data for a tenant
|
||||
Only accessible by internal services (called during tenant deletion)
|
||||
"""
|
||||
|
||||
logger.info(f"Tenant data deletion request received for tenant: {{tenant_id}}")
|
||||
|
||||
# Only allow internal service calls
|
||||
if current_user.get("type") != "service":
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="This endpoint is only accessible to internal services"
|
||||
)
|
||||
|
||||
try:
|
||||
from app.services.tenant_deletion_service import {service_class}
|
||||
|
||||
deletion_service = {service_class}(db)
|
||||
result = await deletion_service.safe_delete_tenant_data(tenant_id)
|
||||
|
||||
return {{
|
||||
"message": "Tenant data deletion completed in {service_name}-service",
|
||||
"summary": result.to_dict()
|
||||
}}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Tenant data deletion failed for {{tenant_id}}: {{e}}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to delete tenant data: {{str(e)}}"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/tenant/{{tenant_id}}/deletion-preview")
|
||||
async def preview_tenant_data_deletion(
|
||||
tenant_id: str,
|
||||
current_user: dict = Depends(get_current_user_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Preview what data would be deleted for a tenant (dry-run)
|
||||
Accessible by internal services and tenant admins
|
||||
"""
|
||||
|
||||
# Allow internal services and admins
|
||||
is_service = current_user.get("type") == "service"
|
||||
is_admin = current_user.get("role") in ["owner", "admin"]
|
||||
|
||||
if not (is_service or is_admin):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail="Insufficient permissions"
|
||||
)
|
||||
|
||||
try:
|
||||
from app.services.tenant_deletion_service import {service_class}
|
||||
|
||||
deletion_service = {service_class}(db)
|
||||
preview = await deletion_service.get_tenant_data_preview(tenant_id)
|
||||
|
||||
return {{
|
||||
"tenant_id": tenant_id,
|
||||
"service": "{service_name}-service",
|
||||
"data_counts": preview,
|
||||
"total_items": sum(preview.values())
|
||||
}}
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Deletion preview failed for {{tenant_id}}: {{e}}")
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Failed to get deletion preview: {{str(e)}}"
|
||||
)
|
||||
'''
|
||||
|
||||
return template
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python generate_deletion_service.py <service_name> <model1,model2,model3>")
|
||||
print("Example: python generate_deletion_service.py pos POSConfiguration,POSTransaction,POSSession")
|
||||
sys.exit(1)
|
||||
|
||||
service_name = sys.argv[1]
|
||||
models = [m.strip() for m in sys.argv[2].split(',')]
|
||||
|
||||
# Generate service file
|
||||
service_code = generate_deletion_service(service_name, models)
|
||||
|
||||
# Generate API endpoints
|
||||
api_code = generate_api_endpoints(service_name)
|
||||
|
||||
# Output files
|
||||
service_dir = Path(f"services/{service_name}/app/services")
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"Generated code for {service_name} service with models: {', '.join(models)}")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
print("1. DELETION SERVICE FILE:")
|
||||
print(f" Location: {service_dir}/tenant_deletion_service.py")
|
||||
print("-" * 80)
|
||||
print(service_code)
|
||||
print()
|
||||
|
||||
print("\n2. API ENDPOINTS TO ADD:")
|
||||
print(f" Add to: services/{service_name}/app/api/<router>.py")
|
||||
print("-" * 80)
|
||||
print(api_code)
|
||||
print()
|
||||
|
||||
# Optionally write files
|
||||
write = input("\nWrite files to disk? (y/n): ").lower().strip()
|
||||
if write == 'y':
|
||||
# Create service file
|
||||
service_dir.mkdir(parents=True, exist_ok=True)
|
||||
service_file = service_dir / "tenant_deletion_service.py"
|
||||
|
||||
with open(service_file, 'w') as f:
|
||||
f.write(service_code)
|
||||
|
||||
print(f"\n✅ Created: {service_file}")
|
||||
print(f"\n⚠️ Next steps:")
|
||||
print(f" 1. Review and customize {service_file}")
|
||||
print(f" 2. Add the API endpoints to services/{service_name}/app/api/<router>.py")
|
||||
print(f" 3. Test with: curl -X GET 'http://localhost:8000/api/v1/{service_name}/tenant/{{id}}/deletion-preview'")
|
||||
else:
|
||||
print("\n✅ Files not written. Copy the code above manually.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
244
scripts/generate_service_token.py
Executable file
244
scripts/generate_service_token.py
Executable file
@@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate Service-to-Service Authentication Token
|
||||
|
||||
This script generates JWT tokens for service-to-service communication
|
||||
in the Bakery-IA tenant deletion system.
|
||||
|
||||
Usage:
|
||||
python scripts/generate_service_token.py <service_name> [--days DAYS]
|
||||
|
||||
Examples:
|
||||
# Generate token for orchestrator (1 year expiration)
|
||||
python scripts/generate_service_token.py tenant-deletion-orchestrator
|
||||
|
||||
# Generate token for specific service with custom expiration
|
||||
python scripts/generate_service_token.py auth-service --days 90
|
||||
|
||||
# Generate tokens for all services
|
||||
python scripts/generate_service_token.py --all
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import argparse
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root to path
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
from shared.auth.jwt_handler import JWTHandler
|
||||
|
||||
# Get JWT secret from environment (same as services use)
|
||||
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "your-secret-key-change-in-production-min-32-chars")
|
||||
|
||||
# Service names used in the system
|
||||
SERVICES = [
|
||||
"tenant-deletion-orchestrator",
|
||||
"auth-service",
|
||||
"tenant-service",
|
||||
"orders-service",
|
||||
"inventory-service",
|
||||
"recipes-service",
|
||||
"sales-service",
|
||||
"production-service",
|
||||
"suppliers-service",
|
||||
"pos-service",
|
||||
"external-service",
|
||||
"forecasting-service",
|
||||
"training-service",
|
||||
"alert-processor-service",
|
||||
"notification-service"
|
||||
]
|
||||
|
||||
|
||||
def generate_token(service_name: str, days: int = 365) -> str:
|
||||
"""
|
||||
Generate a service token
|
||||
|
||||
Args:
|
||||
service_name: Name of the service
|
||||
days: Token expiration in days (default: 365)
|
||||
|
||||
Returns:
|
||||
JWT service token
|
||||
"""
|
||||
jwt_handler = JWTHandler(
|
||||
secret_key=JWT_SECRET_KEY,
|
||||
algorithm="HS256"
|
||||
)
|
||||
|
||||
token = jwt_handler.create_service_token(
|
||||
service_name=service_name,
|
||||
expires_delta=timedelta(days=days)
|
||||
)
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def verify_token(token: str) -> dict:
|
||||
"""
|
||||
Verify a service token and return its payload
|
||||
|
||||
Args:
|
||||
token: JWT token to verify
|
||||
|
||||
Returns:
|
||||
Token payload dictionary
|
||||
"""
|
||||
jwt_handler = JWTHandler(
|
||||
secret_key=JWT_SECRET_KEY,
|
||||
algorithm="HS256"
|
||||
)
|
||||
|
||||
payload = jwt_handler.verify_token(token)
|
||||
if not payload:
|
||||
raise ValueError("Invalid or expired token")
|
||||
|
||||
return payload
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate service-to-service authentication tokens",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
# Generate token for orchestrator
|
||||
%(prog)s tenant-deletion-orchestrator
|
||||
|
||||
# Generate token with custom expiration
|
||||
%(prog)s auth-service --days 90
|
||||
|
||||
# Generate tokens for all services
|
||||
%(prog)s --all
|
||||
|
||||
# Verify a token
|
||||
%(prog)s --verify <token>
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"service_name",
|
||||
nargs="?",
|
||||
help="Name of the service (e.g., 'tenant-deletion-orchestrator')"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--days",
|
||||
type=int,
|
||||
default=365,
|
||||
help="Token expiration in days (default: 365)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--all",
|
||||
action="store_true",
|
||||
help="Generate tokens for all services"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--verify",
|
||||
metavar="TOKEN",
|
||||
help="Verify a token and show its payload"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--list-services",
|
||||
action="store_true",
|
||||
help="List all available service names"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# List services
|
||||
if args.list_services:
|
||||
print("\nAvailable Services:")
|
||||
print("=" * 50)
|
||||
for service in SERVICES:
|
||||
print(f" - {service}")
|
||||
print()
|
||||
return 0
|
||||
|
||||
# Verify token
|
||||
if args.verify:
|
||||
try:
|
||||
payload = verify_token(args.verify)
|
||||
print("\n✓ Token is valid!")
|
||||
print("=" * 50)
|
||||
print(f"Service Name: {payload.get('service')}")
|
||||
print(f"Type: {payload.get('type')}")
|
||||
print(f"Is Service: {payload.get('is_service')}")
|
||||
print(f"Role: {payload.get('role')}")
|
||||
print(f"Issued At: {payload.get('iat')}")
|
||||
print(f"Expires At: {payload.get('exp')}")
|
||||
print("=" * 50)
|
||||
print()
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"\n✗ Token verification failed: {e}\n")
|
||||
return 1
|
||||
|
||||
# Generate for all services
|
||||
if args.all:
|
||||
print(f"\nGenerating service tokens (expires in {args.days} days)...")
|
||||
print("=" * 80)
|
||||
|
||||
for service in SERVICES:
|
||||
try:
|
||||
token = generate_token(service, args.days)
|
||||
print(f"\n{service}:")
|
||||
print(f" export {service.upper().replace('-', '_')}_TOKEN='{token}'")
|
||||
except Exception as e:
|
||||
print(f"\n✗ Failed to generate token for {service}: {e}")
|
||||
|
||||
print("\n" + "=" * 80)
|
||||
print("\nℹ Copy the export statements above to set environment variables")
|
||||
print("ℹ Or save them to a .env file for your services\n")
|
||||
return 0
|
||||
|
||||
# Generate for single service
|
||||
if not args.service_name:
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
try:
|
||||
print(f"\nGenerating service token for: {args.service_name}")
|
||||
print(f"Expiration: {args.days} days")
|
||||
print("=" * 80)
|
||||
|
||||
token = generate_token(args.service_name, args.days)
|
||||
|
||||
print("\n✓ Token generated successfully!\n")
|
||||
print("Token:")
|
||||
print(f" {token}")
|
||||
print()
|
||||
print("Environment Variable:")
|
||||
env_var = args.service_name.upper().replace('-', '_') + '_TOKEN'
|
||||
print(f" export {env_var}='{token}'")
|
||||
print()
|
||||
print("Usage in Code:")
|
||||
print(f" headers = {{'Authorization': f'Bearer {{os.getenv(\"{env_var}\")}}'}}")
|
||||
print()
|
||||
print("Test with curl:")
|
||||
print(f" curl -H 'Authorization: Bearer {token}' https://localhost/api/v1/...")
|
||||
print()
|
||||
print("=" * 80)
|
||||
print()
|
||||
|
||||
# Verify the token we just created
|
||||
print("Verifying token...")
|
||||
payload = verify_token(token)
|
||||
print("✓ Token is valid and verified!\n")
|
||||
|
||||
return 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ Error: {e}\n")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
78
scripts/quick_test_deletion.sh
Executable file
78
scripts/quick_test_deletion.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
# Quick test script for deletion endpoints via localhost (port-forwarded or ingress)
|
||||
# This tests with the real Bakery-IA demo tenant
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Demo tenant from the system
|
||||
TENANT_ID="dbc2128a-7539-470c-94b9-c1e37031bd77"
|
||||
DEMO_SESSION_ID="demo_8rkT9JjXWFuVmdqT798Nyg"
|
||||
|
||||
# Base URL (through ingress or port-forward)
|
||||
BASE_URL="${BASE_URL:-https://localhost}"
|
||||
|
||||
echo -e "${BLUE}Testing Deletion System with Real Services${NC}"
|
||||
echo -e "${BLUE}===========================================${NC}"
|
||||
echo ""
|
||||
echo -e "Tenant ID: ${YELLOW}$TENANT_ID${NC}"
|
||||
echo -e "Base URL: ${YELLOW}$BASE_URL${NC}"
|
||||
echo ""
|
||||
|
||||
# Test function
|
||||
test_service() {
|
||||
local service_name=$1
|
||||
local endpoint=$2
|
||||
|
||||
echo -n "Testing $service_name... "
|
||||
|
||||
# Try to access the deletion preview endpoint
|
||||
response=$(curl -k -s -w "\n%{http_code}" \
|
||||
-H "X-Demo-Session-Id: $DEMO_SESSION_ID" \
|
||||
-H "X-Tenant-ID: $TENANT_ID" \
|
||||
"$BASE_URL$endpoint/tenant/$TENANT_ID/deletion-preview" 2>&1)
|
||||
|
||||
http_code=$(echo "$response" | tail -1)
|
||||
body=$(echo "$response" | sed '$d')
|
||||
|
||||
if [ "$http_code" = "200" ]; then
|
||||
# Try to parse total records
|
||||
total=$(echo "$body" | grep -o '"total_records":[0-9]*' | cut -d':' -f2 || echo "?")
|
||||
echo -e "${GREEN}✓${NC} (HTTP $http_code, Records: $total)"
|
||||
elif [ "$http_code" = "401" ] || [ "$http_code" = "403" ]; then
|
||||
echo -e "${YELLOW}⚠${NC} (HTTP $http_code - Auth required)"
|
||||
elif [ "$http_code" = "404" ]; then
|
||||
echo -e "${RED}✗${NC} (HTTP $http_code - Endpoint not found)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} (HTTP $http_code)"
|
||||
fi
|
||||
}
|
||||
|
||||
# Test all services
|
||||
echo "Testing deletion preview endpoints:"
|
||||
echo ""
|
||||
|
||||
test_service "Orders" "/api/v1/orders"
|
||||
test_service "Inventory" "/api/v1/inventory"
|
||||
test_service "Recipes" "/api/v1/recipes"
|
||||
test_service "Sales" "/api/v1/sales"
|
||||
test_service "Production" "/api/v1/production"
|
||||
test_service "Suppliers" "/api/v1/suppliers"
|
||||
test_service "POS" "/api/v1/pos"
|
||||
test_service "External" "/api/v1/external"
|
||||
test_service "Forecasting" "/api/v1/forecasting"
|
||||
test_service "Training" "/api/v1/training"
|
||||
test_service "Alert Processor" "/api/v1/alerts"
|
||||
test_service "Notification" "/api/v1/notifications"
|
||||
|
||||
echo ""
|
||||
echo -e "${BLUE}Test completed!${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Note:${NC} 401/403 responses are expected - deletion endpoints require service tokens"
|
||||
echo -e "${YELLOW}Note:${NC} To test with proper auth, set up service-to-service authentication"
|
||||
140
scripts/test_deletion_endpoints.sh
Executable file
140
scripts/test_deletion_endpoints.sh
Executable file
@@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
# Quick script to test all deletion endpoints
|
||||
# Usage: ./test_deletion_endpoints.sh <tenant_id>
|
||||
|
||||
set -e
|
||||
|
||||
TENANT_ID=${1:-"test-tenant-123"}
|
||||
BASE_URL=${BASE_URL:-"http://localhost:8000"}
|
||||
TOKEN=${AUTH_TOKEN:-"test-token"}
|
||||
|
||||
echo "================================"
|
||||
echo "Testing Deletion Endpoints"
|
||||
echo "Tenant ID: $TENANT_ID"
|
||||
echo "Base URL: $BASE_URL"
|
||||
echo "================================"
|
||||
echo ""
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Function to test endpoint
|
||||
test_endpoint() {
|
||||
local service=$1
|
||||
local method=$2
|
||||
local path=$3
|
||||
local expected_status=${4:-200}
|
||||
|
||||
echo -n "Testing $service ($method $path)... "
|
||||
|
||||
response=$(curl -s -w "\n%{http_code}" \
|
||||
-X $method \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-H "X-Internal-Service: test-script" \
|
||||
"$BASE_URL/api/v1/$path" 2>&1)
|
||||
|
||||
status_code=$(echo "$response" | tail -n1)
|
||||
body=$(echo "$response" | head -n-1)
|
||||
|
||||
if [ "$status_code" == "$expected_status" ] || [ "$status_code" == "404" ]; then
|
||||
if [ "$status_code" == "404" ]; then
|
||||
echo -e "${YELLOW}NOT IMPLEMENTED${NC} (404)"
|
||||
else
|
||||
echo -e "${GREEN}✓ PASSED${NC} ($status_code)"
|
||||
if [ "$method" == "GET" ]; then
|
||||
# Show preview counts
|
||||
total=$(echo "$body" | jq -r '.total_items // 0' 2>/dev/null || echo "N/A")
|
||||
if [ "$total" != "N/A" ]; then
|
||||
echo " → Preview: $total items would be deleted"
|
||||
fi
|
||||
elif [ "$method" == "DELETE" ]; then
|
||||
# Show deletion summary
|
||||
deleted=$(echo "$body" | jq -r '.summary.total_deleted // 0' 2>/dev/null || echo "N/A")
|
||||
if [ "$deleted" != "N/A" ]; then
|
||||
echo " → Deleted: $deleted items"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ FAILED${NC} ($status_code)"
|
||||
echo " Response: $body"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== COMPLETED SERVICES ==="
|
||||
echo ""
|
||||
|
||||
echo "1. Tenant Service:"
|
||||
test_endpoint "tenant" "GET" "tenants/$TENANT_ID"
|
||||
test_endpoint "tenant" "DELETE" "tenants/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "2. Orders Service:"
|
||||
test_endpoint "orders" "GET" "orders/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "orders" "DELETE" "orders/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "3. Inventory Service:"
|
||||
test_endpoint "inventory" "GET" "inventory/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "inventory" "DELETE" "inventory/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "4. Recipes Service:"
|
||||
test_endpoint "recipes" "GET" "recipes/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "recipes" "DELETE" "recipes/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "5. Sales Service:"
|
||||
test_endpoint "sales" "GET" "sales/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "sales" "DELETE" "sales/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "6. Production Service:"
|
||||
test_endpoint "production" "GET" "production/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "production" "DELETE" "production/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "7. Suppliers Service:"
|
||||
test_endpoint "suppliers" "GET" "suppliers/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "suppliers" "DELETE" "suppliers/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "=== PENDING SERVICES ==="
|
||||
echo ""
|
||||
|
||||
echo "8. POS Service:"
|
||||
test_endpoint "pos" "GET" "pos/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "pos" "DELETE" "pos/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "9. External Service:"
|
||||
test_endpoint "external" "GET" "external/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "external" "DELETE" "external/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "10. Alert Processor Service:"
|
||||
test_endpoint "alert_processor" "GET" "alerts/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "alert_processor" "DELETE" "alerts/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "11. Forecasting Service:"
|
||||
test_endpoint "forecasting" "GET" "forecasts/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "forecasting" "DELETE" "forecasts/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "12. Training Service:"
|
||||
test_endpoint "training" "GET" "models/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "training" "DELETE" "models/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "13. Notification Service:"
|
||||
test_endpoint "notification" "GET" "notifications/tenant/$TENANT_ID/deletion-preview"
|
||||
test_endpoint "notification" "DELETE" "notifications/tenant/$TENANT_ID"
|
||||
echo ""
|
||||
|
||||
echo "================================"
|
||||
echo "Testing Complete!"
|
||||
echo "================================"
|
||||
225
scripts/test_deletion_system.sh
Executable file
225
scripts/test_deletion_system.sh
Executable file
@@ -0,0 +1,225 @@
|
||||
#!/bin/bash
|
||||
# ================================================================
|
||||
# Tenant Deletion System - Integration Test Script
|
||||
# ================================================================
|
||||
# Tests all 12 services' deletion endpoints
|
||||
# Usage: ./scripts/test_deletion_system.sh [tenant_id]
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
TENANT_ID="${1:-dbc2128a-7539-470c-94b9-c1e37031bd77}" # Default demo tenant
|
||||
SERVICE_TOKEN="${SERVICE_TOKEN:-demo_service_token}"
|
||||
|
||||
# Service URLs (update these based on your environment)
|
||||
ORDERS_URL="${ORDERS_URL:-http://localhost:8000/api/v1/orders}"
|
||||
INVENTORY_URL="${INVENTORY_URL:-http://localhost:8001/api/v1/inventory}"
|
||||
RECIPES_URL="${RECIPES_URL:-http://localhost:8002/api/v1/recipes}"
|
||||
SALES_URL="${SALES_URL:-http://localhost:8003/api/v1/sales}"
|
||||
PRODUCTION_URL="${PRODUCTION_URL:-http://localhost:8004/api/v1/production}"
|
||||
SUPPLIERS_URL="${SUPPLIERS_URL:-http://localhost:8005/api/v1/suppliers}"
|
||||
POS_URL="${POS_URL:-http://localhost:8006/api/v1/pos}"
|
||||
EXTERNAL_URL="${EXTERNAL_URL:-http://localhost:8007/api/v1/external}"
|
||||
FORECASTING_URL="${FORECASTING_URL:-http://localhost:8008/api/v1/forecasting}"
|
||||
TRAINING_URL="${TRAINING_URL:-http://localhost:8009/api/v1/training}"
|
||||
ALERT_PROCESSOR_URL="${ALERT_PROCESSOR_URL:-http://localhost:8010/api/v1/alerts}"
|
||||
NOTIFICATION_URL="${NOTIFICATION_URL:-http://localhost:8011/api/v1/notifications}"
|
||||
|
||||
# Test results
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
declare -a FAILED_SERVICES
|
||||
|
||||
# Helper functions
|
||||
print_header() {
|
||||
echo -e "${BLUE}================================================${NC}"
|
||||
echo -e "${BLUE}$1${NC}"
|
||||
echo -e "${BLUE}================================================${NC}"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓${NC} $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗${NC} $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠${NC} $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ${NC} $1"
|
||||
}
|
||||
|
||||
# Test individual service deletion preview
|
||||
test_service_preview() {
|
||||
local service_name=$1
|
||||
local service_url=$2
|
||||
local endpoint_path=$3
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
echo ""
|
||||
print_info "Testing $service_name service..."
|
||||
|
||||
local full_url="${service_url}${endpoint_path}/tenant/${TENANT_ID}/deletion-preview"
|
||||
|
||||
# Make request
|
||||
response=$(curl -k -s -w "\nHTTP_STATUS:%{http_code}" \
|
||||
-H "Authorization: Bearer ${SERVICE_TOKEN}" \
|
||||
-H "X-Service-Token: ${SERVICE_TOKEN}" \
|
||||
"${full_url}" 2>&1)
|
||||
|
||||
# Extract HTTP status
|
||||
http_status=$(echo "$response" | grep "HTTP_STATUS" | cut -d':' -f2)
|
||||
body=$(echo "$response" | sed '/HTTP_STATUS/d')
|
||||
|
||||
if [ "$http_status" = "200" ]; then
|
||||
# Parse total records if available
|
||||
total_records=$(echo "$body" | grep -o '"total_records":[0-9]*' | cut -d':' -f2 || echo "N/A")
|
||||
|
||||
print_success "$service_name: HTTP $http_status (Records: $total_records)"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
|
||||
# Show preview details if verbose
|
||||
if [ "${VERBOSE:-0}" = "1" ]; then
|
||||
echo "$body" | jq '.' 2>/dev/null || echo "$body"
|
||||
fi
|
||||
else
|
||||
print_error "$service_name: HTTP $http_status"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
FAILED_SERVICES+=("$service_name")
|
||||
|
||||
# Show error details
|
||||
echo " URL: $full_url"
|
||||
echo " Response: $body" | head -n 5
|
||||
fi
|
||||
}
|
||||
|
||||
# Main test execution
|
||||
main() {
|
||||
print_header "Tenant Deletion System - Integration Tests"
|
||||
print_info "Testing tenant: $TENANT_ID"
|
||||
print_info "Using service token: ${SERVICE_TOKEN:0:20}..."
|
||||
echo ""
|
||||
|
||||
# Test all services
|
||||
print_header "Testing Individual Services (12 total)"
|
||||
|
||||
test_service_preview "Orders" "$ORDERS_URL" "/orders"
|
||||
test_service_preview "Inventory" "$INVENTORY_URL" "/inventory"
|
||||
test_service_preview "Recipes" "$RECIPES_URL" "/recipes"
|
||||
test_service_preview "Sales" "$SALES_URL" "/sales"
|
||||
test_service_preview "Production" "$PRODUCTION_URL" "/production"
|
||||
test_service_preview "Suppliers" "$SUPPLIERS_URL" "/suppliers"
|
||||
test_service_preview "POS" "$POS_URL" "/pos"
|
||||
test_service_preview "External" "$EXTERNAL_URL" "/external"
|
||||
test_service_preview "Forecasting" "$FORECASTING_URL" "/forecasting"
|
||||
test_service_preview "Training" "$TRAINING_URL" "/training"
|
||||
test_service_preview "Alert Processor" "$ALERT_PROCESSOR_URL" "/alerts"
|
||||
test_service_preview "Notification" "$NOTIFICATION_URL" "/notifications"
|
||||
|
||||
# Print summary
|
||||
echo ""
|
||||
print_header "Test Summary"
|
||||
echo -e "Total Tests: $TOTAL_TESTS"
|
||||
echo -e "${GREEN}Passed: $PASSED_TESTS${NC}"
|
||||
|
||||
if [ $FAILED_TESTS -gt 0 ]; then
|
||||
echo -e "${RED}Failed: $FAILED_TESTS${NC}"
|
||||
echo ""
|
||||
print_error "Failed services:"
|
||||
for service in "${FAILED_SERVICES[@]}"; do
|
||||
echo " - $service"
|
||||
done
|
||||
echo ""
|
||||
print_warning "Some services are not accessible or not implemented."
|
||||
print_info "Make sure all services are running and URLs are correct."
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}Failed: $FAILED_TESTS${NC}"
|
||||
echo ""
|
||||
print_success "All services passed! ✨"
|
||||
exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies() {
|
||||
if ! command -v curl &> /dev/null; then
|
||||
print_error "curl is required but not installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
print_warning "jq not found. Install for better output formatting."
|
||||
fi
|
||||
}
|
||||
|
||||
# Show usage
|
||||
show_usage() {
|
||||
cat << EOF
|
||||
Usage: $0 [OPTIONS] [tenant_id]
|
||||
|
||||
Test the tenant deletion system across all 12 microservices.
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
-v, --verbose Show detailed response bodies
|
||||
-t, --tenant ID Specify tenant ID to test (default: demo tenant)
|
||||
|
||||
Environment Variables:
|
||||
SERVICE_TOKEN Service authentication token
|
||||
*_URL Individual service URLs (e.g., ORDERS_URL)
|
||||
|
||||
Examples:
|
||||
# Test with default demo tenant
|
||||
$0
|
||||
|
||||
# Test specific tenant
|
||||
$0 abc-123-def-456
|
||||
|
||||
# Test with verbose output
|
||||
VERBOSE=1 $0
|
||||
|
||||
# Test with custom service URLs
|
||||
ORDERS_URL=http://orders:8000/api/v1/orders $0
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_usage
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=1
|
||||
shift
|
||||
;;
|
||||
-t|--tenant)
|
||||
TENANT_ID="$2"
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
TENANT_ID="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Run tests
|
||||
check_dependencies
|
||||
main
|
||||
Reference in New Issue
Block a user