@finos/legend-application-studio 28.19.5 → 28.19.7

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 (44) hide show
  1. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts +3 -1
  2. package/lib/__lib__/LegendStudioApplicationNavigationContext.d.ts.map +1 -1
  3. package/lib/__lib__/LegendStudioApplicationNavigationContext.js +3 -0
  4. package/lib/__lib__/LegendStudioApplicationNavigationContext.js.map +1 -1
  5. package/lib/__lib__/LegendStudioTesting.d.ts +2 -1
  6. package/lib/__lib__/LegendStudioTesting.d.ts.map +1 -1
  7. package/lib/__lib__/LegendStudioTesting.js +1 -0
  8. package/lib/__lib__/LegendStudioTesting.js.map +1 -1
  9. package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.d.ts +9 -0
  10. package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.d.ts.map +1 -1
  11. package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.js +128 -28
  12. package/lib/components/editor/editor-group/dataProduct/DataPoductEditor.js.map +1 -1
  13. package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.d.ts.map +1 -1
  14. package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.js +3 -0
  15. package/lib/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.js.map +1 -1
  16. package/lib/index.css +2 -2
  17. package/lib/index.css.map +1 -1
  18. package/lib/package.json +1 -1
  19. package/lib/stores/editor/GraphEditFormModeState.d.ts.map +1 -1
  20. package/lib/stores/editor/GraphEditFormModeState.js +1 -1
  21. package/lib/stores/editor/GraphEditFormModeState.js.map +1 -1
  22. package/lib/stores/editor/GraphEditGrammarModeState.d.ts.map +1 -1
  23. package/lib/stores/editor/GraphEditGrammarModeState.js +3 -1
  24. package/lib/stores/editor/GraphEditGrammarModeState.js.map +1 -1
  25. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.d.ts.map +1 -1
  26. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.js +3 -1
  27. package/lib/stores/editor/editor-state/element-editor-state/RuntimeEditorState.js.map +1 -1
  28. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.d.ts.map +1 -1
  29. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js +5 -3
  30. package/lib/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.js.map +1 -1
  31. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts +8 -1
  32. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.d.ts.map +1 -1
  33. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js +27 -1
  34. package/lib/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js.map +1 -1
  35. package/package.json +15 -15
  36. package/src/__lib__/LegendStudioApplicationNavigationContext.ts +4 -0
  37. package/src/__lib__/LegendStudioTesting.ts +2 -0
  38. package/src/components/editor/editor-group/dataProduct/DataPoductEditor.tsx +263 -41
  39. package/src/components/editor/editor-group/ingest-editor/IngestDefinitionEditor.tsx +6 -0
  40. package/src/stores/editor/GraphEditFormModeState.ts +1 -0
  41. package/src/stores/editor/GraphEditGrammarModeState.ts +3 -0
  42. package/src/stores/editor/editor-state/element-editor-state/RuntimeEditorState.ts +5 -1
  43. package/src/stores/editor/editor-state/element-editor-state/dataProduct/DataProductEditorState.ts +12 -3
  44. package/src/stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.ts +51 -0
