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]