@finos/legend-query-builder 2.1.5 → 2.1.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 (21) hide show
  1. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderResultPanel.js +12 -12
  3. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  4. package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.d.ts +12 -2
  5. package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.d.ts.map +1 -1
  6. package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.js +145 -20
  7. package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.js.map +1 -1
  8. package/lib/index.css +1 -17
  9. package/lib/index.css.map +1 -1
  10. package/lib/package.json +1 -1
  11. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.d.ts +3 -0
  12. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.d.ts.map +1 -1
  13. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js +19 -1
  14. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js.map +1 -1
  15. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeValueSpecificationBuilder.js +11 -11
  16. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeValueSpecificationBuilder.js.map +1 -1
  17. package/package.json +4 -4
  18. package/src/components/QueryBuilderResultPanel.tsx +17 -14
  19. package/src/components/fetch-structure/QueryBuilderGraphFetchTreePanel.tsx +384 -63
  20. package/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.ts +34 -0
  21. package/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeValueSpecificationBuilder.ts +11 -11
@@ -41,9 +41,21 @@ import {
41
41
  ModalFooterButton,
42
42
  ModalFooter,
43
43
  SerializeIcon,
44
+ ResizablePanel,
45
+ ResizablePanelSplitter,
46
+ ResizablePanelGroup,
47
+ BufferIcon,
48
+ CustomSelectorInput,
49
+ FolderIcon,
44
50
  } from '@finos/legend-art';
45
51
  import { QUERY_BUILDER_TEST_ID } from '../../application/QueryBuilderTesting.js';
46
- import { isNonNullable } from '@finos/legend-shared';
52
+ import {
53
+ deepClone,
54
+ filterByType,
55
+ guaranteeNonNullable,
56
+ isNonNullable,
57
+ prettyCONSTName,
58
+ } from '@finos/legend-shared';
47
59
  import {
48
60
  type QueryBuilderGraphFetchTreeData,
49
61
  type QueryBuilderGraphFetchTreeNodeData,
@@ -55,12 +67,51 @@ import {
55
67
  QUERY_BUILDER_EXPLORER_TREE_DND_TYPE,
56
68
  } from '../../stores/explorer/QueryBuilderExplorerState.js';
57
69
  import {
70
+ type GraphFetchSerializationState,
71
+ GraphFetchExternalFormatSerializationState,
58
72
  GraphFetchPureSerializationState,
59
73
  PureSerializationConfig,
74
+ SERIALIZATION_TYPE,
60
75
  type QueryBuilderGraphFetchTreeState,
61
76
  } from '../../stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js';
62
77
  import { getClassPropertyIcon } from '../shared/ElementIconUtils.js';
63
78
  import { QueryBuilderTextEditorMode } from '../../stores/QueryBuilderTextEditorState.js';
79
+ import {
80
+ type PackageableElement,
81
+ Binding,
82
+ Package,
83
+ getDescendantsOfPackage,
84
+ } from '@finos/legend-graph';
85
+ import {
86
+ ActionAlertActionType,
87
+ ActionAlertType,
88
+ buildElementOption,
89
+ type PackageableElementOption,
90
+ } from '@finos/legend-application';
91
+
92
+ const getBindingFormatter = (props: {
93
+ darkMode?: boolean;
94
+ }): ((
95
+ option: PackageableElementOption<PackageableElement>,
96
+ ) => React.ReactNode) =>
97
+ function BindingLabel(
98
+ option: PackageableElementOption<PackageableElement>,
99
+ ): React.ReactNode {
100
+ const className = props.darkMode
101
+ ? 'packageable-element-option-label--dark'
102
+ : 'packageable-element-option-label';
103
+ return (
104
+ <div className={className}>
105
+ <div className={`${className}__name`}>{option.label}</div>
106
+ {option.value.package && (
107
+ <div className={`${className}__tag`}>{option.value.path}</div>
108
+ )}
109
+ <div className={`${className}__tag`}>
110
+ {(option.value as Binding).contentType}
111
+ </div>
112
+ </div>
113
+ );
114
+ };
64
115
 
65
116
  const QueryBuilderGraphFetchTreeNodeContainer: React.FC<
66
117
  TreeNodeContainerProps<
@@ -287,22 +338,131 @@ const PureSerializationConfigModal = observer(
287
338
  },
288
339
  );
289
340
 
