@finos/legend-application-studio 28.19.99 → 28.19.100

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 (48) hide show
  1. package/lib/components/LegendStudioWebApplication.d.ts.map +1 -1
  2. package/lib/components/LegendStudioWebApplication.js +3 -3
  3. package/lib/components/LegendStudioWebApplication.js.map +1 -1
  4. package/lib/components/editor/LegendSQLPlaygroundModal.d.ts +22 -0
  5. package/lib/components/editor/LegendSQLPlaygroundModal.d.ts.map +1 -0
  6. package/lib/components/editor/LegendSQLPlaygroundModal.js +32 -0
  7. package/lib/components/editor/LegendSQLPlaygroundModal.js.map +1 -0
  8. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js +1 -1
  9. package/lib/components/editor/editor-group/dataProduct/DataProductEditor.js.map +1 -1
  10. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js +3 -3
  11. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js.map +1 -1
  12. package/lib/components/editor/side-bar/CreateNewElementModal.d.ts.map +1 -1
  13. package/lib/components/editor/side-bar/CreateNewElementModal.js +4 -4
  14. package/lib/components/editor/side-bar/CreateNewElementModal.js.map +1 -1
  15. package/lib/components/editor/side-bar/Explorer.d.ts.map +1 -1
  16. package/lib/components/editor/side-bar/Explorer.js +10 -3
  17. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  18. package/lib/index.css +2 -2
  19. package/lib/index.css.map +1 -1
  20. package/lib/package.json +1 -1
  21. package/lib/stores/editor/EditorStore.d.ts +2 -0
  22. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  23. package/lib/stores/editor/EditorStore.js +3 -0
  24. package/lib/stores/editor/EditorStore.js.map +1 -1
  25. package/lib/stores/editor/LegendSQLStudioPlaygroundState.d.ts +31 -0
  26. package/lib/stores/editor/LegendSQLStudioPlaygroundState.d.ts.map +1 -0
  27. package/lib/stores/editor/LegendSQLStudioPlaygroundState.js +141 -0
  28. package/lib/stores/editor/LegendSQLStudioPlaygroundState.js.map +1 -0
  29. package/lib/stores/editor/NewElementState.d.ts +2 -2
  30. package/lib/stores/editor/NewElementState.d.ts.map +1 -1
  31. package/lib/stores/editor/NewElementState.js +6 -6
  32. package/lib/stores/editor/NewElementState.js.map +1 -1
  33. package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.d.ts +1 -1
  34. package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.d.ts.map +1 -1
  35. package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.js +1 -1
  36. package/lib/stores/editor/panel-group/StudioSQLPlaygroundPanelState.js.map +1 -1
  37. package/package.json +11 -11
  38. package/src/components/LegendStudioWebApplication.tsx +10 -3
  39. package/src/components/editor/LegendSQLPlaygroundModal.tsx +74 -0
  40. package/src/components/editor/editor-group/dataProduct/DataProductEditor.tsx +2 -2
  41. package/src/components/editor/panel-group/SQLPlaygroundPanel.tsx +5 -5
  42. package/src/components/editor/side-bar/CreateNewElementModal.tsx +11 -10
  43. package/src/components/editor/side-bar/Explorer.tsx +29 -0
  44. package/src/stores/editor/EditorStore.ts +5 -0
  45. package/src/stores/editor/LegendSQLStudioPlaygroundState.ts +228 -0
  46. package/src/stores/editor/NewElementState.ts +9 -6
  47. package/src/stores/editor/panel-group/StudioSQLPlaygroundPanelState.ts +1 -1
  48. package/tsconfig.json +2 -0
@@ -34,7 +34,7 @@ import {
34
34
  Dialog,
35
35
  compareLabelFn,
36
36
  CustomSelectorInput,
37
- Checkbox,
37
+ PanelBooleanField,
38
38
  } from '@finos/legend-art';
39
39
  import type { EditorStore } from '../../../stores/editor/EditorStore.js';
