@finos/legend-query-builder 0.6.26 → 0.6.28

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. package/lib/components/QueryBuilder.d.ts.map +1 -1
  2. package/lib/components/QueryBuilder.js +29 -28
  3. package/lib/components/QueryBuilder.js.map +1 -1
  4. package/lib/components/QueryBuilderComponentTestUtils.d.ts.map +1 -1
  5. package/lib/components/QueryBuilderComponentTestUtils.js +4 -5
  6. package/lib/components/QueryBuilderComponentTestUtils.js.map +1 -1
  7. package/lib/components/QueryBuilderConstantExpressionPanel.d.ts.map +1 -1
  8. package/lib/components/QueryBuilderConstantExpressionPanel.js +5 -4
  9. package/lib/components/QueryBuilderConstantExpressionPanel.js.map +1 -1
  10. package/lib/components/QueryBuilderDiffPanel.d.ts.map +1 -1
  11. package/lib/components/QueryBuilderDiffPanel.js +2 -2
  12. package/lib/components/QueryBuilderDiffPanel.js.map +1 -1
  13. package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
  14. package/lib/components/QueryBuilderParametersPanel.js +13 -8
  15. package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
  16. package/lib/components/QueryBuilderPropertyExpressionEditor.d.ts.map +1 -1
  17. package/lib/components/QueryBuilderPropertyExpressionEditor.js +10 -2
  18. package/lib/components/QueryBuilderPropertyExpressionEditor.js.map +1 -1
  19. package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
  20. package/lib/components/QueryBuilderResultPanel.js +17 -1
  21. package/lib/components/QueryBuilderResultPanel.js.map +1 -1
  22. package/lib/components/QueryBuilder_TestID.d.ts +3 -1
  23. package/lib/components/QueryBuilder_TestID.d.ts.map +1 -1
  24. package/lib/components/QueryBuilder_TestID.js +2 -0
  25. package/lib/components/QueryBuilder_TestID.js.map +1 -1
  26. package/lib/components/explorer/QueryBuilderExplorerPanel.d.ts.map +1 -1
  27. package/lib/components/explorer/QueryBuilderExplorerPanel.js +2 -2
  28. package/lib/components/explorer/QueryBuilderExplorerPanel.js.map +1 -1
  29. package/lib/components/explorer/QueryBuilderMilestoningEditor.d.ts.map +1 -1
  30. package/lib/components/explorer/QueryBuilderMilestoningEditor.js +2 -2
  31. package/lib/components/explorer/QueryBuilderMilestoningEditor.js.map +1 -1
  32. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.d.ts.map +1 -1
  33. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js +2 -2
  34. package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js.map +1 -1
  35. package/lib/components/shared/CustomDatePicker.d.ts +33 -0
  36. package/lib/components/shared/CustomDatePicker.d.ts.map +1 -1
  37. package/lib/components/shared/CustomDatePicker.js +4 -4
  38. package/lib/components/shared/CustomDatePicker.js.map +1 -1
  39. package/lib/components/shared/QueryBuilderVariableSelector.d.ts +1 -1
  40. package/lib/components/shared/QueryBuilderVariableSelector.d.ts.map +1 -1
  41. package/lib/components/shared/QueryBuilderVariableSelector.js +12 -1
  42. package/lib/components/shared/QueryBuilderVariableSelector.js.map +1 -1
  43. package/lib/components/watermark/QueryBuilderWatermark.d.ts.map +1 -1
  44. package/lib/components/watermark/QueryBuilderWatermark.js +2 -2
  45. package/lib/components/watermark/QueryBuilderWatermark.js.map +1 -1
  46. package/lib/index.css +2 -2
  47. package/lib/index.css.map +1 -1
  48. package/lib/index.d.ts +2 -0
  49. package/lib/index.d.ts.map +1 -1
  50. package/lib/index.js +2 -0
  51. package/lib/index.js.map +1 -1
  52. package/lib/package.json +9 -9
  53. package/lib/stores/QueryBuilderEvent.d.ts +25 -0
  54. package/lib/stores/QueryBuilderEvent.d.ts.map +1 -0
  55. package/lib/stores/QueryBuilderEvent.js +26 -0
  56. package/lib/stores/QueryBuilderEvent.js.map +1 -0
  57. package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
  58. package/lib/stores/QueryBuilderResultState.js +44 -6
  59. package/lib/stores/QueryBuilderResultState.js.map +1 -1
  60. package/lib/stores/QueryBuilderState.d.ts +3 -0
  61. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  62. package/lib/stores/QueryBuilderState.js +7 -0
  63. package/lib/stores/QueryBuilderState.js.map +1 -1
  64. package/lib/stores/QueryBuilderTelemetry.d.ts +33 -0
  65. package/lib/stores/QueryBuilderTelemetry.d.ts.map +1 -0
  66. package/lib/stores/QueryBuilderTelemetry.js +37 -0
  67. package/lib/stores/QueryBuilderTelemetry.js.map +1 -0
  68. package/package.json +16 -16
  69. package/src/components/QueryBuilder.tsx +25 -22
  70. package/src/components/QueryBuilderComponentTestUtils.tsx +6 -5
  71. package/src/components/QueryBuilderConstantExpressionPanel.tsx +12 -11
  72. package/src/components/QueryBuilderDiffPanel.tsx +2 -3
  73. package/src/components/QueryBuilderParametersPanel.tsx +13 -7
  74. package/src/components/QueryBuilderPropertyExpressionEditor.tsx +27 -7
  75. package/src/components/QueryBuilderResultPanel.tsx +17 -1
  76. package/src/components/QueryBuilder_TestID.ts +2 -0
  77. package/src/components/explorer/QueryBuilderExplorerPanel.tsx +2 -3
  78. package/src/components/explorer/QueryBuilderMilestoningEditor.tsx +2 -3
  79. package/src/components/fetch-structure/QueryBuilderTDSWindowPanel.tsx +3 -6
  80. package/src/components/shared/CustomDatePicker.tsx +3 -3
  81. package/src/components/shared/QueryBuilderVariableSelector.tsx +18 -4
  82. package/src/components/watermark/QueryBuilderWatermark.tsx +2 -6
  83. package/src/index.ts +2 -0
  84. package/src/stores/QueryBuilderEvent.ts +26 -0
  85. package/src/stores/QueryBuilderResultState.ts +74 -2
  86. package/src/stores/QueryBuilderState.ts +10 -0
  87. package/src/stores/QueryBuilderTelemetry.ts +83 -0
  88. package/tsconfig.json +2 -0
