omnibase_infra 0.3.1__py3-none-any.whl → 0.4.0__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 +3 -0
- omnibase_infra/enums/enum_consumer_group_purpose.py +9 -0
- omnibase_infra/enums/enum_postgres_error_code.py +188 -0
- omnibase_infra/errors/__init__.py +4 -0
- omnibase_infra/errors/error_infra.py +60 -0
- omnibase_infra/handlers/__init__.py +3 -0
- omnibase_infra/handlers/handler_slack_webhook.py +426 -0
- omnibase_infra/handlers/models/__init__.py +14 -0
- omnibase_infra/handlers/models/enum_alert_severity.py +36 -0
- omnibase_infra/handlers/models/model_slack_alert.py +24 -0
- omnibase_infra/handlers/models/model_slack_alert_payload.py +77 -0
- omnibase_infra/handlers/models/model_slack_alert_result.py +73 -0
- omnibase_infra/handlers/registration_storage/handler_registration_storage_postgres.py +29 -20
- omnibase_infra/mixins/__init__.py +14 -0
- omnibase_infra/mixins/mixin_node_introspection.py +42 -20
- omnibase_infra/mixins/mixin_postgres_error_response.py +314 -0
- omnibase_infra/mixins/mixin_postgres_op_executor.py +298 -0
- omnibase_infra/models/__init__.py +3 -0
- omnibase_infra/models/discovery/model_dependency_spec.py +1 -0
- omnibase_infra/models/discovery/model_discovered_capabilities.py +1 -1
- omnibase_infra/models/discovery/model_introspection_config.py +28 -1
- omnibase_infra/models/discovery/model_introspection_performance_metrics.py +1 -0
- omnibase_infra/models/discovery/model_introspection_task_config.py +1 -0
- omnibase_infra/{nodes/effects/models → models}/model_backend_result.py +22 -6
- omnibase_infra/models/projection/__init__.py +11 -0
- omnibase_infra/models/projection/model_contract_projection.py +170 -0
- omnibase_infra/models/projection/model_topic_projection.py +148 -0
- omnibase_infra/models/runtime/__init__.py +4 -0
- omnibase_infra/models/runtime/model_resolved_dependencies.py +116 -0
- omnibase_infra/nodes/contract_registry_reducer/__init__.py +5 -0
- omnibase_infra/nodes/contract_registry_reducer/contract.yaml +6 -5
- omnibase_infra/nodes/contract_registry_reducer/contract_registration_event_router.py +689 -0
- omnibase_infra/nodes/contract_registry_reducer/reducer.py +9 -26
- omnibase_infra/nodes/effects/__init__.py +1 -1
- omnibase_infra/nodes/effects/models/__init__.py +6 -4
- omnibase_infra/nodes/effects/models/model_registry_response.py +1 -1
- omnibase_infra/nodes/effects/protocol_consul_client.py +1 -1
- omnibase_infra/nodes/effects/protocol_postgres_adapter.py +1 -1
- omnibase_infra/nodes/effects/registry_effect.py +1 -1
- omnibase_infra/nodes/node_contract_persistence_effect/__init__.py +101 -0
- omnibase_infra/nodes/node_contract_persistence_effect/contract.yaml +490 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/__init__.py +74 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_cleanup_topics.py +217 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_contract_upsert.py +242 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_deactivate.py +194 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_heartbeat.py +243 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_mark_stale.py +208 -0
- omnibase_infra/nodes/node_contract_persistence_effect/handlers/handler_postgres_topic_update.py +298 -0
- omnibase_infra/nodes/node_contract_persistence_effect/models/__init__.py +15 -0
- omnibase_infra/nodes/node_contract_persistence_effect/models/model_persistence_result.py +52 -0
- omnibase_infra/nodes/node_contract_persistence_effect/node.py +131 -0
- omnibase_infra/nodes/node_contract_persistence_effect/registry/__init__.py +27 -0
- omnibase_infra/nodes/node_contract_persistence_effect/registry/registry_infra_contract_persistence_effect.py +251 -0
- omnibase_infra/nodes/node_registration_orchestrator/models/model_postgres_intent_payload.py +8 -12
- omnibase_infra/nodes/node_registry_effect/models/__init__.py +2 -2
- omnibase_infra/nodes/node_slack_alerter_effect/__init__.py +33 -0
- omnibase_infra/nodes/node_slack_alerter_effect/contract.yaml +291 -0
- omnibase_infra/nodes/node_slack_alerter_effect/node.py +106 -0
- omnibase_infra/projectors/__init__.py +6 -0
- omnibase_infra/projectors/projection_reader_contract.py +1301 -0
- omnibase_infra/runtime/__init__.py +12 -0
- omnibase_infra/runtime/baseline_subscriptions.py +13 -6
- omnibase_infra/runtime/contract_dependency_resolver.py +455 -0
- omnibase_infra/runtime/contract_registration_event_router.py +500 -0
- omnibase_infra/runtime/db/__init__.py +4 -0
- omnibase_infra/runtime/db/models/__init__.py +15 -10
- omnibase_infra/runtime/db/models/model_db_operation.py +40 -0
- omnibase_infra/runtime/db/models/model_db_param.py +24 -0
- omnibase_infra/runtime/db/models/model_db_repository_contract.py +40 -0
- omnibase_infra/runtime/db/models/model_db_return.py +26 -0
- omnibase_infra/runtime/db/models/model_db_safety_policy.py +32 -0
- omnibase_infra/runtime/emit_daemon/event_registry.py +34 -22
- omnibase_infra/runtime/event_bus_subcontract_wiring.py +63 -23
- omnibase_infra/runtime/intent_execution_router.py +430 -0
- omnibase_infra/runtime/models/__init__.py +6 -0
- omnibase_infra/runtime/models/model_contract_registry_config.py +41 -0
- omnibase_infra/runtime/models/model_intent_execution_summary.py +79 -0
- omnibase_infra/runtime/models/model_runtime_config.py +8 -0
- omnibase_infra/runtime/protocols/__init__.py +16 -0
- omnibase_infra/runtime/protocols/protocol_intent_executor.py +107 -0
- omnibase_infra/runtime/publisher_topic_scoped.py +16 -11
- omnibase_infra/runtime/registry_policy.py +29 -15
- omnibase_infra/runtime/request_response_wiring.py +793 -0
- omnibase_infra/runtime/service_kernel.py +295 -8
- omnibase_infra/runtime/service_runtime_host_process.py +149 -5
- omnibase_infra/runtime/util_version.py +5 -1
- omnibase_infra/schemas/schema_latency_baseline.sql +135 -0
- omnibase_infra/services/contract_publisher/config.py +4 -4
- omnibase_infra/services/contract_publisher/service.py +8 -5
- omnibase_infra/services/observability/injection_effectiveness/__init__.py +67 -0
- omnibase_infra/services/observability/injection_effectiveness/config.py +295 -0
- omnibase_infra/services/observability/injection_effectiveness/consumer.py +1461 -0
- omnibase_infra/services/observability/injection_effectiveness/models/__init__.py +32 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_agent_match.py +79 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_context_utilization.py +118 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_latency_breakdown.py +107 -0
- omnibase_infra/services/observability/injection_effectiveness/models/model_pattern_utilization.py +46 -0
- omnibase_infra/services/observability/injection_effectiveness/writer_postgres.py +596 -0
- omnibase_infra/services/registry_api/models/__init__.py +25 -0
- omnibase_infra/services/registry_api/models/model_contract_ref.py +44 -0
- omnibase_infra/services/registry_api/models/model_contract_view.py +81 -0
- omnibase_infra/services/registry_api/models/model_response_contracts.py +50 -0
- omnibase_infra/services/registry_api/models/model_response_topics.py +50 -0
- omnibase_infra/services/registry_api/models/model_topic_summary.py +57 -0
- omnibase_infra/services/registry_api/models/model_topic_view.py +63 -0
- omnibase_infra/services/registry_api/routes.py +205 -6
- omnibase_infra/services/registry_api/service.py +528 -1
- omnibase_infra/utils/__init__.py +7 -0
- omnibase_infra/utils/util_db_error_context.py +292 -0
- omnibase_infra/validation/infra_validators.py +3 -1
- omnibase_infra/validation/validation_exemptions.yaml +65 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/METADATA +3 -3
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/RECORD +117 -58
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/WHEEL +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/entry_points.txt +0 -0
- {omnibase_infra-0.3.1.dist-info → omnibase_infra-0.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract reference model for dashboard display.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ModelContractRef(BaseModel):
|
|
15
|
+
"""Lightweight contract reference.
|
|
16
|
+
|
|
17
|
+
Used to reference a contract without including full details,
|
|
18
|
+
suitable for embedding in topic views.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
contract_id: Unique identifier in format node_name:major.minor.patch
|
|
22
|
+
node_name: Name of the node this contract belongs to
|
|
23
|
+
version: Semantic version string in format major.minor.patch
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
27
|
+
|
|
28
|
+
# ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
|
|
29
|
+
contract_id: str = Field(
|
|
30
|
+
...,
|
|
31
|
+
description="Unique identifier in format node_name:major.minor.patch",
|
|
32
|
+
)
|
|
33
|
+
# ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
|
|
34
|
+
node_name: str = Field(
|
|
35
|
+
...,
|
|
36
|
+
description="Name of the node this contract belongs to",
|
|
37
|
+
)
|
|
38
|
+
version: str = Field(
|
|
39
|
+
...,
|
|
40
|
+
description="Semantic version string in format major.minor.patch",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
__all__ = ["ModelContractRef"]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Contract view model for dashboard display.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ModelContractView(BaseModel):
|
|
17
|
+
"""Contract detail for API responses.
|
|
18
|
+
|
|
19
|
+
Represents a registered contract from the contract registry,
|
|
20
|
+
flattened for dashboard consumption.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
contract_id: Unique identifier in format node_name:major.minor.patch
|
|
24
|
+
node_name: Name of the node this contract belongs to
|
|
25
|
+
version: Semantic version string in format major.minor.patch
|
|
26
|
+
contract_hash: SHA-256 hash of contract content for integrity verification
|
|
27
|
+
is_active: Whether the contract is currently active
|
|
28
|
+
registered_at: Timestamp of initial registration
|
|
29
|
+
last_seen_at: Timestamp of last activity (heartbeat or event)
|
|
30
|
+
deregistered_at: Timestamp when contract was deregistered (None if active)
|
|
31
|
+
topics_published: List of topic suffixes this contract publishes to
|
|
32
|
+
topics_subscribed: List of topic suffixes this contract subscribes to
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
36
|
+
|
|
37
|
+
# ONEX_EXCLUDE: pattern_validator - contract_id is a derived natural key (name:version), not UUID
|
|
38
|
+
contract_id: str = Field(
|
|
39
|
+
...,
|
|
40
|
+
description="Unique identifier in format node_name:major.minor.patch",
|
|
41
|
+
)
|
|
42
|
+
# ONEX_EXCLUDE: pattern_validator - node_name is the contract name, not an entity reference
|
|
43
|
+
node_name: str = Field(
|
|
44
|
+
...,
|
|
45
|
+
description="Name of the node this contract belongs to",
|
|
46
|
+
)
|
|
47
|
+
version: str = Field(
|
|
48
|
+
...,
|
|
49
|
+
description="Semantic version string in format major.minor.patch",
|
|
50
|
+
)
|
|
51
|
+
contract_hash: str = Field(
|
|
52
|
+
...,
|
|
53
|
+
description="SHA-256 hash of contract content for integrity verification",
|
|
54
|
+
)
|
|
55
|
+
is_active: bool = Field(
|
|
56
|
+
...,
|
|
57
|
+
description="Whether the contract is currently active",
|
|
58
|
+
)
|
|
59
|
+
registered_at: datetime = Field(
|
|
60
|
+
...,
|
|
61
|
+
description="Timestamp of initial registration",
|
|
62
|
+
)
|
|
63
|
+
last_seen_at: datetime = Field(
|
|
64
|
+
...,
|
|
65
|
+
description="Timestamp of last activity (heartbeat or event)",
|
|
66
|
+
)
|
|
67
|
+
deregistered_at: datetime | None = Field(
|
|
68
|
+
default=None,
|
|
69
|
+
description="Timestamp when contract was deregistered (None if active)",
|
|
70
|
+
)
|
|
71
|
+
topics_published: tuple[str, ...] = Field(
|
|
72
|
+
default_factory=tuple,
|
|
73
|
+
description="List of topic suffixes this contract publishes to",
|
|
74
|
+
)
|
|
75
|
+
topics_subscribed: tuple[str, ...] = Field(
|
|
76
|
+
default_factory=tuple,
|
|
77
|
+
description="List of topic suffixes this contract subscribes to",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
__all__ = ["ModelContractView"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Response model for list_contracts endpoint.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
from omnibase_infra.services.registry_api.models.model_contract_view import (
|
|
14
|
+
ModelContractView,
|
|
15
|
+
)
|
|
16
|
+
from omnibase_infra.services.registry_api.models.model_pagination_info import (
|
|
17
|
+
ModelPaginationInfo,
|
|
18
|
+
)
|
|
19
|
+
from omnibase_infra.services.registry_api.models.model_warning import ModelWarning
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ModelResponseListContracts(BaseModel):
|
|
23
|
+
"""Response model for the GET /registry/contracts endpoint.
|
|
24
|
+
|
|
25
|
+
Provides a paginated list of registered contracts with optional warnings
|
|
26
|
+
for partial success scenarios.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
contracts: List of registered contracts matching the query
|
|
30
|
+
pagination: Pagination information for the result set
|
|
31
|
+
warnings: List of warnings for partial success scenarios
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
35
|
+
|
|
36
|
+
contracts: list[ModelContractView] = Field(
|
|
37
|
+
default_factory=list,
|
|
38
|
+
description="List of registered contracts matching the query",
|
|
39
|
+
)
|
|
40
|
+
pagination: ModelPaginationInfo = Field(
|
|
41
|
+
...,
|
|
42
|
+
description="Pagination information for the result set",
|
|
43
|
+
)
|
|
44
|
+
warnings: list[ModelWarning] = Field(
|
|
45
|
+
default_factory=list,
|
|
46
|
+
description="Warnings for partial success scenarios",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["ModelResponseListContracts"]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Response model for list_topics endpoint.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
12
|
+
|
|
13
|
+
from omnibase_infra.services.registry_api.models.model_pagination_info import (
|
|
14
|
+
ModelPaginationInfo,
|
|
15
|
+
)
|
|
16
|
+
from omnibase_infra.services.registry_api.models.model_topic_summary import (
|
|
17
|
+
ModelTopicSummary,
|
|
18
|
+
)
|
|
19
|
+
from omnibase_infra.services.registry_api.models.model_warning import ModelWarning
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ModelResponseListTopics(BaseModel):
|
|
23
|
+
"""Response model for the GET /registry/topics endpoint.
|
|
24
|
+
|
|
25
|
+
Provides a paginated list of topics with optional warnings
|
|
26
|
+
for partial success scenarios.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
topics: List of topic summaries matching the query
|
|
30
|
+
pagination: Pagination information for the result set
|
|
31
|
+
warnings: List of warnings for partial success scenarios
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
35
|
+
|
|
36
|
+
topics: list[ModelTopicSummary] = Field(
|
|
37
|
+
default_factory=list,
|
|
38
|
+
description="List of topic summaries matching the query",
|
|
39
|
+
)
|
|
40
|
+
pagination: ModelPaginationInfo = Field(
|
|
41
|
+
...,
|
|
42
|
+
description="Pagination information for the result set",
|
|
43
|
+
)
|
|
44
|
+
warnings: list[ModelWarning] = Field(
|
|
45
|
+
default_factory=list,
|
|
46
|
+
description="Warnings for partial success scenarios",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["ModelResponseListTopics"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Topic summary model for dashboard display.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
from omnibase_infra.models.projection.model_topic_projection import TopicDirection
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ModelTopicSummary(BaseModel):
|
|
19
|
+
"""Topic summary for list view.
|
|
20
|
+
|
|
21
|
+
Provides a compact view of a topic suitable for list endpoints,
|
|
22
|
+
including direction (publish/subscribe) and contract count.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
topic_suffix: Topic suffix (without environment prefix)
|
|
26
|
+
direction: Relationship direction ('publish' or 'subscribe')
|
|
27
|
+
contract_count: Number of contracts with this topic relationship
|
|
28
|
+
last_seen_at: Timestamp of last activity on this topic
|
|
29
|
+
is_active: Whether the topic has active contracts
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
33
|
+
|
|
34
|
+
topic_suffix: str = Field(
|
|
35
|
+
...,
|
|
36
|
+
description="Topic suffix (without environment prefix)",
|
|
37
|
+
)
|
|
38
|
+
direction: TopicDirection = Field(
|
|
39
|
+
...,
|
|
40
|
+
description="Relationship direction ('publish' or 'subscribe')",
|
|
41
|
+
)
|
|
42
|
+
contract_count: int = Field(
|
|
43
|
+
...,
|
|
44
|
+
ge=0,
|
|
45
|
+
description="Number of contracts with this topic relationship",
|
|
46
|
+
)
|
|
47
|
+
last_seen_at: datetime = Field(
|
|
48
|
+
...,
|
|
49
|
+
description="Timestamp of last activity on this topic",
|
|
50
|
+
)
|
|
51
|
+
is_active: bool = Field(
|
|
52
|
+
...,
|
|
53
|
+
description="Whether the topic has active contracts",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
__all__ = ["ModelTopicSummary"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2025 OmniNode Team
|
|
3
|
+
"""Topic view model for dashboard display.
|
|
4
|
+
|
|
5
|
+
Related Tickets:
|
|
6
|
+
- OMN-1845: Contract Registry Persistence
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
|
+
|
|
15
|
+
from omnibase_infra.services.registry_api.models.model_contract_ref import (
|
|
16
|
+
ModelContractRef,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ModelTopicView(BaseModel):
|
|
21
|
+
"""Topic detail for API responses.
|
|
22
|
+
|
|
23
|
+
Represents a topic with its publishers and subscribers,
|
|
24
|
+
providing full detail for topic inspection endpoints.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
topic_suffix: Topic suffix (without environment prefix)
|
|
28
|
+
publishers: List of contracts that publish to this topic
|
|
29
|
+
subscribers: List of contracts that subscribe to this topic
|
|
30
|
+
first_seen_at: Timestamp when topic was first observed
|
|
31
|
+
last_seen_at: Timestamp of last activity on this topic
|
|
32
|
+
is_active: Whether the topic has active publishers or subscribers
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
|
|
36
|
+
|
|
37
|
+
topic_suffix: str = Field(
|
|
38
|
+
...,
|
|
39
|
+
description="Topic suffix (without environment prefix)",
|
|
40
|
+
)
|
|
41
|
+
publishers: list[ModelContractRef] = Field(
|
|
42
|
+
default_factory=list,
|
|
43
|
+
description="List of contracts that publish to this topic",
|
|
44
|
+
)
|
|
45
|
+
subscribers: list[ModelContractRef] = Field(
|
|
46
|
+
default_factory=list,
|
|
47
|
+
description="List of contracts that subscribe to this topic",
|
|
48
|
+
)
|
|
49
|
+
first_seen_at: datetime = Field(
|
|
50
|
+
...,
|
|
51
|
+
description="Timestamp when topic was first observed",
|
|
52
|
+
)
|
|
53
|
+
last_seen_at: datetime = Field(
|
|
54
|
+
...,
|
|
55
|
+
description="Timestamp of last activity on this topic",
|
|
56
|
+
)
|
|
57
|
+
is_active: bool = Field(
|
|
58
|
+
...,
|
|
59
|
+
description="Whether the topic has active publishers or subscribers",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
__all__ = ["ModelTopicView"]
|
|
@@ -6,15 +6,20 @@ FastAPI route handlers for the Registry API. Routes are defined as an
|
|
|
6
6
|
APIRouter for easy mounting into the main FastAPI application.
|
|
7
7
|
|
|
8
8
|
Endpoint Summary:
|
|
9
|
-
GET /registry/discovery
|
|
10
|
-
GET /registry/nodes
|
|
11
|
-
GET /registry/nodes/{id}
|
|
12
|
-
GET /registry/instances
|
|
13
|
-
GET /registry/widgets/mapping
|
|
14
|
-
GET /registry/health
|
|
9
|
+
GET /registry/discovery - Full dashboard payload
|
|
10
|
+
GET /registry/nodes - Node list with pagination
|
|
11
|
+
GET /registry/nodes/{id} - Single node detail
|
|
12
|
+
GET /registry/instances - Live Consul instances
|
|
13
|
+
GET /registry/widgets/mapping - Widget mapping configuration
|
|
14
|
+
GET /registry/health - Service health check
|
|
15
|
+
GET /registry/contracts - Contract list with pagination
|
|
16
|
+
GET /registry/contracts/{id} - Single contract detail
|
|
17
|
+
GET /registry/topics - Topic list with pagination
|
|
18
|
+
GET /registry/topics/{suffix} - Single topic detail
|
|
15
19
|
|
|
16
20
|
Related Tickets:
|
|
17
21
|
- OMN-1278: Contract-Driven Dashboard - Registry Discovery
|
|
22
|
+
- OMN-1845: Contract Registry Persistence
|
|
18
23
|
"""
|
|
19
24
|
|
|
20
25
|
from __future__ import annotations
|
|
@@ -26,11 +31,15 @@ from fastapi import APIRouter, Depends, Header, HTTPException, Query, Request, s
|
|
|
26
31
|
|
|
27
32
|
from omnibase_infra.enums import EnumRegistrationState
|
|
28
33
|
from omnibase_infra.services.registry_api.models import (
|
|
34
|
+
ModelContractView,
|
|
29
35
|
ModelRegistryDiscoveryResponse,
|
|
30
36
|
ModelRegistryHealthResponse,
|
|
31
37
|
ModelRegistryNodeView,
|
|
38
|
+
ModelResponseListContracts,
|
|
32
39
|
ModelResponseListInstances,
|
|
33
40
|
ModelResponseListNodes,
|
|
41
|
+
ModelResponseListTopics,
|
|
42
|
+
ModelTopicView,
|
|
34
43
|
ModelWidgetMapping,
|
|
35
44
|
)
|
|
36
45
|
|
|
@@ -368,4 +377,194 @@ async def health_check(
|
|
|
368
377
|
return response
|
|
369
378
|
|
|
370
379
|
|
|
380
|
+
# ============================================================
|
|
381
|
+
# Contract Registry Endpoints
|
|
382
|
+
# ============================================================
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@router.get(
|
|
386
|
+
"/contracts",
|
|
387
|
+
response_model=ModelResponseListContracts,
|
|
388
|
+
summary="List Registered Contracts",
|
|
389
|
+
description=(
|
|
390
|
+
"Returns a paginated list of registered contracts from the contract registry. "
|
|
391
|
+
"Supports filtering by node name and active status."
|
|
392
|
+
),
|
|
393
|
+
responses={
|
|
394
|
+
400: {"description": "Bad request (e.g., invalid correlation ID format)"},
|
|
395
|
+
200: {"description": "Successful response with contract list"},
|
|
396
|
+
},
|
|
397
|
+
)
|
|
398
|
+
async def list_contracts(
|
|
399
|
+
service: Annotated[ServiceRegistryDiscovery, Depends(get_service)],
|
|
400
|
+
correlation_id: Annotated[UUID, Depends(get_correlation_id)],
|
|
401
|
+
limit: Annotated[
|
|
402
|
+
int,
|
|
403
|
+
Query(ge=1, le=1000, description="Maximum number of contracts to return"),
|
|
404
|
+
] = 100,
|
|
405
|
+
offset: Annotated[
|
|
406
|
+
int,
|
|
407
|
+
Query(ge=0, description="Number of contracts to skip for pagination"),
|
|
408
|
+
] = 0,
|
|
409
|
+
active_only: Annotated[
|
|
410
|
+
bool,
|
|
411
|
+
Query(description="If true, return only active contracts"),
|
|
412
|
+
] = True,
|
|
413
|
+
node_name: Annotated[
|
|
414
|
+
str | None,
|
|
415
|
+
Query(description="Filter by node name"),
|
|
416
|
+
] = None,
|
|
417
|
+
) -> ModelResponseListContracts:
|
|
418
|
+
"""List registered contracts with pagination and optional filtering."""
|
|
419
|
+
contracts, pagination, warnings = await service.list_contracts(
|
|
420
|
+
limit=limit,
|
|
421
|
+
offset=offset,
|
|
422
|
+
active_only=active_only,
|
|
423
|
+
node_name=node_name,
|
|
424
|
+
correlation_id=correlation_id,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
return ModelResponseListContracts(
|
|
428
|
+
contracts=contracts,
|
|
429
|
+
pagination=pagination,
|
|
430
|
+
warnings=warnings,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
@router.get(
|
|
435
|
+
"/contracts/{contract_id:path}",
|
|
436
|
+
response_model=ModelContractView,
|
|
437
|
+
summary="Get Contract Details",
|
|
438
|
+
description=(
|
|
439
|
+
"Returns detailed information for a single contract by ID. "
|
|
440
|
+
"Includes lists of topics the contract publishes to and subscribes from."
|
|
441
|
+
),
|
|
442
|
+
responses={
|
|
443
|
+
400: {"description": "Bad request (e.g., invalid correlation ID format)"},
|
|
444
|
+
200: {"description": "Successful response with contract details"},
|
|
445
|
+
404: {"description": "Contract not found"},
|
|
446
|
+
},
|
|
447
|
+
)
|
|
448
|
+
async def get_contract(
|
|
449
|
+
contract_id: str,
|
|
450
|
+
service: Annotated[ServiceRegistryDiscovery, Depends(get_service)],
|
|
451
|
+
correlation_id: Annotated[UUID, Depends(get_correlation_id)],
|
|
452
|
+
) -> ModelContractView:
|
|
453
|
+
"""Get a single contract by ID."""
|
|
454
|
+
contract, warnings = await service.get_contract(
|
|
455
|
+
contract_id=contract_id,
|
|
456
|
+
correlation_id=correlation_id,
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
if contract is None:
|
|
460
|
+
# Check if it was a service error or genuinely not found
|
|
461
|
+
if warnings:
|
|
462
|
+
raise HTTPException(
|
|
463
|
+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
464
|
+
detail=f"Service error: {warnings[0].message}",
|
|
465
|
+
headers={"X-Correlation-ID": str(correlation_id)},
|
|
466
|
+
)
|
|
467
|
+
raise HTTPException(
|
|
468
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
469
|
+
detail=f"Contract not found: {contract_id}",
|
|
470
|
+
headers={"X-Correlation-ID": str(correlation_id)},
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
return contract
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
@router.get(
|
|
477
|
+
"/topics",
|
|
478
|
+
response_model=ModelResponseListTopics,
|
|
479
|
+
summary="List Event Topics",
|
|
480
|
+
description=(
|
|
481
|
+
"Returns a paginated list of event topics from the contract registry. "
|
|
482
|
+
"Supports filtering by direction (publish/subscribe)."
|
|
483
|
+
),
|
|
484
|
+
responses={
|
|
485
|
+
400: {"description": "Bad request (e.g., invalid correlation ID format)"},
|
|
486
|
+
200: {"description": "Successful response with topic list"},
|
|
487
|
+
},
|
|
488
|
+
)
|
|
489
|
+
async def list_topics(
|
|
490
|
+
service: Annotated[ServiceRegistryDiscovery, Depends(get_service)],
|
|
491
|
+
correlation_id: Annotated[UUID, Depends(get_correlation_id)],
|
|
492
|
+
direction: Annotated[
|
|
493
|
+
str | None,
|
|
494
|
+
Query(description="Filter by direction ('publish' or 'subscribe')"),
|
|
495
|
+
] = None,
|
|
496
|
+
limit: Annotated[
|
|
497
|
+
int,
|
|
498
|
+
Query(ge=1, le=1000, description="Maximum number of topics to return"),
|
|
499
|
+
] = 100,
|
|
500
|
+
offset: Annotated[
|
|
501
|
+
int,
|
|
502
|
+
Query(ge=0, description="Number of topics to skip for pagination"),
|
|
503
|
+
] = 0,
|
|
504
|
+
) -> ModelResponseListTopics:
|
|
505
|
+
"""List event topics with pagination and optional filtering."""
|
|
506
|
+
# Validate direction if provided
|
|
507
|
+
if direction is not None and direction not in ("publish", "subscribe"):
|
|
508
|
+
raise HTTPException(
|
|
509
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
510
|
+
detail=f"Invalid direction: {direction}. Must be 'publish' or 'subscribe'.",
|
|
511
|
+
headers={"X-Correlation-ID": str(correlation_id)},
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
topics, pagination, warnings = await service.list_topics(
|
|
515
|
+
direction=direction,
|
|
516
|
+
limit=limit,
|
|
517
|
+
offset=offset,
|
|
518
|
+
correlation_id=correlation_id,
|
|
519
|
+
)
|
|
520
|
+
|
|
521
|
+
return ModelResponseListTopics(
|
|
522
|
+
topics=topics,
|
|
523
|
+
pagination=pagination,
|
|
524
|
+
warnings=warnings,
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
@router.get(
|
|
529
|
+
"/topics/{topic_suffix:path}",
|
|
530
|
+
response_model=ModelTopicView,
|
|
531
|
+
summary="Get Topic Details",
|
|
532
|
+
description=(
|
|
533
|
+
"Returns detailed information for a single topic by suffix. "
|
|
534
|
+
"Includes lists of publishers and subscribers with contract references."
|
|
535
|
+
),
|
|
536
|
+
responses={
|
|
537
|
+
400: {"description": "Bad request (e.g., invalid correlation ID format)"},
|
|
538
|
+
200: {"description": "Successful response with topic details"},
|
|
539
|
+
404: {"description": "Topic not found"},
|
|
540
|
+
},
|
|
541
|
+
)
|
|
542
|
+
async def get_topic(
|
|
543
|
+
topic_suffix: str,
|
|
544
|
+
service: Annotated[ServiceRegistryDiscovery, Depends(get_service)],
|
|
545
|
+
correlation_id: Annotated[UUID, Depends(get_correlation_id)],
|
|
546
|
+
) -> ModelTopicView:
|
|
547
|
+
"""Get a single topic by suffix."""
|
|
548
|
+
topic, warnings = await service.get_topic_detail(
|
|
549
|
+
topic_suffix=topic_suffix,
|
|
550
|
+
correlation_id=correlation_id,
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if topic is None:
|
|
554
|
+
# Check if it was a service error or genuinely not found
|
|
555
|
+
if warnings:
|
|
556
|
+
raise HTTPException(
|
|
557
|
+
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
|
558
|
+
detail=f"Service error: {warnings[0].message}",
|
|
559
|
+
headers={"X-Correlation-ID": str(correlation_id)},
|
|
560
|
+
)
|
|
561
|
+
raise HTTPException(
|
|
562
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
563
|
+
detail=f"Topic not found: {topic_suffix}",
|
|
564
|
+
headers={"X-Correlation-ID": str(correlation_id)},
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
return topic
|
|
568
|
+
|
|
569
|
+
|
|
371
570
|
__all__ = ["router", "get_service", "get_correlation_id"]
|