vega-framework 0.2.0__tar.gz → 0.2.2__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.
- {vega_framework-0.2.0 → vega_framework-0.2.2}/PKG-INFO +1 -1
- {vega_framework-0.2.0 → vega_framework-0.2.2}/pyproject.toml +1 -1
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/generate.py +15 -15
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/web.py +8 -7
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/ARCHITECTURE.md.j2 +13 -13
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/README.md.j2 +5 -5
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/main.py.j2 +2 -3
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/middleware.py.j2 +3 -3
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/routes_init.py.j2 +3 -3
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/__init__.py +8 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/application.py +63 -0
- vega_framework-0.2.2/vega/web/docs.py +104 -0
- vega_framework-0.2.2/vega/web/openapi.py +292 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/LICENSE +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/README.md +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/add.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/init.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/migrate.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/commands/update.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/main.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/scaffolds/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/scaffolds/fastapi.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/scaffolds/sqlalchemy.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/scaffolds/vega_web.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/cli/command.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/cli/command_simple.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/cli/commands_init.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/components.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/entity.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/event.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/event_handler.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/interactor.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/mediator.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/repository_interface.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/service_interface.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/infrastructure/model.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/infrastructure/repository_impl.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/infrastructure/service_impl.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/loader.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/.env.example +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/.gitignore +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/config.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/events_init.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/main.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/main_fastapi.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/main_standard.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/pyproject.toml.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/settings.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/sqlalchemy/alembic.ini.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/sqlalchemy/database_manager.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/sqlalchemy/env.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/sqlalchemy/script.py.mako +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/__init__.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/app.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/health_route.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/models_init.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/request_model.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/response_model.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/router.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/routes_init_autodiscovery.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/user_models.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/users_route.py.j2 +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/utils/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/utils/async_support.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/utils/messages.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/utils/naming.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/utils/validators.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/di/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/di/container.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/di/decorators.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/di/errors.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/di/scope.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/discovery/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/discovery/commands.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/discovery/events.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/discovery/routes.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/README.md +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/SYNTAX_GUIDE.md +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/bus.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/decorators.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/event.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/events/middleware.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/patterns/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/patterns/interactor.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/patterns/mediator.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/patterns/repository.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/patterns/service.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/settings/__init__.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/settings/base.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/builtin_middlewares.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/exceptions.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/middleware.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/request.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/response.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/route_middleware.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/router.py +0 -0
- {vega_framework-0.2.0 → vega_framework-0.2.2}/vega/web/routing.py +0 -0
@@ -394,13 +394,13 @@ def _register_router_in_init(project_root: Path, resource_file: str, resource_na
|
|
394
394
|
|
395
395
|
|
396
396
|
def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
397
|
-
"""Generate a
|
397
|
+
"""Generate a Vega Web router for a resource"""
|
398
398
|
|
399
399
|
# Check if web folder exists
|
400
400
|
web_path = project_root / "presentation" / "web"
|
401
401
|
if not web_path.exists():
|
402
402
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
403
|
-
click.echo(" Router generation requires
|
403
|
+
click.echo(" Router generation requires Vega Web module")
|
404
404
|
click.echo(" Install it with: vega add web")
|
405
405
|
return
|
406
406
|
|
@@ -440,13 +440,13 @@ def _generate_router(project_root: Path, project_name: str, name: str) -> None:
|
|
440
440
|
|
441
441
|
|
442
442
|
def _generate_web_models(project_root: Path, project_name: str, name: str, is_request: bool, is_response: bool) -> None:
|
443
|
-
"""Generate Pydantic request or response model for
|
443
|
+
"""Generate Pydantic request or response model for Vega Web"""
|
444
444
|
|
445
445
|
# Check if web folder exists
|
446
446
|
web_path = project_root / "presentation" / "web"
|
447
447
|
if not web_path.exists():
|
448
448
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
449
|
-
click.echo(" Model generation requires
|
449
|
+
click.echo(" Model generation requires Vega Web module")
|
450
450
|
click.echo(" Install it with: vega add web")
|
451
451
|
return
|
452
452
|
|
@@ -531,13 +531,13 @@ def _generate_web_models(project_root: Path, project_name: str, name: str, is_re
|
|
531
531
|
|
532
532
|
|
533
533
|
def _generate_middleware(project_root: Path, project_name: str, class_name: str, file_name: str) -> None:
|
534
|
-
"""Generate a
|
534
|
+
"""Generate a Vega Web middleware"""
|
535
535
|
|
536
536
|
# Check if web folder exists
|
537
537
|
web_path = project_root / "presentation" / "web"
|
538
538
|
if not web_path.exists():
|
539
539
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
540
|
-
click.echo(" Middleware generation requires
|
540
|
+
click.echo(" Middleware generation requires Vega Web module")
|
541
541
|
click.echo(" Install it with: vega add web")
|
542
542
|
return
|
543
543
|
|
@@ -554,7 +554,7 @@ def _generate_middleware(project_root: Path, project_name: str, class_name: str,
|
|
554
554
|
# Check if __init__.py exists
|
555
555
|
init_file = middleware_path / "__init__.py"
|
556
556
|
if not init_file.exists():
|
557
|
-
init_file.write_text('"""
|
557
|
+
init_file.write_text('"""Vega Web Middlewares"""\n')
|
558
558
|
click.echo(f"+ Created {click.style(str(init_file.relative_to(project_root)), fg='green')}")
|
559
559
|
|
560
560
|
# Generate middleware file
|
@@ -589,8 +589,8 @@ def _register_middleware_in_app(project_root: Path, class_name: str, file_name:
|
|
589
589
|
click.echo(click.style(f'''
|
590
590
|
from .middleware.{file_name} import {class_name}Middleware
|
591
591
|
|
592
|
-
def create_app() ->
|
593
|
-
app =
|
592
|
+
def create_app() -> VegaApp:
|
593
|
+
app = VegaApp(...)
|
594
594
|
app.add_middleware({class_name}Middleware)
|
595
595
|
app.include_router(get_api_router())
|
596
596
|
return app
|
@@ -619,9 +619,9 @@ def create_app() -> FastAPI:
|
|
619
619
|
break
|
620
620
|
|
621
621
|
if not import_added:
|
622
|
-
# Fallback: add after
|
622
|
+
# Fallback: add after VegaApp import
|
623
623
|
for i, line in enumerate(lines):
|
624
|
-
if 'from
|
624
|
+
if 'from vega.web import' in line:
|
625
625
|
lines.insert(i + 1, middleware_import)
|
626
626
|
lines.insert(i + 2, '')
|
627
627
|
break
|
@@ -629,8 +629,8 @@ def create_app() -> FastAPI:
|
|
629
629
|
# Find create_app function and add middleware registration
|
630
630
|
middleware_added = False
|
631
631
|
for i, line in enumerate(lines):
|
632
|
-
if 'app =
|
633
|
-
# Find the end of
|
632
|
+
if 'app = VegaApp(' in line:
|
633
|
+
# Find the end of VegaApp initialization
|
634
634
|
j = i + 1
|
635
635
|
while j < len(lines) and not lines[j].strip().startswith('app.include_router'):
|
636
636
|
j += 1
|
@@ -649,8 +649,8 @@ def create_app() -> FastAPI:
|
|
649
649
|
click.echo(click.style(f'''
|
650
650
|
from .middleware.{file_name} import {class_name}Middleware
|
651
651
|
|
652
|
-
def create_app() ->
|
653
|
-
app =
|
652
|
+
def create_app() -> VegaApp:
|
653
|
+
app = VegaApp(...)
|
654
654
|
app.add_middleware({class_name}Middleware)
|
655
655
|
app.include_router(get_api_router())
|
656
656
|
return app
|
@@ -1,4 +1,4 @@
|
|
1
|
-
"""Web command - Manage
|
1
|
+
"""Web command - Manage Vega Web server"""
|
2
2
|
import sys
|
3
3
|
from pathlib import Path
|
4
4
|
|
@@ -7,7 +7,7 @@ import click
|
|
7
7
|
|
8
8
|
@click.group()
|
9
9
|
def web():
|
10
|
-
"""Manage
|
10
|
+
"""Manage Vega Web server
|
11
11
|
|
12
12
|
Commands to manage the web server for your Vega project.
|
13
13
|
The web module must be added to the project first using 'vega add web'.
|
@@ -21,7 +21,7 @@ def web():
|
|
21
21
|
@click.option('--reload', is_flag=True, help='Enable auto-reload')
|
22
22
|
@click.option('--path', default='.', help='Path to Vega project (default: current directory)')
|
23
23
|
def run(host: str, port: int, reload: bool, path: str):
|
24
|
-
"""Start the
|
24
|
+
"""Start the Vega Web server
|
25
25
|
|
26
26
|
Examples:
|
27
27
|
vega web run
|
@@ -42,7 +42,7 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
42
42
|
web_main = project_path / "presentation" / "web" / "main.py"
|
43
43
|
if not web_main.exists():
|
44
44
|
click.echo(click.style("ERROR: Web module not found", fg='red'))
|
45
|
-
click.echo("\nThe
|
45
|
+
click.echo("\nThe Vega Web module is not available in this project.")
|
46
46
|
click.echo("Add it using:")
|
47
47
|
click.echo(click.style(" vega add web", fg='cyan', bold=True))
|
48
48
|
sys.exit(1)
|
@@ -56,8 +56,9 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
56
56
|
import uvicorn
|
57
57
|
except ImportError:
|
58
58
|
click.echo(click.style("ERROR: uvicorn not installed", fg='red'))
|
59
|
-
click.echo("\
|
60
|
-
click.echo(
|
59
|
+
click.echo("\nUvicorn is required but not installed.")
|
60
|
+
click.echo("It should be included with vega-framework, but you can also install it with:")
|
61
|
+
click.echo(click.style(" poetry add uvicorn[standard]", fg='cyan', bold=True))
|
61
62
|
sys.exit(1)
|
62
63
|
|
63
64
|
# Initialize DI container first
|
@@ -73,7 +74,7 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
73
74
|
try:
|
74
75
|
from presentation.web.main import app
|
75
76
|
except ImportError as e:
|
76
|
-
click.echo(click.style("ERROR: Failed to import
|
77
|
+
click.echo(click.style("ERROR: Failed to import Vega Web app", fg='red'))
|
77
78
|
click.echo(f"\nDetails: {e}")
|
78
79
|
click.echo("\nMake sure:")
|
79
80
|
click.echo(" 1. You are in the project directory or use --path")
|
@@ -38,7 +38,7 @@ The innermost layer containing pure business logic, completely independent of an
|
|
38
38
|
**Rules:**
|
39
39
|
|
40
40
|
- ✅ **NO** dependencies on any other layer
|
41
|
-
- ✅ **NO** framework-specific code (no
|
41
|
+
- ✅ **NO** framework-specific code (no Vega Web, SQLAlchemy, etc.)
|
42
42
|
- ✅ **NO** infrastructure details (no database, HTTP, file system)
|
43
43
|
- ✅ Pure business logic only
|
44
44
|
- ✅ Only defines **interfaces**, never concrete implementations
|
@@ -90,7 +90,7 @@ Handles user interaction and external communication, acting as the entry point t
|
|
90
90
|
|
91
91
|
**Contains:**
|
92
92
|
|
93
|
-
- **Web API** -
|
93
|
+
- **Web API** - Vega Web routes, controllers, request/response models (when web is enabled)
|
94
94
|
- **CLI** - Command-line interface commands and argument parsing
|
95
95
|
- **GraphQL/gRPC** - Alternative API implementations (if needed)
|
96
96
|
- **WebSockets** - Real-time communication handlers (if needed)
|
@@ -108,7 +108,7 @@ Handles user interaction and external communication, acting as the entry point t
|
|
108
108
|
**Examples:**
|
109
109
|
|
110
110
|
- **CLI**: Uses Click/Typer to define commands that invoke interactors or mediators
|
111
|
-
- **Web API**:
|
111
|
+
- **Web API**: Vega Web endpoints that receive HTTP requests and call domain use cases
|
112
112
|
- **Both**: Can coexist in the same application, sharing the same business logic
|
113
113
|
|
114
114
|
## Core Patterns
|
@@ -344,7 +344,7 @@ The generator will interactively prompt for:
|
|
344
344
|
|
345
345
|
Commands are **automatically discovered** from `presentation/cli/commands/` - no manual registration required.
|
346
346
|
|
347
|
-
**
|
347
|
+
**Vega Web Routers** - HTTP API endpoints (requires web support):
|
348
348
|
```bash
|
349
349
|
vega generate router User
|
350
350
|
vega generate router Product
|
@@ -353,7 +353,7 @@ vega generate router Order
|
|
353
353
|
|
354
354
|
Routers are **automatically discovered** from `presentation/web/routes/` - no manual registration required.
|
355
355
|
|
356
|
-
**
|
356
|
+
**Vega Web Middleware** - Request/response processing (requires web support):
|
357
357
|
```bash
|
358
358
|
vega generate middleware Logging
|
359
359
|
vega generate middleware Authentication
|
@@ -362,12 +362,12 @@ vega generate middleware RateLimiting
|
|
362
362
|
|
363
363
|
### Adding Features to Existing Projects
|
364
364
|
|
365
|
-
**Add
|
365
|
+
**Add Vega Web Support**:
|
366
366
|
```bash
|
367
367
|
vega add web
|
368
368
|
```
|
369
369
|
|
370
|
-
Creates complete
|
370
|
+
Creates complete Vega Web scaffold:
|
371
371
|
- `presentation/web/` directory structure
|
372
372
|
- Routes and middleware setup
|
373
373
|
- Health check endpoints
|
@@ -482,9 +482,9 @@ vega doctor
|
|
482
482
|
| `vega generate interactor <Name>` | Generate use case |
|
483
483
|
| `vega generate mediator <Name>` | Generate workflow |
|
484
484
|
| `vega generate command <Name>` | Generate CLI command |
|
485
|
-
| `vega generate router <Name>` | Generate
|
485
|
+
| `vega generate router <Name>` | Generate Vega Web router |
|
486
486
|
| `vega generate model <Name>` | Generate SQLAlchemy model |
|
487
|
-
| `vega add web` | Add
|
487
|
+
| `vega add web` | Add Vega Web support |
|
488
488
|
| `vega add sqlalchemy` | Add database support |
|
489
489
|
| `vega migrate <command>` | Manage database migrations |
|
490
490
|
| `vega doctor` | Validate project architecture |
|
@@ -519,9 +519,9 @@ vega generate router Product
|
|
519
519
|
|
520
520
|
This creates `presentation/web/routes/product.py`:
|
521
521
|
```python
|
522
|
-
from
|
522
|
+
from vega.web import Router
|
523
523
|
|
524
|
-
router =
|
524
|
+
router = Router() # MUST be named 'router'
|
525
525
|
|
526
526
|
@router.get("/")
|
527
527
|
async def list_products():
|
@@ -682,7 +682,7 @@ Vega projects follow a standard 4-layer structure:
|
|
682
682
|
│ └── sendgrid_email_service.py # Sendgrid implementation
|
683
683
|
│
|
684
684
|
├── presentation/ # 🟠 PRESENTATION LAYER (Delivery Mechanisms)
|
685
|
-
│ ├── web/ #
|
685
|
+
│ ├── web/ # Vega Web interface (if enabled)
|
686
686
|
│ │ ├── __init__.py
|
687
687
|
│ │ ├── routes/
|
688
688
|
│ │ │ └── user_routes.py
|
@@ -833,7 +833,7 @@ async def create_user(name: str, email: str):
|
|
833
833
|
|
834
834
|
**Benefits:**
|
835
835
|
- Execute async interactors directly in CLI commands
|
836
|
-
- Same business logic works in both CLI and Web (
|
836
|
+
- Same business logic works in both CLI and Web (Vega Web) contexts
|
837
837
|
- Clean async/await syntax
|
838
838
|
- Automatic asyncio event loop management
|
839
839
|
|
@@ -20,9 +20,9 @@ Vega Framework application with Clean Architecture.
|
|
20
20
|
│ └── services/ # Service implementations
|
21
21
|
│
|
22
22
|
├── presentation/ # Delivery mechanisms
|
23
|
-
│ ├── web/ #
|
23
|
+
│ ├── web/ # Vega Web interface (if added)
|
24
24
|
│ │ ├── routes/ # HTTP endpoints
|
25
|
-
│ │ ├── app.py #
|
25
|
+
│ │ ├── app.py # Vega app factory
|
26
26
|
│ │ └── main.py # ASGI entrypoint
|
27
27
|
│ └── cli/ # CLI commands
|
28
28
|
│
|
@@ -45,7 +45,7 @@ cp .env.example .env
|
|
45
45
|
python main.py hello
|
46
46
|
python main.py greet --name John
|
47
47
|
|
48
|
-
# If using
|
48
|
+
# If using web template, run the web server
|
49
49
|
vega web run
|
50
50
|
vega web run --reload # With auto-reload
|
51
51
|
# Visit http://localhost:8000/api/health/status
|
@@ -81,7 +81,7 @@ vega generate command ListUsers --impl sync
|
|
81
81
|
|
82
82
|
### Add Features
|
83
83
|
|
84
|
-
Add
|
84
|
+
Add Vega Web support to your project:
|
85
85
|
|
86
86
|
```bash
|
87
87
|
vega add web
|
@@ -140,7 +140,7 @@ async def create_user(name: str):
|
|
140
140
|
click.echo(f"Created: {user.name}")
|
141
141
|
```
|
142
142
|
|
143
|
-
This allows the same async business logic to work in both CLI and web contexts (
|
143
|
+
This allows the same async business logic to work in both CLI and web contexts (Vega Web).
|
144
144
|
|
145
145
|
## Project Commands Quick Reference
|
146
146
|
|
@@ -1,5 +1,4 @@
|
|
1
|
-
"""
|
2
|
-
from fastapi import FastAPI
|
1
|
+
"""Vega Web ASGI entrypoint for {{ project_name }}"""
|
3
2
|
import config # noqa: F401 - Import to initialize DI container
|
4
3
|
|
5
4
|
# Auto-discover event subscribers so @subscribe handlers are ready
|
@@ -12,7 +11,7 @@ else:
|
|
12
11
|
|
13
12
|
from .app import create_app
|
14
13
|
|
15
|
-
app
|
14
|
+
app = create_app()
|
16
15
|
|
17
16
|
|
18
17
|
if __name__ == "__main__":
|
@@ -1,5 +1,5 @@
|
|
1
|
-
"""{{ class_name }} middleware for
|
2
|
-
from
|
1
|
+
"""{{ class_name }} middleware for Vega Web application"""
|
2
|
+
from vega.web import Request, Response
|
3
3
|
from starlette.middleware.base import BaseHTTPMiddleware
|
4
4
|
from starlette.types import ASGIApp
|
5
5
|
import time
|
@@ -14,7 +14,7 @@ class {{ class_name }}Middleware(BaseHTTPMiddleware):
|
|
14
14
|
and responses before they're returned to clients.
|
15
15
|
|
16
16
|
Usage:
|
17
|
-
Add to your
|
17
|
+
Add to your Vega Web app in app.py:
|
18
18
|
from .middleware.{{ file_name }} import {{ class_name }}Middleware
|
19
19
|
app.add_middleware({{ class_name }}Middleware)
|
20
20
|
"""
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"""API routers aggregation"""
|
2
|
-
from
|
2
|
+
from vega.web import Router
|
3
3
|
|
4
4
|
from . import health, users
|
5
5
|
|
@@ -7,9 +7,9 @@ from . import health, users
|
|
7
7
|
API_PREFIX = "/api"
|
8
8
|
|
9
9
|
|
10
|
-
def get_api_router() ->
|
10
|
+
def get_api_router() -> Router:
|
11
11
|
"""Return application API router"""
|
12
|
-
router =
|
12
|
+
router = Router(prefix=API_PREFIX)
|
13
13
|
router.include_router(health.router, tags=["health"], prefix="/health")
|
14
14
|
router.include_router(users.router, tags=["users"], prefix="/users")
|
15
15
|
return router
|
@@ -66,6 +66,10 @@ from .route_middleware import (
|
|
66
66
|
middleware,
|
67
67
|
)
|
68
68
|
|
69
|
+
# OpenAPI / Documentation
|
70
|
+
from .openapi import get_openapi_schema
|
71
|
+
from .docs import get_swagger_ui_html, get_redoc_html
|
72
|
+
|
69
73
|
__all__ = [
|
70
74
|
# Version
|
71
75
|
"__version__",
|
@@ -97,4 +101,8 @@ __all__ = [
|
|
97
101
|
"RouteMiddleware",
|
98
102
|
"MiddlewarePhase",
|
99
103
|
"middleware",
|
104
|
+
# OpenAPI / Docs
|
105
|
+
"get_openapi_schema",
|
106
|
+
"get_swagger_ui_html",
|
107
|
+
"get_redoc_html",
|
100
108
|
]
|
@@ -7,10 +7,13 @@ from starlette.middleware import Middleware
|
|
7
7
|
from starlette.middleware.cors import CORSMiddleware
|
8
8
|
from starlette.routing import Mount, Route as StarletteRoute
|
9
9
|
from starlette.types import ASGIApp
|
10
|
+
from starlette.responses import JSONResponse as StarletteJSONResponse
|
10
11
|
|
11
12
|
from .router import Router
|
12
13
|
from .exceptions import HTTPException
|
13
14
|
from .response import JSONResponse
|
15
|
+
from .openapi import get_openapi_schema
|
16
|
+
from .docs import get_swagger_ui_html, get_redoc_html
|
14
17
|
|
15
18
|
|
16
19
|
class VegaApp:
|
@@ -59,12 +62,20 @@ class VegaApp:
|
|
59
62
|
middleware: Optional[Sequence[Middleware]] = None,
|
60
63
|
on_startup: Optional[Sequence[Callable]] = None,
|
61
64
|
on_shutdown: Optional[Sequence[Callable]] = None,
|
65
|
+
docs_url: Optional[str] = "/docs",
|
66
|
+
redoc_url: Optional[str] = "/redoc",
|
67
|
+
openapi_url: Optional[str] = "/openapi.json",
|
62
68
|
):
|
63
69
|
self.title = title
|
64
70
|
self.description = description
|
65
71
|
self.version = version
|
66
72
|
self.debug = debug
|
67
73
|
|
74
|
+
# Documentation URLs (None to disable)
|
75
|
+
self.docs_url = docs_url
|
76
|
+
self.redoc_url = redoc_url
|
77
|
+
self.openapi_url = openapi_url
|
78
|
+
|
68
79
|
# Internal router for top-level routes
|
69
80
|
self._router = Router()
|
70
81
|
|
@@ -78,6 +89,9 @@ class VegaApp:
|
|
78
89
|
# Starlette app (created lazily)
|
79
90
|
self._starlette_app: Optional[Starlette] = None
|
80
91
|
|
92
|
+
# OpenAPI schema (cached)
|
93
|
+
self._openapi_schema: Optional[Dict[str, Any]] = None
|
94
|
+
|
81
95
|
def add_middleware(
|
82
96
|
self,
|
83
97
|
middleware_class: type,
|
@@ -197,6 +211,22 @@ class VegaApp:
|
|
197
211
|
"""
|
198
212
|
return self._router.route(path, methods, **kwargs)
|
199
213
|
|
214
|
+
def openapi(self) -> Dict[str, Any]:
|
215
|
+
"""
|
216
|
+
Generate and return the OpenAPI schema.
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
OpenAPI schema dictionary
|
220
|
+
"""
|
221
|
+
if self._openapi_schema is None:
|
222
|
+
self._openapi_schema = get_openapi_schema(
|
223
|
+
title=self.title,
|
224
|
+
version=self.version,
|
225
|
+
description=self.description,
|
226
|
+
routes=self._router.get_routes(),
|
227
|
+
)
|
228
|
+
return self._openapi_schema
|
229
|
+
|
200
230
|
def _build_starlette_app(self) -> Starlette:
|
201
231
|
"""Build the Starlette application from routes and middleware."""
|
202
232
|
# Convert Vega routes to Starlette routes
|
@@ -204,6 +234,39 @@ class VegaApp:
|
|
204
234
|
route.to_starlette_route() for route in self._router.get_routes()
|
205
235
|
]
|
206
236
|
|
237
|
+
# Add OpenAPI endpoint
|
238
|
+
if self.openapi_url:
|
239
|
+
async def openapi_endpoint(request):
|
240
|
+
return StarletteJSONResponse(self.openapi())
|
241
|
+
|
242
|
+
starlette_routes.append(
|
243
|
+
StarletteRoute(self.openapi_url, endpoint=openapi_endpoint, methods=["GET"])
|
244
|
+
)
|
245
|
+
|
246
|
+
# Add Swagger UI endpoint
|
247
|
+
if self.docs_url:
|
248
|
+
async def swagger_ui_endpoint(request):
|
249
|
+
return get_swagger_ui_html(
|
250
|
+
openapi_url=self.openapi_url or "/openapi.json",
|
251
|
+
title=f"{self.title} - Swagger UI"
|
252
|
+
)
|
253
|
+
|
254
|
+
starlette_routes.append(
|
255
|
+
StarletteRoute(self.docs_url, endpoint=swagger_ui_endpoint, methods=["GET"])
|
256
|
+
)
|
257
|
+
|
258
|
+
# Add ReDoc endpoint
|
259
|
+
if self.redoc_url:
|
260
|
+
async def redoc_endpoint(request):
|
261
|
+
return get_redoc_html(
|
262
|
+
openapi_url=self.openapi_url or "/openapi.json",
|
263
|
+
title=f"{self.title} - ReDoc"
|
264
|
+
)
|
265
|
+
|
266
|
+
starlette_routes.append(
|
267
|
+
StarletteRoute(self.redoc_url, endpoint=redoc_endpoint, methods=["GET"])
|
268
|
+
)
|
269
|
+
|
207
270
|
# Create Starlette app
|
208
271
|
app = Starlette(
|
209
272
|
debug=self.debug,
|
@@ -0,0 +1,104 @@
|
|
1
|
+
"""Documentation endpoints for Vega Web Framework"""
|
2
|
+
|
3
|
+
from typing import Callable
|
4
|
+
from starlette.responses import HTMLResponse, JSONResponse
|
5
|
+
|
6
|
+
|
7
|
+
def get_swagger_ui_html(
|
8
|
+
*,
|
9
|
+
openapi_url: str,
|
10
|
+
title: str,
|
11
|
+
swagger_js_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js",
|
12
|
+
swagger_css_url: str = "https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css",
|
13
|
+
swagger_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
|
14
|
+
) -> HTMLResponse:
|
15
|
+
"""
|
16
|
+
Generate Swagger UI HTML page.
|
17
|
+
|
18
|
+
Args:
|
19
|
+
openapi_url: URL to OpenAPI schema JSON
|
20
|
+
title: Page title
|
21
|
+
swagger_js_url: URL to Swagger UI JavaScript
|
22
|
+
swagger_css_url: URL to Swagger UI CSS
|
23
|
+
swagger_favicon_url: URL to favicon
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
HTML response with Swagger UI
|
27
|
+
"""
|
28
|
+
html = f"""
|
29
|
+
<!DOCTYPE html>
|
30
|
+
<html>
|
31
|
+
<head>
|
32
|
+
<title>{title}</title>
|
33
|
+
<meta charset="utf-8"/>
|
34
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
35
|
+
<link rel="shortcut icon" href="{swagger_favicon_url}">
|
36
|
+
<link rel="stylesheet" type="text/css" href="{swagger_css_url}" />
|
37
|
+
</head>
|
38
|
+
<body>
|
39
|
+
<div id="swagger-ui"></div>
|
40
|
+
<script src="{swagger_js_url}"></script>
|
41
|
+
<script>
|
42
|
+
const ui = SwaggerUIBundle({{
|
43
|
+
url: '{openapi_url}',
|
44
|
+
dom_id: '#swagger-ui',
|
45
|
+
presets: [
|
46
|
+
SwaggerUIBundle.presets.apis,
|
47
|
+
SwaggerUIBundle.SwaggerUIStandalonePreset
|
48
|
+
],
|
49
|
+
layout: "BaseLayout",
|
50
|
+
deepLinking: true,
|
51
|
+
showExtensions: true,
|
52
|
+
showCommonExtensions: true
|
53
|
+
}})
|
54
|
+
</script>
|
55
|
+
</body>
|
56
|
+
</html>
|
57
|
+
"""
|
58
|
+
return HTMLResponse(content=html)
|
59
|
+
|
60
|
+
|
61
|
+
def get_redoc_html(
|
62
|
+
*,
|
63
|
+
openapi_url: str,
|
64
|
+
title: str,
|
65
|
+
redoc_js_url: str = "https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js",
|
66
|
+
redoc_favicon_url: str = "https://fastapi.tiangolo.com/img/favicon.png",
|
67
|
+
) -> HTMLResponse:
|
68
|
+
"""
|
69
|
+
Generate ReDoc HTML page.
|
70
|
+
|
71
|
+
Args:
|
72
|
+
openapi_url: URL to OpenAPI schema JSON
|
73
|
+
title: Page title
|
74
|
+
redoc_js_url: URL to ReDoc JavaScript
|
75
|
+
redoc_favicon_url: URL to favicon
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
HTML response with ReDoc
|
79
|
+
"""
|
80
|
+
html = f"""
|
81
|
+
<!DOCTYPE html>
|
82
|
+
<html>
|
83
|
+
<head>
|
84
|
+
<title>{title}</title>
|
85
|
+
<meta charset="utf-8"/>
|
86
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
87
|
+
<link rel="shortcut icon" href="{redoc_favicon_url}">
|
88
|
+
<style>
|
89
|
+
body {{
|
90
|
+
margin: 0;
|
91
|
+
padding: 0;
|
92
|
+
}}
|
93
|
+
</style>
|
94
|
+
</head>
|
95
|
+
<body>
|
96
|
+
<redoc spec-url="{openapi_url}"></redoc>
|
97
|
+
<script src="{redoc_js_url}"></script>
|
98
|
+
</body>
|
99
|
+
</html>
|
100
|
+
"""
|
101
|
+
return HTMLResponse(content=html)
|
102
|
+
|
103
|
+
|
104
|
+
__all__ = ["get_swagger_ui_html", "get_redoc_html"]
|
@@ -0,0 +1,292 @@
|
|
1
|
+
"""OpenAPI schema generation for Vega Web Framework"""
|
2
|
+
|
3
|
+
from typing import Any, Dict, List, Optional, Type, get_type_hints
|
4
|
+
from inspect import signature, Parameter
|
5
|
+
import json
|
6
|
+
|
7
|
+
try:
|
8
|
+
from pydantic import BaseModel
|
9
|
+
PYDANTIC_AVAILABLE = True
|
10
|
+
except ImportError:
|
11
|
+
PYDANTIC_AVAILABLE = False
|
12
|
+
BaseModel = None # type: ignore
|
13
|
+
|
14
|
+
|
15
|
+
def get_openapi_schema(
|
16
|
+
*,
|
17
|
+
title: str,
|
18
|
+
version: str,
|
19
|
+
description: str = "",
|
20
|
+
routes: List[Any],
|
21
|
+
openapi_version: str = "3.0.2",
|
22
|
+
) -> Dict[str, Any]:
|
23
|
+
"""
|
24
|
+
Generate OpenAPI 3.0 schema from routes.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
title: API title
|
28
|
+
version: API version
|
29
|
+
description: API description
|
30
|
+
routes: List of Route objects
|
31
|
+
openapi_version: OpenAPI specification version
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
OpenAPI schema dictionary
|
35
|
+
"""
|
36
|
+
schema: Dict[str, Any] = {
|
37
|
+
"openapi": openapi_version,
|
38
|
+
"info": {
|
39
|
+
"title": title,
|
40
|
+
"version": version,
|
41
|
+
},
|
42
|
+
"paths": {},
|
43
|
+
}
|
44
|
+
|
45
|
+
if description:
|
46
|
+
schema["info"]["description"] = description
|
47
|
+
|
48
|
+
# Components for reusable schemas
|
49
|
+
components: Dict[str, Any] = {
|
50
|
+
"schemas": {}
|
51
|
+
}
|
52
|
+
|
53
|
+
# Process each route
|
54
|
+
for route in routes:
|
55
|
+
path = route.path
|
56
|
+
|
57
|
+
# Convert path parameters from {param} to OpenAPI format
|
58
|
+
openapi_path = path
|
59
|
+
|
60
|
+
if openapi_path not in schema["paths"]:
|
61
|
+
schema["paths"][openapi_path] = {}
|
62
|
+
|
63
|
+
for method in route.methods:
|
64
|
+
method_lower = method.lower()
|
65
|
+
|
66
|
+
operation: Dict[str, Any] = {
|
67
|
+
"responses": {
|
68
|
+
"200": {
|
69
|
+
"description": "Successful Response"
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
# Add summary and description
|
75
|
+
if route.summary:
|
76
|
+
operation["summary"] = route.summary
|
77
|
+
|
78
|
+
if route.description:
|
79
|
+
operation["description"] = route.description
|
80
|
+
elif route.endpoint.__doc__:
|
81
|
+
operation["description"] = route.endpoint.__doc__.strip()
|
82
|
+
|
83
|
+
# Add tags
|
84
|
+
if route.tags:
|
85
|
+
operation["tags"] = route.tags
|
86
|
+
|
87
|
+
# Add operation ID
|
88
|
+
operation["operationId"] = f"{method_lower}_{route.name or route.endpoint.__name__}"
|
89
|
+
|
90
|
+
# Analyze endpoint parameters
|
91
|
+
params = _get_parameters(route)
|
92
|
+
if params:
|
93
|
+
operation["parameters"] = params
|
94
|
+
|
95
|
+
# Analyze request body
|
96
|
+
request_body = _get_request_body(route)
|
97
|
+
if request_body:
|
98
|
+
operation["requestBody"] = request_body
|
99
|
+
# Add request body schemas to components
|
100
|
+
if PYDANTIC_AVAILABLE and "content" in request_body:
|
101
|
+
for content_type, content_schema in request_body["content"].items():
|
102
|
+
if "schema" in content_schema and "$ref" in content_schema["schema"]:
|
103
|
+
model_name = content_schema["schema"]["$ref"].split("/")[-1]
|
104
|
+
# Model will be added when processing response_model
|
105
|
+
|
106
|
+
# Analyze response model
|
107
|
+
if route.response_model:
|
108
|
+
response_schema = _get_response_schema(route.response_model, components)
|
109
|
+
if response_schema:
|
110
|
+
operation["responses"]["200"] = {
|
111
|
+
"description": "Successful Response",
|
112
|
+
"content": {
|
113
|
+
"application/json": {
|
114
|
+
"schema": response_schema
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
|
119
|
+
# Add status code if specified
|
120
|
+
if route.status_code and route.status_code != 200:
|
121
|
+
operation["responses"][str(route.status_code)] = operation["responses"].pop("200")
|
122
|
+
|
123
|
+
# Add error responses
|
124
|
+
operation["responses"]["422"] = {
|
125
|
+
"description": "Validation Error"
|
126
|
+
}
|
127
|
+
|
128
|
+
schema["paths"][openapi_path][method_lower] = operation
|
129
|
+
|
130
|
+
# Add components if we have any schemas
|
131
|
+
if components["schemas"]:
|
132
|
+
schema["components"] = components
|
133
|
+
|
134
|
+
return schema
|
135
|
+
|
136
|
+
|
137
|
+
def _get_parameters(route: Any) -> List[Dict[str, Any]]:
|
138
|
+
"""Extract path and query parameters from route."""
|
139
|
+
parameters = []
|
140
|
+
|
141
|
+
# Get function signature
|
142
|
+
sig = signature(route.endpoint)
|
143
|
+
type_hints = get_type_hints(route.endpoint)
|
144
|
+
|
145
|
+
# Find which parameters are Pydantic models (request body)
|
146
|
+
request_body_params = set()
|
147
|
+
if PYDANTIC_AVAILABLE:
|
148
|
+
for param_name, param in sig.parameters.items():
|
149
|
+
param_type = type_hints.get(param_name)
|
150
|
+
if param_type and isinstance(param_type, type) and issubclass(param_type, BaseModel):
|
151
|
+
request_body_params.add(param_name)
|
152
|
+
|
153
|
+
for param_name, param in sig.parameters.items():
|
154
|
+
# Skip special parameters
|
155
|
+
if param_name in ("request", "self", "cls"):
|
156
|
+
continue
|
157
|
+
|
158
|
+
# Skip parameters that are Pydantic models (they're in request body)
|
159
|
+
if param_name in request_body_params:
|
160
|
+
continue
|
161
|
+
|
162
|
+
# Check if it's a path parameter
|
163
|
+
if f"{{{param_name}}}" in route.path:
|
164
|
+
param_schema = {
|
165
|
+
"name": param_name,
|
166
|
+
"in": "path",
|
167
|
+
"required": True,
|
168
|
+
"schema": _get_type_schema(type_hints.get(param_name, str))
|
169
|
+
}
|
170
|
+
parameters.append(param_schema)
|
171
|
+
elif param.default == Parameter.empty:
|
172
|
+
# Required query parameter
|
173
|
+
param_schema = {
|
174
|
+
"name": param_name,
|
175
|
+
"in": "query",
|
176
|
+
"required": True,
|
177
|
+
"schema": _get_type_schema(type_hints.get(param_name, str))
|
178
|
+
}
|
179
|
+
parameters.append(param_schema)
|
180
|
+
else:
|
181
|
+
# Optional query parameter
|
182
|
+
param_schema = {
|
183
|
+
"name": param_name,
|
184
|
+
"in": "query",
|
185
|
+
"required": False,
|
186
|
+
"schema": _get_type_schema(type_hints.get(param_name, str))
|
187
|
+
}
|
188
|
+
if param.default is not None and param.default != Parameter.empty:
|
189
|
+
param_schema["schema"]["default"] = param.default
|
190
|
+
parameters.append(param_schema)
|
191
|
+
|
192
|
+
return parameters
|
193
|
+
|
194
|
+
|
195
|
+
def _get_request_body(route: Any) -> Optional[Dict[str, Any]]:
|
196
|
+
"""Extract request body schema from route."""
|
197
|
+
if not PYDANTIC_AVAILABLE:
|
198
|
+
return None
|
199
|
+
|
200
|
+
# Get function signature
|
201
|
+
sig = signature(route.endpoint)
|
202
|
+
type_hints = get_type_hints(route.endpoint)
|
203
|
+
|
204
|
+
for param_name, param in sig.parameters.items():
|
205
|
+
# Skip special parameters and path parameters
|
206
|
+
if param_name in ("request", "self", "cls"):
|
207
|
+
continue
|
208
|
+
if f"{{{param_name}}}" in route.path:
|
209
|
+
continue
|
210
|
+
|
211
|
+
param_type = type_hints.get(param_name)
|
212
|
+
|
213
|
+
# Check if it's a Pydantic model
|
214
|
+
if param_type and isinstance(param_type, type) and issubclass(param_type, BaseModel):
|
215
|
+
model_schema = param_type.model_json_schema()
|
216
|
+
model_name = param_type.__name__
|
217
|
+
|
218
|
+
return {
|
219
|
+
"required": True,
|
220
|
+
"content": {
|
221
|
+
"application/json": {
|
222
|
+
"schema": {
|
223
|
+
"$ref": f"#/components/schemas/{model_name}"
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
return None
|
230
|
+
|
231
|
+
|
232
|
+
def _get_response_schema(response_model: Type, components: Dict[str, Any]) -> Dict[str, Any]:
|
233
|
+
"""Get response schema from response model."""
|
234
|
+
if not PYDANTIC_AVAILABLE:
|
235
|
+
return {}
|
236
|
+
|
237
|
+
# Handle Pydantic models
|
238
|
+
if isinstance(response_model, type) and issubclass(response_model, BaseModel):
|
239
|
+
model_name = response_model.__name__
|
240
|
+
|
241
|
+
# Add model schema to components
|
242
|
+
if model_name not in components["schemas"]:
|
243
|
+
components["schemas"][model_name] = response_model.model_json_schema()
|
244
|
+
|
245
|
+
return {
|
246
|
+
"$ref": f"#/components/schemas/{model_name}"
|
247
|
+
}
|
248
|
+
|
249
|
+
# Handle List types
|
250
|
+
if hasattr(response_model, "__origin__"):
|
251
|
+
if response_model.__origin__ is list:
|
252
|
+
item_type = response_model.__args__[0]
|
253
|
+
if isinstance(item_type, type) and issubclass(item_type, BaseModel):
|
254
|
+
model_name = item_type.__name__
|
255
|
+
|
256
|
+
# Add model schema to components
|
257
|
+
if model_name not in components["schemas"]:
|
258
|
+
components["schemas"][model_name] = item_type.model_json_schema()
|
259
|
+
|
260
|
+
return {
|
261
|
+
"type": "array",
|
262
|
+
"items": {
|
263
|
+
"$ref": f"#/components/schemas/{model_name}"
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
# Handle dict
|
268
|
+
if response_model is dict:
|
269
|
+
return {"type": "object"}
|
270
|
+
|
271
|
+
return {}
|
272
|
+
|
273
|
+
|
274
|
+
def _get_type_schema(param_type: Any) -> Dict[str, Any]:
|
275
|
+
"""Convert Python type to OpenAPI schema."""
|
276
|
+
if param_type is str or param_type == "str":
|
277
|
+
return {"type": "string"}
|
278
|
+
elif param_type is int or param_type == "int":
|
279
|
+
return {"type": "integer"}
|
280
|
+
elif param_type is float or param_type == "float":
|
281
|
+
return {"type": "number"}
|
282
|
+
elif param_type is bool or param_type == "bool":
|
283
|
+
return {"type": "boolean"}
|
284
|
+
elif param_type is list or (hasattr(param_type, "__origin__") and param_type.__origin__ is list):
|
285
|
+
return {"type": "array", "items": {}}
|
286
|
+
elif param_type is dict:
|
287
|
+
return {"type": "object"}
|
288
|
+
else:
|
289
|
+
return {"type": "string"}
|
290
|
+
|
291
|
+
|
292
|
+
__all__ = ["get_openapi_schema"]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/repository_interface.py.j2
RENAMED
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/domain/service_interface.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/infrastructure/service_impl.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/project/main_standard.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/sqlalchemy/database_manager.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{vega_framework-0.2.0 → vega_framework-0.2.2}/vega/cli/templates/web/routes_init_autodiscovery.py.j2
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|