Skip to content

orionbelt converter: fix round-trip fidelity + validation robustness#206

Merged
khush-bhatia merged 1 commit into
apache:mainfrom
ralfbecher:fix/osi-converter-roundtrip-robustness
Jul 17, 2026
Merged

orionbelt converter: fix round-trip fidelity + validation robustness#206
khush-bhatia merged 1 commit into
apache:mainfrom
ralfbecher:fix/osi-converter-roundtrip-robustness

Conversation

@ralfbecher

@ralfbecher ralfbecher commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Mirrors ralfbecher/orionbelt-semantic-layer#201 (the canonical converter source) into converters/orionbelt/. Three pre-existing bugs found while reviewing #153:

Metric round-trip when a data object's code differs from its display name

OSItoOBML resolved metric references only by dataset name, but the OBML -> OSI emitter writes physical codes (e.g. SUM(fact_orders.amount)), so such metrics were dropped as unconverted on the return trip. Resolution now indexes datasets/fields by both name and physical code (source-table code + field expression, quoted or bare). Resolved refs are bracket-quoted so display names containing spaces stay intact through the dataset.column parsers, and Snowflake/Databricks quoted identifiers resolve too.

Dimension name collision across datasets

_extract_dimensions keyed dimensions by bare field name, so Orders.date and Invoices.date collapsed to one. The later one is now qualified with its data object and a warning is recorded - no silent loss.

validate_osi robustness

It assumed every datasets/fields value was a list of dicts and raised AttributeError on malformed input. It now guards each level and returns schema errors instead.

Tests

Adds tests/test_osi_converter_roundtrip_robustness.py. 154 converter tests pass; ruff clean; mypy clean with the dev extra.

Canonical change: ralforion/orionbelt-semantic-layer#202 (merged).


Follow-up converter fixes (canonical #228 dimension-name restoration, #229 N-dimensions-per-column) are mirrored in a separate PR, not this one.

Mirrors orionbelt-semantic-layer#201 (canonical source) into the ossie converter:

- Metric round-trip: OSItoOBML resolves metric references by BOTH the OSI
  dataset/field name and the physical code (source-table code and field
  expression, quoted or bare), so metrics emitted against physical codes
  (e.g. SUM(fact_orders.amount)) round-trip instead of dropping when a data
  object's code differs from its display name. Resolved refs are bracket-quoted
  so display names with spaces stay intact through the dataset.column parsers.

- Dimension collision: _extract_dimensions qualifies a field name that occurs
  in more than one dataset (Orders.date / Invoices.date) with its data object
  and warns, instead of silently overwriting the earlier dimension.

- Validation robustness: validate_osi guards its semantic loops against
  malformed structures (datasets/fields that are not lists of dicts) and returns
  schema errors instead of raising AttributeError.

Adds tests/test_osi_converter_roundtrip_robustness.py. 154 converter tests pass;
ruff clean; mypy clean with the dev extra.
# instead of silently overwriting the earlier dimension.
key = field_name
if key in dimensions and dimensions[key].get("dataObject") != ds_name:
key = f"{ds_name} {field_name}"

@khush-bhatia khush-bhatia Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the key field, Is the use of space here instead of . intentional ? Wouldn't it better to use the SQL identifier style and use . ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. It comes down to what "a dimension" fundamentally is in each model:

  • OSI is field-centric - a dimension is a field (a physical column), so a dot (table.column) is the natural SQL-identifier spelling.
  • OBML is name-centric - a dimension is a named view onto a column. Its identity is the name; the physical reference lives in the dimension's dataObject + column pair, not the name. OBML names are display-grade by convention and convenience (Country Name, Total Sales), not SQL identifiers, so a dot here would make a display name read like a physical reference.

That's why the qualifier uses a space: the key becomes a name, not an identifier. It also only fires as a fallback - when two datasets share a bare field name and there's no OBML-origin name to prefer - so it stays rare.

@khush-bhatia
khush-bhatia merged commit 09f8c83 into apache:main Jul 17, 2026
@ralfbecher ralfbecher changed the title orionbelt converter: fix round-trip fidelity + validation robustness orionbelt converter: round-trip fidelity + validation robustness (+ dimension name / N-per-column) Jul 17, 2026
@ralfbecher ralfbecher changed the title orionbelt converter: round-trip fidelity + validation robustness (+ dimension name / N-per-column) orionbelt converter: fix round-trip fidelity + validation robustness Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants