@finos/legend-cube-builder 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/docs/adding-an-operation.md +4 -2
  2. package/docs/demo/README.md +1 -1
  3. package/docs/hosting.md +40 -9
  4. package/lib/__lib__/LegendCubeHelpText.d.ts.map +1 -1
  5. package/lib/__lib__/LegendCubeHelpText.js +3 -1
  6. package/lib/__lib__/LegendCubeHelpText.js.map +1 -1
  7. package/lib/__lib__/LegendCubeLabels.d.ts +34 -5
  8. package/lib/__lib__/LegendCubeLabels.d.ts.map +1 -1
  9. package/lib/__lib__/LegendCubeLabels.js +45 -8
  10. package/lib/__lib__/LegendCubeLabels.js.map +1 -1
  11. package/lib/__lib__/LegendCubeNavigation.d.ts +23 -0
  12. package/lib/__lib__/LegendCubeNavigation.d.ts.map +1 -0
  13. package/lib/__lib__/LegendCubeNavigation.js +33 -0
  14. package/lib/__lib__/LegendCubeNavigation.js.map +1 -0
  15. package/lib/__lib__/LegendCubeTesting.d.ts +3 -1
  16. package/lib/__lib__/LegendCubeTesting.d.ts.map +1 -1
  17. package/lib/__lib__/LegendCubeTesting.js +2 -0
  18. package/lib/__lib__/LegendCubeTesting.js.map +1 -1
  19. package/lib/__lib__/LegendCubeTour.d.ts +62 -0
  20. package/lib/__lib__/LegendCubeTour.d.ts.map +1 -0
  21. package/lib/__lib__/LegendCubeTour.js +149 -0
  22. package/lib/__lib__/LegendCubeTour.js.map +1 -0
  23. package/lib/__test-utils__/CubeExampleTestUtils.d.ts +34 -0
  24. package/lib/__test-utils__/CubeExampleTestUtils.d.ts.map +1 -0
  25. package/lib/__test-utils__/CubeExampleTestUtils.js +72 -0
  26. package/lib/__test-utils__/CubeExampleTestUtils.js.map +1 -0
  27. package/lib/__test-utils__/CubeVariantTestUtils.d.ts +16 -0
  28. package/lib/__test-utils__/CubeVariantTestUtils.d.ts.map +1 -0
  29. package/lib/__test-utils__/CubeVariantTestUtils.js +66 -0
  30. package/lib/__test-utils__/CubeVariantTestUtils.js.map +1 -0
  31. package/lib/components/CubeAddItems.d.ts.map +1 -1
  32. package/lib/components/CubeAddItems.js +2 -1
  33. package/lib/components/CubeAddItems.js.map +1 -1
  34. package/lib/components/CubeEditor.d.ts.map +1 -1
  35. package/lib/components/CubeEditor.js +11 -5
  36. package/lib/components/CubeEditor.js.map +1 -1
  37. package/lib/components/CubeFileMenu.d.ts.map +1 -1
  38. package/lib/components/CubeFileMenu.js +2 -1
  39. package/lib/components/CubeFileMenu.js.map +1 -1
  40. package/lib/components/CubeNodeIcon.d.ts.map +1 -1
  41. package/lib/components/CubeNodeIcon.js +2 -1
  42. package/lib/components/CubeNodeIcon.js.map +1 -1
  43. package/lib/components/canvas/CubeCanvas.d.ts.map +1 -1
  44. package/lib/components/canvas/CubeCanvas.js +3 -2
  45. package/lib/components/canvas/CubeCanvas.js.map +1 -1
  46. package/lib/components/canvas/CubeCanvasNode.d.ts.map +1 -1
  47. package/lib/components/canvas/CubeCanvasNode.js +4 -1
  48. package/lib/components/canvas/CubeCanvasNode.js.map +1 -1
  49. package/lib/components/dev-settings/CubeDevSettings.d.ts +2 -2
  50. package/lib/components/dev-settings/CubeDevSettings.d.ts.map +1 -1
  51. package/lib/components/dev-settings/CubeDevSettings.js +12 -8
  52. package/lib/components/dev-settings/CubeDevSettings.js.map +1 -1
  53. package/lib/components/editors/CubeColumnPicker.d.ts.map +1 -1
  54. package/lib/components/editors/CubeColumnPicker.js +3 -1
  55. package/lib/components/editors/CubeColumnPicker.js.map +1 -1
  56. package/lib/components/editors/CubeExplodeEditor.d.ts +26 -0
  57. package/lib/components/editors/CubeExplodeEditor.d.ts.map +1 -0
  58. package/lib/components/editors/CubeExplodeEditor.js +61 -0
  59. package/lib/components/editors/CubeExplodeEditor.js.map +1 -0
  60. package/lib/components/editors/CubeExpressionFilterEditor.d.ts.map +1 -1
  61. package/lib/components/editors/CubeExpressionFilterEditor.js +8 -5
  62. package/lib/components/editors/CubeExpressionFilterEditor.js.map +1 -1
  63. package/lib/components/editors/CubeExtendEditor.d.ts +0 -2
  64. package/lib/components/editors/CubeExtendEditor.d.ts.map +1 -1
  65. package/lib/components/editors/CubeExtendEditor.js +12 -11
  66. package/lib/components/editors/CubeExtendEditor.js.map +1 -1
  67. package/lib/components/editors/CubeJsonFieldsTree.d.ts +46 -0
  68. package/lib/components/editors/CubeJsonFieldsTree.d.ts.map +1 -0
  69. package/lib/components/editors/CubeJsonFieldsTree.js +93 -0
  70. package/lib/components/editors/CubeJsonFieldsTree.js.map +1 -0
  71. package/lib/components/editors/CubeNodeEditorRegistry.d.ts.map +1 -1
  72. package/lib/components/editors/CubeNodeEditorRegistry.js +3 -1
  73. package/lib/components/editors/CubeNodeEditorRegistry.js.map +1 -1
  74. package/lib/components/grid/CubeGridRegion.d.ts.map +1 -1
  75. package/lib/components/grid/CubeGridRegion.js +5 -4
  76. package/lib/components/grid/CubeGridRegion.js.map +1 -1
  77. package/lib/components/grid/CubeGridValues.d.ts +5 -3
  78. package/lib/components/grid/CubeGridValues.d.ts.map +1 -1
  79. package/lib/components/grid/CubeGridValues.js +8 -3
  80. package/lib/components/grid/CubeGridValues.js.map +1 -1
  81. package/lib/components/grid/CubeJsonExplorerDialog.d.ts +27 -0
  82. package/lib/components/grid/CubeJsonExplorerDialog.d.ts.map +1 -0
  83. package/lib/components/grid/CubeJsonExplorerDialog.js +102 -0
  84. package/lib/components/grid/CubeJsonExplorerDialog.js.map +1 -0
  85. package/lib/components/grid/CubeJsonViewerDialog.d.ts +27 -0
  86. package/lib/components/grid/CubeJsonViewerDialog.d.ts.map +1 -0
  87. package/lib/components/grid/CubeJsonViewerDialog.js +68 -0
  88. package/lib/components/grid/CubeJsonViewerDialog.js.map +1 -0
  89. package/lib/components/grid/CubeResultGrid.d.ts +15 -3
  90. package/lib/components/grid/CubeResultGrid.d.ts.map +1 -1
  91. package/lib/components/grid/CubeResultGrid.js +95 -3
  92. package/lib/components/grid/CubeResultGrid.js.map +1 -1
  93. package/lib/components/grid/CubeServerSideGrid.d.ts.map +1 -1
  94. package/lib/components/grid/CubeServerSideGrid.js +4 -2
  95. package/lib/components/grid/CubeServerSideGrid.js.map +1 -1
  96. package/lib/components/palette/CubePalette.d.ts.map +1 -1
  97. package/lib/components/palette/CubePalette.js +4 -1
  98. package/lib/components/palette/CubePalette.js.map +1 -1
  99. package/lib/components/source-picker/CubeIngestTab.d.ts +2 -2
  100. package/lib/components/source-picker/CubeIngestTab.d.ts.map +1 -1
  101. package/lib/components/source-picker/CubeIngestTab.js +19 -13
  102. package/lib/components/source-picker/CubeIngestTab.js.map +1 -1
  103. package/lib/components/tour/CubeTour.d.ts +36 -0
  104. package/lib/components/tour/CubeTour.d.ts.map +1 -0
  105. package/lib/components/tour/CubeTour.js +183 -0
  106. package/lib/components/tour/CubeTour.js.map +1 -0
  107. package/lib/index.css +1 -1
  108. package/lib/index.d.ts +1 -0
  109. package/lib/index.d.ts.map +1 -1
  110. package/lib/index.js +1 -0
  111. package/lib/index.js.map +1 -1
  112. package/lib/stores/CubeEditorState.d.ts +17 -1
  113. package/lib/stores/CubeEditorState.d.ts.map +1 -1
  114. package/lib/stores/CubeEditorState.js +35 -5
  115. package/lib/stores/CubeEditorState.js.map +1 -1
  116. package/lib/stores/CubeExamples.d.ts.map +1 -1
  117. package/lib/stores/CubeExamples.js +101 -4
  118. package/lib/stores/CubeExamples.js.map +1 -1
  119. package/lib/stores/CubeExamplesState.d.ts.map +1 -1
  120. package/lib/stores/CubeExamplesState.js +4 -1
  121. package/lib/stores/CubeExamplesState.js.map +1 -1
  122. package/lib/stores/CubeGridQuickActions.d.ts +7 -0
  123. package/lib/stores/CubeGridQuickActions.d.ts.map +1 -1
  124. package/lib/stores/CubeGridQuickActions.js +20 -11
  125. package/lib/stores/CubeGridQuickActions.js.map +1 -1
  126. package/lib/stores/CubePureText.d.ts +20 -0
  127. package/lib/stores/CubePureText.d.ts.map +1 -0
  128. package/lib/stores/CubePureText.js +24 -0
  129. package/lib/stores/CubePureText.js.map +1 -0
  130. package/lib/stores/CubeTourState.d.ts +49 -0
  131. package/lib/stores/CubeTourState.d.ts.map +1 -0
  132. package/lib/stores/CubeTourState.js +92 -0
  133. package/lib/stores/CubeTourState.js.map +1 -0
  134. package/lib/stores/LegendCubeHost.d.ts +88 -0
  135. package/lib/stores/LegendCubeHost.d.ts.map +1 -0
  136. package/lib/stores/LegendCubeHost.js +149 -0
  137. package/lib/stores/LegendCubeHost.js.map +1 -0
  138. package/lib/stores/editors/CubeExplodeDraft.d.ts +87 -0
  139. package/lib/stores/editors/CubeExplodeDraft.d.ts.map +1 -0
  140. package/lib/stores/editors/CubeExplodeDraft.js +221 -0
  141. package/lib/stores/editors/CubeExplodeDraft.js.map +1 -0
  142. package/lib/stores/editors/CubeExpressionFilterDraft.d.ts +3 -0
  143. package/lib/stores/editors/CubeExpressionFilterDraft.d.ts.map +1 -1
  144. package/lib/stores/editors/CubeExpressionFilterDraft.js +4 -0
  145. package/lib/stores/editors/CubeExpressionFilterDraft.js.map +1 -1
  146. package/lib/stores/editors/CubeExtendDraft.d.ts +9 -0
  147. package/lib/stores/editors/CubeExtendDraft.d.ts.map +1 -1
  148. package/lib/stores/editors/CubeExtendDraft.js +26 -0
  149. package/lib/stores/editors/CubeExtendDraft.js.map +1 -1
  150. package/lib/stores/editors/CubeNodeDraftRegistry.d.ts.map +1 -1
  151. package/lib/stores/editors/CubeNodeDraftRegistry.js +6 -1
  152. package/lib/stores/editors/CubeNodeDraftRegistry.js.map +1 -1
  153. package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts +1 -0
  154. package/lib/stores/fixtures/CubeSalesDuckDbModel.d.ts.map +1 -1
  155. package/lib/stores/fixtures/CubeSalesDuckDbModel.js +8 -2
  156. package/lib/stores/fixtures/CubeSalesDuckDbModel.js.map +1 -1
  157. package/lib/stores/source-picker/CubeIngestTabState.d.ts +48 -14
  158. package/lib/stores/source-picker/CubeIngestTabState.d.ts.map +1 -1
  159. package/lib/stores/source-picker/CubeIngestTabState.js +159 -62
  160. package/lib/stores/source-picker/CubeIngestTabState.js.map +1 -1
  161. package/lib/stores/variant/CubeJson.d.ts +48 -0
  162. package/lib/stores/variant/CubeJson.d.ts.map +1 -0
  163. package/lib/stores/variant/CubeJson.js +207 -0
  164. package/lib/stores/variant/CubeJson.js.map +1 -0
  165. package/lib/stores/variant/CubeJsonExplorerState.d.ts +88 -0
  166. package/lib/stores/variant/CubeJsonExplorerState.d.ts.map +1 -0
  167. package/lib/stores/variant/CubeJsonExplorerState.js +297 -0
  168. package/lib/stores/variant/CubeJsonExplorerState.js.map +1 -0
  169. package/lib/stores/variant/CubeJsonFieldsState.d.ts +83 -0
  170. package/lib/stores/variant/CubeJsonFieldsState.d.ts.map +1 -0
  171. package/lib/stores/variant/CubeJsonFieldsState.js +207 -0
  172. package/lib/stores/variant/CubeJsonFieldsState.js.map +1 -0
  173. package/lib/stores/variant/CubeJsonOffers.d.ts +76 -0
  174. package/lib/stores/variant/CubeJsonOffers.d.ts.map +1 -0
  175. package/lib/stores/variant/CubeJsonOffers.js +250 -0
  176. package/lib/stores/variant/CubeJsonOffers.js.map +1 -0
  177. package/lib/stores/variant/CubeJsonParse.d.ts +28 -0
  178. package/lib/stores/variant/CubeJsonParse.d.ts.map +1 -0
  179. package/lib/stores/variant/CubeJsonParse.js +53 -0
  180. package/lib/stores/variant/CubeJsonParse.js.map +1 -0
  181. package/lib/stores/variant/CubeJsonSampling.d.ts +41 -0
  182. package/lib/stores/variant/CubeJsonSampling.d.ts.map +1 -0
  183. package/lib/stores/variant/CubeJsonSampling.js +59 -0
  184. package/lib/stores/variant/CubeJsonSampling.js.map +1 -0
  185. package/lib/stores/variant/CubeJsonShape.d.ts +87 -0
  186. package/lib/stores/variant/CubeJsonShape.d.ts.map +1 -0
  187. package/lib/stores/variant/CubeJsonShape.js +209 -0
  188. package/lib/stores/variant/CubeJsonShape.js.map +1 -0
  189. package/lib/stores/variant/CubeJsonViewerState.d.ts +33 -0
  190. package/lib/stores/variant/CubeJsonViewerState.d.ts.map +1 -0
  191. package/lib/stores/variant/CubeJsonViewerState.js +53 -0
  192. package/lib/stores/variant/CubeJsonViewerState.js.map +1 -0
  193. package/package.json +2 -2
  194. package/src/__lib__/LegendCubeHelpText.ts +4 -0
  195. package/src/__lib__/LegendCubeLabels.ts +60 -8
  196. package/src/__lib__/LegendCubeNavigation.ts +48 -0
  197. package/src/__lib__/LegendCubeTesting.ts +2 -0
  198. package/src/__lib__/LegendCubeTour.ts +173 -0
  199. package/src/__test-utils__/CubeExampleTestUtils.ts +119 -0
  200. package/src/__test-utils__/CubeVariantTestUtils.ts +103 -0
  201. package/src/components/CubeAddItems.tsx +2 -0
  202. package/src/components/CubeEditor.tsx +15 -0
  203. package/src/components/CubeFileMenu.tsx +2 -0
  204. package/src/components/CubeNodeIcon.tsx +2 -0
  205. package/src/components/canvas/CubeCanvas.tsx +17 -1
  206. package/src/components/canvas/CubeCanvasNode.tsx +7 -0
  207. package/src/components/dev-settings/CubeDevSettings.tsx +17 -2
  208. package/src/components/editors/CubeColumnPicker.tsx +3 -0
  209. package/src/components/editors/CubeExplodeEditor.tsx +207 -0
  210. package/src/components/editors/CubeExpressionFilterEditor.tsx +25 -8
  211. package/src/components/editors/CubeExtendEditor.tsx +32 -19
  212. package/src/components/editors/CubeJsonFieldsTree.tsx +316 -0
  213. package/src/components/editors/CubeNodeEditorRegistry.ts +3 -0
  214. package/src/components/grid/CubeGridRegion.tsx +21 -3
  215. package/src/components/grid/CubeGridValues.ts +8 -3
  216. package/src/components/grid/CubeJsonExplorerDialog.tsx +304 -0
  217. package/src/components/grid/CubeJsonViewerDialog.tsx +190 -0
  218. package/src/components/grid/CubeResultGrid.tsx +144 -2
  219. package/src/components/grid/CubeServerSideGrid.tsx +13 -0
  220. package/src/components/palette/CubePalette.tsx +9 -0
  221. package/src/components/source-picker/CubeIngestTab.tsx +62 -30
  222. package/src/components/tour/CubeTour.tsx +317 -0
  223. package/src/index.ts +7 -0
  224. package/src/stores/CubeEditorState.ts +36 -5
  225. package/src/stores/CubeExamples.ts +120 -2
  226. package/src/stores/CubeExamplesState.ts +9 -1
  227. package/src/stores/CubeGridQuickActions.ts +23 -12
  228. package/src/stores/CubePureText.ts +28 -0
  229. package/src/stores/CubeTourState.ts +116 -0
  230. package/src/stores/LegendCubeHost.ts +243 -0
  231. package/src/stores/editors/CubeExplodeDraft.ts +272 -0
  232. package/src/stores/editors/CubeExpressionFilterDraft.ts +4 -0
  233. package/src/stores/editors/CubeExtendDraft.ts +29 -0
  234. package/src/stores/editors/CubeNodeDraftRegistry.ts +7 -0
  235. package/src/stores/fixtures/CubeSalesDuckDbModel.ts +8 -2
  236. package/src/stores/source-picker/CubeIngestTabState.ts +253 -102
  237. package/src/stores/variant/CubeJson.ts +229 -0
  238. package/src/stores/variant/CubeJsonExplorerState.ts +378 -0
  239. package/src/stores/variant/CubeJsonFieldsState.ts +252 -0
  240. package/src/stores/variant/CubeJsonOffers.ts +359 -0
  241. package/src/stores/variant/CubeJsonParse.ts +75 -0
  242. package/src/stores/variant/CubeJsonSampling.ts +90 -0
  243. package/src/stores/variant/CubeJsonShape.ts +279 -0
  244. package/src/stores/variant/CubeJsonViewerState.ts +59 -0
  245. package/tsconfig.json +22 -1
