@finos/legend-query-builder 3.1.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts +20 -20
  2. package/lib/__lib__/QueryBuilderTelemetryHelper.d.ts.map +1 -1
  3. package/lib/__lib__/QueryBuilderTelemetryHelper.js +40 -40
  4. package/lib/__lib__/QueryBuilderTelemetryHelper.js.map +1 -1
  5. package/lib/components/QueryLoader.d.ts +30 -0
  6. package/lib/components/QueryLoader.d.ts.map +1 -0
  7. package/lib/components/QueryLoader.js +160 -0
  8. package/lib/components/QueryLoader.js.map +1 -0
  9. package/lib/components/shared/QueryBuilderPanelIssueCountBadge.d.ts.map +1 -1
  10. package/lib/components/shared/QueryBuilderPanelIssueCountBadge.js +2 -1
  11. package/lib/components/shared/QueryBuilderPanelIssueCountBadge.js.map +1 -1
  12. package/lib/index.css +2 -2
  13. package/lib/index.css.map +1 -1
  14. package/lib/index.d.ts +3 -0
  15. package/lib/index.d.ts.map +1 -1
  16. package/lib/index.js +3 -0
  17. package/lib/index.js.map +1 -1
  18. package/lib/package.json +1 -1
  19. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts +30 -0
  20. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.d.ts.map +1 -0
  21. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.js +17 -0
  22. package/lib/stores/QueryBuilder_LegendApplicationPlugin_Extension.js.map +1 -0
  23. package/lib/stores/QueryLoaderState.d.ts +67 -0
  24. package/lib/stores/QueryLoaderState.d.ts.map +1 -0
  25. package/lib/stores/QueryLoaderState.js +205 -0
  26. package/lib/stores/QueryLoaderState.js.map +1 -0
  27. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts +3 -1
  28. package/lib/stores/shared/ValueSpecificationEditorHelper.d.ts.map +1 -1
  29. package/lib/stores/shared/ValueSpecificationEditorHelper.js +7 -1
  30. package/lib/stores/shared/ValueSpecificationEditorHelper.js.map +1 -1
  31. package/package.json +8 -8
  32. package/src/__lib__/QueryBuilderTelemetryHelper.ts +40 -59
  33. package/src/components/QueryLoader.tsx +501 -0
  34. package/src/components/shared/QueryBuilderPanelIssueCountBadge.tsx +2 -1
  35. package/src/index.ts +3 -0
  36. package/src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts +36 -0
  37. package/src/stores/QueryLoaderState.ts +298 -0
  38. package/src/stores/shared/ValueSpecificationEditorHelper.ts +39 -2
  39. package/tsconfig.json +3 -0