40
40
  import {
@@ -494,8 +494,8 @@ const NewLakehouseDataProductEditor = observer(() => {
494
494
  event,
495
495
  ) => newProductDriver.setTitle(event.target.value);
496
496
 
497
- const onTypeChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
498
- newProductDriver.setModelled(event.target.checked);
497
+ const setIsModeled = (val: boolean): void => {
498
+ newProductDriver.setModeled(val);
499
499
  };
500
500
 
501
501
  return (
@@ -510,14 +510,15 @@ const NewLakehouseDataProductEditor = observer(() => {
510
510
  placeholder={`Choose a title for this Data Product to display in Marketplace`}
511
511
  />
512
512
  </div>
513
- <div className="explorer__new-element-modal__driver">
514
- <Checkbox
515
- checked={newProductDriver.modelled}
516
- onChange={onTypeChange}
517
- size="small"
518
- className="panel__content__form__section__list__item__content__actions-exclude__btn"
513
+
514
+ <div className="explorer__new-element-modal__driver--bottom-padding">
515
+ <PanelBooleanField
516
+ isReadOnly={false}
517
+ value={newProductDriver.modeled}
518
+ name="Modeled"
519
+ prompt="Expose data product access points through curated data models"
520
+ update={(value?: boolean): void => setIsModeled(Boolean(value))}
519
521
  />
520
- <span>Modeled Data Product</span>
521
522
  </div>
522
523
  </>
523
524
  );
@@ -115,6 +115,8 @@ import {
115
115
  isRelationalDatabaseConnection,
116
116
  LegendSDLC,
117
117
  DataProduct,
118
+ IngestDefinition,
119
+ isAccessorDataProductOrIngestDefinition,
118
120
  } from '@finos/legend-graph';
119
121
  import {
120
122
  ActionAlertActionType,
@@ -125,6 +127,7 @@ import {
125
127
  getPackageableElementOptionFormatter,
126
128
  type PackageableElementOption,
127
129
  } from '@finos/legend-lego/graph-editor';
130
+ import { LegendSQLPlaygroundModal } from '../LegendSQLPlaygroundModal.js';
128
131
  import {
129
132
  PACKAGEABLE_ELEMENT_GROUP_BY_CATEGORY,
130
133
  PACKAGEABLE_ELEMENT_TYPE,
@@ -542,6 +545,16 @@ const ExplorerContextMenu = observer(
542
545
  await queryDataProduct(node.packageableElement, editorStore);
543
546
  }
544
547
  });
548
+ const openLegendSqlPlayground = (): void => {
549
+ if (
550
+ node?.packageableElement &&
551
+ isAccessorDataProductOrIngestDefinition(node.packageableElement)
552
+ ) {
553
+ editorStore.legendSQLStudioPlaygroundState.open(
554
+ node.packageableElement,
555
+ );
556
+ }
557
+ };
545
558
  const buildServiceQuery = editorStore.applicationStore.guardUnhandledError(
546
559
  async () => {
547
560
  if (node?.packageableElement instanceof Service) {
@@ -858,6 +871,9 @@ const ExplorerContextMenu = observer(
858
871
  <MenuContentItem onClick={buildDataProductQuery}>
859
872
  Query...
860
873
  </MenuContentItem>
874
+ <MenuContentItem onClick={openLegendSqlPlayground}>
875
+ Run SQL...
876
+ </MenuContentItem>
861
877
  <MenuContentDivider />
862
878
  </>
863
879
  )}
@@ -877,6 +893,14 @@ const ExplorerContextMenu = observer(
877
893
  </MenuContentItem>
878
894
  </>
879
895
  )}
896
+ {node.packageableElement instanceof IngestDefinition && (
897
+ <>
898
+ <MenuContentItem onClick={openLegendSqlPlayground}>
899
+ Run SQL...
900
+ </MenuContentItem>
901
+ <MenuContentDivider />
902
+ </>
903
+ )}
880
904
  {isElementSupportedByDataCube(node.packageableElement) && (
881
905
  <>
882
906
  <MenuContentItem onClick={openCubeViewer}>
@@ -1579,6 +1603,11 @@ export const Explorer = observer(() => {
1579
1603
  </div>
1580
1604
  </div>
1581
1605
  </div>
1606
+ {editorStore.legendSQLStudioPlaygroundState.isOpen && (
1607
+ <LegendSQLPlaygroundModal
1608
+ playgroundState={editorStore.legendSQLStudioPlaygroundState}
1609
+ />
1610
+ )}
1582
1611
  </div>
1583
1612
  );
1584
1613
  });
@@ -110,6 +110,7 @@ import type { GraphEditorMode } from './GraphEditorMode.js';
110
110
  import { GraphEditGrammarModeState } from './GraphEditGrammarModeState.js';
111
111
  import { GlobalBulkServiceRegistrationState } from './sidebar-state/BulkServiceRegistrationState.js';
112
112
  import { StudioSQLPlaygroundPanelState } from './panel-group/StudioSQLPlaygroundPanelState.js';
113
+ import { LegendSQLStudioPlaygroundState } from './LegendSQLStudioPlaygroundState.js';
113
114
  import type { QuickInputState } from './QuickInputState.js';
114
115
  import { GlobalEndToEndWorkflowState } from './sidebar-state/end-to-end-workflow/GlobalEndToEndFlowState.js';
115
116
  import {
@@ -184,6 +185,7 @@ export class EditorStore implements CommandRegistrar {
184
185
  devToolState: DevToolPanelState;
185
186
  devMetadataState: DevMetadataState;
186
187
  studioSqlPlaygroundState: StudioSQLPlaygroundPanelState;
188
+ legendSQLStudioPlaygroundState: LegendSQLStudioPlaygroundState;
187
189
 
188
190
  modelImporterState: ModelImporterState;
189
191
  projectConfigurationEditorState: ProjectConfigurationEditorState;
@@ -281,6 +283,9 @@ export class EditorStore implements CommandRegistrar {
281
283
  this.devToolState = new DevToolPanelState(this);
282
284
  this.devMetadataState = new DevMetadataState(this);
283
285
  this.studioSqlPlaygroundState = new StudioSQLPlaygroundPanelState(this);
286
+ this.legendSQLStudioPlaygroundState = new LegendSQLStudioPlaygroundState(
287
+ this,
288
+ );
284
289
  this.embeddedQueryBuilderState = new EmbeddedQueryBuilderState(this);
285
290
  // side bar panels
286
291
  this.explorerTreeState = new ExplorerTreeState(this);
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Copyright (c) 2020-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 {
18
+ type GeneratorFn,
19
+ assertErrorThrown,
20
+ LogEvent,
21
+ csvStringify,
22
+ } from '@finos/legend-shared';
23
+ import { observable, makeObservable, flow, flowResult } from 'mobx';
24
+ import {
25
+ type PackageableElement,
26
+ V1_DataProductArtifact,
27
+ DataProduct,
28
+ IngestDefinition,
29
+ type TEMPORARY_IngestContent,
30
+ type ArtifactGenerationExtensionResult,
31
+ TDSExecutionResult,
32
+ type RawLambda,
33
+ type ExecutionResultWithMetadata,
34
+ LakehouseRuntime,
35
+ GRAPH_MANAGER_EVENT,
36
+ } from '@finos/legend-graph';
37
+ import type { EditorStore } from './EditorStore.js';
38
+ import {
39
+ LegendSQLPlaygroundState,
40
+ DEFAULT_SQL_TEXT,
41
+ buildDefaultDataProductQuery,
42
+ buildDefaultIngestQuery,
43
+ } from '@finos/legend-query-builder';
44
+
45
+ const DATA_PRODUCT_ARTIFACT_EXTENSION = 'dataProduct';
46
+
47
+ export class LegendSQLStudioPlaygroundState extends LegendSQLPlaygroundState {
48
+ readonly editorStore: EditorStore;
49
+ targetElement?: PackageableElement | undefined;
50
+ isOpen = false;
51
+
52
+ constructor(editorStore: EditorStore) {
53
+ super();
54
+ makeObservable(this, {
55
+ targetElement: observable,
56
+ isOpen: observable,
57
+ executeRawSQL: flow,
58
+ initializeAccessorExplorer: flow,
59
+ });
60
+ this.editorStore = editorStore;
61
+ }
62
+
63
+ open(element: PackageableElement): void {
64
+ this.isOpen = true;
65
+ this.targetElement = element;
66
+ this.accessorExplorerState = undefined;
67
+ flowResult(this.initializeAccessorExplorer()).catch(
68
+ this.editorStore.applicationStore.alertUnhandledError,
69
+ );
70
+ if (element instanceof DataProduct) {
71
+ const firstAccessPointId =
72
+ element.accessPointGroups[0]?.accessPoints[0]?.id;
73
+ this.setSQLQuery(
74
+ `${DEFAULT_SQL_TEXT}${buildDefaultDataProductQuery(element.path, firstAccessPointId)}`,
75
+ );
76
+ } else if (element instanceof IngestDefinition) {
77
+ const content = element.content as unknown as
78
+ | TEMPORARY_IngestContent
79
+ | undefined;
80
+ const firstDatasetName = content?.datasets?.[0]?.name;
81
+ this.setSQLQuery(
82
+ `${DEFAULT_SQL_TEXT}${buildDefaultIngestQuery(element.path, firstDatasetName)}`,
83
+ );
84
+ } else {
85
+ this.setSQLQuery(DEFAULT_SQL_TEXT);
86
+ }
87
+ }
88
+
89
+ close(): void {
90
+ this.isOpen = false;
91
+ this.targetElement = undefined;
92
+ this.accessorExplorerState = undefined;
93
+ this.setSQLQuery(DEFAULT_SQL_TEXT);
94
+ }
95
+
96
+ *initializeAccessorExplorer(): GeneratorFn<void> {
97
+ if (this.accessorExplorerState || !this.targetElement) {
98
+ return;
99
+ }
100
+ try {
101
+ const entities = this.editorStore.graphManagerState.graph.allElements
102
+ .filter(
103
+ (element) =>
104
+ element instanceof DataProduct ||
105
+ element instanceof IngestDefinition,
106
+ )
107
+ .map((element) =>
108
+ this.editorStore.graphManagerState.graphManager.elementToEntity(
109
+ element,
110
+ ),
111
+ );
112
+
113
+ yield flowResult(
114
+ this.initializeExplorer(
115
+ entities,
116
+ this.editorStore.graphManagerState.graphManager.pluginManager.getPureProtocolProcessorPlugins(),
117
+ (path) => this.fetchDataProductArtifact(path),
118
+ ),
119
+ );
120
+ } catch (error) {
121
+ assertErrorThrown(error);
122
+ this.editorStore.applicationStore.logService.error(
123
+ LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
124
+ error,
125
+ );
126
+ this.editorStore.applicationStore.notificationService.notifyError(error);
127
+ }
128
+ }
129
+
130
+ private async fetchDataProductArtifact(
131
+ dataProductPath: string,
132
+ ): Promise<V1_DataProductArtifact | undefined> {
133
+ try {
134
+ const generatedArtifacts =
135
+ (await this.editorStore.graphManagerState.graphManager.generateArtifacts(
136
+ this.editorStore.graphManagerState.graph,
137
+ this.editorStore.graphEditorMode.getGraphTextInputOption(),
138
+ [dataProductPath],
139
+ )) as unknown as ArtifactGenerationExtensionResult;
140
+
141
+ const dataProductArtifactResults = generatedArtifacts.values.filter(
142
+ (artifact) => artifact.extension === DATA_PRODUCT_ARTIFACT_EXTENSION,
143
+ );
144
+
145
+ for (const artifactResult of dataProductArtifactResults) {
146
+ for (const artifactByElement of artifactResult.artifactsByExtensionElements) {
147
+ const dataProductContent = artifactByElement.files[0]?.content;
148
+ if (dataProductContent) {
149
+ return V1_DataProductArtifact.serialization.fromJson(
150
+ JSON.parse(dataProductContent),
151
+ );
152
+ }
153
+ }
154
+ }
155
+ } catch (error) {
156
+ assertErrorThrown(error);
157
+ this.editorStore.applicationStore.notificationService.notifyError(
158
+ `Error fetching artifact for ${dataProductPath}: ${error}`,
159
+ );
160
+ }
161
+ return undefined;
162
+ }
163
+
164
+ override *executeRawSQL(): GeneratorFn<void> {
165
+ if (this.executeRawSQLState.isInProgress || !this.targetElement) {
166
+ return;
167
+ }
168
+ try {
169
+ this.executeRawSQLState.inProgress();
170
+ const sql = this.getSelectedSQL();
171
+ const start = Date.now();
172
+ const sqlQuery = `#SQL{${sql}}#`;
173
+
174
+ const runtimes = this.editorStore.graphManagerState.graph.ownRuntimes;
175
+ const packageableRuntime = runtimes[0];
176
+ if (!packageableRuntime) {
177
+ this.editorStore.applicationStore.notificationService.notifyError(
178
+ new Error('No runtime found in the graph'),
179
+ );
180
+ return;
181
+ }
182
+ if (!(packageableRuntime.runtimeValue instanceof LakehouseRuntime)) {
183
+ this.editorStore.applicationStore.notificationService.notifyError(
184
+ new Error('Runtime must be a LakehouseRuntime'),
185
+ );
186
+ return;
187
+ }
188
+ const runtime = packageableRuntime.runtimeValue;
189
+ const queryToExecute = `${sqlQuery}->from(${packageableRuntime.path})`;
190
+
191
+ const lambda =
192
+ (yield this.editorStore.graphManagerState.graphManager.pureCodeToLambda(
193
+ queryToExecute,
194
+ )) as RawLambda;
195
+
196
+ const executionResult =
197
+ (yield this.editorStore.graphManagerState.graphManager.runQuery(
198
+ lambda,
199
+ undefined,
200
+ runtime,
201
+ this.editorStore.graphManagerState.graph,
202
+ )) as ExecutionResultWithMetadata;
203
+
204
+ const result = executionResult.executionResult;
205
+ if (result instanceof TDSExecutionResult) {
206
+ const data = result.result.rows.map((row) => row.values);
207
+ const csvData = csvStringify([result.result.columns, ...data]);
208
+ this.setSqlExecutionResult({
209
+ value: csvData,
210
+ sqlDuration: Date.now() - start,
211
+ });
212
+ } else {
213
+ this.editorStore.applicationStore.notificationService.notifyError(
214
+ 'Expected TDS execution result, got unsupported result type',
215
+ );
216
+ }
217
+ } catch (error) {
218
+ assertErrorThrown(error);
219
+ this.editorStore.applicationStore.logService.error(
220
+ LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
221
+ error,
222
+ );
223
+ this.editorStore.applicationStore.notificationService.notifyError(error);
224
+ } finally {
225
+ this.executeRawSQLState.complete();
226
+ }
227
+ }
228
+ }
@@ -517,15 +517,15 @@ export class NewPackageableConnectionDriver extends NewElementDriver<Packageable
517
517
 
518
518
  export class NewLakehouseDataProductDriver extends NewElementDriver<DataProduct> {
519
519
  title: string;
520
- modelled = false;
520
+ modeled = false;
521
521
 
522
522
  constructor(editorStore: EditorStore) {
523
523
  super(editorStore);
524
524
  this.title = '';
525
525
  makeObservable(this, {
526
526
  title: observable,
527
- modelled: observable,
528
- setModelled: action,
527
+ modeled: observable,
528
+ setModeled: action,
529
529
  setTitle: action,
530
530
  isValid: computed,
531
531
  });
@@ -534,18 +534,21 @@ export class NewLakehouseDataProductDriver extends NewElementDriver<DataProduct>
534
534
  override get isValid(): boolean {
535
535
  return Boolean(this.title);
536
536
  }
537
+
537
538
  setTitle(val: string) {
538
539
  this.title = val;
539
540
  }
540
- setModelled(val: boolean) {
541
- this.modelled = val;
541
+
542
+ setModeled(val: boolean) {
543
+ this.modeled = val;
542
544
  }
545
+
543
546
  override createElement(name: string): DataProduct {
544
547
  const dataProduct = new DataProduct(name);
545
548
  dataProduct_setTitle(dataProduct, this.title);
546
549
  dataProduct_setType(dataProduct, new InternalDataProductType());
547
550
 
548
- if (!this.modelled) {
551
+ if (!this.modeled) {
549
552
  const defaultGroup = new AccessPointGroup();
550
553
  defaultGroup.id = 'default';
551
554
  dataProduct_addAccessPointGroup(dataProduct, defaultGroup);
@@ -35,7 +35,7 @@ import type { CommandRegistrar } from '@finos/legend-application';
35
35
  import { STO_RELATIONAL_LEGEND_STUDIO_COMMAND_KEY } from '../../../__lib__/STO_Relational_LegendStudioCommand.js';
36
36
  import { PANEL_MODE } from '../EditorConfig.js';
37
37
  import { DatabaseSchemaExplorerState } from '../editor-state/element-editor-state/connection/DatabaseBuilderState.js';
38
- import { AbstractSQLPlaygroundState } from '@finos/legend-lego/sql-playground';
38
+ import { AbstractSQLPlaygroundState } from '@finos/legend-query-builder';
39
39
 
40
40
  const DEFAULT_SQL_TEXT = `--Start building your SQL. Note that you can also drag-and-drop nodes from schema explorer\n`;
41
41
 
package/tsconfig.json CHANGED
@@ -92,6 +92,7 @@
92
92
  "./src/stores/editor/GraphEditFormModeState.ts",
93
93
  "./src/stores/editor/GraphEditGrammarModeState.ts",
94
94
  "./src/stores/editor/GraphEditorMode.ts",
95
+ "./src/stores/editor/LegendSQLStudioPlaygroundState.ts",
95
96
  "./src/stores/editor/NewElementState.ts",
96
97
  "./src/stores/editor/QuickInputState.ts",
97
98
  "./src/stores/editor/StandardEditorMode.ts",
@@ -232,6 +233,7 @@
232
233
  "./src/components/editor/Editor.tsx",
233
234
  "./src/components/editor/EditorStoreProvider.tsx",
234
235
  "./src/components/editor/EmbeddedQueryBuilder.tsx",
236
+ "./src/components/editor/LegendSQLPlaygroundModal.tsx",
235
237
  "./src/components/editor/QuickInput.tsx",
236
238
  "./src/components/editor/ShowcaseSideBar.tsx",
237
239
  "./src/components/editor/StatusBar.tsx",