-
Notifications
You must be signed in to change notification settings - Fork 21
DEV-1608: Cube → SLayer ingestion (Stage 1) #211
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
Merged
Merged
Changes from 2 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
1d2e325
DEV-1608: Cube → SLayer ingestion (Stage 1)
ZmeiGorynych c1990c4
DEV-1608: address PR review feedback (Sonar gate + Codex)
ZmeiGorynych 3ac7590
DEV-1608: final review nits (Sonar complexity + spec markdown)
ZmeiGorynych 2f55c37
DEV-1608: clarify facade joined-measure doc example (CodeRabbit)
ZmeiGorynych 3959d5f
DEV-1608: report path honors --models-dir (CodeRabbit)
ZmeiGorynych 488f5d0
DEV-1608: address CodeRabbit review batch
ZmeiGorynych 0d9d030
Merge remote-tracking branch 'origin/egor/dev-1625-variable-substitut…
ZmeiGorynych b31318c
Merge remote-tracking branch 'origin/main' into egor/dev-1608-cube-to…
ZmeiGorynych 75d1aa7
feat(DEV-1730): list-valued {variable} substitution for IN pushdown
ZmeiGorynych 30173ab
fix(DEV-1608/DEV-1730): address CodeRabbit + Sonar + Codex review
ZmeiGorynych 00ba92b
feat(DEV-1730): Cube JS ingestion + FILTER_PARAMS representability
ZmeiGorynych 2bb0428
Merge remote-tracking branch 'origin/egor/dev-1608-cube-to-slayer-ing…
ZmeiGorynych 9648d6b
feat(DEV-1727): dialect-aware / complete escaping for Mode-A {var} su…
ZmeiGorynych ad365f9
Merge origin/egor/dev-1608-cube-to-slayer-ingestion into DEV-1727
ZmeiGorynych a944208
fix(DEV-1730): address CodeRabbit + Codex + Sonar review
ZmeiGorynych c8485d0
refactor(DEV-1730): reduce cognitive complexity (Sonar S3776)
ZmeiGorynych 4016f4a
test(DEV-1727): isolate the throwing call in two raises-blocks (Sonar…
ZmeiGorynych 6dec2dc
docs(DEV-1730): demonstrate optional blocks in the variable-substitut…
ZmeiGorynych 8970bc7
Merge pull request #278 from MotleyAI/egor/dev-1727-dialect-aware-com…
ZmeiGorynych f4a7c71
docs(DEV-1730): clarify optional-block + list contract (CodeRabbit)
ZmeiGorynych 0d8be1c
Merge remote-tracking branch 'origin/egor/dev-1608-cube-to-slayer-ing…
ZmeiGorynych 6cb9e7b
refactor(DEV-1730): keep substitute_variables under S3776 after DEV-1…
ZmeiGorynych 17bf5ce
Merge pull request #275 from MotleyAI/egor/dev-1730-cube-js-config-in…
ZmeiGorynych 9bb846e
fix(DEV-1730): coerce scalars for declared list-valued {variable}s
ZmeiGorynych ef5a738
fix(DEV-1730): make the declared-variable bag self-identifying
ZmeiGorynych fb16a22
fix(DEV-1730): require a non-empty member in a variable declaration
ZmeiGorynych File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| # Importing Cube definitions | ||
|
|
||
| SLayer can import [Cube](https://cube.dev) (Cube.js / Cube.dev) YAML data models — | ||
| cubes and views — and convert them to SLayer models. The conversion is **fully | ||
| offline**: data types come from Cube's declared dimension / measure types, so no | ||
| database connection is required. Everything that can't be mapped cleanly is | ||
| captured in a structured JSON report rather than silently dropped. | ||
|
|
||
| ## Quick start | ||
|
|
||
| ```bash | ||
| slayer import-cube ./cube_project --datasource my_postgres --storage ./slayer_data | ||
| ``` | ||
|
|
||
| This recursively reads every `.yml`/`.yaml` file under the path, extracts | ||
| `cubes:` and `views:`, writes SLayer model files to the storage directory, and | ||
| writes `cube_import_report.json` next to it. | ||
|
|
||
| `--datasource` is just the SLayer datasource name to file the models under — it | ||
| does not need to exist or be reachable. After importing, run `slayer ingest` | ||
| against a live connection to profile sample values and refine numeric types. | ||
|
|
||
| ## What gets converted | ||
|
|
||
| ### Cubes → models | ||
|
|
||
| Each cube becomes one `SlayerModel` anchored on its `sql_table` (or `sql`). | ||
|
|
||
| | Cube | SLayer | | ||
| |------|--------| | ||
| | `name` | `name` | | ||
| | `sql_table` / `sql` | `sql_table` / `sql` (with `{CUBE}`/`{member}` refs translated) | | ||
| | `description` | `description` | | ||
| | `public: false` | `hidden: true` | | ||
| | `meta` (incl. `ai_context`) | `meta` | | ||
| | `title` | `meta.cube_title` | | ||
|
|
||
| ### Measures → columns + measures | ||
|
|
||
| Cube bakes the aggregation into each measure; SLayer separates the row-level | ||
| expression (a `Column`) from the named aggregation (a `ModelMeasure`). | ||
|
|
||
| ```yaml | ||
| # Cube | ||
| measures: | ||
| - { name: total_revenue, type: sum, sql: "{CUBE}.amount" } | ||
| # SLayer | ||
| columns: | ||
| - { name: amount, type: DOUBLE } | ||
| measures: | ||
| - { name: total_revenue, formula: "amount:sum" } | ||
| ``` | ||
|
|
||
| - `count` with no `sql` → `*:count`; `count_distinct_approx` → `count_distinct`. | ||
| - Conditional `filters:` become a `CASE WHEN` on the column's `filter`. Two | ||
| measures over the same expression but different filters get distinct columns. | ||
| - A finite trailing `rolling_window` becomes a windowed aggregation | ||
| (`amount:sum(window='30d')`). | ||
| - Calculated measures (`type: number/string/time/boolean`) referencing other | ||
| measures become a `ModelMeasure` formula (`{revenue} / {count}` → `revenue / count`). | ||
| - `format` maps to `NumberFormat` (`percent`, `currency`, `number`). | ||
|
|
||
| ### Dimensions → columns | ||
|
|
||
| `string`→`TEXT`, `number`→`DOUBLE`, `boolean`→`BOOLEAN`, `time`→`TIMESTAMP`. | ||
| `primary_key: true` carries over. A `case:` dimension becomes a `CASE WHEN` | ||
| column. | ||
|
|
||
| ### Joins | ||
|
|
||
| A join's ON clause (`{CUBE}.customer_id = {customers.id}`) becomes | ||
| `join_pairs`; member references resolve to their physical columns. Composite | ||
| (`AND`-joined) keys are supported. All joins emit as `LEFT`. | ||
|
|
||
| ### Segments → boolean columns | ||
|
|
||
| Each segment becomes a boolean column carrying the predicate, so it stays | ||
| filterable (`completed = true`) and group-able. | ||
|
|
||
| ### Views → facade models | ||
|
|
||
| A Cube view (which owns no table) becomes a thin model anchored on its | ||
| `join_path` root cube: included dimensions become derived columns | ||
| (`sql: "customers.name"`), included measures become local or cross-model | ||
| `ModelMeasure`s (`customers.ltv:sum`), `prefix: true` prepends the cube name, | ||
| and `default_filters` become model filters. | ||
|
|
||
| ### `extends` | ||
|
|
||
| Cube inheritance is **flattened** at import time — a child inherits the parent's | ||
| members (child wins on conflicts), and abstract bases (`public: false`) are | ||
| emitted as hidden models. | ||
|
|
||
| ## What does not map (reported) | ||
|
|
||
| These are recorded in `cube_import_report.json` and, where useful, preserved | ||
| under `meta.cube_unmapped.<feature>`: | ||
|
|
||
| - Caching / infra: `pre_aggregations`, `refresh_key`, `calendar`, `sql_alias`. | ||
| - Presentation: `hierarchies`, `drill_members`, folders, dimension `links`/`order`. | ||
| - Security: `access_policy`. | ||
| - No SLayer equivalent: `geo` dimensions, `sub_query` dimensions, custom | ||
| `granularities`, per-cube `data_source`. | ||
| - Non-equality / non-column join ON clauses (the join is dropped). | ||
| - Files or members using Jinja templating (`{{ }}` / `{% %}`) — skipped, since | ||
| conversion is offline and does not render templates. | ||
|
|
||
| ### Tesseract features (deferred) | ||
|
|
||
| Features that require the Tesseract SQL planner — `switch` dimensions, | ||
| `number_agg` measures, `case` measures, and the measure `filter` grain control — | ||
| have no clean SLayer mapping yet and are reported as `deferred_stage2`. The | ||
| cube's other members still convert. | ||
|
|
||
| ## The report | ||
|
|
||
| `CubeConversionResult` carries the emitted `models` and a `CubeConversionReport` | ||
| of categorized issues (each with a category, severity, the owning cube/view/member, | ||
| a message, and the raw Cube fragment when useful). The CLI always writes it to | ||
| `cube_import_report.json` (override with `--report PATH`) and prints a summary | ||
| grouped by severity. | ||
|
|
||
| ## CLI reference | ||
|
|
||
| ```text | ||
| slayer import-cube <cube_project_path> [options] | ||
|
|
||
| Arguments: | ||
| cube_project_path Path to the Cube project (or its model directory) | ||
|
|
||
| Options: | ||
| --datasource NAME SLayer datasource name for the imported models (required) | ||
| --storage PATH Storage directory / .db file (default: platform path) | ||
| --report PATH JSON report path (default: <storage>/cube_import_report.json) | ||
| --include-hidden Also print hidden (public: false) models in the summary | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| """Cube (Cube.js / Cube.dev) data-model ingestion — parse Cube YAML and | ||
| convert to SLayer models. Mirrors ``slayer/dbt/``. See DEV-1608. | ||
| """ |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.