@@ -37,6 +37,7 @@ import {
37
37
  PlusIcon,
38
38
  PanelHeaderActionItem,
39
39
  RocketIcon,
40
+ ListEditor,
40
41
  Modal,
41
42
  ModalHeader,
42
43
  ModalTitle,
@@ -50,21 +51,53 @@ import {
50
51
  MenuContentItem,
51
52
  CaretDownIcon,
52
53
  WarningIcon,
54
+ PanelFormSection,
53
55
  } from '@finos/legend-art';
54
- import React, { useRef, useState, useEffect } from 'react';
56
+ import React, {
57
+ useRef,
58
+ useState,
59
+ useEffect,
60
+ type ChangeEventHandler,
61
+ } from 'react';
55
62
  import { filterByType } from '@finos/legend-shared';
56
63
  import { InlineLambdaEditor } from '@finos/legend-query-builder';
57
64
  import { action, flowResult } from 'mobx';
58
65
  import { useAuth } from 'react-oidc-context';
59
66
  import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
60
67
  import { CodeEditor } from '@finos/legend-lego/code-editor';
61
- import { LakehouseTargetEnv } from '@finos/legend-graph';
68
+ import { LakehouseTargetEnv, Email } from '@finos/legend-graph';
62
69
  import {
63
70
  accessPointGroup_setDescription,
64
71
  accessPointGroup_setName,
65
72
  dataProduct_setDescription,
73
+ dataProduct_setSupportInfoIfAbsent,
66
74
  dataProduct_setTitle,
75
+ supportInfo_setDocumentationUrl,
76
+ supportInfo_setWebsite,
77
+ supportInfo_setFaqUrl,
78
+ supportInfo_setSupportUrl,
79
+ supportInfo_addEmail,
80
+ supportInfo_deleteEmail,
67
81
  } from '../../../../stores/graph-modifier/DSL_DataProduct_GraphModifierHelper.js';
82
+ import { LEGEND_STUDIO_TEST_ID } from '../../../../__lib__/LegendStudioTesting.js';
83
+ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
84
+ import {
85
+ ActionAlertActionType,
86
+ ActionAlertType,
87
+ useApplicationNavigationContext,
88
+ } from '@finos/legend-application';
89
+
90
+ export enum AP_GROUP_MODAL_ERRORS {
91
+ GROUP_NAME_EMPTY = 'Group Name is empty',
92
+ GROUP_NAME_EXISTS = 'Group Name already exists',
93
+ GROUP_DESCRIPTION_EMPTY = 'Group Description is empty',
94
+ AP_NAME_EMPTY = 'Access Point Name is empty',
95
+ AP_NAME_EXISTS = 'Access Point Name already exists',
96
+ AP_DESCRIPTION_EMPTY = 'Access Point Description is empty',
97
+ }
98
+
99
+ export const AP_EMPTY_DESC_WARNING =
100
+ 'Describe the data this access point produces';
68
101
 
69
102
  const NewAccessPointAccessPOint = observer(
70
103
  (props: { dataProductEditorState: DataProductEditorState }) => {
@@ -106,15 +139,17 @@ const NewAccessPointAccessPOint = observer(
106
139
  dataProductEditorState.accessPoints.map((e) => e.id).includes(id);
107
140
  const nameErrors =
108
141
  id === ''
109
- ? `Name is empty`
142
+ ? AP_GROUP_MODAL_ERRORS.AP_NAME_EMPTY
110
143
  : dataProductEditorState.accessPoints
111
144
  .map((e) => e.id)
112
145
  .includes(id ?? '')
113
- ? `Name already exists`
146
+ ? AP_GROUP_MODAL_ERRORS.AP_NAME_EXISTS
114
147
  : undefined;
115
148
 
116
149
  const descriptionErrors =
117
- description === '' ? `Description is empty` : undefined;
150
+ description === ''
151
+ ? AP_GROUP_MODAL_ERRORS.AP_DESCRIPTION_EMPTY
152
+ : undefined;
118
153
  return (
119
154
  <Dialog
120
155
  open={true}
@@ -156,9 +191,9 @@ const NewAccessPointAccessPOint = observer(
156
191
  })}
157
192
  ref={accessPointInputRef}
158
193
  spellCheck={false}
159
- value={id}
194
+ value={id ?? ''}
160
195
  onChange={handleIdChange}
161
- placeholder="Access Point ID"
196
+ placeholder="Access Point Name"
162
197
  error={nameErrors}
163
198
  />
164
199
  </div>
@@ -171,7 +206,7 @@ const NewAccessPointAccessPOint = observer(
171
206
  'input--dark': true,
172
207
  })}
173
208
  spellCheck={false}
174
- value={description}
209
+ value={description ?? ''}
175
210
  onChange={handleDescriptionChange}
176
211
  placeholder="Access Point Description"
177
212
  error={descriptionErrors}
@@ -228,24 +263,28 @@ const NewAccessPointGroupModal = observer(
228
263
 
229
264
  const groupNameErrors =
230
265
  groupName === ''
231
- ? `Group Name is empty`
266
+ ? AP_GROUP_MODAL_ERRORS.GROUP_NAME_EMPTY
232
267
  : dataProductEditorState.accessPointGroupStates
233
268
  .map((e) => e.value.id)
234
269
  .includes(groupName ?? '')
235
- ? `Group Name already exists`
270
+ ? AP_GROUP_MODAL_ERRORS.GROUP_NAME_EXISTS
236
271
  : undefined;
237
272
  const groupDescriptionErrors =
238
- groupDescription === '' ? `Group Description is empty` : undefined;
273
+ groupDescription === ''
274
+ ? AP_GROUP_MODAL_ERRORS.GROUP_DESCRIPTION_EMPTY
275
+ : undefined;
239
276
  const apNameErrors =
240
277
  apName === ''
241
- ? `Access Point Name is empty`
278
+ ? AP_GROUP_MODAL_ERRORS.AP_NAME_EMPTY
242
279
  : dataProductEditorState.accessPoints
243
280
  .map((e) => e.id)
244
281
  .includes(apName ?? '')
245
- ? `Access Point Name already exists`
282
+ ? AP_GROUP_MODAL_ERRORS.AP_NAME_EXISTS
246
283
  : undefined;
247
284
  const apDescriptionErrors =
248
- apDescription === '' ? `Access Point Description is empty` : undefined;
285
+ apDescription === ''
286
+ ? AP_GROUP_MODAL_ERRORS.AP_DESCRIPTION_EMPTY
287
+ : undefined;
249
288
 
250
289
  const disableCreateButton =
251
290
  !groupName ||
@@ -314,7 +353,7 @@ const NewAccessPointGroupModal = observer(
314
353
  })}
