omnibase_infra 0.2.8__py3-none-any.whl → 0.2.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- omnibase_infra/__init__.py +1 -1
- omnibase_infra/enums/__init__.py +4 -0
- omnibase_infra/enums/enum_declarative_node_violation.py +102 -0
- omnibase_infra/event_bus/adapters/__init__.py +31 -0
- omnibase_infra/event_bus/adapters/adapter_protocol_event_publisher_kafka.py +517 -0
- omnibase_infra/mixins/mixin_async_circuit_breaker.py +113 -1
- omnibase_infra/models/__init__.py +9 -0
- omnibase_infra/models/event_bus/__init__.py +22 -0
- omnibase_infra/models/event_bus/model_consumer_retry_config.py +367 -0
- omnibase_infra/models/event_bus/model_dlq_config.py +177 -0
- omnibase_infra/models/event_bus/model_idempotency_config.py +131 -0
- omnibase_infra/models/event_bus/model_offset_policy_config.py +107 -0
- omnibase_infra/models/resilience/model_circuit_breaker_config.py +15 -0
- omnibase_infra/models/validation/__init__.py +8 -0
- omnibase_infra/models/validation/model_declarative_node_validation_result.py +139 -0
- omnibase_infra/models/validation/model_declarative_node_violation.py +169 -0
- omnibase_infra/nodes/architecture_validator/__init__.py +28 -7
- omnibase_infra/nodes/architecture_validator/constants.py +36 -0
- omnibase_infra/nodes/architecture_validator/handlers/__init__.py +28 -0
- omnibase_infra/nodes/architecture_validator/handlers/contract.yaml +120 -0
- omnibase_infra/nodes/architecture_validator/handlers/handler_architecture_validation.py +359 -0
- omnibase_infra/nodes/architecture_validator/node.py +1 -0
- omnibase_infra/nodes/architecture_validator/node_architecture_validator.py +48 -336
- omnibase_infra/nodes/node_ledger_projection_compute/__init__.py +16 -2
- omnibase_infra/nodes/node_ledger_projection_compute/contract.yaml +14 -4
- omnibase_infra/nodes/node_ledger_projection_compute/handlers/__init__.py +18 -0
- omnibase_infra/nodes/node_ledger_projection_compute/handlers/contract.yaml +53 -0
- omnibase_infra/nodes/node_ledger_projection_compute/handlers/handler_ledger_projection.py +354 -0
- omnibase_infra/nodes/node_ledger_projection_compute/node.py +20 -256
- omnibase_infra/nodes/node_registry_effect/node.py +20 -73
- omnibase_infra/protocols/protocol_dispatch_engine.py +90 -0
- omnibase_infra/runtime/__init__.py +11 -0
- omnibase_infra/runtime/baseline_subscriptions.py +150 -0
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +455 -24
- omnibase_infra/runtime/kafka_contract_source.py +13 -5
- omnibase_infra/runtime/service_message_dispatch_engine.py +112 -0
- omnibase_infra/runtime/service_runtime_host_process.py +6 -11
- omnibase_infra/services/__init__.py +36 -0
- omnibase_infra/services/contract_publisher/__init__.py +95 -0
- omnibase_infra/services/contract_publisher/config.py +199 -0
- omnibase_infra/services/contract_publisher/errors.py +243 -0
- omnibase_infra/services/contract_publisher/models/__init__.py +28 -0
- omnibase_infra/services/contract_publisher/models/model_contract_error.py +67 -0
- omnibase_infra/services/contract_publisher/models/model_infra_error.py +62 -0
- omnibase_infra/services/contract_publisher/models/model_publish_result.py +112 -0
- omnibase_infra/services/contract_publisher/models/model_publish_stats.py +79 -0
- omnibase_infra/services/contract_publisher/service.py +617 -0
- omnibase_infra/services/contract_publisher/sources/__init__.py +52 -0
- omnibase_infra/services/contract_publisher/sources/model_discovered.py +155 -0
- omnibase_infra/services/contract_publisher/sources/protocol.py +101 -0
- omnibase_infra/services/contract_publisher/sources/source_composite.py +309 -0
- omnibase_infra/services/contract_publisher/sources/source_filesystem.py +174 -0
- omnibase_infra/services/contract_publisher/sources/source_package.py +221 -0
- omnibase_infra/services/observability/__init__.py +40 -0
- omnibase_infra/services/observability/agent_actions/__init__.py +64 -0
- omnibase_infra/services/observability/agent_actions/config.py +209 -0
- omnibase_infra/services/observability/agent_actions/consumer.py +1320 -0
- omnibase_infra/services/observability/agent_actions/models/__init__.py +87 -0
- omnibase_infra/services/observability/agent_actions/models/model_agent_action.py +142 -0
- omnibase_infra/services/observability/agent_actions/models/model_detection_failure.py +125 -0
- omnibase_infra/services/observability/agent_actions/models/model_envelope.py +85 -0
- omnibase_infra/services/observability/agent_actions/models/model_execution_log.py +159 -0
- omnibase_infra/services/observability/agent_actions/models/model_performance_metric.py +130 -0
- omnibase_infra/services/observability/agent_actions/models/model_routing_decision.py +138 -0
- omnibase_infra/services/observability/agent_actions/models/model_transformation_event.py +124 -0
- omnibase_infra/services/observability/agent_actions/tests/__init__.py +20 -0
- omnibase_infra/services/observability/agent_actions/tests/test_consumer.py +1154 -0
- omnibase_infra/services/observability/agent_actions/tests/test_models.py +645 -0
- omnibase_infra/services/observability/agent_actions/tests/test_writer.py +709 -0
- omnibase_infra/services/observability/agent_actions/writer_postgres.py +926 -0
- omnibase_infra/validation/__init__.py +12 -0
- omnibase_infra/validation/contracts/declarative_node.validation.yaml +143 -0
- omnibase_infra/validation/validation_exemptions.yaml +93 -0
- omnibase_infra/validation/validator_declarative_node.py +850 -0
- {omnibase_infra-0.2.8.dist-info → omnibase_infra-0.2.9.dist-info}/METADATA +2 -2
- {omnibase_infra-0.2.8.dist-info → omnibase_infra-0.2.9.dist-info}/RECORD +79 -27
- {omnibase_infra-0.2.8.dist-info → omnibase_infra-0.2.9.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.2.8.dist-info → omnibase_infra-0.2.9.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.2.8.dist-info → omnibase_infra-0.2.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract Publisher Error Classes.
|
|
4
|
+
|
|
5
|
+
This module defines error classes specific to contract publishing operations.
|
|
6
|
+
All error classes extend from RuntimeHostError to maintain consistency with
|
|
7
|
+
ONEX infrastructure error handling patterns.
|
|
8
|
+
|
|
9
|
+
Error Hierarchy:
|
|
10
|
+
RuntimeHostError (from omnibase_infra.errors)
|
|
11
|
+
└── ContractPublisherError (base contract publishing error)
|
|
12
|
+
├── ContractSourceNotConfiguredError
|
|
13
|
+
├── ContractPublishingInfraError
|
|
14
|
+
└── NoContractsFoundError
|
|
15
|
+
|
|
16
|
+
Related:
|
|
17
|
+
- OMN-1752: Extract ContractPublisher to omnibase_infra
|
|
18
|
+
- ARCH-002: Runtime owns all Kafka plumbing
|
|
19
|
+
|
|
20
|
+
.. versionadded:: 0.3.0
|
|
21
|
+
Created as part of OMN-1752 (ContractPublisher extraction).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
from omnibase_core.enums import EnumCoreErrorCode
|
|
29
|
+
from omnibase_infra.enums import EnumInfraTransportType
|
|
30
|
+
from omnibase_infra.errors import RuntimeHostError
|
|
31
|
+
from omnibase_infra.models.errors import ModelInfraErrorContext
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from omnibase_infra.services.contract_publisher.models import ModelInfraError
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ContractPublisherError(RuntimeHostError):
|
|
38
|
+
"""Base error class for contract publishing errors.
|
|
39
|
+
|
|
40
|
+
All contract publishing-specific errors should inherit from this class.
|
|
41
|
+
Provides common context for contract publishing operations.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> raise ContractPublisherError("Publishing failed")
|
|
45
|
+
|
|
46
|
+
.. versionadded:: 0.3.0
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(
|
|
50
|
+
self,
|
|
51
|
+
message: str,
|
|
52
|
+
error_code: EnumCoreErrorCode | None = None,
|
|
53
|
+
context: ModelInfraErrorContext | None = None,
|
|
54
|
+
**extra_context: object,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Initialize ContractPublisherError.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
message: Human-readable error message
|
|
60
|
+
error_code: Error code (defaults to OPERATION_FAILED)
|
|
61
|
+
context: Bundled infrastructure context
|
|
62
|
+
**extra_context: Additional context information
|
|
63
|
+
"""
|
|
64
|
+
# Default to KAFKA transport type for contract publishing
|
|
65
|
+
if context is None:
|
|
66
|
+
context = ModelInfraErrorContext.with_correlation(
|
|
67
|
+
transport_type=EnumInfraTransportType.KAFKA,
|
|
68
|
+
operation="contract_publish",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
super().__init__(
|
|
72
|
+
message=message,
|
|
73
|
+
error_code=error_code or EnumCoreErrorCode.OPERATION_FAILED,
|
|
74
|
+
context=context,
|
|
75
|
+
**extra_context,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ContractSourceNotConfiguredError(ContractPublisherError):
|
|
80
|
+
"""Raised when no contract source is configured.
|
|
81
|
+
|
|
82
|
+
This error indicates a configuration problem - the ContractPublisherConfig
|
|
83
|
+
does not have the required source configuration for the selected mode.
|
|
84
|
+
|
|
85
|
+
Examples:
|
|
86
|
+
- mode="filesystem" but filesystem_root is None
|
|
87
|
+
- mode="package" but package_module is None
|
|
88
|
+
- mode="composite" but neither source is configured
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> raise ContractSourceNotConfiguredError(
|
|
92
|
+
... mode="filesystem",
|
|
93
|
+
... missing_field="filesystem_root",
|
|
94
|
+
... )
|
|
95
|
+
|
|
96
|
+
.. versionadded:: 0.3.0
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
mode: str,
|
|
102
|
+
missing_field: str,
|
|
103
|
+
message: str | None = None,
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Initialize ContractSourceNotConfiguredError.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
mode: The configured mode (filesystem, package, composite)
|
|
109
|
+
missing_field: The field that is missing
|
|
110
|
+
message: Optional custom message
|
|
111
|
+
"""
|
|
112
|
+
self.mode = mode
|
|
113
|
+
self.missing_field = missing_field
|
|
114
|
+
|
|
115
|
+
default_message = (
|
|
116
|
+
f"Contract source not configured: mode='{mode}' requires '{missing_field}'"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
super().__init__(
|
|
120
|
+
message=message or default_message,
|
|
121
|
+
error_code=EnumCoreErrorCode.CONFIGURATION_ERROR,
|
|
122
|
+
mode=mode,
|
|
123
|
+
missing_field=missing_field,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class ContractPublishingInfraError(ContractPublisherError):
|
|
128
|
+
"""Raised when infrastructure fails during contract publishing.
|
|
129
|
+
|
|
130
|
+
This error wraps one or more ModelInfraError instances that occurred
|
|
131
|
+
during the publishing process. It is raised when fail_fast=True and
|
|
132
|
+
an infrastructure error occurs.
|
|
133
|
+
|
|
134
|
+
Attributes:
|
|
135
|
+
infra_errors: List of infrastructure errors that occurred
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> from omnibase_infra.services.contract_publisher.models import ModelInfraError
|
|
139
|
+
>>> error = ModelInfraError(
|
|
140
|
+
... error_type="publisher_unavailable",
|
|
141
|
+
... message="Event bus publisher not available",
|
|
142
|
+
... retriable=False,
|
|
143
|
+
... )
|
|
144
|
+
>>> raise ContractPublishingInfraError([error])
|
|
145
|
+
|
|
146
|
+
.. versionadded:: 0.3.0
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(
|
|
150
|
+
self,
|
|
151
|
+
infra_errors: list[ModelInfraError],
|
|
152
|
+
message: str | None = None,
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Initialize ContractPublishingInfraError.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
infra_errors: List of infrastructure errors that occurred
|
|
158
|
+
message: Optional custom message
|
|
159
|
+
"""
|
|
160
|
+
self.infra_errors = infra_errors
|
|
161
|
+
|
|
162
|
+
# Build message from errors if not provided
|
|
163
|
+
if message is None:
|
|
164
|
+
error_count = len(infra_errors)
|
|
165
|
+
if error_count == 1:
|
|
166
|
+
message = f"Contract publishing failed: {infra_errors[0].message}"
|
|
167
|
+
else:
|
|
168
|
+
message = f"Contract publishing failed with {error_count} infrastructure errors"
|
|
169
|
+
|
|
170
|
+
super().__init__(
|
|
171
|
+
message=message,
|
|
172
|
+
error_code=EnumCoreErrorCode.OPERATION_FAILED,
|
|
173
|
+
error_count=len(infra_errors),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class NoContractsFoundError(ContractPublisherError):
|
|
178
|
+
"""Raised when no contracts are found and allow_zero_contracts=False.
|
|
179
|
+
|
|
180
|
+
This error indicates that:
|
|
181
|
+
- Discovery found zero contract files, OR
|
|
182
|
+
- Discovery found files but all failed validation (published_count == 0)
|
|
183
|
+
|
|
184
|
+
Both cases result in "no contracts published" which may indicate
|
|
185
|
+
a configuration or deployment problem.
|
|
186
|
+
|
|
187
|
+
Attributes:
|
|
188
|
+
source_description: Description of the source that was searched
|
|
189
|
+
discovered_count: Number of contracts discovered (may be > 0 if all failed)
|
|
190
|
+
valid_count: Number of valid contracts (always 0 when raised)
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
>>> raise NoContractsFoundError(
|
|
194
|
+
... source_description="filesystem: /app/contracts",
|
|
195
|
+
... discovered_count=0,
|
|
196
|
+
... )
|
|
197
|
+
|
|
198
|
+
.. versionadded:: 0.3.0
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
def __init__(
|
|
202
|
+
self,
|
|
203
|
+
source_description: str,
|
|
204
|
+
discovered_count: int = 0,
|
|
205
|
+
valid_count: int = 0,
|
|
206
|
+
message: str | None = None,
|
|
207
|
+
) -> None:
|
|
208
|
+
"""Initialize NoContractsFoundError.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
source_description: Description of the source searched
|
|
212
|
+
discovered_count: Number of contracts discovered
|
|
213
|
+
valid_count: Number of valid contracts (should be 0)
|
|
214
|
+
message: Optional custom message
|
|
215
|
+
"""
|
|
216
|
+
self.source_description = source_description
|
|
217
|
+
self.discovered_count = discovered_count
|
|
218
|
+
self.valid_count = valid_count
|
|
219
|
+
|
|
220
|
+
if message is None:
|
|
221
|
+
if discovered_count == 0:
|
|
222
|
+
message = f"No contracts found in {source_description}"
|
|
223
|
+
else:
|
|
224
|
+
message = (
|
|
225
|
+
f"No valid contracts: discovered {discovered_count} but "
|
|
226
|
+
f"all failed validation in {source_description}"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
super().__init__(
|
|
230
|
+
message=message,
|
|
231
|
+
error_code=EnumCoreErrorCode.RESOURCE_NOT_FOUND,
|
|
232
|
+
source_description=source_description,
|
|
233
|
+
discovered_count=discovered_count,
|
|
234
|
+
valid_count=valid_count,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
__all__ = [
|
|
239
|
+
"ContractPublisherError",
|
|
240
|
+
"ContractSourceNotConfiguredError",
|
|
241
|
+
"ContractPublishingInfraError",
|
|
242
|
+
"NoContractsFoundError",
|
|
243
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract Publisher Models.
|
|
4
|
+
|
|
5
|
+
Re-exports all models for clean imports.
|
|
6
|
+
|
|
7
|
+
.. versionadded:: 0.3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from omnibase_infra.services.contract_publisher.models.model_contract_error import (
|
|
11
|
+
ModelContractError,
|
|
12
|
+
)
|
|
13
|
+
from omnibase_infra.services.contract_publisher.models.model_infra_error import (
|
|
14
|
+
ModelInfraError,
|
|
15
|
+
)
|
|
16
|
+
from omnibase_infra.services.contract_publisher.models.model_publish_result import (
|
|
17
|
+
ModelPublishResult,
|
|
18
|
+
)
|
|
19
|
+
from omnibase_infra.services.contract_publisher.models.model_publish_stats import (
|
|
20
|
+
ModelPublishStats,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"ModelContractError",
|
|
25
|
+
"ModelInfraError",
|
|
26
|
+
"ModelPublishResult",
|
|
27
|
+
"ModelPublishStats",
|
|
28
|
+
]
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract Error Model.
|
|
4
|
+
|
|
5
|
+
Non-fatal error for contract parsing/validation failures.
|
|
6
|
+
|
|
7
|
+
.. versionadded:: 0.3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ModelContractError(BaseModel):
|
|
18
|
+
"""Non-fatal contract-level error.
|
|
19
|
+
|
|
20
|
+
Represents errors that occur during contract parsing, validation, or
|
|
21
|
+
conflict detection. These errors are non-fatal - processing continues
|
|
22
|
+
and the error is collected in the result.
|
|
23
|
+
|
|
24
|
+
Error Types:
|
|
25
|
+
yaml_parse: YAML syntax error
|
|
26
|
+
schema_validation: Contract doesn't match ModelHandlerContract schema
|
|
27
|
+
missing_field: Required field missing (handler_id, etc.)
|
|
28
|
+
invalid_handler_class: handler_class not fully qualified Python path
|
|
29
|
+
duplicate_conflict: Same handler_id with different content hash
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
contract_path: Path or identifier of the contract that failed
|
|
33
|
+
handler_id: Handler ID if extracted, None if parsing failed early
|
|
34
|
+
error_type: Category of the error
|
|
35
|
+
message: Human-readable error description
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> error = ModelContractError(
|
|
39
|
+
... contract_path="/app/contracts/handlers/foo/contract.yaml",
|
|
40
|
+
... handler_id=None,
|
|
41
|
+
... error_type="yaml_parse",
|
|
42
|
+
... message="Invalid YAML syntax at line 10",
|
|
43
|
+
... )
|
|
44
|
+
|
|
45
|
+
.. versionadded:: 0.3.0
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
49
|
+
|
|
50
|
+
contract_path: str = Field(
|
|
51
|
+
description="Path or identifier of the contract that failed"
|
|
52
|
+
)
|
|
53
|
+
handler_id: str | None = Field(
|
|
54
|
+
default=None,
|
|
55
|
+
description="Handler ID if extracted, None if parsing failed early",
|
|
56
|
+
)
|
|
57
|
+
error_type: Literal[
|
|
58
|
+
"yaml_parse",
|
|
59
|
+
"schema_validation",
|
|
60
|
+
"missing_field",
|
|
61
|
+
"invalid_handler_class",
|
|
62
|
+
"duplicate_conflict",
|
|
63
|
+
] = Field(description="Category of the error")
|
|
64
|
+
message: str = Field(description="Human-readable error description")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
__all__ = ["ModelContractError"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Infrastructure Error Model.
|
|
4
|
+
|
|
5
|
+
Fatal error for infrastructure failures (Kafka, network, etc.).
|
|
6
|
+
|
|
7
|
+
.. versionadded:: 0.3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ModelInfraError(BaseModel):
|
|
18
|
+
"""Infrastructure error during contract publishing.
|
|
19
|
+
|
|
20
|
+
Represents errors that occur due to infrastructure failures (Kafka,
|
|
21
|
+
network, etc.). These errors are potentially fatal depending on
|
|
22
|
+
fail_fast configuration.
|
|
23
|
+
|
|
24
|
+
Error Types:
|
|
25
|
+
publisher_unavailable: Event bus publisher not available in container
|
|
26
|
+
kafka_timeout: Kafka operation timed out
|
|
27
|
+
broker_down: Kafka broker unreachable
|
|
28
|
+
publish_failed: Generic publish failure
|
|
29
|
+
serialization_failed: Failed to serialize event envelope
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
error_type: Category of the error
|
|
33
|
+
message: Human-readable error description
|
|
34
|
+
retriable: Whether the operation might succeed on retry
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
>>> error = ModelInfraError(
|
|
38
|
+
... error_type="kafka_timeout",
|
|
39
|
+
... message="Publish timed out after 30s",
|
|
40
|
+
... retriable=True,
|
|
41
|
+
... )
|
|
42
|
+
|
|
43
|
+
.. versionadded:: 0.3.0
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
47
|
+
|
|
48
|
+
error_type: Literal[
|
|
49
|
+
"publisher_unavailable",
|
|
50
|
+
"kafka_timeout",
|
|
51
|
+
"broker_down",
|
|
52
|
+
"publish_failed",
|
|
53
|
+
"serialization_failed",
|
|
54
|
+
] = Field(description="Category of the error")
|
|
55
|
+
message: str = Field(description="Human-readable error description")
|
|
56
|
+
retriable: bool = Field(
|
|
57
|
+
default=False,
|
|
58
|
+
description="Whether the operation might succeed on retry",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
__all__ = ["ModelInfraError"]
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Publish Result Model.
|
|
4
|
+
|
|
5
|
+
Complete result of contract publishing with errors and statistics.
|
|
6
|
+
|
|
7
|
+
.. versionadded:: 0.3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
from omnibase_infra.services.contract_publisher.models.model_contract_error import (
|
|
15
|
+
ModelContractError,
|
|
16
|
+
)
|
|
17
|
+
from omnibase_infra.services.contract_publisher.models.model_infra_error import (
|
|
18
|
+
ModelInfraError,
|
|
19
|
+
)
|
|
20
|
+
from omnibase_infra.services.contract_publisher.models.model_publish_stats import (
|
|
21
|
+
ModelPublishStats,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ModelPublishResult(BaseModel):
|
|
26
|
+
"""Complete result of contract publishing operation.
|
|
27
|
+
|
|
28
|
+
Contains three categories of information:
|
|
29
|
+
- published: List of handler_ids successfully published
|
|
30
|
+
- contract_errors: Non-fatal contract-level errors
|
|
31
|
+
- infra_errors: Infrastructure errors
|
|
32
|
+
- stats: Publishing statistics
|
|
33
|
+
|
|
34
|
+
The __bool__ method returns True if any contracts were published
|
|
35
|
+
successfully, enabling idiomatic conditional checks:
|
|
36
|
+
|
|
37
|
+
>>> result = await publisher.publish_all()
|
|
38
|
+
>>> if result:
|
|
39
|
+
... print(f"Published {len(result.published)} contracts")
|
|
40
|
+
... else:
|
|
41
|
+
... print("No contracts published")
|
|
42
|
+
|
|
43
|
+
Warning:
|
|
44
|
+
This model overrides __bool__ to return True only when contracts
|
|
45
|
+
are published. This differs from standard Pydantic behavior where
|
|
46
|
+
bool(model) always returns True. Use explicit checks if you need
|
|
47
|
+
to verify the model exists rather than its success state.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
published: List of handler_ids successfully published
|
|
51
|
+
contract_errors: Non-fatal contract-level errors
|
|
52
|
+
infra_errors: Infrastructure errors
|
|
53
|
+
stats: Publishing statistics
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> result = ModelPublishResult(
|
|
57
|
+
... published=["handler.foo", "handler.bar"],
|
|
58
|
+
... contract_errors=[],
|
|
59
|
+
... infra_errors=[],
|
|
60
|
+
... stats=stats,
|
|
61
|
+
... )
|
|
62
|
+
>>> if result:
|
|
63
|
+
... print("Success!")
|
|
64
|
+
|
|
65
|
+
.. versionadded:: 0.3.0
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
69
|
+
|
|
70
|
+
published: list[str] = Field(
|
|
71
|
+
description="List of handler_ids successfully published"
|
|
72
|
+
)
|
|
73
|
+
contract_errors: list[ModelContractError] = Field(
|
|
74
|
+
default_factory=list,
|
|
75
|
+
description="Non-fatal contract-level errors",
|
|
76
|
+
)
|
|
77
|
+
infra_errors: list[ModelInfraError] = Field(
|
|
78
|
+
default_factory=list,
|
|
79
|
+
description="Infrastructure errors",
|
|
80
|
+
)
|
|
81
|
+
stats: ModelPublishStats = Field(description="Publishing statistics")
|
|
82
|
+
|
|
83
|
+
def __bool__(self) -> bool:
|
|
84
|
+
"""Return True if any contracts were published successfully.
|
|
85
|
+
|
|
86
|
+
Warning:
|
|
87
|
+
This differs from standard Pydantic behavior. The model
|
|
88
|
+
evaluates to False when no contracts are published, even
|
|
89
|
+
if the model itself is valid.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
True if published list is non-empty, False otherwise.
|
|
93
|
+
"""
|
|
94
|
+
return bool(self.published)
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def has_contract_errors(self) -> bool:
|
|
98
|
+
"""Check if any contract errors occurred."""
|
|
99
|
+
return len(self.contract_errors) > 0
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def has_infra_errors(self) -> bool:
|
|
103
|
+
"""Check if any infrastructure errors occurred."""
|
|
104
|
+
return len(self.infra_errors) > 0
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def has_errors(self) -> bool:
|
|
108
|
+
"""Check if any errors (contract or infra) occurred."""
|
|
109
|
+
return self.has_contract_errors or self.has_infra_errors
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
__all__ = ["ModelPublishResult"]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Publish Statistics Model.
|
|
4
|
+
|
|
5
|
+
Statistics for contract publishing operations including counts, timing, and per-origin breakdown.
|
|
6
|
+
|
|
7
|
+
.. versionadded:: 0.3.0
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ModelPublishStats(BaseModel):
|
|
16
|
+
"""Publishing statistics for observability.
|
|
17
|
+
|
|
18
|
+
Tracks counts, timing, and per-origin breakdown for contract publishing.
|
|
19
|
+
Included in every ModelPublishResult for debugging and monitoring.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
discovered_count: Total contracts found by discovery
|
|
23
|
+
valid_count: Contracts that passed validation
|
|
24
|
+
published_count: Contracts successfully published to Kafka
|
|
25
|
+
errored_count: Contracts that failed validation
|
|
26
|
+
dedup_count: Contracts deduplicated (same handler_id + hash)
|
|
27
|
+
duration_ms: Total duration of publish_all()
|
|
28
|
+
discover_ms: Time spent in discovery phase
|
|
29
|
+
validate_ms: Time spent in validation phase
|
|
30
|
+
publish_ms: Time spent in publishing phase
|
|
31
|
+
environment: Resolved environment (for log clarity)
|
|
32
|
+
filesystem_count: Contracts from filesystem source
|
|
33
|
+
package_count: Contracts from package source
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
>>> stats = ModelPublishStats(
|
|
37
|
+
... discovered_count=10,
|
|
38
|
+
... valid_count=8,
|
|
39
|
+
... published_count=8,
|
|
40
|
+
... errored_count=2,
|
|
41
|
+
... dedup_count=0,
|
|
42
|
+
... duration_ms=1234.5,
|
|
43
|
+
... discover_ms=100.0,
|
|
44
|
+
... validate_ms=500.0,
|
|
45
|
+
... publish_ms=634.5,
|
|
46
|
+
... environment="dev",
|
|
47
|
+
... filesystem_count=10,
|
|
48
|
+
... package_count=0,
|
|
49
|
+
... )
|
|
50
|
+
|
|
51
|
+
.. versionadded:: 0.3.0
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
55
|
+
|
|
56
|
+
# Counts
|
|
57
|
+
discovered_count: int = Field(ge=0, description="Total contracts found")
|
|
58
|
+
valid_count: int = Field(ge=0, description="Contracts that passed validation")
|
|
59
|
+
published_count: int = Field(ge=0, description="Contracts successfully published")
|
|
60
|
+
errored_count: int = Field(ge=0, description="Contracts that failed validation")
|
|
61
|
+
dedup_count: int = Field(ge=0, description="Contracts deduplicated (same hash)")
|
|
62
|
+
|
|
63
|
+
# Timing (milliseconds)
|
|
64
|
+
duration_ms: float = Field(ge=0.0, description="Total duration")
|
|
65
|
+
discover_ms: float = Field(ge=0.0, description="Discovery phase duration")
|
|
66
|
+
validate_ms: float = Field(ge=0.0, description="Validation phase duration")
|
|
67
|
+
publish_ms: float = Field(ge=0.0, description="Publishing phase duration")
|
|
68
|
+
|
|
69
|
+
# Context
|
|
70
|
+
environment: str = Field(description="Resolved environment for topics")
|
|
71
|
+
|
|
72
|
+
# Per-origin breakdown
|
|
73
|
+
filesystem_count: int = Field(
|
|
74
|
+
default=0, ge=0, description="Contracts from filesystem"
|
|
75
|
+
)
|
|
76
|
+
package_count: int = Field(default=0, ge=0, description="Contracts from package")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
__all__ = ["ModelPublishStats"]
|