@@ -52,6 +52,7 @@ import { variableExpression_setName } from '../stores/shared/ValueSpecificationM
52
52
  import { LambdaParameterState } from '../stores/shared/LambdaParameterState.js';
53
53
  import { LambdaParameterValuesEditor } from './shared/LambdaParameterValuesEditor.js';
54
54
  import { VariableViewer } from './shared/QueryBuilderVariableSelector.js';
55
+ import { QUERY_BUILDER_TEST_ID } from './QueryBuilder_TestID.js';
55
56
 
56
57
  type MultiplicityOption = { label: string; value: Multiplicity };
57
58
 
@@ -71,12 +72,16 @@ const VariableExpressionEditor = observer(
71
72
  const { queryBuilderState, lambdaParameterState } = props;
72
73
  const applicationStore = useApplicationStore();
73
74
  const queryParametersState = queryBuilderState.parametersState;
75
+ const allVariableNames = queryBuilderState.allVariableNames;
74
76
  const isCreating =
75
77
  !queryParametersState.parameterStates.includes(lambdaParameterState);
76
78
  const varState = lambdaParameterState.parameter;
77
79
  const multiplity = varState.multiplicity;
78
80
  const validationMessage = !varState.name
79
81
  ? `Parameter name can't be empty`
82
+ : allVariableNames.filter((e) => e === varState.name).length >
83
+ (isCreating ? 0 : 1)
84
+ ? 'Parameter Name Already Exists'
80
85
  : (isCreating &&
81
86
  queryParametersState.parameterStates.find(
82
87
  (p) => p.parameter.name === varState.name,
@@ -200,13 +205,11 @@ const VariableExpressionEditor = observer(
200
205
  </ModalBody>
201
206
  <ModalFooter>
202
207
  {isCreating && (
203
- <button
204
- className="btn modal__footer__close-btn btn--dark"
208
+ <ModalFooterButton
209
+ text="Create"
210
+ inProgress={Boolean(validationMessage)}
205
211
  onClick={onAction}
206
- disabled={Boolean(validationMessage)}
207
- >
208
- Create
209
- </button>
212
+ />
210
213
  )}
211
214
  <ModalFooterButton onClick={close} text="Close" />
212
215
  </ModalFooter>
@@ -247,7 +250,10 @@ export const QueryBuilderParametersPanel = observer(
247
250
  };
248
251
 
249
252
  return (
250
- <div className="panel query-builder__variables">
253
+ <div
254
+ data-testid={QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PARAMETERS}
255
+ className="panel query-builder__variables"
256
+ >
251
257
  <div className="panel__header">
252
258
  <div className="panel__header__title">
253
259
  <div className="panel__header__title__label">parameters</div>
@@ -25,6 +25,7 @@ import {
25
25
  ModalHeader,
26
26
  ModalBody,
27
27
  ModalFooter,
28
+ ModalFooterButton,
28
29
  } from '@finos/legend-art';
29
30
  import { observer } from 'mobx-react-lite';
30
31
  import {
@@ -304,7 +305,25 @@ export const QueryBuilderPropertyExpressionEditor = observer(
304
305
  const { propertyExpressionState } = props;
305
306
  const handleClose = (): void =>
306
307
  propertyExpressionState.setIsEditingDerivedProperty(false);
307
-
308
+ const isParameterCompatibleWithDerivedProperty = (
309
+ variable: VariableExpression,
310
+ derivedProperties: QueryBuilderDerivedPropertyExpressionState[],
311
+ ): boolean =>
312
+ Boolean(
313
+ derivedProperties.find((dp) => {
314
+ if (!variable.genericType?.value.rawType) {
315
+ return false;
316
+ }
317
+ return (
318
+ isSuperType(
319
+ dp.derivedProperty.genericType.value.rawType,
320
+ variable.genericType.value.rawType,
321
+ ) ||
322
+ dp.derivedProperty.genericType.value.rawType.name ===
323
+ variable.genericType.value.rawType.name
324
+ );
325
+ }),
326
+ );
308
327
  return (
309
328
  <Dialog
310
329
  open={Boolean(
@@ -334,16 +353,17 @@ export const QueryBuilderPropertyExpressionEditor = observer(
334
353
  <ModalBody className="query-builder__variables__modal__body">
335
354
  <VariableSelector
336
355
  queryBuilderState={propertyExpressionState.queryBuilderState}
356
+ filterBy={(v: VariableExpression) =>
357
+ isParameterCompatibleWithDerivedProperty(
358
+ v,
359
+ propertyExpressionState.derivedPropertyExpressionStates,
360
+ )
361
+ }
337
362
  />
338
363
  </ModalBody>
339
364
  </ModalBody>
340
365
  <ModalFooter>
341
- <button
342
- className="btn modal__footer__close-btn"
343
- onClick={handleClose}
344
- >
345
- Done
346
- </button>
366
+ <ModalFooterButton text="Done" onClick={handleClose} />
347
367
  </ModalFooter>
348
368
  </Modal>
349
369
  </Dialog>
@@ -326,6 +326,7 @@ const QueryBuilderGridResult = observer(
326
326
  const [cellDoubleClickedEvent, setCellDoubleClickedEvent] =
327
327
  useState<CellMouseOverEvent | null>(null);
328
328
  const columns = executionResult.result.columns;
329
+ let rowNumber = 1;
329
330
  const rowData = executionResult.result.rows.map((_row) => {
330
331
  const row: PlainObject = {};
331
332
  const cols = executionResult.result.columns;
@@ -334,7 +335,9 @@ const QueryBuilderGridResult = observer(
334
335
  // call `.toString()` to avoid this behavior.
335
336
  // See https://github.com/finos/legend-studio/issues/1008
336
337
  row[cols[idx] as string] = isBoolean(value) ? String(value) : value;
338
+ row.rowNumber = rowNumber;
337
339
  });
340
+ rowNumber += 1;
338
341
  return row;
339
342
  });
340
343
 
@@ -351,7 +354,8 @@ const QueryBuilderGridResult = observer(
351
354
  }
352
355
  disabled={
353
356
  !(fetchStructureImplementation instanceof QueryBuilderTDSState) ||
354
- !queryBuilderState.isQuerySupported
357
+ !queryBuilderState.isQuerySupported ||
358
+ !cellDoubleClickedEvent
355
359
  }
356
360
  menuProps={{ elevation: 7 }}
357
361
  key={executionResult._UUID}
@@ -359,7 +363,19 @@ const QueryBuilderGridResult = observer(
359
363
  >
360
364
  <AgGridReact
361
365
  rowData={rowData}
366
+ gridOptions={{
367
+ suppressScrollOnNewData: true,
368
+ getRowId: function (data) {
369
+ return data.data.rowNumber as string;
370
+ },
371
+ }}
362
372
  modules={[ClientSideRowModelModule]}
373
+ // Note: we use onCellMouseOver as a bit of a workaround
374
+ // since we use the context menu so we want the user to be
375
+ // able to right click any cell and have the context menu
376
+ // options use the data belonging to the row that they are
377
+ // in. hence why we set the cell every time we mouse over
378
+ // rather than making user click multiple times.
363
379
  onCellMouseOver={(event): void => {
364
380
  setCellDoubleClickedEvent(event);
365
381
  }}
@@ -27,4 +27,6 @@ export enum QUERY_BUILDER_TEST_ID {
27
27
  QUERY_BUILDER_EXPLORER = 'query__builder__explorer',
28
28
  QUERY_BUILDER_PROPERTY_SEARCH_PANEL = 'query__builder__property__search__panel',
29
29
  QUERY_BUILDER_RESULT_PANEL = 'query__builder__result__panel',
30
+ QUERY_BUILDER_PARAMETERS = 'query-builder__parameters',
31
+ QUERY_BUILDER_CONSTANTS = 'query-builder__constants',
30
32
  }
@@ -52,6 +52,7 @@ import {
52
52
  ModalBody,
53
53
  ModalFooter,
54
54
  ModalHeader,
55
+ ModalFooterButton,
55
56
  } from '@finos/legend-art';
56
57
  import {
57
58
  type QueryBuilderExplorerTreeDragSource,
@@ -266,9 +267,7 @@ const QueryBuilderExplorerPreviewDataModal = observer(
266
267
  )}
267
268
  </ModalBody>
268
269
  <ModalFooter>
269
- <button className="btn modal__footer__close-btn" onClick={close}>
270
- Close
271
- </button>
270
+ <ModalFooterButton text="Close" onClick={close} />
272
271
  </ModalFooter>
273
272
  </Modal>
274
273
  </Dialog>
@@ -35,6 +35,7 @@ import {
35
35
  Modal,
36
36
  ModalBody,
37
37
  ModalFooter,
38
+ ModalFooterButton,
38
39
  ModalHeader,
39
40
  PanelEntryDropZonePlaceholder,
40
41
  PanelFormSection,
@@ -279,9 +280,7 @@ export const MilestoningParametersEditor = observer(
279
280
  </div>
280
281
  </ModalBody>
281
282
  <ModalFooter>
282
- <button className="btn modal__footer__close-btn" onClick={close}>
283
- Close
284
- </button>
283
+ <ModalFooterButton text="Close" onClick={close} />
285
284
  </ModalFooter>
286
285
  </Modal>
287
286
  </Dialog>
@@ -42,6 +42,7 @@ import {
42
42
  Modal,
43
43
  ModalFooter,
44
44
  PanelFormSection,
45
+ ModalFooterButton,
45
46
  } from '@finos/legend-art';
46
47
  import { assertErrorThrown, guaranteeNonNullable } from '@finos/legend-shared';
47
48
  import { observer } from 'mobx-react-lite';
@@ -513,13 +514,9 @@ const QueryBuilderWindowColumnModalEditor = observer(
513
514
  </div>
514
515
  <ModalFooter>
515
516
  {createNewWindow ? (
516
- <button className="btn modal__footer__close-btn" onClick={create}>
517
- Create
518
- </button>
517
+ <ModalFooterButton text="Create" onClick={create} />
519
518
  ) : (
520
- <button className="btn modal__footer__close-btn" onClick={close}>
521
- Close
522
- </button>
519
+ <ModalFooterButton text="Close" onClick={close} />
523
520
  )}
524
521
  </ModalFooter>
525
522
  </Modal>
@@ -56,7 +56,7 @@ import {
56
56
  valueSpecification_setGenericType,
57
57
  } from '../../stores/shared/ValueSpecificationModifierHelper.js';
58
58
 
59
- enum CUSTOM_DATE_PICKER_OPTION {
59
+ export enum CUSTOM_DATE_PICKER_OPTION {
60
60
  ABSOLUTE_DATE = 'Absolute Date',
61
61
  ABSOLUTE_TIME = 'Absolute Time',
62
62
  TODAY = 'Today',
@@ -431,7 +431,7 @@ const buildPureDurationEnumValue = (
431
431
  };
432
432
 
433
433
  /**
434
- * Generate the pure date ajust() function based on the CustomDateOption.
434
+ * Generate the pure date adjust() function based on the CustomDateOption.
435
435
  */
436
436
  const buildPureAdjustDateFunction = (
437
437
  customDateOption: CustomDateOption,
@@ -634,7 +634,7 @@ const buildCustomDateOption = (
634
634
  /**
635
635
  * Build DatePickerOption from pure date functions or PrimitiveInstanceValue
636
636
  */
637
- const buildDatePickerOption = (
637
+ export const buildDatePickerOption = (
638
638
  valueSpecification: SimpleFunctionExpression | PrimitiveInstanceValue,
639
639
  ): DatePickerOption => {
640
640
  if (valueSpecification instanceof SimpleFunctionExpression) {
@@ -23,9 +23,10 @@ import {
23
23
  TimesIcon,
24
24
  useDragPreviewLayer,
25
25
  } from '@finos/legend-art';
26
- import type {
27
- ValueSpecification,
28
- VariableExpression,
26
+ import {
27
+ SimpleFunctionExpression,
28
+ type ValueSpecification,
29
+ type VariableExpression,
29
30
  } from '@finos/legend-graph';
30
31
  import { observer } from 'mobx-react-lite';
31
32
  import { useDrag } from 'react-dnd';
@@ -36,6 +37,7 @@ import {
36
37
  QUERY_BUILDER_VARIABLE_DND_TYPE,
37
38
  VariableInfoTooltip,
38
39
  } from './BasicValueSpecificationEditor.js';
40
+ import { buildDatePickerOption } from './CustomDatePicker.js';
39
41
 
40
42
  export const VariableViewer = observer(
41
43
  (props: {
@@ -50,8 +52,19 @@ export const VariableViewer = observer(
50
52
  }) => {
51
53
  const { variable, constantValue, actions, isReadOnly, queryBuilderState } =
52
54
  props;
55
+
56
+ const getNameOfValue = (value: ValueSpecification): string | undefined => {
57
+ if (value instanceof SimpleFunctionExpression) {
58
+ const possibleDateLabel = buildDatePickerOption(value).label;
59
+ if (possibleDateLabel) {
60
+ return possibleDateLabel;
61
+ }
62
+ }
63
+ return getValueSpecificationStringValue(value);
64
+ };
65
+
53
66
  const valueString = constantValue
54
- ? getValueSpecificationStringValue(constantValue)
67
+ ? getNameOfValue(constantValue)
55
68
  : undefined;
56
69
  const name = variable.name;
57
70
  const variableType = variable.genericType?.value.rawType;
@@ -159,6 +172,7 @@ export const VariableSelector = observer(
159
172
  queryBuilderState.constantState.constants.filter((c) =>
160
173
  filterBy ? filterBy(c.variable) : true,
161
174
  );
175
+
162
176
  return (
163
177
  <>
164
178
  <PanelFormListItems title="Available parameters">
@@ -25,6 +25,7 @@ import {
25
25
  ModalFooter,
26
26
  ModalHeader,
27
27
  PanelFormSection,
28
+ ModalFooterButton,
28
29
  } from '@finos/legend-art';
29
30
  import {
30
31
  areMultiplicitiesEqual,
@@ -169,12 +170,7 @@ export const QueryBuilderWatermarkEditor = observer(
169
170
  </PanelForm>
170
171
  </ModalBody>
171
172
  <ModalFooter>
172
- <button
173
- className="btn modal__footer__close-btn"
174
- onClick={handleClose}
175
- >
176
- Done
177
- </button>
173
+ <ModalFooterButton text="Done" onClick={handleClose} />
178
174
  </ModalFooter>
179
175
  </Modal>
180
176
  </Dialog>
package/src/index.ts CHANGED
@@ -49,6 +49,8 @@ export * from './stores/ServiceInfo.js';
49
49
  export * from './components/ServiceQuerySetupUtils.js';
50
50
  export * from './components/QuerySetupUtils.js';
51
51
  export * from './components/QueryBuilderTextEditor.js';
52
+ export * from './stores/QueryBuilderTelemetry.js';
53
+ export * from './stores/QueryBuilderEvent.js';
52
54
 
53
55
  export { TEST__setUpQueryBuilder } from './components/QueryBuilderComponentTestUtils.js';
54
56
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ export enum QUERY_BUILDER_EVENT {
18
+ RUN_QUERY__LAUNCH = 'editor.execution.run-query.launch',
19
+ GENERATE_EXECUTION_PLAN__LAUNCH = 'editor.execution.generate-plan.launch',
20
+ DEBUG_EXECUTION_PLAN__LAUNCH = 'editor.execution.debug-plan.launch',
21
+
22
+ RUN_QUERY__SUCCESS = 'editor.execution.run-query.success',
23
+ GENERATE_EXECUTION_PLAN__SUCCESS = 'editor.execution.generate-plan.success',
24
+ DEBUG_EXECUTION_PLAN__SUCCESS = 'editor.execution.debug-plan.success',
25
+ BUILD_EXECUTION_PLAN__SUCCESS = 'graph-manager.execution.build-plan.success',
26
+ }
@@ -25,6 +25,7 @@ import {
25
25
  downloadFileUsingDataURI,
26
26
  UnsupportedOperationError,
27
27
  ActionState,
28
+ StopWatch,
28
29
  } from '@finos/legend-shared';
29
30
  import type { QueryBuilderState } from './QueryBuilderState.js';
30
31
  import {
@@ -35,6 +36,7 @@ import {
35
36
  EXECUTION_SERIALIZATION_FORMAT,
36
37
  RawExecutionResult,
37
38
  buildRawLambdaFromLambdaFunction,
39
+ reportGraphAnalytics,
38
40
  } from '@finos/legend-graph';
39
41
  import { buildLambdaFunction } from './QueryBuilderValueSpecificationBuilder.js';
40
42
  import { ExecutionPlanState } from '@finos/legend-application';
@@ -43,6 +45,8 @@ import {
43
45
  getExecutionQueryFromRawLambda,
44
46
  } from './shared/LambdaParameterState.js';
45
47
  import type { LambdaFunctionBuilderOption } from './QueryBuilderValueSpecificationBuilderHelper.js';
48
+ import { QueryBuilderTelemetry } from './QueryBuilderTelemetry.js';
49
+ import { QUERY_BUILDER_EVENT } from './QueryBuilderEvent.js';
46
50
 
47
51
  const DEFAULT_LIMIT = 1000;
48
52
 
@@ -219,7 +223,21 @@ export class QueryBuilderResultState {
219
223
  this.queryBuilderState.parametersState.parameterStates,
220
224
  this.queryBuilderState.graphManagerState,
221
225
  );
222
- const startTime = Date.now();
226
+
227
+ QueryBuilderTelemetry.logEvent_QueryRunLaunched(
228
+ this.queryBuilderState.applicationStore.telemetryService,
229
+ this.queryBuilderState.applicationContext
230
+ ? {
231
+ applicationContext: this.queryBuilderState.applicationContext,
232
+ }
233
+ : {},
234
+ );
235
+
236
+ const stopWatch = new StopWatch();
237
+ const report = reportGraphAnalytics(
238
+ this.queryBuilderState.graphManagerState.graph,
239
+ );
240
+
223
241
  const promise =
224
242
  this.queryBuilderState.graphManagerState.graphManager.runQuery(
225
243
  query,
@@ -230,12 +248,22 @@ export class QueryBuilderResultState {
230
248
  parameterValues,
231
249
  },
232
250
  );
251
+
233
252
  this.setQueryRunPromise(promise);
234
253
  const result = (yield promise) as ExecutionResult;
235
254
  if (this.queryRunPromise === promise) {
236
255
  this.setExecutionResult(result);
237
256
  this.latestRunHashCode = currentHashCode;
238
- this.setExecutionDuration(Date.now() - startTime);
257
+ this.setExecutionDuration(stopWatch.elapsed);
258
+
259
+ report.timings = {
260
+ ...report.timings,
261
+ total: stopWatch.elapsed,
262
+ };
263
+ QueryBuilderTelemetry.logEvent_QueryRunSucceeded(
264
+ this.queryBuilderState.applicationStore.telemetryService,
265
+ report,
266
+ );
239
267
  }
240
268
  } catch (error) {
241
269
  assertErrorThrown(error);
@@ -263,25 +291,51 @@ export class QueryBuilderResultState {
263
291
  );
264
292
  const query = this.queryBuilderState.buildQuery();
265
293
  let rawPlan: RawExecutionPlan;
294
+
295
+ const stopWatch = new StopWatch();
296
+ const report = reportGraphAnalytics(
297
+ this.queryBuilderState.graphManagerState.graph,
298
+ );
299
+
266
300
  if (debug) {
301
+ QueryBuilderTelemetry.logEvent_ExecutionPlanDebugLaunched(
302
+ this.queryBuilderState.applicationStore.telemetryService,
303
+ this.queryBuilderState.applicationContext
304
+ ? {
305
+ applicationContext: this.queryBuilderState.applicationContext,
306
+ }
307
+ : {},
308
+ );
267
309
  const debugResult =
268
310
  (yield this.queryBuilderState.graphManagerState.graphManager.debugExecutionPlanGeneration(
269
311
  query,
270
312
  mapping,
271
313
  runtime,
272
314
  this.queryBuilderState.graphManagerState.graph,
315
+ report,
273
316
  )) as { plan: RawExecutionPlan; debug: string };
274
317
  rawPlan = debugResult.plan;
275
318
  this.executionPlanState.setDebugText(debugResult.debug);
276
319
  } else {
320
+ QueryBuilderTelemetry.logEvent_ExecutionPlanGenerationLaunched(
321
+ this.queryBuilderState.applicationStore.telemetryService,
322
+ this.queryBuilderState.applicationContext
323
+ ? {
324
+ applicationContext: this.queryBuilderState.applicationContext,
325
+ }
326
+ : {},
327
+ );
277
328
  rawPlan =
278
329
  (yield this.queryBuilderState.graphManagerState.graphManager.generateExecutionPlan(
279
330
  query,
280
331
  mapping,
281
332
  runtime,
282
333
  this.queryBuilderState.graphManagerState.graph,
334
+ report,
283
335
  )) as object;
284
336
  }
337
+
338
+ stopWatch.record();
285
339
  try {
286
340
  this.executionPlanState.setRawPlan(rawPlan);
287
341
  const plan =
@@ -293,6 +347,24 @@ export class QueryBuilderResultState {
293
347
  } catch {
294
348
  // do nothing
295
349
  }
350
+ stopWatch.record(QUERY_BUILDER_EVENT.BUILD_EXECUTION_PLAN__SUCCESS);
351
+
352
+ report.timings = {
353
+ ...report.timings,
354
+ ...Object.fromEntries(stopWatch.records),
355
+ total: stopWatch.elapsed,
356
+ };
357
+ if (debug) {
358
+ QueryBuilderTelemetry.logEvent_ExecutionPlanDebugSucceeded(
359
+ this.queryBuilderState.applicationStore.telemetryService,
360
+ report,
361
+ );
362
+ } else {
363
+ QueryBuilderTelemetry.logEvent_ExecutionPlanGenerationSucceeded(
364
+ this.queryBuilderState.applicationStore.telemetryService,
365
+ report,
366
+ );
367
+ }
296
368
  } catch (error) {
297
369
  assertErrorThrown(error);
298
370
  this.queryBuilderState.applicationStore.log.error(
@@ -99,6 +99,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
99
99
  textEditorState: QueryBuilderTextEditorState;
100
100
  unsupportedQueryState: QueryBuilderUnsupportedQueryState;
101
101
  observableContext: ObserverContext;
102
+ titleOfQuery: string | undefined;
102
103
 
103
104
  queryCompileState = ActionState.create();
104
105
  showFunctionsExplorerPanel = false;
@@ -110,6 +111,8 @@ export abstract class QueryBuilderState implements CommandRegistrar {
110
111
  mapping?: Mapping | undefined;
111
112
  runtimeValue?: Runtime | undefined;
112
113
 
114
+ applicationContext?: string | undefined;
115
+
113
116
  // NOTE: this makes it so that we need to import components in stores code,
114
117
  // we probably want to refactor to an extension mechanism
115
118
  TEMPORARY__setupPanelContentRenderer?: (() => React.ReactNode) | undefined;
@@ -126,6 +129,7 @@ export abstract class QueryBuilderState implements CommandRegistrar {
126
129
  fetchStructureState: observable,
127
130
  filterState: observable,
128
131
  watermarkState: observable,
132
+ titleOfQuery: observable,
129
133
  checkEntitlementsState: observable,
130
134
  resultState: observable,
131
135
  textEditorState: observable,
@@ -151,6 +155,8 @@ export abstract class QueryBuilderState implements CommandRegistrar {
151
155
  setMapping: action,
152
156
  setRuntimeValue: action,
153
157
 
158
+ setTitleOfQuery: action,
159
+
154
160
  resetQueryResult: action,
155
161
  resetQueryContent: action,
156
162
  changeClass: action,
@@ -253,6 +259,10 @@ export abstract class QueryBuilderState implements CommandRegistrar {
253
259
  this.runtimeValue = val;
254
260
  }
255
261
 
262
+ setTitleOfQuery(val: string | undefined): void {
263
+ this.titleOfQuery = val;
264
+ }
265
+
256
266
  get isQuerySupported(): boolean {
257
267
  return !this.unsupportedQueryState.rawLambda;
258
268
  }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Copyright (c) 2020-present, Goldman Sachs
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import type { GraphManagerOperationReport } from '@finos/legend-graph';
18
+ import type { TelemetryService } from '@finos/legend-shared';
19
+ import { QUERY_BUILDER_EVENT } from './QueryBuilderEvent.js';
20
+
21
+ type LaunchQueryExecution_TelemteryData = {
22
+ applicationContext?: string | undefined;
23
+ };
24
+
25
+ type QueryExecution_TelemetryData = GraphManagerOperationReport & {
26
+ dependenciesCount: number;
27
+ };
28
+
29
+ export class QueryBuilderTelemetry {
30
+ static logEvent_QueryRunLaunched(
31
+ telemetryService: TelemetryService,
32
+ data: LaunchQueryExecution_TelemteryData,
33
+ ): void {
34
+ telemetryService.logEvent(QUERY_BUILDER_EVENT.RUN_QUERY__LAUNCH, data);
35
+ }
36
+
37
+ static logEvent_ExecutionPlanGenerationLaunched(
38
+ telemetryService: TelemetryService,
39
+ data: LaunchQueryExecution_TelemteryData,
40
+ ): void {
41
+ telemetryService.logEvent(
42
+ QUERY_BUILDER_EVENT.GENERATE_EXECUTION_PLAN__LAUNCH,
43
+ data,
44
+ );
45
+ }
46
+
47
+ static logEvent_ExecutionPlanDebugLaunched(
48
+ telemetryService: TelemetryService,
49
+ data: LaunchQueryExecution_TelemteryData,
50
+ ): void {
51
+ telemetryService.logEvent(
52
+ QUERY_BUILDER_EVENT.DEBUG_EXECUTION_PLAN__LAUNCH,
53
+ data,
54
+ );
55
+ }
56
+
57
+ static logEvent_QueryRunSucceeded(
58
+ telemetryService: TelemetryService,
59
+ data: QueryExecution_TelemetryData,
60
+ ): void {
61
+ telemetryService.logEvent(QUERY_BUILDER_EVENT.RUN_QUERY__SUCCESS, data);
62
+ }
63
+
64
+ static logEvent_ExecutionPlanGenerationSucceeded(
65
+ telemetryService: TelemetryService,
66
+ data: QueryExecution_TelemetryData,
67
+ ): void {
68
+ telemetryService.logEvent(
69
+ QUERY_BUILDER_EVENT.GENERATE_EXECUTION_PLAN__SUCCESS,
70
+ data,
71
+ );
72
+ }
73
+
74
+ static logEvent_ExecutionPlanDebugSucceeded(
75
+ telemetryService: TelemetryService,
76
+ data: QueryExecution_TelemetryData,
77
+ ): void {
78
+ telemetryService.logEvent(
79
+ QUERY_BUILDER_EVENT.DEBUG_EXECUTION_PLAN__SUCCESS,
80
+ data,
81
+ );
82
+ }
83
+ }
package/tsconfig.json CHANGED
@@ -53,6 +53,7 @@
53
53
  "./src/stores/QueryBuilderCommand.ts",
54
54
  "./src/stores/QueryBuilderConfig.ts",
55
55
  "./src/stores/QueryBuilderConstantsState.ts",
56
+ "./src/stores/QueryBuilderEvent.ts",
56
57
  "./src/stores/QueryBuilderGroupOperationHelper.ts",
57
58
  "./src/stores/QueryBuilderParametersState.ts",
58
59
  "./src/stores/QueryBuilderPreviewDataHelper.ts",
@@ -61,6 +62,7 @@
61
62
  "./src/stores/QueryBuilderState.ts",
62
63
  "./src/stores/QueryBuilderStateBuilder.ts",
63
64
  "./src/stores/QueryBuilderStateTestUtils.ts",
65
+ "./src/stores/QueryBuilderTelemetry.ts",
64
66
  "./src/stores/QueryBuilderTextEditorState.ts",
65
67
  "./src/stores/QueryBuilderTypeaheadHelper.ts",
66
68
  "./src/stores/QueryBuilderUnsupportedQueryState.ts",