@@ -15,6 +15,9 @@
15
15
  */
16
16
 
17
17
  import {
18
+ Explode,
19
+ Extend,
20
+ type JsonObject,
18
21
  AggregationFunction,
19
22
  Pivot,
20
23
  ColumnComparisonFilter,
@@ -59,6 +62,7 @@ import {
59
62
  import {
60
63
  CUBE_SALES_DATABASE,
61
64
  CUBE_SALES_MODEL,
65
+ CUBE_SALES_ORDERS_TABLE,
62
66
  CUBE_SALES_RUNTIME,
63
67
  CUBE_SALES_SCHEMA,
64
68
  } from './fixtures/CubeSalesDuckDbModel.js';
@@ -131,8 +135,8 @@ const SALES: CubeExampleDataset = {
131
135
  id: 'sales',
132
136
  name: 'Sales (DuckDB)',
133
137
  description:
134
- 'Made-up sales of tea and coffee by country and year, on DuckDB, which runs Pivot',
135
- tables: ['sales'],
138
+ 'Made-up sales of tea and coffee by country and year, and orders as JSON documents, on DuckDB, which runs Pivot and reads JSON',
139
+ tables: ['sales', 'orders'],
136
140
  model: CUBE_SALES_MODEL,
137
141
  runtime: CUBE_SALES_RUNTIME,
138
142
  database: CUBE_SALES_DATABASE,
@@ -188,6 +192,77 @@ const equal = (column: string, value: string): ColumnComparisonFilter =>
188
192
  value,
189
193
  });
190
194
 
195
+ /** An order's document read as JSON: Parse as JSON's column, as the engine parses its text (PLAN §11.20) */
196
+ const ORDER_DOCUMENT_COLUMN = {
197
+ name: 'document_json',
198
+ code: 'x | $x.document->toOne()->fromJson()',
199
+ lambda: {
200
+ _type: 'lambda',
201
+ body: [
202
+ {
203
+ _type: 'func',
204
+ function: 'fromJson',
205
+ parameters: [
206
+ {
207
+ _type: 'func',
208
+ function: 'toOne',
209
+ parameters: [
210
+ {
211
+ _type: 'property',
212
+ parameters: [{ _type: 'var', name: 'x' }],
213
+ property: 'document',
214
+ },
215
+ ],
216
+ },
217
+ ],
218
+ },
219
+ ],
220
+ parameters: [{ _type: 'var', name: 'x' }],
221
+ },
222
+ };
223
+
224
+ /** An order line's field as an Extend column, `x | $x.item->get('<key>')->to(@<type>)`, as the engine parses it */
225
+ const orderLineColumn = (
226
+ key: string,
227
+ type: string,
228
+ ): { name: string; code: string; lambda: JsonObject } => ({
229
+ name: key,
230
+ code: `x | $x.item->get('${key}')->to(@${type})`,
231
+ lambda: {
232
+ _type: 'lambda',
233
+ body: [
234
+ {
235
+ _type: 'func',
236
+ function: 'to',
237
+ parameters: [
238
+ {
239
+ _type: 'func',
240
+ function: 'get',
241
+ parameters: [
242
+ {
243
+ _type: 'property',
244
+ parameters: [{ _type: 'var', name: 'x' }],
245
+ property: 'item',
246
+ },
247
+ { _type: 'string', value: key },
248
+ ],
249
+ },
250
+ {
251
+ _type: 'genericTypeInstance',
252
+ genericType: {
253
+ multiplicityArguments: [],
254
+ rawType: { _type: 'packageableType', fullPath: type },
255
+ typeArguments: [],
256
+ typeVariableValues: [],
257
+ },
258
+ },
259
+ ],
260
+ },
261
+ ],
262
+ parameters: [{ _type: 'var', name: 'x' }],
263
+ },
264
+ });
265
+
191
266
  export const CUBE_EXAMPLES: readonly CubeExample[] = Object.freeze([
192
267
  example(
193
268
  NORTHWIND,
@@ -523,4 +598,47 @@ export const CUBE_EXAMPLES: readonly CubeExample[] = Object.freeze([
523
598
  connections: chain(['relational101', 'pivot101', 'sort101']),
524
599
  }),
525
600
  ),
601
+ example(
602
+ SALES,
603
+ 'sales-orders-json',
604
+ 'Orders from JSON',
605
+ "Reads each order's JSON document as a Variant column: explore its fields from the column's menu",
606
+ (table) => ({
607
+ nodes: [
608
+ table('relational101', CUBE_SALES_ORDERS_TABLE),
609
+ new Extend('extend101', [ORDER_DOCUMENT_COLUMN]),
610
+ ],
611
+ connections: chain(['relational101', 'extend101']),
612
+ }),
613
+ ),
614
+ example(
615
+ SALES,
616
+ 'sales-order-lines-json',
617
+ 'Order lines from JSON',
618
+ "One row per order line, from the array in each order's JSON document, with its product and quantity",
619
+ (table) => ({
620
+ nodes: [
621
+ table('relational101', CUBE_SALES_ORDERS_TABLE),
622
+ new Extend('extend101', [ORDER_DOCUMENT_COLUMN]),
623
+ new Explode('explode101', 'document_json', ['items'], 'item'),
624
+ new Extend('extend102', [
625
+ orderLineColumn('product', 'String'),
626
+ orderLineColumn('qty', 'Integer'),
627
+ ]),
628
+ // a lateral join keeps no order: the lines by order, then product
629
+ new Sort('sort101', [
630
+ { column: 'id', direction: SortDirection.ASC },
631
+ { column: 'product', direction: SortDirection.ASC },
632
+ { column: 'qty', direction: SortDirection.ASC },
633
+ ]),
634
+ ],
635
+ connections: chain([
636
+ 'relational101',
637
+ 'extend101',
638
+ 'explode101',
639
+ 'extend102',
640
+ 'sort101',
641
+ ]),
642
+ }),
643
+ ),
526
644
  ]);
@@ -94,7 +94,15 @@ export class CubeExamplesState {
94
94
  this.close();
95
95
  const document = example.createDocument();
96
96
  this.editorState.importDocument(document, false);
97
- yield when(() => !this.editorState.isResolvingSources);
97
+ // its tables typed, and its expressions checked by the engine, as an
98
+ // example's Extend parsing JSON is (PLAN §11.20)
99
+ yield when(
100
+ () =>
101
+ !this.editorState.isResolvingSources &&
102
+ !this.editorState.document.query.nodes.some((node) =>
103
+ this.editorState.isTypingNode(node),
104
+ ),
105
+ );
98
106
  const { execution } = this.editorState;
99
107
  // not if the user has moved on, e.g. undone the opening: typing the
100
108
  // tables replaces the document but keeps its context, which is this
@@ -76,6 +76,28 @@ export enum CubeGridCellKind {
76
76
  EMPTY = 'empty',
77
77
  }
78
78
 
79
+ /**
80
+ * What keeps the grid's actions that add a node after the node that ran from
81
+ * being used, in order (the quick actions, Explore JSON and Parse as JSON,
82
+ * PLAN §11.20): rows from an earlier query, edits not applied yet, a run in
83
+ * flight, a cube that can't change; `undefined` when nothing does
84
+ */
85
+ export const getCubeGridActionBlocker = (
86
+ editorState: CubeEditorState,
87
+ ): string | undefined => {
88
+ const { execution, document } = editorState;
89
+ return execution.result?.query !== document.query || execution.isStale
90
+ ? CUBE_QUICK_ACTION_DISABLED_REASON.STALE_ROWS
91
+ : // choosing would apply the edits first, leaving these rows stale
92
+ editorState.hasEditsToApply || editorState.nodeEditor.hasPendingInput()
93
+ ? CUBE_QUICK_ACTION_DISABLED_REASON.EDITING
94
+ : execution.isRunning
95
+ ? CUBE_QUICK_ACTION_DISABLED_REASON.RUNNING
96
+ : editorState.readOnly
97
+ ? READ_ONLY_CUBE_TITLE
98
+ : undefined;
99
+ };
100
+
79
101
  /**
80
102
  * The quick actions on a cell of the shown rows (spec §12.4, PLAN §11.4,
81
103
  * §11.5 and §11.10): "Sort by" adds an ascending Sort on the cell's column,
@@ -109,18 +131,7 @@ export const getCubeGridQuickActions = (
109
131
  cellKind === CubeGridCellKind.VALUE
110
132
  ? buildDrilldownKeyRule(column.name, column.type, cell)
111
133
  : undefined;
112
- // what keeps both from being used, in order
113
- const blocked =
114
- result.query !== query || execution.isStale
115
- ? CUBE_QUICK_ACTION_DISABLED_REASON.STALE_ROWS
116
- : // choosing would apply the edits first, leaving these rows stale
117
- editorState.hasEditsToApply || editorState.nodeEditor.hasPendingInput()
118
- ? CUBE_QUICK_ACTION_DISABLED_REASON.EDITING
119
- : execution.isRunning
120
- ? CUBE_QUICK_ACTION_DISABLED_REASON.RUNNING
121
- : editorState.readOnly
122
- ? READ_ONLY_CUBE_TITLE
123
- : undefined;
134
+ const blocked = getCubeGridActionBlocker(editorState);
124
135
  const sortReason =
125
136
  blocked ??
126
137
  (isSortableType(column.type)
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ // Pure text Cube writes for the engine to parse, as a user would type it: an
17
+ // Extend's columns from Explore JSON (PLAN §11.20), and the column names the
18
+ // expression editors insert.
19
+
20
+ const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/u;
21
+
22
+ /** A Pure string literal: quoted, its backslashes and quotes escaped */
23
+ export const pureStringLiteral = (text: string): string =>
24
+ `'${text.replace(/\\/gu, '\\\\').replace(/'/gu, "\\'")}'`;
25
+
26
+ /** How an expression reads a column: `$x.NAME`, or `$x.'a name'` for one that isn't an identifier */
27
+ export const columnAccessOf = (name: string): string =>
28
+ IDENTIFIER.test(name) ? `$x.${name}` : `$x.${pureStringLiteral(name)}`;
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { action, computed, flowResult, makeObservable, observable } from 'mobx';
18
+ import {
19
+ CUBE_EDITOR_TOUR,
20
+ CUBE_TOUR_EXAMPLE_ID,
21
+ type CubeTourStep,
22
+ } from '../__lib__/LegendCubeTour.js';
23
+ import type { CubeEditorState } from './CubeEditorState.js';
24
+ import { isCubeSampleDataOffered } from './CubeHost.js';
25
+
26
+ /** Whether a step can be shown: its controls are on the page */
27
+ export type CubeTourStepShown = (step: CubeTourStep) => boolean;
28
+
29
+ /**
30
+ * The editor's guided tour (PLAN §11.21): one step at a time, each pointing
31
+ * at its controls; a step whose controls aren't on the page is skipped. It
32
+ * starts on the cube as it is, the node editor's edits applied first; on an
33
+ * empty cube, where examples are offered, it opens one to show (one undo
34
+ * step). Nothing starts it but the user.
35
+ */
36
+ export class CubeTourState {
37
+ readonly editorState: CubeEditorState;
38
+ readonly steps: readonly CubeTourStep[] = CUBE_EDITOR_TOUR;
39
+
40
+ isOpen = false;
41
+ /** The step shown, an index into `steps` */
42
+ index = 0;
43
+ /** Whether this tour opened its example */
44
+ openedExample = false;
45
+
46
+ constructor(editorState: CubeEditorState) {
47
+ makeObservable(this, {
48
+ isOpen: observable,
49
+ index: observable,
50
+ openedExample: observable,
51
+ step: computed,
52
+ start: action,
53
+ move: action,
54
+ close: action,
55
+ });
56
+ this.editorState = editorState;
57
+ }
58
+
59
+ get step(): CubeTourStep | undefined {
60
+ return this.isOpen ? this.steps[this.index] : undefined;
61
+ }
62
+
63
+ /**
64
+ * Starts the tour at its first step; not while the node editor keeps its
65
+ * edits open, as other actions wait for it
66
+ */
67
+ start(): void {
68
+ const { editorState } = this;
69
+ if (this.isOpen || !editorState.nodeEditor.finish()) {
70
+ return;
71
+ }
72
+ const example =
73
+ editorState.document.query.isEmpty &&
74
+ !editorState.readOnly &&
75
+ isCubeSampleDataOffered(editorState.host)
76
+ ? editorState.examples.examples.find(
77
+ (candidate) => candidate.id === CUBE_TOUR_EXAMPLE_ID,
78
+ )
79
+ : undefined;
80
+ this.openedExample = example !== undefined;
81
+ this.index = 0;
82
+ this.isOpen = true;
83
+ if (example) {
84
+ // the tour goes on while it opens and runs: its steps show as they can
85
+ flowResult(editorState.examples.openExample(example)).catch(
86
+ editorState.host.applicationStore.alertUnhandledError,
87
+ );
88
+ }
89
+ }
90
+
91
+ /**
92
+ * The step `by` steps on (1) or back (-1), skipping those not shown; past
93
+ * the last, the tour ends; before the first, it stays
94
+ */
95
+ move(by: 1 | -1, isShown: CubeTourStepShown): void {
96
+ for (
97
+ let index = this.index + by;
98
+ index >= 0 && index < this.steps.length;
99
+ index += by
100
+ ) {
101
+ const step = this.steps[index];
102
+ if (step && isShown(step)) {
103
+ this.index = index;
104
+ return;
105
+ }
106
+ }
107
+ if (by === 1) {
108
+ this.close();
109
+ }
110
+ }
111
+
112
+ close(): void {
113
+ this.isOpen = false;
114
+ this.index = 0;
115
+ }
116
+ }
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Copyright (c) 2026-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { GenericLegendApplicationStore } from '@finos/legend-application';
18
+ import { DepotServerClient } from '@finos/legend-server-depot';
19
+ import {
20
+ LakehouseContractServerClient,
21
+ LakehouseIngestServerClient,
22
+ LakehousePlatformServerClient,
23
+ } from '@finos/legend-server-lakehouse';
24
+ import { MarketplaceServerClient } from '@finos/legend-server-marketplace';
25
+ import { EXTERNAL_APPLICATION_NAVIGATION__generateCubeMarketplaceDataProductUrl } from '../__lib__/LegendCubeNavigation.js';
26
+ import type { CubeConnectionExplorer } from '../graph-manager/CubeConnectionExplorer.js';
27
+ import { CubeDataProductEnvironmentType } from '../graph-manager/CubeDataProduct.js';
28
+ import type { CubeDataProductCatalog } from '../graph-manager/CubeDataProductCatalog.js';
29
+ import type { CubeEngine } from '../graph-manager/CubeEngine.js';
30
+ import { CubeEngineClientSettings } from '../graph-manager/CubeEngineClientSettings.js';
31
+ import type { CubeIngestCatalog } from '../graph-manager/CubeIngestCatalog.js';
32
+ import type { CubeProjectCatalog } from '../graph-manager/CubeProjectCatalog.js';
33
+ import type { CubeServiceCatalog } from '../graph-manager/CubeServiceCatalog.js';
34
+ import {
35
+ buildCubeConnectionExplorer,
36
+ buildCubeDataProductCatalog,
37
+ buildCubeEngine,
38
+ buildCubeIngestCatalog,
39
+ buildCubeLakehouseEnvironment,
40
+ buildCubeProjectCatalog,
41
+ buildCubeServiceCatalog,
42
+ type CubeEngineConfig,
43
+ type CubeLakehouseServices,
44
+ } from '../graph-manager/protocol/pure/CubeEngineBuilder.js';
45
+ import { getCubeRememberedWarehouse } from './CubeDataProductWarehouse.js';
46
+ import type { CubeHost } from './CubeHost.js';
47
+ import { LocalModelCatalog } from './LocalModelCatalog.js';
48
+
49
+ /**
50
+ * The servers of a Legend deployment hosting the Cube page, as its config
51
+ * names them (PLAN §11.18): the engine, and each of the others only where the
52
+ * deployment has one. Legend Query and Legend Analytics each read theirs from
53
+ * their own config.
54
+ */
55
+ export interface LegendCubeHostConfig {
56
+ readonly engine: CubeEngineConfig;
57
+ /** Published projects (PLAN §6.3) and, with a lakehouse, the data products' models */
58
+ readonly depotServerUrl?: string | undefined;
59
+ /** The lakehouse's contracts: data products need it, and a depot (PLAN §6.8) */
60
+ readonly lakehouseContractUrl?: string | undefined;
61
+ /** The lakehouse platform, which names the ingest servers: ingest data sets need it (PLAN §6.7) */
62
+ readonly lakehousePlatformUrl?: string | undefined;
63
+ /** The marketplace server, whose search the data product tab uses */
64
+ readonly marketplaceServerUrl?: string | undefined;
65
+ /** Legend Marketplace, which a production data product links to */
66
+ readonly marketplaceApplicationUrl?: string | undefined;
67
+ /** Legend Marketplace's production-parallel deployment, which a production-parallel data product links to */
68
+ readonly marketplaceProductionParallelUrl?: string | undefined;
69
+ /** The stereotype marking access point groups open to everyone, as Studio and Marketplace configure it */
70
+ readonly enterpriseStereotype?: CubeLakehouseServices['enterpriseStereotype'];
71
+ /** The lakehouse environment the host remembers for the viewer */
72
+ readonly getPreferredEnvironment?: (() => string | undefined) | undefined;
73
+ }
74
+
75
+ /**
76
+ * What Cube needs to read and run data products (PLAN §6.8), with the
77
+ * deployment's lakehouse and depot: none without both, so a deployment
78
+ * without them offers no data products. With a marketplace server too, its
79
+ * search is used; with the lakehouse platform, ingest data sets are offered.
80
+ */
81
+ export const buildLegendCubeLakehouseServices = (
82
+ applicationStore: GenericLegendApplicationStore,
83
+ config: LegendCubeHostConfig,
84
+ ): CubeLakehouseServices | undefined => {
85
+ const { tracerService } = applicationStore;
86
+ if (!config.lakehouseContractUrl || !config.depotServerUrl) {
87
+ return undefined;
88
+ }
89
+ const contractServerClient = new LakehouseContractServerClient({
90
+ baseUrl: config.lakehouseContractUrl,
91
+ });
92
+ contractServerClient.setTracerService(tracerService);
93
+ const depotServerClient = new DepotServerClient({
94
+ serverUrl: config.depotServerUrl,
95
+ });
96
+ depotServerClient.setTracerService(tracerService);
97
+ let marketplaceServerClient: MarketplaceServerClient | undefined;
98
+ if (config.marketplaceServerUrl) {
99
+ marketplaceServerClient = new MarketplaceServerClient({
100
+ serverUrl: config.marketplaceServerUrl,
101
+ // the client only stores it, never reading it, and Cube calls no subscription route
102
+ subscriptionUrl: '',
103
+ });
104
+ marketplaceServerClient.setTracerService(tracerService);
105
+ }
106
+ // ingest data sets need the platform, which names the ingest servers; the
107
+ // ingest client has no server of its own: each call names one
108
+ let platformServerClient: LakehousePlatformServerClient | undefined;
109
+ let ingestServerClient: LakehouseIngestServerClient | undefined;
110
+ if (config.lakehousePlatformUrl) {
111
+ platformServerClient = new LakehousePlatformServerClient(
112
+ config.lakehousePlatformUrl,
113
+ );
114
+ platformServerClient.setTracerService(tracerService);
115
+ ingestServerClient = new LakehouseIngestServerClient(undefined);
116
+ ingestServerClient.setTracerService(tracerService);
117
+ }
118
+ return {
119
+ platformServerClient,
120
+ ingestServerClient,
121
+ contractServerClient,
122
+ depotServerClient,
123
+ marketplaceServerClient,
124
+ getAccessToken: () => applicationStore.getAccessToken(),
125
+ getCurrentUser: () => applicationStore.identityService.currentUser,
126
+ getPreferredEnvironment: config.getPreferredEnvironment,
127
+ enterpriseStereotype: config.enterpriseStereotype,
128
+ // the marketplace of the deployment's class, where its deployment is
129
+ getMarketplaceLink: (target) => {
130
+ const marketplaceUrl =
131
+ target.environmentType ===
132
+ CubeDataProductEnvironmentType.PRODUCTION_PARALLEL
133
+ ? config.marketplaceProductionParallelUrl
134
+ : config.marketplaceApplicationUrl;
135
+ return marketplaceUrl
136
+ ? EXTERNAL_APPLICATION_NAVIGATION__generateCubeMarketplaceDataProductUrl(
137
+ marketplaceUrl,
138
+ target.dataProductId,
139
+ target.deploymentId,
140
+ target.accessPointGroup,
141
+ )
142
+ : undefined;
143
+ },
144
+ };
145
+ };
146
+
147
+ /** The published projects in the deployment's depot, or none without a depot */
148
+ export const buildLegendCubeProjectCatalog = (
149
+ applicationStore: GenericLegendApplicationStore,
150
+ config: LegendCubeHostConfig,
151
+ ): CubeProjectCatalog | undefined => {
152
+ if (!config.depotServerUrl) {
153
+ return undefined;
154
+ }
155
+ const depotServerClient = new DepotServerClient({
156
+ serverUrl: config.depotServerUrl,
157
+ });
158
+ depotServerClient.setTracerService(applicationStore.tracerService);
159
+ return buildCubeProjectCatalog(depotServerClient);
160
+ };
161
+
162
+ /** What tests give the host in place of what it would build */
163
+ export interface LegendCubeHostParts {
164
+ readonly engine?: CubeEngine | undefined;
165
+ readonly connectionExplorer?: CubeConnectionExplorer | undefined;
166
+ readonly projectCatalog?: CubeProjectCatalog | undefined;
167
+ }
168
+
169
+ /**
170
+ * A Legend deployment as the host of the Cube page (PLAN §3.5, §11.18): the
171
+ * application's store, the engine and the bundled models, direct database
172
+ * connections read through the engine, the services in its registry, and
173
+ * the deployment's projects, data products and ingest data sets where it has
174
+ * the servers for them. Legend Query and Legend Analytics each build one per
175
+ * visit to the page, from their own config.
176
+ */
177
+ export class LegendCubeHost implements CubeHost {
178
+ readonly applicationStore: GenericLegendApplicationStore;
179
+ readonly engine: CubeEngine;
180
+ readonly modelCatalog: LocalModelCatalog;
181
+ readonly connectionExplorer: CubeConnectionExplorer;
182
+ readonly dataProductCatalog: CubeDataProductCatalog | undefined;
183
+ readonly ingestCatalog: CubeIngestCatalog | undefined;
184
+ readonly projectCatalog: CubeProjectCatalog | undefined;
185
+ readonly serviceCatalog: CubeServiceCatalog;
186
+ /** The engine URL and compression Cube's engine clients share, which Dev Settings changes */
187
+ readonly engineClientSettings: CubeEngineClientSettings;
188
+
189
+ constructor(
190
+ applicationStore: GenericLegendApplicationStore,
191
+ config: LegendCubeHostConfig,
192
+ parts?: LegendCubeHostParts,
193
+ ) {
194
+ this.applicationStore = applicationStore;
195
+ const { tracerService } = applicationStore;
196
+ this.engineClientSettings = new CubeEngineClientSettings(config.engine);
197
+ const lakehouse = buildLegendCubeLakehouseServices(
198
+ applicationStore,
199
+ config,
200
+ );
201
+ this.ingestCatalog = lakehouse
202
+ ? buildCubeIngestCatalog(
203
+ config.engine,
204
+ tracerService,
205
+ lakehouse,
206
+ this.engineClientSettings,
207
+ )
208
+ : undefined;
209
+ this.engine =
210
+ parts?.engine ??
211
+ buildCubeEngine(config.engine, tracerService, {
212
+ ingestCatalog: this.ingestCatalog,
213
+ lakehouseEnvironment: lakehouse
214
+ ? buildCubeLakehouseEnvironment(lakehouse)
215
+ : undefined,
216
+ getRememberedWarehouse: () =>
217
+ getCubeRememberedWarehouse(applicationStore.userDataService),
218
+ clientSettings: this.engineClientSettings,
219
+ });
220
+ this.dataProductCatalog = lakehouse
221
+ ? buildCubeDataProductCatalog(lakehouse)
222
+ : undefined;
223
+ this.modelCatalog = new LocalModelCatalog(this.engine);
224
+ // its own client, configured as the engine's
225
+ this.connectionExplorer =
226
+ parts?.connectionExplorer ??
227
+ buildCubeConnectionExplorer(
228
+ config.engine,
229
+ tracerService,
230
+ this.engineClientSettings,
231
+ );
232
+ // it reads nothing until the source dialog's Project tab opens
233
+ this.projectCatalog =
234
+ parts?.projectCatalog ??
235
+ buildLegendCubeProjectCatalog(applicationStore, config);
236
+ // in progress (PLAN §6.4); it reads nothing until the Service tab opens
237
+ this.serviceCatalog = buildCubeServiceCatalog(
238
+ config.engine,
239
+ tracerService,
240
+ this.engineClientSettings,
241
+ );
242
+ }
243
+ }