@finos/legend-query-builder 3.2.7 → 3.2.9

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 (41) hide show
  1. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  2. package/lib/components/QueryBuilderResultPanel.js +30 -11
  3. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  4. package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -1
  5. package/lib/components/execution-plan/ExecutionPlanViewer.js +11 -3
  6. package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -1
  7. package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts +2 -2
  8. package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts.map +1 -1
  9. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +15 -4
  10. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  11. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  12. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +29 -9
  13. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  14. package/lib/index.css +2 -2
  15. package/lib/index.css.map +1 -1
  16. package/lib/package.json +1 -1
  17. package/lib/stores/QueryBuilderResultState.d.ts +1 -0
  18. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  19. package/lib/stores/QueryBuilderResultState.js +26 -5
  20. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  21. package/lib/stores/QueryBuilderValueSpecificationBuilderHelper.d.ts +5 -0
  22. package/lib/stores/QueryBuilderValueSpecificationBuilderHelper.d.ts.map +1 -1
  23. package/lib/stores/QueryLoaderState.d.ts.map +1 -1
  24. package/lib/stores/QueryLoaderState.js +2 -1
  25. package/lib/stores/QueryLoaderState.js.map +1 -1
  26. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.d.ts.map +1 -1
  27. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js.map +1 -1
  28. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.d.ts +1 -0
  29. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.d.ts.map +1 -1
  30. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js +1 -1
  31. package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js.map +1 -1
  32. package/package.json +9 -9
  33. package/src/components/QueryBuilderResultPanel.tsx +93 -7
  34. package/src/components/execution-plan/ExecutionPlanViewer.tsx +29 -11
  35. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +99 -12
  36. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +32 -16
  37. package/src/stores/QueryBuilderResultState.ts +43 -18
  38. package/src/stores/QueryBuilderValueSpecificationBuilderHelper.ts +5 -0
  39. package/src/stores/QueryLoaderState.ts +2 -1
  40. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts +1 -4
  41. package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.ts +5 -3
@@ -15,13 +15,19 @@
15
15
  */
16
16
 
17
17
  import { observer } from 'mobx-react-lite';
18
- import type { ExecutionPlanState } from '../../stores/execution-plan/ExecutionPlanState.js';
18
+ import {
19
+ type ExecutionPlanState,
20
+ EXECUTION_PLAN_VIEW_MODE,
21
+ } from '../../stores/execution-plan/ExecutionPlanState.js';
19
22
  import { format as formatSQL } from 'sql-formatter';
20
- import type { SQLResultColumn } from '@finos/legend-graph';
23
+ import { type SQLResultColumn, stringifyDataType } from '@finos/legend-graph';
21
24
  import {
22
- CODE_EDITOR_LANGUAGE,
23
- CodeEditor,
24
- } from '@finos/legend-lego/code-editor';
25
+ PanelListItem,
26
+ CopyIcon,
27
+ PanelDivider,
28
+ Button,
29
+ PanelContent,
30
+ } from '@finos/legend-art';
25
31
 