341
+ export const QueryBuilderGraphFetchExternalConfig = observer(
342
+ (props: {
343
+ graphFetchState: QueryBuilderGraphFetchTreeState;
344
+ serializationState: GraphFetchExternalFormatSerializationState;
345
+ serializationTreeData: QueryBuilderGraphFetchTreeData;
346
+ bindings: Binding[];
347
+ isReadOnly: boolean;
348
+ }) => {
349
+ const {
350
+ graphFetchState,
351
+ serializationState,
352
+ serializationTreeData,
353
+ bindings,
354
+ isReadOnly,
355
+ } = props;
356
+ const bindingOptions = bindings.map((result) => buildElementOption(result));
357
+ const selectedBinding = {
358
+ value: serializationState.targetBinding,
359
+ label: serializationState.targetBinding.name,
360
+ };
361
+ const onBindingChange = (
362
+ val: PackageableElementOption<Binding> | null,
363
+ ): void => {
364
+ if (val !== null) {
365
+ serializationState.setBinding(val.value);
366
+ serializationState.setGraphFetchTree(serializationTreeData);
367
+ }
368
+ };
369
+ const onNodeSelect = (node: QueryBuilderGraphFetchTreeNodeData): void => {
370
+ if (node.childrenIds.length) {
371
+ node.isOpen = !node.isOpen;
372
+ }
373
+ };
374
+ const getChildNodes = (
375
+ node: QueryBuilderGraphFetchTreeNodeData,
376
+ ): QueryBuilderGraphFetchTreeNodeData[] =>
377
+ node.childrenIds
378
+ .map((id) => serializationTreeData.nodes.get(id))
379
+ .filter(isNonNullable);
380
+ const removeNode = (node: QueryBuilderGraphFetchTreeNodeData): void => {
381
+ if (serializationTreeData.nodes.size === 1) {
382
+ graphFetchState.queryBuilderState.applicationStore.notificationService.notifyWarning(
383
+ 'externalize serialization tree can not be empty',
384
+ );
385
+ } else {
386
+ removeNodeRecursively(serializationTreeData, node);
387
+ serializationState.setGraphFetchTree({ ...serializationTreeData });
388
+ }
389
+ };
390
+
391
+ return (
392
+ <div className="query-builder-graph-fetch-external-format">
393
+ <div className="service-execution-editor__configuration__items">
394
+ <div className="service-execution-editor__configuration__item">
395
+ <div className="btn--sm service-execution-editor__configuration__item__label">
396
+ <BufferIcon />
397
+ </div>
398
+ <CustomSelectorInput
399
+ className="panel__content__form__section__dropdown service-execution-editor__configuration__item__dropdown"
400
+ disabled={isReadOnly}
401
+ options={bindingOptions}
402
+ onChange={onBindingChange}
403
+ value={selectedBinding}
404
+ formatOptionLabel={getBindingFormatter({
405
+ darkMode: true,
406
+ })}
407
+ darkMode={true}
408
+ />
409
+ </div>
410
+ <div className="service-execution-editor__configuration__item">
411
+ <div className="btn--sm service-execution-editor__configuration__item__label">
412
+ <FolderIcon />
413
+ </div>
414
+ <TreeView
415
+ components={{
416
+ TreeNodeContainer: QueryBuilderGraphFetchTreeNodeContainer,
417
+ }}
418
+ className="query-builder-graph-fetch-tree__container__tree"
419
+ treeData={serializationTreeData}
420
+ onNodeSelect={onNodeSelect}
421
+ getChildNodes={getChildNodes}
422
+ innerProps={{
423
+ isReadOnly,
424
+ removeNode,
425
+ }}
426
+ />
427
+ </div>
428
+ </div>
429
+ </div>
430
+ );
431
+ },
432
+ );
433
+
290
434
  export const QueryBuilderGraphFetchTreeExplorer = observer(
291
435
  (props: {
292
436
  graphFetchState: QueryBuilderGraphFetchTreeState;
293
- pureSerializationState: GraphFetchPureSerializationState;
437
+ serializationState: GraphFetchSerializationState;
294
438
  treeData: QueryBuilderGraphFetchTreeData;
295
439
  updateTreeData: (data: QueryBuilderGraphFetchTreeData) => void;
296
440
  isReadOnly: boolean;
297
441
  }) => {
298
442
  const {
299
443
  graphFetchState,
300
- pureSerializationState,
444
+ serializationState,
301
445
  treeData,
302
446
  updateTreeData,
303
447
  isReadOnly,
304
448
  } = props;
305
449
 
450
+ // Retrieve all bindings whose packageableElementIncludes contain the root class of main graph fetch tree
451
+ const compatibleBindings =
452
+ graphFetchState.queryBuilderState.graphManagerState.usableStores
453
+ .filter(filterByType(Binding))
454
+ .filter((b) => {
455
+ const elements = b.modelUnit.packageableElementIncludes.map(
456
+ (p) => p.value,
457
+ );
458
+ return elements
459
+ .filter(filterByType(Package))
460
+ .map((p) => Array.from(getDescendantsOfPackage(p)))
461
+ .flat()
462
+ .concat(elements.filter((e) => !(e instanceof Package)))
463
+ .includes(treeData.tree.class.value);
464
+ });
465
+
306
466
  const onNodeSelect = (node: QueryBuilderGraphFetchTreeNodeData): void => {
307
467
  if (node.childrenIds.length) {
308
468
  node.isOpen = !node.isOpen;
@@ -319,6 +479,24 @@ export const QueryBuilderGraphFetchTreeExplorer = observer(
319
479
 
320
480
  const removeNode = (node: QueryBuilderGraphFetchTreeNodeData): void => {
321
481
  removeNodeRecursively(treeData, node);
482
+ if (treeData.nodes.size === 0) {
483
+ graphFetchState.setSerializationState(
484
+ new GraphFetchPureSerializationState(graphFetchState),
485
+ );
486
+ }
487
+ // Remove node from external format serialization tree as well
488
+ if (
489
+ serializationState instanceof
490
+ GraphFetchExternalFormatSerializationState &&
491
+ serializationState.treeData &&
492
+ serializationState.treeData.nodes.get(node.id)
493
+ ) {
494
+ removeNodeRecursively(
495
+ serializationState.treeData,
496
+ guaranteeNonNullable(serializationState.treeData.nodes.get(node.id)),
497
+ );
498
+ updateTreeData({ ...serializationState.treeData });
499
+ }
322
500
  updateTreeData({ ...treeData });
323
501
  };
324
502
 
@@ -326,71 +504,202 @@ export const QueryBuilderGraphFetchTreeExplorer = observer(
326
504
  graphFetchState.setChecked(!graphFetchState.isChecked);
327
505
 
328
506
  const openConfigModal = (): void => {
329
- pureSerializationState.setConfigModal(true);
507
+ if (serializationState instanceof GraphFetchPureSerializationState) {
508
+ serializationState.setConfigModal(true);
509
+ }
330
510
  };
331
511
 
512
+ const onChangeSerializationType =
513
+ (implementationType: SERIALIZATION_TYPE): (() => void) =>
514
+ (): void => {
515
+ if (implementationType !== serializationState.getLabel()) {
516
+ graphFetchState.queryBuilderState.applicationStore.alertService.setActionAlertInfo(
517
+ {
518
+ message:
519
+ 'Current graph-fetch will be lost when switching to a different serialization mode. Do you still want to proceed?',
520
+ type: ActionAlertType.CAUTION,
521
+ actions: [
522
+ {
523
+ label: 'Proceed',
524
+ type: ActionAlertActionType.PROCEED_WITH_CAUTION,
525
+ handler:
526
+ graphFetchState.queryBuilderState.applicationStore.guardUnhandledError(
527
+ async () => {
528
+ switch (implementationType) {
529
+ case SERIALIZATION_TYPE.EXTERNAL_FORMAT:
530
+ if (
531
+ compatibleBindings.length > 0 &&
532
+ compatibleBindings[0]
533
+ ) {
534
+ const externalizeState =
535
+ new GraphFetchExternalFormatSerializationState(
536
+ graphFetchState,
537
+ compatibleBindings[0],
538
+ undefined,
539
+ );
540
+ graphFetchState.setGraphFetchTree(treeData);
541
+ externalizeState.setGraphFetchTree(
542
+ deepClone(treeData),
543
+ );
544
+ graphFetchState.setSerializationState(
545
+ externalizeState,
546
+ );
547
+ } else {
548
+ graphFetchState.queryBuilderState.applicationStore.notificationService.notifyWarning(
549
+ `Can't switch to external format serialization: No compatible bindings found`,
550
+ );
551
+ }
552
+ break;
553
+ case SERIALIZATION_TYPE.PURE:
554
+ default:
555
+ graphFetchState.setSerializationState(
556
+ new GraphFetchPureSerializationState(
557
+ graphFetchState,
558
+ ),
559
+ );
560
+ break;
561
+ }
562
+ },
563
+ ),
564
+ },
565
+ {
566
+ label: 'Cancel',
567
+ type: ActionAlertActionType.PROCEED,
568
+ default: true,
569
+ },
570
+ ],
571
+ },
572
+ );
573
+ }
574
+ };
575
+
332
576
  return (
333
577
  <div className="query-builder-graph-fetch-tree">
334
578
  <div className="query-builder-graph-fetch-tree__toolbar">
579
+ <div className="query-builder__fetch__structure__modes">
580
+ {Object.values(SERIALIZATION_TYPE).map((type) => (
581
+ <button
582
+ onClick={onChangeSerializationType(type)}
583
+ className={clsx('query-builder__fetch__structure__mode', {
584
+ 'query-builder__fetch__structure__mode--selected':
585
+ type === serializationState.getLabel(),
586
+ })}
587
+ key={type}
588
+ >
589
+ {prettyCONSTName(type)}
590
+ </button>
591
+ ))}
592
+ </div>
335
593
  <div className="query-builder-graph-fetch-tree__actions">
336
- <button
337
- className="query-builder-graph-fetch-tree__actions__action-btn__label"
338
- onClick={openConfigModal}
339
- title={`${
340
- pureSerializationState.config
341
- ? 'Edit pure serialization config'
342
- : 'Add pure serialization config'
343
- }`}
344
- tabIndex={-1}
345
- >
346
- <SerializeIcon className="query-builder-graph-fetch-tree__actions__action-btn__label__icon" />
347
- <div className="query-builder-graph-fetch-tree__actions__action-btn__label__title">
348
- {pureSerializationState.config ? 'Edit Config' : 'Add Config'}
594
+ {serializationState instanceof GraphFetchPureSerializationState && (
595
+ <div className="query-builder-graph-fetch-tree__actions__action">
596
+ <button
597
+ className="query-builder-graph-fetch-tree__actions__action-btn__label"
598
+ onClick={openConfigModal}
599
+ title={`${
600
+ serializationState.config
601
+ ? 'Edit pure serialization config'
602
+ : 'Add pure serialization config'
603
+ }`}
604
+ tabIndex={-1}
605
+ >
606
+ <SerializeIcon className="query-builder-graph-fetch-tree__actions__action-btn__label__icon" />
607
+ <div className="query-builder-graph-fetch-tree__actions__action-btn__label__title">
608
+ {serializationState.config ? 'Edit Config' : 'Add Config'}
609
+ </div>
610
+ </button>
349
611
  </div>
350
- </button>
351
- </div>
352
- <div
353
- className={clsx('panel__content__form__section__toggler')}
354
- onClick={toggleChecked}
355
- >
356
- <button
357
- className={clsx('panel__content__form__section__toggler__btn', {
358
- 'panel__content__form__section__toggler__btn--toggled':
359
- graphFetchState.isChecked,
360
- })}
612
+ )}
613
+ <div
614
+ className={clsx('panel__content__form__section__toggler')}
615
+ onClick={toggleChecked}
361
616
  >
362
- {graphFetchState.isChecked ? <CheckSquareIcon /> : <SquareIcon />}
363
- </button>
364
- <div className="panel__content__form__section__toggler__prompt">
365
- Check graph fetch
366
- </div>
367
- <div className="query-builder-graph-fetch-tree__toolbar__hint-icon">
368
- <InfoCircleIcon title="With this enabled, while executing, violations of constraints will reported as part of the result, rather than causing a failure" />
617
+ <button
618
+ className={clsx('panel__content__form__section__toggler__btn', {
619
+ 'panel__content__form__section__toggler__btn--toggled':
620
+ graphFetchState.isChecked,
621
+ })}
622
+ >
623
+ {graphFetchState.isChecked ? (
624
+ <CheckSquareIcon />
625
+ ) : (
626
+ <SquareIcon />
627
+ )}
628
+ </button>
629
+ <div className="panel__content__form__section__toggler__prompt">
630
+ Check graph fetch
631
+ </div>
632
+ <div className="query-builder-graph-fetch-tree__toolbar__hint-icon">
633
+ <InfoCircleIcon title="With this enabled, while executing, violations of constraints will reported as part of the result, rather than causing a failure" />
634
+ </div>
369
635
  </div>
370
636
  </div>
371
637
  </div>
372
638
  <div className="query-builder-graph-fetch-tree__container">
373
- {pureSerializationState.configModal && (
374
- <PureSerializationConfigModal
375
- pureSerializationState={pureSerializationState}
376
- graphFetchState={graphFetchState}
377
- config={
378
- pureSerializationState.config ?? new PureSerializationConfig()
379
- }
380
- />
381
- )}
382
- <TreeView
383
- components={{
384
- TreeNodeContainer: QueryBuilderGraphFetchTreeNodeContainer,
385
- }}
386
- treeData={treeData}
387
- onNodeSelect={onNodeSelect}
388
- getChildNodes={getChildNodes}
389
- innerProps={{
390
- isReadOnly,
391
- removeNode,
392
- }}
393
- />
639
+ {serializationState instanceof GraphFetchPureSerializationState &&
640
+ serializationState.configModal && (
641
+ <PureSerializationConfigModal
642
+ pureSerializationState={serializationState}
643
+ graphFetchState={graphFetchState}
644
+ config={
645
+ serializationState.config ?? new PureSerializationConfig()
646
+ }
647
+ />
648
+ )}
649
+ <ResizablePanelGroup orientation="horizontal">
650
+ <ResizablePanel>
651
+ <div className="query-builder-graph-fetch-external-format__config-group">
652
+ <div className="query-builder-graph-fetch-external-format__config-group__header">
653
+ <div className="query-builder-graph-fetch-external-format__config-group__header__title">
654
+ Graph Fetch Tree
655
+ </div>
656
+ </div>
657
+ <div className="query-builder-graph-fetch-external-format__config-group__content">
658
+ <div className="query-builder-graph-fetch-external-format__config-group__item">
659
+ <TreeView
660
+ components={{
661
+ TreeNodeContainer:
662
+ QueryBuilderGraphFetchTreeNodeContainer,
663
+ }}
664
+ className="query-builder-graph-fetch-tree__container__tree"
665
+ treeData={treeData}
666
+ onNodeSelect={onNodeSelect}
667
+ getChildNodes={getChildNodes}
668
+ innerProps={{
669
+ isReadOnly,
670
+ removeNode,
671
+ }}
672
+ />
673
+ </div>
674
+ </div>
675
+ </div>
676
+ </ResizablePanel>
677
+ <ResizablePanelSplitter />
678
+ {serializationState instanceof
679
+ GraphFetchExternalFormatSerializationState &&
680
+ serializationState.treeData && (
681
+ <ResizablePanel>
682
+ <div className="query-builder-graph-fetch-external-format__config-group">
683
+ <div className="query-builder-graph-fetch-external-format__config-group__header">
684
+ <div className="query-builder-graph-fetch-external-format__config-group__header__title">
685
+ Externalize
686
+ </div>
687
+ </div>
688
+ <div className="query-builder-graph-fetch-external-format__config-group__content">
689
+ <div className="query-builder-graph-fetch-external-format_config-group__item">
690
+ <QueryBuilderGraphFetchExternalConfig
691
+ graphFetchState={graphFetchState}
692
+ serializationState={serializationState}
693
+ serializationTreeData={serializationState.treeData}
694
+ bindings={compatibleBindings}
695
+ isReadOnly={false}
696
+ />
697
+ </div>
698
+ </div>
699
+ </div>
700
+ </ResizablePanel>
701
+ )}
702
+ </ResizablePanelGroup>
394
703
  </div>
395
704
  </div>
396
705
  );
@@ -400,10 +709,9 @@ export const QueryBuilderGraphFetchTreeExplorer = observer(
400
709
  const QueryBuilderGraphFetchTreePanel = observer(
401
710
  (props: {
402
711
  graphFetchTreeState: QueryBuilderGraphFetchTreeState;
403
-
404
- pureSerializationState: GraphFetchPureSerializationState;
712
+ serializationState: GraphFetchSerializationState;
405
713
  }) => {
406
- const { graphFetchTreeState, pureSerializationState } = props;
714
+ const { graphFetchTreeState, serializationState } = props;
407
715
  const treeData = graphFetchTreeState.treeData;
408
716
 
409
717
  // Deep/Graph Fetch Tree
@@ -415,8 +723,18 @@ const QueryBuilderGraphFetchTreePanel = observer(
415
723
  const handleDrop = useCallback(
416
724
  (item: QueryBuilderExplorerTreeDragSource): void => {
417
725
  graphFetchTreeState.addProperty(item.node, { refreshTreeData: true });
726
+ // If serializationState is GraphFetchExternalFormatSerializationState, we should add this node to
727
+ // the external format serialization tree as well
728
+ if (
729
+ serializationState instanceof
730
+ GraphFetchExternalFormatSerializationState
731
+ ) {
732
+ serializationState.addProperty(deepClone(item.node), {
733
+ refreshTreeData: true,
734
+ });
735
+ }
418
736
  },
419
- [graphFetchTreeState],
737
+ [graphFetchTreeState, serializationState],
420
738
  );
421
739
  const [{ isDragOver }, dropTargetConnector] = useDrop<
422
740
  QueryBuilderExplorerTreeDragSource,
@@ -458,7 +776,7 @@ const QueryBuilderGraphFetchTreePanel = observer(
458
776
  {treeData && !isGraphFetchTreeDataEmpty(treeData) && (
459
777
  <QueryBuilderGraphFetchTreeExplorer
460
778
  graphFetchState={graphFetchTreeState}
461
- pureSerializationState={pureSerializationState}
779
+ serializationState={serializationState}
462
780
  treeData={treeData}
463
781
  isReadOnly={false}
464
782
  updateTreeData={updateTreeData}
@@ -478,11 +796,14 @@ export const QueryBuilderGraphFetchPanel = observer(
478
796
  graphFetchTreeState.queryBuilderState.textEditorState.openModal(
479
797
  QueryBuilderTextEditorMode.TEXT,
480
798
  );
481
- if (serializationState instanceof GraphFetchPureSerializationState) {
799
+ if (
800
+ serializationState instanceof GraphFetchPureSerializationState ||
801
+ serializationState instanceof GraphFetchExternalFormatSerializationState
802
+ ) {
482
803
  return (
483
804
  <QueryBuilderGraphFetchTreePanel
484
805
  graphFetchTreeState={graphFetchTreeState}
485
- pureSerializationState={serializationState}
806
+ serializationState={serializationState}
486
807
  />
487
808
  );
488
809
  }
@@ -48,6 +48,7 @@ import {
48
48
  import type { LambdaFunctionBuilderOption } from '../../QueryBuilderValueSpecificationBuilderHelper.js';
49
49
  import { appendGraphFetch } from './QueryBuilderGraphFetchTreeValueSpecificationBuilder.js';
50
50
  import {
51
+ deepClone,
51
52
  guaranteeNonNullable,
52
53
  hashArray,
53
54
  type Hashable,
@@ -190,6 +191,7 @@ export class GraphFetchExternalFormatSerializationState extends GraphFetchSerial
190
191
  targetBinding: observable,
191
192
  treeData: observable.ref,
192
193
  serializationContentType: computed,
194
+ setGraphFetchTree: action,
193
195
  });
194
196
  this.targetBinding = targetBinding;
195
197
  this.treeData = treeData;
@@ -203,6 +205,30 @@ export class GraphFetchExternalFormatSerializationState extends GraphFetchSerial
203
205
  this.treeData = val;
204
206
  }
205
207
 
208
+ addProperty(
209
+ node: QueryBuilderExplorerTreePropertyNodeData,
210
+ options?: {
211
+ refreshTreeData?: boolean;
212
+ },
213
+ ): void {
214
+ if (!this.treeData) {
215
+ this.queryBuilderGraphFetchTreeState.queryBuilderState.applicationStore.notificationService.notifyWarning(
216
+ `Can't add property: graph-fetch tree has not been properly initialized`,
217
+ );
218
+ return;
219
+ }
220
+ addQueryBuilderPropertyNode(
221
+ this.treeData,
222
+ this.queryBuilderGraphFetchTreeState.queryBuilderState.explorerState
223
+ .nonNullableTreeData,
224
+ node,
225
+ this.queryBuilderGraphFetchTreeState.queryBuilderState,
226
+ );
227
+ if (options?.refreshTreeData) {
228
+ this.setGraphFetchTree({ ...this.treeData });
229
+ }
230
+ }
231
+
206
232
  override getLabel(): string {
207
233
  return SERIALIZATION_TYPE.EXTERNAL_FORMAT;
208
234
  }
@@ -409,6 +435,14 @@ export class QueryBuilderGraphFetchTreeState
409
435
 
410
436
  fetchProperty(node: QueryBuilderExplorerTreePropertyNodeData): void {
411
437
  this.addProperty(node, { refreshTreeData: true });
438
+ if (
439
+ this.serializationState instanceof
440
+ GraphFetchExternalFormatSerializationState
441
+ ) {
442
+ this.serializationState.addProperty(deepClone(node), {
443
+ refreshTreeData: true,
444
+ });
445
+ }
412
446
  }
413
447
 
414
448
  fetchProperties(nodes: QueryBuilderExplorerTreePropertyNodeData[]): void {
@@ -175,8 +175,8 @@ export const appendGraphFetch = (
175
175
  `Can't build graph-fetch tree expression: preceding expression is not defined`,
176
176
  );
177
177
 
178
- const seriaizationState = graphFetchTreeState.serializationState;
179
- if (seriaizationState instanceof GraphFetchPureSerializationState) {
178
+ const serializationState = graphFetchTreeState.serializationState;
179
+ if (serializationState instanceof GraphFetchPureSerializationState) {
180
180
  // build graph-fetch tree
181
181
  if (
182
182
  graphFetchTreeState.treeData &&
@@ -201,9 +201,9 @@ export const appendGraphFetch = (
201
201
  graphFetchInstance,
202
202
  ];
203
203
  serializeFunction.parametersValues = [graphFetchFunc, graphFetchInstance];
204
- if (seriaizationState.config) {
204
+ if (serializationState.config) {
205
205
  const configFunction = buildPureSerializationConfig(
206
- seriaizationState.config as unknown as Record<PropertyKey, boolean>,
206
+ serializationState.config as unknown as Record<PropertyKey, boolean>,
207
207
  graphFetchTreeState.queryBuilderState.graphManagerState.graph,
208
208
  );
209
209
  serializeFunction.parametersValues.push(configFunction);
@@ -211,18 +211,18 @@ export const appendGraphFetch = (
211
211
  lambdaFunction.expressionSequence[0] = serializeFunction;
212
212
  }
213
213
  } else if (
214
- seriaizationState instanceof GraphFetchExternalFormatSerializationState
214
+ serializationState instanceof GraphFetchExternalFormatSerializationState
215
215
  ) {
216
216
  const externalizeFunction = new SimpleFunctionExpression(
217
217
  extractElementNameFromPath(QUERY_BUILDER_SUPPORTED_FUNCTIONS.EXTERNALIZE),
218
218
  );
219
219
  const mainGraphTree = graphFetchTreeState.treeData;
220
- const externalizeTree = seriaizationState.treeData;
220
+ const externalizeGraphFetchTreeData = serializationState.treeData;
221
221
  if (
222
222
  mainGraphTree &&
223
- externalizeTree &&
223
+ externalizeGraphFetchTreeData &&
224
224
  !isGraphFetchTreeDataEmpty(mainGraphTree) &&
225
- !isGraphFetchTreeDataEmpty(externalizeTree)
225
+ !isGraphFetchTreeDataEmpty(externalizeGraphFetchTreeData)
226
226
  ) {
227
227
  // 0th param
228
228
  const graphFetchInstance = new GraphFetchTreeInstanceValue();
@@ -244,12 +244,12 @@ export const appendGraphFetch = (
244
244
  const bindingInstance = new InstanceValue(Multiplicity.ONE, undefined);
245
245
  bindingInstance.values = [
246
246
  PackageableElementExplicitReference.create(
247
- seriaizationState.targetBinding,
247
+ serializationState.targetBinding,
248
248
  ),
249
249
  ];
250
250
  // 2nd parameter
251
251
  const xtGraphFetchInstance = new GraphFetchTreeInstanceValue();
252
- xtGraphFetchInstance.values = [externalizeTree.tree];
252
+ xtGraphFetchInstance.values = [externalizeGraphFetchTreeData.tree];
253
253
  // build externalize
254
254
  externalizeFunction.parametersValues = [
255
255
  graphFetchFunc,
@@ -260,7 +260,7 @@ export const appendGraphFetch = (
260
260
  }
261
261
  } else {
262
262
  throw new UnsupportedOperationError(
263
- `Unsupported serialization state ${seriaizationState.getLabel()}`,
263
+ `Unsupported serialization state ${serializationState.getLabel()}`,
264
264
  );
265
265
  }
266
266
  // build result set modifier: i.e. preview limit