@finos/legend-application-studio 28.1.1 → 28.1.3

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 (47) hide show
  1. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts +1 -0
  2. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioApplicationNavigationContext.js +1 -0
  4. package/lib/__lib__/LegendStudioApplicationNavigationContext.js.map +1 -1
  5. package/lib/__lib__/LegendStudioEvent.d.ts +1 -0
  6. package/lib/__lib__/LegendStudioEvent.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioEvent.js +1 -0
  8. package/lib/__lib__/LegendStudioEvent.js.map +1 -1
  9. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilderWizard.d.ts.map +1 -1
  10. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilderWizard.js +16 -2
  11. package/lib/components/editor/editor-group/connection-editor/DatabaseBuilderWizard.js.map +1 -1
  12. package/lib/components/editor/editor-group/connection-editor/DatabaseModelBuilder.d.ts +23 -0
  13. package/lib/components/editor/editor-group/connection-editor/DatabaseModelBuilder.d.ts.map +1 -0
  14. package/lib/components/editor/editor-group/connection-editor/DatabaseModelBuilder.js +45 -0
  15. package/lib/components/editor/editor-group/connection-editor/DatabaseModelBuilder.js.map +1 -0
  16. package/lib/components/editor/side-bar/Explorer.d.ts.map +1 -1
  17. package/lib/components/editor/side-bar/Explorer.js +10 -10
  18. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  19. package/lib/index.css +1 -1
  20. package/lib/package.json +1 -1
  21. package/lib/stores/editor/ExplorerTreeState.d.ts +6 -3
  22. package/lib/stores/editor/ExplorerTreeState.d.ts.map +1 -1
  23. package/lib/stores/editor/ExplorerTreeState.js +13 -31
  24. package/lib/stores/editor/ExplorerTreeState.js.map +1 -1
  25. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts +4 -0
  26. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.d.ts.map +1 -1
  27. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js +42 -7
  28. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.js.map +1 -1
  29. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderWizardState.d.ts +1 -1
  30. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderWizardState.d.ts.map +1 -1
  31. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderWizardState.js +0 -1
  32. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderWizardState.js.map +1 -1
  33. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.d.ts +37 -0
  34. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.d.ts.map +1 -0
  35. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.js +111 -0
  36. package/lib/stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.js.map +1 -0
  37. package/package.json +2 -2
  38. package/src/__lib__/LegendStudioApplicationNavigationContext.ts +1 -0
  39. package/src/__lib__/LegendStudioEvent.ts +1 -0
  40. package/src/components/editor/editor-group/connection-editor/DatabaseBuilderWizard.tsx +29 -5
  41. package/src/components/editor/editor-group/connection-editor/DatabaseModelBuilder.tsx +158 -0
  42. package/src/components/editor/side-bar/Explorer.tsx +24 -20
  43. package/src/stores/editor/ExplorerTreeState.ts +19 -50
  44. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderState.ts +80 -12
  45. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseBuilderWizardState.ts +1 -2
  46. package/src/stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.ts +156 -0
  47. package/tsconfig.json +2 -0
@@ -33,6 +33,7 @@ import {
33
33
  BlankPanelContent,
34
34
  PanelHeader,
35
35
  Panel,
36
+ InputWithInlineValidation,
36
37
  } from '@finos/legend-art';
37
38
  import { useEffect } from 'react';
38
39
  import { noop } from '@finos/legend-shared';