26
32
  /**
27
33
  * TODO: Create a new `AbstractPlugin` for this, called `ExecutionPlanViewerPlugin`
@@ -35,14 +41,95 @@ export const SQLExecutionNodeViewer: React.FC<{
35
41
  resultColumns: SQLResultColumn[];
36
42
  executionPlanState: ExecutionPlanState;
37
43
  }> = observer((props) => {
38
- const { query } = props;
44
+ const { query, resultColumns, executionPlanState } = props;
39
45
 
46
+ const applicationStore = executionPlanState.applicationStore;
47
+ const copyExpression = (value: string): void => {
48
+ applicationStore.clipboardService
49
+ .copyTextToClipboard(value)
50
+ .then(() =>
51
+ applicationStore.notificationService.notifySuccess(
52
+ 'SQL Query copied',
53
+ undefined,
54
+ 2500,
55
+ ),
56
+ )
57
+ .catch(applicationStore.alertUnhandledError);
58
+ };
40
59
  return (
41
- <CodeEditor
42
- inputValue={formatSQL(query)}
43
- isReadOnly={true}
44
- language={CODE_EDITOR_LANGUAGE.SQL}
45
- hideMinimap={true}
46
- />
60
+ <PanelContent
61
+ darkMode={
62
+ !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
63
+ }
64
+ >
65
+ <div className="query-builder__sql__container">
66
+ <PanelDivider />
67
+ <div key={query}>
68
+ <div className="query-builder__sql__container__item__label">
69
+ <PanelListItem>
70
+ SQL
71
+ <div>
72
+ <button
73
+ onClick={() => {
74
+ copyExpression(query);
75
+ }}
76
+ title="Copy SQL Expression"
77
+ className="query-builder__sql__container__icon"
78
+ >
79
+ <CopyIcon />
80
+ </button>
81
+ </div>
82
+ </PanelListItem>
83
+ </div>
84
+ <PanelListItem className="query-builder__sql__container__item">
85
+ <pre>{formatSQL(query)} </pre>
86
+ </PanelListItem>
87
+ <PanelDivider />
88
+ </div>
89
+ </div>
90
+ <div className="query-builder__sql__container">
91
+ <div>
92
+ <PanelListItem className="query-builder__sql__container__item__label">
93
+ Result Columns
94
+ </PanelListItem>
95
+ <PanelDivider />
96
+ <table className="table query-builder__sql__container__table">
97
+ <thead>
98
+ <tr>
99
+ <th className="table__cell--left">Label</th>
100
+ <th className="table__cell--left">Data Type</th>
101
+ </tr>
102
+ </thead>
103
+ <tbody>
104
+ {resultColumns.map((column) => (
105
+ <tr key={column.label}>
106
+ <td className="table__cell--left">
107
+ {column.label.replaceAll(`"`, '')}
108
+ </td>
109
+
110
+ {column.dataType && (
111
+ <td className="table__cell--left">
112
+ {stringifyDataType(column.dataType)}
113
+ </td>
114
+ )}
115
+ </tr>
116
+ ))}
117
+ </tbody>
118
+ </table>
119
+ </div>
120
+ </div>
121
+ <PanelDivider />
122
+
123
+ <div className="query-builder__sql__container">
124
+ <Button
125
+ className="btn--dark execution-node-viewer__unsupported-view__to-text-mode__btn"
126
+ onClick={(): void =>
127
+ executionPlanState.setViewMode(EXECUTION_PLAN_VIEW_MODE.JSON)
128
+ }
129
+ text="View JSON"
130
+ />
131
+ </div>
132
+ <PanelDivider />
133
+ </PanelContent>
47
134
  );
48
135
  });
@@ -433,26 +433,42 @@ const QueryBuilderProjectionColumnEditor = observer(
433
433
  if (option?.value !== aggregateColumnState?.calendarFunction) {
434
434
  const lambdaParameterName =
435
435
  aggregateColumnState?.calendarFunction?.lambdaParameterName;
436
+ const dateColumn = aggregateColumnState?.calendarFunction?.dateColumn;
437
+ const endDate = aggregateColumnState?.calendarFunction?.endDate;
438
+ const calendarType =
439
+ aggregateColumnState?.calendarFunction?.calendarType;
436
440
  aggregateColumnState?.setCalendarFunction(option?.value ?? undefined);
437
- if (lambdaParameterName && aggregateColumnState.calendarFunction) {
438
- aggregateColumnState.calendarFunction.setLambdaParameterName(
439
- lambdaParameterName,
440
- );
441
- }
442
- if (aggregateColumnState?.calendarFunction) {
443
- if (calendarFunctionDateColumnOptions[0]) {
444
- aggregateColumnState.calendarFunction.setDateColumn(
445
- guaranteeNonNullable(calendarFunctionDateColumnOptions[0]).value,
446
- );
441
+ const calendarFunction = aggregateColumnState?.calendarFunction;
442
+ if (calendarFunction) {
443
+ if (lambdaParameterName) {
444
+ calendarFunction.setLambdaParameterName(lambdaParameterName);
445
+ }
446
+ if (dateColumn) {
447
+ calendarFunction.setDateColumn(dateColumn);
447
448
  } else {
448
- applicationStore.notificationService.notifyWarning(
449
- 'Please provide the date column field for the calendar function',
449
+ if (calendarFunctionDateColumnOptions[0]) {
450
+ calendarFunction.setDateColumn(
451
+ guaranteeNonNullable(calendarFunctionDateColumnOptions[0])
452
+ .value,
453
+ );
454
+ } else {
455
+ applicationStore.notificationService.notifyWarning(
456
+ 'Please provide a date attribute for the calendar function',
457
+ );
458
+ }
459
+ }
460
+ if (endDate) {
461
+ calendarFunction.setEndDate(endDate);
462
+ } else {
463
+ calendarFunction.setEndDate(defaultEndDate);
464
+ }
465
+ if (calendarType) {
466
+ calendarFunction.setCalendarType(calendarType);
467
+ } else {
468
+ calendarFunction.setCalendarType(
469
+ guaranteeNonNullable(calendarTypeOptions[0]).value,
450
470
  );
451
471
  }
452
- aggregateColumnState.calendarFunction.setCalendarType(
453
- guaranteeNonNullable(calendarTypeOptions[0]).value,
454
- );
455
- aggregateColumnState.calendarFunction.setEndDate(defaultEndDate);
456
472
  }
457
473
  }
458
474
  };
@@ -86,6 +86,7 @@ export class QueryBuilderResultState {
86
86
  setQueryRunPromise: action,
87
87
  exportData: flow,
88
88
  runQuery: flow,
89
+ cancelQuery: flow,
89
90
  generatePlan: flow,
90
91
  });
91
92
 
@@ -163,7 +164,9 @@ export class QueryBuilderResultState {
163
164
  const contentType = exportData.contentType;
164
165
  const serializationFormat = exportData.serializationFormat;
165
166
  this.exportDataState.inProgress();
166
- const query = this.buildExecutionRawLambda();
167
+ const query = this.buildExecutionRawLambda({
168
+ isExportingResult: true,
169
+ });
167
170
  const result =
168
171
  (yield this.queryBuilderState.graphManagerState.graphManager.runQuery(
169
172
  query,
@@ -205,6 +208,7 @@ export class QueryBuilderResultState {
205
208
  }
206
209
 
207
210
  *runQuery(): GeneratorFn<void> {
211
+ let promise;
208
212
  try {
209
213
  this.setIsRunningQuery(true);
210
214
  const currentHashCode = this.queryBuilderState.hashCode;
@@ -231,16 +235,15 @@ export class QueryBuilderResultState {
231
235
  this.queryBuilderState.graphManagerState.graph,
232
236
  );
233
237
 
234
- const promise =
235
- this.queryBuilderState.graphManagerState.graphManager.runQuery(
236
- query,
237
- mapping,
238
- runtime,
239
- this.queryBuilderState.graphManagerState.graph,
240
- {
241
- parameterValues,
242
- },
243
- );
238
+ promise = this.queryBuilderState.graphManagerState.graphManager.runQuery(
239
+ query,
240
+ mapping,
241
+ runtime,
242
+ this.queryBuilderState.graphManagerState.graph,
243
+ {
244
+ parameterValues,
245
+ },
246
+ );
244
247
 
245
248
  this.setQueryRunPromise(promise);
246
249
  const result = (yield promise) as ExecutionResult;
@@ -260,17 +263,39 @@ export class QueryBuilderResultState {
260
263
  );
261
264
  }
262
265
  } catch (error) {
263
- assertErrorThrown(error);
266
+ // When user cancels the query by calling the cancelQuery api, it will throw an exeuction failure error.
267
+ // For now, we don't want to notify users about this failure. Therefore we check to ensure the promise is still the same one.
268
+ // When cancelled the query, we set the queryRunPromise as undefined.
269
+ if (this.queryRunPromise === promise) {
270
+ assertErrorThrown(error);
271
+ this.queryBuilderState.applicationStore.logService.error(
272
+ LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
273
+ error,
274
+ );
275
+ this.queryBuilderState.applicationStore.notificationService.notifyError(
276
+ error,
277
+ );
278
+ }
279
+ } finally {
280
+ this.setIsRunningQuery(false);
281
+ this.pressedRunQuery.complete();
282
+ }
283
+ }
284
+
285
+ *cancelQuery(): GeneratorFn<void> {
286
+ this.pressedRunQuery.complete();
287
+ this.setIsRunningQuery(false);
288
+ this.setQueryRunPromise(undefined);
289
+ try {
290
+ yield this.queryBuilderState.graphManagerState.graphManager.cancelUserExecutions(
291
+ true,
292
+ );
293
+ } catch (error) {
294
+ // Don't notify users about success or failure
264
295
  this.queryBuilderState.applicationStore.logService.error(
265
296
  LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
266
297
  error,
267
298
  );
268
- this.queryBuilderState.applicationStore.notificationService.notifyError(
269
- error,
270
- );
271
- } finally {
272
- this.setIsRunningQuery(false);
273
- this.pressedRunQuery.complete();
274
299
  }
275
300
  }
276
301
 
@@ -194,4 +194,9 @@ export type LambdaFunctionBuilderOption = {
194
194
  */
