@finos/legend-application-query 5.0.0 → 5.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 (148) hide show
  1. package/lib/components/QueryBuilderExplorerPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderExplorerPanel.js +3 -1
  3. package/lib/components/QueryBuilderExplorerPanel.js.map +1 -1
  4. package/lib/components/QueryBuilderFetchStructurePanel.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderFetchStructurePanel.js +39 -7
  6. package/lib/components/QueryBuilderFetchStructurePanel.js.map +1 -1
  7. package/lib/components/QueryBuilderFilterPanel.d.ts.map +1 -1
  8. package/lib/components/QueryBuilderFilterPanel.js +59 -15
  9. package/lib/components/QueryBuilderFilterPanel.js.map +1 -1
  10. package/lib/components/QueryBuilderFunctionsExplorerPanel.d.ts.map +1 -1
  11. package/lib/components/QueryBuilderFunctionsExplorerPanel.js.map +1 -1
  12. package/lib/components/QueryBuilderParameterPanel.d.ts.map +1 -1
  13. package/lib/components/QueryBuilderParameterPanel.js +3 -2
  14. package/lib/components/QueryBuilderParameterPanel.js.map +1 -1
  15. package/lib/components/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  16. package/lib/components/QueryBuilderPostFilterPanel.js +29 -7
  17. package/lib/components/QueryBuilderPostFilterPanel.js.map +1 -1
  18. package/lib/components/QueryBuilderProjectionPanel.d.ts.map +1 -1
  19. package/lib/components/QueryBuilderProjectionPanel.js +6 -2
  20. package/lib/components/QueryBuilderProjectionPanel.js.map +1 -1
  21. package/lib/components/QueryBuilderPropertySearchPanel.d.ts.map +1 -1
  22. package/lib/components/QueryBuilderPropertySearchPanel.js +3 -2
  23. package/lib/components/QueryBuilderPropertySearchPanel.js.map +1 -1
  24. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  25. package/lib/components/QueryBuilderResultPanel.js +13 -7
  26. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  27. package/lib/components/QueryBuilderSetupPanel.d.ts.map +1 -1
  28. package/lib/components/QueryBuilderSetupPanel.js +5 -3
  29. package/lib/components/QueryBuilderSetupPanel.js.map +1 -1
  30. package/lib/index.css +2 -2
  31. package/lib/index.css.map +1 -1
  32. package/lib/index.d.ts +2 -1
  33. package/lib/index.d.ts.map +1 -1
  34. package/lib/index.js +2 -1
  35. package/lib/index.js.map +1 -1
  36. package/lib/package.json +2 -3
  37. package/lib/stores/QueryBuilderExplorerState.d.ts +1 -1
  38. package/lib/stores/QueryBuilderExplorerState.d.ts.map +1 -1
  39. package/lib/stores/QueryBuilderExplorerState.js +13 -3
  40. package/lib/stores/QueryBuilderExplorerState.js.map +1 -1
  41. package/lib/stores/QueryBuilderFetchStructureState.d.ts +2 -1
  42. package/lib/stores/QueryBuilderFetchStructureState.d.ts.map +1 -1
  43. package/lib/stores/QueryBuilderFetchStructureState.js +2 -2
  44. package/lib/stores/QueryBuilderFetchStructureState.js.map +1 -1
  45. package/lib/stores/QueryBuilderFilterState.d.ts +11 -1
  46. package/lib/stores/QueryBuilderFilterState.d.ts.map +1 -1
  47. package/lib/stores/QueryBuilderFilterState.js +33 -2
  48. package/lib/stores/QueryBuilderFilterState.js.map +1 -1
  49. package/lib/stores/QueryBuilderOperatorLoader.d.ts +47 -0
  50. package/lib/stores/QueryBuilderOperatorLoader.d.ts.map +1 -0
  51. package/lib/stores/QueryBuilderOperatorLoader.js +94 -0
  52. package/lib/stores/QueryBuilderOperatorLoader.js.map +1 -0
  53. package/lib/stores/QueryBuilderOperatorsHelper.d.ts +1 -0
  54. package/lib/stores/QueryBuilderOperatorsHelper.d.ts.map +1 -1
  55. package/lib/stores/QueryBuilderOperatorsHelper.js +28 -1
  56. package/lib/stores/QueryBuilderOperatorsHelper.js.map +1 -1
  57. package/lib/stores/QueryBuilderPostFilterState.d.ts +5 -2
  58. package/lib/stores/QueryBuilderPostFilterState.d.ts.map +1 -1
  59. package/lib/stores/QueryBuilderPostFilterState.js +25 -1
  60. package/lib/stores/QueryBuilderPostFilterState.js.map +1 -1
  61. package/lib/stores/QueryBuilderPreviewDataHelper.d.ts +4 -3
  62. package/lib/stores/QueryBuilderPreviewDataHelper.d.ts.map +1 -1
  63. package/lib/stores/QueryBuilderPreviewDataHelper.js +77 -97
  64. package/lib/stores/QueryBuilderPreviewDataHelper.js.map +1 -1
  65. package/lib/stores/QueryBuilderProjectionState.d.ts +4 -2
  66. package/lib/stores/QueryBuilderProjectionState.d.ts.map +1 -1
  67. package/lib/stores/QueryBuilderProjectionState.js +24 -28
  68. package/lib/stores/QueryBuilderProjectionState.js.map +1 -1
  69. package/lib/stores/QueryBuilderPropertySearchPanelState.js +1 -1
  70. package/lib/stores/QueryBuilderPropertySearchPanelState.js.map +1 -1
  71. package/lib/stores/QueryBuilderResultState.d.ts +5 -2
  72. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  73. package/lib/stores/QueryBuilderResultState.js +19 -7
  74. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  75. package/lib/stores/QueryBuilderState.d.ts +2 -0
  76. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  77. package/lib/stores/QueryBuilderState.js +11 -58
  78. package/lib/stores/QueryBuilderState.js.map +1 -1
  79. package/lib/stores/QueryBuilderTestUtils.d.ts +24 -0
  80. package/lib/stores/QueryBuilderTestUtils.d.ts.map +1 -0
  81. package/lib/stores/QueryBuilderTestUtils.js +49 -0
  82. package/lib/stores/QueryBuilderTestUtils.js.map +1 -0
  83. package/lib/stores/QueryBuilderTypeaheadHelper.d.ts +24 -0
  84. package/lib/stores/QueryBuilderTypeaheadHelper.d.ts.map +1 -0
  85. package/lib/stores/QueryBuilderTypeaheadHelper.js +89 -0
  86. package/lib/stores/QueryBuilderTypeaheadHelper.js.map +1 -0
  87. package/lib/stores/QueryEditorStore.d.ts.map +1 -1
  88. package/lib/stores/QueryEditorStore.js +3 -7
  89. package/lib/stores/QueryEditorStore.js.map +1 -1
  90. package/lib/stores/filterOperators/QueryBuilderFilterOperator_Equal.d.ts.map +1 -1
  91. package/lib/stores/filterOperators/QueryBuilderFilterOperator_Equal.js +5 -31
  92. package/lib/stores/filterOperators/QueryBuilderFilterOperator_Equal.js.map +1 -1
  93. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThan.d.ts.map +1 -1
  94. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThan.js +5 -31
  95. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThan.js.map +1 -1
  96. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThanEqual.d.ts.map +1 -1
  97. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThanEqual.js +5 -31
  98. package/lib/stores/filterOperators/QueryBuilderFilterOperator_GreaterThanEqual.js.map +1 -1
  99. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThan.d.ts.map +1 -1
  100. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThan.js +5 -32
  101. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThan.js.map +1 -1
  102. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThanEqual.d.ts.map +1 -1
  103. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThanEqual.js +5 -31
  104. package/lib/stores/filterOperators/QueryBuilderFilterOperator_LessThanEqual.js.map +1 -1
  105. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_Equal.d.ts.map +1 -1
  106. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_Equal.js +3 -29
  107. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_Equal.js.map +1 -1
  108. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_GreaterThan.d.ts.map +1 -1
  109. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_GreaterThan.js +4 -30
  110. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_GreaterThan.js.map +1 -1
  111. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_LessThan.d.ts.map +1 -1
  112. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_LessThan.js +4 -30
  113. package/lib/stores/postFilterOperators/QueryBuilderPostFilterOperator_LessThan.js.map +1 -1
  114. package/package.json +9 -10
  115. package/src/components/QueryBuilderExplorerPanel.tsx +3 -1
  116. package/src/components/QueryBuilderFetchStructurePanel.tsx +47 -7
  117. package/src/components/QueryBuilderFilterPanel.tsx +101 -21
  118. package/src/components/QueryBuilderFunctionsExplorerPanel.tsx +1 -0
  119. package/src/components/QueryBuilderParameterPanel.tsx +3 -2
  120. package/src/components/QueryBuilderPostFilterPanel.tsx +53 -7
  121. package/src/components/QueryBuilderProjectionPanel.tsx +19 -2
  122. package/src/components/QueryBuilderPropertySearchPanel.tsx +2 -1
  123. package/src/components/QueryBuilderResultPanel.tsx +75 -46
  124. package/src/components/QueryBuilderSetupPanel.tsx +5 -3
  125. package/src/index.ts +2 -4
  126. package/src/stores/QueryBuilderExplorerState.ts +17 -3
  127. package/src/stores/QueryBuilderFetchStructureState.ts +9 -2
  128. package/src/stores/QueryBuilderFilterState.ts +50 -0
  129. package/src/stores/QueryBuilderOperatorLoader.ts +133 -0
  130. package/src/stores/QueryBuilderOperatorsHelper.ts +35 -0
  131. package/src/stores/QueryBuilderPostFilterState.ts +40 -1
  132. package/src/stores/QueryBuilderPreviewDataHelper.ts +122 -217
  133. package/src/stores/QueryBuilderProjectionState.ts +40 -42
  134. package/src/stores/QueryBuilderPropertySearchPanelState.ts +1 -1
  135. package/src/stores/QueryBuilderResultState.ts +27 -9
  136. package/src/stores/QueryBuilderState.ts +27 -94
  137. package/src/stores/QueryBuilderTestUtils.ts +93 -0
  138. package/src/stores/QueryBuilderTypeaheadHelper.ts +149 -0
  139. package/src/stores/QueryEditorStore.ts +11 -12
  140. package/src/stores/filterOperators/QueryBuilderFilterOperator_Equal.ts +6 -35
  141. package/src/stores/filterOperators/QueryBuilderFilterOperator_GreaterThan.ts +9 -35
  142. package/src/stores/filterOperators/QueryBuilderFilterOperator_GreaterThanEqual.ts +9 -35
  143. package/src/stores/filterOperators/QueryBuilderFilterOperator_LessThan.ts +9 -36
  144. package/src/stores/filterOperators/QueryBuilderFilterOperator_LessThanEqual.ts +9 -35
  145. package/src/stores/postFilterOperators/QueryBuilderPostFilterOperator_Equal.ts +6 -33
  146. package/src/stores/postFilterOperators/QueryBuilderPostFilterOperator_GreaterThan.ts +11 -36
  147. package/src/stores/postFilterOperators/QueryBuilderPostFilterOperator_LessThan.ts +11 -36
  148. package/tsconfig.json +3 -0
