vega-framework 0.1.28__tar.gz → 0.1.30__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.1.28 → vega_framework-0.1.30}/PKG-INFO +1 -1
- {vega_framework-0.1.28 → vega_framework-0.1.30}/pyproject.toml +1 -1
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/generate.py +114 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/init.py +6 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/web.py +9 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/main.py +18 -1
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/__init__.py +6 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/components.py +34 -0
- vega_framework-0.1.30/vega/cli/templates/domain/event.py.j2 +23 -0
- vega_framework-0.1.30/vega/cli/templates/domain/event_handler.py.j2 +22 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/repository_interface.py.j2 +1 -1
- vega_framework-0.1.30/vega/cli/templates/project/events_init.py.j2 +32 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/discovery/__init__.py +2 -1
- vega_framework-0.1.30/vega/discovery/events.py +86 -0
- vega_framework-0.1.30/vega/events/README.md +564 -0
- vega_framework-0.1.30/vega/events/SYNTAX_GUIDE.md +360 -0
- vega_framework-0.1.30/vega/events/__init__.py +30 -0
- vega_framework-0.1.30/vega/events/bus.py +382 -0
- vega_framework-0.1.30/vega/events/decorators.py +181 -0
- vega_framework-0.1.30/vega/events/event.py +156 -0
- vega_framework-0.1.30/vega/events/middleware.py +259 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/patterns/interactor.py +47 -1
- {vega_framework-0.1.28 → vega_framework-0.1.30}/LICENSE +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/README.md +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/add.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/migrate.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/commands/update.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/scaffolds/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/scaffolds/fastapi.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/scaffolds/sqlalchemy.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/cli/command.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/cli/command_simple.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/cli/commands_init.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/entity.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/interactor.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/mediator.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/service_interface.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/infrastructure/model.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/infrastructure/repository_impl.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/infrastructure/service_impl.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/loader.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/.env.example +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/.gitignore +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/ARCHITECTURE.md.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/README.md.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/config.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/main.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/main_fastapi.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/main_standard.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/pyproject.toml.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/project/settings.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/sqlalchemy/alembic.ini.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/sqlalchemy/database_manager.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/sqlalchemy/env.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/sqlalchemy/script.py.mako +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/__init__.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/app.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/health_route.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/main.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/middleware.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/models_init.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/request_model.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/response_model.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/router.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/routes_init.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/routes_init_autodiscovery.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/user_models.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/web/users_route.py.j2 +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/utils/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/utils/async_support.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/utils/messages.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/utils/naming.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/utils/validators.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/di/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/di/container.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/di/decorators.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/di/errors.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/di/scope.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/discovery/commands.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/discovery/routes.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/patterns/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/patterns/mediator.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/patterns/repository.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/patterns/service.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/settings/__init__.py +0 -0
- {vega_framework-0.1.28 → vega_framework-0.1.30}/vega/settings/base.py +0 -0
@@ -15,6 +15,8 @@ from vega.cli.templates import (
|
|
15
15
|
render_sqlalchemy_model,
|
16
16
|
render_cli_command,
|
17
17
|
render_cli_command_simple,
|
18
|
+
render_event,
|
19
|
+
render_event_handler,
|
18
20
|
render_template,
|
19
21
|
)
|
20
22
|
from vega.cli.scaffolds import create_fastapi_scaffold
|
@@ -61,6 +63,11 @@ def generate_component(
|
|
61
63
|
class_name = to_pascal_case(name)
|
62
64
|
implementation = implementation.strip() if implementation else None
|
63
65
|
|
66
|
+
if component_type == 'repo':
|
67
|
+
component_type = 'repository'
|
68
|
+
if component_type in {'event-handler', 'subscriber'}:
|
69
|
+
component_type = 'event_handler'
|
70
|
+
|
64
71
|
suffixes = {
|
65
72
|
"repository": "Repository",
|
66
73
|
"service": "Service",
|
@@ -105,6 +112,10 @@ def generate_component(
|
|
105
112
|
_generate_web_models(project_root, project_name, name, is_request, is_response)
|
106
113
|
elif component_type == 'command':
|
107
114
|
_generate_command(project_root, project_name, name, implementation)
|
115
|
+
elif component_type == 'event':
|
116
|
+
_generate_event(project_root, project_name, class_name, file_name)
|
117
|
+
elif component_type == 'event_handler':
|
118
|
+
_generate_event_handler(project_root, project_name, class_name, file_name)
|
108
119
|
|
109
120
|
|
110
121
|
def _generate_entity(project_root: Path, project_name: str, class_name: str, file_name: str):
|
@@ -875,3 +886,106 @@ def _generate_command(project_root: Path, project_name: str, name: str, is_async
|
|
875
886
|
click.echo(click.style(f" (Commands are auto-discovered from cli/commands/)", fg='bright_black'))
|
876
887
|
if with_interactor:
|
877
888
|
click.echo(f" 3. Create interactor: vega generate interactor {interactor_name}")
|
889
|
+
|
890
|
+
|
891
|
+
def _generate_event(project_root: Path, project_name: str, class_name: str, file_name: str):
|
892
|
+
"""Generate a domain event."""
|
893
|
+
|
894
|
+
events_path = project_root / "domain" / "events"
|
895
|
+
events_path.mkdir(parents=True, exist_ok=True)
|
896
|
+
|
897
|
+
init_file = events_path / "__init__.py"
|
898
|
+
if not init_file.exists():
|
899
|
+
init_file.write_text("")
|
900
|
+
|
901
|
+
file_path = events_path / f"{file_name}.py"
|
902
|
+
if file_path.exists():
|
903
|
+
click.echo(click.style(f"ERROR: Error: {file_path.relative_to(project_root)} already exists", fg='red'))
|
904
|
+
return
|
905
|
+
|
906
|
+
click.echo("\nDefine event payload fields (press Enter to skip):")
|
907
|
+
fields: list[dict[str, str]] = []
|
908
|
+
while True:
|
909
|
+
field_name = click.prompt("Field name", default="", show_default=False)
|
910
|
+
if not field_name:
|
911
|
+
break
|
912
|
+
snake_name = to_snake_case(field_name)
|
913
|
+
type_hint = click.prompt("Type hint", default="str")
|
914
|
+
description = click.prompt(
|
915
|
+
"Description",
|
916
|
+
default=f"{snake_name.replace('_', ' ').capitalize()} value",
|
917
|
+
)
|
918
|
+
fields.append(
|
919
|
+
{
|
920
|
+
"name": snake_name,
|
921
|
+
"type_hint": type_hint,
|
922
|
+
"description": description,
|
923
|
+
}
|
924
|
+
)
|
925
|
+
|
926
|
+
content = render_event(class_name, fields)
|
927
|
+
file_path.write_text(content)
|
928
|
+
click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
|
929
|
+
|
930
|
+
click.echo("\nNext steps:")
|
931
|
+
click.echo(" 1. Publish the event from your domain logic.")
|
932
|
+
click.echo(" 2. Generate subscribers: vega generate subscriber <HandlerName>")
|
933
|
+
|
934
|
+
|
935
|
+
def _generate_event_handler(project_root: Path, project_name: str, class_name: str, file_name: str):
|
936
|
+
"""Generate an application-level event handler/subscriber."""
|
937
|
+
|
938
|
+
handlers_path = project_root / "application" / "events"
|
939
|
+
handlers_path.mkdir(parents=True, exist_ok=True)
|
940
|
+
|
941
|
+
init_file = handlers_path / "__init__.py"
|
942
|
+
if not init_file.exists():
|
943
|
+
init_file.write_text("")
|
944
|
+
|
945
|
+
handler_file = handlers_path / f"{file_name}.py"
|
946
|
+
if handler_file.exists():
|
947
|
+
click.echo(click.style(f"ERROR: Error: {handler_file.relative_to(project_root)} already exists", fg='red'))
|
948
|
+
return
|
949
|
+
|
950
|
+
default_event_class = class_name
|
951
|
+
if default_event_class.lower().endswith("handler"):
|
952
|
+
default_event_class = default_event_class[:-7] or class_name
|
953
|
+
|
954
|
+
event_class = click.prompt("Event class name", default=default_event_class)
|
955
|
+
event_module_default = f"domain.events.{to_snake_case(event_class)}"
|
956
|
+
event_module = click.prompt("Event module path", default=event_module_default)
|
957
|
+
|
958
|
+
priority = click.prompt("Handler priority (higher runs first)", default=0, type=int)
|
959
|
+
retry_on_error = click.confirm("Retry on failure?", default=False)
|
960
|
+
max_retries = None
|
961
|
+
if retry_on_error:
|
962
|
+
max_retries = click.prompt("Max retries", default=3, type=int)
|
963
|
+
|
964
|
+
decorator_args = event_class
|
965
|
+
options: list[str] = []
|
966
|
+
if priority:
|
967
|
+
options.append(f"priority={priority}")
|
968
|
+
if retry_on_error:
|
969
|
+
options.append("retry_on_error=True")
|
970
|
+
if max_retries is not None:
|
971
|
+
options.append(f"max_retries={max_retries}")
|
972
|
+
if options:
|
973
|
+
decorator_args = f"{event_class}, " + ", ".join(options)
|
974
|
+
|
975
|
+
handler_func_name = to_snake_case(class_name)
|
976
|
+
|
977
|
+
content = render_event_handler(
|
978
|
+
class_name=class_name,
|
979
|
+
handler_func_name=handler_func_name,
|
980
|
+
event_name=event_class,
|
981
|
+
event_module=event_module,
|
982
|
+
decorator_args=decorator_args,
|
983
|
+
)
|
984
|
+
|
985
|
+
handler_file.write_text(content)
|
986
|
+
click.echo(f"+ Created {click.style(str(handler_file.relative_to(project_root)), fg='green')}")
|
987
|
+
|
988
|
+
click.echo("\nNext steps:")
|
989
|
+
click.echo(f" 1. Implement your handler in {handler_file.relative_to(project_root)}")
|
990
|
+
click.echo(" 2. Ensure the module is imported during application bootstrap (autodiscovery or manual import).")
|
991
|
+
click.echo(" 3. Run your workflow and verify the subscriber reacts to the event.")
|
@@ -38,6 +38,7 @@ def init_project(project_name: str, template: str, parent_path: str):
|
|
38
38
|
"infrastructure/repositories",
|
39
39
|
"infrastructure/services",
|
40
40
|
"presentation/cli/commands",
|
41
|
+
"events",
|
41
42
|
"tests/domain",
|
42
43
|
"tests/application",
|
43
44
|
"tests/infrastructure",
|
@@ -53,6 +54,11 @@ def init_project(project_name: str, template: str, parent_path: str):
|
|
53
54
|
from vega.cli.templates import render_cli_commands_init
|
54
55
|
content = render_cli_commands_init()
|
55
56
|
(dir_path / "__init__.py").write_text(content)
|
57
|
+
# Use auto-discovery template for events/
|
58
|
+
elif directory == "events":
|
59
|
+
from vega.cli.templates import render_events_init
|
60
|
+
content = render_events_init()
|
61
|
+
(dir_path / "__init__.py").write_text(content)
|
56
62
|
|
57
63
|
click.echo(f" + Created {directory}/")
|
58
64
|
|
@@ -60,6 +60,15 @@ def run(host: str, port: int, reload: bool, path: str):
|
|
60
60
|
click.echo(click.style(" poetry add fastapi uvicorn[standard]", fg='cyan', bold=True))
|
61
61
|
sys.exit(1)
|
62
62
|
|
63
|
+
# Initialize DI container first
|
64
|
+
try:
|
65
|
+
import config # noqa: F401
|
66
|
+
except ImportError as e:
|
67
|
+
click.echo(click.style("ERROR: Failed to load DI container", fg='red'))
|
68
|
+
click.echo(f"\nDetails: {e}")
|
69
|
+
click.echo("\nMake sure config.py exists in the project root")
|
70
|
+
sys.exit(1)
|
71
|
+
|
63
72
|
# Try to import the app
|
64
73
|
try:
|
65
74
|
from presentation.web.main import app
|
@@ -57,7 +57,20 @@ def init(project_name, template, path):
|
|
57
57
|
|
58
58
|
@cli.command()
|
59
59
|
@click.argument('component_type', type=click.Choice([
|
60
|
-
'entity',
|
60
|
+
'entity',
|
61
|
+
'repository',
|
62
|
+
'repo',
|
63
|
+
'service',
|
64
|
+
'interactor',
|
65
|
+
'mediator',
|
66
|
+
'router',
|
67
|
+
'middleware',
|
68
|
+
'webmodel',
|
69
|
+
'model',
|
70
|
+
'command',
|
71
|
+
'event',
|
72
|
+
'event-handler',
|
73
|
+
'subscriber',
|
61
74
|
]))
|
62
75
|
@click.argument('name')
|
63
76
|
@click.option('--path', default='.', help='Project root path')
|
@@ -80,6 +93,8 @@ def generate(component_type, name, path, impl, request, response):
|
|
80
93
|
webmodel - Pydantic request/response models (requires web module)
|
81
94
|
model - SQLAlchemy model (requires sqlalchemy module)
|
82
95
|
command - CLI command (async by default)
|
96
|
+
event - Domain event (immutable dataclass + metadata)
|
97
|
+
event-handler/subscriber - Application-level event subscriber
|
83
98
|
|
84
99
|
Examples:
|
85
100
|
vega generate entity Product
|
@@ -94,6 +109,8 @@ def generate(component_type, name, path, impl, request, response):
|
|
94
109
|
vega generate model User
|
95
110
|
vega generate command CreateUser
|
96
111
|
vega generate command ListUsers --impl sync
|
112
|
+
vega generate event UserCreated
|
113
|
+
vega generate subscriber SendWelcomeEmail --name UserCreated
|
97
114
|
"""
|
98
115
|
# Normalize 'repo' to 'repository'
|
99
116
|
if component_type == 'repo':
|
@@ -28,6 +28,9 @@ from .components import (
|
|
28
28
|
render_cli_command_simple,
|
29
29
|
render_cli_commands_init,
|
30
30
|
render_fastapi_routes_init_autodiscovery,
|
31
|
+
render_event,
|
32
|
+
render_event_handler,
|
33
|
+
render_events_init,
|
31
34
|
)
|
32
35
|
from .loader import render_template
|
33
36
|
|
@@ -61,5 +64,8 @@ __all__ = [
|
|
61
64
|
"render_cli_command_simple",
|
62
65
|
"render_cli_commands_init",
|
63
66
|
"render_fastapi_routes_init_autodiscovery",
|
67
|
+
"render_event",
|
68
|
+
"render_event_handler",
|
69
|
+
"render_events_init",
|
64
70
|
"render_template",
|
65
71
|
]
|
@@ -247,3 +247,37 @@ def render_cli_commands_init() -> str:
|
|
247
247
|
def render_fastapi_routes_init_autodiscovery() -> str:
|
248
248
|
"""Return the template for web/routes/__init__.py with auto-discovery"""
|
249
249
|
return render_template("routes_init_autodiscovery.py.j2", subfolder="web")
|
250
|
+
|
251
|
+
|
252
|
+
def render_event(class_name: str, fields: list[dict]) -> str:
|
253
|
+
"""Return the template for a domain event"""
|
254
|
+
return render_template(
|
255
|
+
"event.py.j2",
|
256
|
+
subfolder="domain",
|
257
|
+
class_name=class_name,
|
258
|
+
fields=fields,
|
259
|
+
)
|
260
|
+
|
261
|
+
|
262
|
+
def render_event_handler(
|
263
|
+
class_name: str,
|
264
|
+
handler_func_name: str,
|
265
|
+
event_name: str,
|
266
|
+
event_module: str,
|
267
|
+
decorator_args: str,
|
268
|
+
) -> str:
|
269
|
+
"""Return the template for an event handler"""
|
270
|
+
return render_template(
|
271
|
+
"event_handler.py.j2",
|
272
|
+
subfolder="domain",
|
273
|
+
class_name=class_name,
|
274
|
+
handler_func_name=handler_func_name,
|
275
|
+
event_name=event_name,
|
276
|
+
event_module=event_module,
|
277
|
+
decorator_args=decorator_args,
|
278
|
+
)
|
279
|
+
|
280
|
+
|
281
|
+
def render_events_init() -> str:
|
282
|
+
"""Return the template for events/__init__.py with auto-discovery"""
|
283
|
+
return render_template("events_init.py.j2", subfolder="project")
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""{{ class_name }} domain event"""
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from vega.events import Event
|
4
|
+
|
5
|
+
|
6
|
+
@dataclass(frozen=True)
|
7
|
+
class {{ class_name }}(Event):
|
8
|
+
"""
|
9
|
+
{{ class_name }} event.
|
10
|
+
|
11
|
+
This event is published when {{ class_name|lower|replace('created', 'is created')|replace('updated', 'is updated')|replace('deleted', 'is deleted') }}.
|
12
|
+
|
13
|
+
Attributes:
|
14
|
+
{% for field in fields %} {{ field.name }}: {{ field.type_hint }} - {{ field.description }}
|
15
|
+
{% endfor %}
|
16
|
+
"""
|
17
|
+
{% for field in fields %}
|
18
|
+
{{ field.name }}: {{ field.type_hint }}
|
19
|
+
{% endfor %}
|
20
|
+
|
21
|
+
def __post_init__(self):
|
22
|
+
"""Initialize event metadata"""
|
23
|
+
super().__init__()
|
@@ -0,0 +1,22 @@
|
|
1
|
+
"""{{ class_name }} event handler"""
|
2
|
+
from vega.events import subscribe
|
3
|
+
from {{ event_module }} import {{ event_name }}
|
4
|
+
|
5
|
+
|
6
|
+
@subscribe({{ decorator_args }})
|
7
|
+
async def {{ handler_func_name }}(event: {{ event_name }}):
|
8
|
+
"""
|
9
|
+
Handle {{ event_name }} event.
|
10
|
+
|
11
|
+
This handler is automatically called when a {{ event_name }} event is published.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
event: The {{ event_name }} event instance
|
15
|
+
|
16
|
+
Example:
|
17
|
+
# Event is automatically published from domain logic
|
18
|
+
# This handler will be called automatically
|
19
|
+
pass
|
20
|
+
"""
|
21
|
+
# TODO: Implement event handling logic
|
22
|
+
raise NotImplementedError("Implement {{ class_name }} handler")
|
{vega_framework-0.1.28 → vega_framework-0.1.30}/vega/cli/templates/domain/repository_interface.py.j2
RENAMED
@@ -3,7 +3,7 @@ from abc import abstractmethod
|
|
3
3
|
from typing import List, Optional
|
4
4
|
|
5
5
|
from vega.patterns import Repository
|
6
|
-
from entities.{{ entity_file }} import {{ entity_name }}
|
6
|
+
from domain.entities.{{ entity_file }} import {{ entity_name }}
|
7
7
|
|
8
8
|
|
9
9
|
class {{ class_name }}(Repository[{{ entity_name }}]):
|
@@ -0,0 +1,32 @@
|
|
1
|
+
"""Event handlers auto-discovery
|
2
|
+
|
3
|
+
This module automatically discovers and registers all event handlers
|
4
|
+
decorated with @subscribe() in this directory.
|
5
|
+
|
6
|
+
The auto-discovery happens when you call register_all_handlers().
|
7
|
+
This should be called during application startup.
|
8
|
+
"""
|
9
|
+
from vega.discovery import discover_event_handlers
|
10
|
+
|
11
|
+
|
12
|
+
def register_all_handlers():
|
13
|
+
"""
|
14
|
+
Auto-discover and register all event handlers in this directory.
|
15
|
+
|
16
|
+
This function scans all Python modules in the events/ directory and
|
17
|
+
imports them to trigger @subscribe() decorator registration.
|
18
|
+
|
19
|
+
Call this function during application bootstrap to ensure all event
|
20
|
+
handlers are registered with the global event bus.
|
21
|
+
|
22
|
+
Example:
|
23
|
+
# In your main.py or application startup
|
24
|
+
from events import register_all_handlers
|
25
|
+
|
26
|
+
# Register all event handlers
|
27
|
+
register_all_handlers()
|
28
|
+
|
29
|
+
# Now events will be handled automatically
|
30
|
+
await UserCreated(user_id="123", email="test@test.com", name="John")
|
31
|
+
"""
|
32
|
+
discover_event_handlers(__package__)
|
@@ -1,5 +1,6 @@
|
|
1
1
|
"""Auto-discovery utilities for Vega framework"""
|
2
2
|
from .routes import discover_routers
|
3
3
|
from .commands import discover_commands
|
4
|
+
from .events import discover_event_handlers
|
4
5
|
|
5
|
-
__all__ = ["discover_routers", "discover_commands"]
|
6
|
+
__all__ = ["discover_routers", "discover_commands", "discover_event_handlers"]
|
@@ -0,0 +1,86 @@
|
|
1
|
+
"""Event handlers auto-discovery utilities"""
|
2
|
+
import importlib
|
3
|
+
import logging
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
|
9
|
+
def discover_event_handlers(
|
10
|
+
base_package: str,
|
11
|
+
events_subpackage: str = "events"
|
12
|
+
) -> None:
|
13
|
+
"""
|
14
|
+
Auto-discover and register event handlers from a package.
|
15
|
+
|
16
|
+
This function scans a package directory for Python modules containing
|
17
|
+
event handlers decorated with @subscribe() and automatically imports them
|
18
|
+
to trigger registration with the global event bus.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
base_package: Base package name (use __package__ from calling module)
|
22
|
+
events_subpackage: Subpackage path containing events (default: "events")
|
23
|
+
|
24
|
+
Example:
|
25
|
+
# In your project's events/__init__.py
|
26
|
+
from vega.discovery import discover_event_handlers
|
27
|
+
|
28
|
+
def register_all_handlers():
|
29
|
+
discover_event_handlers(__package__)
|
30
|
+
|
31
|
+
# Or with custom configuration
|
32
|
+
def register_all_handlers():
|
33
|
+
discover_event_handlers(
|
34
|
+
__package__,
|
35
|
+
events_subpackage="application.events"
|
36
|
+
)
|
37
|
+
|
38
|
+
Note:
|
39
|
+
Event handlers are registered automatically when modules are imported.
|
40
|
+
This function simply imports all modules in the events directory to
|
41
|
+
trigger the @subscribe() decorator registration.
|
42
|
+
|
43
|
+
The function doesn't return anything - handlers register themselves
|
44
|
+
with the global event bus via the @subscribe() decorator.
|
45
|
+
"""
|
46
|
+
# Resolve the events package path
|
47
|
+
try:
|
48
|
+
# Determine the package to scan
|
49
|
+
if base_package.endswith(events_subpackage):
|
50
|
+
events_package = base_package
|
51
|
+
else:
|
52
|
+
# Extract base from fully qualified package name
|
53
|
+
parts = base_package.split('.')
|
54
|
+
# Find the root package (usually the project name)
|
55
|
+
root_package = parts[0]
|
56
|
+
events_package = f"{root_package}.{events_subpackage}"
|
57
|
+
|
58
|
+
# Import the events package to get its path
|
59
|
+
events_module = importlib.import_module(events_package)
|
60
|
+
events_dir = Path(events_module.__file__).parent
|
61
|
+
|
62
|
+
logger.debug(f"Discovering event handlers in: {events_dir}")
|
63
|
+
|
64
|
+
# Scan for event handler modules
|
65
|
+
discovered_count = 0
|
66
|
+
for file in events_dir.glob("*.py"):
|
67
|
+
if file.stem == "__init__":
|
68
|
+
continue
|
69
|
+
|
70
|
+
module_name = f"{events_package}.{file.stem}"
|
71
|
+
|
72
|
+
try:
|
73
|
+
# Import the module to trigger @subscribe() decorator registration
|
74
|
+
importlib.import_module(module_name)
|
75
|
+
discovered_count += 1
|
76
|
+
logger.info(f"Loaded event handlers from: {module_name}")
|
77
|
+
|
78
|
+
except Exception as e:
|
79
|
+
logger.warning(f"Failed to import {module_name}: {e}")
|
80
|
+
continue
|
81
|
+
|
82
|
+
logger.info(f"Auto-discovery complete: {discovered_count} event module(s) loaded")
|
83
|
+
|
84
|
+
except ImportError as e:
|
85
|
+
logger.error(f"Failed to import events package '{events_package}': {e}")
|
86
|
+
raise
|