Repository navigation
feat(validation): add topic suffix validation utilities and contract schema [OMN-1537] #435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d1f34d9
d799a82
a118dbb
d3ddec7
5f5d9fb
1ef8938
2dbc080
d7e3ca6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,12 +18,13 @@ | |
|
|
||
| from typing import ClassVar | ||
|
|
||
| from pydantic import BaseModel, ConfigDict, Field, model_validator | ||
| from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator | ||
|
|
||
| from omnibase_core.constants import KAFKA_REQUEST_TIMEOUT_MS | ||
| from omnibase_core.enums.enum_core_error_code import EnumCoreErrorCode | ||
| from omnibase_core.models.common.model_error_context import ModelErrorContext | ||
| from omnibase_core.models.common.model_schema_value import ModelSchemaValue | ||
| from omnibase_core.models.contracts.subcontracts.model_topic_meta import ModelTopicMeta | ||
| from omnibase_core.models.errors.model_onex_error import ModelOnexError | ||
| from omnibase_core.models.primitives.model_semver import ModelSemVer | ||
|
|
||
|
|
@@ -199,6 +200,41 @@ class ModelEventBusSubcontract(BaseModel): | |
| description="Default event patterns if no other patterns can be determined", | ||
| ) | ||
|
|
||
| # Topic-based routing configuration (OMN-1537) | ||
| publish_topics: list[str] = Field( | ||
| default_factory=list, | ||
| description="Topic suffixes this node publishes to. Format: onex.{kind}.{producer}.{event-name}.v{n}", | ||
| ) | ||
|
|
||
| subscribe_topics: list[str] = Field( | ||
| default_factory=list, | ||
| description="Topic suffixes this node subscribes to. Format: onex.{kind}.{producer}.{event-name}.v{n}", | ||
| ) | ||
|
|
||
| # Extension path for future schema_ref support | ||
| publish_topic_metadata: dict[str, ModelTopicMeta] | None = Field( | ||
| default=None, | ||
| description="Optional metadata per publish topic suffix (keyed by suffix string)", | ||
| ) | ||
|
|
||
| subscribe_topic_metadata: dict[str, ModelTopicMeta] | None = Field( | ||
| default=None, | ||
| description="Optional metadata per subscribe topic suffix (keyed by suffix string)", | ||
| ) | ||
|
|
||
| @field_validator("publish_topics", "subscribe_topics", mode="after") | ||
| @classmethod | ||
| def validate_topic_suffixes(cls, topics: list[str]) -> list[str]: | ||
| """Validate each topic suffix against ONEX naming convention.""" | ||
| # Import here to avoid circular import at module load time | ||
| from omnibase_core.validation import validate_topic_suffix | ||
|
|
||
| for topic in topics: | ||
| result = validate_topic_suffix(topic) | ||
| if not result.is_valid: | ||
| raise ValueError(f"Invalid topic suffix '{topic}': {result.error}") | ||
| return topics | ||
|
Comment on lines
+225
to
+236
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use ModelOnexError for topic suffix validation failures. Example adjustment for topic in topics:
result = validate_topic_suffix(topic)
if not result.is_valid:
- raise ValueError(f"Invalid topic suffix '{topic}': {result.error}")
+ raise ModelOnexError(
+ message=f"Invalid topic suffix '{topic}': {result.error}",
+ error_code=EnumCoreErrorCode.VALIDATION_ERROR,
+ details=ModelErrorContext.with_context(
+ {
+ "error_type": ModelSchemaValue.from_value("valueerror"),
+ "validation_context": ModelSchemaValue.from_value(
+ "topic_suffix_validation"
+ ),
+ "topic": ModelSchemaValue.from_value(topic),
+ },
+ ),
+ )
return topics🤖 Prompt for AI Agents |
||
|
|
||
| @model_validator(mode="after") | ||
| def validate_event_bus_configuration(self) -> "ModelEventBusSubcontract": | ||
| """Validate event bus configuration fields after model construction.""" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| """ | ||
| Topic Metadata Model. | ||
|
|
||
| Model for topic subscription/publication metadata in ONEX event bus contracts. | ||
| Provides extension point for schema_ref and description per topic. | ||
| """ | ||
|
|
||
| from pydantic import BaseModel, ConfigDict, Field | ||
|
|
||
| __all__ = ["ModelTopicMeta"] | ||
|
|
||
|
|
||
| class ModelTopicMeta(BaseModel): | ||
| """ | ||
| Metadata for a topic subscription/publication. | ||
|
|
||
| Provides optional metadata for topics declared in publish_topics | ||
| and subscribe_topics fields, keyed by the topic suffix string. | ||
| This is an extension point for future schema validation and | ||
| documentation generation. | ||
| """ | ||
|
|
||
| schema_ref: str | None = Field( | ||
| default=None, | ||
| description="Reference to the Pydantic model for this topic's payload", | ||
| ) | ||
|
|
||
| description: str | None = Field( | ||
| default=None, | ||
| description="Human-readable description of this topic's purpose", | ||
| ) | ||
|
|
||
| model_config = ConfigDict( | ||
| frozen=True, | ||
| extra="forbid", | ||
| from_attributes=True, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: OmniNode-ai/omnibase_core
Length of output: 3705
🏁 Script executed:
Repository: OmniNode-ai/omnibase_core
Length of output: 2183
🏁 Script executed:
Repository: OmniNode-ai/omnibase_core
Length of output: 2336
🏁 Script executed:
Repository: OmniNode-ai/omnibase_core
Length of output: 21823
Remove the "intent" token from token_to_type mapping.
The
token_to_typedictionary includes"intent", but the validator andModelTopicSuffixPartsonly allowcmd,evt,dlq, andsnapshotas valid topic suffix tokens. The validator regex explicitly restricts kind to(cmd|evt|dlq|snapshot), and the centralizedVALID_TOPIC_KINDSinmodel_topic_suffix_parts.pyomits"intent". This mismatch meansget_valid_topic_suffix_kinds()will incorrectly include"intent"even though it fails validation. Remove"intent"from the token map to align with the validator's canonical definition.Proposed fix
token_to_type: dict[str, EnumTopicType] = { "cmd": EnumTopicType.COMMANDS, "dlq": EnumTopicType.DLQ, "evt": EnumTopicType.EVENTS, - "intent": EnumTopicType.INTENTS, "snapshot": EnumTopicType.SNAPSHOTS, }🤖 Prompt for AI Agents