195
195
  useAllVersionsForMilestoning?: boolean | undefined;
196
196
  keepSourceInformation?: boolean | undefined;
197
+ /**
198
+ * Set this to `true` when we export query results since we do want to ignore an overriding
199
+ * limit for the query results if it exists so the exported results contain all the data
200
+ */
201
+ isExportingResult?: boolean | undefined;
197
202
  };
@@ -205,6 +205,7 @@ export class QueryLoaderState {
205
205
  let searchSpecification = new QuerySearchSpecification();
206
206
  searchSpecification.searchTerm = searchText;
207
207
  searchSpecification.limit = QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT + 1;
208
+ searchSpecification.exactMatchName = true;
208
209
  searchSpecification.showCurrentUserQueriesOnly =
209
210
  this.showCurrentUserQueriesOnly;
210
211
  if (this.queryBuilderState) {
@@ -246,7 +247,7 @@ export class QueryLoaderState {
246
247
  name,
247
248
  )) as Query;
248
249
  this.onQueryRenamed?.(query);
249
- this.applicationStore.notificationService.notify(
250
+ this.applicationStore.notificationService.notifySuccess(
250
251
  'Renamed query successfully',
251
252
  );
252
253
  this.renameQueryState.pass();
@@ -36,7 +36,6 @@ import {
36
36
  guaranteeType,
37
37
  isNonNullable,
38
38
  returnUndefOnError,
39
- type PlainObject,
40
39
  } from '@finos/legend-shared';
41
40
  import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../../graph/QueryBuilderMetaModelConst.js';
42
41
  import type { QueryBuilderState } from '../../../QueryBuilderState.js';
@@ -218,9 +217,7 @@ export const processTDSProjectionDerivationExpression = (
218
217
  queryBuilderState.fetchStructureState.implementation;
219
218
  const rawLambdaProtocol = returnUndefOnError(() =>
220
219
  guaranteeType(
221
- V1_deserializeRawValueSpecification(
222
- value.content as PlainObject<V1_RawLambda>,
223
- ),
220
+ V1_deserializeRawValueSpecification(value.content),
224
221
  V1_RawLambda,
225
222
  ),
226
223
  );
@@ -164,6 +164,7 @@ export const appendProjection = (
164
164
  */
165
165
  isBuildingExecutionQuery?: boolean | undefined;
166
166
  keepSourceInformation?: boolean | undefined;
167
+ isExportingResult?: boolean | undefined;
167
168
  },
168
169
  ): void => {
169
170
  const queryBuilderState = tdsState.queryBuilderState;
@@ -394,8 +395,9 @@ export const appendProjection = (
394
395
 
395
396
  // build result set modifiers
396
397
  appendResultSetModifier(tdsState.resultSetModifierState, lambdaFunction, {
397
- overridingLimit: options?.isBuildingExecutionQuery
398
- ? queryBuilderState.resultState.previewLimit
399
- : undefined,
398
+ overridingLimit:
399
+ options?.isBuildingExecutionQuery && !options.isExportingResult
400
+ ? queryBuilderState.resultState.previewLimit
401
+ : undefined,
400
402
  });
401
403
  };