@@ -0,0 +1,298 @@
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 {
18
+ DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH,
19
+ type GenericLegendApplicationStore,
20
+ } from '@finos/legend-application';
21
+ import {
22
+ type LightQuery,
23
+ QuerySearchSpecification,
24
+ type QueryInfo,
25
+ type BasicGraphManagerState,
26
+ type Query,
27
+ } from '@finos/legend-graph';
28
+ import {
29
+ ActionState,
30
+ type GeneratorFn,
31
+ assertErrorThrown,
32
+ guaranteeNonNullable,
33
+ } from '@finos/legend-shared';
34
+ import { makeObservable, observable, action, flow } from 'mobx';
35
+ import type { QueryBuilderState } from './QueryBuilderState.js';
36
+ import type {
37
+ LoadQueryFilterOption,
38
+ QueryBuilder_LegendApplicationPlugin_Extension,
39
+ } from './QueryBuilder_LegendApplicationPlugin_Extension.js';
40
+
41
+ export const QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT = 20;
42
+
43
+ export class QueryLoaderState {
44
+ readonly applicationStore: GenericLegendApplicationStore;
45
+ readonly graphManagerState: BasicGraphManagerState;
46
+
47
+ readonly searchQueriesState = ActionState.create();
48
+ readonly renameQueryState = ActionState.create();
49
+ readonly deleteQueryState = ActionState.create();
50
+ readonly previewQueryState = ActionState.create();
51
+
52
+ readonly decorateSearchSpecification?:
53
+ | ((val: QuerySearchSpecification) => QuerySearchSpecification)
54
+ | undefined;
55
+
56
+ readonly loadQuery: (query: LightQuery) => void;
57
+ readonly fetchDefaultQueries?: (() => Promise<LightQuery[]>) | undefined;
58
+ readonly generateDefaultQueriesSummaryText?:
59
+ | ((queries: LightQuery[]) => string)
60
+ | undefined;
61
+
62
+ readonly isReadOnly?: boolean | undefined;
63
+ readonly onQueryRenamed?: ((query: LightQuery) => void) | undefined;
64
+ readonly onQueryDeleted?: ((query: LightQuery) => void) | undefined;
65
+
66
+ queryBuilderState?: QueryBuilderState | undefined;
67
+
68
+ searchText = '';
69
+ showCurrentUserQueriesOnly = false; // TODO: if we start having more native filters, we should make them part of `extraFilters`
70
+ extraFilters = new Map<string, boolean>();
71
+ extraFilterOptions: LoadQueryFilterOption[] = [];
72
+ queries: LightQuery[] = [];
73
+
74
+ isQueryLoaderDialogOpen = false;
75
+ showingDefaultQueries = true;
76
+ showPreviewViewer = false;
77
+ queryPreviewContent?: QueryInfo;
78
+
79
+ constructor(
80
+ applicationStore: GenericLegendApplicationStore,
81
+ graphManagerState: BasicGraphManagerState,
82
+ options: {
83
+ decorateSearchSpecification?:
84
+ | ((val: QuerySearchSpecification) => QuerySearchSpecification)
85
+ | undefined;
86
+
87
+ loadQuery: (query: LightQuery) => void;
88
+ fetchDefaultQueries?: (() => Promise<LightQuery[]>) | undefined;
89
+ generateDefaultQueriesSummaryText?:
90
+ | ((queries: LightQuery[]) => string)
91
+ | undefined;
92
+
93
+ isReadOnly?: boolean | undefined;
94
+ onQueryRenamed?: ((query: LightQuery) => void) | undefined;
95
+ onQueryDeleted?: ((query: LightQuery) => void) | undefined;
96
+ },
97
+ ) {
98
+ makeObservable(this, {
99
+ isQueryLoaderDialogOpen: observable,
100
+ queryPreviewContent: observable,
101
+ showingDefaultQueries: observable,
102
+ queries: observable,
103
+ showCurrentUserQueriesOnly: observable,
104
+ showPreviewViewer: observable,
105
+ searchText: observable,
106
+ setSearchText: action,
107
+ setQueryLoaderDialogOpen: action,
108
+ setQueries: action,
109
+ setShowCurrentUserQueriesOnly: action,
110
+ setShowPreviewViewer: action,
111
+ searchQueries: flow,
112
+ getPreviewQueryContent: flow,
113
+ deleteQuery: flow,
114
+ renameQuery: flow,
115
+ initialize: flow,
116
+ });
117
+
118
+ this.applicationStore = applicationStore;
119
+ this.graphManagerState = graphManagerState;
120
+
121
+ this.loadQuery = options.loadQuery;
122
+ this.fetchDefaultQueries = options.fetchDefaultQueries;
123
+ this.generateDefaultQueriesSummaryText =
124
+ options.generateDefaultQueriesSummaryText;
125
+ this.decorateSearchSpecification = options.decorateSearchSpecification;
126
+ this.isReadOnly = options.isReadOnly;
127
+ this.onQueryRenamed = options.onQueryRenamed;
128
+ this.onQueryDeleted = options.onQueryDeleted;
129
+ }
130
+
131
+ setSearchText(val: string): void {
132
+ this.searchText = val;
133
+ }
134
+
135
+ setQueryLoaderDialogOpen(val: boolean): void {
136
+ this.isQueryLoaderDialogOpen = val;
137
+ }
138
+
139
+ setQueries(val: LightQuery[]): void {
140
+ this.queries = val;
141
+ }
142
+
143
+ setShowPreviewViewer(val: boolean): void {
144
+ this.showPreviewViewer = val;
145
+ }
146
+
147
+ setShowCurrentUserQueriesOnly(val: boolean): void {
148
+ this.showCurrentUserQueriesOnly = val;
149
+ }
150
+
151
+ reset(): void {
152
+ this.setShowCurrentUserQueriesOnly(false);
153
+ }
154
+
155
+ *initialize(queryBuilderState: QueryBuilderState): GeneratorFn<void> {
156
+ this.queryBuilderState = queryBuilderState;
157
+ this.extraFilterOptions = this.applicationStore.pluginManager
158
+ .getApplicationPlugins()
159
+ .flatMap(
160
+ (plugin) =>
161
+ (
162
+ plugin as QueryBuilder_LegendApplicationPlugin_Extension
163
+ ).getExtraLoadQueryFilterOptions?.() ?? [],
164
+ );
165
+ const extraFilters = this.extraFilterOptions.map((filterOption) =>
166
+ filterOption.label(guaranteeNonNullable(this.queryBuilderState)),
167
+ );
168
+ extraFilters.forEach(
169
+ (filter) => filter && this.extraFilters.set(filter, false),
170
+ );
171
+ }
172
+
173
+ *searchQueries(searchText: string): GeneratorFn<void> {
174
+ if (
175
+ searchText.length < DEFAULT_TYPEAHEAD_SEARCH_MINIMUM_SEARCH_LENGTH &&
176
+ !this.showCurrentUserQueriesOnly &&
177
+ Array.from(this.extraFilters.values()).every((value) => value === false)
178
+ ) {
179
+ // if no search text is specified, use fetch the default queries
180
+ if (!searchText) {
181
+ this.showingDefaultQueries = true;
182
+ this.searchQueriesState.inProgress();
183
+ this.queries = [];
184
+ try {
185
+ if (!this.fetchDefaultQueries) {
186
+ return;
187
+ }
188
+ this.queries = (yield this.fetchDefaultQueries()) as LightQuery[];
189
+ this.searchQueriesState.pass();
190
+ } catch (error) {
191
+ this.searchQueriesState.fail();
192
+ assertErrorThrown(error);
193
+ this.applicationStore.notificationService.notifyError(error);
194
+ }
195
+ }
196
+
197
+ // skip otherwise
198
+ return;
199
+ }
200
+
201
+ // search using the search term
202
+ this.showingDefaultQueries = false;
203
+ this.searchQueriesState.inProgress();
204
+ try {
205
+ let searchSpecification = new QuerySearchSpecification();
206
+ searchSpecification.searchTerm = searchText;
207
+ searchSpecification.limit = QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT + 1;
208
+ searchSpecification.showCurrentUserQueriesOnly =
209
+ this.showCurrentUserQueriesOnly;
210
+ if (this.queryBuilderState) {
211
+ Array.from(this.extraFilters.entries()).forEach(([key, value]) => {
212
+ if (value) {
213
+ const filterOption = this.extraFilterOptions.find(
214
+ (option) =>
215
+ option.label(guaranteeNonNullable(this.queryBuilderState)) ===
216
+ key,
217
+ );
218
+ if (filterOption) {
219
+ searchSpecification = filterOption.filterFunction(
220
+ searchSpecification,
221
+ guaranteeNonNullable(this.queryBuilderState),
222
+ );
223
+ }
224
+ }
225
+ });
226
+ }
227
+ searchSpecification =
228
+ this.decorateSearchSpecification?.(searchSpecification) ??
229
+ searchSpecification;
230
+ this.queries = (yield this.graphManagerState.graphManager.searchQueries(
231
+ searchSpecification,
232
+ )) as LightQuery[];
233
+ this.searchQueriesState.pass();
234
+ } catch (error) {
235
+ assertErrorThrown(error);
236
+ this.applicationStore.notificationService.notifyError(error);
237
+ this.searchQueriesState.fail();
238
+ }
239
+ }
240
+
241
+ *renameQuery(queryId: string, name: string): GeneratorFn<void> {
242
+ this.renameQueryState.inProgress();
243
+ try {
244
+ const query = (yield this.graphManagerState.graphManager.renameQuery(
245
+ queryId,
246
+ name,
247
+ )) as Query;
248
+ this.onQueryRenamed?.(query);
249
+ this.applicationStore.notificationService.notify(
250
+ 'Renamed query successfully',
251
+ );
252
+ this.renameQueryState.pass();
253
+ this.searchQueries(this.searchText); // trigger a search to refresh the query list
254
+ } catch (error) {
255
+ assertErrorThrown(error);
256
+ this.applicationStore.notificationService.notifyError(error);
257
+ this.renameQueryState.fail();
258
+ }
259
+ }
260
+
261
+ *deleteQuery(queryId: string): GeneratorFn<void> {
262
+ this.deleteQueryState.inProgress();
263
+ try {
264
+ const query = (yield this.graphManagerState.graphManager.deleteQuery(
265
+ queryId,
266
+ )) as Query;
267
+ this.onQueryDeleted?.(query);
268
+ this.applicationStore.notificationService.notify(
269
+ 'Deleted query successfully',
270
+ );
271
+ this.deleteQueryState.pass();
272
+ this.searchQueries(this.searchText); // trigger a search to refresh the query list
273
+ } catch (error) {
274
+ assertErrorThrown(error);
275
+ this.applicationStore.notificationService.notifyError(error);
276
+ this.deleteQueryState.fail();
277
+ }
278
+ }
279
+
280
+ *getPreviewQueryContent(queryId: string): GeneratorFn<void> {
281
+ this.previewQueryState.inProgress();
282
+ try {
283
+ const queryInfo = (yield this.graphManagerState.graphManager.getQueryInfo(
284
+ queryId,
285
+ )) as QueryInfo;
286
+ this.queryPreviewContent = queryInfo;
287
+ this.queryPreviewContent.content =
288
+ (yield this.graphManagerState.graphManager.prettyLambdaContent(
289
+ queryInfo.content,
290
+ )) as string;
291
+ this.previewQueryState.pass();
292
+ } catch (error) {
293
+ assertErrorThrown(error);
294
+ this.applicationStore.notificationService.notifyError(error);
295
+ this.previewQueryState.fail();
296
+ }
297
+ }
298
+ }
@@ -16,10 +16,13 @@
16
16
 
