@finos/legend-query-builder 4.7.4 → 4.9.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 (87) hide show
  1. package/lib/components/QueryBuilderConstantExpressionPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderConstantExpressionPanel.js +27 -8
  3. package/lib/components/QueryBuilderConstantExpressionPanel.js.map +1 -1
  4. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderResultPanel.js +11 -5
  6. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  7. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
  8. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +5 -3
  9. package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
  10. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  11. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +8 -1
  12. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  13. package/lib/components/shared/LambdaEditor.d.ts +1 -11
  14. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  15. package/lib/components/shared/LambdaEditor.js +11 -4
  16. package/lib/components/shared/LambdaEditor.js.map +1 -1
  17. package/lib/index.css +2 -2
  18. package/lib/index.css.map +1 -1
  19. package/lib/package.json +1 -1
  20. package/lib/stores/QueryBuilderConstantsState.d.ts +3 -1
  21. package/lib/stores/QueryBuilderConstantsState.d.ts.map +1 -1
  22. package/lib/stores/QueryBuilderConstantsState.js +27 -3
  23. package/lib/stores/QueryBuilderConstantsState.js.map +1 -1
  24. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  25. package/lib/stores/QueryBuilderState.js +1 -0
  26. package/lib/stores/QueryBuilderState.js.map +1 -1
  27. package/lib/stores/QueryBuilderTypeaheadHelper.d.ts +1 -1
  28. package/lib/stores/QueryBuilderTypeaheadHelper.d.ts.map +1 -1
  29. package/lib/stores/QueryBuilderTypeaheadHelper.js +190 -30
  30. package/lib/stores/QueryBuilderTypeaheadHelper.js.map +1 -1
  31. package/lib/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.d.ts +1 -0
  32. package/lib/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.d.ts.map +1 -1
  33. package/lib/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.js.map +1 -1
  34. package/lib/stores/fetch-structure/QueryBuilderFetchStructureState.d.ts +1 -0
  35. package/lib/stores/fetch-structure/QueryBuilderFetchStructureState.d.ts.map +1 -1
  36. package/lib/stores/fetch-structure/QueryBuilderFetchStructureState.js +4 -0
  37. package/lib/stores/fetch-structure/QueryBuilderFetchStructureState.js.map +1 -1
  38. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.d.ts +1 -0
  39. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.d.ts.map +1 -1
  40. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js +3 -0
  41. package/lib/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.js.map +1 -1
  42. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts +2 -0
  43. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.d.ts.map +1 -1
  44. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js +25 -1
  45. package/lib/stores/fetch-structure/tds/QueryBuilderTDSState.js.map +1 -1
  46. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts +3 -1
  47. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.d.ts.map +1 -1
  48. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js +6 -0
  49. package/lib/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.js.map +1 -1
  50. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.d.ts +9 -2
  51. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.d.ts.map +1 -1
  52. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js +40 -6
  53. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js.map +1 -1
  54. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.d.ts +1 -0
  55. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.d.ts.map +1 -1
  56. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js +8 -1
  57. package/lib/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.js.map +1 -1
  58. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.d.ts +1 -0
  59. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.d.ts.map +1 -1
  60. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js +7 -1
  61. package/lib/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.js.map +1 -1
  62. package/lib/stores/milestoning/QueryBuilderMilestoningImplementation.d.ts +4 -0
  63. package/lib/stores/milestoning/QueryBuilderMilestoningImplementation.d.ts.map +1 -1
  64. package/lib/stores/milestoning/QueryBuilderMilestoningImplementation.js.map +1 -1
  65. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.d.ts +1 -0
  66. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.d.ts.map +1 -1
  67. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js +7 -1
  68. package/lib/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.js.map +1 -1
  69. package/package.json +3 -3
  70. package/src/components/QueryBuilderConstantExpressionPanel.tsx +85 -24
  71. package/src/components/QueryBuilderResultPanel.tsx +17 -13
  72. package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +11 -1
  73. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +12 -1
  74. package/src/components/shared/LambdaEditor.tsx +15 -2
  75. package/src/stores/QueryBuilderConstantsState.ts +44 -1
  76. package/src/stores/QueryBuilderState.ts +1 -0
  77. package/src/stores/QueryBuilderTypeaheadHelper.ts +382 -54
  78. package/src/stores/fetch-structure/QueryBuilderFetchStructureImplementationState.ts +1 -0
  79. package/src/stores/fetch-structure/QueryBuilderFetchStructureState.ts +5 -0
  80. package/src/stores/fetch-structure/graph-fetch/QueryBuilderGraphFetchTreeState.ts +4 -0
  81. package/src/stores/fetch-structure/tds/QueryBuilderTDSState.ts +43 -1
  82. package/src/stores/fetch-structure/tds/post-filter/QueryBuilderPostFilterState.ts +11 -0
  83. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionColumnState.ts +66 -9
  84. package/src/stores/milestoning/QueryBuilderBitemporalMilestoningImplementation.ts +14 -0
  85. package/src/stores/milestoning/QueryBuilderBusinessTemporalMilestoningImplementation.ts +13 -0
  86. package/src/stores/milestoning/QueryBuilderMilestoningImplementation.ts +7 -0
  87. package/src/stores/milestoning/QueryBuilderProcessingTemporalMilestoningImplementation.ts +13 -0
