fast-clean-architecture 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fast_clean_architecture/__init__.py +24 -0
- fast_clean_architecture/cli.py +480 -0
- fast_clean_architecture/config.py +506 -0
- fast_clean_architecture/exceptions.py +63 -0
- fast_clean_architecture/generators/__init__.py +11 -0
- fast_clean_architecture/generators/component_generator.py +1039 -0
- fast_clean_architecture/generators/config_updater.py +308 -0
- fast_clean_architecture/generators/package_generator.py +174 -0
- fast_clean_architecture/generators/template_validator.py +546 -0
- fast_clean_architecture/generators/validation_config.py +75 -0
- fast_clean_architecture/generators/validation_metrics.py +193 -0
- fast_clean_architecture/templates/__init__.py +7 -0
- fast_clean_architecture/templates/__init__.py.j2 +26 -0
- fast_clean_architecture/templates/api.py.j2 +65 -0
- fast_clean_architecture/templates/command.py.j2 +26 -0
- fast_clean_architecture/templates/entity.py.j2 +49 -0
- fast_clean_architecture/templates/external.py.j2 +61 -0
- fast_clean_architecture/templates/infrastructure_repository.py.j2 +69 -0
- fast_clean_architecture/templates/model.py.j2 +38 -0
- fast_clean_architecture/templates/query.py.j2 +26 -0
- fast_clean_architecture/templates/repository.py.j2 +57 -0
- fast_clean_architecture/templates/schemas.py.j2 +32 -0
- fast_clean_architecture/templates/service.py.j2 +109 -0
- fast_clean_architecture/templates/value_object.py.j2 +34 -0
- fast_clean_architecture/utils.py +553 -0
- fast_clean_architecture-1.0.0.dist-info/METADATA +541 -0
- fast_clean_architecture-1.0.0.dist-info/RECORD +30 -0
- fast_clean_architecture-1.0.0.dist-info/WHEEL +4 -0
- fast_clean_architecture-1.0.0.dist-info/entry_points.txt +2 -0
- fast_clean_architecture-1.0.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,193 @@
|
|
1
|
+
"""Performance monitoring and metrics collection for template validation."""
|
2
|
+
|
3
|
+
import time
|
4
|
+
import logging
|
5
|
+
import threading
|
6
|
+
from contextlib import contextmanager
|
7
|
+
from typing import Dict, Any, Optional, Generator
|
8
|
+
from dataclasses import dataclass, field
|
9
|
+
from threading import Lock
|
10
|
+
|
11
|
+
from .validation_config import ValidationMetrics
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass
|
17
|
+
class ValidationStats:
|
18
|
+
"""Simplified statistics for template validation operations."""
|
19
|
+
|
20
|
+
total_validations: int = 0
|
21
|
+
successful_validations: int = 0
|
22
|
+
failed_validations: int = 0
|
23
|
+
errors_by_type: Dict[str, int] = field(default_factory=dict)
|
24
|
+
fallback_used_count: int = 0
|
25
|
+
total_time_ms: float = 0.0
|
26
|
+
min_time_ms: float = float("inf")
|
27
|
+
max_time_ms: float = 0.0
|
28
|
+
strategy_usage: Dict[str, int] = field(default_factory=dict)
|
29
|
+
_lock: threading.Lock = field(default_factory=threading.Lock)
|
30
|
+
|
31
|
+
def update(
|
32
|
+
self,
|
33
|
+
metrics: ValidationMetrics,
|
34
|
+
success: bool,
|
35
|
+
error_type: Optional[str] = None,
|
36
|
+
):
|
37
|
+
"""Update statistics with new validation metrics."""
|
38
|
+
with self._lock:
|
39
|
+
self.total_validations += 1
|
40
|
+
|
41
|
+
if success:
|
42
|
+
self.successful_validations += 1
|
43
|
+
else:
|
44
|
+
self.failed_validations += 1
|
45
|
+
if error_type:
|
46
|
+
self.errors_by_type[error_type] = (
|
47
|
+
self.errors_by_type.get(error_type, 0) + 1
|
48
|
+
)
|
49
|
+
|
50
|
+
if metrics.fallback_used:
|
51
|
+
self.fallback_used_count += 1
|
52
|
+
|
53
|
+
# Update timing statistics
|
54
|
+
self.total_time_ms += metrics.validation_time_ms
|
55
|
+
self.min_time_ms = min(self.min_time_ms, metrics.validation_time_ms)
|
56
|
+
self.max_time_ms = max(self.max_time_ms, metrics.validation_time_ms)
|
57
|
+
|
58
|
+
# Update strategy usage
|
59
|
+
strategy = metrics.strategy_used
|
60
|
+
self.strategy_usage[strategy] = self.strategy_usage.get(strategy, 0) + 1
|
61
|
+
|
62
|
+
@property
|
63
|
+
def average_time_ms(self) -> float:
|
64
|
+
"""Calculate average validation time."""
|
65
|
+
if self.total_validations == 0:
|
66
|
+
return 0.0
|
67
|
+
return self.total_time_ms / self.total_validations
|
68
|
+
|
69
|
+
@property
|
70
|
+
def success_rate(self) -> float:
|
71
|
+
"""Calculate success rate as percentage."""
|
72
|
+
if self.total_validations == 0:
|
73
|
+
return 0.0
|
74
|
+
return (self.successful_validations / self.total_validations) * 100
|
75
|
+
|
76
|
+
@property
|
77
|
+
def fallback_rate(self) -> float:
|
78
|
+
"""Calculate fallback usage rate as percentage."""
|
79
|
+
if self.total_validations == 0:
|
80
|
+
return 0.0
|
81
|
+
return (self.fallback_used_count / self.total_validations) * 100
|
82
|
+
|
83
|
+
def to_dict(self) -> Dict[str, Any]:
|
84
|
+
"""Convert statistics to dictionary for reporting."""
|
85
|
+
return {
|
86
|
+
"total_validations": self.total_validations,
|
87
|
+
"successful_validations": self.successful_validations,
|
88
|
+
"failed_validations": self.failed_validations,
|
89
|
+
"success_rate_percent": round(self.success_rate, 2),
|
90
|
+
"fallback_used_count": self.fallback_used_count,
|
91
|
+
"fallback_rate_percent": round(self.fallback_rate, 2),
|
92
|
+
"timing": {
|
93
|
+
"total_time_ms": round(self.total_time_ms, 3),
|
94
|
+
"average_time_ms": round(self.average_time_ms, 3),
|
95
|
+
"min_time_ms": (
|
96
|
+
round(self.min_time_ms, 3)
|
97
|
+
if self.min_time_ms != float("inf")
|
98
|
+
else 0
|
99
|
+
),
|
100
|
+
"max_time_ms": round(self.max_time_ms, 3),
|
101
|
+
},
|
102
|
+
"strategy_usage": self.strategy_usage,
|
103
|
+
"error_types": self.errors_by_type,
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
class ValidationMetricsCollector:
|
108
|
+
"""Simplified collector for validation metrics."""
|
109
|
+
|
110
|
+
def __init__(self):
|
111
|
+
self.stats = ValidationStats()
|
112
|
+
|
113
|
+
def record_validation(self, success: bool, error_type: Optional[str] = None):
|
114
|
+
"""Record a validation operation.
|
115
|
+
|
116
|
+
Args:
|
117
|
+
success: Whether the validation succeeded
|
118
|
+
error_type: Type of error if validation failed
|
119
|
+
"""
|
120
|
+
self.stats.total_validations += 1
|
121
|
+
if success:
|
122
|
+
self.stats.successful_validations += 1
|
123
|
+
else:
|
124
|
+
self.stats.failed_validations += 1
|
125
|
+
if error_type:
|
126
|
+
self.stats.errors_by_type[error_type] = (
|
127
|
+
self.stats.errors_by_type.get(error_type, 0) + 1
|
128
|
+
)
|
129
|
+
|
130
|
+
def get_stats(self) -> ValidationStats:
|
131
|
+
"""Get current statistics."""
|
132
|
+
return self.stats
|
133
|
+
|
134
|
+
def reset(self) -> None:
|
135
|
+
"""Reset all statistics."""
|
136
|
+
self.stats = ValidationStats()
|
137
|
+
|
138
|
+
|
139
|
+
# Global metrics collector instance
|
140
|
+
_metrics_collector = ValidationMetricsCollector()
|
141
|
+
|
142
|
+
|
143
|
+
def get_metrics_collector() -> ValidationMetricsCollector:
|
144
|
+
"""Get the global metrics collector instance."""
|
145
|
+
return _metrics_collector
|
146
|
+
|
147
|
+
|
148
|
+
@contextmanager
|
149
|
+
def timed_validation(
|
150
|
+
strategy_name: str,
|
151
|
+
template_size: int,
|
152
|
+
variables_count: int,
|
153
|
+
enable_timing: bool = True,
|
154
|
+
) -> Generator[ValidationMetrics, None, None]:
|
155
|
+
"""Context manager for timing validation operations."""
|
156
|
+
start_time = time.perf_counter()
|
157
|
+
|
158
|
+
metrics = ValidationMetrics(
|
159
|
+
strategy_used=strategy_name,
|
160
|
+
validation_time_ms=0.0,
|
161
|
+
template_size_bytes=template_size,
|
162
|
+
variables_count=variables_count,
|
163
|
+
undefined_variables_found=0,
|
164
|
+
)
|
165
|
+
|
166
|
+
try:
|
167
|
+
yield metrics
|
168
|
+
finally:
|
169
|
+
if enable_timing:
|
170
|
+
end_time = time.perf_counter()
|
171
|
+
metrics.validation_time_ms = (end_time - start_time) * 1000
|
172
|
+
|
173
|
+
# Log slow validations
|
174
|
+
if metrics.validation_time_ms > 100: # Configurable threshold
|
175
|
+
logger.warning(
|
176
|
+
f"Slow validation detected: {strategy_name} took {metrics.validation_time_ms:.3f}ms"
|
177
|
+
)
|
178
|
+
|
179
|
+
|
180
|
+
@contextmanager
|
181
|
+
def validation_timeout(timeout_seconds: float):
|
182
|
+
"""Context manager for validation timeout (placeholder for future implementation)."""
|
183
|
+
# Note: This is a placeholder. Full timeout implementation would require
|
184
|
+
# threading or async support, which depends on the application architecture.
|
185
|
+
start_time = time.time()
|
186
|
+
try:
|
187
|
+
yield
|
188
|
+
finally:
|
189
|
+
elapsed = time.time() - start_time
|
190
|
+
if elapsed > timeout_seconds:
|
191
|
+
logger.warning(
|
192
|
+
f"Validation exceeded timeout: {elapsed:.3f}s > {timeout_seconds}s"
|
193
|
+
)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{% if package_type == "empty" %}
|
2
|
+
"""
|
3
|
+
{{ package_description }} package for {{ context }}.
|
4
|
+
"""
|
5
|
+
{% elif package_type == "component" %}
|
6
|
+
"""
|
7
|
+
{{ component_type|title }} components for {{ module_name }} module.
|
8
|
+
"""
|
9
|
+
{% for component in components %}
|
10
|
+
from .{{ component.file_name }} import {{ component.class_name }}
|
11
|
+
{% endfor %}
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
{% for component in components %}
|
15
|
+
"{{ component.class_name }}",
|
16
|
+
{% endfor %}
|
17
|
+
]
|
18
|
+
{% elif package_type == "system" %}
|
19
|
+
"""
|
20
|
+
{{ system_name|title }} system context.
|
21
|
+
"""
|
22
|
+
{% elif package_type == "module" %}
|
23
|
+
"""
|
24
|
+
{{ module_name|title }} module for {{ system_name }} system.
|
25
|
+
"""
|
26
|
+
{% endif %}
|
@@ -0,0 +1,65 @@
|
|
1
|
+
"""
|
2
|
+
{{ router_name }} API router for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from fastapi import APIRouter, Depends, HTTPException
|
5
|
+
from typing import List
|
6
|
+
from ..schemas.{{ schema_file }} import {{ SchemaName }}Create, {{ SchemaName }}Response
|
7
|
+
from ...application.services.{{ service_file }} import {{ ServiceName }}Service
|
8
|
+
|
9
|
+
router = APIRouter(prefix="/{{ resource_name }}", tags=["{{ resource_name }}"])
|
10
|
+
|
11
|
+
|
12
|
+
@router.post("/", response_model={{ SchemaName }}Response)
|
13
|
+
async def create_{{ entity_name }}(
|
14
|
+
data: {{ SchemaName }}Create,
|
15
|
+
service: {{ ServiceName }}Service = Depends()
|
16
|
+
):
|
17
|
+
"""Create new {{ entity_name }}."""
|
18
|
+
entity = await service.create_{{ entity_name }}(data.model_dump())
|
19
|
+
return {{ SchemaName }}Response.model_validate(entity)
|
20
|
+
|
21
|
+
|
22
|
+
@router.get("/", response_model=List[{{ SchemaName }}Response])
|
23
|
+
async def list_{{ resource_name }}(
|
24
|
+
service: {{ ServiceName }}Service = Depends()
|
25
|
+
):
|
26
|
+
"""List all {{ resource_name }}."""
|
27
|
+
entities = await service.list_{{ resource_name }}()
|
28
|
+
return [{{ SchemaName }}Response.model_validate(entity) for entity in entities]
|
29
|
+
|
30
|
+
|
31
|
+
@router.get("/{id}", response_model={{ SchemaName }}Response)
|
32
|
+
async def get_{{ entity_name }}(
|
33
|
+
id: str,
|
34
|
+
service: {{ ServiceName }}Service = Depends()
|
35
|
+
):
|
36
|
+
"""Get {{ entity_name }} by ID."""
|
37
|
+
entity = await service.get_{{ entity_name }}_by_id(id)
|
38
|
+
if not entity:
|
39
|
+
raise HTTPException(status_code=404, detail="{{ EntityName }} not found")
|
40
|
+
return {{ SchemaName }}Response.model_validate(entity)
|
41
|
+
|
42
|
+
|
43
|
+
@router.put("/{id}", response_model={{ SchemaName }}Response)
|
44
|
+
async def update_{{ entity_name }}(
|
45
|
+
id: str,
|
46
|
+
data: {{ SchemaName }}Create,
|
47
|
+
service: {{ ServiceName }}Service = Depends()
|
48
|
+
):
|
49
|
+
"""Update {{ entity_name }}."""
|
50
|
+
entity = await service.update_{{ entity_name }}(id, data.model_dump())
|
51
|
+
if not entity:
|
52
|
+
raise HTTPException(status_code=404, detail="{{ EntityName }} not found")
|
53
|
+
return {{ SchemaName }}Response.model_validate(entity)
|
54
|
+
|
55
|
+
|
56
|
+
@router.delete("/{id}")
|
57
|
+
async def delete_{{ entity_name }}(
|
58
|
+
id: str,
|
59
|
+
service: {{ ServiceName }}Service = Depends()
|
60
|
+
):
|
61
|
+
"""Delete {{ entity_name }}."""
|
62
|
+
success = await service.delete_{{ entity_name }}(id)
|
63
|
+
if not success:
|
64
|
+
raise HTTPException(status_code=404, detail="{{ EntityName }} not found")
|
65
|
+
return {"message": "{{ EntityName }} deleted successfully"}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
{{ command_name }} command for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any, Dict
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class {{ CommandName }}Command:
|
10
|
+
"""Command for {{ command_name }} operation."""
|
11
|
+
|
12
|
+
def __post_init__(self):
|
13
|
+
"""Post-initialization validation."""
|
14
|
+
pass
|
15
|
+
|
16
|
+
|
17
|
+
class {{ CommandName }}Handler:
|
18
|
+
"""Handler for {{ CommandName }}Command."""
|
19
|
+
|
20
|
+
def __init__(self):
|
21
|
+
pass
|
22
|
+
|
23
|
+
async def handle(self, command: {{ CommandName }}Command) -> Any:
|
24
|
+
"""Handle the {{ command_name }} command."""
|
25
|
+
# Implementation here
|
26
|
+
pass
|
@@ -0,0 +1,49 @@
|
|
1
|
+
"""
|
2
|
+
{{ entity_name }} entity for {{ module_name }} module.
|
3
|
+
|
4
|
+
Generated at: {{ generated_at }}
|
5
|
+
Generator version: {{ generator_version }}
|
6
|
+
"""
|
7
|
+
from dataclasses import dataclass, field
|
8
|
+
from typing import Optional
|
9
|
+
from datetime import datetime
|
10
|
+
from uuid import UUID, uuid4
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class {{ EntityName }}:
|
15
|
+
"""{{ EntityName }} domain entity.
|
16
|
+
|
17
|
+
Represents a {{ entity_description }} in the {{ module_name }} domain.
|
18
|
+
"""
|
19
|
+
|
20
|
+
# Primary identifier
|
21
|
+
id: Optional[UUID] = field(default_factory=uuid4)
|
22
|
+
|
23
|
+
# Add your domain-specific fields here
|
24
|
+
# Example:
|
25
|
+
# name: str = ""
|
26
|
+
# description: Optional[str] = None
|
27
|
+
|
28
|
+
# Audit fields
|
29
|
+
created_at: Optional[datetime] = field(default_factory=lambda: datetime.utcnow())
|
30
|
+
updated_at: Optional[datetime] = field(default_factory=lambda: datetime.utcnow())
|
31
|
+
|
32
|
+
def __post_init__(self) -> None:
|
33
|
+
"""Post-initialization validation and setup."""
|
34
|
+
if self.id is None:
|
35
|
+
self.id = uuid4()
|
36
|
+
|
37
|
+
now = datetime.utcnow()
|
38
|
+
if self.created_at is None:
|
39
|
+
self.created_at = now
|
40
|
+
if self.updated_at is None:
|
41
|
+
self.updated_at = now
|
42
|
+
|
43
|
+
def update_timestamp(self) -> None:
|
44
|
+
"""Update the updated_at timestamp."""
|
45
|
+
self.updated_at = datetime.utcnow()
|
46
|
+
|
47
|
+
def is_new(self) -> bool:
|
48
|
+
"""Check if this is a new entity (not persisted yet)."""
|
49
|
+
return self.created_at == self.updated_at
|
@@ -0,0 +1,61 @@
|
|
1
|
+
"""
|
2
|
+
{{ external_service_name }} client for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
import httpx
|
5
|
+
from typing import Any, Dict, Optional
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
|
9
|
+
class {{ ExternalServiceName }}Config(BaseModel):
|
10
|
+
"""Configuration for {{ ExternalServiceName }} client."""
|
11
|
+
|
12
|
+
base_url: str
|
13
|
+
api_key: Optional[str] = None
|
14
|
+
timeout: int = 30
|
15
|
+
|
16
|
+
|
17
|
+
class {{ ExternalServiceName }}Client:
|
18
|
+
"""Client for {{ external_service_name }} external service."""
|
19
|
+
|
20
|
+
def __init__(self, config: {{ ExternalServiceName }}Config):
|
21
|
+
self._config = config
|
22
|
+
self._client = httpx.AsyncClient(
|
23
|
+
base_url=config.base_url,
|
24
|
+
timeout=config.timeout,
|
25
|
+
)
|
26
|
+
|
27
|
+
async def __aenter__(self):
|
28
|
+
return self
|
29
|
+
|
30
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
31
|
+
await self._client.aclose()
|
32
|
+
|
33
|
+
async def get_data(self, endpoint: str, params: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
34
|
+
"""Get data from external service."""
|
35
|
+
headers = {}
|
36
|
+
if self._config.api_key:
|
37
|
+
headers["Authorization"] = f"Bearer {self._config.api_key}"
|
38
|
+
|
39
|
+
response = await self._client.get(
|
40
|
+
endpoint,
|
41
|
+
params=params,
|
42
|
+
headers=headers,
|
43
|
+
)
|
44
|
+
response.raise_for_status()
|
45
|
+
|
46
|
+
return response.json()
|
47
|
+
|
48
|
+
async def post_data(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
49
|
+
"""Post data to external service."""
|
50
|
+
headers = {"Content-Type": "application/json"}
|
51
|
+
if self._config.api_key:
|
52
|
+
headers["Authorization"] = f"Bearer {self._config.api_key}"
|
53
|
+
|
54
|
+
response = await self._client.post(
|
55
|
+
endpoint,
|
56
|
+
json=data,
|
57
|
+
headers=headers,
|
58
|
+
)
|
59
|
+
response.raise_for_status()
|
60
|
+
|
61
|
+
return response.json()
|
@@ -0,0 +1,69 @@
|
|
1
|
+
"""
|
2
|
+
SQLAlchemy {{ repository_name }} repository implementation for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from typing import List, Optional
|
5
|
+
from sqlalchemy.orm import Session
|
6
|
+
from ...domain.entities.{{ entity_file }} import {{ EntityName }}
|
7
|
+
from ...domain.repositories.{{ repository_file }} import {{ RepositoryName }}Repository
|
8
|
+
from ..models.{{ model_file }} import {{ ModelName }}Model
|
9
|
+
|
10
|
+
|
11
|
+
class SQLAlchemy{{ RepositoryName }}Repository({{ RepositoryName }}Repository):
|
12
|
+
"""SQLAlchemy implementation of {{ RepositoryName }}Repository."""
|
13
|
+
|
14
|
+
def __init__(self, session: Session):
|
15
|
+
self._session = session
|
16
|
+
|
17
|
+
async def get_by_id(self, id: str) -> Optional[{{ EntityName }}]:
|
18
|
+
"""Retrieve {{ entity_name }} by ID."""
|
19
|
+
model = self._session.query({{ ModelName }}Model).filter(
|
20
|
+
{{ ModelName }}Model.id == id
|
21
|
+
).first()
|
22
|
+
|
23
|
+
return model.to_entity() if model else None
|
24
|
+
|
25
|
+
async def save(self, entity: {{ EntityName }}) -> {{ EntityName }}:
|
26
|
+
"""Save {{ entity_name }} entity."""
|
27
|
+
if entity.id:
|
28
|
+
# Update existing
|
29
|
+
model = self._session.query({{ ModelName }}Model).filter(
|
30
|
+
{{ ModelName }}Model.id == entity.id
|
31
|
+
).first()
|
32
|
+
|
33
|
+
if model:
|
34
|
+
# Update model from entity
|
35
|
+
for key, value in entity.__dict__.items():
|
36
|
+
if hasattr(model, key) and key != 'id':
|
37
|
+
setattr(model, key, value)
|
38
|
+
else:
|
39
|
+
model = {{ ModelName }}Model.from_entity(entity)
|
40
|
+
self._session.add(model)
|
41
|
+
else:
|
42
|
+
# Create new
|
43
|
+
import uuid
|
44
|
+
entity.id = str(uuid.uuid4())
|
45
|
+
model = {{ ModelName }}Model.from_entity(entity)
|
46
|
+
self._session.add(model)
|
47
|
+
|
48
|
+
self._session.commit()
|
49
|
+
self._session.refresh(model)
|
50
|
+
|
51
|
+
return model.to_entity()
|
52
|
+
|
53
|
+
async def delete(self, id: str) -> bool:
|
54
|
+
"""Delete {{ entity_name }} by ID."""
|
55
|
+
model = self._session.query({{ ModelName }}Model).filter(
|
56
|
+
{{ ModelName }}Model.id == id
|
57
|
+
).first()
|
58
|
+
|
59
|
+
if model:
|
60
|
+
self._session.delete(model)
|
61
|
+
self._session.commit()
|
62
|
+
return True
|
63
|
+
|
64
|
+
return False
|
65
|
+
|
66
|
+
async def list_all(self) -> List[{{ EntityName }}]:
|
67
|
+
"""List all {{ entity_name }} entities."""
|
68
|
+
models = self._session.query({{ ModelName }}Model).all()
|
69
|
+
return [model.to_entity() for model in models]
|
@@ -0,0 +1,38 @@
|
|
1
|
+
"""
|
2
|
+
{{ model_name }} model for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import Optional
|
6
|
+
from sqlalchemy import Column, String, DateTime, func
|
7
|
+
from sqlalchemy.ext.declarative import declarative_base
|
8
|
+
|
9
|
+
Base = declarative_base()
|
10
|
+
|
11
|
+
|
12
|
+
class {{ ModelName }}Model(Base):
|
13
|
+
"""SQLAlchemy model for {{ entity_name }}."""
|
14
|
+
|
15
|
+
__tablename__ = "{{ resource_name }}"
|
16
|
+
|
17
|
+
id = Column(String, primary_key=True)
|
18
|
+
created_at = Column(DateTime, default=func.now())
|
19
|
+
updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
|
20
|
+
|
21
|
+
def to_entity(self):
|
22
|
+
"""Convert model to domain entity."""
|
23
|
+
from ...domain.entities.{{ entity_file }} import {{ EntityName }}
|
24
|
+
|
25
|
+
return {{ EntityName }}(
|
26
|
+
id=self.id,
|
27
|
+
created_at=self.created_at,
|
28
|
+
updated_at=self.updated_at,
|
29
|
+
)
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def from_entity(cls, entity):
|
33
|
+
"""Create model from domain entity."""
|
34
|
+
return cls(
|
35
|
+
id=entity.id,
|
36
|
+
created_at=entity.created_at,
|
37
|
+
updated_at=entity.updated_at,
|
38
|
+
)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
"""
|
2
|
+
{{ query_name }} query for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Any, List, Optional
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class {{ QueryName }}Query:
|
10
|
+
"""Query for {{ query_name }} operation."""
|
11
|
+
|
12
|
+
def __post_init__(self):
|
13
|
+
"""Post-initialization validation."""
|
14
|
+
pass
|
15
|
+
|
16
|
+
|
17
|
+
class {{ QueryName }}Handler:
|
18
|
+
"""Handler for {{ QueryName }}Query."""
|
19
|
+
|
20
|
+
def __init__(self):
|
21
|
+
pass
|
22
|
+
|
23
|
+
async def handle(self, query: {{ QueryName }}Query) -> Any:
|
24
|
+
"""Handle the {{ query_name }} query."""
|
25
|
+
# Implementation here
|
26
|
+
pass
|
@@ -0,0 +1,57 @@
|
|
1
|
+
"""{{ repository_name }} repository interface for {{ module_name }} module."""
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from typing import List, Optional
|
4
|
+
from ..entities.{{ entity_file }} import {{ EntityName }}
|
5
|
+
|
6
|
+
|
7
|
+
class {{ RepositoryName }}Repository(ABC):
|
8
|
+
"""Abstract repository interface for {{ entity_name }} operations.
|
9
|
+
|
10
|
+
This is an abstract base class that enforces implementation of all methods
|
11
|
+
in concrete repository classes.
|
12
|
+
"""
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
async def get_by_id(self, id: str) -> Optional[{{ EntityName }}]:
|
16
|
+
"""Retrieve {{ entity_name }} by ID.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
id: The unique identifier of the {{ entity_name }}
|
20
|
+
|
21
|
+
Returns:
|
22
|
+
The {{ entity_name }} entity if found, None otherwise
|
23
|
+
"""
|
24
|
+
pass
|
25
|
+
|
26
|
+
@abstractmethod
|
27
|
+
async def save(self, entity: {{ EntityName }}) -> {{ EntityName }}:
|
28
|
+
"""Save {{ entity_name }} entity.
|
29
|
+
|
30
|
+
Args:
|
31
|
+
entity: The {{ entity_name }} entity to save
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
The saved {{ entity_name }} entity with updated fields
|
35
|
+
"""
|
36
|
+
pass
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
async def delete(self, id: str) -> bool:
|
40
|
+
"""Delete {{ entity_name }} by ID.
|
41
|
+
|
42
|
+
Args:
|
43
|
+
id: The unique identifier of the {{ entity_name }} to delete
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
True if deletion was successful, False otherwise
|
47
|
+
"""
|
48
|
+
pass
|
49
|
+
|
50
|
+
@abstractmethod
|
51
|
+
async def list_all(self) -> List[{{ EntityName }}]:
|
52
|
+
"""List all {{ entity_name }} entities.
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
List of all {{ entity_name }} entities
|
56
|
+
"""
|
57
|
+
pass
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""
|
2
|
+
{{ schema_name }} schemas for {{ module_name }} module.
|
3
|
+
"""
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import Optional
|
6
|
+
from pydantic import BaseModel, Field
|
7
|
+
|
8
|
+
|
9
|
+
class {{ SchemaName }}Base(BaseModel):
|
10
|
+
"""Base schema for {{ entity_name }}."""
|
11
|
+
pass
|
12
|
+
|
13
|
+
|
14
|
+
class {{ SchemaName }}Create({{ SchemaName }}Base):
|
15
|
+
"""Schema for creating {{ entity_name }}."""
|
16
|
+
pass
|
17
|
+
|
18
|
+
|
19
|
+
class {{ SchemaName }}Update({{ SchemaName }}Base):
|
20
|
+
"""Schema for updating {{ entity_name }}."""
|
21
|
+
pass
|
22
|
+
|
23
|
+
|
24
|
+
class {{ SchemaName }}Response({{ SchemaName }}Base):
|
25
|
+
"""Schema for {{ entity_name }} response."""
|
26
|
+
|
27
|
+
id: Optional[str] = None
|
28
|
+
created_at: Optional[datetime] = None
|
29
|
+
updated_at: Optional[datetime] = None
|
30
|
+
|
31
|
+
class Config:
|
32
|
+
from_attributes = True
|