@finos/legend-application-studio 27.0.0 → 27.1.0

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 (98) hide show
  1. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts +9 -1
  2. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioApplicationNavigationContext.js +11 -1
  4. package/lib/__lib__/LegendStudioApplicationNavigationContext.js.map +1 -1
  5. package/lib/application/LegendStudioApplicationConfig.d.ts +6 -0
  6. package/lib/application/LegendStudioApplicationConfig.d.ts.map +1 -1
  7. package/lib/application/LegendStudioApplicationConfig.js +7 -0
  8. package/lib/application/LegendStudioApplicationConfig.js.map +1 -1
  9. package/lib/components/ElementIconUtils.d.ts.map +1 -1
  10. package/lib/components/ElementIconUtils.js +3 -1
  11. package/lib/components/ElementIconUtils.js.map +1 -1
  12. package/lib/components/editor/editor-group/ModelImporter.d.ts.map +1 -1
  13. package/lib/components/editor/editor-group/ModelImporter.js +1 -0
  14. package/lib/components/editor/editor-group/ModelImporter.js.map +1 -1
  15. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.d.ts.map +1 -1
  16. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.js +8 -10
  17. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilder.js.map +1 -1
  18. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.d.ts.map +1 -1
  19. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js +5 -0
  20. package/lib/components/editor/editor-group/data-editor/EmbeddedDataEditor.js.map +1 -1
  21. package/lib/components/editor/editor-group/data-editor/RelationalCSVDataEditor.d.ts.map +1 -1
  22. package/lib/components/editor/editor-group/data-editor/RelationalCSVDataEditor.js +3 -0
  23. package/lib/components/editor/editor-group/data-editor/RelationalCSVDataEditor.js.map +1 -1
  24. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.d.ts.map +1 -1
  25. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.js +3 -0
  26. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.js.map +1 -1
  27. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetElementEditor.d.ts.map +1 -1
  28. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetElementEditor.js +3 -0
  29. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetElementEditor.js.map +1 -1
  30. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetModelGenerationEditor.d.ts.map +1 -1
  31. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetModelGenerationEditor.js +3 -0
  32. package/lib/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetModelGenerationEditor.js.map +1 -1
  33. package/lib/components/editor/editor-group/mapping-editor/NewMappingElementModal.js +1 -2
  34. package/lib/components/editor/editor-group/mapping-editor/NewMappingElementModal.js.map +1 -1
  35. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.d.ts.map +1 -1
  36. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.js +9 -4
  37. package/lib/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.js.map +1 -1
  38. package/lib/components/editor/panel-group/SQLPlaygroundPanel.d.ts.map +1 -1
  39. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js +38 -6
  40. package/lib/components/editor/panel-group/SQLPlaygroundPanel.js.map +1 -1
  41. package/lib/components/editor/side-bar/CreateNewElementModal.d.ts +3 -0
  42. package/lib/components/editor/side-bar/CreateNewElementModal.d.ts.map +1 -1
  43. package/lib/components/editor/side-bar/CreateNewElementModal.js +43 -1
  44. package/lib/components/editor/side-bar/CreateNewElementModal.js.map +1 -1
  45. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.d.ts.map +1 -1
  46. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.js +10 -0
  47. package/lib/components/extensions/Core_LegendStudioApplicationPlugin.js.map +1 -1
  48. package/lib/index.css +2 -2
  49. package/lib/index.css.map +1 -1
  50. package/lib/package.json +1 -1
  51. package/lib/stores/editor/EditorGraphState.js +1 -1
  52. package/lib/stores/editor/EditorGraphState.js.map +1 -1
  53. package/lib/stores/editor/EditorStore.d.ts.map +1 -1
  54. package/lib/stores/editor/EditorStore.js +10 -2
  55. package/lib/stores/editor/EditorStore.js.map +1 -1
  56. package/lib/stores/editor/NewElementState.d.ts +0 -1
  57. package/lib/stores/editor/NewElementState.d.ts.map +1 -1
  58. package/lib/stores/editor/NewElementState.js +43 -10
  59. package/lib/stores/editor/NewElementState.js.map +1 -1
  60. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts +0 -1
  61. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts.map +1 -1
  62. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js +26 -30
  63. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js.map +1 -1
  64. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.d.ts +4 -1
  65. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.d.ts.map +1 -1
  66. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js +5 -1
  67. package/lib/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.js.map +1 -1
  68. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.d.ts +9 -0
  69. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.d.ts.map +1 -1
  70. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.js +149 -3
  71. package/lib/stores/editor/panel-group/SQLPlaygroundPanelState.js.map +1 -1
  72. package/lib/stores/editor/utils/ModelClassifierUtils.d.ts +2 -5
  73. package/lib/stores/editor/utils/ModelClassifierUtils.d.ts.map +1 -1
  74. package/lib/stores/editor/utils/ModelClassifierUtils.js +2 -6
  75. package/lib/stores/editor/utils/ModelClassifierUtils.js.map +1 -1
  76. package/package.json +3 -3
  77. package/src/__lib__/LegendStudioApplicationNavigationContext.ts +12 -1
  78. package/src/application/LegendStudioApplicationConfig.ts +8 -0
  79. package/src/components/ElementIconUtils.tsx +3 -0
  80. package/src/components/editor/editor-group/ModelImporter.tsx +4 -0
  81. package/src/components/editor/editor-group/connection-editor/DatabaseBuilder.tsx +12 -13
  82. package/src/components/editor/editor-group/data-editor/EmbeddedDataEditor.tsx +11 -1
  83. package/src/components/editor/editor-group/data-editor/RelationalCSVDataEditor.tsx +6 -0
  84. package/src/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_BindingElementEditor.tsx +7 -0
  85. package/src/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetElementEditor.tsx +6 -0
  86. package/src/components/editor/editor-group/external-format-editor/DSL_ExternalFormat_SchemaSetModelGenerationEditor.tsx +6 -0
  87. package/src/components/editor/editor-group/mapping-editor/NewMappingElementModal.tsx +1 -1
  88. package/src/components/editor/editor-group/service-editor/testable/ServiceTestsEditor.tsx +12 -7
  89. package/src/components/editor/panel-group/SQLPlaygroundPanel.tsx +104 -18
  90. package/src/components/editor/side-bar/CreateNewElementModal.tsx +111 -1
  91. package/src/components/extensions/Core_LegendStudioApplicationPlugin.tsx +10 -0
  92. package/src/stores/editor/EditorGraphState.ts +1 -1
  93. package/src/stores/editor/EditorStore.ts +29 -17
  94. package/src/stores/editor/NewElementState.ts +109 -20
  95. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.ts +33 -40
  96. package/src/stores/editor/editor-state/element-editor-state/mapping/MappingEditorState.ts +5 -1
  97. package/src/stores/editor/panel-group/SQLPlaygroundPanelState.ts +224 -1
  98. package/src/stores/editor/utils/ModelClassifierUtils.ts +2 -6
