@finos/legend-query-builder 3.2.0 → 3.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. package/lib/__lib__/QueryBuilderColorTheme.d.ts +23 -0
  2. package/lib/__lib__/QueryBuilderColorTheme.d.ts.map +1 -0
  3. package/lib/__lib__/QueryBuilderColorTheme.js +24 -0
  4. package/lib/__lib__/QueryBuilderColorTheme.js.map +1 -0
  5. package/lib/components/QueryBuilder.d.ts.map +1 -1
  6. package/lib/components/QueryBuilder.js +24 -34
  7. package/lib/components/QueryBuilder.js.map +1 -1
  8. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts +0 -1
  9. package/lib/components/QueryBuilder_LegendApplicationPlugin.d.ts.map +1 -1
  10. package/lib/components/QueryBuilder_LegendApplicationPlugin.js +3 -4
  11. package/lib/components/QueryBuilder_LegendApplicationPlugin.js.map +1 -1
  12. package/lib/components/QueryLoader.d.ts.map +1 -1
  13. package/lib/components/QueryLoader.js +6 -2
  14. package/lib/components/QueryLoader.js.map +1 -1
  15. package/lib/components/data-access/DataAccessOverview.d.ts +23 -0
  16. package/lib/components/data-access/DataAccessOverview.d.ts.map +1 -0
  17. package/lib/components/data-access/DataAccessOverview.js +146 -0
  18. package/lib/components/data-access/DataAccessOverview.js.map +1 -0
  19. package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -1
  20. package/lib/components/execution-plan/ExecutionPlanViewer.js +3 -3
  21. package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -1
  22. package/lib/components/execution-plan/SQLExecutionNodeViewer.js +1 -1
  23. package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
  24. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js +1 -1
  25. package/lib/components/explorer/QueryBuilderPropertySearchPanel.js.map +1 -1
  26. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.d.ts.map +1 -1
  27. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js +2 -2
  28. package/lib/components/fetch-structure/QueryBuilderResultModifierPanel.js.map +1 -1
  29. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts +0 -18
  30. package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
  31. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +34 -36
  32. package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
  33. package/lib/components/shared/LambdaEditor.d.ts.map +1 -1
  34. package/lib/components/shared/LambdaEditor.js +7 -19
  35. package/lib/components/shared/LambdaEditor.js.map +1 -1
  36. package/lib/index.css +2 -2
  37. package/lib/index.css.map +1 -1
  38. package/lib/index.d.ts +2 -0
  39. package/lib/index.d.ts.map +1 -1
  40. package/lib/index.js +2 -0
  41. package/lib/index.js.map +1 -1
  42. package/lib/package.json +6 -4
  43. package/lib/stores/QueryBuilderState.d.ts +3 -3
  44. package/lib/stores/QueryBuilderState.d.ts.map +1 -1
  45. package/lib/stores/QueryBuilderState.js +34 -18
  46. package/lib/stores/QueryBuilderState.js.map +1 -1
  47. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +9 -0
  48. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -1
  49. package/lib/stores/data-access/DataAccessState.d.ts +56 -0
  50. package/lib/stores/data-access/DataAccessState.d.ts.map +1 -0
  51. package/lib/stores/data-access/DataAccessState.js +212 -0
  52. package/lib/stores/data-access/DataAccessState.js.map +1 -0
  53. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.d.ts +4 -2
  54. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.d.ts.map +1 -1
  55. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.js +20 -6
  56. package/lib/stores/entitlements/QueryBuilderCheckEntitlementsState.js.map +1 -1
  57. package/package.json +14 -12
  58. package/src/__lib__/QueryBuilderColorTheme.ts +23 -0
  59. package/src/components/QueryBuilder.tsx +85 -96
  60. package/src/components/QueryBuilder_LegendApplicationPlugin.ts +4 -5
  61. package/src/components/QueryLoader.tsx +4 -1
  62. package/src/components/data-access/DataAccessOverview.tsx +308 -0
  63. package/src/components/execution-plan/ExecutionPlanViewer.tsx +1 -3
  64. package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +1 -1
  65. package/src/components/explorer/QueryBuilderPropertySearchPanel.tsx +1 -1
  66. package/src/components/fetch-structure/QueryBuilderResultModifierPanel.tsx +2 -3
  67. package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +99 -102
  68. package/src/components/shared/LambdaEditor.tsx +4 -21
  69. package/src/index.ts +4 -0
  70. package/src/stores/QueryBuilderState.ts +65 -19
  71. package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +10 -0
  72. package/src/stores/data-access/DataAccessState.ts +322 -0
  73. package/src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts +53 -6
  74. package/tsconfig.json +3 -0