@@ -25,6 +25,7 @@ import {
25
25
  CustomSelectorInput,
26
26
  Dialog,
27
27
  InfoCircleIcon,
28
+ InputWithInlineValidation,
28
29
  Modal,
29
30
  ModalBody,
30
31
  ModalFooter,
@@ -42,10 +43,7 @@ import {
42
43
  Multiplicity,
43
44
  isValidIdentifier,
44
45
  } from '@finos/legend-graph';
45
- import {
46
- debounce,
47
- generateEnumerableNameFromToken,
48
- } from '@finos/legend-shared';
46
+ import { generateEnumerableNameFromToken } from '@finos/legend-shared';
49
47
  import { observer } from 'mobx-react-lite';
50
48
  import { DEFAULT_CONSTANT_VARIABLE_NAME } from '../stores/QueryBuilderConfig.js';
51
49
  import type { QueryBuilderState } from '../stores/QueryBuilderState.js';
@@ -58,10 +56,11 @@ import { buildDefaultInstanceValue } from '../stores/shared/ValueSpecificationEd
58
56
  import { BasicValueSpecificationEditor } from './shared/BasicValueSpecificationEditor.js';
59
57
  import { QUERY_BUILDER_TEST_ID } from '../__lib__/QueryBuilderTesting.js';
60
58
  import { QUERY_BUILDER_DOCUMENTATION_KEY } from '../__lib__/QueryBuilderDocumentation.js';
61
- import React, { useMemo, useState } from 'react';
59
+ import React, { useEffect, useState } from 'react';
62
60
  import { variableExpression_setName } from '../stores/shared/ValueSpecificationModifierHelper.js';
63
- import { LambdaEditor_PopUp } from './shared/LambdaEditor.js';
61
+ import { LambdaEditor } from './shared/LambdaEditor.js';
64
62
  import { VariableViewer } from './shared/QueryBuilderVariableSelector.js';
63
+ import { flowResult } from 'mobx';
65
64
 
66
65
  // NOTE: We currently only allow constant variables for primitive types of multiplicity ONE.
67
66
  // This is why we don't show multiplicity in the editor.
@@ -219,26 +218,88 @@ const QuerryBuilderCalculatedConstantExpressionEditor = observer(
219
218
  const { constantState } = props;
220
219
  const queryBuilderState = constantState.queryBuilderState;
221
220
  const lambdaState = constantState.lambdaState;
222
- const closePopUp = (): void =>
221
+ const close = (): void => {
223
222
  queryBuilderState.constantState.setSelectedConstant(undefined);
224
- const debouncedTransformStringToLambda = useMemo(
225
- () =>
226
- debounce(() => lambdaState.convertLambdaGrammarStringToObject(), 1000),
227
- [lambdaState],
228
- );
229
- const canDrop = true;
223
+ flowResult(constantState.updateVariableExpressionType()).catch(
224
+ queryBuilderState.applicationStore.alertUnhandledError,
225
+ );
226
+ };
227
+ const applicationStore = queryBuilderState.applicationStore;
228
+ const changeConstantName: React.ChangeEventHandler<HTMLInputElement> = (
229
+ event,
230
+ ) => {
231
+ variableExpression_setName(constantState.variable, event.target.value);
232
+ };
233
+ useEffect(() => {
234
+ flowResult(
235
+ lambdaState.convertLambdaObjectToGrammarString({
236
+ pretty: true,
237
+ }),
238
+ ).catch(applicationStore.alertUnhandledError);
239
+ }, [applicationStore, lambdaState]);
230
240
  return (
231
- <>
232
- <div className="lambda-editor" />
233
- <LambdaEditor_PopUp
234
- title={`Edit Constant ${constantState.variable.name}`}
235
- className={clsx({ 'lambda-editor--dnd-match': canDrop })}
236
- disabled={false}
237
- lambdaEditorState={lambdaState}
238
- transformStringToLambda={debouncedTransformStringToLambda}
239
- onClose={closePopUp}
240
- />
241
- </>
241
+ <Dialog
242
+ open={true}
243
+ onClose={close}
244
+ classes={{
245
+ root: 'editor-modal__root-container',
246
+ container: 'editor-modal__container',
247
+ paper: 'editor-modal__content',
248
+ }}
249
+ >
250
+ <Modal
251
+ darkMode={true}
252
+ className={clsx('editor-modal query-builder__constants__modal', {
253
+ 'query-builder__constants__modal--has-error': Boolean(
254
+ lambdaState.parserError,
255
+ ),
256
+ })}
257
+ >
258
+ <ModalHeader>
259
+ <div className="modal__title">Update Calculated Constants</div>
260
+ {lambdaState.parserError && (
261
+ <div className="modal__title__error-badge">
262
+ Failed to parse query
263
+ </div>
264
+ )}
265
+ </ModalHeader>
266
+ <ModalBody>
267
+ <div
268
+ className={clsx('query-builder__constants__modal__content', {
269
+ backdrop__element: Boolean(lambdaState.parserError),
270
+ })}
271
+ >
272
+ <div className="query-builder__constants__modal__name">
273
+ <InputWithInlineValidation
274
+ className="query-builder__constants__modal__name__input input--dark"
275
+ spellCheck={false}
276
+ value={constantState.variable.name}
277
+ onChange={changeConstantName}
278
+ placeholder="Constant Name"
279
+ />
280
+ </div>
281
+ <LambdaEditor
282
+ className="query-builder__constants__lambda-editor"
283
+ disabled={
284
+ lambdaState.convertingLambdaToStringState.isInProgress
285
+ }
286
+ lambdaEditorState={lambdaState}
287
+ forceBackdrop={false}
288
+ autoFocus={true}
289
+ />
290
+ </div>
291
+ </ModalBody>
292
+ <ModalFooter>
293
+ <button
294
+ className="btn btn--dark"
295
+ onClick={close}
296
+ disabled={Boolean(lambdaState.parserError)}
297
+ >
298
+ Close
299
+ </button>
300
+ </ModalFooter>
301
+ </Modal>
302
+ </Dialog>
242
303
  );
243
304
  },
244
305
  );
