From 35843791ea1da7f8151c95d9fca93286286378a4 Mon Sep 17 00:00:00 2001 From: Adegbite Ayoade Date: Wed, 23 Sep 2026 23:57:08 +0100 Subject: [PATCH 1/2] fix(ontology): accept datatype on DatasetField and Metric The core schema allows an optional datatype on fields and metrics, but the spec models omitted it. With extra="forbid" on the base model, spec-conformant documents such as examples/tpcds_semantic_model.yaml failed to parse. Add the field to both models, in schema order. Closes #424 --- .../ontology/src/ossie_ontology/spec.py | 2 + converters/ontology/tests/test_spec.py | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 converters/ontology/tests/test_spec.py diff --git a/converters/ontology/src/ossie_ontology/spec.py b/converters/ontology/src/ossie_ontology/spec.py index e61ce52a..f32de40f 100644 --- a/converters/ontology/src/ossie_ontology/spec.py +++ b/converters/ontology/src/ossie_ontology/spec.py @@ -134,6 +134,7 @@ class DatasetField(OssieObject): dimension: Dimension | None = None label: str | None = None description: str | None = None + datatype: str | None = None ai_context: AiContext | None = None custom_extensions: list[CustomExtension] = Field(default_factory=list) @@ -170,6 +171,7 @@ class Metric(OssieObject): name: str expression: Expression description: str | None = None + datatype: str | None = None ai_context: AiContext | None = None custom_extensions: list[CustomExtension] = Field(default_factory=list) diff --git a/converters/ontology/tests/test_spec.py b/converters/ontology/tests/test_spec.py new file mode 100644 index 00000000..a4ffe12d --- /dev/null +++ b/converters/ontology/tests/test_spec.py @@ -0,0 +1,64 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +"""Tests for the spec DTOs in ossie_ontology.spec.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +import yaml + +from ossie_ontology.spec import DatasetField, Metric, SemanticModel + +# tests/ -> ontology -> converters -> +_EXAMPLES_DIR = Path(__file__).resolve().parents[3] / "examples" + + +def test_dataset_field_accepts_datatype(): + field = DatasetField.model_validate({ + "name": "ss_quantity", + "expression": {"dialects": [{"dialect": "ANSI_SQL", "expression": "ss_quantity"}]}, + "datatype": "Integer", + }) + assert field.datatype == "Integer" + + +def test_metric_accepts_datatype(): + metric = Metric.model_validate({ + "name": "total_sales", + "expression": {"dialects": [{"dialect": "ANSI_SQL", "expression": "SUM(ss_sales_price)"}]}, + "datatype": "Decimal", + }) + assert metric.datatype == "Decimal" + + +def test_tpcds_example_parses(): + example_path = _EXAMPLES_DIR / "tpcds_semantic_model.yaml" + if not example_path.is_file(): + pytest.skip(f"canonical example not present at {example_path}") + + doc = yaml.safe_load(example_path.read_text(encoding="utf-8")) + # A standalone core document carries `version` at the root alongside the + # semantic model contents. + doc.pop("version", None) + model = SemanticModel.model_validate(doc) + + assert model.name == "tpcds_retail_model" + assert any(f.datatype for d in model.datasets for f in d.fields) + assert any(m.datatype for m in model.metrics) From 8beae9ab1bc671cf5c5ae29971c9eb016928d8b2 Mon Sep 17 00:00:00 2001 From: Adegbite Ayoade Date: Thu, 24 Sep 2026 19:33:39 +0100 Subject: [PATCH 2/2] test(ontology): keep root version once SemanticModel declares it #441 adds a required version to SemanticModel. Only drop the root version from the tpcds example while the model does not declare it, so the test passes before and after that change. --- converters/ontology/tests/test_spec.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/converters/ontology/tests/test_spec.py b/converters/ontology/tests/test_spec.py index a4ffe12d..63938b7b 100644 --- a/converters/ontology/tests/test_spec.py +++ b/converters/ontology/tests/test_spec.py @@ -55,8 +55,9 @@ def test_tpcds_example_parses(): doc = yaml.safe_load(example_path.read_text(encoding="utf-8")) # A standalone core document carries `version` at the root alongside the - # semantic model contents. - doc.pop("version", None) + # semantic model contents. Keep it once SemanticModel declares it (#441). + if "version" not in SemanticModel.model_fields: + doc.pop("version", None) model = SemanticModel.model_validate(doc) assert model.name == "tpcds_retail_model"