@@ -23,7 +23,6 @@ import {
23
23
  LongArrowAltDownIcon,
24
24
  LongArrowAltUpIcon,
25
25
  Dialog,
26
- useResizeDetector,
27
26
  Modal,
28
27
  ModalBody,
29
28
  ModalFooter,
@@ -154,13 +153,6 @@ const LambdaEditorInline = observer(
154
153
  }
155
154
  };
156
155
 
157
- const { ref, width, height } = useResizeDetector<HTMLDivElement>();
158
- useEffect(() => {
159
- if (width !== undefined && height !== undefined) {
160
- editor?.layout({ width, height });
161
- }
162
- }, [editor, width, height]);
163
-
164
156
  useEffect(() => {
165
157
  if (!editor && textInputRef.current) {
166
158
  const element = textInputRef.current;
@@ -187,8 +179,8 @@ const LambdaEditorInline = observer(
187
179
  language: CODE_EDITOR_LANGUAGE.PURE,
188
180
  theme: applicationStore.layoutService
189
181
  .TEMPORARY__isLightColorThemeEnabled
190
- ? CODE_EDITOR_THEME.TEMPORARY__VSCODE_LIGHT
191
- : CODE_EDITOR_THEME.LEGEND,
182
+ ? CODE_EDITOR_THEME.BUILT_IN__VSCODE_LIGHT
183
+ : CODE_EDITOR_THEME.DEFAULT_DARK,
192
184
  ...lambdaEditorOptions,
193
185
  });
194
186
  setEditor(_editor);
@@ -341,7 +333,6 @@ const LambdaEditorInline = observer(
341
333
  })}
342
334
  >
343
335
  <div
344
- ref={ref}
345
336
  data-testid={QUERY_BUILDER_TEST_ID.LAMBDA_EDITOR__EDITOR_INPUT}
346
337
  className="lambda-editor__editor__input"
347
338
  >
@@ -451,13 +442,6 @@ const LambdaEditorPopUp = observer(
451
442
  transformLambdaToString(true),
452
443
  );
453
444
 
454
- const { ref, width, height } = useResizeDetector<HTMLDivElement>();
455
- useEffect(() => {
456
- if (width !== undefined && height !== undefined) {
457
- editor?.layout({ width, height });
458
- }
459
- }, [editor, width, height]);
460
-
461
445
  const onEnter = (): void => {
462
446
  if (!editor && textInputRef.current) {
463
447
  const element = textInputRef.current;
@@ -466,8 +450,8 @@ const LambdaEditorPopUp = observer(
466
450
  language: CODE_EDITOR_LANGUAGE.PURE,
467
451
  theme: applicationStore.layoutService
468
452
  .TEMPORARY__isLightColorThemeEnabled
469
- ? CODE_EDITOR_THEME.TEMPORARY__VSCODE_LIGHT
470
- : CODE_EDITOR_THEME.LEGEND,
453
+ ? CODE_EDITOR_THEME.BUILT_IN__VSCODE_LIGHT
454
+ : CODE_EDITOR_THEME.DEFAULT_DARK,
471
455
  });
472
456
  setEditor(_editor);
473
457
  }
