This is an automated email from the ASF dual-hosted git repository.

FreeOnePlus pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris-mcp-server.git


The following commit(s) were added to refs/heads/master by this push:
     new 9f211dc  fix(semantic): accept exact model revision metadata
9f211dc is described below

commit 9f211dc989ccfee1218d8363bd300430434b7950
Author: Yijia Su <[email protected]>
AuthorDate: Wed Aug 12 19:43:00 2026 +0800

    fix(semantic): accept exact model revision metadata
---
 CHANGELOG.md                                   |   2 +
 doris_mcp_server/semantic/loader.py            |   3 +-
 doris_mcp_server/semantic/metricflow.py        |   3 +-
 doris_mcp_server/semantic/models.py            |   2 +
 doris_mcp_server/semantic/runtime.py           |   3 +-
 doris_mcp_server/tools/domain_catalog.py       |   9 +-
 test/integration/test_real_doris_transports.py | 196 +++++++++++++++++++++++--
 test/semantic/test_metricflow_runtime.py       |  15 +-
 test/semantic/test_ossie_loader.py             |  45 +++++-
 test/semantic/test_semantic_runtime.py         |  24 ++-
 test/tools/test_domain_catalog.py              |  48 ++++++
 11 files changed, 322 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 824d6f5..17f16f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -65,6 +65,8 @@ under **Unreleased** until a new version is selected and 
published.
 
 ### Fixed
 
+- Accepted `+` revision metadata in exact Ossie and MetricFlow model
+  references across binding loading, runtime lookup, and public Child schemas.
 - Enforced the canonical read-only SQL allowlist again at the production
   connection boundary, including calls without an authentication context, and
   restricted internal session changes to an exact `USE`/`SWITCH`/profile