@@ -56,10 +57,24 @@ export const DatabaseBuilderWizard = observer(
56
57
  }) => {
57
58
  const { databaseBuilderState, isReadOnly } = props;
58
59
  const schemaExplorerState = databaseBuilderState.schemaExplorerState;
60
+ const isCreatingNewDatabase = schemaExplorerState.isCreatingNewDatabase;
61
+ const elementAlreadyExistsMessage =
62
+ isCreatingNewDatabase &&
63
+ databaseBuilderState.editorStore.graphManagerState.graph.allElements
64
+ .map((s) => s.path)
65
+ .includes(schemaExplorerState.targetDatabasePath)
66
+ ? 'Element with same path already exists'
67
+ : undefined;
68
+
59
69
  const applicationStore = useApplicationStore();
60
70
  const preview = applicationStore.guardUnhandledError(() =>
61
71
  flowResult(databaseBuilderState.previewDatabaseModel()),
62
72
  );
73
+ const onTargetPathChange: React.ChangeEventHandler<HTMLInputElement> = (
74
+ event,
75
+ ) => {
76
+ schemaExplorerState.setTargetDatabasePath(event.target.value);
77
+ };
63
78
  const updateDatabase = applicationStore.guardUnhandledError(() =>
64
79
  flowResult(databaseBuilderState.updateDatabase()),
65
80
  );
@@ -131,18 +146,23 @@ export const DatabaseBuilderWizard = observer(
131
146
  <ResizablePanel>
132
147
  <Panel className="database-builder__model">
133
148
  <PanelHeader title="database model" />
134
-
135
149
  <PanelContent>
136
150
  <div className="database-builder__modeller">
137
151
  <div className="panel__content__form__section database-builder__modeller__path">
138
152
  <div className="panel__content__form__section__header__label">
139
153
  Target Database Path
140
154
  </div>
141
- <input
155
+ <InputWithInlineValidation
142
156
  className="panel__content__form__section__input"
143
157
  spellCheck={false}
144
- disabled={true}
145
- value={schemaExplorerState.database.path}
158
+ onChange={onTargetPathChange}
159
+ disabled={!isCreatingNewDatabase}
160
+ value={
161
+ isCreatingNewDatabase
162
+ ? schemaExplorerState.targetDatabasePath
163
+ : schemaExplorerState.database.path
164
+ }
165
+ error={elementAlreadyExistsMessage}
146
166
  />
147
167
  </div>
148
168
  <div className="database-builder__modeller__preview">
@@ -178,7 +198,11 @@ export const DatabaseBuilderWizard = observer(
178
198
  </ModalFooterButton>
179
199
  <ModalFooterButton
180
200
  className="database-builder__action--btn"
181
- disabled={isReadOnly || isExecutingAction}
201
+ disabled={
202
+ isReadOnly ||
203
+ isExecutingAction ||
204
+ Boolean(elementAlreadyExistsMessage)
205
+ }
182
206
  onClick={updateDatabase}
183
207
  >
184
208
  Update Database
@@ -0,0 +1,158 @@
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 { observer } from 'mobx-react-lite';
18
+ import type { DatabaseModelBuilderState } from '../../../../stores/editor/editor-state/element-editor-state/connection/DatabaseModelBuilderState.js';
19
+ import {
20
+ BlankPanelContent,
21
+ Dialog,
22
+ Modal,
23
+ ModalBody,
24
+ ModalFooter,
25
+ ModalFooterButton,
26
+ ModalHeader,
27
+ ModalHeaderActions,
28
+ ModalTitle,
29
+ Panel,
30
+ PanelContent,
31
+ PanelHeader,
32
+ PanelLoadingIndicator,
33
+ ResizablePanel,
34
+ ResizablePanelGroup,
35
+ TimesIcon,
36
+ } from '@finos/legend-art';
37
+ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
38
+ import {
39
+ useApplicationStore,
40
+ useConditionedApplicationNavigationContext,
41
+ } from '@finos/legend-application';
42
+ import { flowResult } from 'mobx';
43
+ import { useEffect } from 'react';
44
+ import {
45
+ CODE_EDITOR_LANGUAGE,
46
+ CodeEditor,
47
+ } from '@finos/legend-lego/code-editor';
48
+ import { noop } from '@finos/legend-shared';
49
+
50
+ export const DatabaseModelBuilder = observer(
51
+ (props: {
52
+ databaseModelBuilderState: DatabaseModelBuilderState;
53
+ isReadOnly: boolean;
54
+ }) => {
55
+ const { databaseModelBuilderState, isReadOnly } = props;
56
+
57
+ const applicationStore = useApplicationStore();
58
+ const preview = applicationStore.guardUnhandledError(() =>
59
+ flowResult(databaseModelBuilderState.previewDatabaseModels()),
60
+ );
61
+ const saveModels = applicationStore.guardUnhandledError(() =>
62
+ flowResult(databaseModelBuilderState.saveModels()),
63
+ );
64
+ const closeModal = (): void => {
65
+ databaseModelBuilderState.close();
66
+ };
67
+
68
+ const isExecutingAction =
69
+ databaseModelBuilderState.generatingModelState.isInProgress ||
70
+ databaseModelBuilderState.saveModelState.isInProgress;
71
+
72
+ useEffect(() => {
73
+ flowResult(databaseModelBuilderState.previewDatabaseModels()).catch(
74
+ applicationStore.alertUnhandledError,
75
+ );
76
+ }, [applicationStore, databaseModelBuilderState]);
77
+
78
+ useConditionedApplicationNavigationContext(
79
+ LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.DATABASE_MODEL_BUILDER,
80
+ databaseModelBuilderState.showModal,
81
+ );
82
+
83
+ return (
84
+ <Dialog
85
+ open={databaseModelBuilderState.showModal}
86
+ classes={{ container: 'search-modal__container' }}
87
+ onClose={noop}
88
+ PaperProps={{
89
+ classes: {
90
+ root: 'search-modal__inner-container database-builder__container',
91
+ },
92
+ }}
93
+ >
94
+ <Modal darkMode={true} className="database-builder">
95
+ <ModalHeader>
96
+ <ModalTitle title="Database Model Builder" />
97
+ <ModalHeaderActions>
98
+ <button
99
+ className="modal__header__action"
100
+ tabIndex={-1}
101
+ onClick={closeModal}
102
+ >
103
+ <TimesIcon />
104
+ </button>
105
+ </ModalHeaderActions>
106
+ </ModalHeader>
107
+ <ModalBody className="database-builder__content">
108
+ <PanelLoadingIndicator isLoading={isExecutingAction} />
109
+ <ResizablePanelGroup orientation="vertical">
110
+ <ResizablePanel>
111
+ <Panel className="database-builder__model">
112
+ <PanelHeader title="database model" />
113
+ <PanelContent>
114
+ <div className="database-builder__modeller">
115
+ <div className="database-builder__modeller__preview">
116
+ {databaseModelBuilderState.generatedGrammarCode && (
117
+ <CodeEditor
118
+ language={CODE_EDITOR_LANGUAGE.PURE}
119
+ inputValue={
120
+ databaseModelBuilderState.generatedGrammarCode
121
+ }
122
+ isReadOnly={true}
123
+ />
124
+ )}
125
+ {!databaseModelBuilderState.generatedGrammarCode && (
126
+ <BlankPanelContent>
127
+ No model preview
128
+ </BlankPanelContent>
129
+ )}
130
+ </div>
131
+ </div>
132
+ </PanelContent>
133
+ </Panel>
134
+ </ResizablePanel>
135
+ </ResizablePanelGroup>
136
+ </ModalBody>
137
+ <ModalFooter>
138
+ <ModalFooterButton
139
+ className="database-builder__action--btn"
140
+ disabled={isReadOnly || isExecutingAction}
141
+ onClick={preview}
142
+ title="Preview models..."
143
+ >
144
+ Preview
145
+ </ModalFooterButton>
146
+ <ModalFooterButton
147
+ className="database-builder__action--btn"
148
+ disabled={isReadOnly || isExecutingAction}
149
+ onClick={saveModels}
150
+ >
151
+ Save Models
152
+ </ModalFooterButton>
153
+ </ModalFooter>
154
+ </Modal>
155
+ </Dialog>
156
+ );
157
+ },
158
+ );
@@ -140,6 +140,7 @@ import {
140
140
  } from '@finos/legend-lego/code-editor';
141
141
  import { DatabaseBuilderWizard } from '../editor-group/connection-editor/DatabaseBuilderWizard.js';
142
142
  import { FunctionEditorState } from '../../../stores/editor/editor-state/element-editor-state/FunctionEditorState.js';
143
+ import { DatabaseModelBuilder } from '../editor-group/connection-editor/DatabaseModelBuilder.js';
143
144
 
144
145
  const ElementRenamer = observer(() => {
145
146
  const editorStore = useEditorStore();
@@ -457,8 +458,9 @@ const isRelationalDatabaseConnection = (
457
458
  val instanceof PackageableConnection &&
458
459
  val.connectionValue instanceof RelationalDatabaseConnection;
459
460
 
460
- const isRelationalDatabase = (val: PackageableElement | undefined): boolean =>
461
- val instanceof Database;
461
+ const isRelationalDatabase = (
462
+ val: PackageableElement | undefined,
463
+ ): Database | undefined => (val instanceof Database ? val : undefined);
462
464
 
463
465
  const ExplorerContextMenu = observer(
464
466
  forwardRef<
@@ -533,27 +535,22 @@ const ExplorerContextMenu = observer(
533
535
  );
534
536
  const generateModelsFromDatabaseSpecification =
535
537
  editorStore.applicationStore.guardUnhandledError(async () => {
536
- if (isRelationalDatabase(node?.packageableElement)) {
537
- const databasePath = guaranteeNonEmptyString(
538
- node?.packageableElement.path,
539
- );
540
- const graph = editorStore.graphManagerState.graph;
541
- if (graph.getDatabase(databasePath).joins.length === 0) {
538
+ const database = isRelationalDatabase(node?.packageableElement);
539
+ if (database) {
540
+ if (database.joins.length === 0) {
542
541
  applicationStore.alertService.setActionAlertInfo({
543
542
  message:
544
- 'You are attempting to generate models but have defined no joins. Are you sure you wish to proceed?',
543
+ 'You are attempting to build models but have defined no joins. Are you sure you wish to proceed?',
545
544
  type: ActionAlertType.CAUTION,
546
545
  actions: [
547
546
  {
548
547
  label: 'Proceed',
549
548
  type: ActionAlertActionType.PROCEED_WITH_CAUTION,
550
549
  handler: () => {
551
- flowResult(
552
- editorStore.explorerTreeState.generateModelsFromDatabaseSpecification(
553
- databasePath,
554
- graph,
555
- ),
556
- ).catch(applicationStore.alertUnhandledError);
550
+ editorStore.explorerTreeState.buildDatabaseModels(
551
+ database,
552
+ editorStore.isInViewerMode,
553
+ );
557
554
  },
558
555
  },
559
556
  {
@@ -564,9 +561,9 @@ const ExplorerContextMenu = observer(
564
561
  ],
565
562
  });
566
563
  } else {
567
- editorStore.explorerTreeState.generateModelsFromDatabaseSpecification(
568
- databasePath,
569
- graph,
564
+ editorStore.explorerTreeState.buildDatabaseModels(
565
+ database,
566
+ editorStore.isInViewerMode,
570
567
  );
571
568
  }
572
569
  }
@@ -863,7 +860,7 @@ const ExplorerContextMenu = observer(
863
860
  {isRelationalDatabase(node.packageableElement) && (
864
861
  <>
865
862
  <MenuContentItem onClick={generateModelsFromDatabaseSpecification}>
866
- Generate Models
863
+ Build Models
867
864
  </MenuContentItem>
868
865
  <MenuContentDivider />
869
866
  </>
@@ -1157,7 +1154,6 @@ const ExplorerTrees = observer(() => {
1157
1154
  getTreeChildNodes(editorStore, node, dependencyTreeData, true);
1158
1155
  const showPackageTrees =
1159
1156
  treeData.nodes.size || graph.dependencyManager.hasDependencies;
1160
-
1161
1157
  return (
1162
1158
  <ContextMenu
1163
1159
  className="explorer__content"
@@ -1191,6 +1187,14 @@ const ExplorerTrees = observer(() => {
1191
1187
  isReadOnly={false}
1192
1188
  />
1193
1189
  )}
1190
+ {editorStore.explorerTreeState.databaseModelBuilderState && (
1191
+ <DatabaseModelBuilder
1192
+ databaseModelBuilderState={
1193
+ editorStore.explorerTreeState.databaseModelBuilderState
1194
+ }
1195
+ isReadOnly={false}
1196
+ />
1197
+ )}
1194
1198
  {editorStore.projectConfigurationEditorState
1195
1199
  .projectConfiguration && <ProjectConfig />}
1196
1200
  {/* SYSTEM TREE */}
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { action, observable, makeObservable, flow, flowResult } from 'mobx';
17
+ import { action, observable, makeObservable } from 'mobx';
18
18
  import type { EditorStore } from './EditorStore.js';
19
19
  import {
20
20
  LogEvent,
@@ -23,8 +23,6 @@ import {
23
23
  UnsupportedOperationError,
24
24
  guaranteeNonNullable,
25
25
  ActionState,
26
- type GeneratorFn,
27
- assertErrorThrown,
28
26
  } from '@finos/legend-shared';
29
27
  import {
30
28
  getDependenciesPackableElementTreeData,
@@ -51,12 +49,11 @@ import {
51
49
  isDependencyElement,
52
50
  type Class,
53
51
  type RelationalDatabaseConnection,
54
- type PureModel,
52
+ type Database,
55
53
  } from '@finos/legend-graph';
56
54
  import { APPLICATION_EVENT } from '@finos/legend-application';
57
55
  import { DatabaseBuilderWizardState } from './editor-state/element-editor-state/connection/DatabaseBuilderWizardState.js';
58
- import type { Entity } from '@finos/legend-storage';
59
- import { EntityChangeType, type EntityChange } from '@finos/legend-server-sdlc';
56
+ import { DatabaseModelBuilderState } from './editor-state/element-editor-state/connection/DatabaseModelBuilderState.js';
60
57
 
61
58
  export enum ExplorerTreeRootPackageLabel {
62
59
  FILE_GENERATION = 'generated-files',
@@ -81,6 +78,7 @@ export class ExplorerTreeState {
81
78
  elementToRename?: PackageableElement | undefined;
82
79
  classToGenerateSampleData?: Class | undefined;
83
80
  databaseBuilderState: DatabaseBuilderWizardState | undefined;
81
+ databaseModelBuilderState: DatabaseModelBuilderState | undefined;
84
82
 
85
83
  constructor(editorStore: EditorStore) {
86
84
  makeObservable(this, {
@@ -94,6 +92,7 @@ export class ExplorerTreeState {
94
92
  elementToRename: observable,
95
93
  classToGenerateSampleData: observable,
96
94
  databaseBuilderState: observable,
95
+ databaseModelBuilderState: observable,
97
96
  setTreeData: action,
98
97
  setGenerationTreeData: action,
99
98
  setSystemTreeData: action,
@@ -106,13 +105,13 @@ export class ExplorerTreeState {
106
105
  build: action,
107
106
  buildImmutableModelTrees: action,
108
107
  buildTreeInTextMode: action,
108
+ buildDatabaseModels: action,
109
109
  openExplorerTreeNodes: action,
110
110
  reprocess: action,
111
111
  buildDatabase: action,
112
112
  setDatabaseBuilderState: action,
113
113
  onTreeNodeSelect: action,
114
114
  openNode: action,
115
- generateModelsFromDatabaseSpecification: flow,
116
115
  });
117
116
 
118
117
  this.editorStore = editorStore;
@@ -194,49 +193,19 @@ export class ExplorerTreeState {
194
193
  dbBuilderState.setShowModal(true);
195
194
  this.setDatabaseBuilderState(dbBuilderState);
196
195
  }
197
-
198
- *generateModelsFromDatabaseSpecification(
199
- databasePath: string,
200
- graph: PureModel,
201
- ): GeneratorFn<void> {
202
- try {
203
- const entities =
204
- (yield this.editorStore.graphManagerState.graphManager.generateModelsFromDatabaseSpecification(
205
- databasePath,
206
- graph,
207
- )) as Entity[];
208
- const newEntities: EntityChange[] = [];
209
- for (const entity of entities) {
210
- let entityChangeType: EntityChangeType;
211
- if (graph.getNullableElement(entity.path) === undefined) {
212
- entityChangeType = EntityChangeType.CREATE;
213
- } else {
214
- entityChangeType = EntityChangeType.MODIFY;
215
- }
216
- newEntities.push({
217
- type: entityChangeType,
218
- entityPath: entity.path,
219
- content: entity.content,
220
- });
221
- }
222
- yield flowResult(
223
- this.editorStore.graphState.loadEntityChangesToGraph(
224
- newEntities,
225
- undefined,
226
- ),
227
- );
228
- this.editorStore.applicationStore.notificationService.notifySuccess(
229
- 'Generated models successfully!',
230
- );
231
- } catch (error) {
232
- assertErrorThrown(error);
233
- this.editorStore.applicationStore.logService.error(
234
- LogEvent.create(LEGEND_STUDIO_APP_EVENT.GENERATION_FAILURE),
235
- error,
236
- );
237
- this.editorStore.applicationStore.notificationService.notifyError(error);
238
- throw error;
239
- }
196
+ setDatabaseModelBuilderState(
197
+ val: DatabaseModelBuilderState | undefined,
198
+ ): void {
199
+ this.databaseModelBuilderState = val;
200
+ }
201
+ buildDatabaseModels(val: Database, isReadOnly: boolean): void {
202
+ const dbBuilderState = new DatabaseModelBuilderState(
203
+ this.editorStore,
204
+ val,
205
+ isReadOnly,
206
+ );
207
+ dbBuilderState.setShowModal(true);
208
+ this.setDatabaseModelBuilderState(dbBuilderState);
240
209
  }
241
210
 
242
211
  setSelectedNode(node: PackageTreeNodeData | undefined): void {
@@ -28,8 +28,17 @@ import {
28
28
  ActionState,
29
29
  getNonNullableEntry,
30
30
  guaranteeType,
31
+ assertNonEmptyString,
32
+ assertTrue,
31
33
  } from '@finos/legend-shared';
32
- import { observable, action, makeObservable, flow, flowResult } from 'mobx';
34
+ import {
35
+ observable,
36
+ action,
37
+ makeObservable,
38
+ flow,
39
+ flowResult,
40
+ computed,
41
+ } from 'mobx';
33
42
  import { LEGEND_STUDIO_APP_EVENT } from '../../../../../__lib__/LegendStudioEvent.js';
34
43
  import type { EditorStore } from '../../../EditorStore.js';
35
44
  import {
@@ -45,8 +54,12 @@ import {
45
54
  getSchema,
46
55
  getNullableSchema,
47
56
  getNullableTable,
57
+ isStubbed_PackageableElement,
58
+ isValidFullPath,
59
+ PackageableElementExplicitReference,
48
60
  } from '@finos/legend-graph';
49
61
  import { GraphEditFormModeState } from '../../../GraphEditFormModeState.js';
62
+ import { connection_setStore } from '../../../../graph-modifier/DSL_Mapping_GraphModifierHelper.js';
50
63
 
51
64
  export abstract class DatabaseSchemaExplorerTreeNodeData
52
65
  implements TreeNodeData
@@ -114,10 +127,13 @@ export interface DatabaseExplorerTreeData
114
127
  database: Database;
115
128
  }
116
129
 
130
+ const DEFAULT_DATABASE_PATH = 'store::MyDatabase';
131
+
117
132
  export class DatabaseSchemaExplorerState {
118
133
  readonly editorStore: EditorStore;
119
134
  readonly connection: RelationalDatabaseConnection;
120
135
  readonly database: Database;
136
+ targetDatabasePath: string;
121
137
 
122
138
  isGeneratingDatabase = false;
123
139
  isUpdatingDatabase = false;
@@ -131,7 +147,11 @@ export class DatabaseSchemaExplorerState {
131
147
  isGeneratingDatabase: observable,
132
148
  isUpdatingDatabase: observable,
133
149
  treeData: observable,
150
+ targetDatabasePath: observable,
151
+ isCreatingNewDatabase: computed,
152
+ resolveDatabasePackageAndName: computed,
134
153
  setTreeData: action,
154
+ setTargetDatabasePath: action,
135
155
  onNodeSelect: flow,
136
156
  fetchDatabaseMetadata: flow,
137
157
  fetchSchemaMetadata: flow,
@@ -143,6 +163,33 @@ export class DatabaseSchemaExplorerState {
143
163
  this.connection = connection;
144
164
  this.database = guaranteeType(connection.store.value, Database);
145
165
  this.editorStore = editorStore;
166
+ this.targetDatabasePath = DEFAULT_DATABASE_PATH;
167
+ }
168
+
169
+ get isCreatingNewDatabase(): boolean {
170
+ return isStubbed_PackageableElement(this.connection.store.value);
171
+ }
172
+
173
+ get resolveDatabasePackageAndName(): [string, string] {
174
+ if (!this.isCreatingNewDatabase) {
175
+ return [
176
+ guaranteeNonNullable(this.database.package).path,
177
+ this.database.name,
178
+ ];
179
+ }
180
+ assertNonEmptyString(this.targetDatabasePath, 'Must specify database path');
181
+ assertTrue(
182
+ isValidFullPath(this.targetDatabasePath),
183
+ 'Invalid database path',
184
+ );
185
+ return resolvePackagePathAndElementName(
186
+ this.targetDatabasePath,
187
+ this.targetDatabasePath,
188
+ );
189
+ }
190
+
191
+ setTargetDatabasePath(val: string): void {
192
+ this.targetDatabasePath = val;
146
193
  }
147
194
 
148
195
  setTreeData(builderTreeData?: DatabaseExplorerTreeData): void {
@@ -208,9 +255,10 @@ export class DatabaseSchemaExplorerState {
208
255
  try {
209
256
  this.isGeneratingDatabase = true;
210
257
  const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
258
+ const [packagePath, name] = this.resolveDatabasePackageAndName;
211
259
  databaseBuilderInput.targetDatabase = new TargetDatabase(
212
- guaranteeNonNullable(this.database.package).path,
213
- this.database.name,
260
+ packagePath,
261
+ name,
214
262
  );
215
263
  databaseBuilderInput.config.maxTables = undefined;
216
264
  databaseBuilderInput.config.enrichTables = false;
@@ -266,9 +314,10 @@ export class DatabaseSchemaExplorerState {
266
314
 
267
315
  const schema = schemaNode.schema;
268
316
  const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
317
+ const [packagePath, name] = this.resolveDatabasePackageAndName;
269
318
  databaseBuilderInput.targetDatabase = new TargetDatabase(
270
- guaranteeNonNullable(this.database.package).path,
271
- this.database.name,
319
+ packagePath,
320
+ name,
272
321
  );
273
322
  databaseBuilderInput.config.maxTables = undefined;
274
323
  databaseBuilderInput.config.enrichTables = true;
@@ -328,9 +377,7 @@ export class DatabaseSchemaExplorerState {
328
377
  this.isGeneratingDatabase = true;
329
378
 
330
379
  const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
331
- const [packagePath, name] = resolvePackagePathAndElementName(
332
- this.database.path,
333
- );
380
+ const [packagePath, name] = this.resolveDatabasePackageAndName;
334
381
  databaseBuilderInput.targetDatabase = new TargetDatabase(
335
382
  packagePath,
336
383
  name,
@@ -414,9 +461,10 @@ export class DatabaseSchemaExplorerState {
414
461
 
415
462
  const treeData = guaranteeNonNullable(this.treeData);
416
463
  const databaseBuilderInput = new DatabaseBuilderInput(this.connection);
464
+ const [packagePath, name] = this.resolveDatabasePackageAndName;
417
465
  databaseBuilderInput.targetDatabase = new TargetDatabase(
418
- guaranteeNonNullable(this.database.package).path,
419
- this.database.name,
466
+ packagePath,
467
+ name,
420
468
  );
421
469
  const config = databaseBuilderInput.config;
422
470
  config.maxTables = undefined;
@@ -472,7 +520,11 @@ export class DatabaseSchemaExplorerState {
472
520
 
473
521
  try {
474
522
  this.isUpdatingDatabase = true;
475
-
523
+ const createDatabase =
524
+ this.isCreatingNewDatabase &&
525
+ !this.editorStore.graphManagerState.graph.databases.includes(
526
+ this.database,
527
+ );
476
528
  const graph = this.editorStore.graphManagerState.createNewGraph();
477
529
  (yield this.editorStore.graphManagerState.graphManager.buildGraph(
478
530
  graph,
@@ -516,7 +568,23 @@ export class DatabaseSchemaExplorerState {
516
568
  this.database.schemas.push(schema);
517
569
  }
518
570
  });
519
-
571
+ if (createDatabase) {
572
+ connection_setStore(
573
+ this.connection,
574
+ PackageableElementExplicitReference.create(database),
575
+ );
576
+ const packagePath = guaranteeNonNullable(
577
+ database.package?.name,
578
+ 'Database package is missing',
579
+ );
580
+ yield flowResult(
581
+ this.editorStore.graphEditorMode.addElement(
582
+ database,
583
+ packagePath,
584
+ false,
585
+ ),
586
+ );
587
+ }
520
588
  this.editorStore.applicationStore.notificationService.notifySuccess(
521
589
  `Database successfully updated`,
522
590
  );
@@ -23,8 +23,8 @@ import {
23
23
  import { observable, action, makeObservable, flow, flowResult } from 'mobx';
24
24
  import { LEGEND_STUDIO_APP_EVENT } from '../../../../../__lib__/LegendStudioEvent.js';
25
25
  import type { EditorStore } from '../../../EditorStore.js';
26
- import { type RelationalDatabaseConnection } from '@finos/legend-graph';
27
26
  import { DatabaseSchemaExplorerState } from './DatabaseBuilderState.js';
27
+ import type { RelationalDatabaseConnection } from '@finos/legend-graph';
28
28
 
29
29
  export class DatabaseBuilderWizardState {
30
30
  readonly editorStore: EditorStore;
@@ -70,7 +70,6 @@ export class DatabaseBuilderWizardState {
70
70
  if (!this.schemaExplorerState.treeData) {
71
71
  return;
72
72
  }
73
-
74
73
  try {
75
74
  this.setDatabaseGrammarCode(
76
75
  (yield this.editorStore.graphManagerState.graphManager.entitiesToPureCode(