17
17
  import {
18
18
  type PureModel,
19
- VariableExpression,
19
+ type GraphManagerState,
20
20
  type ValueSpecification,
21
21
  type Type,
22
22
  type Enum,
23
+ type ObserverContext,
24
+ type RawLambda,
25
+ VariableExpression,
23
26
  CollectionInstanceValue,
24
27
  Enumeration,
25
28
  EnumValueExplicitReference,
@@ -32,7 +35,12 @@ import {
32
35
  PRIMITIVE_TYPE,
33
36
  INTERNAL__PropagatedValue,
34
37
  SimpleFunctionExpression,
35
- type ObserverContext,
38
+ LambdaFunction,
39
+ FunctionType,
40
+ PackageableElementExplicitReference,
41
+ Multiplicity,
42
+ CORE_PURE_PATH,
43
+ buildRawLambdaFromLambdaFunction,
36
44
  } from '@finos/legend-graph';
37
45
  import { Randomizer, UnsupportedOperationError } from '@finos/legend-shared';
38
46
  import { generateDefaultValueForPrimitiveType } from '../QueryBuilderValueSpecificationHelper.js';
@@ -177,6 +185,35 @@ export const buildDefaultInstanceValue = (
177
185
  }
178
186
  };
179
187
 
188
+ export const buildDefaultEmptyStringLambda = (
189
+ graph: PureModel,
190
+ observerContext: ObserverContext,
191
+ ): LambdaFunction => {
192
+ const lambdaFunction = new LambdaFunction(
193
+ new FunctionType(
194
+ PackageableElementExplicitReference.create(
195
+ graph.getType(CORE_PURE_PATH.ANY),
196
+ ),
197
+ Multiplicity.ONE,
198
+ ),
199
+ );
200
+ lambdaFunction.expressionSequence[0] = buildDefaultInstanceValue(
201
+ graph,
202
+ PrimitiveType.STRING,
203
+ observerContext,
204
+ );
205
+ return lambdaFunction;
206
+ };
207
+
208
+ export const buildDefaultEmptyStringRawLambda = (
209
+ graphState: GraphManagerState,
210
+ observerContext: ObserverContext,
211
+ ): RawLambda =>
212
+ buildRawLambdaFromLambdaFunction(
213
+ buildDefaultEmptyStringLambda(graphState.graph, observerContext),
214
+ graphState,
215
+ );
216
+
180
217
  export const generateVariableExpressionMockValue = (
181
218
  parameter: VariableExpression,
182
219
  graph: PureModel,
package/tsconfig.json CHANGED
@@ -71,6 +71,8 @@
71
71
  "./src/stores/QueryBuilderValueSpecificationBuilder.ts",
72
72
  "./src/stores/QueryBuilderValueSpecificationBuilderHelper.ts",
73
73
  "./src/stores/QueryBuilderValueSpecificationHelper.ts",
74
+ "./src/stores/QueryBuilder_LegendApplicationPlugin_Extension.ts",
75
+ "./src/stores/QueryLoaderState.ts",
74
76
  "./src/stores/ServiceInfo.ts",
75
77
  "./src/stores/__test-utils__/QueryBuilderStateTestUtils.ts",
76
78
  "./src/stores/entitlements/QueryBuilderCheckEntitlementsState.ts",
@@ -213,6 +215,7 @@
213
215
  "./src/components/QueryBuilderSideBar.tsx",
214
216
  "./src/components/QueryBuilderTextEditor.tsx",
215
217
  "./src/components/QueryBuilderUnsupportedQueryEditor.tsx",
218
+ "./src/components/QueryLoader.tsx",
216
219
  "./src/components/ServiceQuerySetupUtils.tsx",
217
220
  "./src/components/__test-utils__/QueryBuilderComponentTestUtils.tsx",
218
221
  "./src/components/execution-plan/ExecutionPlanViewer.tsx",