@@ -929,25 +929,37 @@ export class EditorStore implements CommandRegistrar {
929
929
  PACKAGEABLE_ELEMENT_TYPE.ASSOCIATION,
930
930
  PACKAGEABLE_ELEMENT_TYPE.FUNCTION,
931
931
  PACKAGEABLE_ELEMENT_TYPE.MEASURE,
932
- PACKAGEABLE_ELEMENT_TYPE.MAPPING,
933
- PACKAGEABLE_ELEMENT_TYPE.RUNTIME,
934
- PACKAGEABLE_ELEMENT_TYPE.CONNECTION,
935
- PACKAGEABLE_ELEMENT_TYPE.SERVICE,
936
- PACKAGEABLE_ELEMENT_TYPE.GENERATION_SPECIFICATION,
937
- PACKAGEABLE_ELEMENT_TYPE.FILE_GENERATION,
938
- PACKAGEABLE_ELEMENT_TYPE.FLAT_DATA_STORE,
939
- PACKAGEABLE_ELEMENT_TYPE.DATABASE,
940
- PACKAGEABLE_ELEMENT_TYPE.DATA,
941
932
  ] as string[]
942
933
  ).concat(
943
- this.pluginManager
944
- .getApplicationPlugins()
945
- .flatMap(
946
- (plugin) =>
947
- (
948
- plugin as DSL_LegendStudioApplicationPlugin_Extension
949
- ).getExtraSupportedElementTypes?.() ?? [],
950
- ),
934
+ (
935
+ [
936
+ PACKAGEABLE_ELEMENT_TYPE.MAPPING,
937
+ PACKAGEABLE_ELEMENT_TYPE.RUNTIME,
938
+ PACKAGEABLE_ELEMENT_TYPE.CONNECTION,
939
+ PACKAGEABLE_ELEMENT_TYPE.SERVICE,
940
+ PACKAGEABLE_ELEMENT_TYPE.GENERATION_SPECIFICATION,
941
+ PACKAGEABLE_ELEMENT_TYPE.FILE_GENERATION,
942
+ PACKAGEABLE_ELEMENT_TYPE.FLAT_DATA_STORE,
943
+ PACKAGEABLE_ELEMENT_TYPE.DATABASE,
944
+ PACKAGEABLE_ELEMENT_TYPE.DATA,
945
+ this.applicationStore.config.options
946
+ .TEMPORARY__enableLocalConnectionBuilder
947
+ ? PACKAGEABLE_ELEMENT_TYPE.TEMPORARY__LOCAL_CONNECTION
948
+ : undefined,
949
+ ] as (string | undefined)[]
950
+ )
951
+ .filter(isNonNullable)
952
+ .concat(
953
+ this.pluginManager
954
+ .getApplicationPlugins()
955
+ .flatMap(
956
+ (plugin) =>
957
+ (
958
+ plugin as DSL_LegendStudioApplicationPlugin_Extension
959
+ ).getExtraSupportedElementTypes?.() ?? [],
960
+ ),
961
+ )
962
+ .sort((a, b) => a.localeCompare(b)),
951
963
  );