diff --git a/doris_mcp_server/semantic/loader.py 
b/doris_mcp_server/semantic/loader.py
index 4323497..fc8f9da 100644
--- a/doris_mcp_server/semantic/loader.py
+++ b/doris_mcp_server/semantic/loader.py
@@ -39,6 +39,7 @@ from .models import (
     OSSIE_BINDING_API_VERSION,
     OSSIE_SCHEMA_SHA256,
     OSSIE_SPEC_VERSION,
+    SEMANTIC_MODEL_REF_PATTERN,
     DatasetBinding,
     SemanticAIContext,
     SemanticDataset,
@@ -49,7 +50,7 @@ from .models import (
     SemanticRelationship,
 )
 
-_MODEL_REF_RE = re.compile(r"^[A-Za-z0-9_.:/@-]{1,192}$")
+_MODEL_REF_RE = re.compile(SEMANTIC_MODEL_REF_PATTERN)
 _NAME_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_.-]{0,127}$")
 _SELECTOR_COMPONENT_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_-]{0,127}$")
 _SOURCE_RE = re.compile(
diff --git a/doris_mcp_server/semantic/metricflow.py 
b/doris_mcp_server/semantic/metricflow.py
index 3c881f0..49a5684 100644
--- a/doris_mcp_server/semantic/metricflow.py
+++ b/doris_mcp_server/semantic/metricflow.py
@@ -27,10 +27,11 @@ from pathlib import Path
 from typing import Any, Protocol, cast
 
 from ..utils.query_runtime import DorisQueryRuntime, ReadOnlySQLGuard
+from .models import SEMANTIC_MODEL_REF_PATTERN
 from .runtime import SemanticRuntimeFailure
 
 METRICFLOW_PROVIDER_PROTOCOL = "doris-mcp-metricflow/v1"
-_MODEL_REF_RE = re.compile(r"^[A-Za-z0-9_.:/@-]{1,192}$")
+_MODEL_REF_RE = re.compile(SEMANTIC_MODEL_REF_PATTERN)
 _PROVIDER_OPERATIONS = frozenset(
     {
         "list_models",
diff --git a/doris_mcp_server/semantic/models.py 
b/doris_mcp_server/semantic/models.py
index 9417d97..3844e3e 100644
--- a/doris_mcp_server/semantic/models.py
+++ b/doris_mcp_server/semantic/models.py
@@ -29,6 +29,7 @@ OSSIE_SPEC_COMMIT = "9ffc3be3886e82fddc9bbf28722440864644d371"
 OSSIE_SCHEMA_SHA256 = 
"8ce9f82aa92080265f9ae119e31cda5bef062f489674d3c467245c2d4c5ff264"
 OSSIE_ADAPTER_VERSION = "doris-mcp-ossie/v1alpha1"
 OSSIE_BINDING_API_VERSION = "doris-mcp.apache.org/ossie-binding/v1alpha1"
+SEMANTIC_MODEL_REF_PATTERN = 
r"^(?=.{1,192}$)[A-Za-z0-9_.:/@-]+(?:\+[A-Za-z0-9_.-]+)*$"
 
 
 @dataclass(frozen=True, slots=True)
@@ -195,6 +196,7 @@ __all__ = [
     "ResolvedDataset",
     "ResolvedField",
     "ResolvedSemanticModel",
+    "SEMANTIC_MODEL_REF_PATTERN",
     "SemanticAIContext",
     "SemanticDataset",
     "SemanticExpression",
diff --git a/doris_mcp_server/semantic/runtime.py 
b/doris_mcp_server/semantic/runtime.py
index be44056..2f6bb9d 100644
--- a/doris_mcp_server/semantic/runtime.py
+++ b/doris_mcp_server/semantic/runtime.py
@@ -42,6 +42,7 @@ from .models import (
     OSSIE_SCHEMA_SHA256,
     OSSIE_SPEC_COMMIT,
     OSSIE_SPEC_VERSION,
+    SEMANTIC_MODEL_REF_PATTERN,
     DatasetBinding,
     ResolvedDataset,
     ResolvedField,
@@ -56,7 +57,7 @@ from .models import (
 
 _MAX_QUERY_ROWS = 2048
 _MAX_QUERY_BYTES = 2 * 1024 * 1024
-_MODEL_REF_RE = re.compile(r"^[A-Za-z0-9_.:/@-]{1,192}$")
+_MODEL_REF_RE = re.compile(SEMANTIC_MODEL_REF_PATTERN)
 _SELECTOR_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_.-]{0,127}$")
 _DIMENSION_SELECTOR_RE = re.compile(
     r"^[A-Za-z_][A-Za-z0-9_-]{0,127}"
diff --git a/doris_mcp_server/tools/domain_catalog.py 
b/doris_mcp_server/tools/domain_catalog.py
index d15a490..2a83857 100644
--- a/doris_mcp_server/tools/domain_catalog.py
+++ b/doris_mcp_server/tools/domain_catalog.py
@@ -23,6 +23,7 @@ from typing import Annotated, Any, Self
 
 from pydantic import Field, model_validator
 
+from ..semantic.models import SEMANTIC_MODEL_REF_PATTERN
 from .domain_models import (
     ChildToolDefinition,
     CompositePlan,
@@ -528,7 +529,7 @@ def _metricflow_order_by_array() -> dict[str, Any]:
 def _metricflow_model_ref() -> dict[str, Any]:
     return _string(
         "Exact MetricFlow model reference returned by model discovery.",
-        pattern=r"^[A-Za-z0-9_.:/@-]+$",
+        pattern=SEMANTIC_MODEL_REF_PATTERN,
         max_length=192,
     )
 
@@ -2022,7 +2023,7 @@ DOMAIN_DEFINITIONS = (
                     {
                         "model_ref": _string(
                             "Exact semantic model reference.",
-                            pattern=r"^[A-Za-z0-9_.:/@-]+$",
+                            pattern=SEMANTIC_MODEL_REF_PATTERN,
                             max_length=192,
                         ),
                         "include_bindings": _boolean(
@@ -2042,7 +2043,7 @@ DOMAIN_DEFINITIONS = (
                     {
                         "model_ref": _string(
                             "Exact semantic model reference.",
-                            pattern=r"^[A-Za-z0-9_.:/@-]+$",
+                            pattern=SEMANTIC_MODEL_REF_PATTERN,
                             max_length=192,
                         ),
                         "request": {
@@ -2129,7 +2130,7 @@ DOMAIN_DEFINITIONS = (
                     {
                         "model_ref": _string(
                             "Exact semantic model reference.",
-                            pattern=r"^[A-Za-z0-9_.:/@-]+$",
+                            pattern=SEMANTIC_MODEL_REF_PATTERN,
                             max_length=192,
                         ),
                         "datasource": _string(
diff --git a/test/integration/test_real_doris_transports.py 
b/test/integration/test_real_doris_transports.py
index bd994d2..5e2e397 100644
--- a/test/integration/test_real_doris_transports.py
+++ b/test/integration/test_real_doris_transports.py
@@ -1400,12 +1400,190 @@ async def 
test_real_doris_flat_tool_list_stays_bounded_and_callable(
         )
 
 
[email protected]("transport", ["http", "stdio"])
+async def test_real_doris_ossie_exact_revision_model_is_read_only_and_live(
+    transport: str,
+    doris_sandbox: DorisSandbox,
+    tmp_path: Path,
+) -> None:
+    """Resolve an exact ``+revision`` Ossie model over real Doris metadata."""
+    model_ref = "sales/[email protected]+4f91c2a"
+    (tmp_path / "sales-commerce.yaml").write_text(
+        """
+version: "0.2.0.dev0"
+semantic_model:
+  - name: sales_commerce
+    description: Governed sales acceptance model
+    datasets:
+      - name: orders
+        source: commerce.orders
+        primary_key: [order_id]
+        fields:
+          - name: order_id
+            expression:
+              dialects:
+                - dialect: ANSI_SQL
+                  expression: id
+            datatype: Integer
+          - name: order_marker
+            expression:
+              dialects:
+                - dialect: ANSI_SQL
+                  expression: marker
+            datatype: String
+    metrics:
+      - name: order_count
+        expression:
+          dialects:
+            - dialect: ANSI_SQL
+              expression: COUNT(orders.order_id)
+        datatype: Integer
+""".strip(),
+        encoding="utf-8",
+    )
+    (tmp_path / "bindings.yaml").write_text(
+        f"""
+api_version: doris-mcp.apache.org/ossie-binding/v1alpha1
+model_sources:
+  {model_ref}:
+    model_file: sales-commerce.yaml
+    model_name: sales_commerce
+    namespace: commerce
+    tags: [certified]
+    route_profile: global
+    datasets:
+      orders:
+        catalog: internal
+        database: {doris_sandbox.settings.database}
+        object: {doris_sandbox.table}
+        kind: table
+""".strip(),
+        encoding="utf-8",
+    )
+    environment = _server_environment(
+        doris_sandbox.settings,
+        user=doris_sandbox.readonly_user,
+        password=doris_sandbox.readonly_password,
+    )
+    environment.update(
+        {
+            "OSSIE_ENABLED": "true",
+            "OSSIE_MODEL_DIRECTORY": str(tmp_path),
+            "OSSIE_BINDING_MANIFEST": str(tmp_path / "bindings.yaml"),
+            "METRICFLOW_ENABLED": "false",
+        }
+    )
+
+    with doris_sandbox.admin_connection.cursor() as cursor:
+        cursor.execute(
+            f"SELECT COUNT(*) AS row_count FROM 
{doris_sandbox.qualified_table}"
+        )
+        rows_before = int(cursor.fetchone()[0])
+
+    async with _transport_client(
+        transport,
+        environment,
+        read_timeout_seconds=60,
+    ) as client:
+        manifest = await _discover_domain(client, "doris_semantic")
+        children = {child["name"]: child for child in manifest["children"]}
+        ossie_children = {
+            "list_semantic_models",
+            "get_semantic_model_summary",
+            "get_semantic_context",
+            "get_semantic_mapping_status",
+        }
+        metricflow_children = {name for name in children if "metricflow" in 
name}
+        assert len(children) == 12
+        assert len(metricflow_children) == 8
+        assert all(
+            children[name]["availability"]["callable"] for name in 
ossie_children
+        )
+        assert all(
+            not children[name]["availability"]["callable"]
+            for name in metricflow_children
+        )
+        manifest_version = manifest["manifest_version"]
+
+        listed = await _call_domain_child(
+            client,
+            domain="doris_semantic",
+            child_tool="list_semantic_models",
+            arguments={},
+            manifest_version=manifest_version,
+        )
+        assert [item["model_ref"] for item in listed["data"]["items"]] == 
[model_ref]
+
+        summary = await _call_domain_child(
+            client,
+            domain="doris_semantic",
+            child_tool="get_semantic_model_summary",
+            arguments={"model_ref": model_ref, "include_bindings": True},
+            manifest_version=manifest_version,
+        )
+        assert summary["data"]["model_ref"] == model_ref
+        assert summary["data"]["datasets"][0]["binding"] == {
+            "catalog": "internal",
+            "database": doris_sandbox.settings.database,
+            "object": doris_sandbox.table,
+            "kind": "table",
+        }
+
+        context = await _call_domain_child(
+            client,
+            domain="doris_semantic",
+            child_tool="get_semantic_context",
+            arguments={
+                "model_ref": model_ref,
+                "request": {
+                    "question": "How many governed orders are available?",
+                    "metrics": ["order_count"],
+                    "dimensions": ["orders.order_marker"],
+                },
+            },
+            manifest_version=manifest_version,
+        )
+        assert context["data"]["context"]["metrics"][0]["name"] == 
"order_count"
+        assert context["data"]["execution_boundary"].endswith(
+            "Doris Query domain for SQL execution."
+        )
+
+        mapping = await _call_domain_child(
+            client,
+            domain="doris_semantic",
+            child_tool="get_semantic_mapping_status",
+            arguments={"model_ref": model_ref, "datasource": "orders"},
+            manifest_version=manifest_version,
+        )
+        assert mapping["data"]["mapping_status"] == "resolved"
+        assert mapping["data"]["datasets"][0]["visible_fields"] == [
+            {
+                "name": "order_id",
+                "physical_columns": ["id"],
+                "physical_types": ["BIGINT"],
+            },
+            {
+                "name": "order_marker",
+                "physical_columns": ["marker"],
+                "physical_types": ["VARCHAR"],
+            },
+        ]
+
+    with doris_sandbox.admin_connection.cursor() as cursor:
+        cursor.execute(
+            f"SELECT COUNT(*) AS row_count FROM 
{doris_sandbox.qualified_table}"
+        )
+        rows_after = int(cursor.fetchone()[0])
+    assert rows_after == rows_before
+
+
 @pytest.mark.parametrize("transport", ["http", "stdio"])
 async def test_real_doris_metricflow_provider_contract_is_guarded_and_live(
     transport: str,
     tmp_path: Path,
 ) -> None:
     """Exercise every MetricFlow child and execute compiled SQL on real 
Doris."""
+    model_ref = "sales/[email protected]+4f91c2a"
     settings = _real_doris_settings()
     provider_script = tmp_path / "metricflow_provider.py"
     provider_script.write_text(
@@ -1418,7 +1596,7 @@ operation = request["operation"]
 arguments = request["arguments"]
 responses = {
     "list_models": {
-        "items": [{"model_ref": "acceptance/main", "revision": "v1"}],
+        "items": [{"model_ref": "sales/[email protected]+4f91c2a", 
"revision": "v1"}],
     },
     "get_status": {
         "model_ref": arguments.get("model_ref"),
@@ -1503,13 +1681,13 @@ sys.stdout.write(json.dumps(response))
             arguments={},
             manifest_version=manifest_version,
         )
-        assert models["data"]["items"][0]["model_ref"] == "acceptance/main"
+        assert models["data"]["items"][0]["model_ref"] == model_ref
 
         status = await _call_domain_child(
             client,
             domain="doris_semantic",
             child_tool="get_metricflow_status",
-            arguments={"model_ref": "acceptance/main"},
+            arguments={"model_ref": model_ref},
             manifest_version=manifest_version,
         )
         assert status["data"]["dialect"] == "doris"
@@ -1519,7 +1697,7 @@ sys.stdout.write(json.dumps(response))
             domain="doris_semantic",
             child_tool="list_metricflow_metrics",
             arguments={
-                "model_ref": "acceptance/main",
+                "model_ref": model_ref,
                 "include_dimensions": True,
                 "limit": 20,
             },
@@ -1532,7 +1710,7 @@ sys.stdout.write(json.dumps(response))
             domain="doris_semantic",
             child_tool="get_metricflow_group_bys",
             arguments={
-                "model_ref": "acceptance/main",
+                "model_ref": model_ref,
                 "metrics": ["acceptance_count"],
             },
             manifest_version=manifest_version,
@@ -1543,7 +1721,7 @@ sys.stdout.write(json.dumps(response))
             client,
             domain="doris_semantic",
             child_tool="list_metricflow_saved_queries",
-            arguments={"model_ref": "acceptance/main", "limit": 20},
+            arguments={"model_ref": model_ref, "limit": 20},
             manifest_version=manifest_version,
         )
         assert saved_queries["data"]["items"][0]["name"] == (
@@ -1555,7 +1733,7 @@ sys.stdout.write(json.dumps(response))
             domain="doris_semantic",
             child_tool="get_metricflow_dimension_values",
             arguments={
-                "model_ref": "acceptance/main",
+                "model_ref": model_ref,
                 "metrics": ["acceptance_count"],
                 "dimension": "segment",
                 "limit": 20,
@@ -1570,7 +1748,7 @@ sys.stdout.write(json.dumps(response))
             domain="doris_semantic",
             child_tool="compile_metricflow_query",
             arguments={
-                "model_ref": "acceptance/main",
+                "model_ref": model_ref,
                 "request": {"metrics": ["acceptance_count"]},
             },
             manifest_version=manifest_version,
@@ -1582,7 +1760,7 @@ sys.stdout.write(json.dumps(response))
             domain="doris_semantic",
             child_tool="execute_metricflow_query",
             arguments={
-                "model_ref": "acceptance/main",
+                "model_ref": model_ref,
                 "request": {"saved_query": "acceptance_saved_query"},
                 "max_rows": 1,
             },
diff --git a/test/semantic/test_metricflow_runtime.py 
b/test/semantic/test_metricflow_runtime.py
index c914ab5..ec58146 100644
--- a/test/semantic/test_metricflow_runtime.py
+++ b/test/semantic/test_metricflow_runtime.py
@@ -72,9 +72,10 @@ def _query_runtime() -> Any:
 
 @pytest.mark.asyncio
 async def test_metricflow_metadata_operations_require_exact_model_refs() -> 
None:
+    model_ref = "sales/[email protected]+4f91c2a"
     provider = _Provider(
         {
-            "list_models": {"items": [{"model_ref": "sales/main"}]},
+            "list_models": {"items": [{"model_ref": model_ref}]},
             "get_status": {"valid": True, "dialect": "doris"},
             "list_metrics": {"items": [{"name": "revenue"}]},
             "get_group_bys": {"dimensions": ["metric_time"]},
@@ -88,31 +89,31 @@ async def 
test_metricflow_metadata_operations_require_exact_model_refs() -> None
     )
 
     models = await runtime.list_models()
-    status = await runtime.get_status(model_ref="sales/main")
+    status = await runtime.get_status(model_ref=model_ref)
     metrics = await runtime.list_metrics(
-        model_ref="sales/main",
+        model_ref=model_ref,
         search="rev",
         include_dimensions=True,
         limit=20,
     )
     group_bys = await runtime.get_group_bys(
-        model_ref="sales/main",
+        model_ref=model_ref,
         metrics=["revenue"],
     )
     saved = await runtime.list_saved_queries(
-        model_ref="sales/main",
+        model_ref=model_ref,
         search=None,
         limit=20,
     )
 
-    assert models["data"]["items"] == [{"model_ref": "sales/main"}]
+    assert models["data"]["items"] == [{"model_ref": model_ref}]
     assert status["data"]["dialect"] == "doris"
     assert metrics["data"]["items"] == [{"name": "revenue"}]
     assert group_bys["data"]["dimensions"] == ["metric_time"]
     assert saved["data"]["items"] == [{"name": "weekly_sales"}]
     assert provider.calls[1] == (
         "get_status",
-        {"model_ref": "sales/main"},
+        {"model_ref": model_ref},
     )
 
 
diff --git a/test/semantic/test_ossie_loader.py 
b/test/semantic/test_ossie_loader.py
index c1be5f1..47ec10d 100644
--- a/test/semantic/test_ossie_loader.py
+++ b/test/semantic/test_ossie_loader.py
@@ -79,11 +79,15 @@ semantic_model:
 """.strip()
 
 
-def _binding_yaml(model_file: str = "retail.yaml") -> str:
+def _binding_yaml(
+    model_file: str = "retail.yaml",
+    *,
+    model_ref: str = "retail/main",
+) -> str:
     return f"""
 api_version: doris-mcp.apache.org/ossie-binding/v1alpha1
 model_sources:
-  retail/main:
+  {model_ref}:
     model_file: {model_file}
     model_name: retail
     namespace: commerce
@@ -131,6 +135,43 @@ def test_loads_exact_model_and_pinned_schema(tmp_path: 
Path) -> None:
     assert model.binding_id.startswith("binding.")
 
 
+def test_loads_exact_model_ref_with_revision_metadata(tmp_path: Path) -> None:
+    model_ref = "sales/[email protected]+4f91c2a"
+    (tmp_path / "retail.yaml").write_text(_model_yaml(), encoding="utf-8")
+    (tmp_path / "bindings.yaml").write_text(
+        _binding_yaml(model_ref=model_ref),
+        encoding="utf-8",
+    )
+
+    registry = _loader(tmp_path).load()
+
+    assert registry.get(model_ref) is not None
+
+
[email protected](
+    "model_ref",
+    (
+        "+sales/[email protected]",
+        "sales/[email protected]+",
+        "sales/[email protected]++4f91c2a",
+    ),
+)
+def test_rejects_malformed_revision_metadata(
+    tmp_path: Path,
+    model_ref: str,
+) -> None:
+    (tmp_path / "retail.yaml").write_text(_model_yaml(), encoding="utf-8")
+    (tmp_path / "bindings.yaml").write_text(
+        _binding_yaml(model_ref=model_ref),
+        encoding="utf-8",
+    )
+
+    with pytest.raises(SemanticConfigurationError) as failure:
+        _loader(tmp_path).load()
+
+    assert failure.value.reason_code == "OSSIE_BINDING_INVALID"
+
+
 @pytest.mark.parametrize(
     ("model_text", "reason_code"),
     [
diff --git a/test/semantic/test_semantic_runtime.py 
b/test/semantic/test_semantic_runtime.py
index 6b9836b..654f00d 100644
--- a/test/semantic/test_semantic_runtime.py
+++ b/test/semantic/test_semantic_runtime.py
@@ -119,11 +119,11 @@ semantic_model:
 """.strip()
 
 
-def _binding_yaml() -> str:
-    return """
+def _binding_yaml(*, model_ref: str = "retail/main") -> str:
+    return f"""
 api_version: doris-mcp.apache.org/ossie-binding/v1alpha1
 model_sources:
-  retail/main:
+  {model_ref}:
     model_file: retail.yaml
     model_name: retail
     namespace: commerce
@@ -390,6 +390,24 @@ async def 
test_exact_model_route_and_selector_fail_closed(tmp_path: Path) -> Non
     assert dimension_failure.value.reason_code == "SEMANTIC_ARGUMENT_INVALID"
 
 
[email protected]
+async def test_exact_model_ref_with_revision_metadata_is_resolved(
+    tmp_path: Path,
+) -> None:
+    model_ref = "sales/[email protected]+4f91c2a"
+    (tmp_path / "retail.yaml").write_text(_model_yaml(), encoding="utf-8")
+    (tmp_path / "bindings.yaml").write_text(
+        _binding_yaml(model_ref=model_ref),
+        encoding="utf-8",
+    )
+    manager = _ConnectionManager(tmp_path, _metadata_responder)
+    runtime = DorisSemanticRuntime(manager)
+
+    summary = await runtime.get_semantic_model_summary(model_ref=model_ref)
+
+    assert summary["data"]["model_ref"] == model_ref
+
+
 @pytest.mark.asyncio
 async def test_backend_errors_are_sanitized(tmp_path: Path) -> None:
     def denied(
diff --git a/test/tools/test_domain_catalog.py 
b/test/tools/test_domain_catalog.py
index 1ea5e05..8ffa70a 100644
--- a/test/tools/test_domain_catalog.py
+++ b/test/tools/test_domain_catalog.py
@@ -263,6 +263,54 @@ def 
test_semantic_content_children_require_explicit_model_ref() -> None:
         assert "model_ref" in schemas[name]["properties"]
 
 
+def test_semantic_model_ref_schemas_accept_revision_metadata() -> None:
+    semantic = DORIS_DOMAIN_CATALOG.resolve_domain("doris_semantic")
+    model_ref = "sales/[email protected]+4f91c2a"
+
+    for child in semantic.children:
+        schema = _wire_input(child)
+        if "model_ref" not in schema.get("properties", {}):
+            continue
+        validator = Draft202012Validator(schema)
+        arguments: dict[str, object] = {"model_ref": model_ref}
+        if child.name == "get_semantic_context":
+            arguments["request"] = {"metrics": ["total_sales"]}
+        elif child.name == "get_metricflow_group_bys":
+            arguments["metrics"] = ["revenue"]
+        elif child.name == "get_metricflow_dimension_values":
+            arguments.update({"metrics": ["revenue"], "dimension": 
"metric_time"})
+        elif child.name in {
+            "compile_metricflow_query",
+            "execute_metricflow_query",
+        }:
+            arguments["request"] = {"metrics": ["revenue"]}
+
+        assert list(validator.iter_errors(arguments)) == []
+
+
[email protected](
+    "model_ref",
+    (
+        "+sales/[email protected]",
+        "sales/[email protected]+",
+        "sales/[email protected]++4f91c2a",
+    ),
+)
+def test_semantic_model_ref_schemas_reject_malformed_revision_metadata(
+    model_ref: str,
+) -> None:
+    child = DORIS_DOMAIN_CATALOG.resolve_child(
+        "doris_semantic",
+        "get_semantic_model_summary",
+    )
+
+    errors = list(
+        Draft202012Validator(_wire_input(child)).iter_errors({"model_ref": 
model_ref})
+    )
+
+    assert len(errors) == 1
+
+
 def test_semantic_context_schema_requires_nonempty_unambiguous_selectors() -> 
None:
     child = DORIS_DOMAIN_CATALOG.resolve_child(
         "doris_semantic",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to