@@ -613,7 +597,6 @@ const LambdaEditorPopUp = observer(
613
597
  <ModalBody>
614
598
  <div className={clsx('lambda-editor__popup__content', className)}>
615
599
  <div
616
- ref={ref}
617
600
  data-testid={QUERY_BUILDER_TEST_ID.LAMBDA_EDITOR__EDITOR_INPUT}
618
601
  className="lambda-editor__editor__input"
619
602
  >
package/src/index.ts CHANGED
@@ -70,6 +70,10 @@ export * from './stores/shared/ValueSpecificationEditorHelper.js';
70
70
 
71
71
  export * from './components/execution-plan/ExecutionPlanViewer.js';
72
72
  export * from './stores/execution-plan/ExecutionPlanState.js';
73
+
73
74
  export * from './components/QueryLoader.js';
74
75
  export * from './stores/QueryLoaderState.js';
75
76
  export * from './stores/QueryBuilder_LegendApplicationPlugin_Extension.js';
77
+
78
+ export * from './stores/data-access/DataAccessState.js';
79
+ export * from './components/data-access/DataAccessOverview.js';
@@ -31,6 +31,7 @@ import {
31
31
  filterByType,
32
32
  ActionState,
33
33
  hashArray,
34
+ assertTrue,
34
35
  } from '@finos/legend-shared';
35
36
  import { QueryBuilderFilterState } from './filter/QueryBuilderFilterState.js';
36
37
  import { QueryBuilderFetchStructureState } from './fetch-structure/QueryBuilderFetchStructureState.js';
@@ -62,6 +63,15 @@ import {
62
63
  buildLambdaVariableExpressions,
63
64
  buildRawLambdaFromLambdaFunction,
64
65
  type ValueSpecification,
66
+ PrimitiveType,
67
+ type Type,
68
+ SimpleFunctionExpression,
69
+ extractElementNameFromPath,
70
+ SUPPORTED_FUNCTIONS,
71
+ PackageableElementExplicitReference,
72
+ InstanceValue,
73
+ Multiplicity,
74
+ RuntimePointer,
65
75
  } from '@finos/legend-graph';
66
76
  import { buildLambdaFunction } from './QueryBuilderValueSpecificationBuilder.js';
67
77
  import type {
@@ -79,6 +89,8 @@ import { QUERY_BUILDER_COMMAND_KEY } from './QueryBuilderCommand.js';
79
89
  import { QueryBuilderWatermarkState } from './watermark/QueryBuilderWatermarkState.js';
80
90
  import { QueryBuilderConstantsState } from './QueryBuilderConstantsState.js';
81
91
  import { QueryBuilderCheckEntitlementsState } from './entitlements/QueryBuilderCheckEntitlementsState.js';
92
+ import { QueryBuilderTDSState } from './fetch-structure/tds/QueryBuilderTDSState.js';
93
+ import { QUERY_BUILDER_PURE_PATH } from '../graph/QueryBuilderMetaModelConst.js';
82
94
 
83
95
  export abstract class QueryBuilderState implements CommandRegistrar {
84
96
  readonly applicationStore: GenericLegendApplicationStore;
@@ -87,7 +99,6 @@ export abstract class QueryBuilderState implements CommandRegistrar {
87
99
  readonly changeDetectionState: QueryBuilderChangeDetectionState;
88
100
  readonly queryCompileState = ActionState.create();
89
101
  readonly observerContext: ObserverContext;
90
- readonly saveQueryProgressState = ActionState.create();
91
102
 
92
103
  explorerState: QueryBuilderExplorerState;
93
104
  functionsExplorerState: QueryFunctionsExplorerState;
@@ -162,7 +173,6 @@ export abstract class QueryBuilderState implements CommandRegistrar {
162
173
  changeMapping: action,
163
174
 
164
175
  rebuildWithQuery: action,
165
- saveQuery: action,
166
176
  compileQuery: flow,
167
177
  hashCode: computed,
168
178
  });
@@ -381,6 +391,59 @@ export abstract class QueryBuilderState implements CommandRegistrar {
381
391
  );
382
392
  }
383
393
 
394
+ buildFromQuery(): RawLambda {
395
+ assertTrue(
396
+ this.isQuerySupported,
397
+ 'Query must be supported to build from function',
398
+ );
399
+ const mapping = guaranteeNonNullable(
400
+ this.mapping,
401
+ 'Mapping required to build from() function',
402
+ );
403
+ const runtime = guaranteeNonNullable(
404
+ this.runtimeValue,
405
+ 'Runtime required to build from query',
406
+ );
407
+ const runtimePointer = guaranteeType(
408
+ runtime,
409
+ RuntimePointer,
410
+ ).packageableRuntime;
411
+ const lambdaFunc = buildLambdaFunction(this);
412
+ const currentExpression = guaranteeNonNullable(
413
+ lambdaFunc.expressionSequence[0],
414
+ );
415
+ const _func = new SimpleFunctionExpression(
416
+ extractElementNameFromPath(SUPPORTED_FUNCTIONS.FROM),
417
+ );
418
+
419
+ const mappingInstance = new InstanceValue(Multiplicity.ONE, undefined);
420
+ mappingInstance.values = [
421
+ PackageableElementExplicitReference.create(mapping),
422
+ ];
423
+ const runtimeInstance = new InstanceValue(Multiplicity.ONE, undefined);
424
+ runtimeInstance.values = [
425
+ PackageableElementExplicitReference.create(runtimePointer.value),
426
+ ];
427
+ _func.parametersValues = [
428
+ currentExpression,
429
+ mappingInstance,
430
+ runtimeInstance,
431
+ ];
432
+ lambdaFunc.expressionSequence = [_func];
433
+ return buildRawLambdaFromLambdaFunction(lambdaFunc, this.graphManagerState);
434
+ }
435
+
436
+ getQueryReturnType(): Type {
437
+ if (
438
+ this.fetchStructureState.implementation instanceof QueryBuilderTDSState
439
+ ) {
440
+ return this.graphManagerState.graph.getClass(
441
+ QUERY_BUILDER_PURE_PATH.TDS_TABULAR_DATASET,
442
+ );
443
+ }
444
+ return PrimitiveType.STRING;
445
+ }
446
+
384
447
  initializeWithQuery(query: RawLambda): void {
385
448
  this.rebuildWithQuery(query);
386
449
  this.resetQueryResult();
@@ -452,23 +515,6 @@ export abstract class QueryBuilderState implements CommandRegistrar {
452
515
  }
453
516
  }
454
517
 
455
- async saveQuery(
456
- onSaveQuery: (lambda: RawLambda) => Promise<void>,
457
- ): Promise<void> {
458
- this.saveQueryProgressState.inProgress();
459
- try {
460
- const query = this.buildQuery();
461
- await onSaveQuery(query);
462
- } catch (error) {
463
- assertErrorThrown(error);
464
- this.applicationStore.notificationService.notifyError(
465
- `Can't save query: ${error.message}`,
466
- );
467
- } finally {
468
- this.saveQueryProgressState.complete();
469
- }
470
- }
471
-
472
518
  *compileQuery(): GeneratorFn<void> {
473
519
  if (!this.textEditorState.mode) {
474
520
  this.queryCompileState.inProgress();
@@ -17,6 +17,7 @@
17
17
  import type { LegendApplicationPlugin } from '@finos/legend-application';
18
18
  import type { QueryBuilderState } from './QueryBuilderState.js';
19
19
  import type { QuerySearchSpecification } from '@finos/legend-graph';
20
+ import type { DatasetAccessInfo } from './data-access/DataAccessState.js';
20
21
 
21
22
  export type LoadQueryFilterOption = {
22
23
  key: string;
@@ -27,10 +28,19 @@ export type LoadQueryFilterOption = {
27
28
  ) => QuerySearchSpecification;
28
29
  };
29
30
 
31
+ export type DatasetEntitlementAccessReportActionConfiguration = {
32
+ renderer: (info: DatasetAccessInfo) => React.ReactNode;
33
+ };
34
+
30
35
  export interface QueryBuilder_LegendApplicationPlugin_Extension
31
36
  extends LegendApplicationPlugin {
32
37
  /**
33
38
  * Get the list of filter options for query loader.
34
39
  */
35
40
  getExtraLoadQueryFilterOptions?(): LoadQueryFilterOption[];
41
+
42
+ /**
43
+ * Get the list of dataset entitlement access report action configurations.
44
+ */
45
+ getExtraDatasetEntitlementAccessNotGrantedReportActionConfigurations?(): DatasetEntitlementAccessReportActionConfiguration[];
36
46
  }
@@ -0,0 +1,322 @@
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 { GenericLegendApplicationStore } from '@finos/legend-application';
18
+ import {
19
+ DatasetEntitlementAccessGrantedReport,
20
+ type BasicGraphManagerState,
21
+ type DatasetEntitlementReport,
22
+ type DatasetSpecification,
23
+ DatasetEntitlementAccessApprovedReport,
24
+ DatasetEntitlementAccessRequestedReport,
25
+ DatasetEntitlementAccessNotGrantedReport,
26
+ DatasetEntitlementUnsupportedReport,
27
+ } from '@finos/legend-graph';
28
+ import {
29
+ ActionState,
30
+ assertErrorThrown,
31
+ uuid,
32
+ type GeneratorFn,
33
+ getNonNullableEntry,
34
+ } from '@finos/legend-shared';
35
+ import {
36
+ action,
37
+ computed,
38
+ flow,
39
+ flowResult,
40
+ makeObservable,
41
+ observable,
42
+ } from 'mobx';
43
+ import { QUERY_BUILDER_COLOR_THEME_KEY } from '../../__lib__/QueryBuilderColorTheme.js';
44
+
45
+ export class DatasetAccessInfo {
46
+ readonly uuid = uuid();
47
+ readonly specification!: DatasetSpecification;
48
+
49
+ entitlementReport?: DatasetEntitlementReport | undefined;
50
+
51
+ constructor(specification: DatasetSpecification) {
52
+ makeObservable(this, {
53
+ entitlementReport: observable,
54
+ setEntitlementReport: action,
55
+ });
56
+
57
+ this.specification = specification;
58
+ }
59
+
60
+ setEntitlementReport(val: DatasetEntitlementReport | undefined): void {
61
+ this.entitlementReport = val;
62
+ }
63
+ }
64
+
65
+ type EntitlementCheckInfo = {
66
+ total: number;
67
+ data: { label: string; count: number; percentage: number; color: string }[];
68
+ };
69
+
70
+ export class DataAccessState {
71
+ readonly applicationStore: GenericLegendApplicationStore;
72
+ readonly graphManagerState: BasicGraphManagerState;
73
+
74
+ readonly surveyDatasets: () => Promise<DatasetSpecification[]>;
75
+ readonly checkDatasetEntitlements: (
76
+ datasets: DatasetSpecification[],
77
+ ) => Promise<DatasetEntitlementReport[]>;
78
+
79
+ readonly initialDatasets?: DatasetSpecification[] | undefined;
80
+ readonly surveyDatasetsState = ActionState.create();
81
+ readonly checkEntitlementsState = ActionState.create();
82
+
83
+ datasets: DatasetAccessInfo[] = [];
84
+
85
+ constructor(
86
+ applicationStore: GenericLegendApplicationStore,
87
+ graphManagerState: BasicGraphManagerState,
88
+ options: {
89
+ initialDatasets?: DatasetSpecification[] | undefined;
90
+ surveyDatasets: () => Promise<DatasetSpecification[]>;
91
+ checkDatasetEntitlements: (
92
+ datasets: DatasetSpecification[],
93
+ ) => Promise<DatasetEntitlementReport[]>;
94
+ },
95
+ ) {
96
+ makeObservable(this, {
97
+ datasets: observable,
98
+ entitlementCheckInfo: computed,
99
+ fetchDatasetSpecifications: flow,
100
+ fetchDatasetEntitlementReports: flow,
101
+ });
102
+
103
+ this.applicationStore = applicationStore;
104
+ this.graphManagerState = graphManagerState;
105
+ this.initialDatasets = options.initialDatasets;
106
+ this.datasets = (options.initialDatasets ?? []).map(
107
+ (dataset) => new DatasetAccessInfo(dataset),
108
+ );
109
+ this.surveyDatasets = options.surveyDatasets;
110
+ this.checkDatasetEntitlements = options.checkDatasetEntitlements;
111
+ }
112
+
113
+ get entitlementCheckInfo(): EntitlementCheckInfo {
114
+ const total = this.datasets.length;
115
+ if (!total) {
116
+ return {
117
+ total,
118
+ data: [
119
+ {
120
+ label: 'Access Granted',
121
+ count: 1,
122
+ percentage: 100,
123
+ color: this.applicationStore.layoutService.getColor(
124
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__ACCESS_GRANTED,
125
+ ),
126
+ },
127
+ ],
128
+ };
129
+ }
130
+
131
+ const info: EntitlementCheckInfo = {
132
+ total,
133
+ data: [],
134
+ };
135
+
136
+ const accessGrantedCount = this.datasets.filter(
137
+ (dataset) =>
138
+ dataset.entitlementReport instanceof
139
+ DatasetEntitlementAccessGrantedReport,
140
+ ).length;
141
+ const accessGrantedPercentage =
142
+ Math.round(accessGrantedCount / total) * 100;
143
+ info.data.push({
144
+ label: 'Access Granted',
145
+ count: accessGrantedCount,
146
+ percentage: accessGrantedPercentage,
147
+ color: this.applicationStore.layoutService.getColor(
148
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__ACCESS_GRANTED,
149
+ ),
150
+ });
151
+
152
+ const accessApprovedCount = this.datasets.filter(
153
+ (dataset) =>
154
+ dataset.entitlementReport instanceof
155
+ DatasetEntitlementAccessApprovedReport,
156
+ ).length;
157
+ const accessApprovedPercentage = Math.round(
158
+ (accessApprovedCount / total) * 100,
159
+ );
160
+ info.data.push({
161
+ label: 'Access Approved',
162
+ count: accessApprovedCount,
163
+ percentage: accessApprovedPercentage,
164
+ color: this.applicationStore.layoutService.getColor(
165
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__ACCESS_APPROVED,
166
+ ),
167
+ });
168
+
169
+ const accessRequestedCount = this.datasets.filter(
170
+ (dataset) =>
171
+ dataset.entitlementReport instanceof
172
+ DatasetEntitlementAccessRequestedReport,
173
+ ).length;
174
+ const accessRequestedPercentage = Math.round(
175
+ (accessRequestedCount / total) * 100,
176
+ );
177
+ info.data.push({
178
+ label: 'Access Requested',
179
+ count: accessRequestedCount,
180
+ percentage: accessRequestedPercentage,
181
+ color: this.applicationStore.layoutService.getColor(
182
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__ACCESS_REQUESTED,
183
+ ),
184
+ });
185
+
186
+ const accessNotGrantedCount = this.datasets.filter(
187
+ (dataset) =>
188
+ dataset.entitlementReport instanceof
189
+ DatasetEntitlementAccessNotGrantedReport,
190
+ ).length;
191
+ const accessNotGrantedPercentage =
192
+ Math.round(accessNotGrantedCount / total) * 100;
193
+ info.data.push({
194
+ label: 'Access Not Granted',
195
+ count: accessNotGrantedCount,
196
+ percentage: accessNotGrantedPercentage,
197
+ color: this.applicationStore.layoutService.getColor(
198
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__ACCESS_NOT_GRANTED,
199
+ ),
200
+ });
201
+
202
+ const unsupportedCount = this.datasets.filter(
203
+ (dataset) =>
204
+ dataset.entitlementReport instanceof
205
+ DatasetEntitlementUnsupportedReport,
206
+ ).length;
207
+ const unsupportedPercentage = Math.round((unsupportedCount / total) * 100);
208
+ info.data.push({
209
+ label: 'Unsupported',
210
+ count: unsupportedCount,
211
+ percentage: unsupportedPercentage,
212
+ color: this.applicationStore.layoutService.getColor(
213
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__UNSUPPORTED_ACCESS,
214
+ ),
215
+ });
216
+
217
+ const unknownCount =
218
+ total -
219
+ accessGrantedCount -
220
+ accessApprovedCount -
221
+ accessRequestedCount -
222
+ accessNotGrantedCount -
223
+ unsupportedCount;
224
+ const unknownPercentage = Math.round(unknownCount / total) * 100;
225
+ info.data.push({
226
+ label: 'Unknown',
227
+ count: unknownCount,
228
+ percentage: unknownPercentage,
229
+ color: this.applicationStore.layoutService.getColor(
230
+ QUERY_BUILDER_COLOR_THEME_KEY.DATA_ACCESS_OVERVIEW__CHART__UNSUPPORTED_ACCESS,
231
+ ),
232
+ });
233
+
234
+ let currentPercentageSum = 0;
235
+ for (let i = 0; i < info.data.length; ++i) {
236
+ const data = getNonNullableEntry(info.data, i);
237
+ if (currentPercentageSum + data.percentage >= 100) {
238
+ data.percentage = 100 - currentPercentageSum;
239
+ info.data = info.data.slice(0, i + 1);
240
+ break;
241
+ }
242
+ currentPercentageSum += data.percentage;
243
+ }
244
+
245
+ return info;
246
+ }
247
+
248
+ *fetchDatasetSpecifications(): GeneratorFn<void> {
249
+ this.surveyDatasetsState.inProgress();
250
+
251
+ try {
252
+ const datasets = (yield this.surveyDatasets()) as DatasetSpecification[];
253
+ this.datasets = datasets.map((dataset) => {
254
+ const existingDataset = this.datasets.find(
255
+ (ds) => ds.specification.hashCode === dataset.hashCode,
256
+ );
257
+ if (existingDataset) {
258
+ return existingDataset;
259
+ }
260
+ return new DatasetAccessInfo(dataset);
261
+ });
262
+ } catch (error) {
263
+ assertErrorThrown(error);
264
+ this.applicationStore.notificationService.notifyError(error);
265
+ } finally {
266
+ this.surveyDatasetsState.complete();
267
+ }
268
+ }
269
+
270
+ *fetchDatasetEntitlementReports(): GeneratorFn<void> {
271
+ this.checkEntitlementsState.inProgress();
272
+
273
+ try {
274
+ const reports = (yield this.checkDatasetEntitlements(
275
+ this.datasets.map((dataset) => dataset.specification),
276
+ )) as DatasetEntitlementReport[];
277
+ this.datasets.forEach((dataset) => {
278
+ const matchingReport = reports.find(
279
+ (report) =>
280
+ report.dataset.hashCode === dataset.specification.hashCode,
281
+ );
282
+ if (matchingReport) {
283
+ dataset.setEntitlementReport(matchingReport);
284
+ return;
285
+ }
286
+ dataset.setEntitlementReport(undefined);
287
+ });
288
+ const newDatasets: DatasetAccessInfo[] = [];
289
+ reports.forEach((report) => {
290
+ const matchingDataset = this.datasets.find(
291
+ (dataset) =>
292
+ dataset.specification.hashCode === report.dataset.hashCode,
293
+ );
294
+ if (!matchingDataset) {
295
+ const newDataset = new DatasetAccessInfo(report.dataset);
296
+ newDataset.setEntitlementReport(report);
297
+ newDatasets.push(newDataset);
298
+ }
299
+ });
300
+ this.datasets = this.datasets.concat(newDatasets);
301
+ } catch (error) {
302
+ assertErrorThrown(error);
303
+ this.applicationStore.notificationService.notifyError(error);
304
+ } finally {
305
+ this.checkEntitlementsState.complete();
306
+ }
307
+ }
308
+
309
+ async intialize(): Promise<void> {
310
+ if (!this.initialDatasets) {
311
+ await flowResult(this.fetchDatasetSpecifications());
312
+ await flowResult(this.fetchDatasetEntitlementReports());
313
+ } else {
314
+ await flowResult(this.fetchDatasetEntitlementReports());
315
+ }
316
+ }
317
+
318
+ async refresh(): Promise<void> {
319
+ await flowResult(this.fetchDatasetSpecifications());
320
+ await flowResult(this.fetchDatasetEntitlementReports());
321
+ }
322
+ }
@@ -18,29 +18,76 @@ import { type Hashable, hashArray } from '@finos/legend-shared';
18
18
  import { makeObservable, observable, action, computed } from 'mobx';
19
19
  import { QUERY_BUILDER_STATE_HASH_STRUCTURE } from '../QueryBuilderStateHashUtils.js';
20
20
  import type { QueryBuilderState } from '../QueryBuilderState.js';
21
+ import { DataAccessState } from '../data-access/DataAccessState.js';
22
+ import {
23
+ RuntimePointer,
24
+ type DatasetEntitlementReport,
25
+ type DatasetSpecification,
26
+ InMemoryGraphData,
27
+ } from '@finos/legend-graph';
21
28
 
22
29
  export class QueryBuilderCheckEntitlementsState implements Hashable {
23
30
  readonly queryBuilderState: QueryBuilderState;
24
- isCheckingEntitlements = false;
31
+
32
+ dataAccessState?: DataAccessState | undefined;
33
+ showCheckEntitlementsViewer = false;
25
34
 
26
35
  constructor(queryBuilderState: QueryBuilderState) {
27
36
  makeObservable(this, {
28
- isCheckingEntitlements: observable,
29
- setIsCheckingEntitlements: action,
37
+ showCheckEntitlementsViewer: observable,
38
+ dataAccessState: observable,
39
+ setShowCheckEntitlementsViewer: action,
30
40
  hashCode: computed,
31
41
  });
32
42
 
33
43
  this.queryBuilderState = queryBuilderState;
34
44
  }
35
45
 
36
- setIsCheckingEntitlements(val: boolean): void {
37
- this.isCheckingEntitlements = val;
46
+ setShowCheckEntitlementsViewer(val: boolean): void {
47
+ this.showCheckEntitlementsViewer = val;
48
+
49
+ this.dataAccessState = undefined;
50
+ if (
51
+ this.queryBuilderState.mapping &&
52
+ this.queryBuilderState.runtimeValue instanceof RuntimePointer
53
+ ) {
54
+ const mappingPath = this.queryBuilderState.mapping.path;
55
+ const runtimePath =
56
+ this.queryBuilderState.runtimeValue.packageableRuntime.value.path;
57
+ this.dataAccessState = new DataAccessState(
58
+ this.queryBuilderState.applicationStore,
59
+ this.queryBuilderState.graphManagerState,
60
+ {
61
+ surveyDatasets: async (): Promise<DatasetSpecification[]> =>
62
+ this.queryBuilderState.graphManagerState.graphManager.surveyDatasets(
63
+ mappingPath,
64
+ runtimePath,
65
+ this.queryBuilderState.buildQuery(),
66
+ new InMemoryGraphData(
67
+ this.queryBuilderState.graphManagerState.graph,
68
+ ),
69
+ ),
70
+ checkDatasetEntitlements: async (
71
+ datasets: DatasetSpecification[],
72
+ ): Promise<DatasetEntitlementReport[]> =>
73
+ this.queryBuilderState.graphManagerState.graphManager.checkDatasetEntitlements(
74
+ datasets,
75
+ mappingPath,
76
+ runtimePath,
77
+ this.queryBuilderState.buildQuery(),
78
+ new InMemoryGraphData(
79
+ this.queryBuilderState.graphManagerState.graph,
80
+ ),
81
+ ),
82
+ },
83
+ );
84
+ }
38
85
  }
39
86
 
40
87
  get hashCode(): string {
41
88
  return hashArray([
42
89
  QUERY_BUILDER_STATE_HASH_STRUCTURE.CHECK_ENTITLEMENTS_STATE,
43
- this.isCheckingEntitlements,
90
+ this.showCheckEntitlementsViewer,
44
91
  ]);
45
92
  }
46
93
  }
package/tsconfig.json CHANGED
@@ -34,6 +34,7 @@
34
34
  "files": [
35
35
  "./src/__test__.ts",
36
36
  "./src/index.ts",
37
+ "./src/__lib__/QueryBuilderColorTheme.ts",
37
38
  "./src/__lib__/QueryBuilderDocumentation.ts",
38
39
  "./src/__lib__/QueryBuilderEvent.ts",
39
40
  "./src/__lib__/QueryBuilderSetting.ts",
@@ -75,6 +76,7 @@
75
76
  "./src/stores/QueryLoaderState.ts",
76
77
  "./src/stores/ServiceInfo.ts",
77
78
  "./src/stores/__test-utils__/QueryBuilderStateTestUtils.ts",
79
+ "./src/stores/data-access/DataAccessState.ts",
78
80
  "./src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts",
79
81
  "./src/stores/execution-plan/ExecutionPlanState.ts",
80
82
  "./src/stores/explorer/QueryBuilderExplorerState.ts",
@@ -218,6 +220,7 @@
218
220
  "./src/components/QueryLoader.tsx",
219
221
  "./src/components/ServiceQuerySetupUtils.tsx",
220
222
  "./src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx",
223
+ "./src/components/data-access/DataAccessOverview.tsx",
221
224
  "./src/components/execution-plan/ExecutionPlanViewer.tsx",
222
225
  "./src/components/execution-plan/SQLExecutionNodeViewer.tsx",
223
226
  "./src/components/explorer/QueryBuilderExplorerPanel.tsx",