952
964
  }
953
965
 
@@ -74,6 +74,12 @@ import {
74
74
  Multiplicity,
75
75
  PrimitiveType,
76
76
  LocalH2DatasourceSpecification,
77
+ SnowflakeDatasourceSpecification,
78
+ SnowflakePublicAuthenticationStrategy,
79
+ StoreConnections,
80
+ ConnectionPointer,
81
+ IdentifiedConnection,
82
+ generateIdentifiedConnectionId,
77
83
  } from '@finos/legend-graph';
78
84
  import type { DSL_Mapping_LegendStudioApplicationPlugin_Extension } from '../extensions/DSL_Mapping_LegendStudioApplicationPlugin_Extension.js';
79
85
  import {
@@ -646,7 +652,6 @@ export class NewElementState {
646
652
  _package: observable,
647
653
  name: observable,
648
654
  newElementDriver: observable,
649
- elementAndPackageName: computed,
650
655
  selectedPackage: computed,
651
656
  isValid: computed,
652
657
  setShowModal: action,
@@ -665,14 +670,6 @@ export class NewElementState {
665
670
  this.type = PACKAGEABLE_ELEMENT_TYPE.PACKAGE;
666
671
  }
667
672
 
668
- get elementAndPackageName(): [string, string] {
669
- return resolvePackageAndElementName(
670
- this.selectedPackage,
671
- this._package === this.editorStore.graphManagerState.graph.root,
672
- this.name,
673
- );
674
- }
675
-
676
673
  get selectedPackage(): Package {
677
674
  return this._package
678
675
  ? this._package
@@ -778,7 +775,11 @@ export class NewElementState {
778
775
 
779
776
  *save(): GeneratorFn<void> {
780
777
  if (this.name && this.isValid) {
781
- const [packagePath, elementName] = this.elementAndPackageName;
778
+ const [packagePath, elementName] = resolvePackageAndElementName(
779
+ this.selectedPackage,
780
+ this._package === this.editorStore.graphManagerState.graph.root,
781
+ this.name,
782
+ );
782
783
  if (
783
784
  this.editorStore.graphManagerState.graph.getNullablePackage(
784
785
  packagePath,
@@ -789,17 +790,105 @@ export class NewElementState {
789
790
  `Can't create elements for type other than 'package' in root package`,
790
791
  );
791
792
  } else {
792
- const element = this.createElement(elementName);
793
- yield flowResult(
794
- this.editorStore.graphEditorMode.addElement(
795
- element,
796
- packagePath,
797
- true,
798
- ),
799
- );
793
+ if (
794
+ this.editorStore.applicationStore.config.options
795
+ .TEMPORARY__enableLocalConnectionBuilder &&
796
+ this.type === PACKAGEABLE_ELEMENT_TYPE.TEMPORARY__LOCAL_CONNECTION
797
+ ) {
798
+ // NOTE: this is temporary until we have proper support for local connection
799
+ // For now, we aim to fulfill the PoC for SnowflakeApp use case and will generate
800
+ // everything: mapping, store, connection, runtime, etc.
801
+ const store = new Database(`${this.name}_Database`);
802
+ const mapping = new Mapping(`${this.name}_Mapping`);
803
+ // connection
804
+ const connection = new PackageableConnection(
805
+ `${this.name}_LocalConnection`,
806
+ );
807
+ const _suffix = `${packagePath.replaceAll(
808
+ ELEMENT_PATH_DELIMITER,
809
+ '-',
810
+ )}-${connection.name}`;
811
+ const datasourceSpecification = new SnowflakeDatasourceSpecification(
812
+ `legend-local-snowflake-accountName-${_suffix}`,
813
+ `legend-local-snowflake-region-${_suffix}`,
814
+ `legend-local-snowflake-warehouseName-${_suffix}`,
815
+ `legend-local-snowflake-databaseName-${_suffix}`,
816
+ );
817
+ datasourceSpecification.cloudType = `legend-local-snowflake-cloudType-${_suffix}`;
818
+ datasourceSpecification.role = `legend-local-snowflake-role-${_suffix}`;
819
+ const connectionValue = new RelationalDatabaseConnection(
820
+ PackageableElementExplicitReference.create(store),
821
+ DatabaseType.Snowflake,
822
+ datasourceSpecification,
823
+ new SnowflakePublicAuthenticationStrategy(
824
+ `legend-local-snowflake-privateKeyVaultReference-${_suffix}`,
825
+ `legend-local-snowflake-passphraseVaultReference-${_suffix}`,
826
+ `legend-local-snowflake-publicuserName-${_suffix}`,
827
+ ),
828
+ );
829
+ connectionValue.localMode = true;
830
+ connection.connectionValue = connectionValue;
831
+ // runtime
832
+ const runtime = new PackageableRuntime(`${this.name}_Runtime`);
833
+ const engineRuntime = new EngineRuntime();
834
+ engineRuntime.mappings = [
835
+ PackageableElementExplicitReference.create(mapping),
836
+ ];
837
+ const storeConnections = new StoreConnections(
838
+ PackageableElementExplicitReference.create(store),
839
+ );
840
+ storeConnections.storeConnections = [
841
+ new IdentifiedConnection(
842
+ generateIdentifiedConnectionId(engineRuntime),
843
+ new ConnectionPointer(
844
+ PackageableElementExplicitReference.create(connection),
845
+ ),
846
+ ),
847
+ ];
848
+ engineRuntime.connections = [storeConnections];
849
+ runtime.runtimeValue = engineRuntime;
850
+ // add the elements
851
+ yield flowResult(
852
+ this.editorStore.graphEditorMode.addElement(
853
+ store,
854
+ packagePath,
855
+ false,
856
+ ),
857
+ );
858
+ yield flowResult(
859
+ this.editorStore.graphEditorMode.addElement(
860
+ connection,
861
+ packagePath,
862
+ false,
863
+ ),
864
+ );
865
+ yield flowResult(
866
+ this.editorStore.graphEditorMode.addElement(
867
+ mapping,
868
+ packagePath,
869
+ false,
870
+ ),
871
+ );
872
+ yield flowResult(
873
+ this.editorStore.graphEditorMode.addElement(
874
+ runtime,
875
+ packagePath,
876
+ false,
877
+ ),
878
+ );
879
+ } else {
880
+ const element = this.createElement(elementName);
881
+ yield flowResult(
882
+ this.editorStore.graphEditorMode.addElement(
883
+ element,
884
+ packagePath,
885
+ true,
886
+ ),
887
+ );
800
888
 
801
- // post creation handling
802
- yield handlePostCreateAction(element, this.editorStore);
889
+ // post creation handling
890
+ yield handlePostCreateAction(element, this.editorStore);
891
+ }
803
892
  }
804
893
  }
805
894
  this.closeModal();
@@ -146,8 +146,8 @@ export class DatabaseBuilderState {
146
146
  makeObservable<DatabaseBuilderState>(this, {
147
147
  showModal: observable,
148
148
  targetDatabasePath: observable,
149
- isBuildingDatabase: observable,
150
149
  databaseGrammarCode: observable,
150
+ isBuildingDatabase: observable,
151
151
  isSavingDatabase: observable,
152
152
  currentDatabase: computed,
153
153
  setTargetDatabasePath: action,
@@ -219,7 +219,7 @@ export class DatabaseBuilderState {
219
219
  treeData: DatabaseBuilderTreeData,
220
220
  ): DatabaseBuilderTreeNodeData[] | undefined {
221
221
  return node.childrenIds
222
- ?.map((n) => treeData.nodes.get(n))
222
+ ?.map((childNode) => treeData.nodes.get(childNode))
223
223
  .filter(isNonNullable);
224
224
  }
225
225
 
@@ -245,7 +245,8 @@ export class DatabaseBuilderState {
245
245
  }
246
246
  }
247
247
  }
248
- // TODO: handle ColumnDatabaseBuilderTreeNodeData
248
+
249
+ // TODO: support toggling check for columns
249
250
  this.setTreeData({ ...treeData });
250
251
  }
251
252
 
@@ -279,6 +280,7 @@ export class DatabaseBuilderState {
279
280
  schemaId,
280
281
  schema,
281
282
  );
283
+ nodes.set(schemaId, schemaNode);
282
284
 
283
285
  schemaNode.setChecked(
284
286
  Boolean(
@@ -287,7 +289,6 @@ export class DatabaseBuilderState {
287
289
  ),
288
290
  ),
289
291
  );
290
- nodes.set(schemaId, schemaNode);
291
292
  });
292
293
  const treeData = { rootIds, nodes, database };
293
294
  this.setTreeData(treeData);
@@ -341,6 +342,8 @@ export class DatabaseBuilderState {
341
342
  schema,
342
343
  table,
343
344
  );
345
+ treeData.nodes.set(tableId, tableNode);
346
+ addUniqueEntry(childrenIds, tableId);
344
347
 
345
348
  if (this.currentDatabase) {
346
349
  const matchingSchema = getNullableSchema(
@@ -357,9 +360,6 @@ export class DatabaseBuilderState {
357
360
  } else {
358
361
  tableNode.setChecked(false);
359
362
  }
360
-
361
- treeData.nodes.set(tableId, tableNode);
362
- addUniqueEntry(childrenIds, tableId);
363
363
  });
364
364
  schemaNode.childrenIds = childrenIds;
365
365
  this.setTreeData({ ...treeData });
@@ -605,6 +605,8 @@ export class DatabaseBuilderState {
605
605
  } else {
606
606
  currentDatabase = this.currentDatabase;
607
607
  }
608
+
609
+ // remove undefined schemas
608
610
  const schemas = Array.from(this.treeData.nodes.values())
609
611
  .map((schemaNode) => {
610
612
  if (schemaNode instanceof SchemaDatabaseBuilderTreeNodeData) {
@@ -613,11 +615,32 @@ export class DatabaseBuilderState {
613
615
  return undefined;
614
616
  })
615
617
  .filter(isNonNullable);
616
- this.updateDatabase(currentDatabase, database, schemas);
618
+ currentDatabase.schemas = currentDatabase.schemas.filter((schema) => {
619
+ if (
620
+ schemas.find((item) => item.name === schema.name) &&
621
+ !database.schemas.find((s) => s.name === schema.name)
622
+ ) {
623
+ return false;
624
+ }
625
+ return true;
626
+ });
627
+
628
+ // update existing schemas
629
+ database.schemas.forEach((schema) => {
630
+ (schema as Writable<Schema>)._OWNER = currentDatabase;
631
+ const currentSchemaIndex = currentDatabase.schemas.findIndex(
632
+ (item) => item.name === schema.name,
633
+ );
634
+ if (currentSchemaIndex !== -1) {
635
+ currentDatabase.schemas[currentSchemaIndex] = schema;
636
+ } else {
637
+ currentDatabase.schemas.push(schema);
638
+ }
639
+ });
640
+
617
641
  this.editorStore.applicationStore.notificationService.notifySuccess(
618
- `Database successfully '${isUpdating ? 'updated' : 'created'}.`,
642
+ `Database successfully '${isUpdating ? 'updated' : 'created'}`,
619
643
  );
620
- this.fetchDatabaseMetadata();
621
644
  if (isUpdating) {
622
645
  yield flowResult(
623
646
  this.editorStore
@@ -639,34 +662,4 @@ export class DatabaseBuilderState {
639
662
  this.isSavingDatabase = false;
640
663
  }
641
664
  }
642
-
643
- updateDatabase(
644
- current: Database,
645
- generatedDatabase: Database,
646
- allSchemas: Schema[],
647
- ): void {
648
- // remove undefined schemas
649
- current.schemas = current.schemas.filter((schema) => {
650
- if (
651
- allSchemas.find((item) => item.name === schema.name) &&
652
- !generatedDatabase.schemas.find((c) => c.name === schema.name)
653
- ) {
654
- return false;
655
- }
656
- return true;
657
- });
658
-
659
- // update existing schemas
660
- generatedDatabase.schemas.forEach((schema) => {
661
- (schema as Writable<Schema>)._OWNER = current;
662
- const currentSchemaIndex = current.schemas.findIndex(
663
- (item) => item.name === schema.name,
664
- );
665
- if (currentSchemaIndex !== -1) {
666
- current.schemas[currentSchemaIndex] = schema;
667
- } else {
668
- current.schemas.push(schema);
669
- }
670
- });
671
- }
672
665
  }
@@ -121,7 +121,6 @@ import {
121
121
  setImpl_updateRootOnCreate,
122
122
  setImpl_updateRootOnDelete,
123
123
  } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
124
- import { BASIC_SET_IMPLEMENTATION_TYPE } from '../../../utils/ModelClassifierUtils.js';
125
124
  import { rootRelationalSetImp_setMainTableAlias } from '../../../../graph-modifier/STO_Relational_GraphModifierHelper.js';
126
125
  import { LambdaEditorState } from '@finos/legend-query-builder';
127
126
  import type { MappingEditorTabState } from './MappingTabManagerState.js';
@@ -163,6 +162,11 @@ export enum MAPPING_ELEMENT_TYPE {
163
162
  ASSOCIATION = 'ASSOCIATION',
164
163
  }
165
164
 
165
+ export enum BASIC_SET_IMPLEMENTATION_TYPE {
166
+ OPERATION = 'operation',
167
+ INSTANCE = 'instance',
168
+ }
169
+
166
170
  export type MappingElement =
167
171
  | EnumerationMapping
168
172
  | SetImplementation
@@ -25,6 +25,9 @@ import {
25
25
  ActionState,
26
26
  getNonNullableEntry,
27
27
  getNullableLastEntry,
28
+ type Writable,
29
+ guaranteeNonNullable,
30
+ IllegalStateError,
28
31
  } from '@finos/legend-shared';
29
32
  import { observable, makeObservable, flow, flowResult, action } from 'mobx';
30
33
  import { editor as monacoEditorAPI } from 'monaco-editor';
@@ -40,6 +43,8 @@ import {
40
43
  getSchema,
41
44
  guaranteeRelationalDatabaseConnection,
42
45
  GRAPH_MANAGER_EVENT,
46
+ getNullableSchema,
47
+ getNullableTable,
43
48
  } from '@finos/legend-graph';
44
49
  import type { EditorStore } from '../EditorStore.js';
45
50
  import { LEGEND_STUDIO_APP_EVENT } from '../../../__lib__/LegendStudioEvent.js';
@@ -50,6 +55,8 @@ import {
50
55
  import type { CommandRegistrar } from '@finos/legend-application';
51
56
  import { STO_RELATIONAL_LEGEND_STUDIO_COMMAND_KEY } from '../../../__lib__/STO_Relational_LegendStudioCommand.js';
52
57
  import { PANEL_MODE } from '../EditorConfig.js';
58
+ import type { Entity } from '@finos/legend-storage';
59
+ import { GraphEditFormModeState } from '../GraphEditFormModeState.js';
53
60
 
54
61
  export abstract class DatabaseSchemaExplorerTreeNodeData
55
62
  implements TreeNodeData
@@ -59,12 +66,22 @@ export abstract class DatabaseSchemaExplorerTreeNodeData
59
66
  label: string;
60
67
  parentId?: string | undefined;
61
68
  childrenIds?: string[] | undefined;
69
+ isChecked = false;
62
70
 
63
71
  constructor(id: string, label: string, parentId: string | undefined) {
72
+ makeObservable(this, {
73
+ isChecked: observable,
74
+ setChecked: action,
75
+ });
76
+
64
77
  this.id = id;
65
78
  this.label = label;
66
79
  this.parentId = parentId;
67
80
  }
81
+
82
+ setChecked(val: boolean): void {
83
+ this.isChecked = val;
84
+ }
68
85
  }
69
86
 
70
87
  export class DatabaseSchemaExplorerTreeSchemaNodeData extends DatabaseSchemaExplorerTreeNodeData {
@@ -118,6 +135,7 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
118
135
  isExecutingRawSQL = false;
119
136
 
120
137
  connection?: PackageableConnection | undefined;
138
+ database?: Database | undefined;
121
139
  treeData?: DatabaseSchemaExplorerTreeData | undefined;
122
140
  readonly sqlEditorTextModel: monacoEditorAPI.ITextModel;
123
141
  sqlEditor?: monacoEditorAPI.IStandaloneCodeEditor | undefined;
@@ -125,17 +143,23 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
125
143
  sqlText = DEFAULT_SQL_TEXT;
126
144
  sqlExecutionResult?: string | undefined;
127
145
 
146
+ isBuildingDatabase = false;
147
+ isUpdatingDatabase = false;
148
+
128
149
  constructor(editorStore: EditorStore) {
129
150
  makeObservable(this, {
130
151
  isFetchingSchema: observable,
131
152
  isExecutingRawSQL: observable,
132
153
  connection: observable,
154
+ database: observable,
133
155
  treeData: observable,
134
156
  sqlText: observable,
135
157
  resetSQL: action,
136
158
  sqlExecutionResult: observable,
137
159
  sqlEditor: observable.ref,
138
160
  sqlEditorViewState: observable.ref,
161
+ isBuildingDatabase: observable,
162
+ isUpdatingDatabase: observable,
139
163
  setTreeData: action,
140
164
  setConnection: action,
141
165
  setSQLEditor: action,
@@ -146,6 +170,8 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
146
170
  fetchSchemaMetadata: flow,
147
171
  fetchTableMetadata: flow,
148
172
  executeRawSQL: flow,
173
+ generateDatabase: flow,
174
+ updateDatabase: flow,
149
175
  });
150
176
 
151
177
  this.editorStore = editorStore;
@@ -161,6 +187,9 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
161
187
 
162
188
  setConnection(val: PackageableConnection | undefined): void {
163
189
  this.connection = val;
190
+ if (val) {
191
+ this.database = guaranteeRelationalDatabaseConnection(val).store.value;
192
+ }
164
193
  this.sqlEditorTextModel.setValue(DEFAULT_SQL_TEXT);
165
194
  }
166
195
 
@@ -238,10 +267,37 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
238
267
  treeData: DatabaseSchemaExplorerTreeData,
239
268
  ): DatabaseSchemaExplorerTreeNodeData[] | undefined {
240
269
  return node.childrenIds
241
- ?.map((n) => treeData.nodes.get(n))
270
+ ?.map((childNode) => treeData.nodes.get(childNode))
242
271
  .filter(isNonNullable);
243
272
  }
244
273
 
274
+ toggleCheckedNode(
275
+ node: DatabaseSchemaExplorerTreeNodeData,
276
+ treeData: DatabaseSchemaExplorerTreeData,
277
+ ): void {
278
+ node.setChecked(!node.isChecked);
279
+ if (node instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
280
+ this.getChildNodes(node, treeData)?.forEach((childNode) => {
281
+ childNode.setChecked(node.isChecked);
282
+ });
283
+ } else if (node instanceof DatabaseSchemaExplorerTreeTableNodeData) {
284
+ if (node.parentId) {
285
+ const parent = treeData.nodes.get(node.parentId);
286
+ if (
287
+ parent &&
288
+ this.getChildNodes(parent, treeData)?.every(
289
+ (e) => e.isChecked === node.isChecked,
290
+ )
291
+ ) {
292
+ parent.setChecked(node.isChecked);
293
+ }
294
+ }
295
+ }
296
+
297
+ // TODO: support toggling check for columns
298
+ this.setTreeData({ ...treeData });
299
+ }
300
+
245
301
  *fetchDatabaseMetadata(): GeneratorFn<void> {
246
302
  if (!this.connection) {
247
303
  return;
@@ -279,6 +335,14 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
279
335
  dbSchema,
280
336
  );
281
337
  nodes.set(schemaId, schemaNode);
338
+
339
+ schemaNode.setChecked(
340
+ Boolean(
341
+ this.database?.schemas.find(
342
+ (schema) => schema.name === dbSchema.name,
343
+ ),
344
+ ),
345
+ );
282
346
  });
283
347
  const treeData = { rootIds, nodes, database };
284
348
  this.setTreeData(treeData);
@@ -339,6 +403,22 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
339
403
  );
340
404
  treeData.nodes.set(tableId, tableNode);
341
405
  addUniqueEntry(childrenIds, tableId);
406
+
407
+ if (this.database) {
408
+ const matchingSchema = getNullableSchema(
409
+ this.database,
410
+ schema.name,
411
+ );
412
+ tableNode.setChecked(
413
+ Boolean(
414
+ matchingSchema
415
+ ? getNullableTable(matchingSchema, table.name)
416
+ : undefined,
417
+ ),
418
+ );
419
+ } else {
420
+ tableNode.setChecked(false);
421
+ }
342
422
  });
343
423
  schemaNode.childrenIds = childrenIds;
344
424
  this.setTreeData({ ...treeData });
@@ -482,4 +562,147 @@ export class SQLPlaygroundPanelState implements CommandRegistrar {
482
562
  this.isExecutingRawSQL = false;
483
563
  }
484
564
  }
565
+
566
+ *generateDatabase(): GeneratorFn<Entity> {
567
+ if (!this.database || !this.connection || !this.treeData) {
568
+ throw new IllegalStateError(
569
+ `Can't build database: builder is not properly set up`,
570
+ );
571
+ }
572
+
573
+ try {
574
+ this.isBuildingDatabase = true;
575
+
576
+ const treeData = this.treeData;
577
+ const databaseBuilderInput = new DatabaseBuilderInput(
578
+ guaranteeRelationalDatabaseConnection(this.connection),
579
+ );
580
+ const packagePath = guaranteeNonNullable(this.database.package).path;
581
+ const databaseName = this.database.name;
582
+ databaseBuilderInput.targetDatabase = new TargetDatabase(
583
+ packagePath,
584
+ databaseName,
585
+ );
586
+ const config = databaseBuilderInput.config;
587
+ config.maxTables = undefined;
588
+ config.enrichTables = true;
589
+ config.enrichColumns = true;
590
+ config.enrichPrimaryKeys = true;
591
+ treeData.rootIds
592
+ .map((e) => treeData.nodes.get(e))
593
+ .filter(isNonNullable)
594
+ .forEach((schemaNode) => {
595
+ if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
596
+ const tableNodes = this.getChildNodes(schemaNode, treeData);
597
+ const allChecked = tableNodes?.every((t) => t.isChecked === true);
598
+ if (
599
+ allChecked ||
600
+ (schemaNode.isChecked && !schemaNode.childrenIds)
601
+ ) {
602
+ config.patterns.push(
603
+ new DatabasePattern(schemaNode.schema.name, undefined),
604
+ );
605
+ } else {
606
+ tableNodes?.forEach((t) => {
607
+ if (
608
+ t instanceof DatabaseSchemaExplorerTreeTableNodeData &&
609
+ t.isChecked
610
+ ) {
611
+ config.patterns.push(
612
+ new DatabasePattern(schemaNode.schema.name, t.table.name),
613
+ );
614
+ }
615
+ });
616
+ }
617
+ }
618
+ });
619
+ const entities =
620
+ (yield this.editorStore.graphManagerState.graphManager.buildDatabase(
621
+ databaseBuilderInput,
622
+ )) as Entity[];
623
+ return getNonNullableEntry(
624
+ entities,
625
+ 0,
626
+ 'Expected a database to be generated',
627
+ );
628
+ } finally {
629
+ this.isBuildingDatabase = false;
630
+ }
631
+ }
632
+
633
+ *updateDatabase(): GeneratorFn<void> {
634
+ if (!this.treeData || !this.database || !this.connection) {
635
+ return;
636
+ }
637
+
638
+ try {
639
+ this.isUpdatingDatabase = true;
640
+
641
+ const graph = this.editorStore.graphManagerState.createNewGraph();
642
+ (yield this.editorStore.graphManagerState.graphManager.buildGraph(
643
+ graph,
644
+ [(yield flowResult(this.generateDatabase())) as Entity],
645
+ ActionState.create(),
646
+ )) as Entity[];
647
+ const generatedDatabase = getNonNullableEntry(
648
+ graph.ownDatabases,
649
+ 0,
650
+ 'Expected one database to be generated from input',
651
+ );
652
+
653
+ const currentDatabase = this.database;
654
+
655
+ // remove undefined schemas
656
+ const schemas = Array.from(this.treeData.nodes.values())
657
+ .map((schemaNode) => {
658
+ if (schemaNode instanceof DatabaseSchemaExplorerTreeSchemaNodeData) {
659
+ return schemaNode.schema;
660
+ }
661
+ return undefined;
662
+ })
663
+ .filter(isNonNullable);
664
+ currentDatabase.schemas = currentDatabase.schemas.filter((schema) => {
665
+ if (
666
+ schemas.find((item) => item.name === schema.name) &&
667
+ !generatedDatabase.schemas.find((s) => s.name === schema.name)
668
+ ) {
669
+ return false;
670
+ }
671
+ return true;
672
+ });
673
+
674
+ // update existing schemas
675
+ generatedDatabase.schemas.forEach((schema) => {
676
+ (schema as Writable<Schema>)._OWNER = currentDatabase;
677
+ const currentSchemaIndex = currentDatabase.schemas.findIndex(
678
+ (item) => item.name === schema.name,
679
+ );
680
+ if (currentSchemaIndex !== -1) {
681
+ currentDatabase.schemas[currentSchemaIndex] = schema;
682
+ } else {
683
+ currentDatabase.schemas.push(schema);
684
+ }
685
+ });
686
+
687
+ this.editorStore.applicationStore.notificationService.notifySuccess(
688
+ `Database successfully updated`,
689
+ );
690
+ yield flowResult(
691
+ this.editorStore
692
+ .getGraphEditorMode(GraphEditFormModeState)
693
+ .globalCompile({
694
+ message: `Can't compile graph after editing database. Redirecting you to text mode`,
695
+ }),
696
+ );
697
+ } catch (error) {
698
+ assertErrorThrown(error);
699
+ this.editorStore.applicationStore.logService.error(
700
+ LogEvent.create(LEGEND_STUDIO_APP_EVENT.DATABASE_BUILDER_FAILURE),
701
+ error,
702
+ );
703
+ this.editorStore.applicationStore.notificationService.notifyError(error);
704
+ } finally {
705
+ this.isUpdatingDatabase = false;
706
+ }
707
+ }
485
708
  }