@@ -68,6 +68,8 @@ import {
68
68
  prettyDuration,
69
69
  filterByType,
70
70
  isValidURL,
71
+ isString,
72
+ isNumber,
71
73
  } from '@finos/legend-shared';
72
74
  import { forwardRef, useRef, useState } from 'react';
73
75
  import {
@@ -482,15 +484,18 @@ const QueryResultCellRenderer = observer(
482
484
  const tdsExecutionResult = params.tdsExecutionResult;
483
485
  const fetchStructureImplementation =
484
486
  resultState.queryBuilderState.fetchStructureState.implementation;
485
-
486
- const cellValue = params.value as string;
487
- const formattedCellValue = !isNaN(Number(cellValue))
488
- ? Intl.NumberFormat(DEFAULT_LOCALE, { maximumFractionDigits: 4 }).format(
489
- Number(cellValue),
490
- )
491
- : cellValue;
487
+ const cellValue = params.value as string | null | number | undefined;
488
+ const formattedCellValue = (): string | null | number | undefined => {
489
+ if (isNumber(cellValue)) {
490
+ return Intl.NumberFormat(DEFAULT_LOCALE, {
491
+ maximumFractionDigits: 4,
492
+ }).format(Number(cellValue));
493
+ }
494
+ return cellValue;
495
+ };
496
+ const cellValueUrlLink =
497
+ isString(cellValue) && isValidURL(cellValue) ? cellValue : undefined;
492
498
  const columnName = params.column?.getColId() ?? '';
493
-
494
499
  const findCoordinatesFromResultValue = (
495
500
  colId: string,
496
501
  rowNumber: number,
@@ -498,7 +503,6 @@ const QueryResultCellRenderer = observer(
498
503
  const colIndex = tdsExecutionResult.result.columns.findIndex(
499
504
  (col) => col === colId,
500
505
  );
501
-
502
506
  return { rowIndex: rowNumber, colIndex: colIndex };
503
507
  };
504
508
 
@@ -710,12 +714,12 @@ const QueryResultCellRenderer = observer(
710
714
  onMouseUp={(event) => mouseUp(event)}
711
715
  onMouseOver={(event) => mouseOver(event)}
712
716
  >
713
- {isValidURL(cellValue) ? (
714
- <a href={cellValue} target="_blank" rel="noreferrer">
715
- {cellValue}
717
+ {cellValueUrlLink ? (
718
+ <a href={cellValueUrlLink} target="_blank" rel="noreferrer">
719
+ {cellValueUrlLink}
716
720
  </a>
717
721
  ) : (
718
- <span>{formattedCellValue}</span>
722
+ <span>{formattedCellValue()}</span>
719
723
  )}
720
724
  </div>
721
725
  </ContextMenu>
@@ -46,6 +46,7 @@ import {
46
46
  MoreVerticalIcon,
47
47
  MenuContentItemIcon,
48
48
  MenuContentItemLabel,
49
+ PanelLoadingIndicator,
49
50
  } from '@finos/legend-art';
50
51
  import {
51
52
  type ValueSpecification,
@@ -883,7 +884,11 @@ const QueryBuilderPostFilterPanelContent = observer(
883
884
  !aggregateColumnState &&
884
885
  columnState instanceof QueryBuilderDerivationProjectionColumnState
885
886
  ) {
886
- await flowResult(columnState.fetchDerivationLambdaReturnType());
887
+ await flowResult(
888
+ columnState.fetchDerivationLambdaReturnType({
889
+ isBeingDropped: true,
890
+ }),
891
+ );
887
892
  }
888
893
  postFilterConditionState = new PostFilterConditionState(
889
894
  postFilterState,
@@ -1034,6 +1039,11 @@ const QueryBuilderPostFilterPanelContent = observer(
1034
1039
  isDroppable={isDroppable && postFilterState.isEmpty}
1035
1040
  dropTargetConnector={dropTargetConnector}
1036
1041
  >
1042
+ {
1043
+ <PanelLoadingIndicator
1044
+ isLoading={Boolean(postFilterState.derivedColumnBeingDropped)}
1045
+ />
1046
+ }
1037
1047
  {postFilterState.isEmpty && (
1038
1048
  <BlankPanelPlaceholder
1039
1049
  text="Add a post-filter condition"
@@ -174,7 +174,17 @@ const QueryBuilderDerivationProjectionColumnEditor = observer(
174
174
  }) => {
175
175
  const { projectionColumnState } = props;
176
176
  const hasParserError = projectionColumnState.tdsState.hasParserError;
177
-
177
+ const onEditorBlur = (): void => {
178
+ flowResult(
179
+ projectionColumnState.fetchDerivationLambdaReturnType({
180
+ forceConversionStringToLambda: true,
181
+ forceRefresh: true,
182
+ }),
183
+ ).catch(
184
+ projectionColumnState.tdsState.queryBuilderState.applicationStore
185
+ .alertUnhandledError,
186
+ );
187
+ };
178
188
  const handleDrop = useCallback(
179
189
  (
180
190
  item:
@@ -247,6 +257,7 @@ const QueryBuilderDerivationProjectionColumnEditor = observer(
247
257
  }
248
258
  lambdaEditorState={projectionColumnState.derivationLambdaEditorState}
249
259
  forceBackdrop={hasParserError}
260
+ onEditorBlur={onEditorBlur}
250
261
  />
251
262
  </div>
252
263
  );
@@ -99,6 +99,7 @@ const LambdaEditor_Inner = observer(
99
99
  autoFocus?: boolean | undefined;
100
100
  openInPopUp?: (() => void) | undefined;
101
101
  onEditorFocus?: (() => void) | undefined;
102
+ onEditorBlur?: (() => void) | undefined;
102
103
  disableExpansion?: boolean | undefined;
103
104
  forceExpansion?: boolean | undefined;
104
105
  }) => {
@@ -119,6 +120,7 @@ const LambdaEditor_Inner = observer(
119
120
  autoFocus,
120
121
  openInPopUp,
121
122
  onEditorFocus,
123
+ onEditorBlur,
122
124
  } = props;
123
125
  const applicationStore = useApplicationStore();
124
126
  const onDidChangeModelContentEventDisposer = useRef<
@@ -127,6 +129,9 @@ const LambdaEditor_Inner = observer(
127
129
  const onDidFocusEditorWidgetDisposer = useRef<IDisposable | undefined>(
128
130
  undefined,
129
131
  );
132
+ const onDidBlurEditorTextDisposer = useRef<IDisposable | undefined>(
133
+ undefined,
134
+ );
130
135
  const value = normalizeLineEnding(lambdaEditorState.lambdaString);
131
136
  const parserError = lambdaEditorState.parserError;
132
137
  const compilationError = lambdaEditorState.compilationError;
@@ -285,7 +290,12 @@ const LambdaEditor_Inner = observer(
285
290
  onEditorFocus?.();
286
291
  },
287
292
  );
288
-
293
+ if (onEditorBlur) {
294
+ onDidBlurEditorTextDisposer.current = editor.onDidBlurEditorText(() => {
295
+ transformStringToLambda?.cancel();
296
+ onEditorBlur();
297
+ });
298
+ }
289
299
  // Set the text value
290
300
  const currentValue = getCodeEditorValue(editor);
291
301
  const editorModel = editor.getModel();
@@ -419,7 +429,7 @@ const LambdaEditor_Inner = observer(
419
429
  },
420
430
  );
421
431
 
422
- export const LambdaEditor_PopUp = observer(
432
+ const LambdaEditor_PopUp = observer(
423
433
  (props: {
424
434
  title?: string | undefined;
425
435
  className?: string | undefined;
@@ -668,6 +678,7 @@ type LambdaEditorBaseProps = {
668
678
  forceBackdrop: boolean;
669
679
  autoFocus?: boolean | undefined;
670
680
  onEditorFocus?: (() => void) | undefined;
681
+ onEditorBlur?: (() => void) | undefined;
671
682
  };
672
683
 
673
684
  export const InlineLambdaEditor = observer(
@@ -716,6 +727,7 @@ export const InlineLambdaEditor = observer(
716
727
  hideErrorBar,
717
728
  autoFocus,
718
729
  onEditorFocus,
730
+ onEditorBlur,
719
731
  } = props;
720
732
  const [showPopUp, setShowPopUp] = useState(false);
721
733
  const openInPopUp = (): void => setShowPopUp(true);
@@ -786,6 +798,7 @@ export const InlineLambdaEditor = observer(
786
798
  autoFocus={autoFocus}
787
799
  openInPopUp={openInPopUp}
788
800
  onEditorFocus={onEditorFocus}
801
+ onEditorBlur={onEditorBlur}
789
802
  hideErrorBar={hideErrorBar}
790
803
  forceExpansion={
791
804
  disableExpansion !== undefined
@@ -24,6 +24,10 @@ import {
24
24
  buildSourceInformationSourceId,
25
25
  ParserError,
26
26
  GRAPH_MANAGER_EVENT,
27
+ RawLambda,
28
+ PureModel,
29
+ CoreModel,
30
+ SystemModel,
27
31
  } from '@finos/legend-graph';
28
32
  import {
29
33
  type Hashable,
@@ -38,8 +42,9 @@ import {
38
42
  LogEvent,
39
43
  changeEntry,
40
44
  assertTrue,
45
+ ActionState,
41
46
  } from '@finos/legend-shared';
42
- import { action, makeObservable, observable } from 'mobx';
47
+ import { action, flow, flowResult, makeObservable, observable } from 'mobx';
43
48
  import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from './QueryBuilderStateHashUtils.js';
44
49
  import type { QueryBuilderState } from './QueryBuilderState.js';
45
50
  import {
@@ -152,10 +157,13 @@ export class QueryBuilderSimpleConstantExpressionState
152
157
  export class QueryBuilderConstantLambdaEditorState extends LambdaEditorState {
153
158
  readonly queryBuilderState: QueryBuilderState;
154
159
  calculatedState: QueryBuilderCalculatedConstantExpressionState;
160
+ convertingLambdaToStringState = ActionState.create();
161
+
155
162
  constructor(calculatedState: QueryBuilderCalculatedConstantExpressionState) {
156
163
  super('', '');
157
164
  makeObservable(this, {
158
165
  calculatedState: observable,
166
+ convertingLambdaToStringState: observable,
159
167
  buildEmptyValueSpec: observable,
160
168
  });
161
169
  this.calculatedState = calculatedState;
@@ -184,6 +192,7 @@ export class QueryBuilderConstantLambdaEditorState extends LambdaEditorState {
184
192
  override *convertLambdaGrammarStringToObject(): GeneratorFn<void> {
185
193
  if (this.lambdaString) {
186
194
  try {
195
+ this.convertingLambdaToStringState.inProgress();
187
196
  const valSpec =
188
197
  (yield this.queryBuilderState.graphManagerState.graphManager.pureCodeToValueSpecification(
189
198
  this.fullLambdaString,
@@ -199,10 +208,13 @@ export class QueryBuilderConstantLambdaEditorState extends LambdaEditorState {
199
208
  LogEvent.create(GRAPH_MANAGER_EVENT.PARSING_FAILURE),
200
209
  error,
201
210
  );
211
+ } finally {
212
+ this.convertingLambdaToStringState.complete();
202
213
  }
203
214
  } else {
204
215
  this.clearErrors();
205
216
  this.calculatedState.setValue(this.buildEmptyValueSpec());
217
+ this.convertingLambdaToStringState.complete();
206
218
  }
207
219
  }
208
220
  override *convertLambdaObjectToGrammarString(
@@ -252,14 +264,45 @@ export class QueryBuilderCalculatedConstantExpressionState
252
264
  lambdaState: observable,
253
265
  value: observable,
254
266
  setValue: action,
267
+ updateVariableExpressionType: flow,
255
268
  });
256
269
  this.value = value;
257
270
  this.lambdaState = new QueryBuilderConstantLambdaEditorState(this);
271
+ observe_ValueSpecification(
272
+ variable,
273
+ this.queryBuilderState.observerContext,
274
+ );
258
275
  }
259
276
 
260
277
  setValue(val: PlainObject): void {
261
278
  this.value = val;
262
279
  }
280
+
281
+ *updateVariableExpressionType(): GeneratorFn<void> {
282
+ try {
283
+ if (this.lambdaState.lambdaString) {
284
+ yield flowResult(this.lambdaState.convertLambdaGrammarStringToObject());
285
+ }
286
+ const rawLambda = new RawLambda([], [this.value]);
287
+ const type =
288
+ (yield this.queryBuilderState.graphManagerState.graphManager.getLambdaReturnType(
289
+ rawLambda,
290
+ new PureModel(new CoreModel([]), new SystemModel([]), []),
291
+ )) as string;
292
+ const resolvedType =
293
+ this.queryBuilderState.graphManagerState.graph.getType(type);
294
+ valueSpecification_setGenericType(
295
+ this.variable,
296
+ GenericTypeExplicitReference.create(new GenericType(resolvedType)),
297
+ );
298
+ } catch (error) {
299
+ assertErrorThrown(error);
300
+ this.queryBuilderState.applicationStore.logService.error(
301
+ LogEvent.create(GRAPH_MANAGER_EVENT.COMPILATION_FAILURE),
302
+ error,
303
+ );
304
+ }
305
+ }
263
306
  }
264
307
 
265
308
  export class QueryBuilderConstantsState implements Hashable {
@@ -552,6 +552,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
552
552
  if (this.parametersState.parameterStates.length > 0) {
553
553
  this.setShowParametersPanel(true);
554
554
  }
555
+ this.fetchStructureState.initializeWithQuery();
555
556
  } catch (error) {
556
557
  assertErrorThrown(error);
557
558
  this.resetQueryResult({ preserveResult: options?.preserveResult });