@finos/legend-query-builder 4.19.12 → 4.19.13
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.
- package/lib/__lib__/QueryBuilderDocumentation.d.ts +2 -1
- package/lib/__lib__/QueryBuilderDocumentation.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderDocumentation.js +1 -0
- package/lib/__lib__/QueryBuilderDocumentation.js.map +1 -1
- package/lib/__lib__/QueryBuilderEvent.d.ts +45 -1
- package/lib/__lib__/QueryBuilderEvent.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderEvent.js +48 -0
- package/lib/__lib__/QueryBuilderEvent.js.map +1 -1
- package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts +162 -8
- package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts.map +1 -1
- package/lib/__lib__/QueryBuilderTelemetryHelper.js +48 -0
- package/lib/__lib__/QueryBuilderTelemetryHelper.js.map +1 -1
- package/lib/components/QueryBuilder.d.ts.map +1 -1
- package/lib/components/QueryBuilder.js +9 -5
- package/lib/components/QueryBuilder.js.map +1 -1
- package/lib/components/QueryBuilderConstantExpressionPanel.d.ts.map +1 -1
- package/lib/components/QueryBuilderConstantExpressionPanel.js +18 -1
- package/lib/components/QueryBuilderConstantExpressionPanel.js.map +1 -1
- package/lib/components/QueryBuilderParametersPanel.d.ts.map +1 -1
- package/lib/components/QueryBuilderParametersPanel.js +18 -1
- package/lib/components/QueryBuilderParametersPanel.js.map +1 -1
- package/lib/components/QueryBuilderSideBar.d.ts.map +1 -1
- package/lib/components/QueryBuilderSideBar.js +19 -0
- package/lib/components/QueryBuilderSideBar.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.js +42 -1
- package/lib/components/fetch-structure/QueryBuilderGraphFetchTreePanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js +25 -5
- package/lib/components/fetch-structure/QueryBuilderPostFilterPanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +55 -0
- package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +95 -8
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js +37 -4
- package/lib/components/fetch-structure/QueryBuilderTDSWindowPanel.js.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.d.ts.map +1 -1
- package/lib/components/filter/QueryBuilderFilterPanel.js +24 -5
- package/lib/components/filter/QueryBuilderFilterPanel.js.map +1 -1
- package/lib/components/result/QueryBuilderDataProductErrorPanel.d.ts +61 -0
- package/lib/components/result/QueryBuilderDataProductErrorPanel.d.ts.map +1 -0
- package/lib/components/result/QueryBuilderDataProductErrorPanel.js +176 -0
- package/lib/components/result/QueryBuilderDataProductErrorPanel.js.map +1 -0
- package/lib/components/result/QueryBuilderResultPanel.d.ts.map +1 -1
- package/lib/components/result/QueryBuilderResultPanel.js +24 -18
- package/lib/components/result/QueryBuilderResultPanel.js.map +1 -1
- package/lib/data-access-overview.css +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderResultState.d.ts +11 -1
- package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderResultState.js +79 -15
- package/lib/stores/QueryBuilderResultState.js.map +1 -1
- package/lib/stores/QueryBuilderState.d.ts +131 -9
- package/lib/stores/QueryBuilderState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderState.js +124 -21
- package/lib/stores/QueryBuilderState.js.map +1 -1
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +13 -0
- package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -1
- package/lib/stores/QueryLoaderState.d.ts +33 -5
- package/lib/stores/QueryLoaderState.d.ts.map +1 -1
- package/lib/stores/QueryLoaderState.js +10 -1
- package/lib/stores/QueryLoaderState.js.map +1 -1
- package/lib/stores/data-access/DataProductAccessInfo.d.ts +37 -0
- package/lib/stores/data-access/DataProductAccessInfo.d.ts.map +1 -0
- package/lib/stores/data-access/DataProductAccessInfo.js +17 -0
- package/lib/stores/data-access/DataProductAccessInfo.js.map +1 -0
- package/lib/stores/data-cube/QueryBuilderDataCubeEngine.js +1 -1
- package/lib/stores/data-cube/QueryBuilderDataCubeEngine.js.map +1 -1
- package/lib/stores/explorer/QueryBuilderExplorerState.js +1 -1
- package/lib/stores/explorer/QueryBuilderExplorerState.js.map +1 -1
- package/lib/stores/milestoning/QueryBuilderMilestoningHelper.d.ts.map +1 -1
- package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js +34 -9
- package/lib/stores/milestoning/QueryBuilderMilestoningHelper.js.map +1 -1
- package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts +5 -0
- package/lib/stores/milestoning/QueryBuilderMilestoningState.d.ts.map +1 -1
- package/lib/stores/milestoning/QueryBuilderMilestoningState.js +13 -0
- package/lib/stores/milestoning/QueryBuilderMilestoningState.js.map +1 -1
- package/lib/stores/workflows/dataProduct/DataProductQueryBuilderState.d.ts +2 -0
- package/lib/stores/workflows/dataProduct/DataProductQueryBuilderState.d.ts.map +1 -1
- package/lib/stores/workflows/dataProduct/DataProductQueryBuilderState.js +50 -1
- package/lib/stores/workflows/dataProduct/DataProductQueryBuilderState.js.map +1 -1
- package/package.json +10 -10
- package/src/__lib__/QueryBuilderDocumentation.ts +1 -0
- package/src/__lib__/QueryBuilderEvent.ts +52 -0
- package/src/__lib__/QueryBuilderTelemetryHelper.ts +292 -8
- package/src/components/QueryBuilder.tsx +31 -23
- package/src/components/QueryBuilderConstantExpressionPanel.tsx +24 -2
- package/src/components/QueryBuilderParametersPanel.tsx +25 -2
- package/src/components/QueryBuilderSideBar.tsx +28 -0
- package/src/components/fetch-structure/QueryBuilderGraphFetchTreePanel.tsx +56 -1
- package/src/components/fetch-structure/QueryBuilderPostFilterPanel.tsx +31 -4
- package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +74 -0
- package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +123 -8
- package/src/components/fetch-structure/QueryBuilderTDSWindowPanel.tsx +49 -4
- package/src/components/filter/QueryBuilderFilterPanel.tsx +31 -4
- package/src/components/result/QueryBuilderDataProductErrorPanel.tsx +441 -0
- package/src/components/result/QueryBuilderResultPanel.tsx +49 -32
- package/src/index.ts +1 -0
- package/src/stores/QueryBuilderResultState.ts +107 -19
- package/src/stores/QueryBuilderState.ts +239 -29
- package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +20 -0
- package/src/stores/QueryLoaderState.ts +47 -6
- package/src/stores/data-access/DataProductAccessInfo.ts +37 -0
- package/src/stores/data-cube/QueryBuilderDataCubeEngine.ts +1 -1
- package/src/stores/explorer/QueryBuilderExplorerState.ts +1 -1
- package/src/stores/milestoning/QueryBuilderMilestoningHelper.ts +63 -8
- package/src/stores/milestoning/QueryBuilderMilestoningState.ts +20 -0
- package/src/stores/workflows/dataProduct/DataProductQueryBuilderState.ts +68 -0
- package/tsconfig.json +2 -0
|
@@ -24,6 +24,8 @@ import {
|
|
|
24
24
|
ActionState,
|
|
25
25
|
StopWatch,
|
|
26
26
|
getContentTypeFileExtension,
|
|
27
|
+
buildTelemetryErrorFields,
|
|
28
|
+
type TimingsRecord,
|
|
27
29
|
} from '@finos/legend-shared';
|
|
28
30
|
import type { QueryBuilderState } from './QueryBuilderState.js';
|
|
29
31
|
import {
|
|
@@ -49,7 +51,10 @@ import {
|
|
|
49
51
|
getExecutionQueryFromRawLambda,
|
|
50
52
|
} from './shared/LambdaParameterState.js';
|
|
51
53
|
import type { LambdaFunctionBuilderOption } from './QueryBuilderValueSpecificationBuilderHelper.js';
|
|
52
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
QueryBuilderTelemetryHelper,
|
|
56
|
+
type QueryExecutionFailure_TelemetryData,
|
|
57
|
+
} from '../__lib__/QueryBuilderTelemetryHelper.js';
|
|
53
58
|
import { QUERY_BUILDER_EVENT } from '../__lib__/QueryBuilderEvent.js';
|
|
54
59
|
import { ExecutionPlanState } from './execution-plan/ExecutionPlanState.js';
|
|
55
60
|
import type { DataGridColumnState } from '@finos/legend-lego/data-grid';
|
|
@@ -392,6 +397,27 @@ export class QueryBuilderResultState {
|
|
|
392
397
|
return query;
|
|
393
398
|
}
|
|
394
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Builds the shared payload for query execution / plan / export failure
|
|
402
|
+
* telemetry, so all four failure callsites emit the same set of dimensions.
|
|
403
|
+
*/
|
|
404
|
+
buildFailureTelemetryData(
|
|
405
|
+
error: Error,
|
|
406
|
+
extra?: {
|
|
407
|
+
executionDurationMs?: number | undefined;
|
|
408
|
+
executionTraceId?: string | undefined;
|
|
409
|
+
timings?: TimingsRecord | undefined;
|
|
410
|
+
},
|
|
411
|
+
): QueryExecutionFailure_TelemetryData {
|
|
412
|
+
return {
|
|
413
|
+
...buildTelemetryErrorFields(error),
|
|
414
|
+
queryInfo: this.queryBuilderState.safeGetQueryInfo(),
|
|
415
|
+
...extra,
|
|
416
|
+
...this.queryBuilderState.safeGetTelemetryContext(),
|
|
417
|
+
...this.queryBuilderState.safeGetExtraTelemetryMetadata(),
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
395
421
|
*exportData(format: string): GeneratorFn<void> {
|
|
396
422
|
try {
|
|
397
423
|
this.exportState.inProgress();
|
|
@@ -450,7 +476,7 @@ export class QueryBuilderResultState {
|
|
|
450
476
|
const reportWithState = Object.assign(
|
|
451
477
|
{},
|
|
452
478
|
report,
|
|
453
|
-
this.queryBuilderState.
|
|
479
|
+
this.queryBuilderState.safeGetTelemetryContext(),
|
|
454
480
|
);
|
|
455
481
|
QueryBuilderTelemetryHelper.logEvent_ExportQueryDataSucceeded(
|
|
456
482
|
this.queryBuilderState.applicationStore.telemetryService,
|
|
@@ -464,6 +490,13 @@ export class QueryBuilderResultState {
|
|
|
464
490
|
LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
|
|
465
491
|
error,
|
|
466
492
|
);
|
|
493
|
+
QueryBuilderTelemetryHelper.logEvent_ExportQueryDataFailed(
|
|
494
|
+
this.queryBuilderState.applicationStore.telemetryService,
|
|
495
|
+
this.buildFailureTelemetryData(error),
|
|
496
|
+
);
|
|
497
|
+
// without this the action state is left IN_PROGRESS forever when a
|
|
498
|
+
// download fails, unlike the `.then` branch which passes it
|
|
499
|
+
this.exportState.fail();
|
|
467
500
|
});
|
|
468
501
|
} catch (error) {
|
|
469
502
|
this.exportState.fail();
|
|
@@ -475,12 +508,26 @@ export class QueryBuilderResultState {
|
|
|
475
508
|
this.queryBuilderState.applicationStore.notificationService.notifyError(
|
|
476
509
|
error,
|
|
477
510
|
);
|
|
478
|
-
|
|
511
|
+
QueryBuilderTelemetryHelper.logEvent_ExportQueryDataFailed(
|
|
512
|
+
this.queryBuilderState.applicationStore.telemetryService,
|
|
513
|
+
this.buildFailureTelemetryData(error),
|
|
514
|
+
);
|
|
515
|
+
// NOTE: no `complete()` here — it defaults to `hasSucceeded = true`, so
|
|
516
|
+
// calling it after `fail()` above flipped the state back to SUCCEEDED and
|
|
517
|
+
// masked the failure in the UI
|
|
479
518
|
}
|
|
480
519
|
}
|
|
481
520
|
|
|
482
521
|
*runQuery(): GeneratorFn<void> {
|
|
483
522
|
let promise;
|
|
523
|
+
// hoisted so the catch below can report the phase timings collected up to
|
|
524
|
+
// the point of failure — the absence of an engine server-call lap is itself
|
|
525
|
+
// the signal that the query never made it out of the client
|
|
526
|
+
// NOTE: `reportGraphAnalytics` widens the report with `dependenciesCount`;
|
|
527
|
+
// declaring the narrower `GraphManagerOperationReport` here would drop it
|
|
528
|
+
// from the success payload's type
|
|
529
|
+
let report: ReturnType<typeof reportGraphAnalytics> | undefined;
|
|
530
|
+
const stopWatch = new StopWatch();
|
|
484
531
|
try {
|
|
485
532
|
this.setIsRunningQuery(true);
|
|
486
533
|
const currentHashCode = this.queryBuilderState.hashCode;
|
|
@@ -502,13 +549,18 @@ export class QueryBuilderResultState {
|
|
|
502
549
|
this.queryBuilderState.graphManagerState,
|
|
503
550
|
);
|
|
504
551
|
|
|
552
|
+
stopWatch.record(QUERY_BUILDER_EVENT.RUN_QUERY__PREPARE);
|
|
553
|
+
|
|
505
554
|
QueryBuilderTelemetryHelper.logEvent_QueryRunLaunched(
|
|
506
555
|
this.queryBuilderState.applicationStore.telemetryService,
|
|
507
|
-
|
|
556
|
+
{
|
|
557
|
+
...this.queryBuilderState.safeGetTelemetryContext(),
|
|
558
|
+
queryInfo: this.queryBuilderState.safeGetQueryInfo(),
|
|
559
|
+
...this.queryBuilderState.safeGetExtraTelemetryMetadata(),
|
|
560
|
+
},
|
|
508
561
|
);
|
|
509
562
|
|
|
510
|
-
|
|
511
|
-
const report = reportGraphAnalytics(
|
|
563
|
+
report = reportGraphAnalytics(
|
|
512
564
|
this.queryBuilderState.graphManagerState.graph,
|
|
513
565
|
);
|
|
514
566
|
const contextstate = this.queryBuilderState.executionContextState;
|
|
@@ -527,16 +579,25 @@ export class QueryBuilderResultState {
|
|
|
527
579
|
floatingExecutionElements:
|
|
528
580
|
this.queryBuilderState.floatingExecutionElements,
|
|
529
581
|
},
|
|
582
|
+
// passing the report is what surfaces the graph manager's own engine
|
|
583
|
+
// laps (`V1_ENGINE_OPERATION_INPUT__SUCCESS` / `..._SERVER_CALL__SUCCESS`)
|
|
584
|
+
// in `report.timings` — without it they are computed and discarded
|
|
585
|
+
report,
|
|
530
586
|
);
|
|
531
587
|
|
|
532
588
|
this.setQueryRunPromise(promise);
|
|
533
589
|
const result = (yield promise) as ExecutionResultWithMetadata;
|
|
534
590
|
if (this.queryRunPromise === promise) {
|
|
591
|
+
// close the engine lap without naming it: the graph manager has already
|
|
592
|
+
// recorded that span in `report.timings`, so recording it again here
|
|
593
|
+
// would double-count it
|
|
594
|
+
stopWatch.record();
|
|
535
595
|
this.processExecutionResult(result.executionResult);
|
|
536
596
|
if (result.executionTraceId) {
|
|
537
597
|
this.setExecutionTraceId(result.executionTraceId);
|
|
538
598
|
}
|
|
539
599
|
this.latestRunHashCode = currentHashCode;
|
|
600
|
+
stopWatch.record(QUERY_BUILDER_EVENT.RUN_QUERY__PROCESS_RESULT);
|
|
540
601
|
this.setExecutionDuration(stopWatch.elapsed);
|
|
541
602
|
|
|
542
603
|
report.timings =
|
|
@@ -544,12 +605,16 @@ export class QueryBuilderResultState {
|
|
|
544
605
|
stopWatch,
|
|
545
606
|
report.timings,
|
|
546
607
|
);
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
608
|
+
// NOTE: spread rather than `Object.assign` — with four sources the
|
|
609
|
+
// latter falls through to the `(...sources: any[]) => any` overload,
|
|
610
|
+
// which silently drops type checking on the whole payload
|
|
611
|
+
const reportWithState = {
|
|
612
|
+
...report,
|
|
613
|
+
...this.queryBuilderState.safeGetTelemetryContext(),
|
|
614
|
+
...this.queryBuilderState.safeGetExtraTelemetryMetadata(),
|
|
615
|
+
queryInfo: this.queryBuilderState.safeGetQueryInfo(),
|
|
616
|
+
executionDurationMs: stopWatch.elapsed,
|
|
617
|
+
};
|
|
553
618
|
QueryBuilderTelemetryHelper.logEvent_QueryRunSucceeded(
|
|
554
619
|
this.queryBuilderState.applicationStore.telemetryService,
|
|
555
620
|
reportWithState,
|
|
@@ -571,11 +636,18 @@ export class QueryBuilderResultState {
|
|
|
571
636
|
}
|
|
572
637
|
QueryBuilderTelemetryHelper.logEvent_QueryRunFailed(
|
|
573
638
|
this.queryBuilderState.applicationStore.telemetryService,
|
|
574
|
-
{
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
639
|
+
this.buildFailureTelemetryData(error, {
|
|
640
|
+
executionDurationMs: stopWatch.elapsed,
|
|
641
|
+
executionTraceId:
|
|
642
|
+
error instanceof ExecutionError
|
|
643
|
+
? error.executionTraceId
|
|
644
|
+
: undefined,
|
|
645
|
+
timings:
|
|
646
|
+
this.queryBuilderState.applicationStore.timeService.finalizeTimingsRecord(
|
|
647
|
+
stopWatch,
|
|
648
|
+
report?.timings,
|
|
649
|
+
),
|
|
650
|
+
}),
|
|
579
651
|
);
|
|
580
652
|
}
|
|
581
653
|
} finally {
|
|
@@ -594,7 +666,11 @@ export class QueryBuilderResultState {
|
|
|
594
666
|
);
|
|
595
667
|
QueryBuilderTelemetryHelper.logEvent_QueryRunCancelled(
|
|
596
668
|
this.queryBuilderState.applicationStore.telemetryService,
|
|
597
|
-
|
|
669
|
+
{
|
|
670
|
+
...this.queryBuilderState.safeGetTelemetryContext(),
|
|
671
|
+
queryInfo: this.queryBuilderState.safeGetQueryInfo(),
|
|
672
|
+
...this.queryBuilderState.safeGetExtraTelemetryMetadata(),
|
|
673
|
+
},
|
|
598
674
|
);
|
|
599
675
|
} catch (error) {
|
|
600
676
|
// Don't notify users about success or failure
|
|
@@ -689,7 +765,7 @@ export class QueryBuilderResultState {
|
|
|
689
765
|
const reportWithState = Object.assign(
|
|
690
766
|
{},
|
|
691
767
|
report,
|
|
692
|
-
this.queryBuilderState.
|
|
768
|
+
this.queryBuilderState.safeGetTelemetryContext(),
|
|
693
769
|
);
|
|
694
770
|
if (debug) {
|
|
695
771
|
QueryBuilderTelemetryHelper.logEvent_ExecutionPlanDebugSucceeded(
|
|
@@ -711,6 +787,18 @@ export class QueryBuilderResultState {
|
|
|
711
787
|
this.queryBuilderState.applicationStore.notificationService.notifyError(
|
|
712
788
|
error,
|
|
713
789
|
);
|
|
790
|
+
const failurePayload = this.buildFailureTelemetryData(error);
|
|
791
|
+
if (debug) {
|
|
792
|
+
QueryBuilderTelemetryHelper.logEvent_ExecutionPlanDebugFailed(
|
|
793
|
+
this.queryBuilderState.applicationStore.telemetryService,
|
|
794
|
+
failurePayload,
|
|
795
|
+
);
|
|
796
|
+
} else {
|
|
797
|
+
QueryBuilderTelemetryHelper.logEvent_ExecutionPlanGenerationFailed(
|
|
798
|
+
this.queryBuilderState.applicationStore.telemetryService,
|
|
799
|
+
failurePayload,
|
|
800
|
+
);
|
|
801
|
+
}
|
|
714
802
|
} finally {
|
|
715
803
|
this.isGeneratingPlan = false;
|
|
716
804
|
}
|
|
@@ -84,9 +84,10 @@ import {
|
|
|
84
84
|
buildLambdaFunction,
|
|
85
85
|
buildExecutionContextState,
|
|
86
86
|
} from './QueryBuilderValueSpecificationBuilder.js';
|
|
87
|
-
import
|
|
88
|
-
CommandRegistrar,
|
|
89
|
-
GenericLegendApplicationStore,
|
|
87
|
+
import {
|
|
88
|
+
type CommandRegistrar,
|
|
89
|
+
type GenericLegendApplicationStore,
|
|
90
|
+
APPLICATION_EVENT,
|
|
90
91
|
} from '@finos/legend-application';
|
|
91
92
|
import { QueryFunctionsExplorerState } from './explorer/QueryFunctionsExplorerState.js';
|
|
92
93
|
import {
|
|
@@ -96,13 +97,17 @@ import {
|
|
|
96
97
|
import type { QueryBuilderFilterOperator } from './filter/QueryBuilderFilterOperator.js';
|
|
97
98
|
import { getQueryBuilderCoreFilterOperators } from './filter/QueryBuilderFilterOperatorLoader.js';
|
|
98
99
|
import { QueryBuilderChangeDetectionState } from './QueryBuilderChangeDetectionState.js';
|
|
99
|
-
import {
|
|
100
|
+
import {
|
|
101
|
+
QueryBuilderMilestoningState,
|
|
102
|
+
type QueryBuilderMilestoningKind,
|
|
103
|
+
} from './milestoning/QueryBuilderMilestoningState.js';
|
|
100
104
|
import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from './QueryBuilderStateHashUtils.js';
|
|
101
105
|
import { QUERY_BUILDER_COMMAND_KEY } from './QueryBuilderCommand.js';
|
|
102
106
|
import { QueryBuilderWatermarkState } from './watermark/QueryBuilderWatermarkState.js';
|
|
103
107
|
import { QueryBuilderConstantsState } from './QueryBuilderConstantsState.js';
|
|
104
108
|
import { QueryBuilderCheckEntitlementsState } from './entitlements/QueryBuilderCheckEntitlementsState.js';
|
|
105
109
|
import { QueryBuilderTDSState } from './fetch-structure/tds/QueryBuilderTDSState.js';
|
|
110
|
+
import { QueryBuilderRelationColumnProjectionColumnState } from './fetch-structure/tds/projection/QueryBuilderProjectionColumnState.js';
|
|
106
111
|
import {
|
|
107
112
|
QUERY_BUILDER_PURE_PATH,
|
|
108
113
|
QUERY_BUILDER_SUPPORTED_GET_ALL_FUNCTIONS,
|
|
@@ -114,7 +119,10 @@ import {
|
|
|
114
119
|
type QueryBuilderExecutionContextState,
|
|
115
120
|
} from './QueryBuilderExecutionContextState.js';
|
|
116
121
|
import type { QueryBuilderConfig } from '../graph-manager/QueryBuilderConfig.js';
|
|
117
|
-
import {
|
|
122
|
+
import {
|
|
123
|
+
QUERY_BUILDER_EVENT,
|
|
124
|
+
type QUERY_BUILDER_OPENED_FROM,
|
|
125
|
+
} from '../__lib__/QueryBuilderEvent.js';
|
|
118
126
|
import { QUERY_BUILDER_SETTING_KEY } from '../__lib__/QueryBuilderSetting.js';
|
|
119
127
|
import { QueryBuilderChangeHistoryState } from './QueryBuilderChangeHistoryState.js';
|
|
120
128
|
import { type QueryBuilderWorkflowState } from './query-workflow/QueryBuilderWorkFlowState.js';
|
|
@@ -127,11 +135,79 @@ import type {
|
|
|
127
135
|
DepotEntityWithOrigin,
|
|
128
136
|
QueryableSourceInfo,
|
|
129
137
|
} from '@finos/legend-storage';
|
|
138
|
+
import type { FETCH_STRUCTURE_IMPLEMENTATION } from './fetch-structure/QueryBuilderFetchStructureImplementationState.js';
|
|
130
139
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
/**
|
|
141
|
+
* The execution context the query builder has resolved to, as reported in
|
|
142
|
+
* telemetry under the `state` key. See
|
|
143
|
+
* {@link QueryBuilderState.getExecutionContextInfo}.
|
|
144
|
+
*/
|
|
145
|
+
export type QueryBuilderExecutionContextInfo = {
|
|
146
|
+
class?: string | undefined;
|
|
147
|
+
mapping?: string | undefined;
|
|
148
|
+
runtime?: string | undefined;
|
|
149
|
+
/**
|
|
150
|
+
* `true` when the query runs against an inline (engineered) runtime rather
|
|
151
|
+
* than a `RuntimePointer`. Those have no element path to report, so `runtime`
|
|
152
|
+
* is absent — this flag keeps that population visible instead of leaving it
|
|
153
|
+
* indistinguishable from "no runtime selected yet".
|
|
154
|
+
*/
|
|
155
|
+
isInlineRuntime?: boolean | undefined;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The shared envelope carried by every query builder telemetry event.
|
|
160
|
+
*
|
|
161
|
+
* The entry point the builder was opened with (`sourceInfo` — the route) is
|
|
162
|
+
* spread *flat* at the top level, while the execution context it resolved to is
|
|
163
|
+
* nested under `state`. Keeping them separate is what lets a dashboard tell
|
|
164
|
+
* "arrived on mapping X" apart from "currently querying mapping X"; merging the
|
|
165
|
+
* two into one flat object would collapse that distinction.
|
|
166
|
+
*/
|
|
167
|
+
export type QueryBuilderTelemetryContext = QueryableSourceInfo &
|
|
168
|
+
/**
|
|
169
|
+
* `QueryableSourceInfo` is a marker interface with no declared members — the
|
|
170
|
+
* concrete keys (`sourceType`, `groupId`, `dataSpace`, `dataProduct`, …) vary
|
|
171
|
+
* by entry point and are only known to the application layer. The index
|
|
172
|
+
* signature is what lets those keys sit flat at the top level; it does mean
|
|
173
|
+
* top-level excess-property checking is off. The fields that matter are kept
|
|
174
|
+
* inside the strictly-typed `state` and `change` sub-objects for exactly this
|
|
175
|
+
* reason.
|
|
176
|
+
*/
|
|
177
|
+
Record<PropertyKey, unknown> & {
|
|
178
|
+
state?: QueryBuilderExecutionContextInfo | undefined;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Summary of the current query builder authoring state, used to enrich
|
|
183
|
+
* telemetry (e.g. query-execution events) with lightweight, non-PII shape
|
|
184
|
+
* information such as which fetch structure is in use and how many columns
|
|
185
|
+
* / filters / parameters have been configured.
|
|
186
|
+
*/
|
|
187
|
+
export type QueryBuilderQueryInfo = {
|
|
188
|
+
fetchStructureType: FETCH_STRUCTURE_IMPLEMENTATION | string;
|
|
189
|
+
/**
|
|
190
|
+
* `true` when the TDS query is authored against the typed relation function
|
|
191
|
+
* family (`->project`/`->groupBy`/etc. over relation columns). `false` for
|
|
192
|
+
* classic property-driven TDS. `undefined` when the fetch structure is not
|
|
193
|
+
* TDS or when there are no columns to determine yet.
|
|
194
|
+
*/
|
|
195
|
+
isTypedFetchStructure?: boolean | undefined;
|
|
196
|
+
parameterCount: number;
|
|
197
|
+
constantCount: number;
|
|
198
|
+
hasFilter: boolean;
|
|
199
|
+
filterNodeCount: number;
|
|
200
|
+
watermarkEnabled: boolean;
|
|
201
|
+
milestoningKind: QueryBuilderMilestoningKind;
|
|
202
|
+
// TDS-specific fields (present when fetchStructureType is TABULAR_DATA_STRUCTURE)
|
|
203
|
+
projectionColumnCount?: number | undefined;
|
|
204
|
+
windowColumnCount?: number | undefined;
|
|
205
|
+
aggregationColumnCount?: number | undefined;
|
|
206
|
+
postFilterNodeCount?: number | undefined;
|
|
207
|
+
hasLimit?: boolean | undefined;
|
|
208
|
+
hasDistinct?: boolean | undefined;
|
|
209
|
+
sortColumnCount?: number | undefined;
|
|
210
|
+
hasSlice?: boolean | undefined;
|
|
135
211
|
};
|
|
136
212
|
|
|
137
213
|
export type QueryBuilderExtraFunctionAnalysisInfo = {
|
|
@@ -525,28 +601,162 @@ export abstract class QueryBuilderState implements CommandRegistrar {
|
|
|
525
601
|
}
|
|
526
602
|
|
|
527
603
|
/**
|
|
528
|
-
* Gets
|
|
529
|
-
*
|
|
604
|
+
* Gets the execution context the query builder has currently *resolved to*:
|
|
605
|
+
* the class being queried and the mapping/runtime it will execute against.
|
|
606
|
+
*
|
|
607
|
+
* This is deliberately distinct from `sourceInfo`, which records what the
|
|
608
|
+
* builder was *opened with* (the route). For a mapping-sourced query both are
|
|
609
|
+
* populated and initially identical, so a divergence means the user switched;
|
|
610
|
+
* for a data space / data product / service query only this side carries a
|
|
611
|
+
* mapping and runtime, because the entry point resolves to one.
|
|
612
|
+
*
|
|
613
|
+
* Degrades gracefully — whichever of class / mapping / runtime are known get
|
|
614
|
+
* reported, so events that fire while the user is still setting up (picking a
|
|
615
|
+
* class before a mapping, say) are not blank. Returns `undefined` only when
|
|
616
|
+
* none of the three has resolved.
|
|
530
617
|
*/
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
mapping: mappingPath,
|
|
544
|
-
runtime: runtimePath,
|
|
545
|
-
};
|
|
546
|
-
return Object.assign({}, this.sourceInfo, contextInfo);
|
|
547
|
-
}
|
|
618
|
+
getExecutionContextInfo(): QueryBuilderExecutionContextInfo | undefined {
|
|
619
|
+
const classPath = this.sourceClass?.path;
|
|
620
|
+
const mappingPath = this.executionContextState.mapping?.path;
|
|
621
|
+
const runtimeValue = this.executionContextState.runtimeValue;
|
|
622
|
+
const runtimePath =
|
|
623
|
+
runtimeValue instanceof RuntimePointer
|
|
624
|
+
? runtimeValue.packageableRuntime.value.path
|
|
625
|
+
: undefined;
|
|
626
|
+
const isInlineRuntime =
|
|
627
|
+
runtimeValue !== undefined && !(runtimeValue instanceof RuntimePointer);
|
|
628
|
+
if (!classPath && !mappingPath && !runtimePath && !isInlineRuntime) {
|
|
629
|
+
return undefined;
|
|
548
630
|
}
|
|
549
|
-
return
|
|
631
|
+
return {
|
|
632
|
+
class: classPath,
|
|
633
|
+
mapping: mappingPath,
|
|
634
|
+
runtime: runtimePath,
|
|
635
|
+
isInlineRuntime: isInlineRuntime ? true : undefined,
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Gets a lightweight snapshot of the current query builder authoring state
|
|
641
|
+
* (fetch structure kind, filter/parameter/constant counts, milestoning /
|
|
642
|
+
* watermark configuration, etc.) for telemetry payloads. Kept intentionally
|
|
643
|
+
* shape-only — no user values, no identifiers.
|
|
644
|
+
*/
|
|
645
|
+
getQueryInfo(): QueryBuilderQueryInfo {
|
|
646
|
+
const base: QueryBuilderQueryInfo = {
|
|
647
|
+
fetchStructureType: this.fetchStructureState.implementation.type,
|
|
648
|
+
parameterCount: this.parametersState.parameterStates.length,
|
|
649
|
+
constantCount: this.constantState.constants.length,
|
|
650
|
+
hasFilter: !this.filterState.isEmpty,
|
|
651
|
+
filterNodeCount: this.filterState.nodes.size,
|
|
652
|
+
watermarkEnabled: this.watermarkState.value !== undefined,
|
|
653
|
+
milestoningKind: this.milestoningState.milestoningKind,
|
|
654
|
+
};
|
|
655
|
+
if (
|
|
656
|
+
this.fetchStructureState.implementation instanceof QueryBuilderTDSState
|
|
657
|
+
) {
|
|
658
|
+
const tdsState = this.fetchStructureState.implementation;
|
|
659
|
+
const modifier = tdsState.resultSetModifierState;
|
|
660
|
+
base.isTypedFetchStructure =
|
|
661
|
+
tdsState.projectionColumns.length > 0 &&
|
|
662
|
+
tdsState.projectionColumns.every(
|
|
663
|
+
(col) =>
|
|
664
|
+
col instanceof QueryBuilderRelationColumnProjectionColumnState,
|
|
665
|
+
);
|
|
666
|
+
base.projectionColumnCount = tdsState.projectionColumns.length;
|
|
667
|
+
base.windowColumnCount = tdsState.windowState.windowColumns.length;
|
|
668
|
+
base.aggregationColumnCount = tdsState.aggregationState.columns.length;
|
|
669
|
+
base.postFilterNodeCount = tdsState.postFilterState.nodes.size;
|
|
670
|
+
base.hasLimit = modifier.limit !== undefined;
|
|
671
|
+
base.hasDistinct = modifier.distinct;
|
|
672
|
+
base.sortColumnCount = modifier.sortColumns.length;
|
|
673
|
+
base.hasSlice = modifier.slice !== undefined;
|
|
674
|
+
}
|
|
675
|
+
return base;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Telemetry must never crash a user-visible action, so every `safeGet*`
|
|
680
|
+
* wrapper below swallows errors from its snapshot builder (state might be
|
|
681
|
+
* mid-construction, or a downstream computed getter might throw in edge
|
|
682
|
+
* cases) and logs them instead of throwing.
|
|
683
|
+
*/
|
|
684
|
+
private safeGetTelemetry<T>(getter: () => T, fallback: T, label: string): T {
|
|
685
|
+
try {
|
|
686
|
+
return getter();
|
|
687
|
+
} catch (error) {
|
|
688
|
+
this.applicationStore.logService.warn(
|
|
689
|
+
LogEvent.create(APPLICATION_EVENT.GENERIC_FAILURE),
|
|
690
|
+
label,
|
|
691
|
+
error,
|
|
692
|
+
);
|
|
693
|
+
return fallback;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Builds the shared telemetry envelope every query builder event carries: the
|
|
699
|
+
* entry point the builder was opened with, spread flat, plus the execution
|
|
700
|
+
* context it resolved to, nested under `state`. Spread this into a payload
|
|
701
|
+
* rather than assembling the two halves by hand at each callsite.
|
|
702
|
+
*/
|
|
703
|
+
safeGetTelemetryContext(): QueryBuilderTelemetryContext {
|
|
704
|
+
return this.safeGetTelemetry<QueryBuilderTelemetryContext>(
|
|
705
|
+
() => ({ ...this.sourceInfo, state: this.getExecutionContextInfo() }),
|
|
706
|
+
{},
|
|
707
|
+
'Failed to build query builder telemetry context',
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/**
|
|
712
|
+
* Reports that a query builder was opened, from whichever surface opened it.
|
|
713
|
+
* This is the canonical "a query builder exists" signal — hosts should prefer
|
|
714
|
+
* it over their own route-specific load events, which each cover only part of
|
|
715
|
+
* the population.
|
|
716
|
+
*
|
|
717
|
+
* Deliberately called by the host once the builder is *loaded*, rather than
|
|
718
|
+
* emitted from the constructor. The constructor runs before subclass field
|
|
719
|
+
* initializers, so the execution context would be unresolved and the `safeGet*`
|
|
720
|
+
* wrappers would silently report an empty envelope; it would also fire for
|
|
721
|
+
* derived states such as {@link INTERNAL__toBasicQueryBuilderState}, which are
|
|
722
|
+
* built on every data preview and are not user-facing opens.
|
|
723
|
+
*
|
|
724
|
+
* `extra` carries host-specific supplementary fields (load timings, and for
|
|
725
|
+
* Legend Query creators `restoredFromRecent`). It is untyped to keep this
|
|
726
|
+
* package host-agnostic.
|
|
727
|
+
*/
|
|
728
|
+
logOpened(
|
|
729
|
+
openedFrom: QUERY_BUILDER_OPENED_FROM,
|
|
730
|
+
extra?: Record<string, unknown> | undefined,
|
|
731
|
+
): void {
|
|
732
|
+
QueryBuilderTelemetryHelper.logEvent_QueryBuilderOpened(
|
|
733
|
+
this.applicationStore.telemetryService,
|
|
734
|
+
{
|
|
735
|
+
openedFrom,
|
|
736
|
+
...this.safeGetTelemetryContext(),
|
|
737
|
+
...extra,
|
|
738
|
+
},
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/**
|
|
743
|
+
* Prefer this over the raw `getQueryInfo()` method on the hot path of
|
|
744
|
+
* execution telemetry.
|
|
745
|
+
*/
|
|
746
|
+
safeGetQueryInfo(): QueryBuilderQueryInfo | undefined {
|
|
747
|
+
return this.safeGetTelemetry(
|
|
748
|
+
() => this.getQueryInfo(),
|
|
749
|
+
undefined,
|
|
750
|
+
'Failed to build query builder telemetry query info',
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
safeGetExtraTelemetryMetadata(): Record<string, unknown> {
|
|
755
|
+
return this.safeGetTelemetry(
|
|
756
|
+
() => this.getExtraTelemetryMetadata(),
|
|
757
|
+
{},
|
|
758
|
+
'Failed to build query builder extra telemetry metadata',
|
|
759
|
+
);
|
|
550
760
|
}
|
|
551
761
|
|
|
552
762
|
setIsAgentChatOpened(val: boolean): void {
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
DataAccessState,
|
|
22
22
|
DatasetAccessInfo,
|
|
23
23
|
} from './data-access/DataAccessState.js';
|
|
24
|
+
import type { DataProductAccessInfo } from './data-access/DataProductAccessInfo.js';
|
|
24
25
|
|
|
25
26
|
export type CuratedTemplateQuery = {
|
|
26
27
|
id: string;
|
|
@@ -67,6 +68,16 @@ export type WarehouseEntitlementRender = {
|
|
|
67
68
|
renderer: (dataAccessState: DataAccessState) => React.ReactNode;
|
|
68
69
|
};
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Builds the address where access to a data product can be requested (e.g. its
|
|
73
|
+
* Marketplace page). Returns `undefined` when the host application cannot produce
|
|
74
|
+
* one, in which case no request-access action is offered.
|
|
75
|
+
*/
|
|
76
|
+
export type DataProductAccessRequestLinkBuilder = (
|
|
77
|
+
info: DataProductAccessInfo,
|
|
78
|
+
queryBuilderState: QueryBuilderState,
|
|
79
|
+
) => string | undefined;
|
|
80
|
+
|
|
70
81
|
export type QueryAgentChatRenderer = (
|
|
71
82
|
queryBuilderState: QueryBuilderState,
|
|
72
83
|
) => React.ReactNode;
|
|
@@ -94,6 +105,9 @@ export type QueryBuilderMenuActionConfiguration = {
|
|
|
94
105
|
queryBuilderState: QueryBuilderState,
|
|
95
106
|
) => React.ReactNode;
|
|
96
107
|
disableMessage?: string | undefined;
|
|
108
|
+
getDisableMessage?:
|
|
109
|
+
| ((queryBuilderState: QueryBuilderState) => string | undefined)
|
|
110
|
+
| undefined;
|
|
97
111
|
};
|
|
98
112
|
|
|
99
113
|
export type QueryBuilderPropagateExecutionContextChangeHelper = (
|
|
@@ -140,6 +154,12 @@ export interface QueryBuilder_LegendApplicationPlugin_Extension
|
|
|
140
154
|
*/
|
|
141
155
|
getExtraDatasetEntitlementAccessNotGrantedReportActionConfigurations?(): DatasetEntitlementAccessReportActionConfiguration[];
|
|
142
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Get the list of builders for the address where access to a data product can be
|
|
159
|
+
* requested. The first one to produce an address wins.
|
|
160
|
+
*/
|
|
161
|
+
getDataProductAccessRequestLinkBuilders?(): DataProductAccessRequestLinkBuilder[];
|
|
162
|
+
|
|
143
163
|
/**
|
|
144
164
|
* Get the list of query usage configurations
|
|
145
165
|
*/
|