315
354
  ref={accessPointGroupInputRef}
316
355
  spellCheck={false}
317
- value={groupName}
356
+ value={groupName ?? ''}
318
357
  onChange={handleGroupNameChange}
319
358
  placeholder="Access Point Group Name"
320
359
  error={groupNameErrors}
@@ -329,7 +368,7 @@ const NewAccessPointGroupModal = observer(
329
368
  'input--dark': true,
330
369
  })}
331
370
  spellCheck={false}
332
- value={groupDescription}
371
+ value={groupDescription ?? ''}
333
372
  onChange={handleGroupDescriptionChange}
334
373
  placeholder="Access Point Group Description"
335
374
  error={groupDescriptionErrors}
@@ -348,7 +387,7 @@ const NewAccessPointGroupModal = observer(
348
387
  'input--dark': true,
349
388
  })}
350
389
  spellCheck={false}
351
- value={apName}
390
+ value={apName ?? ''}
352
391
  onChange={handleApNameChange}
353
392
  placeholder="Access Point Name"
354
393
  error={apNameErrors}
@@ -361,7 +400,7 @@ const NewAccessPointGroupModal = observer(
361
400
  'input--dark': true,
362
401
  })}
363
402
  spellCheck={false}
364
- value={apDescription}
403
+ value={apDescription ?? ''}
365
404
  onChange={handleApDescriptionChange}
366
405
  placeholder="Access Point Description"
367
406
  error={apDescriptionErrors}
