new api service and logic implemented
This commit is contained in:
203
docs/architecture/system_architecture.md
Normal file
203
docs/architecture/system_architecture.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# System Architecture
|
||||
|
||||
## Core Services
|
||||
|
||||
### Top-Level Services
|
||||
1. **AuthServiceApi**
|
||||
- User authentication and authorization
|
||||
- Token management
|
||||
- Permission handling
|
||||
|
||||
2. **EventServiceApi**
|
||||
- Event processing and management
|
||||
- Event routing and handling
|
||||
- Event validation
|
||||
|
||||
3. **ValidationServiceApi**
|
||||
- Input validation
|
||||
- Data verification
|
||||
- Schema validation
|
||||
|
||||
## System Components
|
||||
|
||||
### AllConfigs
|
||||
Configuration management for various system components.
|
||||
|
||||
| Category | Context | Dependencies |
|
||||
|----------|----------|--------------|
|
||||
| Email | configs, email_send_model | - |
|
||||
| NoSqlDatabase | configs | - |
|
||||
| Redis | configs | - |
|
||||
| SqlDatabase | configs | - |
|
||||
| Token | configs | - |
|
||||
|
||||
### Schemas
|
||||
- SQL Alchemy schema definitions
|
||||
- Data models and relationships
|
||||
- Database structure definitions
|
||||
|
||||
### ApiLibrary
|
||||
|
||||
| Category | Description |
|
||||
|----------|-------------|
|
||||
| common | Error line number tracking |
|
||||
| date_time_actions | DateTime handling functions |
|
||||
| extensions | Password module and utilities |
|
||||
|
||||
### ApiServices
|
||||
|
||||
| Category | Context | Dependencies |
|
||||
|----------|----------|--------------|
|
||||
| Login | UserLoginModule | ApiLibrary, Schemas, ErrorHandlers, ApiValidations, ApiServices |
|
||||
| Token | TokenService | Services, Schemas, ApiLibrary, ErrorHandlers, AllConfigs, ApiValidations |
|
||||
|
||||
### Services
|
||||
|
||||
| Category | Dependencies |
|
||||
|----------|--------------|
|
||||
| Email | ApiLibrary, Schemas, ErrorHandlers, ApiValidations, ApiServices |
|
||||
| MongoDb | - |
|
||||
| PostgresDb | - |
|
||||
| Redis | - |
|
||||
|
||||
### ErrorHandlers
|
||||
- ErrorHandlers: General error handling
|
||||
- Exceptions: Custom exception definitions
|
||||
|
||||
### LanguageModels
|
||||
- Database: Database-related language models
|
||||
- Errors: Error message translations
|
||||
|
||||
### ApiValidations
|
||||
- Custom: Custom validation rules
|
||||
- Request: Request validation logic
|
||||
|
||||
## Testing Framework
|
||||
|
||||
### Test Categories
|
||||
- AlchemyResponse pagination testing
|
||||
- Redis function testing
|
||||
- MongoDB function testing
|
||||
- Validation testing
|
||||
- Header testing
|
||||
- Auth function testing
|
||||
- Language testing
|
||||
- Property definition testing
|
||||
- SmartQuery testing
|
||||
|
||||
### Error Categories
|
||||
- AlchemyError
|
||||
- ApiError
|
||||
- RedisError
|
||||
- MongoError
|
||||
- EmailError
|
||||
- Validation[Pydantic]
|
||||
|
||||
## Alchemy Implementation Phases
|
||||
|
||||
1. **BaseAlchemyNeed**
|
||||
- Session management
|
||||
- Core functionality
|
||||
|
||||
2. **PlainModel**
|
||||
- Basic model implementation
|
||||
|
||||
3. **FilteredModel**
|
||||
- Filter functionality
|
||||
|
||||
4. **PaginatedModel**
|
||||
- Pagination attributes
|
||||
- Filter integration
|
||||
|
||||
5. **LanguageModel**
|
||||
- Function retrieval
|
||||
- Header management
|
||||
|
||||
6. **ResponseModel**
|
||||
- Plain AlchemyClass
|
||||
- Dictionary conversion
|
||||
- Multiple response handling
|
||||
|
||||
## System Layers
|
||||
|
||||
1. **DependenciesLayer**
|
||||
- External dependencies
|
||||
- System requirements
|
||||
|
||||
2. **ApplicationLayer**
|
||||
- Core application logic
|
||||
- Business rules
|
||||
|
||||
3. **ServiceLayer**
|
||||
- Service implementations
|
||||
- API endpoints
|
||||
|
||||
4. **TestLayer**
|
||||
- Test suites
|
||||
- Test utilities
|
||||
|
||||
5. **DevLayer**
|
||||
- Development tools
|
||||
- Debug utilities
|
||||
|
||||
6. **RootLayer**
|
||||
- Main directory
|
||||
- Configuration files
|
||||
- Documentation
|
||||
|
||||
## TODO Items
|
||||
|
||||
1. **Event Directory Structure**
|
||||
- Move to ApiEvents
|
||||
- Docker file integration
|
||||
- Import organization
|
||||
|
||||
2. **MethodToEvent Renewal**
|
||||
- Update implementation
|
||||
- Improve flexibility
|
||||
|
||||
3. **Endpoint Configuration**
|
||||
- Remove unnecessary fields
|
||||
- Streamline configuration
|
||||
|
||||
4. **Middleware Organization**
|
||||
- Consolidate into /TokenEventMiddleware/
|
||||
- Standardize naming
|
||||
|
||||
5. **Code Cleanup**
|
||||
- Remove ActionsSchemaFactory
|
||||
- Remove ActionsSchema
|
||||
- Move endpoint_wrapper to Middleware.wrappers
|
||||
|
||||
6. **Function Organization**
|
||||
- Support sync/async functions
|
||||
- Improve API function organization
|
||||
|
||||
7. **Directory Structure**
|
||||
- Consolidate AllConfigs, ApiLibrary, ErrorHandlers
|
||||
- Move to RootLayer
|
||||
|
||||
8. **Configuration Management**
|
||||
- Update RouteFactoryConfig
|
||||
- Update EndpointFactoryConfig
|
||||
- Implement event validation interface
|
||||
|
||||
9. **Language Model**
|
||||
- Review Schemas.__language_model__
|
||||
- Update implementation
|
||||
|
||||
10. **Service Container**
|
||||
- Review ApiServices
|
||||
- Plan container migration
|
||||
|
||||
11. **Language Defaults**
|
||||
- Add to config
|
||||
- Implement ["tr", "en"] as default
|
||||
|
||||
## Notes
|
||||
|
||||
- Redis implementation needs RedisRow class
|
||||
- Event validation needs database integration
|
||||
- Consider containerization for ApiServices
|
||||
- Review language model implementation
|
||||
- Test coverage needs improvement
|
||||
135
docs/events/ReadMe.md
Normal file
135
docs/events/ReadMe.md
Normal file
@@ -0,0 +1,135 @@
|
||||
# System Architecture Documentation
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. ClusterToMethod (Router)
|
||||
API router that groups related endpoints into logical clusters.
|
||||
|
||||
**Key Components:**
|
||||
- `TAGS`: List of router categorization tags
|
||||
- `PREFIX`: Base URL prefix for all contained endpoints
|
||||
- `PAGEINFO`: Page metadata and UI information
|
||||
- `ENDPOINTS`: Collection of MethodEvent instances
|
||||
- `SUBCATEGORY`: Nested ClusterToMethod instances for hierarchical routing
|
||||
|
||||
### 2. MethodToEvent (Endpoint Handler)
|
||||
Handles individual API endpoints and their event mappings.
|
||||
|
||||
**Key Components:**
|
||||
- `EVENTS`: Collection of Event instances
|
||||
- `HEADER_LANGUAGE_MODELS`: Header localization
|
||||
- `ERROR_LANGUAGE_MODELS`: Error message localization
|
||||
- Endpoint metadata (URL, method, summary, description)
|
||||
- Endpoint callable with request validation
|
||||
|
||||
### 3. Event (Business Logic)
|
||||
Represents individual business operations with validation.
|
||||
|
||||
**Core Features:**
|
||||
- Unique `KEY_` identifier
|
||||
- Request/Response validation using PydanticModel
|
||||
- Callable business logic function
|
||||
- Language model integration
|
||||
|
||||
## Data Flow & Storage
|
||||
|
||||
### Redis Key Structure
|
||||
```
|
||||
CLUSTER_FUNCTION_CODES:{ClusterToMethod}
|
||||
└── {PageInfo, [FunctionCodes]}
|
||||
|
||||
METHOD_FUNCTION_CODES:{ClusterToMethod}:MethodEvent:Endpoint
|
||||
└── [FunctionCodes]
|
||||
|
||||
LANGUAGE_MODELS:*
|
||||
└── Localization data
|
||||
|
||||
PAGE_MAPPER:{Type}:{BuildingID}:{UserID}
|
||||
└── PageInfo
|
||||
|
||||
MENU_MAPPER:{Type}:{BuildingID}:{UserID}
|
||||
└── PageInfo
|
||||
```
|
||||
|
||||
### Application Initialization Flow
|
||||
1. **Pages Iteration**
|
||||
- Saves router/endpoint mappings
|
||||
- Caches menu structure
|
||||
|
||||
2. **Events Iteration**
|
||||
- Stores endpoint information
|
||||
- Caches validation schemas
|
||||
|
||||
3. **Web Statics**
|
||||
- Caches localization data
|
||||
- Builds UI components
|
||||
|
||||
### Request Flow
|
||||
```
|
||||
Request → Router(ClusterToMethod) → Endpoint(MethodEvent) → Event Handler
|
||||
↓
|
||||
Validation
|
||||
↓
|
||||
Business Logic
|
||||
```
|
||||
|
||||
## Core Services
|
||||
|
||||
### 1. ValidationService
|
||||
- Model validation handling
|
||||
- Schema caching
|
||||
- Language-specific validation
|
||||
- Redis-first validation lookup
|
||||
|
||||
### 2. EventService
|
||||
- Event routing management
|
||||
- Function code mapping
|
||||
- User-specific event access
|
||||
- Login state management
|
||||
|
||||
### 3. AuthService
|
||||
- User authentication
|
||||
- Event access control
|
||||
- User preferences (timezone, language)
|
||||
- Token management
|
||||
|
||||
## Design Patterns
|
||||
|
||||
### 1. Multi-layer Validation
|
||||
- Language model validation
|
||||
- Function code validation
|
||||
- User access validation
|
||||
- Request/Response schema validation
|
||||
|
||||
### 2. Hierarchical Routing
|
||||
- ClusterToMethod → MethodEvent → Event
|
||||
- Nested routing via SUBCATEGORY
|
||||
- URL prefix inheritance
|
||||
|
||||
### 3. Internationalization
|
||||
- Comprehensive language support
|
||||
- Cached translations
|
||||
- Header and error localization
|
||||
- Per-user language preferences
|
||||
|
||||
## Cache Layer
|
||||
|
||||
### Redis Categories
|
||||
```
|
||||
RedisCategoryKeys:
|
||||
├── LANGUAGE_MODELS
|
||||
├── VALIDATION_USER
|
||||
├── CLUSTER_FUNCTION_CODES
|
||||
├── METHOD_FUNCTION_CODES
|
||||
├── MENU_FIRST_LAYER
|
||||
├── PAGE_MAPPER
|
||||
├── MENU_MAPPER
|
||||
├── AUTH (Authorization)
|
||||
├── OCC (Occupant)
|
||||
└── EMP (Employee)
|
||||
```
|
||||
|
||||
### Cache Invalidation
|
||||
- On login: User-specific caches
|
||||
- On language change: Localization caches
|
||||
- On permission change: Access control caches
|
||||
55
docs/improvements/README.md
Normal file
55
docs/improvements/README.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Improvements Documentation
|
||||
|
||||
This directory contains documentation and example implementations for various system improvements.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
improvements/
|
||||
├── detailed_improvement_plan.md # Overall improvement plan
|
||||
├── language_service/ # Language service implementation
|
||||
│ ├── backend/
|
||||
│ │ ├── language_service.py # Basic language service
|
||||
│ │ └── zod_messages.py # Zod validation messages
|
||||
│ └── frontend/
|
||||
│ └── languageService.ts # Frontend language service
|
||||
└── validation_service/ # Validation service implementation
|
||||
├── backend/
|
||||
│ └── schema_converter.py # Pydantic to Zod converter
|
||||
└── frontend/
|
||||
└── dynamicSchema.ts # Dynamic Zod schema builder
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### Language Service
|
||||
The language service provides internationalization support with:
|
||||
- Backend API for serving translations
|
||||
- Frontend service for managing translations
|
||||
- Integration with Zod for validation messages
|
||||
|
||||
### Validation Service
|
||||
The validation service provides dynamic form validation with:
|
||||
- Automatic conversion of Pydantic models to Zod schemas
|
||||
- Frontend builder for dynamic schema creation
|
||||
- Integration with language service for messages
|
||||
|
||||
## Implementation Status
|
||||
|
||||
These are example implementations that demonstrate the proposed improvements. To implement in the actual system:
|
||||
|
||||
1. Create appropriate service directories
|
||||
2. Copy and adapt the code
|
||||
3. Add tests
|
||||
4. Update dependencies
|
||||
5. Integrate with existing systems
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Review the implementations
|
||||
2. Decide on integration approach
|
||||
3. Create implementation tickets
|
||||
4. Plan phased rollout
|
||||
5. Add monitoring and metrics
|
||||
|
||||
For detailed implementation plans and timelines, see [detailed_improvement_plan.md](./detailed_improvement_plan.md).
|
||||
311
docs/improvements/detailed_improvement_plan.md
Normal file
311
docs/improvements/detailed_improvement_plan.md
Normal file
@@ -0,0 +1,311 @@
|
||||
# Detailed Improvement Plan
|
||||
|
||||
## 1. Infrastructure & Deployment
|
||||
|
||||
### Service Isolation and Containerization
|
||||
- **Microservices Architecture**
|
||||
```
|
||||
/services
|
||||
├── auth-service/
|
||||
│ ├── Dockerfile
|
||||
│ └── docker-compose.yml
|
||||
├── event-service/
|
||||
│ ├── Dockerfile
|
||||
│ └── docker-compose.yml
|
||||
└── validation-service/
|
||||
├── Dockerfile
|
||||
└── docker-compose.yml
|
||||
```
|
||||
- **Service Discovery**
|
||||
- Implement Consul for service registry
|
||||
- Add health check endpoints
|
||||
- Create service mesh with Istio
|
||||
|
||||
### API Gateway Implementation
|
||||
```yaml
|
||||
# api-gateway.yml
|
||||
services:
|
||||
gateway:
|
||||
routes:
|
||||
- id: auth-service
|
||||
uri: lb://auth-service
|
||||
predicates:
|
||||
- Path=/api/auth/**
|
||||
filters:
|
||||
- RateLimit=100,1s
|
||||
- CircuitBreaker=3,10s
|
||||
```
|
||||
|
||||
### Monitoring Stack
|
||||
- **Distributed Tracing**
|
||||
```python
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.exporter import jaeger
|
||||
|
||||
tracer = trace.get_tracer(__name__)
|
||||
with tracer.start_as_current_span("operation") as span:
|
||||
span.set_attribute("attribute", value)
|
||||
```
|
||||
- **Metrics Collection**
|
||||
- Prometheus for metrics
|
||||
- Grafana for visualization
|
||||
- Custom dashboards for each service
|
||||
|
||||
### Configuration Management
|
||||
```python
|
||||
# config_service.py
|
||||
class ConfigService:
|
||||
def __init__(self):
|
||||
self.consul_client = Consul()
|
||||
|
||||
def get_config(self, service_name: str) -> Dict:
|
||||
return self.consul_client.kv.get(f"config/{service_name}")
|
||||
|
||||
def update_config(self, service_name: str, config: Dict):
|
||||
self.consul_client.kv.put(f"config/{service_name}", config)
|
||||
```
|
||||
|
||||
## 2. Performance & Scaling
|
||||
|
||||
### Enhanced Caching Strategy
|
||||
```python
|
||||
# redis_cache.py
|
||||
class RedisCache:
|
||||
def __init__(self):
|
||||
self.client = Redis(cluster_mode=True)
|
||||
|
||||
async def get_or_set(self, key: str, callback: Callable):
|
||||
if value := await self.client.get(key):
|
||||
return value
|
||||
value = await callback()
|
||||
await self.client.set(key, value, ex=3600)
|
||||
return value
|
||||
```
|
||||
|
||||
### Database Optimization
|
||||
```sql
|
||||
-- Sharding Example
|
||||
CREATE TABLE users_shard_1 PARTITION OF users
|
||||
FOR VALUES WITH (modulus 3, remainder 0);
|
||||
CREATE TABLE users_shard_2 PARTITION OF users
|
||||
FOR VALUES WITH (modulus 3, remainder 1);
|
||||
```
|
||||
|
||||
### Event System Enhancement
|
||||
```python
|
||||
# event_publisher.py
|
||||
class EventPublisher:
|
||||
def __init__(self):
|
||||
self.kafka_producer = KafkaProducer()
|
||||
|
||||
async def publish(self, topic: str, event: Dict):
|
||||
await self.kafka_producer.send(
|
||||
topic,
|
||||
value=event,
|
||||
headers=[("version", "1.0")]
|
||||
)
|
||||
```
|
||||
|
||||
### Background Processing
|
||||
```python
|
||||
# job_processor.py
|
||||
class JobProcessor:
|
||||
def __init__(self):
|
||||
self.celery = Celery()
|
||||
self.connection_pool = ConnectionPool(max_size=100)
|
||||
|
||||
@celery.task
|
||||
async def process_job(self, job_data: Dict):
|
||||
async with self.connection_pool.acquire() as conn:
|
||||
await conn.execute(job_data)
|
||||
```
|
||||
|
||||
## 3. Security & Reliability
|
||||
|
||||
### API Security Enhancement
|
||||
```python
|
||||
# security.py
|
||||
class SecurityMiddleware:
|
||||
def __init__(self):
|
||||
self.rate_limiter = RateLimiter()
|
||||
self.key_rotator = KeyRotator()
|
||||
|
||||
async def process_request(self, request: Request):
|
||||
await self.rate_limiter.check(request.client_ip)
|
||||
await self.key_rotator.validate(request.api_key)
|
||||
```
|
||||
|
||||
### Error Handling System
|
||||
```python
|
||||
# error_handler.py
|
||||
class ErrorHandler:
|
||||
def __init__(self):
|
||||
self.sentry_client = Sentry()
|
||||
self.circuit_breaker = CircuitBreaker()
|
||||
|
||||
async def handle_error(self, error: Exception):
|
||||
await self.sentry_client.capture_exception(error)
|
||||
await self.circuit_breaker.record_error()
|
||||
```
|
||||
|
||||
### Testing Framework
|
||||
```python
|
||||
# integration_tests.py
|
||||
class IntegrationTests:
|
||||
async def setup(self):
|
||||
self.containers = await TestContainers.start([
|
||||
"postgres", "redis", "kafka"
|
||||
])
|
||||
|
||||
async def test_end_to_end(self):
|
||||
await self.setup()
|
||||
# Test complete user journey
|
||||
await self.cleanup()
|
||||
```
|
||||
|
||||
### Audit System
|
||||
```python
|
||||
# audit.py
|
||||
class AuditLogger:
|
||||
def __init__(self):
|
||||
self.elastic = Elasticsearch()
|
||||
|
||||
async def log_action(
|
||||
self,
|
||||
user_id: str,
|
||||
action: str,
|
||||
resource: str,
|
||||
changes: Dict
|
||||
):
|
||||
await self.elastic.index({
|
||||
"user_id": user_id,
|
||||
"action": action,
|
||||
"resource": resource,
|
||||
"changes": changes,
|
||||
"timestamp": datetime.utcnow()
|
||||
})
|
||||
```
|
||||
|
||||
## 4. Development Experience
|
||||
|
||||
### Domain-Driven Design
|
||||
```
|
||||
/src
|
||||
├── domain/
|
||||
│ ├── entities/
|
||||
│ ├── value_objects/
|
||||
│ └── aggregates/
|
||||
├── application/
|
||||
│ ├── commands/
|
||||
│ └── queries/
|
||||
└── infrastructure/
|
||||
├── repositories/
|
||||
└── services/
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
```python
|
||||
# main.py
|
||||
from fastapi import FastAPI
|
||||
from fastapi.openapi.utils import get_openapi
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
def custom_openapi():
|
||||
return get_openapi(
|
||||
title="WAG Management API",
|
||||
version="4.0.0",
|
||||
description="Complete API documentation",
|
||||
routes=app.routes
|
||||
)
|
||||
|
||||
app.openapi = custom_openapi
|
||||
```
|
||||
|
||||
### Translation Management
|
||||
```python
|
||||
# i18n.py
|
||||
class TranslationService:
|
||||
def __init__(self):
|
||||
self.translations = {}
|
||||
self.fallback_chain = ["tr", "en"]
|
||||
|
||||
async def get_translation(
|
||||
self,
|
||||
key: str,
|
||||
lang: str,
|
||||
fallback: bool = True
|
||||
) -> str:
|
||||
if translation := self.translations.get(f"{lang}.{key}"):
|
||||
return translation
|
||||
if fallback:
|
||||
for lang in self.fallback_chain:
|
||||
if translation := self.translations.get(f"{lang}.{key}"):
|
||||
return translation
|
||||
return key
|
||||
```
|
||||
|
||||
### Developer Tools
|
||||
```python
|
||||
# debug_toolkit.py
|
||||
class DebugToolkit:
|
||||
def __init__(self):
|
||||
self.profiler = cProfile.Profile()
|
||||
self.debugger = pdb.Pdb()
|
||||
|
||||
def profile_function(self, func: Callable):
|
||||
def wrapper(*args, **kwargs):
|
||||
self.profiler.enable()
|
||||
result = func(*args, **kwargs)
|
||||
self.profiler.disable()
|
||||
return result
|
||||
return wrapper
|
||||
```
|
||||
|
||||
## Implementation Priority
|
||||
|
||||
1. **Phase 1 - Foundation** (1-2 months)
|
||||
- Service containerization
|
||||
- Basic monitoring
|
||||
- API gateway setup
|
||||
- Security enhancements
|
||||
|
||||
2. **Phase 2 - Scaling** (2-3 months)
|
||||
- Caching implementation
|
||||
- Database optimization
|
||||
- Event system upgrade
|
||||
- Background jobs
|
||||
|
||||
3. **Phase 3 - Reliability** (1-2 months)
|
||||
- Error handling
|
||||
- Testing framework
|
||||
- Audit system
|
||||
- Performance monitoring
|
||||
|
||||
4. **Phase 4 - Developer Experience** (1-2 months)
|
||||
- Documentation
|
||||
- Development tools
|
||||
- Translation system
|
||||
- Code organization
|
||||
|
||||
## Success Metrics
|
||||
|
||||
- **Performance**
|
||||
- Response time < 100ms for 95% of requests
|
||||
- Cache hit rate > 80%
|
||||
- Zero downtime deployments
|
||||
|
||||
- **Reliability**
|
||||
- 99.99% uptime
|
||||
- < 0.1% error rate
|
||||
- < 1s failover time
|
||||
|
||||
- **Security**
|
||||
- Zero critical vulnerabilities
|
||||
- 100% audit log coverage
|
||||
- < 1hr security incident response time
|
||||
|
||||
- **Development**
|
||||
- 80% test coverage
|
||||
- < 24hr PR review time
|
||||
- < 1 day developer onboarding
|
||||
@@ -0,0 +1,6 @@
|
||||
# Original content from ApiEvents/LanguageServiceApi/language_service.py
|
||||
from typing import Dict, List, Optional
|
||||
from fastapi import APIRouter, Header
|
||||
from pydantic import BaseModel
|
||||
|
||||
# ... rest of the file content ...
|
||||
@@ -0,0 +1,7 @@
|
||||
# Original content from ApiEvents/LanguageServiceApi/zod_messages.py
|
||||
from typing import Dict
|
||||
from fastapi import APIRouter, Header
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
# ... rest of the file content ...
|
||||
@@ -0,0 +1,4 @@
|
||||
// Original content from frontend/src/services/languageService.ts
|
||||
import axios from 'axios';
|
||||
|
||||
// ... rest of the file content ...
|
||||
@@ -0,0 +1,9 @@
|
||||
# Original content from ApiEvents/ValidationServiceApi/schema_converter.py
|
||||
from typing import Dict, Any, Type, get_type_hints, get_args, get_origin
|
||||
from pydantic import BaseModel, Field, EmailStr
|
||||
from enum import Enum
|
||||
import inspect
|
||||
from fastapi import APIRouter
|
||||
from datetime import datetime
|
||||
|
||||
# ... rest of the file content ...
|
||||
@@ -0,0 +1,146 @@
|
||||
from typing import Dict, Any, Type, Optional
|
||||
from pydantic import BaseModel
|
||||
from fastapi import APIRouter, Header
|
||||
|
||||
class ValidationMessages(BaseModel):
|
||||
"""Messages for Zod validation"""
|
||||
required: str
|
||||
invalid_type: str
|
||||
invalid_string: Dict[str, str] # email, url, etc
|
||||
too_small: Dict[str, str] # string, array, number
|
||||
too_big: Dict[str, str] # string, array, number
|
||||
invalid_date: str
|
||||
invalid_enum: str
|
||||
custom: Dict[str, str]
|
||||
|
||||
class SchemaField(BaseModel):
|
||||
"""Schema field definition"""
|
||||
type: str
|
||||
items: Optional[str] = None # For arrays
|
||||
values: Optional[list] = None # For enums
|
||||
validations: Optional[Dict[str, Any]] = None
|
||||
|
||||
class SchemaDefinition(BaseModel):
|
||||
"""Complete schema definition"""
|
||||
name: str
|
||||
fields: Dict[str, SchemaField]
|
||||
messages: ValidationMessages
|
||||
|
||||
class UnifiedSchemaService:
|
||||
def __init__(self):
|
||||
self.messages = {
|
||||
"tr": ValidationMessages(
|
||||
required="Bu alan zorunludur",
|
||||
invalid_type="Geçersiz tip",
|
||||
invalid_string={
|
||||
"email": "Geçerli bir e-posta adresi giriniz",
|
||||
"url": "Geçerli bir URL giriniz",
|
||||
"uuid": "Geçerli bir UUID giriniz"
|
||||
},
|
||||
too_small={
|
||||
"string": "{min} karakterden az olamaz",
|
||||
"array": "En az {min} öğe gereklidir",
|
||||
"number": "En az {min} olmalıdır"
|
||||
},
|
||||
too_big={
|
||||
"string": "{max} karakterden fazla olamaz",
|
||||
"array": "En fazla {max} öğe olabilir",
|
||||
"number": "En fazla {max} olabilir"
|
||||
},
|
||||
invalid_date="Geçerli bir tarih giriniz",
|
||||
invalid_enum="Geçersiz seçim",
|
||||
custom={
|
||||
"password_match": "Şifreler eşleşmiyor",
|
||||
"strong_password": "Şifre güçlü değil"
|
||||
}
|
||||
),
|
||||
"en": ValidationMessages(
|
||||
required="This field is required",
|
||||
invalid_type="Invalid type",
|
||||
invalid_string={
|
||||
"email": "Please enter a valid email",
|
||||
"url": "Please enter a valid URL",
|
||||
"uuid": "Please enter a valid UUID"
|
||||
},
|
||||
too_small={
|
||||
"string": "Must be at least {min} characters",
|
||||
"array": "Must contain at least {min} items",
|
||||
"number": "Must be at least {min}"
|
||||
},
|
||||
too_big={
|
||||
"string": "Must be at most {max} characters",
|
||||
"array": "Must contain at most {max} items",
|
||||
"number": "Must be at most {max}"
|
||||
},
|
||||
invalid_date="Please enter a valid date",
|
||||
invalid_enum="Invalid selection",
|
||||
custom={
|
||||
"password_match": "Passwords do not match",
|
||||
"strong_password": "Password is not strong enough"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
def get_schema_with_messages(
|
||||
self,
|
||||
model: Type[BaseModel],
|
||||
lang: str = "tr"
|
||||
) -> SchemaDefinition:
|
||||
"""Get schema definition with validation messages"""
|
||||
fields: Dict[str, SchemaField] = {}
|
||||
|
||||
for field_name, field in model.__fields__.items():
|
||||
field_info = SchemaField(
|
||||
type=self._get_field_type(field.outer_type_),
|
||||
items=self._get_items_type(field.outer_type_),
|
||||
values=self._get_enum_values(field.outer_type_),
|
||||
validations=self._get_validations(field)
|
||||
)
|
||||
fields[field_name] = field_info
|
||||
|
||||
return SchemaDefinition(
|
||||
name=model.__name__,
|
||||
fields=fields,
|
||||
messages=self.messages[lang]
|
||||
)
|
||||
|
||||
def _get_field_type(self, type_: Type) -> str:
|
||||
# Implementation similar to SchemaConverter
|
||||
pass
|
||||
|
||||
def _get_items_type(self, type_: Type) -> Optional[str]:
|
||||
# Implementation similar to SchemaConverter
|
||||
pass
|
||||
|
||||
def _get_enum_values(self, type_: Type) -> Optional[list]:
|
||||
# Implementation similar to SchemaConverter
|
||||
pass
|
||||
|
||||
def _get_validations(self, field) -> Optional[Dict[str, Any]]:
|
||||
# Implementation similar to SchemaConverter
|
||||
pass
|
||||
|
||||
router = APIRouter(prefix="/api/schema", tags=["Schema"])
|
||||
schema_service = UnifiedSchemaService()
|
||||
|
||||
@router.get("/model/{model_name}")
|
||||
async def get_model_schema(
|
||||
model_name: str,
|
||||
accept_language: Optional[str] = Header(default="tr")
|
||||
) -> SchemaDefinition:
|
||||
"""Get model schema with validation messages"""
|
||||
# You'd need to implement model lookup
|
||||
models = {
|
||||
"User": UserModel,
|
||||
"Product": ProductModel,
|
||||
# Add your models here
|
||||
}
|
||||
|
||||
if model_name not in models:
|
||||
raise ValueError(f"Model {model_name} not found")
|
||||
|
||||
lang = accept_language.split(",")[0][:2]
|
||||
return schema_service.get_schema_with_messages(
|
||||
models[model_name],
|
||||
lang if lang in ["tr", "en"] else "tr"
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
// Original content from frontend/src/validation/dynamicSchema.ts
|
||||
import { z } from 'zod';
|
||||
import axios from 'axios';
|
||||
import { zodMessages } from './zodMessages';
|
||||
|
||||
// ... rest of the file content ...
|
||||
@@ -0,0 +1,219 @@
|
||||
import { z } from 'zod';
|
||||
import axios from 'axios';
|
||||
|
||||
interface ValidationMessages {
|
||||
required: string;
|
||||
invalid_type: string;
|
||||
invalid_string: Record<string, string>;
|
||||
too_small: Record<string, string>;
|
||||
too_big: Record<string, string>;
|
||||
invalid_date: string;
|
||||
invalid_enum: string;
|
||||
custom: Record<string, string>;
|
||||
}
|
||||
|
||||
interface SchemaField {
|
||||
type: string;
|
||||
items?: string;
|
||||
values?: any[];
|
||||
validations?: Record<string, any>;
|
||||
}
|
||||
|
||||
interface SchemaDefinition {
|
||||
name: string;
|
||||
fields: Record<string, SchemaField>;
|
||||
messages: ValidationMessages;
|
||||
}
|
||||
|
||||
class UnifiedSchemaBuilder {
|
||||
private static instance: UnifiedSchemaBuilder;
|
||||
private schemaCache: Map<string, z.ZodSchema> = new Map();
|
||||
|
||||
private constructor() {}
|
||||
|
||||
static getInstance(): UnifiedSchemaBuilder {
|
||||
if (!UnifiedSchemaBuilder.instance) {
|
||||
UnifiedSchemaBuilder.instance = new UnifiedSchemaBuilder();
|
||||
}
|
||||
return UnifiedSchemaBuilder.instance;
|
||||
}
|
||||
|
||||
async getSchema(modelName: string): Promise<z.ZodSchema> {
|
||||
// Check cache first
|
||||
if (this.schemaCache.has(modelName)) {
|
||||
return this.schemaCache.get(modelName)!;
|
||||
}
|
||||
|
||||
// Fetch schema definition with messages from backend
|
||||
const response = await axios.get<SchemaDefinition>(
|
||||
`/api/schema/model/${modelName}`,
|
||||
{
|
||||
headers: {
|
||||
'Accept-Language': navigator.language || 'tr'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const schema = this.buildSchema(response.data);
|
||||
this.schemaCache.set(modelName, schema);
|
||||
return schema;
|
||||
}
|
||||
|
||||
private buildSchema(definition: SchemaDefinition): z.ZodSchema {
|
||||
const shape: Record<string, z.ZodTypeAny> = {};
|
||||
|
||||
for (const [fieldName, field] of Object.entries(definition.fields)) {
|
||||
shape[fieldName] = this.buildField(field, definition.messages);
|
||||
}
|
||||
|
||||
return z.object(shape);
|
||||
}
|
||||
|
||||
private buildField(
|
||||
field: SchemaField,
|
||||
messages: ValidationMessages
|
||||
): z.ZodTypeAny {
|
||||
let zodField: z.ZodTypeAny;
|
||||
|
||||
switch (field.type) {
|
||||
case 'string':
|
||||
zodField = z.string({
|
||||
required_error: messages.required,
|
||||
invalid_type_error: messages.invalid_type
|
||||
});
|
||||
break;
|
||||
case 'email':
|
||||
zodField = z.string().email(messages.invalid_string.email);
|
||||
break;
|
||||
case 'number':
|
||||
zodField = z.number({
|
||||
required_error: messages.required,
|
||||
invalid_type_error: messages.invalid_type
|
||||
});
|
||||
break;
|
||||
case 'boolean':
|
||||
zodField = z.boolean({
|
||||
required_error: messages.required,
|
||||
invalid_type_error: messages.invalid_type
|
||||
});
|
||||
break;
|
||||
case 'date':
|
||||
zodField = z.date({
|
||||
required_error: messages.required,
|
||||
invalid_type_error: messages.invalid_date
|
||||
});
|
||||
break;
|
||||
case 'array':
|
||||
zodField = z.array(
|
||||
this.buildField({ type: field.items! }, messages)
|
||||
);
|
||||
break;
|
||||
case 'enum':
|
||||
zodField = z.enum(field.values as [string, ...string[]], {
|
||||
required_error: messages.required,
|
||||
invalid_type_error: messages.invalid_enum
|
||||
});
|
||||
break;
|
||||
default:
|
||||
zodField = z.any();
|
||||
}
|
||||
|
||||
// Apply validations if any
|
||||
if (field.validations) {
|
||||
zodField = this.applyValidations(zodField, field.validations, messages);
|
||||
}
|
||||
|
||||
return zodField;
|
||||
}
|
||||
|
||||
private applyValidations(
|
||||
field: z.ZodTypeAny,
|
||||
validations: Record<string, any>,
|
||||
messages: ValidationMessages
|
||||
): z.ZodTypeAny {
|
||||
let result = field;
|
||||
|
||||
if ('min_length' in validations) {
|
||||
result = (result as z.ZodString).min(
|
||||
validations.min_length,
|
||||
messages.too_small.string.replace(
|
||||
'{min}',
|
||||
validations.min_length.toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ('max_length' in validations) {
|
||||
result = (result as z.ZodString).max(
|
||||
validations.max_length,
|
||||
messages.too_big.string.replace(
|
||||
'{max}',
|
||||
validations.max_length.toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ('pattern' in validations) {
|
||||
result = (result as z.ZodString).regex(
|
||||
new RegExp(validations.pattern),
|
||||
messages.custom[validations.pattern_message] || 'Invalid format'
|
||||
);
|
||||
}
|
||||
|
||||
if ('gt' in validations) {
|
||||
result = (result as z.ZodNumber).gt(
|
||||
validations.gt,
|
||||
messages.too_small.number.replace(
|
||||
'{min}',
|
||||
(validations.gt + 1).toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ('lt' in validations) {
|
||||
result = (result as z.ZodNumber).lt(
|
||||
validations.lt,
|
||||
messages.too_big.number.replace(
|
||||
'{max}',
|
||||
(validations.lt - 1).toString()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const schemaBuilder = UnifiedSchemaBuilder.getInstance();
|
||||
|
||||
// Usage example:
|
||||
/*
|
||||
import { schemaBuilder } from './validation/unifiedSchemaBuilder';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
|
||||
function UserForm() {
|
||||
const [schema, setSchema] = useState<z.ZodSchema | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
async function loadSchema() {
|
||||
const userSchema = await schemaBuilder.getSchema('User');
|
||||
setSchema(userSchema);
|
||||
}
|
||||
loadSchema();
|
||||
}, []);
|
||||
|
||||
const form = useForm({
|
||||
resolver: schema ? zodResolver(schema) : undefined
|
||||
});
|
||||
|
||||
if (!schema) return <div>Loading...</div>;
|
||||
|
||||
return (
|
||||
<form onSubmit={form.handleSubmit(data => console.log(data))}>
|
||||
{/* Your form fields */}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
*/
|
||||
229
docs/method_event_system.md
Normal file
229
docs/method_event_system.md
Normal file
@@ -0,0 +1,229 @@
|
||||
# MethodToEvent System Documentation
|
||||
|
||||
## Overview
|
||||
The MethodToEvent system provides a unified way to manage API endpoints and frontend menu structure with built-in permission handling. It uses UUIDs for permission management and supports hierarchical menu structures.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. MethodToEvent Base Class
|
||||
Base class for defining event methods with API endpoints and frontend page configuration.
|
||||
|
||||
#### Class Variables
|
||||
- `action_key`: Unique identifier for the action
|
||||
- `event_type`: Type of event (e.g., 'query', 'command')
|
||||
- `event_description`: Human-readable description
|
||||
- `event_category`: Category for grouping
|
||||
- `__event_keys__`: UUID to event name mapping
|
||||
- `__event_validation__`: Validation rules
|
||||
- `__endpoint_config__`: API endpoint configuration
|
||||
- `__page_info__`: Frontend page configuration
|
||||
|
||||
#### Methods
|
||||
|
||||
##### Configure API Endpoints
|
||||
```python
|
||||
@classmethod
|
||||
def register_endpoint(
|
||||
cls,
|
||||
event_uuid: str,
|
||||
path: str,
|
||||
method: str = "POST",
|
||||
response_model: Optional[Type] = None,
|
||||
**kwargs
|
||||
) -> None
|
||||
```
|
||||
Registers an API endpoint for an event UUID.
|
||||
|
||||
##### Configure Router
|
||||
```python
|
||||
@classmethod
|
||||
def configure_router(cls, prefix: str, tags: List[str]) -> None
|
||||
```
|
||||
Sets the router prefix and OpenAPI tags.
|
||||
|
||||
##### Configure Page
|
||||
```python
|
||||
@classmethod
|
||||
def configure_page(
|
||||
cls,
|
||||
name: str,
|
||||
title: Dict[str, str],
|
||||
icon: str,
|
||||
url: str,
|
||||
component: Optional[str] = None,
|
||||
parent: Optional[str] = None
|
||||
) -> None
|
||||
```
|
||||
Configures frontend page information.
|
||||
|
||||
##### Get Page Info with Permissions
|
||||
```python
|
||||
@classmethod
|
||||
def get_page_info_with_permissions(
|
||||
cls,
|
||||
user_permission_uuids: Set[str],
|
||||
include_endpoints: bool = False
|
||||
) -> Optional[Dict[str, Any]]
|
||||
```
|
||||
Returns page info if user has required permissions.
|
||||
|
||||
### 2. EventMethodRegistry
|
||||
Singleton registry for managing all MethodToEvent classes and building menu structures.
|
||||
|
||||
#### Methods
|
||||
|
||||
##### Register Method Class
|
||||
```python
|
||||
@classmethod
|
||||
def register_method_class(cls, method_class: Type[MethodToEvent]) -> None
|
||||
```
|
||||
Registers a MethodToEvent class in the registry.
|
||||
|
||||
##### Get All Menu Items
|
||||
```python
|
||||
@classmethod
|
||||
def get_all_menu_items(
|
||||
cls,
|
||||
user_permission_uuids: Set[str],
|
||||
include_endpoints: bool = False
|
||||
) -> List[Dict[str, Any]]
|
||||
```
|
||||
Returns complete menu structure based on permissions.
|
||||
|
||||
##### Get Available Endpoints
|
||||
```python
|
||||
@classmethod
|
||||
def get_available_endpoints(
|
||||
cls,
|
||||
user_permission_uuids: Set[str]
|
||||
) -> Dict[str, Dict[str, Any]]
|
||||
```
|
||||
Returns all available API endpoints based on permissions.
|
||||
|
||||
## Example Usage
|
||||
|
||||
### 1. Define Event Methods
|
||||
```python
|
||||
class AccountEventMethods(MethodToEvent):
|
||||
event_category = "account"
|
||||
event_type = "query"
|
||||
event_description = "Account management operations"
|
||||
__event_keys__ = {
|
||||
"uuid1": "view_account",
|
||||
"uuid2": "edit_account"
|
||||
}
|
||||
|
||||
# Configure API
|
||||
configure_router("/api/account", ["Account"])
|
||||
register_endpoint(
|
||||
"uuid1",
|
||||
"/view",
|
||||
method="GET",
|
||||
response_model=AccountResponse
|
||||
)
|
||||
|
||||
# Configure frontend
|
||||
configure_page(
|
||||
name="AccountPage",
|
||||
title={"tr": "Hesaplar", "en": "Accounts"},
|
||||
icon="User",
|
||||
url="/account"
|
||||
)
|
||||
|
||||
class AccountDetailsEventMethods(MethodToEvent):
|
||||
event_category = "account_details"
|
||||
__event_keys__ = {
|
||||
"uuid3": "view_details",
|
||||
"uuid4": "edit_details"
|
||||
}
|
||||
|
||||
configure_page(
|
||||
name="AccountDetailsPage",
|
||||
title={"tr": "Hesap Detayları", "en": "Account Details"},
|
||||
icon="FileText",
|
||||
url="/account/details",
|
||||
parent="AccountPage" # Link to parent
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Register and Use
|
||||
```python
|
||||
# Register classes
|
||||
registry = EventMethodRegistry()
|
||||
registry.register_method_class(AccountEventMethods)
|
||||
registry.register_method_class(AccountDetailsEventMethods)
|
||||
|
||||
# Get menu structure
|
||||
user_permissions = {"uuid1", "uuid2", "uuid3"}
|
||||
menu_items = registry.get_all_menu_items(user_permissions, include_endpoints=True)
|
||||
```
|
||||
|
||||
## Menu Structure Rules
|
||||
|
||||
1. **Parent-Child Visibility**
|
||||
- Parent page must have permissions to be visible
|
||||
- If parent is not visible, children are never shown
|
||||
- If parent is visible, all children are shown
|
||||
|
||||
2. **Permission Checking**
|
||||
- Based on UUID intersection
|
||||
- Page is visible if user has any of its event UUIDs
|
||||
- Endpoints only included if user has specific permission
|
||||
|
||||
3. **Menu Organization**
|
||||
- Automatic tree structure based on parent field
|
||||
- Sorted by name for consistency
|
||||
- Optional endpoint information included
|
||||
|
||||
## Example Menu Structure
|
||||
```python
|
||||
[
|
||||
{
|
||||
"name": "AccountPage",
|
||||
"title": {"tr": "Hesaplar", "en": "Accounts"},
|
||||
"icon": "User",
|
||||
"url": "/account",
|
||||
"category": "account",
|
||||
"type": "query",
|
||||
"description": "Account management operations",
|
||||
"available_endpoints": {
|
||||
"uuid1": {"path": "/api/account/view", "method": "GET"},
|
||||
"uuid2": {"path": "/api/account/edit", "method": "POST"}
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"name": "AccountDetailsPage",
|
||||
"title": {"tr": "Hesap Detayları", "en": "Account Details"},
|
||||
"icon": "FileText",
|
||||
"url": "/account/details",
|
||||
"parent": "AccountPage",
|
||||
"available_endpoints": {
|
||||
"uuid3": {"path": "/api/account/details/view", "method": "GET"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **UUID Management**
|
||||
- Use consistent UUIDs across the system
|
||||
- Document UUID meanings and permissions
|
||||
- Group related permissions under same parent
|
||||
|
||||
2. **Page Organization**
|
||||
- Use meaningful page names
|
||||
- Provide translations for all titles
|
||||
- Keep URL structure consistent with hierarchy
|
||||
|
||||
3. **API Endpoints**
|
||||
- Use consistent router prefixes
|
||||
- Group related endpoints under same router
|
||||
- Use appropriate HTTP methods
|
||||
|
||||
4. **Permission Structure**
|
||||
- Design permissions hierarchically
|
||||
- Consider access patterns when grouping
|
||||
- Document permission requirements
|
||||
42
docs/notes/README.md
Normal file
42
docs/notes/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Development Notes
|
||||
|
||||
This directory contains development notes and documentation organized by topic and date.
|
||||
|
||||
## Structure
|
||||
|
||||
- Each note is stored as a markdown file
|
||||
- Files are organized by topic in subdirectories
|
||||
- File naming format: `YYYY-MM-DD_topic_name.md`
|
||||
- Each note includes:
|
||||
- Date
|
||||
- Topic/Category
|
||||
- Content
|
||||
- Related files/components
|
||||
- Action items (if any)
|
||||
|
||||
## How to Add Notes
|
||||
|
||||
1. Create a new markdown file with the date prefix
|
||||
2. Use the standard note template
|
||||
3. Place in appropriate topic directory
|
||||
4. Link related notes if applicable
|
||||
|
||||
## Note Template
|
||||
|
||||
```markdown
|
||||
# [Topic] - [Date]
|
||||
|
||||
## Overview
|
||||
Brief description of the topic/issue
|
||||
|
||||
## Details
|
||||
Main content of the note
|
||||
|
||||
## Related
|
||||
- Links to related files/components
|
||||
- References to other notes
|
||||
|
||||
## Action Items
|
||||
- [ ] Todo items if any
|
||||
- [ ] Next steps
|
||||
```
|
||||
Reference in New Issue
Block a user