neuroglia-python 0.6.17__tar.gz → 0.7.8__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.
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/PKG-INFO +39 -38
- neuroglia_python-0.7.8/pyproject.toml +97 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/__init__.py +1 -1
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/event_sourcing/__init__.py +12 -6
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/event_sourcing/abstractions.py +15 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/event_sourcing/event_sourcing_repository.py +17 -5
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/event_sourcing/event_store/event_store.py +24 -12
- neuroglia_python-0.7.8/src/neuroglia/data/infrastructure/event_sourcing/read_model_reconciliator.py +328 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/__init__.py +5 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/mongo_repository.py +3 -3
- neuroglia_python-0.7.8/src/neuroglia/data/infrastructure/mongo/motor_query.py +399 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/motor_repository.py +166 -20
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/queryable.py +68 -13
- neuroglia_python-0.7.8/src/neuroglia/expressions/javascript_expression_translator.py +207 -0
- neuroglia_python-0.7.8/src/neuroglia/hosting/configuration/data_access_layer.py +642 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/serialization/json.py +114 -8
- neuroglia_python-0.6.17/pyproject.toml +0 -97
- neuroglia_python-0.6.17/src/neuroglia/data/infrastructure/event_sourcing/read_model_reconciliator.py +0 -89
- neuroglia_python-0.6.17/src/neuroglia/expressions/javascript_expression_translator.py +0 -147
- neuroglia_python-0.6.17/src/neuroglia/hosting/configuration/data_access_layer.py +0 -57
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/LICENSE +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/README.md +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/application/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/application/background_scheduler.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/coordination/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/coordination/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/coordination/leader_election.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/coordination/redis_backend.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/module_loader.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/operation_result.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/problem_details.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/type_extensions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/type_finder.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/core/type_registry.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/exceptions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/event_sourcing/event_store/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/filesystem/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/filesystem/filesystem_repository.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/memory/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/memory/memory_repository.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/enhanced_mongo_repository.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/serialization_helper.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/mongo/typed_mongo_query.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/resources/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/resources/in_memory_storage_backend.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/resources/postgres_resource_store.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/resources/redis_resource_store.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/resources/resource_repository.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/infrastructure/tracing_mixin.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/queries/generic/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/queries/generic/get_by_id_query.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/queries/generic/list_query.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/controller.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/serializers/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/serializers/xml_serializer.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/serializers/yaml_serializer.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/state_machine.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/resources/watcher.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/data/unit_of_work.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/dependency_injection/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/dependency_injection/service_provider.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/dependency_injection/service_provider_backup.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/cloud_event.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/decorators.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/infrastructure/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_bus.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_ingestor.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_middleware.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/cloud_events/infrastructure/cloud_event_publisher.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/eventing/tracing.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/expressions/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/extensions/cqrs_metrics_extensions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/extensions/mediator_extensions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/extensions/state_persistence_extensions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/hosting/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/hosting/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/hosting/web.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/integration/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/integration/cache_repository.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/integration/http_service_client.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/integration/models.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/logging/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/logging/logger.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mapping/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mapping/mapper.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/behaviors/domain_event_cloudevent_behavior.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/behaviors/domain_event_dispatching_middleware.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/mediator.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/metrics_middleware.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/pipeline_behavior.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/simple.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mediation/tracing_middleware.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mvc/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/mvc/controller_base.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/framework.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/logging.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/metrics.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/otel_sdk.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/settings.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/observability/tracing.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/py.typed +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/reactive/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/reactive/rx_async.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/serialization/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/serialization/abstractions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/serialization/aggregate_serializer.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/utils/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/utils/camel_model.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/utils/case_conversion.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/validation/__init__.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/validation/business_rules.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/validation/exceptions.py +0 -0
- {neuroglia_python-0.6.17 → neuroglia_python-0.7.8}/src/neuroglia/validation/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: neuroglia-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.8
|
|
4
4
|
Summary: Port from .NET to Python of the Neuroglia Framework
|
|
5
5
|
License: Apache
|
|
6
6
|
License-File: LICENSE
|
|
@@ -21,43 +21,44 @@ Provides-Extra: etcd
|
|
|
21
21
|
Provides-Extra: eventstore
|
|
22
22
|
Provides-Extra: mongodb
|
|
23
23
|
Provides-Extra: redis
|
|
24
|
-
Requires-Dist: annotated-types (
|
|
25
|
-
Requires-Dist: apscheduler (
|
|
26
|
-
Requires-Dist: boto3 (
|
|
27
|
-
Requires-Dist: classy-fastapi (
|
|
28
|
-
Requires-Dist: email-validator (
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
Requires-Dist:
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
Requires-Dist: httpx (
|
|
34
|
-
Requires-Dist: itsdangerous (
|
|
35
|
-
Requires-Dist: jinja2 (
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist:
|
|
38
|
-
Requires-Dist:
|
|
39
|
-
Requires-Dist: opentelemetry-
|
|
40
|
-
Requires-Dist: opentelemetry-exporter-otlp-proto-
|
|
41
|
-
Requires-Dist: opentelemetry-exporter-
|
|
42
|
-
Requires-Dist: opentelemetry-
|
|
43
|
-
Requires-Dist: opentelemetry-instrumentation
|
|
44
|
-
Requires-Dist: opentelemetry-instrumentation-
|
|
45
|
-
Requires-Dist: opentelemetry-instrumentation-
|
|
46
|
-
Requires-Dist: opentelemetry-instrumentation-
|
|
47
|
-
Requires-Dist: opentelemetry-
|
|
48
|
-
Requires-Dist:
|
|
49
|
-
Requires-Dist:
|
|
50
|
-
Requires-Dist:
|
|
51
|
-
Requires-Dist:
|
|
52
|
-
Requires-Dist: pydantic
|
|
53
|
-
Requires-Dist:
|
|
54
|
-
Requires-Dist:
|
|
55
|
-
Requires-Dist:
|
|
56
|
-
Requires-Dist: python-
|
|
57
|
-
Requires-Dist:
|
|
58
|
-
Requires-Dist:
|
|
59
|
-
Requires-Dist:
|
|
60
|
-
Requires-Dist:
|
|
24
|
+
Requires-Dist: annotated-types (==0.7.0)
|
|
25
|
+
Requires-Dist: apscheduler (==3.11.0)
|
|
26
|
+
Requires-Dist: boto3 (==1.40.64) ; extra == "aws" or extra == "all"
|
|
27
|
+
Requires-Dist: classy-fastapi (==0.6.1)
|
|
28
|
+
Requires-Dist: email-validator (==2.2.0)
|
|
29
|
+
Requires-Dist: etcd3-py (==0.1.6) ; extra == "etcd" or extra == "all"
|
|
30
|
+
Requires-Dist: fastapi (==0.115.5)
|
|
31
|
+
Requires-Dist: grpcio (==1.76.0)
|
|
32
|
+
Requires-Dist: h11 (==0.16.0)
|
|
33
|
+
Requires-Dist: httpx (==0.27.2)
|
|
34
|
+
Requires-Dist: itsdangerous (==2.2.0)
|
|
35
|
+
Requires-Dist: jinja2 (==3.1.4)
|
|
36
|
+
Requires-Dist: kurrentdbclient (==1.1.2) ; extra == "eventstore" or extra == "all"
|
|
37
|
+
Requires-Dist: motor (==3.7.0) ; extra == "mongodb" or extra == "all"
|
|
38
|
+
Requires-Dist: multipledispatch (==1.0.0)
|
|
39
|
+
Requires-Dist: opentelemetry-api (==1.38.0)
|
|
40
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.38.0)
|
|
41
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.38.0)
|
|
42
|
+
Requires-Dist: opentelemetry-exporter-prometheus (==0.59b0)
|
|
43
|
+
Requires-Dist: opentelemetry-instrumentation (==0.59b0)
|
|
44
|
+
Requires-Dist: opentelemetry-instrumentation-fastapi (==0.59b0)
|
|
45
|
+
Requires-Dist: opentelemetry-instrumentation-httpx (==0.59b0)
|
|
46
|
+
Requires-Dist: opentelemetry-instrumentation-logging (==0.59b0)
|
|
47
|
+
Requires-Dist: opentelemetry-instrumentation-system-metrics (==0.59b0)
|
|
48
|
+
Requires-Dist: opentelemetry-sdk (==1.38.0)
|
|
49
|
+
Requires-Dist: passlib[bcrypt] (==1.7.4)
|
|
50
|
+
Requires-Dist: prometheus-client (==0.21.0)
|
|
51
|
+
Requires-Dist: protobuf (==6.33.1) ; extra == "etcd" or extra == "all"
|
|
52
|
+
Requires-Dist: pydantic-settings (==2.6.1)
|
|
53
|
+
Requires-Dist: pydantic[email] (==2.10.3)
|
|
54
|
+
Requires-Dist: pyjwt (==2.10.1)
|
|
55
|
+
Requires-Dist: pymongo (==4.15.4) ; extra == "mongodb" or extra == "all"
|
|
56
|
+
Requires-Dist: python-dotenv (==1.0.1)
|
|
57
|
+
Requires-Dist: python-multipart (==0.0.17)
|
|
58
|
+
Requires-Dist: redis (==5.2.0) ; extra == "redis" or extra == "all"
|
|
59
|
+
Requires-Dist: rx (==3.2.0)
|
|
60
|
+
Requires-Dist: typing-extensions (==4.12.2)
|
|
61
|
+
Requires-Dist: uvicorn (==0.32.1)
|
|
61
62
|
Description-Content-Type: text/markdown
|
|
62
63
|
|
|
63
64
|
# Neuroglia Python Framework
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "neuroglia-python"
|
|
3
|
+
version = "0.7.8"
|
|
4
|
+
description = "Port from .NET to Python of the Neuroglia Framework"
|
|
5
|
+
authors = ["Charles d'Avernas <charles.davernas@neuroglia.io>", "Bruno van de Werve <bvandewe@gmail.com>"]
|
|
6
|
+
license = "Apache"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
packages = [
|
|
9
|
+
{ include = "neuroglia", from = "src" },
|
|
10
|
+
]
|
|
11
|
+
package-mode = true
|
|
12
|
+
|
|
13
|
+
[tool.poetry.dependencies]
|
|
14
|
+
python = "^3.9"
|
|
15
|
+
# Core framework dependencies - always required
|
|
16
|
+
annotated-types = "0.7.0"
|
|
17
|
+
classy-fastapi = "0.6.1"
|
|
18
|
+
fastapi = "0.115.5"
|
|
19
|
+
multipledispatch = "1.0.0"
|
|
20
|
+
pydantic-settings = "2.6.1"
|
|
21
|
+
python-dotenv = "1.0.1"
|
|
22
|
+
typing-extensions = "4.12.2"
|
|
23
|
+
rx = { version = "3.2.0" }
|
|
24
|
+
uvicorn = { version = "0.32.1" }
|
|
25
|
+
httpx = { version = "0.27.2" }
|
|
26
|
+
grpcio = { version = "1.76.0" }
|
|
27
|
+
h11 = { version = "0.16.0" } # Security fix for CVE-2025-43859
|
|
28
|
+
|
|
29
|
+
# Authentication dependencies
|
|
30
|
+
pyjwt = "2.10.1"
|
|
31
|
+
passlib = { extras = ["bcrypt"], version = "1.7.4" }
|
|
32
|
+
python-multipart = "0.0.17"
|
|
33
|
+
itsdangerous = "2.2.0"
|
|
34
|
+
|
|
35
|
+
# Core dependencies
|
|
36
|
+
protobuf = "6.33.1" # Pinned for compatibility with kurrentdbclient, OpenTelemetry, and etcd3-py
|
|
37
|
+
apscheduler = "3.11.0"
|
|
38
|
+
pydantic = {extras = ["email"], version = "2.10.3"}
|
|
39
|
+
email-validator = "2.2.0"
|
|
40
|
+
jinja2 = "3.1.4"
|
|
41
|
+
|
|
42
|
+
# Optional dependencies - install with extras
|
|
43
|
+
redis = { version = "5.2.0", optional = true }
|
|
44
|
+
pymongo = { version = "4.15.4", optional = true }
|
|
45
|
+
motor = { version = "3.7.0", optional = true }
|
|
46
|
+
kurrentdbclient = "1.1.2"
|
|
47
|
+
etcd3-py = { version = "0.1.6", optional = true } # Maintained fork with protobuf 5.x/6.x support
|
|
48
|
+
|
|
49
|
+
# OpenTelemetry dependencies - pinned versions for protobuf 6.x compatibility
|
|
50
|
+
opentelemetry-api = "1.38.0"
|
|
51
|
+
opentelemetry-sdk = "1.38.0"
|
|
52
|
+
opentelemetry-exporter-otlp-proto-grpc = "1.38.0"
|
|
53
|
+
opentelemetry-exporter-otlp-proto-http = "1.38.0"
|
|
54
|
+
opentelemetry-instrumentation = "0.59b0"
|
|
55
|
+
opentelemetry-instrumentation-fastapi = "0.59b0"
|
|
56
|
+
opentelemetry-instrumentation-httpx = "0.59b0"
|
|
57
|
+
opentelemetry-instrumentation-logging = "0.59b0"
|
|
58
|
+
opentelemetry-instrumentation-system-metrics = "0.59b0"
|
|
59
|
+
opentelemetry-exporter-prometheus = "0.59b0" # Prometheus /metrics endpoint support
|
|
60
|
+
prometheus-client = "0.21.0" # Required by opentelemetry-exporter-prometheus
|
|
61
|
+
boto3 = { version = "1.40.64", optional = true }
|
|
62
|
+
|
|
63
|
+
[tool.poetry.extras]
|
|
64
|
+
mongodb = ["pymongo", "motor"]
|
|
65
|
+
eventstore = ["kurrentdbclient"] # Formerly esdbclient
|
|
66
|
+
redis = ["redis"]
|
|
67
|
+
etcd = ["etcd3-py", "protobuf"]
|
|
68
|
+
aws = ["boto3"]
|
|
69
|
+
all = ["pymongo", "motor", "kurrentdbclient", "redis", "etcd3-py", "protobuf", "boto3"]
|
|
70
|
+
|
|
71
|
+
[tool.poetry.group.dev.dependencies]
|
|
72
|
+
# Development and testing dependencies
|
|
73
|
+
pre-commit = "^4.0.1"
|
|
74
|
+
pytest = "^8.3.3"
|
|
75
|
+
pytest-asyncio = "^0.24.0"
|
|
76
|
+
mypy = "^1.13.0"
|
|
77
|
+
autopep8 = "^2.3.1"
|
|
78
|
+
coverage = "^7.6.9"
|
|
79
|
+
mypy-extensions = "^1.0.0"
|
|
80
|
+
flake8 = "^7.1.1"
|
|
81
|
+
isort = "^5.13.2"
|
|
82
|
+
|
|
83
|
+
[tool.poetry.group.docs]
|
|
84
|
+
optional = true
|
|
85
|
+
|
|
86
|
+
[tool.poetry.group.docs.dependencies]
|
|
87
|
+
# Documentation dependencies
|
|
88
|
+
mkdocs = "^1.6.1"
|
|
89
|
+
mkdocs-material = "^9.5.48"
|
|
90
|
+
mkdocs-mermaid2-plugin = "^1.2.2"
|
|
91
|
+
|
|
92
|
+
[build-system]
|
|
93
|
+
requires = ["poetry-core"]
|
|
94
|
+
build-backend = "poetry.core.masonry.api"
|
|
95
|
+
|
|
96
|
+
[tool.black]
|
|
97
|
+
line-length = 500
|
|
@@ -5,23 +5,29 @@ Provides event store implementations and aggregate root support.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from .abstractions import (
|
|
8
|
-
|
|
9
|
-
EventRecord,
|
|
8
|
+
Aggregator,
|
|
10
9
|
EventDescriptor,
|
|
10
|
+
EventRecord,
|
|
11
|
+
EventStore,
|
|
12
|
+
EventStoreOptions,
|
|
11
13
|
StreamDescriptor,
|
|
12
14
|
StreamReadDirection,
|
|
13
|
-
EventStoreOptions,
|
|
14
|
-
Aggregator
|
|
15
15
|
)
|
|
16
16
|
from .event_sourcing_repository import EventSourcingRepository
|
|
17
|
+
from .read_model_reconciliator import (
|
|
18
|
+
ReadModelConciliationOptions,
|
|
19
|
+
ReadModelReconciliator,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
__all__ = [
|
|
19
23
|
"EventStore",
|
|
20
24
|
"EventSourcingRepository",
|
|
21
25
|
"EventRecord",
|
|
22
|
-
"EventDescriptor",
|
|
26
|
+
"EventDescriptor",
|
|
23
27
|
"StreamDescriptor",
|
|
24
28
|
"StreamReadDirection",
|
|
25
29
|
"EventStoreOptions",
|
|
26
30
|
"Aggregator",
|
|
27
|
-
|
|
31
|
+
"ReadModelConciliationOptions",
|
|
32
|
+
"ReadModelReconciliator",
|
|
33
|
+
]
|
|
@@ -202,8 +202,23 @@ class EventStore(ABC):
|
|
|
202
202
|
|
|
203
203
|
class Aggregator:
|
|
204
204
|
def aggregate(self, events: list, aggregate_type: type):
|
|
205
|
+
"""
|
|
206
|
+
Reconstitutes an aggregate from a list of domain events.
|
|
207
|
+
|
|
208
|
+
Creates a new aggregate instance using __new__() to bypass __init__,
|
|
209
|
+
then manually initializes the state and _pending_events list before
|
|
210
|
+
replaying events to rebuild the aggregate's state.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
events: List of event records to replay
|
|
214
|
+
aggregate_type: The aggregate root class to instantiate
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
Reconstituted aggregate with replayed state
|
|
218
|
+
"""
|
|
205
219
|
aggregate: AggregateRoot = object.__new__(aggregate_type)
|
|
206
220
|
aggregate.state = aggregate.__orig_bases__[0].__args__[0]()
|
|
221
|
+
aggregate._pending_events = list() # Initialize _pending_events to prevent AttributeError
|
|
207
222
|
for e in events:
|
|
208
223
|
aggregate.state.on(e.data)
|
|
209
224
|
aggregate.state.state_version = e.data.aggregate_version
|
|
@@ -113,10 +113,20 @@ class EventSourcingRepository(Generic[TAggregate, TKey], Repository[TAggregate,
|
|
|
113
113
|
return self._eventstore.contains_stream(self._build_stream_id_for(id))
|
|
114
114
|
|
|
115
115
|
async def get_async(self, id: TKey) -> Optional[TAggregate]:
|
|
116
|
-
"""
|
|
116
|
+
"""
|
|
117
|
+
Gets the aggregate with the specified id, if any.
|
|
118
|
+
|
|
119
|
+
Returns None if the stream does not exist.
|
|
120
|
+
"""
|
|
117
121
|
stream_id = self._build_stream_id_for(id)
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
try:
|
|
123
|
+
events = await self._eventstore.read_async(stream_id, StreamReadDirection.FORWARDS, 0)
|
|
124
|
+
if not events:
|
|
125
|
+
return None
|
|
126
|
+
return self._aggregator.aggregate(events, self.__orig_class__.__args__[0])
|
|
127
|
+
except Exception:
|
|
128
|
+
# If stream doesn't exist or any other error occurs, return None
|
|
129
|
+
return None
|
|
120
130
|
|
|
121
131
|
async def _do_add_async(self, aggregate: TAggregate) -> TAggregate:
|
|
122
132
|
"""Adds and persists the specified aggregate"""
|
|
@@ -127,7 +137,8 @@ class EventSourcingRepository(Generic[TAggregate, TKey], Repository[TAggregate,
|
|
|
127
137
|
encoded_events = [self._encode_event(e) for e in events]
|
|
128
138
|
await self._eventstore.append_async(stream_id, encoded_events)
|
|
129
139
|
aggregate.state.state_version = events[-1].aggregate_version
|
|
130
|
-
|
|
140
|
+
# DON'T clear pending events here - let base class do it after publishing!
|
|
141
|
+
# aggregate.clear_pending_events()
|
|
131
142
|
return aggregate
|
|
132
143
|
|
|
133
144
|
async def _do_update_async(self, aggregate: TAggregate) -> TAggregate:
|
|
@@ -139,7 +150,8 @@ class EventSourcingRepository(Generic[TAggregate, TKey], Repository[TAggregate,
|
|
|
139
150
|
encoded_events = [self._encode_event(e) for e in events]
|
|
140
151
|
await self._eventstore.append_async(stream_id, encoded_events, aggregate.state.state_version)
|
|
141
152
|
aggregate.state.state_version = events[-1].aggregate_version
|
|
142
|
-
|
|
153
|
+
# DON'T clear pending events here - let base class do it after publishing!
|
|
154
|
+
# aggregate.clear_pending_events()
|
|
143
155
|
return aggregate
|
|
144
156
|
|
|
145
157
|
async def _do_remove_async(self, id: TKey) -> None:
|
|
@@ -5,9 +5,9 @@ import sys
|
|
|
5
5
|
from typing import Any, Dict, Optional
|
|
6
6
|
|
|
7
7
|
import rx
|
|
8
|
-
from
|
|
9
|
-
from
|
|
10
|
-
from
|
|
8
|
+
from kurrentdbclient import AsyncKurrentDBClient as AsyncClientFactory
|
|
9
|
+
from kurrentdbclient import NewEvent, RecordedEvent, StreamState
|
|
10
|
+
from kurrentdbclient.exceptions import AlreadyExistsError as AlreadyExists
|
|
11
11
|
from rx.core.observable.observable import Observable
|
|
12
12
|
from rx.disposable.disposable import Disposable
|
|
13
13
|
from rx.subject.subject import Subject
|
|
@@ -64,11 +64,15 @@ class ESEventStore(EventStore):
|
|
|
64
64
|
self._eventstore_client = connection_string_or_client
|
|
65
65
|
|
|
66
66
|
async def _ensure_client(self) -> Any:
|
|
67
|
-
"""Lazily initialize the async
|
|
67
|
+
"""Lazily initialize the async KurrentDB client on first use"""
|
|
68
68
|
if self._eventstore_client is None:
|
|
69
69
|
if self._connection_string is None:
|
|
70
70
|
raise RuntimeError("Neither connection string nor client provided")
|
|
71
|
-
|
|
71
|
+
# AsyncKurrentDBClient constructor is NOT awaitable - returns client directly
|
|
72
|
+
client = AsyncClientFactory(uri=self._connection_string)
|
|
73
|
+
# Must call connect() to establish the connection
|
|
74
|
+
await client.connect()
|
|
75
|
+
self._eventstore_client = client
|
|
72
76
|
return self._eventstore_client
|
|
73
77
|
|
|
74
78
|
async def contains_async(self, stream_id: str) -> bool:
|
|
@@ -101,7 +105,7 @@ class ESEventStore(EventStore):
|
|
|
101
105
|
return None
|
|
102
106
|
truncate_before = metadata.get("$tb")
|
|
103
107
|
offset = 0 if truncate_before is None else truncate_before
|
|
104
|
-
read_response = client.read_stream(
|
|
108
|
+
read_response = await client.read_stream(
|
|
105
109
|
stream_name=stream_name,
|
|
106
110
|
stream_position=offset,
|
|
107
111
|
backwards=False,
|
|
@@ -109,7 +113,7 @@ class ESEventStore(EventStore):
|
|
|
109
113
|
limit=1,
|
|
110
114
|
)
|
|
111
115
|
recorded_events = [event async for event in read_response]
|
|
112
|
-
read_response = client.read_stream(
|
|
116
|
+
read_response = await client.read_stream(
|
|
113
117
|
stream_name=stream_name,
|
|
114
118
|
stream_position=offset,
|
|
115
119
|
backwards=True,
|
|
@@ -131,7 +135,7 @@ class ESEventStore(EventStore):
|
|
|
131
135
|
) -> list[EventRecord]:
|
|
132
136
|
client = await self._ensure_client()
|
|
133
137
|
stream_name = self._get_stream_name(stream_id)
|
|
134
|
-
read_response = client.read_stream(
|
|
138
|
+
read_response = await client.read_stream(
|
|
135
139
|
stream_name=stream_name,
|
|
136
140
|
stream_position=offset,
|
|
137
141
|
backwards=True if read_direction == StreamReadDirection.BACKWARDS else False,
|
|
@@ -253,7 +257,9 @@ class ESEventStore(EventStore):
|
|
|
253
257
|
logging.debug(f"Skipping tombstone event from stream: {e.stream_name}")
|
|
254
258
|
# Acknowledge tombstone to continue processing
|
|
255
259
|
if is_persistent:
|
|
256
|
-
|
|
260
|
+
# Use ack_id for resolved link events, fall back to id
|
|
261
|
+
ack_id = getattr(e, "ack_id", e.id)
|
|
262
|
+
await subscription.ack(ack_id)
|
|
257
263
|
continue
|
|
258
264
|
|
|
259
265
|
# Skip system event types (prefixed with $)
|
|
@@ -262,7 +268,9 @@ class ESEventStore(EventStore):
|
|
|
262
268
|
logging.debug(f"Skipping system event type '{e.type}' from stream: {e.stream_name}")
|
|
263
269
|
# Acknowledge system event to continue processing
|
|
264
270
|
if is_persistent:
|
|
265
|
-
|
|
271
|
+
# Use ack_id for resolved link events, fall back to id
|
|
272
|
+
ack_id = getattr(e, "ack_id", e.id)
|
|
273
|
+
await subscription.ack(ack_id)
|
|
266
274
|
continue
|
|
267
275
|
|
|
268
276
|
try:
|
|
@@ -271,12 +279,16 @@ class ESEventStore(EventStore):
|
|
|
271
279
|
logging.warning(f"Could not decode event with offset '{e.stream_position}' from stream '{e.stream_name}': {ex}")
|
|
272
280
|
# Acknowledge failed decode to continue processing (don't park/retry invalid events)
|
|
273
281
|
if is_persistent:
|
|
274
|
-
|
|
282
|
+
# Use ack_id for resolved link events, fall back to id
|
|
283
|
+
ack_id = getattr(e, "ack_id", e.id)
|
|
284
|
+
await subscription.ack(ack_id)
|
|
275
285
|
continue
|
|
276
286
|
|
|
277
287
|
# Convert to AckableEventRecord if subscription supports ack/nack
|
|
278
288
|
if is_persistent:
|
|
279
|
-
|
|
289
|
+
# Use ack_id for resolved link events (resolveLinktos=true), fall back to id
|
|
290
|
+
# This is critical for persistent subscriptions to category streams ($ce-*)
|
|
291
|
+
event_id = getattr(e, "ack_id", e.id)
|
|
280
292
|
|
|
281
293
|
async def ack_delegate(eid=event_id, sub=subscription):
|
|
282
294
|
"""
|