@@ -411,7 +450,7 @@ const HoverTextArea: React.FC<HoverTextAreaProps> = ({
411
450
 
412
451
  const hoverIcon = () => {
413
452
  return (
414
- <div>
453
+ <div data-testid={LEGEND_STUDIO_TEST_ID.HOVER_EDIT_ICON}>
415
454
  <PencilEditIcon />
416
455
  </div>
417
456
  );
@@ -445,11 +484,10 @@ export const LakehouseDataProductAcccessPointEditor = observer(
445
484
  setIsHovering(false);
446
485
  };
447
486
 
448
- const updateAccessPointDescription: React.ChangeEventHandler<
449
- HTMLTextAreaElement
450
- > = (event) => {
451
- action((accessPoint.description = event.target.value));
452
- };
487
+ const updateAccessPointDescription: React.ChangeEventHandler<HTMLTextAreaElement> =
488
+ action((event) => {
489
+ accessPoint.description = event.target.value;
490
+ });
453
491
 
454
492
  const updateAccessPointTargetEnvironment = action(
455
493
  (targetEnvironment: LakehouseTargetEnv) => {
@@ -502,7 +540,7 @@ export const LakehouseDataProductAcccessPointEditor = observer(
502
540
  onMouseOut={handleMouseOut}
503
541
  >
504
542
  <WarningIcon />
505
- Describe the data this access point produces
543
+ {AP_EMPTY_DESC_WARNING}
506
544
  </div>
507
545
  )}
508
546
 
@@ -673,6 +711,7 @@ const DataProductDeploymentResponseModal = observer(
673
711
  const AccessPointGroupSection = observer(
674
712
  (props: { groupState: AccessPointGroupState; isReadOnly: boolean }) => {
675
713
  const { groupState, isReadOnly } = props;
714
+ const editorStore = useEditorStore();
676
715
  const productEditorState = groupState.state;
677
716
  const [editingDescription, setEditingDescription] = useState(false);
678
717
  const [isHoveringDescription, setIsHoveringDescription] = useState(false);
@@ -715,6 +754,27 @@ const AccessPointGroupSection = observer(
715
754
  }
716
755
  };
717
756
 
757
+ const handleRemoveAccessPointGroup = (): void => {
758
+ editorStore.applicationStore.alertService.setActionAlertInfo({
759
+ message: `Deleting access point group ${groupState.value.id} will permanently remove it and all associated access points. Are you sure you want to proceed?`,
760
+ type: ActionAlertType.CAUTION,
761
+ actions: [
762
+ {
763
+ label: 'Cancel',
764
+ type: ActionAlertActionType.PROCEED,
765
+ default: true,
766
+ },
767
+ {
768
+ label: 'Proceed',
769
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
770
+ handler: (): void => {
771
+ productEditorState.deleteAccessPointGroup(groupState);
772
+ },
773
+ },
774
+ ],
775
+ });
776
+ };
777
+
718
778
  const openNewModal = () => {
719
779
  productEditorState.setEditingGroupState(groupState);
720
780
  productEditorState.setAccessPointModal(true);
@@ -755,7 +815,8 @@ const AccessPointGroupSection = observer(
755
815
  <button
756
816
  className="access-point-editor__generic-entry__remove-btn--group"
757
817
  onClick={() => {
758
- productEditorState.deleteAccessPointGroup(groupState);
818
+ // productEditorState.deleteAccessPointGroup(groupState);
819
+ handleRemoveAccessPointGroup();
759
820
  }}
760
821
  tabIndex={-1}
761
822
  title="Remove Access Point Group"
@@ -879,9 +940,42 @@ export const DataProductEditor = observer(() => {
879
940
  const updateDataProductTitle = (val: string | undefined): void => {
880
941
  dataProduct_setTitle(product, val ?? '');
881
942
  };
882
- const updateDataProductDescription = (val: string | undefined): void => {
883
- dataProduct_setDescription(product, val ?? '');
943
+ const updateDataProductDescription: ChangeEventHandler<
944
+ HTMLTextAreaElement
945
+ > = (event) => {
946
+ dataProduct_setDescription(product, event.target.value);
947
+ };
948
+
949
+ const updateSupportInfoDocumentationUrl = (val: string | undefined): void => {
950
+ dataProduct_setSupportInfoIfAbsent(product);
951
+ if (product.supportInfo) {
952
+ supportInfo_setDocumentationUrl(product.supportInfo, val ?? '');
953
+ }
954
+ };
955
+
956
+ const updateSupportInfoWebsite = (val: string | undefined): void => {
957
+ dataProduct_setSupportInfoIfAbsent(product);
958
+ if (product.supportInfo) {
959
+ supportInfo_setWebsite(product.supportInfo, val ?? '');
960
+ }
961
+ };
962
+
963
+ const updateSupportInfoFaqUrl = (val: string | undefined): void => {
964
+ dataProduct_setSupportInfoIfAbsent(product);
965
+ if (product.supportInfo) {
966
+ supportInfo_setFaqUrl(product.supportInfo, val ?? '');
967
+ }
968
+ };
969
+
970
+ const updateSupportInfoSupportUrl = (val: string | undefined): void => {
971
+ dataProduct_setSupportInfoIfAbsent(product);
972
+ if (product.supportInfo) {
973
+ supportInfo_setSupportUrl(product.supportInfo, val ?? '');
974
+ }
884
975
  };
976
+ useApplicationNavigationContext(
977
+ LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.DATA_PRODUCT_EDITOR,
978
+ );
885
979
 
886
980
  useEffect(() => {
887
981
  flowResult(dataProductEditorState.convertAccessPointsFuncObjects()).catch(
@@ -901,6 +995,84 @@ export const DataProductEditor = observer(() => {
901
995
  dataProductEditorState,
902
996
  ]);
903
997
 
998
+ const handleSupportInfoEmailAdd = (address: string, title: string): void => {
999
+ dataProduct_setSupportInfoIfAbsent(product);
1000
+ if (product.supportInfo) {
1001
+ supportInfo_addEmail(product.supportInfo, new Email(address, title));
1002
+ }
1003
+ };
1004
+
1005
+ const handleSupportInfoEmailRemove = (email: Email): void => {
1006
+ if (product.supportInfo) {
1007
+ supportInfo_deleteEmail(product.supportInfo, email);
1008
+ }
1009
+ };
1010
+
1011
+ const SupportEmailComponent = observer(
1012
+ (props: { item: Email }): React.ReactElement => {
1013
+ const { item } = props;
1014
+
1015
+ return (
1016
+ <div className="panel__content__form__section__list__item__rows">
1017
+ <div className="row">
1018
+ <label className="label">Address</label>
1019
+ <div className="textbox">{item.address}</div>
1020
+ </div>
1021
+ <div className="row">
1022
+ <label className="label">Title</label>
1023
+ <div className="textbox">{item.title}</div>
1024
+ </div>
1025
+ </div>
1026
+ );
1027
+ },
1028
+ );
1029
+
1030
+ const NewSupportEmailComponent = observer(
1031
+ (props: { onFinishEditing: () => void }) => {
1032
+ const { onFinishEditing } = props;
1033
+ const [address, setAddress] = useState('');
1034
+ const [title, setTitle] = useState('');
1035
+
1036
+ return (
1037
+ <div className="data-product-editor__support-info__new-email">
1038
+ <div className="panel__content__form__section__list__new-item__input">
1039
+ <input
1040
+ className="input input-group__input panel__content__form__section__input input--dark"
1041
+ type="email"
1042
+ placeholder="Enter email"
1043
+ value={address}
1044
+ onChange={(event) => {
1045
+ setAddress(event.target.value);
1046
+ }}
1047
+ />
1048
+ </div>
1049
+ <div className="panel__content__form__section__list__new-item__input">
1050
+ <input
1051
+ className="input input-group__input panel__content__form__section__input input--dark"
1052
+ type="title"
1053
+ placeholder="Enter title"
1054
+ value={title}
1055
+ onChange={(event) => {
1056
+ setTitle(event.target.value);
1057
+ }}
1058
+ />
1059
+ </div>
1060
+ <button
1061
+ className="panel__content__form__section__list__new-item__add-btn btn btn--dark"
1062
+ onClick={() => {
1063
+ handleSupportInfoEmailAdd(address, title);
1064
+ setAddress('');
1065
+ setTitle('');
1066
+ onFinishEditing();
1067
+ }}
1068
+ >
1069
+ Save
1070
+ </button>
1071
+ </div>
1072
+ );
1073
+ },
1074
+ );
1075
+
904
1076
  return (
905
1077
  <div className="data-product-editor">
906
1078
  <div className="panel">
@@ -936,13 +1108,69 @@ export const DataProductEditor = observer(() => {
936
1108
  update={updateDataProductTitle}
937
1109
  placeholder="Enter title"
938
1110
  />
939
- <PanelFormTextField
940
- name="Description"
941
- value={product.description}
942
- prompt="Provide a description for this Lakehouse Data Product."
943
- update={updateDataProductDescription}
944
- placeholder="Enter description"
945
- />
1111
+ <div style={{ margin: '1rem' }}>
1112
+ <div className="panel__content__form__section__header__label">
1113
+ Description
1114
+ </div>
1115
+ <div className="panel__content__form__section__header__prompt">
1116
+ Provide a description for this Lakehouse Data Product.
1117
+ </div>
1118
+ <textarea
1119
+ className="panel__content__form__section__textarea"
1120
+ spellCheck={false}
1121
+ disabled={isReadOnly}
1122
+ value={product.description}
1123
+ onChange={updateDataProductDescription}
1124
+ style={{
1125
+ padding: '0.5rem',
1126
+ width: '45rem',
1127
+ maxWidth: '45rem !important',
1128
+ }}
1129
+ />
1130
+ </div>
1131
+
1132
+ <PanelFormSection>
1133
+ <div className="panel__content__form__section__header__label">
1134
+ Support Information
1135
+ </div>
1136
+ <div className="panel__content__form__section__header__prompt">
1137
+ Configure support information for this Lakehouse Data Product.
1138
+ </div>
1139
+ <PanelFormTextField
1140
+ name="Documentation URL"
1141
+ value={product.supportInfo?.documentationUrl ?? ''}
1142
+ update={updateSupportInfoDocumentationUrl}
1143
+ placeholder="Enter Documentation URL"
1144
+ />
1145
+ <PanelFormTextField
1146
+ name="Website"
1147
+ value={product.supportInfo?.website}
1148
+ update={updateSupportInfoWebsite}
1149
+ placeholder="Enter Website"
1150
+ />
1151
+ <PanelFormTextField
1152
+ name="FAQ URL"
1153
+ value={product.supportInfo?.faqUrl}
1154
+ update={updateSupportInfoFaqUrl}
1155
+ placeholder="Enter FAQ URL"
1156
+ />
1157
+ <PanelFormTextField
1158
+ name="Support URL"
1159
+ value={product.supportInfo?.supportUrl}
1160
+ update={updateSupportInfoSupportUrl}
1161
+ placeholder="Enter Support URL"
1162
+ />
1163
+ <ListEditor
1164
+ title="Emails"
1165
+ items={product.supportInfo?.emails}
1166
+ keySelector={(email: Email) => email.address + email.title}
1167
+ ItemComponent={SupportEmailComponent}
1168
+ NewItemComponent={NewSupportEmailComponent}
1169
+ handleRemoveItem={handleSupportInfoEmailRemove}
1170
+ isReadOnly={isReadOnly}
1171
+ emptyMessage="No emails specified"
1172
+ />
1173
+ </PanelFormSection>
946
1174
  </div>
947
1175
  <div className="panel" style={{ overflow: 'auto' }}>
948
1176
  <PanelHeader>
@@ -983,12 +1211,6 @@ export const DataProductEditor = observer(() => {
983
1211
  />
984
1212
  )}
985
1213
  </PanelContent>
986
-
987
- {/* {dataProductEditorState.accessPointModal && (
988
- <NewAccessPointAccessPOint
989
- dataProductEditorState={dataProductEditorState}
990
- />
991
- )} */}
992
1214
  {dataProductEditorState.accessPointGroupModal && (
993
1215
  <NewAccessPointGroupModal
994
1216
  dataProductEditorState={dataProductEditorState}
@@ -44,6 +44,8 @@ import {
44
44
  IngestDefinitionDeploymentResponse,
45
45
  IngestDefinitionValidationResponse,
46
46
  } from '@finos/legend-server-lakehouse';
47
+ import { useApplicationNavigationContext } from '@finos/legend-application';
48
+ import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../__lib__/LegendStudioApplicationNavigationContext.js';
47
49
 
48
50
  const IngestValidationError = observer(
49
51
  (props: {
@@ -259,6 +261,10 @@ export const IngestDefinitionEditor = observer(() => {
259
261
  ingestDefinitionEditorState.generateElementGrammar();
260
262
  }, [ingestDefinitionEditorState]);
261
263
 
264
+ useApplicationNavigationContext(
265
+ LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.INGEST_DEFINITION_EDITOR,
266
+ );
267
+
262
268
  useEffect(() => {
263
269
  if (ingestDefinitionEditorState.deployOnOpen) {
264
270
  flowResult(ingestDefinitionEditorState.init_with_deploy(auth)).catch(
@@ -540,6 +540,7 @@ export class GraphEditFormModeState extends GraphEditorMode {
540
540
  this.editorStore.graphState.error = error;
541
541
  this.editorStore.applicationStore.notificationService.notifyWarning(
542
542
  `Compilation failed: ${error.message}`,
543
+ error.trace,
543
544
  );
544
545
  this.editorStore.graphState.setMostRecentCompilationOutcome(
545
546
  GraphCompilationOutcome.FAILED,
@@ -449,6 +449,7 @@ export class GraphEditGrammarModeState extends GraphEditorMode {
449
449
  this.editorStore.graphState.setMostRecentCompilationGraphHash(
450
450
  currentGraphHash,
451
451
  );
452
+ let detail: string | undefined = undefined;
452
453
  if (error instanceof EngineError) {
453
454
  this.editorStore.graphState.error = error;
454
455
  if (error.sourceInformation) {
@@ -457,6 +458,7 @@ export class GraphEditGrammarModeState extends GraphEditorMode {
457
458
  column: error.sourceInformation.startColumn,
458
459
  });
459
460
  }
461
+ detail = error.trace;
460
462
  }
461
463
  if (
462
464
  !this.editorStore.applicationStore.notificationService.notification ||
@@ -464,6 +466,7 @@ export class GraphEditGrammarModeState extends GraphEditorMode {
464
466
  ) {
465
467
  this.editorStore.applicationStore.notificationService.notifyWarning(
466
468
  `Compilation failed: ${error.message}`,
469
+ detail,
467
470
  );
468
471
  }
469
472
  this.editorStore.graphState.setMostRecentCompilationOutcome(
@@ -968,7 +968,11 @@ export class LakehouseRuntimeEditorState extends EngineRuntimeEditorState {
968
968
  token,
969
969
  )) as unknown as IngestDeploymentServerConfig[] | undefined;
970
970
  this.setEnvSummaries(res);
971
- if (!this.runtimeValue.environment && this.envOptions.length) {
971
+ if (
972
+ this.lakehouseRuntimeType === LakehouseRuntimeType.ENVIRONMENT &&
973
+ !this.runtimeValue.environment &&
974
+ this.envOptions.length
975
+ ) {
972
976
  this.runtimeValue.environment = this.envOptions[0]?.value;
973
977
  }
974
978
  }
@@ -32,7 +32,14 @@ import {
32
32
  } from '@finos/legend-graph';
33
33
  import type { EditorStore } from '../../../EditorStore.js';
34
34
  import { ElementEditorState } from '../ElementEditorState.js';
35
- import { action, computed, flow, makeObservable, observable } from 'mobx';
35
+ import {
36
+ action,
37
+ computed,
38
+ flow,
39
+ makeObservable,
40
+ observable,
41
+ runInAction,
42
+ } from 'mobx';
36
43
  import {
37
44
  guaranteeType,
38
45
  addUniqueEntry,
@@ -391,8 +398,10 @@ export class DataProductEditorState extends ElementEditorState {
391
398
 
392
399
  deleteAccessPointGroup(val: AccessPointGroupState): void {
393
400
  const state = this.accessPointGroupStates.find((a) => a === val);
394
- deleteEntry(this.accessPointGroupStates, state);
395
- dataProduct_deleteAccessPointGroup(this.product, val.value);
401
+ runInAction(() => {
402
+ deleteEntry(this.accessPointGroupStates, state);
403
+ dataProduct_deleteAccessPointGroup(this.product, val.value);
404
+ });
396
405
  }
397
406
 
398
407
  *deploy(token: string | undefined): GeneratorFn<void> {
@@ -18,8 +18,12 @@ import {
18
18
  type AccessPoint,
19
19
  type AccessPointGroup,
20
20
  type DataProduct,
21
+ type Email,
21
22
  observe_AccessPoint,
22
23
  observe_AccessPointGroup,
24
+ observe_SupportInfo,
25
+ observe_Email,
26
+ SupportInfo,
23
27
  } from '@finos/legend-graph';
24
28
  import { addUniqueEntry, deleteEntry } from '@finos/legend-shared';
25
29
 
@@ -75,3 +79,50 @@ export const dataProduct_setDescription = action(
75
79
  product.description = description;
76
80
  },
77
81
  );
82
+
83
+ export const dataProduct_setSupportInfoIfAbsent = action(
84
+ (product: DataProduct) => {
85
+ if (!product.supportInfo) {
86
+ product.supportInfo = observe_SupportInfo(new SupportInfo());
87
+ }
88
+ },
89
+ );
90
+
91
+ export const supportInfo_setDocumentationUrl = action(
92
+ (supportInfo: SupportInfo, documentationUrl: string) => {
93
+ supportInfo.documentationUrl = documentationUrl;
94
+ },
95
+ );
96
+
97
+ export const supportInfo_setWebsite = action(
98
+ (supportInfo: SupportInfo, website: string) => {
99
+ supportInfo.website = website;
100
+ },
101
+ );
102
+
103
+ export const supportInfo_setFaqUrl = action(
104
+ (supportInfo: SupportInfo, faqUrl: string) => {
105
+ supportInfo.faqUrl = faqUrl;
106
+ },
107
+ );
108
+
109
+ export const supportInfo_setSupportUrl = action(
110
+ (supportInfo: SupportInfo, supportUrl: string) => {
111
+ supportInfo.supportUrl = supportUrl;
112
+ },
113
+ );
114
+
115
+ export const supportInfo_addEmail = action(
116
+ (supportInfo: SupportInfo, email: Email) => {
117
+ addUniqueEntry(supportInfo.emails, observe_Email(email));
118
+ },
119
+ );
120
+
121
+ export const supportInfo_deleteEmail = action(
122
+ (supportInfo: SupportInfo, email: Email): void => {
123
+ const index = supportInfo.emails.indexOf(email);
124
+ if (index !== -1) {
125
+ supportInfo.emails.splice(index, 1);
126
+ }
127
+ },
128
+ );