@finos/legend-query-builder 3.2.7 → 3.2.9

Sign up to get free protection for your applications and to get access to all the features.
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
  };