@@ -61,10 +61,18 @@ import {
61
61
  assertErrorThrown,
62
62
  guaranteeNonNullable,
63
63
  returnUndefOnError,
64
+ debounce,
64
65
  } from '@finos/legend-shared';
65
66
  import { flowResult } from 'mobx';
66
67
  import { observer } from 'mobx-react-lite';
67
- import { forwardRef, useCallback, useEffect, useRef, useState } from 'react';
68
+ import {
69
+ forwardRef,
70
+ useCallback,
71
+ useEffect,
72
+ useMemo,
73
+ useRef,
74
+ useState,
75
+ } from 'react';
68
76
  import {
69
77
  type DropTargetMonitor,
70
78
  useDragLayer,
@@ -74,7 +82,10 @@ import {
74
82
  import { getEmptyImage } from 'react-dnd-html5-backend';
75
83
  import { getColumnMultiplicity } from '../stores/postFilterOperators/QueryBuilderPostFilterOperatorHelper.js';
76
84
  import { QueryBuilderAggregateColumnState } from '../stores/QueryBuilderAggregationState.js';
77
- import { QUERY_BUILDER_GROUP_OPERATION } from '../stores/QueryBuilderOperatorsHelper.js';
85
+ import {
86
+ isTypeCompatibleWithConditionValueType,
87
+ QUERY_BUILDER_GROUP_OPERATION,
88
+ } from '../stores/QueryBuilderOperatorsHelper.js';
78
89
  import type { QueryBuilderPostFilterOperator } from '../stores/QueryBuilderPostFilterOperator.js';
79
90
  import {
80
91
  type QueryBuilderPostFilterTreeNodeData,
@@ -405,6 +416,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
405
416
  const { node, isPropertyDragOver } = props;
406
417
  const graph =
407
418
  node.condition.postFilterState.queryBuilderState.graphManagerState.graph;
419
+ const applicationStore = useApplicationStore();
408
420
  const changeOperator = (val: QueryBuilderPostFilterOperator) => (): void =>
409
421
  node.condition.changeOperator(val);
410
422
  const changeColumn = async (
@@ -421,9 +433,24 @@ const QueryBuilderPostFilterConditionEditor = observer(
421
433
  // Drag and Drop on filter condition value
422
434
  const handleDrop = useCallback(
423
435
  (item: QueryBuilderParameterDragSource): void => {
424
- node.condition.setValue(item.variable.parameter);
436
+ const parameterType =
437
+ item.variable.parameter.genericType?.value.rawType;
438
+ const conditionValueType = node.condition.columnState.getReturnType();
439
+ if (
440
+ conditionValueType &&
441
+ isTypeCompatibleWithConditionValueType(
442
+ parameterType,
443
+ conditionValueType,
444
+ )
445
+ ) {
446
+ node.condition.setValue(item.variable.parameter);
447
+ } else {
448
+ applicationStore.notifyWarning(
449
+ `Incompatible parameter type ${parameterType?.name}. ${parameterType?.name} is not compatible with type ${conditionValueType?.name}.`,
450
+ );
451
+ }
425
452
  },
426
- [node],
453
+ [applicationStore, node.condition],
427
454
  );
428
455
  const [{ isFilterValueDragOver }, dropConnector] = useDrop(
429
456
  () => ({
@@ -447,6 +474,26 @@ const QueryBuilderPostFilterConditionEditor = observer(
447
474
  node.condition.operator.getDefaultFilterConditionValue(node.condition),
448
475
  );
449
476
  };
477
+ const debouncedTypeaheadSearch = useMemo(
478
+ () =>
479
+ debounce(
480
+ (inputVal: string) => node.condition.handleTypeaheadSearch(),
481
+ 1000,
482
+ ),
483
+ [node],
484
+ );
485
+ const cleanUpReloadValues = (): void => {
486
+ node.condition.typeaheadSearchState.complete();
487
+ };
488
+ const changeValueSpecification = (val: ValueSpecification): void => {
489
+ node.condition.setValue(val);
490
+ };
491
+ const selectorConfig = {
492
+ values: node.condition.typeaheadSearchResults,
493
+ isLoading: node.condition.typeaheadSearchState.isInProgress,
494
+ reloadValues: debouncedTypeaheadSearch,
495
+ cleanUpReloadValues,
496
+ };
450
497
 
451
498
  return (
452
499
  <div className="query-builder-post-filter-tree__node__label__content dnd__overlay__container">
@@ -506,9 +553,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
506
553
  )}
507
554
  <BasicValueSpecificationEditor
508
555
  valueSpecification={node.condition.value}
509
- setValueSpecification={(val: ValueSpecification): void =>
510
- node.condition.setValue(val)
511
- }
556
+ setValueSpecification={changeValueSpecification}
512
557
  graph={graph}
513
558
  typeCheckOption={{
514
559
  expectedType: guaranteeNonNullable(
@@ -516,6 +561,7 @@ const QueryBuilderPostFilterConditionEditor = observer(
516
561
  ),
517
562
  }}
518
563
  resetValue={resetNode}
564
+ selectorConfig={selectorConfig}
519
565
  />
520
566
  </div>
521
567
  )}
@@ -28,6 +28,7 @@ import {
28
28
  ContextMenu,
29
29
  InputWithInlineValidation,
30
30
  SigmaIcon,
31
+ TimesCircleIcon,
31
32
  } from '@finos/legend-art';
32
33
  import {
33
34
  type QueryBuilderExplorerTreeDragSource,
@@ -588,6 +589,8 @@ export const QueryBuilderProjectionPanel = observer(
588
589
  },
589
590
  [queryBuilderState, projectionState],
590
591
  );
592
+
593
+ const isInvalidProjection = !projectionState.isValidProjectionState();
591
594
  const [{ isPropertyDragOver }, dropConnector] = useDrop(
592
595
  () => ({
593
596
  accept: [
@@ -620,10 +623,21 @@ export const QueryBuilderProjectionPanel = observer(
620
623
 
621
624
  return (
622
625
  <div
623
- className="panel__content dnd__overlay__container"
624
626
  ref={dropConnector}
627
+ className={`panel__content dnd__overlay__container`}
625
628
  >
629
+ {isInvalidProjection && (
630
+ <div className="query-builder__projection__container__error">
631
+ <div className="query-builder__projection__error__label">
632
+ <TimesCircleIcon className="query-builder__projection__error__icon" />
633
+ {projectionState.getValidationErrorMessage() ??
634
+ 'There is an error with the projection'}
635
+ </div>
636
+ </div>
637
+ )}
638
+
626
639
  <div className={clsx({ dnd__overlay: isPropertyDragOver })} />
640
+
627
641
  {!projectionColumns.length && (
628
642
  <BlankPanelPlaceholder
629
643
  placeholderText="Add a projection column"
@@ -633,7 +647,10 @@ export const QueryBuilderProjectionPanel = observer(
633
647
  {Boolean(projectionColumns.length) && (
634
648
  <div
635
649
  data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PROJECTION}
636
- className="query-builder__projection__columns"
650
+ className={`query-builder__projection__columns ${clsx({
651
+ ['query-builder__projection__columns--with-error']:
652
+ isInvalidProjection,
653
+ })} `}
637
654
  >
638
655
  <ProjectionColumnDragLayer />
639
656
  {projectionColumns.map((projectionColumnState) => (
@@ -169,6 +169,7 @@ const QueryBuilderTreeNodeViewer = observer(
169
169
  ),
170
170
  );
171
171
  if (
172
+ propertyTreeNodeData &&
172
173
  !(propertyTreeNodeData.type instanceof Class) &&
173
174
  propertyTreeNodeData.mappingData.mapped
174
175
  ) {
@@ -192,7 +193,7 @@ const QueryBuilderTreeNodeViewer = observer(
192
193
  return (
193
194
  <div>
194
195
  <div
195
- className="tree-view__node__container"
196
+ className="tree-view__node__container query-builder-property-search-panel__node__container"
196
197
  ref={dragConnector}
197
198
  style={{
198
199
  paddingLeft: `${(level - 1) * stepPaddingInRem + 0.5}rem`,
@@ -25,6 +25,7 @@ import {
25
25
  CaretDownIcon,
26
26
  ContextMenu,
27
27
  clsx,
28
+ PauseCircleIcon,
28
29
  } from '@finos/legend-art';
29
30
  import { observer } from 'mobx-react-lite';
30
31
  import { flowResult } from 'mobx';
@@ -413,27 +414,32 @@ export const QueryBuilderResultPanel = observer(
413
414
  ],
414
415
  });
415
416
  };
416
- const execute = (): void => {
417
+ const runQuery = (): void => {
417
418
  if (queryParametersState.parameterStates.length) {
418
419
  queryParametersState.parameterValuesEditorState.open(
419
420
  (): Promise<void> =>
420
- flowResult(resultState.execute()).catch(
421
+ flowResult(resultState.runQuery()).catch(
421
422
  applicationStore.alertUnhandledError,
422
423
  ),
423
424
  PARAMETER_SUBMIT_ACTION.EXECUTE,
424
425
  );
425
426
  } else {
426
- flowResult(resultState.execute()).catch(
427
+ flowResult(resultState.runQuery()).catch(
427
428
  applicationStore.alertUnhandledError,
428
429
  );
429
430
  }
430
431
  };
432
+ const cancelQuery = (): void => {
433
+ resultState.setIsRunningQuery(false);
434
+ queryBuilderState.resultState.setQueryRunPromise(undefined);
435
+ };
431
436
  const generatePlan = applicationStore.guardUnhandledError(() =>
432
437
  flowResult(resultState.generatePlan(false)),
433
438
  );
434
439
  const debugPlanGeneration = applicationStore.guardUnhandledError(() =>
435
440
  flowResult(resultState.generatePlan(true)),
436
441
  );
442
+
437
443
  const changeLimit: React.ChangeEventHandler<HTMLInputElement> = (event) => {
438
444
  const val = event.target.value;
439
445
  queryBuilderState.resultState.setPreviewLimit(
@@ -469,52 +475,70 @@ export const QueryBuilderResultPanel = observer(
469
475
  type="number"
470
476
  value={resultState.previewLimit}
471
477
  onChange={changeLimit}
478
+ disabled={!queryBuilderState.isValidQueryBuilderState()}
472
479
  />
473
480
  </div>
474
481
  )}
475
- <button
476
- className="query-builder__result__execute-btn"
477
- onClick={execute}
478
- disabled={
479
- resultState.isExecutingQuery || resultState.isGeneratingPlan
480
- }
481
- tabIndex={-1}
482
- >
483
- <div className="query-builder__result__execute-btn__label">
484
- <PlayIcon className="query-builder__result__execute-btn__label__icon" />
485
- <div className="query-builder__result__execute-btn__label__title">
486
- Execute
482
+ {resultState.isRunningQuery ? (
483
+ <button
484
+ className="query-builder__result__stop-btn"
485
+ onClick={cancelQuery}
486
+ tabIndex={-1}
487
+ disabled={!queryBuilderState.isValidQueryBuilderState()}
488
+ >
489
+ <div className="btn--dark btn--caution query-builder__result__stop-btn__label">
490
+ <PauseCircleIcon className="query-builder__result__stop-btn__label__icon" />
491
+ <div className="query-builder__result__stop-btn__label__title">
492
+ Stop
493
+ </div>
487
494
  </div>
488
- </div>
489
- <DropdownMenu
490
- className="query-builder__result__execute-btn__dropdown-btn"
491
- disabled={
492
- resultState.isExecutingQuery || resultState.isGeneratingPlan
493
- }
494
- content={
495
- <MenuContent>
496
- <MenuContentItem
497
- className="query-builder__result__execute-btn__option"
498
- onClick={generatePlan}
499
- >
500
- Generate Plan
501
- </MenuContentItem>
502
- <MenuContentItem
503
- className="query-builder__result__execute-btn__option"
504
- onClick={debugPlanGeneration}
505
- >
506
- Debug
507
- </MenuContentItem>
508
- </MenuContent>
495
+ </button>
496
+ ) : (
497
+ <button
498
+ className="query-builder__result__execute-btn"
499
+ onClick={runQuery}
500
+ tabIndex={-1}
501
+ title={
502
+ !queryBuilderState.isValidQueryBuilderState()
503
+ ? 'Query is not valid'
504
+ : undefined
509
505
  }
510
- menuProps={{
511
- anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
512
- transformOrigin: { vertical: 'top', horizontal: 'right' },
513
- }}
506
+ disabled={!queryBuilderState.isValidQueryBuilderState()}
514
507
  >
515
- <CaretDownIcon />
516
- </DropdownMenu>
517
- </button>
508
+ <div className="query-builder__result__execute-btn__label">
509
+ <PlayIcon className="query-builder__result__execute-btn__label__icon" />
510
+ <div className="query-builder__result__execute-btn__label__title">
511
+ Run Query
512
+ </div>
513
+ </div>
514
+ <DropdownMenu
515
+ className="query-builder__result__execute-btn__dropdown-btn"
516
+ disabled={resultState.isGeneratingPlan}
517
+ content={
518
+ <MenuContent>
519
+ <MenuContentItem
520
+ className="query-builder__result__execute-btn__option"
521
+ onClick={generatePlan}
522
+ >
523
+ Generate Plan
524
+ </MenuContentItem>
525
+ <MenuContentItem
526
+ className="query-builder__result__execute-btn__option"
527
+ onClick={debugPlanGeneration}
528
+ >
529
+ Debug
530
+ </MenuContentItem>
531
+ </MenuContent>
532
+ }
533
+ menuProps={{
534
+ anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
535
+ transformOrigin: { vertical: 'top', horizontal: 'right' },
536
+ }}
537
+ >
538
+ <CaretDownIcon />
539
+ </DropdownMenu>
540
+ </button>
541
+ )}
518
542
  <DropdownMenu
519
543
  className="query-builder__result__export__dropdown"
520
544
  content={
@@ -542,19 +566,24 @@ export const QueryBuilderResultPanel = observer(
542
566
  className="query-builder__result__export__dropdown__label"
543
567
  tabIndex={-1}
544
568
  title="Export"
569
+ disabled={!queryBuilderState.isValidQueryBuilderState()}
545
570
  >
546
571
  Export
547
572
  </button>
548
- <div className="query-builder__result__export__dropdown__trigger">
573
+ <button
574
+ className="query-builder__result__export__dropdown__trigger"
575
+ tabIndex={-1}
576
+ disabled={!queryBuilderState.isValidQueryBuilderState()}
577
+ >
549
578
  <CaretDownIcon />
550
- </div>
579
+ </button>
551
580
  </DropdownMenu>
552
581
  </div>
553
582
  </div>
554
583
  <div className="panel__content">
555
584
  <PanelLoadingIndicator
556
585
  isLoading={
557
- resultState.isExecutingQuery ||
586
+ resultState.isRunningQuery ||
558
587
  resultState.isGeneratingPlan ||
559
588
  resultState.exportDataState.isInProgress
560
589
  }
@@ -46,7 +46,7 @@ import {
46
46
  } from '@finos/legend-graph';
47
47
  import {
48
48
  type PackageableElementOption,
49
- getPackageableElementOptionalFormatter,
49
+ getPackageableElementOptionFormatter,
50
50
  buildElementOption,
51
51
  } from '@finos/legend-application';
52
52
  import { MilestoningParametersEditor } from './QueryBuilderMilestoneEditor.js';
@@ -269,8 +269,9 @@ export const QueryBuilderSetupPanel = observer(
269
269
  darkMode={true}
270
270
  disabled={!isQuerySupported || querySetupState.classIsReadOnly}
271
271
  filterOption={elementFilterOption}
272
- formatOptionLabel={getPackageableElementOptionalFormatter({
272
+ formatOptionLabel={getPackageableElementOptionFormatter({
273
273
  darkMode: true,
274
+ graphManagerState: queryBuilderState.graphManagerState,
274
275
  })}
275
276
  />
276
277
  <button
@@ -304,8 +305,9 @@ export const QueryBuilderSetupPanel = observer(
304
305
  value={selectedMappingOption}
305
306
  darkMode={true}
306
307
  filterOption={elementFilterOption}
307
- formatOptionLabel={getPackageableElementOptionalFormatter({
308
+ formatOptionLabel={getPackageableElementOptionFormatter({
308
309
  darkMode: true,
310
+ graphManagerState: queryBuilderState.graphManagerState,
309
311
  })}
310
312
  />
311
313
  </div>
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@
15
15
  */
16
16
 
17
17
  export * from './application/LegendQuery.js';
18
+ export * from './components/LegendQueryBaseStoreProvider.js';
19
+ export * from './stores/LegendQueryBaseStore.js';
18
20
 
19
21
  export { QUERY_BUILDER_TEST_ID } from './components/QueryBuilder_TestID.js';
20
22
 
@@ -26,10 +28,6 @@ export {
26
28
  useQueryEditorStore,
27
29
  } from './components/QueryEditorStoreProvider.js';
28
30
  export { useQuerySetupStore } from './components/QuerySetupStoreProvider.js';
29
- export {
30
- useLegendQueryBaseStore,
31
- useLegendQueryApplicationStore,
32
- } from './components/LegendQueryBaseStoreProvider.js';
33
31
  export { QueryEditor } from './components/QueryEditor.js';
34
32
 
35
33
  export { LegendQueryPluginManager } from './application/LegendQueryPluginManager.js';
@@ -50,6 +50,8 @@ import {
50
50
  EntityMappedProperty,
51
51
  Enumeration,
52
52
  DerivedProperty,
53
+ Property,
54
+ Association,
53
55
  } from '@finos/legend-graph';
54
56
  import type { QueryBuilderState } from './QueryBuilderState.js';
55
57
  import {
@@ -342,7 +344,7 @@ export const getQueryBuilderPropertyNodeData = (
342
344
  property: AbstractProperty,
343
345
  parentNode: QueryBuilderExplorerTreeNodeData,
344
346
  modelCoverageAnalysisResult: MappingModelCoverageAnalysisResult,
345
- ): QueryBuilderExplorerTreePropertyNodeData => {
347
+ ): QueryBuilderExplorerTreePropertyNodeData | undefined => {
346
348
  const mappingNodeData = generatePropertyNodeMappingData(
347
349
  property,
348
350
  parentNode.mappingData,
@@ -353,6 +355,16 @@ export const getQueryBuilderPropertyNodeData = (
353
355
  parentNode.isPartOfDerivedPropertyBranch ||
354
356
  (parentNode instanceof QueryBuilderExplorerTreePropertyNodeData &&
355
357
  parentNode.property instanceof DerivedProperty);
358
+ if (
359
+ property instanceof Property &&
360
+ parentNode instanceof QueryBuilderExplorerTreePropertyNodeData &&
361
+ parentNode.property instanceof Property &&
362
+ property._OWNER instanceof Association &&
363
+ parentNode.property._OWNER instanceof Association &&
364
+ parentNode.property._OWNER === property._OWNER
365
+ ) {
366
+ return undefined;
367
+ }
356
368
  const propertyNode = new QueryBuilderExplorerTreePropertyNodeData(
357
369
  `${
358
370
  parentNode instanceof QueryBuilderExplorerTreeRootNodeData
@@ -455,8 +467,10 @@ const getQueryBuilderTreeData = (
455
467
  treeRootNode,
456
468
  modelCoverageAnalysisResult,
457
469
  );
458
- addUniqueEntry(treeRootNode.childrenIds, propertyTreeNodeData.id);
459
- nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
470
+ if (propertyTreeNodeData) {
471
+ addUniqueEntry(treeRootNode.childrenIds, propertyTreeNodeData.id);
472
+ nodes.set(propertyTreeNodeData.id, propertyTreeNodeData);
473
+ }
460
474
  });
461
475
  rootClass._subclasses.forEach((subclass) => {
462
476
  const subTypeTreeNodeData = getQueryBuilderSubTypeNodeData(
@@ -18,6 +18,7 @@ import { action, makeAutoObservable } from 'mobx';
18
18
  import type { QueryBuilderState } from './QueryBuilderState.js';
19
19
  import { QueryBuilderGraphFetchTreeState } from './QueryBuilderGraphFetchTreeState.js';
20
20
  import { QueryBuilderProjectionState } from './QueryBuilderProjectionState.js';
21
+ import type { QueryBuilderAggregateOperator } from './QueryBuilderAggregationState.js';
21
22
 
22
23
  export enum FETCH_STRUCTURE_MODE {
23
24
  PROJECTION = 'PROJECTION',
@@ -30,7 +31,10 @@ export class QueryBuilderFetchStructureState {
30
31
  projectionState: QueryBuilderProjectionState;
31
32
  graphFetchTreeState: QueryBuilderGraphFetchTreeState;
32
33
 
33
- constructor(queryBuilderState: QueryBuilderState) {
34
+ constructor(
35
+ queryBuilderState: QueryBuilderState,
36
+ operators: QueryBuilderAggregateOperator[],
37
+ ) {
34
38
  makeAutoObservable(this, {
35
39
  queryBuilderState: false,
36
40
  setFetchStructureMode: action,
@@ -39,7 +43,10 @@ export class QueryBuilderFetchStructureState {
39
43
  this.queryBuilderState = queryBuilderState;
40
44
  // TODO: we probably should modularize this a bit better
41
45
  // See https://github.com/finos/legend-studio/issues/731
42
- this.projectionState = new QueryBuilderProjectionState(queryBuilderState);
46
+ this.projectionState = new QueryBuilderProjectionState(
47
+ queryBuilderState,
48
+ operators,
49
+ );
43
50
  this.graphFetchTreeState = new QueryBuilderGraphFetchTreeState(
44
51
  queryBuilderState,
45
52
  );
@@ -20,9 +20,11 @@ import {
20
20
  makeAutoObservable,
21
21
  makeObservable,
22
22
  observable,
23
+ flow,
23
24
  } from 'mobx';
24
25
  import type { TreeNodeData, TreeData } from '@finos/legend-art';
25
26
  import {
27
+ type GeneratorFn,
26
28
  assertTrue,
27
29
  getNullableFirstElement,
28
30
  guaranteeNonNullable,
@@ -34,11 +36,13 @@ import {
34
36
  deleteEntry,
35
37
  assertErrorThrown,
36
38
  filterByType,
39
+ ActionState,
37
40
  } from '@finos/legend-shared';
38
41
  import type { QueryBuilderExplorerTreeDragSource } from './QueryBuilderExplorerState.js';
39
42
  import { QueryBuilderPropertyExpressionState } from './QueryBuilderPropertyEditorState.js';
40
43
  import type { QueryBuilderState } from './QueryBuilderState.js';
41
44
  import {
45
+ type ExecutionResult,
42
46
  type AbstractPropertyExpression,
43
47
  type ValueSpecification,
44
48
  extractElementNameFromPath,
@@ -55,6 +59,12 @@ import {
55
59
  fromGroupOperation,
56
60
  QUERY_BUILDER_GROUP_OPERATION,
57
61
  } from './QueryBuilderOperatorsHelper.js';
62
+ import type { QueryBuilderProjectionColumnDragSource } from './QueryBuilderProjectionState.js';
63
+ import {
64
+ buildPropertyTypeAheadQuery,
65
+ buildTypeAheadOptions,
66
+ performTypeAhead,
67
+ } from './QueryBuilderTypeaheadHelper.js';
58
68
 
59
69
  export abstract class QueryBuilderFilterOperator {
60
70
  readonly uuid = uuid();
@@ -95,6 +105,7 @@ export interface QueryBuilderFilterConditionDragSource {
95
105
 
96
106
  export type QueryBuilderFilterDropTarget =
97
107
  | QueryBuilderExplorerTreeDragSource
108
+ | QueryBuilderProjectionColumnDragSource
98
109
  | QueryBuilderFilterConditionDragSource;
99
110
  export type QueryBuilderFilterConditionRearrangeDropTarget =
100
111
  QueryBuilderFilterConditionDragSource;
@@ -105,6 +116,8 @@ export class FilterConditionState {
105
116
  operator!: QueryBuilderFilterOperator;
106
117
  value?: ValueSpecification | undefined;
107
118
  existsLambdaParamNames: string[] = [];
119
+ typeaheadSearchResults: string[] | undefined;
120
+ typeaheadSearchState = ActionState.create();
108
121
 
109
122
  constructor(
110
123
  filterState: QueryBuilderFilterState,
@@ -117,7 +130,10 @@ export class FilterConditionState {
117
130
  changeOperator: action,
118
131
  setOperator: action,
119
132
  setValue: action,
133
+ typeaheadSearchResults: observable,
134
+ typeaheadSearchState: observable,
120
135
  addExistsLambdaParamNames: action,
136
+ handleTypeaheadSearch: flow,
121
137
  });
122
138
 
123
139
  this.filterState = filterState;
@@ -141,6 +157,35 @@ export class FilterConditionState {
141
157
  );
142
158
  }
143
159
 
160
+ *handleTypeaheadSearch(): GeneratorFn<void> {
161
+ try {
162
+ this.typeaheadSearchState.inProgress();
163
+ this.typeaheadSearchResults = undefined;
164
+ if (performTypeAhead(this.value)) {
165
+ const builderState = buildPropertyTypeAheadQuery(
166
+ this.filterState.queryBuilderState,
167
+ this.propertyExpressionState.propertyExpression,
168
+ this.value,
169
+ );
170
+ const result =
171
+ (yield builderState.graphManagerState.graphManager.executeMapping(
172
+ builderState.resultState.buildExecutionRawLambda(),
173
+ guaranteeNonNullable(builderState.querySetupState.mapping),
174
+ guaranteeNonNullable(builderState.querySetupState.runtimeValue),
175
+ builderState.graphManagerState.graph,
176
+ )) as ExecutionResult;
177
+ this.typeaheadSearchResults = buildTypeAheadOptions(result);
178
+ }
179
+ this.typeaheadSearchState.pass();
180
+ } catch (error) {
181
+ assertErrorThrown(error);
182
+ this.typeaheadSearchResults = [];
183
+ this.typeaheadSearchState.fail();
184
+ } finally {
185
+ this.typeaheadSearchState.complete();
186
+ }
187
+ }
188
+
144
189
  changeProperty(propertyExpression: AbstractPropertyExpression): void {
145
190
  try {
146
191
  // first, check if the new property is supported
@@ -407,6 +452,11 @@ export class QueryBuilderFilterState
407
452
  isRearrangingConditions = false;
408
453
  operators: QueryBuilderFilterOperator[] = [];
409
454
  private _suppressClickawayEventListener = false;
455
+ /**
456
+ * This flag is for turning on/off dnd from projection panel to filter panel,
457
+ * and will be leveraged when the concepts of workflows are introduced into query builder.
458
+ */
459
+ allowDnDProjectionToFilter = true;
410
460
 
411
461
  constructor(
412
462
  queryBuilderState: QueryBuilderState,