vega-framework 0.1.0__tar.gz
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.
Potentially problematic release.
This version of vega-framework might be problematic. Click here for more details.
- vega_framework-0.1.0/LICENSE +21 -0
- vega_framework-0.1.0/PKG-INFO +116 -0
- vega_framework-0.1.0/README.md +89 -0
- vega_framework-0.1.0/pyproject.toml +77 -0
- vega_framework-0.1.0/vega/__init__.py +43 -0
- vega_framework-0.1.0/vega/cli/__init__.py +5 -0
- vega_framework-0.1.0/vega/cli/commands/__init__.py +1 -0
- vega_framework-0.1.0/vega/cli/commands/generate.py +337 -0
- vega_framework-0.1.0/vega/cli/commands/init.py +280 -0
- vega_framework-0.1.0/vega/cli/main.py +109 -0
- vega_framework-0.1.0/vega/cli/scaffolds/__init__.py +7 -0
- vega_framework-0.1.0/vega/cli/scaffolds/fastapi.py +108 -0
- vega_framework-0.1.0/vega/cli/templates/__init__.py +35 -0
- vega_framework-0.1.0/vega/cli/templates/components.py +322 -0
- vega_framework-0.1.0/vega/cli/utils/__init__.py +11 -0
- vega_framework-0.1.0/vega/cli/utils/messages.py +77 -0
- vega_framework-0.1.0/vega/cli/utils/naming.py +114 -0
- vega_framework-0.1.0/vega/cli/utils/validators.py +113 -0
- vega_framework-0.1.0/vega/di/__init__.py +52 -0
- vega_framework-0.1.0/vega/di/container.py +175 -0
- vega_framework-0.1.0/vega/di/decorators.py +276 -0
- vega_framework-0.1.0/vega/di/errors.py +6 -0
- vega_framework-0.1.0/vega/di/scope.py +148 -0
- vega_framework-0.1.0/vega/patterns/__init__.py +40 -0
- vega_framework-0.1.0/vega/patterns/interactor.py +76 -0
- vega_framework-0.1.0/vega/patterns/mediator.py +75 -0
- vega_framework-0.1.0/vega/patterns/repository.py +47 -0
- vega_framework-0.1.0/vega/patterns/service.py +42 -0
- vega_framework-0.1.0/vega/settings/__init__.py +34 -0
- vega_framework-0.1.0/vega/settings/base.py +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vega Framework Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: vega-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Lightweight Python framework for Clean Architecture with automatic dependency injection
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: clean-architecture,dependency-injection,framework,python,async,vega
|
|
7
|
+
Author: Roberto Ferro
|
|
8
|
+
Requires-Python: >=3.10,<4.0
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Dist: click (>=8.0,<9.0)
|
|
20
|
+
Requires-Dist: pydantic (>=2.0,<3.0)
|
|
21
|
+
Requires-Dist: pydantic-settings (>=2.0,<3.0)
|
|
22
|
+
Project-URL: Documentation, https://vega-framework.readthedocs.io
|
|
23
|
+
Project-URL: Homepage, https://github.com/your-org/vega-framework
|
|
24
|
+
Project-URL: Repository, https://github.com/your-org/vega-framework
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# Vega Framework
|
|
28
|
+
|
|
29
|
+
A lightweight Python framework for building applications with **Clean Architecture** principles.
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- ✅ **Automatic Dependency Injection** - Zero boilerplate, type-safe DI
|
|
34
|
+
- ✅ **Clean Architecture Patterns** - Interactor, Mediator, Repository, Service
|
|
35
|
+
- ✅ **Scope Management** - Singleton, Scoped, Transient lifetimes
|
|
36
|
+
- ✅ **Type-Safe** - Full type hints support
|
|
37
|
+
- ✅ **Framework-Agnostic** - Works with any domain (web, AI, IoT, fintech, etc.)
|
|
38
|
+
- ✅ **CLI Scaffolding** - Generate projects and components instantly
|
|
39
|
+
- ✅ **Lightweight** - No unnecessary dependencies
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install vega-framework
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Create new project
|
|
51
|
+
vega init my-app
|
|
52
|
+
|
|
53
|
+
# Generate components
|
|
54
|
+
vega generate entity User
|
|
55
|
+
vega generate repository UserRepository
|
|
56
|
+
vega generate interactor CreateUser
|
|
57
|
+
|
|
58
|
+
# Create FastAPI project
|
|
59
|
+
vega init my-api --template fastapi
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## CLI Commands
|
|
63
|
+
|
|
64
|
+
### Initialize Project
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
vega init <project_name> [--template basic|fastapi|ai-rag] [--path .]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Creates a new Vega project with Clean Architecture structure:
|
|
71
|
+
|
|
72
|
+
- `domain/` - Entities, repositories, services, interactors
|
|
73
|
+
- `application/` - Mediators and workflows
|
|
74
|
+
- `infrastructure/` - Repository and service implementations
|
|
75
|
+
- `config.py` - DI container setup
|
|
76
|
+
- `settings.py` - Application configuration
|
|
77
|
+
|
|
78
|
+
### Generate Components
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
vega generate entity <Name>
|
|
82
|
+
vega generate repository <Name> [--impl memory|sql]
|
|
83
|
+
vega generate service <Name>
|
|
84
|
+
vega generate interactor <Name>
|
|
85
|
+
vega generate mediator <Name>
|
|
86
|
+
vega generate web fastapi
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Validate Project
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
vega doctor [--path .]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Validates project structure, DI configuration, and architecture compliance.
|
|
96
|
+
|
|
97
|
+
## Use Cases
|
|
98
|
+
|
|
99
|
+
Perfect for:
|
|
100
|
+
|
|
101
|
+
- AI/RAG applications
|
|
102
|
+
- E-commerce platforms
|
|
103
|
+
- Fintech systems
|
|
104
|
+
- Mobile backends
|
|
105
|
+
- Microservices
|
|
106
|
+
- CLI tools
|
|
107
|
+
- Any Python application requiring clean architecture
|
|
108
|
+
|
|
109
|
+
## License
|
|
110
|
+
|
|
111
|
+
MIT
|
|
112
|
+
|
|
113
|
+
## Contributing
|
|
114
|
+
|
|
115
|
+
Contributions welcome! This framework is extracted from production code and battle-tested.
|
|
116
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Vega Framework
|
|
2
|
+
|
|
3
|
+
A lightweight Python framework for building applications with **Clean Architecture** principles.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **Automatic Dependency Injection** - Zero boilerplate, type-safe DI
|
|
8
|
+
- ✅ **Clean Architecture Patterns** - Interactor, Mediator, Repository, Service
|
|
9
|
+
- ✅ **Scope Management** - Singleton, Scoped, Transient lifetimes
|
|
10
|
+
- ✅ **Type-Safe** - Full type hints support
|
|
11
|
+
- ✅ **Framework-Agnostic** - Works with any domain (web, AI, IoT, fintech, etc.)
|
|
12
|
+
- ✅ **CLI Scaffolding** - Generate projects and components instantly
|
|
13
|
+
- ✅ **Lightweight** - No unnecessary dependencies
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install vega-framework
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Create new project
|
|
25
|
+
vega init my-app
|
|
26
|
+
|
|
27
|
+
# Generate components
|
|
28
|
+
vega generate entity User
|
|
29
|
+
vega generate repository UserRepository
|
|
30
|
+
vega generate interactor CreateUser
|
|
31
|
+
|
|
32
|
+
# Create FastAPI project
|
|
33
|
+
vega init my-api --template fastapi
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## CLI Commands
|
|
37
|
+
|
|
38
|
+
### Initialize Project
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
vega init <project_name> [--template basic|fastapi|ai-rag] [--path .]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Creates a new Vega project with Clean Architecture structure:
|
|
45
|
+
|
|
46
|
+
- `domain/` - Entities, repositories, services, interactors
|
|
47
|
+
- `application/` - Mediators and workflows
|
|
48
|
+
- `infrastructure/` - Repository and service implementations
|
|
49
|
+
- `config.py` - DI container setup
|
|
50
|
+
- `settings.py` - Application configuration
|
|
51
|
+
|
|
52
|
+
### Generate Components
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
vega generate entity <Name>
|
|
56
|
+
vega generate repository <Name> [--impl memory|sql]
|
|
57
|
+
vega generate service <Name>
|
|
58
|
+
vega generate interactor <Name>
|
|
59
|
+
vega generate mediator <Name>
|
|
60
|
+
vega generate web fastapi
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Validate Project
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
vega doctor [--path .]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Validates project structure, DI configuration, and architecture compliance.
|
|
70
|
+
|
|
71
|
+
## Use Cases
|
|
72
|
+
|
|
73
|
+
Perfect for:
|
|
74
|
+
|
|
75
|
+
- AI/RAG applications
|
|
76
|
+
- E-commerce platforms
|
|
77
|
+
- Fintech systems
|
|
78
|
+
- Mobile backends
|
|
79
|
+
- Microservices
|
|
80
|
+
- CLI tools
|
|
81
|
+
- Any Python application requiring clean architecture
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT
|
|
86
|
+
|
|
87
|
+
## Contributing
|
|
88
|
+
|
|
89
|
+
Contributions welcome! This framework is extracted from production code and battle-tested.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "vega-framework"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Lightweight Python framework for Clean Architecture with automatic dependency injection"
|
|
5
|
+
authors = ["Roberto Ferro"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/your-org/vega-framework"
|
|
9
|
+
repository = "https://github.com/your-org/vega-framework"
|
|
10
|
+
documentation = "https://vega-framework.readthedocs.io"
|
|
11
|
+
keywords = [
|
|
12
|
+
"clean-architecture",
|
|
13
|
+
"dependency-injection",
|
|
14
|
+
"framework",
|
|
15
|
+
"python",
|
|
16
|
+
"async",
|
|
17
|
+
"vega",
|
|
18
|
+
]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 3 - Alpha",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
28
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
29
|
+
]
|
|
30
|
+
packages = [{ include = "vega", from = "." }]
|
|
31
|
+
|
|
32
|
+
[tool.poetry.dependencies]
|
|
33
|
+
python = "^3.10"
|
|
34
|
+
pydantic = "^2.0"
|
|
35
|
+
pydantic-settings = "^2.0"
|
|
36
|
+
click = "^8.0"
|
|
37
|
+
|
|
38
|
+
[tool.poetry.group.dev.dependencies]
|
|
39
|
+
pytest = "^7.0"
|
|
40
|
+
pytest-asyncio = "^0.21"
|
|
41
|
+
pytest-cov = "^4.0"
|
|
42
|
+
black = "^23.0"
|
|
43
|
+
isort = "^5.0"
|
|
44
|
+
mypy = "^1.0"
|
|
45
|
+
ruff = "^0.1"
|
|
46
|
+
|
|
47
|
+
[tool.poetry.scripts]
|
|
48
|
+
vega = "vega.cli.main:cli"
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["poetry-core>=1.0.0"]
|
|
52
|
+
build-backend = "poetry.core.masonry.api"
|
|
53
|
+
|
|
54
|
+
[tool.black]
|
|
55
|
+
line-length = 100
|
|
56
|
+
target-version = ['py310']
|
|
57
|
+
|
|
58
|
+
[tool.isort]
|
|
59
|
+
profile = "black"
|
|
60
|
+
line_length = 100
|
|
61
|
+
|
|
62
|
+
[tool.mypy]
|
|
63
|
+
python_version = "3.10"
|
|
64
|
+
warn_return_any = true
|
|
65
|
+
warn_unused_configs = true
|
|
66
|
+
disallow_untyped_defs = true
|
|
67
|
+
|
|
68
|
+
[tool.pytest.ini_options]
|
|
69
|
+
asyncio_mode = "auto"
|
|
70
|
+
testpaths = ["tests"]
|
|
71
|
+
python_files = ["test_*.py"]
|
|
72
|
+
python_classes = ["Test*"]
|
|
73
|
+
python_functions = ["test_*"]
|
|
74
|
+
|
|
75
|
+
[tool.ruff]
|
|
76
|
+
line-length = 100
|
|
77
|
+
target-version = "py310"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CleanArch Framework
|
|
3
|
+
|
|
4
|
+
A lightweight Python framework for building applications with Clean Architecture.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Automatic Dependency Injection
|
|
8
|
+
- Clean Architecture patterns (Interactor, Mediator, Repository)
|
|
9
|
+
- Type-safe with Python type hints
|
|
10
|
+
- Scoped dependency management (Singleton, Scoped, Transient)
|
|
11
|
+
- CLI scaffolding tools
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
from vega.patterns import Interactor
|
|
15
|
+
from vega.di import bind
|
|
16
|
+
|
|
17
|
+
class CreateUser(Interactor[User]):
|
|
18
|
+
def __init__(self, name: str, email: str):
|
|
19
|
+
self.name = name
|
|
20
|
+
self.email = email
|
|
21
|
+
|
|
22
|
+
@bind
|
|
23
|
+
async def call(self, repository: UserRepository) -> User:
|
|
24
|
+
user = User(name=self.name, email=self.email)
|
|
25
|
+
return await repository.save(user)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
__version__ = "0.1.0"
|
|
29
|
+
__author__ = "CleanArch Contributors"
|
|
30
|
+
|
|
31
|
+
from vega.di import bind, injectable, Scope, scope_context
|
|
32
|
+
from vega.patterns import Interactor, Mediator, Repository, Service
|
|
33
|
+
|
|
34
|
+
__all__ = [
|
|
35
|
+
"bind",
|
|
36
|
+
"injectable",
|
|
37
|
+
"Scope",
|
|
38
|
+
"scope_context",
|
|
39
|
+
"Interactor",
|
|
40
|
+
"Mediator",
|
|
41
|
+
"Repository",
|
|
42
|
+
"Service",
|
|
43
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI commands"""
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""Generate command - Create components in Vega project"""
|
|
2
|
+
import click
|
|
3
|
+
import re
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from vega.cli.templates import (
|
|
7
|
+
render_entity,
|
|
8
|
+
render_infrastructure_repository,
|
|
9
|
+
render_infrastructure_service,
|
|
10
|
+
render_interactor,
|
|
11
|
+
render_mediator,
|
|
12
|
+
render_repository_interface,
|
|
13
|
+
render_service_interface,
|
|
14
|
+
)
|
|
15
|
+
from vega.cli.scaffolds import create_fastapi_scaffold
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def to_snake_case(name: str) -> str:
|
|
20
|
+
"""Convert CamelCase to snake_case"""
|
|
21
|
+
name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
|
|
22
|
+
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def to_pascal_case(name: str) -> str:
|
|
26
|
+
"""Convert strings to PascalCase, handling separators and camelCase input"""
|
|
27
|
+
cleaned = name.strip()
|
|
28
|
+
if not cleaned:
|
|
29
|
+
return ""
|
|
30
|
+
|
|
31
|
+
# Normalize common separators to spaces
|
|
32
|
+
normalized = cleaned.replace('-', ' ').replace('_', ' ')
|
|
33
|
+
if ' ' in normalized:
|
|
34
|
+
parts = normalized.split()
|
|
35
|
+
else:
|
|
36
|
+
parts = re.findall(r'[A-Z]+(?=$|[A-Z][a-z0-9])|[A-Z]?[a-z0-9]+|[0-9]+', cleaned)
|
|
37
|
+
if not parts:
|
|
38
|
+
parts = [cleaned]
|
|
39
|
+
|
|
40
|
+
def _pascal_piece(piece: str) -> str:
|
|
41
|
+
return piece if piece.isupper() else piece[:1].upper() + piece[1:].lower()
|
|
42
|
+
|
|
43
|
+
return ''.join(_pascal_piece(part) for part in parts if part)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _resolve_implementation_names(class_name: str, implementation: str) -> tuple[str, str]:
|
|
47
|
+
"""Derive implementation class and file names from flag input."""
|
|
48
|
+
impl_pascal = to_pascal_case(implementation) or "Impl"
|
|
49
|
+
base = class_name
|
|
50
|
+
|
|
51
|
+
if impl_pascal.lower() in {"impl", "implementation"}:
|
|
52
|
+
impl_class = f"{base}{impl_pascal}"
|
|
53
|
+
elif base.lower().startswith(impl_pascal.lower()):
|
|
54
|
+
impl_class = base
|
|
55
|
+
else:
|
|
56
|
+
impl_class = f"{impl_pascal}{base}"
|
|
57
|
+
|
|
58
|
+
impl_file = to_snake_case(impl_class)
|
|
59
|
+
return impl_class, impl_file
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def generate_component(
|
|
63
|
+
component_type: str,
|
|
64
|
+
name: str,
|
|
65
|
+
project_path: str,
|
|
66
|
+
implementation: str | None = None,
|
|
67
|
+
):
|
|
68
|
+
"""Generate a component in the Vega project"""
|
|
69
|
+
|
|
70
|
+
project_root = Path(project_path).resolve()
|
|
71
|
+
|
|
72
|
+
# Check if we're in a Vega project
|
|
73
|
+
if not (project_root / "config.py").exists():
|
|
74
|
+
click.echo(click.style("ERROR: Error: Not a Vega project (config.py not found)", fg='red'))
|
|
75
|
+
click.echo(" Run this command from your project root, or use --path option")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Get project name from directory
|
|
79
|
+
project_name = project_root.name
|
|
80
|
+
|
|
81
|
+
class_name = to_pascal_case(name)
|
|
82
|
+
implementation = implementation.strip() if implementation else None
|
|
83
|
+
if component_type == "web":
|
|
84
|
+
_generate_fastapi_web(project_root, project_name, name)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
suffixes = {
|
|
89
|
+
"repository": "Repository",
|
|
90
|
+
"service": "Service",
|
|
91
|
+
"mediator": "Mediator",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if implementation and component_type not in {'repository', 'service'}:
|
|
95
|
+
click.echo(
|
|
96
|
+
click.style(
|
|
97
|
+
"WARNING: Implementation option is only supported for repositories and services",
|
|
98
|
+
fg='yellow',
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
implementation = None
|
|
102
|
+
|
|
103
|
+
if component_type in suffixes:
|
|
104
|
+
suffix = suffixes[component_type]
|
|
105
|
+
if class_name.lower().endswith(suffix.lower()):
|
|
106
|
+
class_name = f"{class_name[:-len(suffix)]}{suffix}"
|
|
107
|
+
else:
|
|
108
|
+
class_name = f"{class_name}{suffix}"
|
|
109
|
+
|
|
110
|
+
file_name = to_snake_case(class_name)
|
|
111
|
+
|
|
112
|
+
if component_type == 'entity':
|
|
113
|
+
_generate_entity(project_root, project_name, class_name, file_name)
|
|
114
|
+
elif component_type == 'repository':
|
|
115
|
+
_generate_repository(project_root, project_name, class_name, file_name, implementation)
|
|
116
|
+
elif component_type == 'service':
|
|
117
|
+
_generate_service(project_root, project_name, class_name, file_name, implementation)
|
|
118
|
+
elif component_type == 'interactor':
|
|
119
|
+
_generate_interactor(project_root, project_name, class_name, file_name)
|
|
120
|
+
elif component_type == 'mediator':
|
|
121
|
+
_generate_mediator(project_root, project_name, class_name, file_name)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _generate_entity(project_root: Path, project_name: str, class_name: str, file_name: str):
|
|
125
|
+
"""Generate domain entity"""
|
|
126
|
+
|
|
127
|
+
file_path = project_root / "domain" / "entities" / f"{file_name}.py"
|
|
128
|
+
|
|
129
|
+
if file_path.exists():
|
|
130
|
+
click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
content = render_entity(class_name)
|
|
134
|
+
|
|
135
|
+
file_path.write_text(content)
|
|
136
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _generate_repository(
|
|
140
|
+
project_root: Path,
|
|
141
|
+
project_name: str,
|
|
142
|
+
class_name: str,
|
|
143
|
+
file_name: str,
|
|
144
|
+
implementation: str | None = None,
|
|
145
|
+
):
|
|
146
|
+
"""Generate repository interface"""
|
|
147
|
+
|
|
148
|
+
# Remove 'Repository' suffix if present to get entity name
|
|
149
|
+
entity_name = class_name[:-len('Repository')] if class_name.endswith('Repository') else class_name
|
|
150
|
+
entity_file = to_snake_case(entity_name)
|
|
151
|
+
|
|
152
|
+
file_path = project_root / "domain" / "repositories" / f"{file_name}.py"
|
|
153
|
+
|
|
154
|
+
if file_path.exists():
|
|
155
|
+
click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
# Check if entity exists
|
|
159
|
+
entity_path = project_root / "domain" / "entities" / f"{entity_file}.py"
|
|
160
|
+
if not entity_path.exists():
|
|
161
|
+
click.echo(
|
|
162
|
+
click.style(
|
|
163
|
+
f"⚠️ Warning: Entity {entity_name} does not exist at {entity_path.relative_to(project_root)}",
|
|
164
|
+
fg='yellow',
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if click.confirm(f"Do you want to create the entity {entity_name}?", default=True):
|
|
169
|
+
_generate_entity(project_root, project_name, entity_name, entity_file)
|
|
170
|
+
click.echo() # Empty line for readability
|
|
171
|
+
else:
|
|
172
|
+
click.echo(click.style(f"ERROR: Cannot create repository without entity {entity_name}", fg='red'))
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
content = render_repository_interface(class_name, entity_name, entity_file)
|
|
176
|
+
|
|
177
|
+
file_path.write_text(content)
|
|
178
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
179
|
+
|
|
180
|
+
# Suggest next steps
|
|
181
|
+
click.echo(f"\n💡 Next steps:")
|
|
182
|
+
click.echo(f" 1. Create entity: vega generate entity {entity_name}")
|
|
183
|
+
click.echo(f" 2. Implement repository in infrastructure/repositories/")
|
|
184
|
+
click.echo(f" 3. Register in config.py SERVICES dict")
|
|
185
|
+
|
|
186
|
+
if implementation:
|
|
187
|
+
_generate_infrastructure_repository(
|
|
188
|
+
project_root,
|
|
189
|
+
class_name,
|
|
190
|
+
file_name,
|
|
191
|
+
entity_name,
|
|
192
|
+
entity_file,
|
|
193
|
+
implementation,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _generate_service(
|
|
198
|
+
project_root: Path,
|
|
199
|
+
project_name: str,
|
|
200
|
+
class_name: str,
|
|
201
|
+
file_name: str,
|
|
202
|
+
implementation: str | None = None,
|
|
203
|
+
):
|
|
204
|
+
"""Generate service interface"""
|
|
205
|
+
|
|
206
|
+
file_path = project_root / "domain" / "services" / f"{file_name}.py"
|
|
207
|
+
|
|
208
|
+
if file_path.exists():
|
|
209
|
+
click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
content = render_service_interface(class_name)
|
|
213
|
+
|
|
214
|
+
file_path.write_text(content)
|
|
215
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
216
|
+
|
|
217
|
+
click.echo(f"\n💡 Next steps:")
|
|
218
|
+
click.echo(f" 1. Implement service in infrastructure/services/")
|
|
219
|
+
click.echo(f" 2. Register in config.py SERVICES dict")
|
|
220
|
+
|
|
221
|
+
if implementation:
|
|
222
|
+
_generate_infrastructure_service(
|
|
223
|
+
project_root,
|
|
224
|
+
class_name,
|
|
225
|
+
file_name,
|
|
226
|
+
implementation,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _generate_interactor(project_root: Path, project_name: str, class_name: str, file_name: str):
|
|
231
|
+
"""Generate interactor (use case)"""
|
|
232
|
+
|
|
233
|
+
# Try to infer entity from name (e.g., CreateUser -> User)
|
|
234
|
+
entity_name = class_name
|
|
235
|
+
for prefix in ['Create', 'Update', 'Delete', 'Get', 'List', 'Find']:
|
|
236
|
+
if class_name.startswith(prefix):
|
|
237
|
+
entity_name = class_name[len(prefix):]
|
|
238
|
+
break
|
|
239
|
+
|
|
240
|
+
entity_file = to_snake_case(entity_name)
|
|
241
|
+
|
|
242
|
+
file_path = project_root / "domain" / "interactors" / f"{file_name}.py"
|
|
243
|
+
|
|
244
|
+
if file_path.exists():
|
|
245
|
+
click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
|
|
246
|
+
return
|
|
247
|
+
|
|
248
|
+
content = render_interactor(class_name, entity_name, entity_file)
|
|
249
|
+
|
|
250
|
+
file_path.write_text(content)
|
|
251
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
252
|
+
|
|
253
|
+
click.echo(f"\n💡 Usage:")
|
|
254
|
+
click.echo(f" result = await {class_name}(param=value)")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _generate_mediator(project_root: Path, project_name: str, class_name: str, file_name: str):
|
|
258
|
+
"""Generate mediator (workflow)"""
|
|
259
|
+
|
|
260
|
+
file_path = project_root / "application" / "mediators" / f"{file_name}.py"
|
|
261
|
+
|
|
262
|
+
if file_path.exists():
|
|
263
|
+
click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
content = render_mediator(class_name)
|
|
267
|
+
|
|
268
|
+
file_path.write_text(content)
|
|
269
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
270
|
+
|
|
271
|
+
click.echo(f"\n💡 Usage:")
|
|
272
|
+
click.echo(f" result = await {class_name}(param=value)")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def _generate_infrastructure_repository(
|
|
276
|
+
project_root: Path,
|
|
277
|
+
interface_class_name: str,
|
|
278
|
+
interface_file_name: str,
|
|
279
|
+
entity_name: str,
|
|
280
|
+
entity_file: str,
|
|
281
|
+
implementation: str,
|
|
282
|
+
) -> None:
|
|
283
|
+
"""Generate infrastructure repository implementation extending the domain interface."""
|
|
284
|
+
impl_class, impl_file = _resolve_implementation_names(interface_class_name, implementation)
|
|
285
|
+
file_path = project_root / "infrastructure" / "repositories" / f"{impl_file}.py"
|
|
286
|
+
|
|
287
|
+
if file_path.exists():
|
|
288
|
+
click.echo(click.style(f"WARNING: Implementation {file_path} already exists", fg='yellow'))
|
|
289
|
+
return
|
|
290
|
+
|
|
291
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
292
|
+
|
|
293
|
+
content = render_infrastructure_repository(
|
|
294
|
+
impl_class,
|
|
295
|
+
interface_class_name,
|
|
296
|
+
interface_file_name,
|
|
297
|
+
entity_name,
|
|
298
|
+
entity_file,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
file_path.write_text(content)
|
|
302
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _generate_infrastructure_service(
|
|
306
|
+
project_root: Path,
|
|
307
|
+
interface_class_name: str,
|
|
308
|
+
interface_file_name: str,
|
|
309
|
+
implementation: str,
|
|
310
|
+
) -> None:
|
|
311
|
+
"""Generate infrastructure service implementation extending the domain interface."""
|
|
312
|
+
impl_class, impl_file = _resolve_implementation_names(interface_class_name, implementation)
|
|
313
|
+
file_path = project_root / "infrastructure" / "services" / f"{impl_file}.py"
|
|
314
|
+
|
|
315
|
+
if file_path.exists():
|
|
316
|
+
click.echo(click.style(f"WARNING: Implementation {file_path} already exists", fg='yellow'))
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
320
|
+
|
|
321
|
+
content = render_infrastructure_service(
|
|
322
|
+
impl_class,
|
|
323
|
+
interface_class_name,
|
|
324
|
+
interface_file_name,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
file_path.write_text(content)
|
|
328
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
|
329
|
+
|
|
330
|
+
def _generate_fastapi_web(project_root: Path, project_name: str, name: str) -> None:
|
|
331
|
+
"""Generate FastAPI web scaffold"""
|
|
332
|
+
if name.lower() not in {"fastapi", "fast-api"}:
|
|
333
|
+
click.echo(click.style("ERROR: Unsupported web scaffold. Use: vega generate web fastapi", fg='red'))
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
create_fastapi_scaffold(project_root, project_name)
|
|
337
|
+
|