@theseam/ui-common 1.0.0-beta.9 → 1.0.1-beta.7
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/ai/package.json +3 -0
- package/asset-reader/package.json +3 -0
- package/breadcrumbs/package.json +3 -0
- package/buttons/index.d.ts +4 -1
- package/buttons/package.json +3 -0
- package/card/package.json +3 -0
- package/carousel/package.json +3 -0
- package/checkbox/package.json +3 -0
- package/confirm-dialog/package.json +3 -0
- package/core/package.json +3 -0
- package/data-exporter/package.json +3 -0
- package/data-filters/package.json +3 -0
- package/datatable/index.d.ts +44 -24
- package/datatable/package.json +3 -0
- package/datatable-alterations-display/package.json +3 -0
- package/datatable-dynamic/package.json +3 -0
- package/dynamic/package.json +3 -0
- package/dynamic-component-loader/package.json +3 -0
- package/fesm2022/theseam-ui-common-asset-reader.mjs +10 -10
- package/fesm2022/theseam-ui-common-asset-reader.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-breadcrumbs.mjs +6 -6
- package/fesm2022/theseam-ui-common-breadcrumbs.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-buttons.mjs +35 -28
- package/fesm2022/theseam-ui-common-buttons.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-card.mjs +16 -16
- package/fesm2022/theseam-ui-common-card.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-carousel.mjs +10 -10
- package/fesm2022/theseam-ui-common-carousel.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-checkbox.mjs +7 -7
- package/fesm2022/theseam-ui-common-checkbox.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-confirm-dialog.mjs +13 -13
- package/fesm2022/theseam-ui-common-confirm-dialog.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-data-exporter.mjs +31 -14
- package/fesm2022/theseam-ui-common-data-exporter.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-data-filters.mjs +13 -13
- package/fesm2022/theseam-ui-common-data-filters.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-datatable-alterations-display.mjs +12 -12
- package/fesm2022/theseam-ui-common-datatable-alterations-display.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-datatable-dynamic.mjs +25 -25
- package/fesm2022/theseam-ui-common-datatable-dynamic.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-datatable.mjs +177 -145
- package/fesm2022/theseam-ui-common-datatable.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-dynamic-component-loader.mjs +7 -7
- package/fesm2022/theseam-ui-common-dynamic-component-loader.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-dynamic.mjs +21 -21
- package/fesm2022/theseam-ui-common-dynamic.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-footer-bar.mjs +7 -7
- package/fesm2022/theseam-ui-common-footer-bar.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-form-field-error.mjs +16 -16
- package/fesm2022/theseam-ui-common-form-field-error.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-form-field.mjs +22 -22
- package/fesm2022/theseam-ui-common-form-field.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-framework.mjs +160 -158
- package/fesm2022/theseam-ui-common-framework.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-google-maps.mjs +40 -40
- package/fesm2022/theseam-ui-common-google-maps.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-graphql.mjs +852 -479
- package/fesm2022/theseam-ui-common-graphql.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-icon.mjs +13 -13
- package/fesm2022/theseam-ui-common-icon.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-layout.mjs +7 -7
- package/fesm2022/theseam-ui-common-layout.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-loading.mjs +10 -10
- package/fesm2022/theseam-ui-common-loading.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-menu.mjs +25 -25
- package/fesm2022/theseam-ui-common-menu.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-modal.mjs +43 -43
- package/fesm2022/theseam-ui-common-modal.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-navigation-reload.mjs +3 -3
- package/fesm2022/theseam-ui-common-navigation-reload.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-popover.mjs +10 -10
- package/fesm2022/theseam-ui-common-popover.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-progress.mjs +7 -7
- package/fesm2022/theseam-ui-common-progress.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-rich-text.mjs +7 -7
- package/fesm2022/theseam-ui-common-rich-text.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-scrollbar.mjs +6 -6
- package/fesm2022/theseam-ui-common-scrollbar.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-services.mjs +12 -12
- package/fesm2022/theseam-ui-common-services.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-shared.mjs +37 -37
- package/fesm2022/theseam-ui-common-shared.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-storage.mjs +3 -3
- package/fesm2022/theseam-ui-common-storage.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-story-helpers.mjs +26 -26
- package/fesm2022/theseam-ui-common-story-helpers.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-tabbed.mjs +22 -22
- package/fesm2022/theseam-ui-common-tabbed.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-table-cell-type.mjs +10 -10
- package/fesm2022/theseam-ui-common-table-cell-type.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-table-cell-types.mjs +31 -31
- package/fesm2022/theseam-ui-common-table-cell-types.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-table.mjs +20 -28
- package/fesm2022/theseam-ui-common-table.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-tel-input.mjs +13 -13
- package/fesm2022/theseam-ui-common-tel-input.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-tiled-select.mjs +22 -22
- package/fesm2022/theseam-ui-common-tiled-select.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-toggle-edit.mjs +16 -16
- package/fesm2022/theseam-ui-common-toggle-edit.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-toggle-group.mjs +10 -10
- package/fesm2022/theseam-ui-common-toggle-group.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-tooltip.mjs +10 -10
- package/fesm2022/theseam-ui-common-tooltip.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-unsaved-changes-dialog.mjs +10 -10
- package/fesm2022/theseam-ui-common-unsaved-changes-dialog.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-vertical-list-filter.mjs +3 -3
- package/fesm2022/theseam-ui-common-vertical-list-filter.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-viewers.mjs +12 -12
- package/fesm2022/theseam-ui-common-viewers.mjs.map +1 -1
- package/fesm2022/theseam-ui-common-widget.mjs +135 -135
- package/fesm2022/theseam-ui-common-widget.mjs.map +1 -1
- package/footer-bar/package.json +3 -0
- package/form-field/package.json +3 -0
- package/form-field-error/package.json +3 -0
- package/framework/package.json +3 -0
- package/google-maps/package.json +3 -0
- package/graphql/index.d.ts +265 -54
- package/graphql/package.json +3 -0
- package/icon/package.json +3 -0
- package/layout/package.json +3 -0
- package/loading/package.json +3 -0
- package/menu/package.json +3 -0
- package/modal/package.json +3 -0
- package/models/package.json +3 -0
- package/navigation-reload/package.json +3 -0
- package/package.json +55 -55
- package/popover/package.json +3 -0
- package/progress/package.json +3 -0
- package/rich-text/package.json +3 -0
- package/scrollbar/package.json +3 -0
- package/services/package.json +3 -0
- package/shared/package.json +3 -0
- package/storage/package.json +3 -0
- package/story-helpers/package.json +3 -0
- package/tabbed/package.json +3 -0
- package/table/index.d.ts +1 -5
- package/table/package.json +3 -0
- package/table-cell-type/package.json +3 -0
- package/table-cell-types/package.json +3 -0
- package/tel-input/package.json +3 -0
- package/testing/package.json +3 -0
- package/tiled-select/package.json +3 -0
- package/toggle-edit/package.json +3 -0
- package/toggle-group/package.json +3 -0
- package/tooltip/package.json +3 -0
- package/unsaved-changes-dialog/package.json +3 -0
- package/utils/package.json +3 -0
- package/validators/package.json +3 -0
- package/vertical-list-filter/package.json +3 -0
- package/viewers/package.json +3 -0
- package/widget/package.json +3 -0
|
@@ -1,16 +1,78 @@
|
|
|
1
|
+
import { ApolloLink, NetworkStatus, Observable as Observable$1 } from '@apollo/client/core';
|
|
2
|
+
import { print, visit as visit$1, parse, BREAK as BREAK$1, parseValue, graphqlSync, buildSchema } from 'graphql';
|
|
3
|
+
import { hasProperty, notNullOrUndefined, isNullOrUndefined, withoutProperty, wrapIntoObservable, withoutProperties } from '@theseam/ui-common/utils';
|
|
1
4
|
import { visit, BREAK } from 'graphql/language';
|
|
2
5
|
import * as i0 from '@angular/core';
|
|
3
6
|
import { isDevMode, InjectionToken, Optional, Inject, Injectable, EventEmitter } from '@angular/core';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { Observable, EMPTY, from, of, ReplaySubject, combineLatest, BehaviorSubject, defer, Subject, isObservable } from 'rxjs';
|
|
7
|
-
import { switchMap, tap, take, concatMap, filter, toArray, map, shareReplay, startWith, distinctUntilChanged, skip, auditTime, finalize } from 'rxjs/operators';
|
|
8
|
-
import { NetworkStatus, ApolloLink, Observable as Observable$1 } from '@apollo/client/core';
|
|
7
|
+
import { Observable, EMPTY, merge, from, defer, of, combineLatest, Subscription, Subject, BehaviorSubject, isObservable } from 'rxjs';
|
|
8
|
+
import { switchMap, auditTime, map, tap, take, concatMap, filter, toArray, distinctUntilChanged, finalize, catchError, shareReplay, startWith, skip } from 'rxjs/operators';
|
|
9
9
|
import * as i1 from 'apollo-angular';
|
|
10
|
-
import { gql,
|
|
10
|
+
import { gql, provideApollo } from 'apollo-angular';
|
|
11
11
|
import { THESEAM_COLUMNS_DATA_FILTER_DATE_TEXT_SEARCH_TYPES, getFormattedDateForComparison, THESEAM_COLUMNS_DATA_FILTER_DATE_RANGE_SEARCH_TYPES, THESEAM_COLUMNS_DATA_FILTER_NUMERIC_TEXT_SEARCH_TYPES, THESEAM_COLUMNS_DATA_FILTER_NUMERIC_RANGE_SEARCH_TYPES, THESEAM_COLUMNS_DATA_FILTER_TEXT_TEXT_SEARCH_TYPES } from '@theseam/ui-common/datatable';
|
|
12
12
|
import { concat, InMemoryCache } from '@apollo/client';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Wraps an Apollo link and logs the operation state immediately before and
|
|
16
|
+
* after the wrapped link processes it.
|
|
17
|
+
*
|
|
18
|
+
* Useful for inspecting what `queryProcessingLink` does to a query:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* link: concat(
|
|
22
|
+
* logQueryLink(queryProcessingLink),
|
|
23
|
+
* httpLink,
|
|
24
|
+
* )
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The output uses `console.log` with optional CSS styles so each snapshot
|
|
28
|
+
* is easy to distinguish in the browser console. Tree-shaking will remove
|
|
29
|
+
* this from production builds when it is not imported.
|
|
30
|
+
*/
|
|
31
|
+
function logQueryLink(inner, options) {
|
|
32
|
+
const beforeStyles = options?.beforeStyles ?? 'color: cyan';
|
|
33
|
+
const afterStyles = options?.afterStyles ?? 'color: limegreen';
|
|
34
|
+
const beforeLink = new ApolloLink((operation, forward) => {
|
|
35
|
+
// eslint-disable-next-line no-console
|
|
36
|
+
console.log(`%c~~~BEFORE\n${print(operation.query)}\n${JSON.stringify(operation.variables, null, 2)}`, beforeStyles);
|
|
37
|
+
return forward(operation);
|
|
38
|
+
});
|
|
39
|
+
const afterLink = new ApolloLink((operation, forward) => {
|
|
40
|
+
// eslint-disable-next-line no-console
|
|
41
|
+
console.log(`%c~~~AFTER\n${print(operation.query)}\n${JSON.stringify(operation.variables, null, 2)}`, afterStyles);
|
|
42
|
+
return forward(operation);
|
|
43
|
+
});
|
|
44
|
+
return ApolloLink.from([beforeLink, inner, afterLink]);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class GQLDirection {
|
|
48
|
+
direction;
|
|
49
|
+
constructor(direction) {
|
|
50
|
+
this.direction = direction;
|
|
51
|
+
}
|
|
52
|
+
static ASC = new GQLDirection('ASC');
|
|
53
|
+
static DESC = new GQLDirection('DESC');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
var HintsKind;
|
|
57
|
+
(function (HintsKind) {
|
|
58
|
+
HintsKind["OperationDefinition"] = "OperationDefinition";
|
|
59
|
+
HintsKind["Field"] = "Field";
|
|
60
|
+
HintsKind["VariableDefinition"] = "VariableDefinition";
|
|
61
|
+
HintsKind["Variable"] = "Variable";
|
|
62
|
+
HintsKind["Argument"] = "Argument";
|
|
63
|
+
})(HintsKind || (HintsKind = {}));
|
|
64
|
+
|
|
65
|
+
const DEFAULT_TO_REMOVE_ON_UNDEFINED = ['where', 'order'];
|
|
66
|
+
|
|
67
|
+
class GQLVariable {
|
|
68
|
+
name;
|
|
69
|
+
type;
|
|
70
|
+
constructor(name, type) {
|
|
71
|
+
this.name = name;
|
|
72
|
+
this.type = type;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
14
76
|
function containsVariable(node, variableName) {
|
|
15
77
|
let found = false;
|
|
16
78
|
visit(node, {
|
|
@@ -101,35 +163,6 @@ function parseComments(ast) {
|
|
|
101
163
|
return comments;
|
|
102
164
|
}
|
|
103
165
|
|
|
104
|
-
class GQLDirection {
|
|
105
|
-
direction;
|
|
106
|
-
constructor(direction) {
|
|
107
|
-
this.direction = direction;
|
|
108
|
-
}
|
|
109
|
-
static ASC = new GQLDirection('ASC');
|
|
110
|
-
static DESC = new GQLDirection('DESC');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
var HintsKind;
|
|
114
|
-
(function (HintsKind) {
|
|
115
|
-
HintsKind["OperationDefinition"] = "OperationDefinition";
|
|
116
|
-
HintsKind["Field"] = "Field";
|
|
117
|
-
HintsKind["VariableDefinition"] = "VariableDefinition";
|
|
118
|
-
HintsKind["Variable"] = "Variable";
|
|
119
|
-
HintsKind["Argument"] = "Argument";
|
|
120
|
-
})(HintsKind || (HintsKind = {}));
|
|
121
|
-
|
|
122
|
-
const DEFAULT_TO_REMOVE_ON_UNDEFINED = ['where', 'order'];
|
|
123
|
-
|
|
124
|
-
class GQLVariable {
|
|
125
|
-
name;
|
|
126
|
-
type;
|
|
127
|
-
constructor(name, type) {
|
|
128
|
-
this.name = name;
|
|
129
|
-
this.type = type;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
166
|
const HINT_PREFIX_REGEX = /^\s*@gql-hint:.+/;
|
|
134
167
|
const HINT_NAMES_CAPTURE_REGEX = /^\s*@gql-hint:([a-zA-z-\d\s]+)$/;
|
|
135
168
|
/**
|
|
@@ -294,6 +327,23 @@ function removeVariable(query, variableName) {
|
|
|
294
327
|
// type-safe one, but that is becoming surprisingly harder to find than I
|
|
295
328
|
// expected for GraphQL.
|
|
296
329
|
function toGQL(json) {
|
|
330
|
+
// Handle primitive top-level values so callers can pass non-objects (e.g.
|
|
331
|
+
// when inlining a String or Int variable directly into a query argument).
|
|
332
|
+
if (json === null || json === undefined) {
|
|
333
|
+
return 'null';
|
|
334
|
+
}
|
|
335
|
+
if (typeof json === 'string') {
|
|
336
|
+
return `"${json}"`;
|
|
337
|
+
}
|
|
338
|
+
if (typeof json === 'number' || typeof json === 'boolean') {
|
|
339
|
+
return `${json}`;
|
|
340
|
+
}
|
|
341
|
+
if (json instanceof GQLDirection) {
|
|
342
|
+
return `${json.direction}`;
|
|
343
|
+
}
|
|
344
|
+
if (Array.isArray(json)) {
|
|
345
|
+
return `[${json.map((v) => toGQL(v)).join(',')}]`;
|
|
346
|
+
}
|
|
297
347
|
const props = Object.keys(json).map((prop) => {
|
|
298
348
|
const value = json[prop];
|
|
299
349
|
let resultValue;
|
|
@@ -329,6 +379,215 @@ function hintsTokensContainingHint(hintsTokens, hint) {
|
|
|
329
379
|
return hintsTokens.filter((r) => r.hints.indexOf(hint) !== -1);
|
|
330
380
|
}
|
|
331
381
|
|
|
382
|
+
/**
|
|
383
|
+
* Inlines the variable's current value directly into the query AST and removes
|
|
384
|
+
* the variable definition from the parameter list. The variable is also
|
|
385
|
+
* removed from the operation's variables map so it is not sent to the server.
|
|
386
|
+
*
|
|
387
|
+
* Applies to VariableDefinition (comment above the `$var` in the parameter
|
|
388
|
+
* list) or Variable (inline comment beside a `$var` usage in the query body).
|
|
389
|
+
*/
|
|
390
|
+
const inlineVariableTransformer = (operation, hintsToken) => {
|
|
391
|
+
let varName = null;
|
|
392
|
+
if (hintsToken.kind === HintsKind.VariableDefinition) {
|
|
393
|
+
varName = hintsToken.node.variable.name.value;
|
|
394
|
+
}
|
|
395
|
+
else if (hintsToken.kind === HintsKind.Variable) {
|
|
396
|
+
varName = hintsToken.node.name.value;
|
|
397
|
+
}
|
|
398
|
+
if (varName === null) {
|
|
399
|
+
return operation;
|
|
400
|
+
}
|
|
401
|
+
const varValue = operation.variables[varName];
|
|
402
|
+
const newVariables = withoutProperty(operation.variables, varName);
|
|
403
|
+
let query = removeVariableDefinition(operation.query, varName);
|
|
404
|
+
query = inlineVariable(query, varName, parseValue(toGQL(varValue)));
|
|
405
|
+
return { query, variables: newVariables };
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Removes every variable from the query that is null or undefined in the
|
|
410
|
+
* operation's variables map. Both the variable definition (from the parameter
|
|
411
|
+
* list) and any argument usages in the query body are removed so the resulting
|
|
412
|
+
* document remains valid.
|
|
413
|
+
*/
|
|
414
|
+
const removeNotDefinedTransformer = (operation, hintsToken) => {
|
|
415
|
+
const operationNode = hintsToken.node;
|
|
416
|
+
const undefinedVarNames = (operationNode.variableDefinitions ?? [])
|
|
417
|
+
.filter((varDef) => isNullOrUndefined(operation.variables[varDef.variable.name.value]))
|
|
418
|
+
.map((varDef) => varDef.variable.name.value);
|
|
419
|
+
let query = operation.query;
|
|
420
|
+
for (const varName of undefinedVarNames) {
|
|
421
|
+
query = removeVariable(query, varName);
|
|
422
|
+
}
|
|
423
|
+
return { query, variables: operation.variables };
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Maps variable value to gql and inlines it into the query.
|
|
428
|
+
*/
|
|
429
|
+
const inlineVariableHintDef = {
|
|
430
|
+
name: 'inline-variable',
|
|
431
|
+
appliesTo: [HintsKind.Variable, HintsKind.VariableDefinition],
|
|
432
|
+
transformer: inlineVariableTransformer,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Remove the variable from the query if it is not defined in the operation
|
|
437
|
+
* variables.
|
|
438
|
+
*/
|
|
439
|
+
const removeNotDefinedHintDef = {
|
|
440
|
+
name: 'remove-not-defined',
|
|
441
|
+
appliesTo: [HintsKind.OperationDefinition],
|
|
442
|
+
transformer: removeNotDefinedTransformer,
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
// /**
|
|
446
|
+
// * Remove the variable from the query if it is not defined in the operation
|
|
447
|
+
// * variables.
|
|
448
|
+
// *
|
|
449
|
+
// * Applies to: OperationDefinition
|
|
450
|
+
// */
|
|
451
|
+
// export const GQL_HINT_REMOVE_NOT_DEFINED = 'remove-not-defined'
|
|
452
|
+
// /**
|
|
453
|
+
// * Maps variable value to gql and inlines it into the query.
|
|
454
|
+
// *
|
|
455
|
+
// * Applies to: Variable, VariableDefinition
|
|
456
|
+
// */
|
|
457
|
+
// export const GQL_HINT_INLINE_VARIABLE = 'inline-variable'
|
|
458
|
+
// /**
|
|
459
|
+
// * Remove variable definition from the query if it is not used by the operation.
|
|
460
|
+
// *
|
|
461
|
+
// * Applies to: VariableDefinition
|
|
462
|
+
// */
|
|
463
|
+
// // export const GQL_HINT_REMOVE_IF_NOT_USED = 'remove-if-not-used'
|
|
464
|
+
// export const GQL_HINTS = [
|
|
465
|
+
// // GQL_HINT_REMOVE_IF_NOT_USED,
|
|
466
|
+
// GQL_HINT_REMOVE_NOT_DEFINED,
|
|
467
|
+
// GQL_HINT_INLINE_VARIABLE
|
|
468
|
+
// ]
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Apollo link that transforms GraphQL operations before they are sent.
|
|
472
|
+
*
|
|
473
|
+
* Two mechanisms are supported and can be combined freely:
|
|
474
|
+
*
|
|
475
|
+
* **Hint-based** — place `# @gql-hint: <name>` comments directly in the query.
|
|
476
|
+
* Supported hints:
|
|
477
|
+
* - `remove-not-defined` on the operation definition: removes every variable
|
|
478
|
+
* whose value is null/undefined (definition + argument usage).
|
|
479
|
+
* - `inline-variable` on a variable definition or usage: substitutes the
|
|
480
|
+
* variable's current value directly into the query AST and removes it from
|
|
481
|
+
* the variables map.
|
|
482
|
+
*
|
|
483
|
+
* **Config-based** — pass a `QueryProcessingConfig` via Apollo context under
|
|
484
|
+
* the key `queryProcessingConfig`. Supported options:
|
|
485
|
+
* - `variables.removeIfNotDefined`: remove named variables when null/undefined.
|
|
486
|
+
* - `variables.removeIfNotUsed`: remove named variable definitions when the
|
|
487
|
+
* variable is not referenced anywhere in the (possibly already-transformed)
|
|
488
|
+
* query body.
|
|
489
|
+
* - `variables.inline`: inline named variables into the query AST.
|
|
490
|
+
*
|
|
491
|
+
* Hints are applied first, then config-based processing.
|
|
492
|
+
*/
|
|
493
|
+
const queryProcessingLink = new ApolloLink((operation, forward) => {
|
|
494
|
+
const context = operation.getContext();
|
|
495
|
+
const queryProcessingConfig = context.queryProcessingConfig ?? {};
|
|
496
|
+
// Reparse to ensure token/comment info is present for hint parsing.
|
|
497
|
+
let _ast = parseAst(operation.query);
|
|
498
|
+
const rules = parseHints(_ast);
|
|
499
|
+
// ---- Hint: remove-not-defined ----------------------------------------
|
|
500
|
+
for (const hintsToken of hintsTokensContainingHint(rules, removeNotDefinedHintDef.name)) {
|
|
501
|
+
const result = removeNotDefinedHintDef.transformer({ query: _ast, variables: operation.variables }, hintsToken);
|
|
502
|
+
_ast = result.query;
|
|
503
|
+
operation.variables = result.variables;
|
|
504
|
+
}
|
|
505
|
+
// ---- Hint: inline-variable --------------------------------------------
|
|
506
|
+
for (const hintsToken of hintsTokensContainingHint(rules, inlineVariableHintDef.name)) {
|
|
507
|
+
const result = inlineVariableHintDef.transformer({ query: _ast, variables: operation.variables }, hintsToken);
|
|
508
|
+
_ast = result.query;
|
|
509
|
+
operation.variables = result.variables;
|
|
510
|
+
}
|
|
511
|
+
// ---- Config: removeIfNotDefined ---------------------------------------
|
|
512
|
+
for (const varName of queryProcessingConfig?.variables?.removeIfNotDefined ??
|
|
513
|
+
[]) {
|
|
514
|
+
if (isNullOrUndefined(operation.variables[varName])) {
|
|
515
|
+
_ast = removeVariable(_ast, varName);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// ---- Config: removeIfNotUsed -----------------------------------------
|
|
519
|
+
// Intentionally runs after removeIfNotDefined so that variables which were
|
|
520
|
+
// only referenced inside another (now-removed) variable can be cleaned up.
|
|
521
|
+
for (const varName of queryProcessingConfig?.variables?.removeIfNotUsed ??
|
|
522
|
+
[]) {
|
|
523
|
+
if (!containsVariable(_ast, varName)) {
|
|
524
|
+
_ast = removeVariable(_ast, varName);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
// ---- Config: inline --------------------------------------------------
|
|
528
|
+
for (const varName of queryProcessingConfig?.variables?.inline ?? []) {
|
|
529
|
+
const varValue = operation.variables[varName];
|
|
530
|
+
operation.variables = withoutProperty(operation.variables, varName);
|
|
531
|
+
_ast = removeVariableDefinition(_ast, varName);
|
|
532
|
+
_ast = inlineVariable(_ast, varName, parseValue(toGQL(varValue)));
|
|
533
|
+
}
|
|
534
|
+
operation.query = _ast;
|
|
535
|
+
return forward(operation);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Creates a {@link SortsMapper} from a declarative field-name map.
|
|
540
|
+
*
|
|
541
|
+
* Each key must correspond to a datatable column `prop` value. The value
|
|
542
|
+
* controls how that column's sort is translated to a GQL sort object:
|
|
543
|
+
*
|
|
544
|
+
* - `string` – emits `{ [gqlField]: 'ASC' | 'DESC' }`
|
|
545
|
+
* - `null` – column is not sortable; the sort item is dropped
|
|
546
|
+
* - `function` – called with `(prop, context)` and may return a field
|
|
547
|
+
* name or `null` to drop the item dynamically
|
|
548
|
+
*
|
|
549
|
+
* In dev mode an error is thrown when a sort item's `prop` is not present
|
|
550
|
+
* in the map. In production the item is silently dropped.
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* // Simple static mapping
|
|
554
|
+
* const mapSorts = createSortsMapper<'id' | 'name'>({
|
|
555
|
+
* id: 'id',
|
|
556
|
+
* name: 'name',
|
|
557
|
+
* })
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* // Dynamic mapping with context access
|
|
561
|
+
* const mapSorts = createSortsMapper<'id' | 'name' | 'computed'>({
|
|
562
|
+
* id: 'id',
|
|
563
|
+
* name: 'name',
|
|
564
|
+
* computed: (prop, context) =>
|
|
565
|
+
* context.extraVariables.useAlt ? 'altField' : prop,
|
|
566
|
+
* })
|
|
567
|
+
*/
|
|
568
|
+
function createSortsMapper(fieldMap) {
|
|
569
|
+
return (sorts, context) => {
|
|
570
|
+
const result = [];
|
|
571
|
+
for (const s of sorts) {
|
|
572
|
+
const prop = s?.prop;
|
|
573
|
+
if (!(prop in fieldMap)) {
|
|
574
|
+
if (isDevMode()) {
|
|
575
|
+
throw new Error(`createSortsMapper: no mapping found for column prop "${prop}". ` +
|
|
576
|
+
`Add an entry to the field map or set the value to null to ignore it.`);
|
|
577
|
+
}
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
const entry = fieldMap[prop];
|
|
581
|
+
const dir = s?.dir?.toUpperCase();
|
|
582
|
+
const gqlField = typeof entry === 'function' ? entry(prop, context) : entry;
|
|
583
|
+
if (gqlField === null)
|
|
584
|
+
continue;
|
|
585
|
+
result.push({ [gqlField]: dir });
|
|
586
|
+
}
|
|
587
|
+
return result;
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
332
591
|
const DEFAULT_PAGE_SIZE = 20;
|
|
333
592
|
function getPageInfo(datatable, defaultPageSize = DEFAULT_PAGE_SIZE) {
|
|
334
593
|
return {
|
|
@@ -357,7 +616,10 @@ function createPageInfoObservable(datatable$, defaultPageSize = 20) {
|
|
|
357
616
|
return EMPTY;
|
|
358
617
|
}
|
|
359
618
|
handlePageInfo(getPageInfo(dt, defaultPageSize));
|
|
360
|
-
|
|
619
|
+
// `page` does not emit when the page size changes (e.g. on
|
|
620
|
+
// window resize). Merging the `resize` event is a workaround.
|
|
621
|
+
const resize$ = dt.resize.pipe(auditTime(100), map(() => dt.pageInfo));
|
|
622
|
+
return merge(dt.page, resize$).pipe(tap((p) => handlePageInfo(p)));
|
|
361
623
|
}))
|
|
362
624
|
.subscribe();
|
|
363
625
|
return () => {
|
|
@@ -387,8 +649,12 @@ function resolveMapper(filterState, filterStateMappers, context) {
|
|
|
387
649
|
function resolveMappers(filterStates, filterStateMappers, context) {
|
|
388
650
|
return from(filterStates).pipe(concatMap((filterState) => resolveMapper(filterState, filterStateMappers, context)), filter(notNullOrUndefined), toArray());
|
|
389
651
|
}
|
|
652
|
+
/**
|
|
653
|
+
* Combines multiple active filter results with AND so that all conditions must
|
|
654
|
+
* be satisfied simultaneously (e.g. a search filter AND a status filter).
|
|
655
|
+
*/
|
|
390
656
|
function mergeFilters(filters) {
|
|
391
|
-
return {
|
|
657
|
+
return { and: filters };
|
|
392
658
|
}
|
|
393
659
|
/**
|
|
394
660
|
* Merges variable objects.
|
|
@@ -452,180 +718,123 @@ function mapPageInfo(pageInfo) {
|
|
|
452
718
|
}
|
|
453
719
|
|
|
454
720
|
function observeRowsWithGqlInputsHandling(queryRef, rows, datatable, extraVariables, sortsMapper, filterStateMappers) {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
};
|
|
469
|
-
const datatableResults$ = _createDatatableResultsObservable(datatable$, datatableMappers, context$);
|
|
470
|
-
const queryVariablesChanged$ = datatableResults$.pipe(tap((results) => {
|
|
471
|
-
queryRef.setVariables({
|
|
472
|
-
...(results.context || {}),
|
|
473
|
-
...results.pageInfo,
|
|
474
|
-
...(results.sorts.length > 0 ? { order: results.sorts } : {}),
|
|
475
|
-
...(results.filter?.variables || {}),
|
|
476
|
-
...(results.filter?.filter ? { where: results.filter.filter } : {}),
|
|
477
|
-
});
|
|
478
|
-
}));
|
|
479
|
-
// const _emitSubject = new Subject<void>()
|
|
480
|
-
// const queryVarsChangedSub = queryVariablesChanged$.pipe(
|
|
481
|
-
// switchMap(() => subscriberCount(rows, 'rows'))
|
|
482
|
-
// ).subscribe(subscriber)
|
|
483
|
-
const queryVarsChangedSub = queryVariablesChanged$.subscribe();
|
|
484
|
-
const _sub = subscriberCount(rows, 'rows').subscribe(subscriber);
|
|
485
|
-
return () => {
|
|
486
|
-
queryVarsChangedSub.unsubscribe();
|
|
487
|
-
_sub.unsubscribe();
|
|
488
|
-
};
|
|
721
|
+
const datatable$ = wrapIntoObservable(datatable);
|
|
722
|
+
const extraVariables$ = wrapIntoObservable(extraVariables);
|
|
723
|
+
// Only emit page changes past the first when paging is enabled. When paging
|
|
724
|
+
// is disabled, all data is already in the buffer so there is no need to
|
|
725
|
+
// re-query on page changes.
|
|
726
|
+
const pageInfo$ = defer(() => {
|
|
727
|
+
let firstEmit = true;
|
|
728
|
+
return createPageInfoObservable(datatable$).pipe(switchMap((pageInfo) => {
|
|
729
|
+
if (!firstEmit && _isPagingDisabled(queryRef)) {
|
|
730
|
+
return EMPTY;
|
|
731
|
+
}
|
|
732
|
+
firstEmit = false;
|
|
733
|
+
return of(pageInfo);
|
|
734
|
+
}), map(mapPageInfo));
|
|
489
735
|
});
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
// ...(v.filterInfo?.filter ? { where: v.filterInfo.filter } : {})
|
|
541
|
-
// } as any)
|
|
542
|
-
// })
|
|
543
|
-
// )
|
|
544
|
-
// return defer(() => {
|
|
545
|
-
// const _emitted = new Subject<void>()
|
|
546
|
-
// const handlerSub = handleQueryInputs.pipe(
|
|
547
|
-
// // skip(1)
|
|
548
|
-
// // ).subscribe(() => _emitted.next())
|
|
549
|
-
// ).subscribe(() => {
|
|
550
|
-
// _emitted.next()
|
|
551
|
-
// })
|
|
552
|
-
// return _emitted.pipe(
|
|
553
|
-
// // tap(v => {
|
|
554
|
-
// // console.log('emitted', v)
|
|
555
|
-
// // }),
|
|
556
|
-
// distinctUntilChanged(),
|
|
557
|
-
// switchMap(() => subscriberCount(rows, 'rows')),
|
|
558
|
-
// // tap(v => {
|
|
559
|
-
// // console.log('emitting rows', v)
|
|
560
|
-
// // }),
|
|
561
|
-
// finalize(() => handlerSub.unsubscribe())
|
|
562
|
-
// )
|
|
563
|
-
// }).pipe(
|
|
564
|
-
// // tap(v => {
|
|
565
|
-
// // console.log('rows', v)
|
|
566
|
-
// // }),
|
|
567
|
-
// catchError(err => {
|
|
568
|
-
// console.error(err)
|
|
569
|
-
// return of([] as TRow[])
|
|
570
|
-
// }),
|
|
571
|
-
// shareReplay({ bufferSize: 1, refCount: true })
|
|
572
|
-
// )
|
|
736
|
+
// Combines extraVariables and pageInfo, then derives sorts and filter state.
|
|
737
|
+
// auditTime(0) debounces rapid synchronous emissions (e.g. at startup) so
|
|
738
|
+
// only one setVariables call is made per event-loop turn.
|
|
739
|
+
const handleQueryInputs = combineLatest([extraVariables$, pageInfo$]).pipe(auditTime(0), switchMap(([_extraVariables, pageInfo]) => {
|
|
740
|
+
const context = { extraVariables: _extraVariables };
|
|
741
|
+
const sorts$ = _createSortsObservable(datatable$).pipe(switchMap((m) => wrapIntoObservable(sortsMapper(m, context))));
|
|
742
|
+
const filterInfo$ = _createFilterStatesObservable(datatable$).pipe(switchMap((x) => mapFilterStates(x, filterStateMappers, context)),
|
|
743
|
+
// TODO: Remove when the datatable fixes the bug causing it to emit more than it should.
|
|
744
|
+
distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y)));
|
|
745
|
+
return combineLatest([sorts$, filterInfo$]).pipe(map(([sorts, filterInfo]) => ({
|
|
746
|
+
extraVariables: _extraVariables,
|
|
747
|
+
pageInfo,
|
|
748
|
+
sorts,
|
|
749
|
+
filterInfo,
|
|
750
|
+
})));
|
|
751
|
+
}), tap((v) => {
|
|
752
|
+
queryRef.setVariables({
|
|
753
|
+
...(v.extraVariables || {}),
|
|
754
|
+
...v.pageInfo,
|
|
755
|
+
...(v.sorts.length > 0 ? { order: v.sorts } : {}),
|
|
756
|
+
...(v.filterInfo?.variables || {}),
|
|
757
|
+
...(v.filterInfo?.filter ? { where: v.filterInfo.filter } : {}),
|
|
758
|
+
});
|
|
759
|
+
}));
|
|
760
|
+
return defer(() => {
|
|
761
|
+
// Observe the optional refresh-button patch attached externally to the
|
|
762
|
+
// datatable instance. When the user triggers a refresh, refetch the data.
|
|
763
|
+
let refreshBtnSub = Subscription.EMPTY;
|
|
764
|
+
refreshBtnSub = datatable$
|
|
765
|
+
.pipe(switchMap((dt) => {
|
|
766
|
+
if (!dt || !dt.__refreshPatch) {
|
|
767
|
+
return EMPTY;
|
|
768
|
+
}
|
|
769
|
+
return dt.__refreshPatch.refreshTriggered.pipe(tap(() => queryRef.refetch(undefined, true)));
|
|
770
|
+
}))
|
|
771
|
+
.subscribe();
|
|
772
|
+
// Bridge query-input changes to row emissions via a Subject so that the
|
|
773
|
+
// rows observable is only subscribed once (on the first input change) and
|
|
774
|
+
// then continues to receive updates as the live query produces new data.
|
|
775
|
+
const _emitted = new Subject();
|
|
776
|
+
const handlerSub = handleQueryInputs.subscribe(() => _emitted.next(true));
|
|
777
|
+
return _emitted.pipe(distinctUntilChanged(), switchMap(() => rows), finalize(() => {
|
|
778
|
+
handlerSub.unsubscribe();
|
|
779
|
+
refreshBtnSub.unsubscribe();
|
|
780
|
+
}));
|
|
781
|
+
}).pipe(catchError((err) => {
|
|
782
|
+
// eslint-disable-next-line no-console
|
|
783
|
+
console.error(err);
|
|
784
|
+
return of([]);
|
|
785
|
+
}), shareReplay({ bufferSize: 1, refCount: true }));
|
|
573
786
|
}
|
|
574
787
|
function _createSortsObservable(datatable$) {
|
|
575
|
-
|
|
576
|
-
//
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
788
|
+
// NOTE: There is a bug in our datatable wrapper that isn't propagating
|
|
789
|
+
// external sorting changes to the wrapped datatable component, which we observe
|
|
790
|
+
// sort events from. This workaround observes our wrapper's internal column
|
|
791
|
+
// change events, which emit all changes to columns that our datatable tracks,
|
|
792
|
+
// and reads sorts from our wrapper when externalSorting is enabled.
|
|
793
|
+
const _observeSortsWorkaround = (dt) => {
|
|
794
|
+
if (!dt._columnsAlterationsManager) {
|
|
795
|
+
// Fallback for environments (e.g. tests) where the internal manager
|
|
796
|
+
// is not present.
|
|
797
|
+
return dt.sort.pipe(map((v) => v.sorts), startWith(dt.sorts));
|
|
798
|
+
}
|
|
799
|
+
return dt._columnsAlterationsManager.changes.pipe(map(() => (dt.externalSorting ? dt._sorts : dt.sorts)), startWith(dt.externalSorting ? dt._sorts : dt.sorts));
|
|
800
|
+
};
|
|
801
|
+
return datatable$.pipe(switchMap((dt) => (dt ? _observeSortsWorkaround(dt) : of([]))), shareReplay({ bufferSize: 1, refCount: true }));
|
|
580
802
|
}
|
|
581
803
|
function _createFilterStatesObservable(datatable$) {
|
|
582
|
-
return datatable$.pipe(
|
|
583
|
-
// tap(v => console.log('filters got dt', v)),
|
|
584
|
-
switchMap((dt) => dt
|
|
585
|
-
? dt.filterStates // .pipe(tap(v => console.log('filterStates 1', v)))
|
|
586
|
-
: of([])),
|
|
587
|
-
// TODO: Remove when the datatable fixes the bug causing it to emit more than it should.
|
|
588
|
-
distinctUntilChanged((x, y) => JSON.stringify(x) === JSON.stringify(y)));
|
|
804
|
+
return datatable$.pipe(switchMap((dt) => (dt ? dt.filterStates : of([]))));
|
|
589
805
|
}
|
|
590
|
-
function
|
|
591
|
-
return
|
|
592
|
-
const datatableSubject = new ReplaySubject();
|
|
593
|
-
const dtSub = datatable$.subscribe((dt) => datatableSubject.next(dt), (err) => datatableSubject.error(err), () => datatableSubject.complete());
|
|
594
|
-
const ctxSub = context$
|
|
595
|
-
.pipe(switchMap((context) => {
|
|
596
|
-
// TODO: Decide if the disabled paging feature will be reimplemented in a way
|
|
597
|
-
// that it should be considered here. `_isPagingDisabled(queryRef)`
|
|
598
|
-
const pageInfo$ = createPageInfoObservable(datatable$).pipe(map((info) => mappers.pageInfo(info)));
|
|
599
|
-
const sorts$ = _createSortsObservable(datatable$).pipe(switchMap((m) => wrapIntoObservable(mappers.sorts(m, context))));
|
|
600
|
-
const filterInfo$ = _createFilterStatesObservable(datatable$).pipe(switchMap((x) => mapFilterStates(x, mappers.filters, context)));
|
|
601
|
-
return combineLatest([pageInfo$, sorts$, filterInfo$]).pipe(map(([pageInfo, sorts, filterInfo]) => ({
|
|
602
|
-
pageInfo,
|
|
603
|
-
sorts,
|
|
604
|
-
filter: filterInfo,
|
|
605
|
-
context,
|
|
606
|
-
})));
|
|
607
|
-
}))
|
|
608
|
-
.subscribe(subscriber);
|
|
609
|
-
return () => {
|
|
610
|
-
dtSub.unsubscribe();
|
|
611
|
-
ctxSub.unsubscribe();
|
|
612
|
-
};
|
|
613
|
-
});
|
|
806
|
+
function _isPagingDisabled(queryRef) {
|
|
807
|
+
return queryRef.getQueryProcessingConfig()?.disablePaging ?? false;
|
|
614
808
|
}
|
|
615
|
-
// function _isPagingDisabled<TData, TVariables, TRow>(queryRef: DatatableGraphQLQueryRef<TData, TVariables, TRow>): boolean {
|
|
616
|
-
// return queryRef.getQueryProcessingConfig()?.disablePaging ?? false
|
|
617
|
-
// }
|
|
618
809
|
|
|
810
|
+
// `isNetworkRequestInFlight` is only exported from the ES module subpath
|
|
811
|
+
// `@apollo/client/core/networkStatus`, which can't be loaded by Jest's CJS
|
|
812
|
+
// transform. Reimplementing it here keeps the logic accessible in tests.
|
|
813
|
+
function isNetworkRequestInFlight(networkStatus) {
|
|
814
|
+
return networkStatus ? networkStatus < NetworkStatus.ready : false;
|
|
815
|
+
}
|
|
619
816
|
/**
|
|
620
|
-
*
|
|
621
|
-
|
|
622
|
-
|
|
817
|
+
* Maximum number of error recovery attempts before throwing.
|
|
818
|
+
*/
|
|
819
|
+
const MAX_ERROR_RECOVERY_ATTEMPTS = 10;
|
|
820
|
+
/**
|
|
821
|
+
* Partially wraps ApolloClient's QueryRef with datatable paging, loading
|
|
822
|
+
* state, error handling, and debounced variable updates.
|
|
623
823
|
*/
|
|
624
824
|
class DatatableGraphQLQueryRef {
|
|
625
825
|
_queryRef;
|
|
626
826
|
_updatesPollDelay;
|
|
827
|
+
_defaultErrorHandler;
|
|
627
828
|
_variablesSubject = new BehaviorSubject({});
|
|
628
829
|
_observingChangesSubject = new BehaviorSubject(false);
|
|
830
|
+
_errorSubject = new Subject();
|
|
831
|
+
/**
|
|
832
|
+
* Used to manually trigger a pending/loading state when we know a request is
|
|
833
|
+
* coming but Apollo hasn't started it yet (e.g. after setVariables resolves to
|
|
834
|
+
* a non-paging variable change while a request is already in-flight).
|
|
835
|
+
*/
|
|
836
|
+
_manualPendingSubject = new BehaviorSubject(false);
|
|
837
|
+
_needToRequerySubject = new Subject();
|
|
629
838
|
/**
|
|
630
839
|
* Temporary way of tracking total count when paging is disabled.
|
|
631
840
|
*/
|
|
@@ -639,6 +848,18 @@ class DatatableGraphQLQueryRef {
|
|
|
639
848
|
_variablesUpdatePending = false;
|
|
640
849
|
_valueChanges;
|
|
641
850
|
loading$;
|
|
851
|
+
/**
|
|
852
|
+
* Emits whenever the query variables change.
|
|
853
|
+
*/
|
|
854
|
+
variables$ = this._variablesSubject.asObservable();
|
|
855
|
+
/**
|
|
856
|
+
* Emits when a GraphQL error occurs.
|
|
857
|
+
*
|
|
858
|
+
* If nothing is subscribed to this, the `_defaultErrorHandler` is used
|
|
859
|
+
* (if provided). Once subscribed, the subscriber is responsible for
|
|
860
|
+
* handling the error.
|
|
861
|
+
*/
|
|
862
|
+
error$ = this._errorSubject.asObservable();
|
|
642
863
|
get updatesPollDelay() {
|
|
643
864
|
return this._updatesPollDelay;
|
|
644
865
|
}
|
|
@@ -649,107 +870,167 @@ class DatatableGraphQLQueryRef {
|
|
|
649
870
|
/** Original ApolloClient's QueryRef. */
|
|
650
871
|
_queryRef,
|
|
651
872
|
/**
|
|
652
|
-
* How long to wait before
|
|
873
|
+
* How long to wait (ms) before applying variable changes and refetching.
|
|
874
|
+
*/
|
|
875
|
+
_updatesPollDelay = 500,
|
|
876
|
+
/**
|
|
877
|
+
* Default error handler used when no subscriber is listening to `error$`.
|
|
653
878
|
*/
|
|
654
|
-
|
|
879
|
+
_defaultErrorHandler) {
|
|
655
880
|
this._queryRef = _queryRef;
|
|
656
881
|
this._updatesPollDelay = _updatesPollDelay;
|
|
882
|
+
this._defaultErrorHandler = _defaultErrorHandler;
|
|
657
883
|
this._variablesSubject.next(this._queryRef.obsQuery.options.variables || {});
|
|
658
|
-
//
|
|
884
|
+
// Tracks whether the loading overlay should be held open until we know
|
|
885
|
+
// whether the first response will trigger a follow-up requery (e.g. when
|
|
886
|
+
// disablePaging is on and we first probe for totalCount). This prevents the
|
|
887
|
+
// loading overlay from flickering off and back on.
|
|
888
|
+
let hasEmittedTriggered = false;
|
|
889
|
+
const hasEmittedSubject = new BehaviorSubject(false);
|
|
890
|
+
const setHasEmitted = () => {
|
|
891
|
+
if (hasEmittedTriggered) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
hasEmittedTriggered = true;
|
|
895
|
+
const sub = this._needToRequerySubject.pipe(take(1)).subscribe(() => {
|
|
896
|
+
hasEmittedSubject.next(true);
|
|
897
|
+
sub.unsubscribe();
|
|
898
|
+
});
|
|
899
|
+
};
|
|
659
900
|
this._valueChanges = defer(() => {
|
|
660
|
-
|
|
901
|
+
let prev;
|
|
661
902
|
const varChangesSub = this._variablesSubject
|
|
662
903
|
.pipe(skip(1), tap(() => {
|
|
663
904
|
this._variablesUpdatePending = true;
|
|
664
905
|
}), auditTime(this._updatesPollDelay), finalize(() => {
|
|
665
|
-
// If the query stopped being observed before
|
|
666
|
-
//
|
|
906
|
+
// If the query stopped being observed before the debounce fired,
|
|
907
|
+
// apply the pending variables now.
|
|
667
908
|
if (this._variablesUpdatePending) {
|
|
668
909
|
this.refetch();
|
|
669
910
|
this._variablesUpdatePending = false;
|
|
670
911
|
}
|
|
671
912
|
}))
|
|
672
913
|
.subscribe((variables) => {
|
|
673
|
-
// console.log('set vars', variables)
|
|
674
914
|
this._setVariablesImmediate(variables);
|
|
675
|
-
|
|
915
|
+
const current = withoutProperties(this.getVariables(), [
|
|
916
|
+
'skip',
|
|
917
|
+
'take',
|
|
918
|
+
]);
|
|
919
|
+
const isVarsChanged = prev === undefined ||
|
|
920
|
+
JSON.stringify(prev) !== JSON.stringify(current);
|
|
921
|
+
prev = current;
|
|
676
922
|
this._variablesUpdatePending = false;
|
|
923
|
+
// When the non-paging variables change while Apollo is already
|
|
924
|
+
// in setVariables state, manually signal a pending load so the
|
|
925
|
+
// loading overlay doesn't disappear until the response arrives.
|
|
926
|
+
if (isVarsChanged &&
|
|
927
|
+
this._queryRef.getCurrentResult().networkStatus ===
|
|
928
|
+
NetworkStatus.setVariables) {
|
|
929
|
+
this._manualPendingSubject.next(true);
|
|
930
|
+
}
|
|
677
931
|
});
|
|
932
|
+
let repeatedErrors = 0;
|
|
678
933
|
this._observingChanges = true;
|
|
679
934
|
return this._queryRef.valueChanges.pipe(
|
|
680
|
-
//
|
|
681
|
-
//
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
935
|
+
// Once a response lands (not in-flight), begin tracking whether the
|
|
936
|
+
// initial load has truly completed (accounting for disablePaging requery).
|
|
937
|
+
tap((v) => !isNetworkRequestInFlight(v.networkStatus)
|
|
938
|
+
? setHasEmitted()
|
|
939
|
+
: undefined),
|
|
940
|
+
// Guard against infinite error loops when polling / retrying.
|
|
941
|
+
tap((v) => {
|
|
942
|
+
if (v.networkStatus === NetworkStatus.error) {
|
|
943
|
+
repeatedErrors++;
|
|
944
|
+
if (repeatedErrors >= MAX_ERROR_RECOVERY_ATTEMPTS) {
|
|
945
|
+
throw Error('Max error recovery attempts reached.');
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
else if (v.networkStatus === NetworkStatus.ready) {
|
|
949
|
+
repeatedErrors = 0;
|
|
950
|
+
}
|
|
951
|
+
}), finalize(() => {
|
|
685
952
|
varChangesSub.unsubscribe();
|
|
686
953
|
this._observingChanges = false;
|
|
687
954
|
}));
|
|
688
|
-
}).pipe(
|
|
689
|
-
// share()
|
|
690
|
-
shareReplay({ bufferSize: 1, refCount: true }));
|
|
955
|
+
}).pipe(shareReplay({ bufferSize: 1, refCount: true }));
|
|
691
956
|
this.loading$ = this._observingChangesSubject.pipe(switchMap((observingChanges) => {
|
|
692
957
|
if (!observingChanges) {
|
|
693
958
|
return of(false);
|
|
694
959
|
}
|
|
695
|
-
return this._valueChanges.pipe(map((result) => result.loading), startWith(this._queryRef.getCurrentResult().loading),
|
|
696
|
-
|
|
960
|
+
return this._valueChanges.pipe(map((result) => result.loading), startWith(this._queryRef.getCurrentResult().loading), tap((loading) => {
|
|
961
|
+
if (!loading) {
|
|
962
|
+
this._manualPendingSubject.next(false);
|
|
963
|
+
}
|
|
964
|
+
}),
|
|
965
|
+
// Swap the actual loading flag for the manual pending flag so that
|
|
966
|
+
// we can hold the loading state open when needed.
|
|
967
|
+
switchMap(() => this._manualPendingSubject), auditTime(0), shareReplay({ bufferSize: 1, refCount: true }));
|
|
968
|
+
}),
|
|
969
|
+
// Keep loading=true until we've confirmed the first full load cycle
|
|
970
|
+
// (including any disablePaging requery).
|
|
971
|
+
switchMap((v) => hasEmittedSubject.pipe(map((hasEmitted) => (hasEmitted ? v : true)))));
|
|
697
972
|
}
|
|
698
973
|
rows(mapper) {
|
|
699
974
|
return this._rowsObservable(mapper);
|
|
700
975
|
}
|
|
701
976
|
_rowsObservable(mapper) {
|
|
702
977
|
return new Observable((subscriber) => {
|
|
703
|
-
|
|
704
|
-
// console.log('obs _rowsObservable')
|
|
705
|
-
let rowsBuffer = [];
|
|
706
|
-
// const rowsBufferSubject = new ReplaySubject<TRow[]>()
|
|
707
|
-
const rowsBufferSubject = new Subject();
|
|
978
|
+
const rowsBufferSubject = new BehaviorSubject([]);
|
|
708
979
|
const querySub = this._valueChanges
|
|
709
|
-
.pipe(switchMap((result) => {
|
|
980
|
+
.pipe(filter((result) => !isNetworkRequestInFlight(result.networkStatus)), switchMap((result) => {
|
|
710
981
|
if (result.data === undefined) {
|
|
711
|
-
return of(
|
|
982
|
+
return of();
|
|
712
983
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
984
|
+
// Capture the skip offset *before* any variable changes that the
|
|
985
|
+
// mapper might trigger (e.g. patchVariables for disablePaging).
|
|
986
|
+
const querySkip = this._getVariablesFromQueryRef().skip;
|
|
987
|
+
const _result = this._handleResult(result);
|
|
988
|
+
return this._resolveRowMapper(mapper(_result.data)).pipe(tap((mapperResult) => {
|
|
989
|
+
const needsToRequery = this._needsToRequeryWithAllRecords(mapperResult);
|
|
990
|
+
if (needsToRequery) {
|
|
716
991
|
this.patchVariables({ take: mapperResult.totalCount });
|
|
717
992
|
}
|
|
993
|
+
this._needToRequerySubject.next(needsToRequery);
|
|
718
994
|
if (hasProperty(mapperResult, 'totalCount')) {
|
|
719
995
|
this._totalCount = mapperResult.totalCount;
|
|
720
996
|
}
|
|
721
|
-
|
|
722
|
-
let rows = rowsBuffer || [];
|
|
997
|
+
let rows = rowsBufferSubject.value || [];
|
|
723
998
|
const hasTotalCount = mapperResult.totalCount !== undefined &&
|
|
724
999
|
mapperResult.totalCount !== null;
|
|
725
|
-
// If the rows buffer is not the same size as
|
|
726
|
-
//
|
|
727
|
-
//
|
|
728
|
-
// TODO: Find out if this is resetting the buffer too eagerly.
|
|
729
|
-
// ApolloClient may have a better solution.
|
|
1000
|
+
// If the rows buffer is not the same size as totalCount, create
|
|
1001
|
+
// a fresh buffer (sparse array — unfilled slots are undefined,
|
|
1002
|
+
// which is what checkRecordsHaveValue expects).
|
|
730
1003
|
if (hasTotalCount) {
|
|
731
1004
|
if (mapperResult.totalCount !== rows.length) {
|
|
732
1005
|
rows = new Array(mapperResult.totalCount || 0);
|
|
733
1006
|
}
|
|
734
|
-
let startIndex =
|
|
1007
|
+
let startIndex = querySkip ?? 0;
|
|
735
1008
|
if (this.getQueryProcessingConfig()?.disablePaging) {
|
|
736
1009
|
startIndex = 0;
|
|
737
1010
|
}
|
|
738
|
-
// Insert rows into buffer
|
|
739
|
-
|
|
1011
|
+
// Insert rows into the correct buffer positions.
|
|
1012
|
+
for (let i = 0; i < mapperResult.rows.length; i++) {
|
|
1013
|
+
rows[startIndex + i] = mapperResult.rows[i];
|
|
1014
|
+
}
|
|
740
1015
|
rows = [...rows];
|
|
741
1016
|
}
|
|
742
1017
|
else {
|
|
743
1018
|
rows = [...mapperResult.rows];
|
|
744
1019
|
}
|
|
745
|
-
|
|
1020
|
+
// ngx-datatable does row lookups in a WeakMap and my assumption
|
|
1021
|
+
// is that the pre-allocated empty objects seem to be getting
|
|
1022
|
+
// recognized as the same object, so to avoid that, we add a
|
|
1023
|
+
// unique property to each row.
|
|
1024
|
+
rows = rows.map((v, i) => ({
|
|
1025
|
+
...v,
|
|
1026
|
+
__dt_id: `row-${i}`,
|
|
1027
|
+
}));
|
|
746
1028
|
rowsBufferSubject.next(rows);
|
|
747
1029
|
}));
|
|
748
1030
|
}))
|
|
749
1031
|
.subscribe();
|
|
750
1032
|
const rowsSub = rowsBufferSubject.subscribe(subscriber);
|
|
751
1033
|
return () => {
|
|
752
|
-
// console.log('unsub')
|
|
753
1034
|
querySub.unsubscribe();
|
|
754
1035
|
rowsSub.unsubscribe();
|
|
755
1036
|
rowsBufferSubject.next([]);
|
|
@@ -757,6 +1038,28 @@ class DatatableGraphQLQueryRef {
|
|
|
757
1038
|
};
|
|
758
1039
|
});
|
|
759
1040
|
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Reads the result and returns it unchanged if no errors.
|
|
1043
|
+
*
|
|
1044
|
+
* On errors: emits them via `error$` and replaces any `null` data properties
|
|
1045
|
+
* with an empty collection shape `{ items: [], totalCount: 0 }` so mapper
|
|
1046
|
+
* functions can run safely without special-casing the error path.
|
|
1047
|
+
*/
|
|
1048
|
+
_handleResult(result) {
|
|
1049
|
+
if (!result.errors || result.errors.length === 0)
|
|
1050
|
+
return result;
|
|
1051
|
+
this._emitError(result.errors);
|
|
1052
|
+
const defaultDataPropValue = { items: [], totalCount: 0 };
|
|
1053
|
+
const data = Object.keys(result.data).reduce((acc, key) => {
|
|
1054
|
+
;
|
|
1055
|
+
acc[key] =
|
|
1056
|
+
result.data[key] === null
|
|
1057
|
+
? defaultDataPropValue
|
|
1058
|
+
: result.data[key];
|
|
1059
|
+
return acc;
|
|
1060
|
+
}, {});
|
|
1061
|
+
return { ...result, data };
|
|
1062
|
+
}
|
|
760
1063
|
_needsToRequeryWithAllRecords(data) {
|
|
761
1064
|
if (!this.getQueryProcessingConfig()?.disablePaging) {
|
|
762
1065
|
return false;
|
|
@@ -774,9 +1077,6 @@ class DatatableGraphQLQueryRef {
|
|
|
774
1077
|
return from(Promise.resolve(mapperReturn));
|
|
775
1078
|
}
|
|
776
1079
|
getVariables() {
|
|
777
|
-
// The types aren't accurate or Apollo has a bug, so I had to use `any`.
|
|
778
|
-
// return (this._queryRef as any).obsQuery.options.variables || {}
|
|
779
|
-
// TODO: Look into debouncing our variable setting, while still depending on Apollo for managing them.
|
|
780
1080
|
return this._variablesSubject.value;
|
|
781
1081
|
}
|
|
782
1082
|
_setVariablesImmediate(variables) {
|
|
@@ -807,18 +1107,20 @@ class DatatableGraphQLQueryRef {
|
|
|
807
1107
|
this._setVariablesImmediate(_variables);
|
|
808
1108
|
}
|
|
809
1109
|
}
|
|
810
|
-
refetch(variables) {
|
|
1110
|
+
refetch(variables, showLoading = false) {
|
|
811
1111
|
const _vars = this._withVariableOverrides(variables);
|
|
1112
|
+
if (showLoading) {
|
|
1113
|
+
this._manualPendingSubject.next(true);
|
|
1114
|
+
}
|
|
1115
|
+
// NOTE: There seems to be a bug causing Apollo to not emit changes unless
|
|
1116
|
+
// getCurrentResult() is called. This setTimeout is a workaround.
|
|
1117
|
+
setTimeout(() => this._queryRef.getCurrentResult());
|
|
812
1118
|
return this._queryRef.refetch(_vars);
|
|
813
1119
|
}
|
|
814
1120
|
setQuery(query, triggerRefetch = false) {
|
|
815
1121
|
this._queryRef.setOptions({ query });
|
|
816
1122
|
if (triggerRefetch) {
|
|
817
|
-
//
|
|
818
|
-
// refetch directly, because I want it to share the update triggered
|
|
819
|
-
// polling delay with the setting of variables.
|
|
820
|
-
// this._queryRef.refetch(this.getVariables())
|
|
821
|
-
// Fake variables update to trigger a refetch that shares it's refetch delay.
|
|
1123
|
+
// Fake a variables update to share the debounce delay with setVariables.
|
|
822
1124
|
this.setVariables(this.getVariables());
|
|
823
1125
|
}
|
|
824
1126
|
}
|
|
@@ -828,6 +1130,15 @@ class DatatableGraphQLQueryRef {
|
|
|
828
1130
|
getQueryProcessingConfig() {
|
|
829
1131
|
return this.getOptions()?.context?.queryProcessingConfig;
|
|
830
1132
|
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Returns the variables that were actually sent with the last request, read
|
|
1135
|
+
* directly from Apollo's observable query. This is more accurate than
|
|
1136
|
+
* `_variablesSubject.value` when variables can change between the debounce
|
|
1137
|
+
* firing and the response arriving.
|
|
1138
|
+
*/
|
|
1139
|
+
_getVariablesFromQueryRef() {
|
|
1140
|
+
return this._queryRef.obsQuery.variables;
|
|
1141
|
+
}
|
|
831
1142
|
_withVariableOverrides(variables) {
|
|
832
1143
|
if (!notNullOrUndefined(variables) &&
|
|
833
1144
|
!this.getQueryProcessingConfig()?.disablePaging) {
|
|
@@ -839,6 +1150,12 @@ class DatatableGraphQLQueryRef {
|
|
|
839
1150
|
}
|
|
840
1151
|
return _vars;
|
|
841
1152
|
}
|
|
1153
|
+
_emitError(error) {
|
|
1154
|
+
this._errorSubject.next(error);
|
|
1155
|
+
if (this._defaultErrorHandler && !this._errorSubject.observed) {
|
|
1156
|
+
this._defaultErrorHandler(error);
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
842
1159
|
}
|
|
843
1160
|
|
|
844
1161
|
const DATATABLE_GRAPHQL_SERVICE_CONFIG = new InjectionToken('DATATABLE_GRAPHQL_SERVICE_CONFIG');
|
|
@@ -874,10 +1191,10 @@ class DatatableGraphqlService {
|
|
|
874
1191
|
const queryRef = this._apollo.watchQuery(_options);
|
|
875
1192
|
return new DatatableGraphQLQueryRef(queryRef);
|
|
876
1193
|
}
|
|
877
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.
|
|
878
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.
|
|
1194
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DatatableGraphqlService, deps: [{ token: i1.Apollo }, { token: DATATABLE_GRAPHQL_SERVICE_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1195
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DatatableGraphqlService, providedIn: 'root' });
|
|
879
1196
|
}
|
|
880
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.
|
|
1197
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DatatableGraphqlService, decorators: [{
|
|
881
1198
|
type: Injectable,
|
|
882
1199
|
args: [{
|
|
883
1200
|
providedIn: 'root',
|
|
@@ -1176,68 +1493,6 @@ const mapSearchTextColumnsDataFilterStateToGql = (filterState, context) => {
|
|
|
1176
1493
|
return filter;
|
|
1177
1494
|
};
|
|
1178
1495
|
|
|
1179
|
-
const inlineVariableTransformer = (operation, hintsToken) => {
|
|
1180
|
-
const query = operation.query;
|
|
1181
|
-
const variables = operation.variables;
|
|
1182
|
-
return {
|
|
1183
|
-
query,
|
|
1184
|
-
variables,
|
|
1185
|
-
};
|
|
1186
|
-
};
|
|
1187
|
-
|
|
1188
|
-
const removeNotDefinedTransformer = (operation, hintsToken) => {
|
|
1189
|
-
const query = operation.query;
|
|
1190
|
-
const variables = operation.variables;
|
|
1191
|
-
return {
|
|
1192
|
-
query,
|
|
1193
|
-
variables,
|
|
1194
|
-
};
|
|
1195
|
-
};
|
|
1196
|
-
|
|
1197
|
-
/**
|
|
1198
|
-
* Maps variable value to gql and inlines it into the query.
|
|
1199
|
-
*/
|
|
1200
|
-
const inlineVariableHintDef = {
|
|
1201
|
-
name: 'inline-variable',
|
|
1202
|
-
appliesTo: [HintsKind.Variable, HintsKind.VariableDefinition],
|
|
1203
|
-
transformer: inlineVariableTransformer,
|
|
1204
|
-
};
|
|
1205
|
-
|
|
1206
|
-
/**
|
|
1207
|
-
* Remove the variable from the query if it is not defined in the operation
|
|
1208
|
-
* variables.
|
|
1209
|
-
*/
|
|
1210
|
-
const removeNotDefinedHintDef = {
|
|
1211
|
-
name: 'remove-not-defined',
|
|
1212
|
-
appliesTo: [HintsKind.OperationDefinition],
|
|
1213
|
-
transformer: removeNotDefinedTransformer,
|
|
1214
|
-
};
|
|
1215
|
-
|
|
1216
|
-
// /**
|
|
1217
|
-
// * Remove the variable from the query if it is not defined in the operation
|
|
1218
|
-
// * variables.
|
|
1219
|
-
// *
|
|
1220
|
-
// * Applies to: OperationDefinition
|
|
1221
|
-
// */
|
|
1222
|
-
// export const GQL_HINT_REMOVE_NOT_DEFINED = 'remove-not-defined'
|
|
1223
|
-
// /**
|
|
1224
|
-
// * Maps variable value to gql and inlines it into the query.
|
|
1225
|
-
// *
|
|
1226
|
-
// * Applies to: Variable, VariableDefinition
|
|
1227
|
-
// */
|
|
1228
|
-
// export const GQL_HINT_INLINE_VARIABLE = 'inline-variable'
|
|
1229
|
-
// /**
|
|
1230
|
-
// * Remove variable definition from the query if it is not used by the operation.
|
|
1231
|
-
// *
|
|
1232
|
-
// * Applies to: VariableDefinition
|
|
1233
|
-
// */
|
|
1234
|
-
// // export const GQL_HINT_REMOVE_IF_NOT_USED = 'remove-if-not-used'
|
|
1235
|
-
// export const GQL_HINTS = [
|
|
1236
|
-
// // GQL_HINT_REMOVE_IF_NOT_USED,
|
|
1237
|
-
// GQL_HINT_REMOVE_NOT_DEFINED,
|
|
1238
|
-
// GQL_HINT_INLINE_VARIABLE
|
|
1239
|
-
// ]
|
|
1240
|
-
|
|
1241
1496
|
const baseSchemaFragment = gql `
|
|
1242
1497
|
input ComparableInt32OperationFilterInput {
|
|
1243
1498
|
eq: Int
|
|
@@ -1347,65 +1602,150 @@ const baseSchemaFragment = gql `
|
|
|
1347
1602
|
}
|
|
1348
1603
|
`;
|
|
1349
1604
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1605
|
+
/**
|
|
1606
|
+
* The set of leaf-level operator keys used by HotChocolate filter inputs
|
|
1607
|
+
* (e.g. StringOperationFilterInput, ComparableInt32OperationFilterInput).
|
|
1608
|
+
* Any key not in this set is treated as a field name, causing the evaluator
|
|
1609
|
+
* to recurse into `value[key]`.
|
|
1610
|
+
*/
|
|
1611
|
+
const OPERATOR_KEYS = new Set([
|
|
1612
|
+
'eq',
|
|
1613
|
+
'neq',
|
|
1614
|
+
'gt',
|
|
1615
|
+
'gte',
|
|
1616
|
+
'lt',
|
|
1617
|
+
'lte',
|
|
1618
|
+
'ngt',
|
|
1619
|
+
'ngte',
|
|
1620
|
+
'nlt',
|
|
1621
|
+
'nlte',
|
|
1622
|
+
'in',
|
|
1623
|
+
'nin',
|
|
1624
|
+
'contains',
|
|
1625
|
+
'ncontains',
|
|
1626
|
+
'startsWith',
|
|
1627
|
+
'nstartsWith',
|
|
1628
|
+
'endsWith',
|
|
1629
|
+
'nendsWith',
|
|
1630
|
+
'objectContains',
|
|
1631
|
+
]);
|
|
1632
|
+
function applyOperator(operator, value, operand) {
|
|
1633
|
+
switch (operator) {
|
|
1634
|
+
case 'eq':
|
|
1635
|
+
return value === operand;
|
|
1636
|
+
case 'neq':
|
|
1637
|
+
return value !== operand;
|
|
1638
|
+
case 'gt':
|
|
1639
|
+
return value > operand;
|
|
1640
|
+
case 'gte':
|
|
1641
|
+
return value >= operand;
|
|
1642
|
+
case 'lt':
|
|
1643
|
+
return value < operand;
|
|
1644
|
+
case 'lte':
|
|
1645
|
+
return value <= operand;
|
|
1646
|
+
// Negated comparisons — logical complements of the above
|
|
1647
|
+
case 'ngt':
|
|
1648
|
+
return !(value > operand); // equivalent to lte
|
|
1649
|
+
case 'ngte':
|
|
1650
|
+
return !(value >= operand); // equivalent to lt
|
|
1651
|
+
case 'nlt':
|
|
1652
|
+
return !(value < operand); // equivalent to gte
|
|
1653
|
+
case 'nlte':
|
|
1654
|
+
return !(value <= operand); // equivalent to gt
|
|
1655
|
+
case 'in':
|
|
1656
|
+
return Array.isArray(operand) && operand.includes(value);
|
|
1657
|
+
case 'nin':
|
|
1658
|
+
return Array.isArray(operand) && !operand.includes(value);
|
|
1659
|
+
case 'contains':
|
|
1660
|
+
return (typeof value === 'string' &&
|
|
1661
|
+
typeof operand === 'string' &&
|
|
1662
|
+
value.includes(operand));
|
|
1663
|
+
case 'ncontains':
|
|
1664
|
+
return (typeof value === 'string' &&
|
|
1665
|
+
typeof operand === 'string' &&
|
|
1666
|
+
!value.includes(operand));
|
|
1667
|
+
case 'startsWith':
|
|
1668
|
+
return (typeof value === 'string' &&
|
|
1669
|
+
typeof operand === 'string' &&
|
|
1670
|
+
value.startsWith(operand));
|
|
1671
|
+
case 'nstartsWith':
|
|
1672
|
+
return (typeof value === 'string' &&
|
|
1673
|
+
typeof operand === 'string' &&
|
|
1674
|
+
!value.startsWith(operand));
|
|
1675
|
+
case 'endsWith':
|
|
1676
|
+
return (typeof value === 'string' &&
|
|
1677
|
+
typeof operand === 'string' &&
|
|
1678
|
+
value.endsWith(operand));
|
|
1679
|
+
case 'nendsWith':
|
|
1680
|
+
return (typeof value === 'string' &&
|
|
1681
|
+
typeof operand === 'string' &&
|
|
1682
|
+
!value.endsWith(operand));
|
|
1683
|
+
case 'objectContains': {
|
|
1684
|
+
// Custom Seam/HotChocolate operator: convert the field value to a string
|
|
1685
|
+
// and check if it contains the operand (case-insensitive, matching search UX).
|
|
1686
|
+
const strValue = value == null ? '' : String(value);
|
|
1687
|
+
const strOperand = operand == null ? '' : String(operand);
|
|
1688
|
+
return strValue.toLowerCase().includes(strOperand.toLowerCase());
|
|
1689
|
+
}
|
|
1690
|
+
default:
|
|
1691
|
+
throw new Error(`Unknown filter operator: "${operator}"`);
|
|
1357
1692
|
}
|
|
1358
|
-
return f;
|
|
1359
1693
|
}
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1694
|
+
/**
|
|
1695
|
+
* Recursively evaluates a HotChocolate-style filter against a value.
|
|
1696
|
+
*
|
|
1697
|
+
* Rules:
|
|
1698
|
+
* - `and` array: all sub-filters must pass (AND logic)
|
|
1699
|
+
* - `or` array: at least one sub-filter must pass (OR logic)
|
|
1700
|
+
* - Known operator keys (`eq`, `contains`, `gt`, ...): apply the operator to
|
|
1701
|
+
* the current `value`
|
|
1702
|
+
* - Any other key: treat as a field name; recurse with `value[key]` and the
|
|
1703
|
+
* nested filter
|
|
1704
|
+
* - All non-`and`/`or` conditions are implicitly ANDed together
|
|
1705
|
+
*/
|
|
1706
|
+
function evaluateCondition(value, filter) {
|
|
1707
|
+
if (filter === null || filter === undefined) {
|
|
1708
|
+
return true;
|
|
1709
|
+
}
|
|
1710
|
+
if (Array.isArray(filter.and)) {
|
|
1711
|
+
if (!filter.and.every((f) => evaluateCondition(value, f))) {
|
|
1712
|
+
return false;
|
|
1372
1713
|
}
|
|
1373
1714
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1715
|
+
if (Array.isArray(filter.or)) {
|
|
1716
|
+
if (!filter.or.some((f) => evaluateCondition(value, f))) {
|
|
1717
|
+
return false;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
for (const key of Object.keys(filter)) {
|
|
1721
|
+
if (key === 'and' || key === 'or') {
|
|
1722
|
+
continue;
|
|
1723
|
+
}
|
|
1724
|
+
if (OPERATOR_KEYS.has(key)) {
|
|
1725
|
+
if (!applyOperator(key, value, filter[key])) {
|
|
1726
|
+
return false;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
else {
|
|
1730
|
+
if (!evaluateCondition(value?.[key], filter[key])) {
|
|
1731
|
+
return false;
|
|
1387
1732
|
}
|
|
1388
|
-
conditions.push(c.condition(_createWhereFieldFn(`${k}`), c.value));
|
|
1389
1733
|
}
|
|
1390
1734
|
}
|
|
1391
|
-
return
|
|
1735
|
+
return true;
|
|
1392
1736
|
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Filters an array using a HotChocolate-style where clause.
|
|
1739
|
+
*
|
|
1740
|
+
* Top-level field conditions are implicitly ANDed. Use `and`/`or` arrays for
|
|
1741
|
+
* explicit logical grouping.
|
|
1742
|
+
*
|
|
1743
|
+
* @example
|
|
1744
|
+
* filterWhere(records, { name: { contains: 'foo' }, id: { gt: 5 } })
|
|
1745
|
+
* filterWhere(records, { or: [{ name: { eq: 'a' } }, { name: { eq: 'b' } }] })
|
|
1746
|
+
*/
|
|
1393
1747
|
function filterWhere(data, where) {
|
|
1394
|
-
|
|
1395
|
-
const filteredClaims = data.filter((c) => {
|
|
1396
|
-
// const idx = items.indexOf(itm => itm(c))
|
|
1397
|
-
// return idx !== -1
|
|
1398
|
-
let found = false;
|
|
1399
|
-
for (const itm of items) {
|
|
1400
|
-
const b = itm(c);
|
|
1401
|
-
if (b) {
|
|
1402
|
-
found = b;
|
|
1403
|
-
break;
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
return found;
|
|
1407
|
-
});
|
|
1408
|
-
return filteredClaims;
|
|
1748
|
+
return data.filter((item) => evaluateCondition(item, where));
|
|
1409
1749
|
}
|
|
1410
1750
|
|
|
1411
1751
|
function skipAndTake(data, skip, take) {
|
|
@@ -1416,18 +1756,76 @@ function skipAndTake(data, skip, take) {
|
|
|
1416
1756
|
// throw Error(`Invalid 'take' value: ${take}`)
|
|
1417
1757
|
// }
|
|
1418
1758
|
const tmp = [];
|
|
1419
|
-
for (let i = skip; i < take + skip && i < data.length
|
|
1759
|
+
for (let i = skip; i < take + skip && i < data.length; i++) {
|
|
1420
1760
|
tmp.push(data[i]);
|
|
1421
1761
|
}
|
|
1422
1762
|
return tmp;
|
|
1423
1763
|
}
|
|
1424
1764
|
|
|
1765
|
+
/**
|
|
1766
|
+
* Sorts an array by a HotChocolate-style `order` argument.
|
|
1767
|
+
*
|
|
1768
|
+
* Earlier entries in the array take higher sort precedence. When two records
|
|
1769
|
+
* are equal on all sort clauses they retain their original relative order
|
|
1770
|
+
* (stable sort).
|
|
1771
|
+
*
|
|
1772
|
+
* Handles strings (locale-aware), numbers/dates, booleans, and null/undefined
|
|
1773
|
+
* values (nulls sort last for both ASC and DESC).
|
|
1774
|
+
*
|
|
1775
|
+
* @example
|
|
1776
|
+
* sortItems(records, [{ name: 'ASC' }, { id: 'DESC' }])
|
|
1777
|
+
*/
|
|
1778
|
+
function sortItems(items, order) {
|
|
1779
|
+
if (!order || order.length === 0) {
|
|
1780
|
+
return items;
|
|
1781
|
+
}
|
|
1782
|
+
return [...items].sort((a, b) => {
|
|
1783
|
+
for (const clause of order) {
|
|
1784
|
+
const entries = Object.entries(clause);
|
|
1785
|
+
if (entries.length === 0) {
|
|
1786
|
+
continue;
|
|
1787
|
+
}
|
|
1788
|
+
const [field, direction] = entries[0];
|
|
1789
|
+
const aVal = a[field];
|
|
1790
|
+
const bVal = b[field];
|
|
1791
|
+
// Nulls always sort last regardless of direction — check before inverting.
|
|
1792
|
+
const aNull = aVal == null;
|
|
1793
|
+
const bNull = bVal == null;
|
|
1794
|
+
if (aNull && bNull)
|
|
1795
|
+
continue;
|
|
1796
|
+
if (aNull)
|
|
1797
|
+
return 1;
|
|
1798
|
+
if (bNull)
|
|
1799
|
+
return -1;
|
|
1800
|
+
const comparison = _compareNonNullValues(aVal, bVal);
|
|
1801
|
+
if (comparison !== 0) {
|
|
1802
|
+
return direction === 'DESC' ? -comparison : comparison;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
return 0;
|
|
1806
|
+
});
|
|
1807
|
+
}
|
|
1808
|
+
function _compareNonNullValues(a, b) {
|
|
1809
|
+
if (typeof a === 'string' && typeof b === 'string') {
|
|
1810
|
+
return a.localeCompare(b);
|
|
1811
|
+
}
|
|
1812
|
+
if (a < b)
|
|
1813
|
+
return -1;
|
|
1814
|
+
if (a > b)
|
|
1815
|
+
return 1;
|
|
1816
|
+
return 0;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1425
1819
|
function filteredResults(items, args) {
|
|
1426
1820
|
let _items = items;
|
|
1427
1821
|
const where = args?.where;
|
|
1428
1822
|
if (where !== undefined) {
|
|
1429
1823
|
_items = filterWhere(_items, where);
|
|
1430
1824
|
}
|
|
1825
|
+
const order = args?.order;
|
|
1826
|
+
if (order !== undefined) {
|
|
1827
|
+
_items = sortItems(_items, order);
|
|
1828
|
+
}
|
|
1431
1829
|
const totalCount = _items.length;
|
|
1432
1830
|
const skip = args?.skip ?? 0;
|
|
1433
1831
|
const take = args?.take ?? _items.length;
|
|
@@ -1454,6 +1852,8 @@ class MockDatatable {
|
|
|
1454
1852
|
_bodyHeight = 500;
|
|
1455
1853
|
_scrolledPosV = 0;
|
|
1456
1854
|
page = new EventEmitter();
|
|
1855
|
+
resize = new EventEmitter();
|
|
1856
|
+
externalSorting = false;
|
|
1457
1857
|
sort = new EventEmitter();
|
|
1458
1858
|
get sorts() {
|
|
1459
1859
|
return this._sorts;
|
|
@@ -1461,6 +1861,9 @@ class MockDatatable {
|
|
|
1461
1861
|
set sorts(value) {
|
|
1462
1862
|
this._sorts = value;
|
|
1463
1863
|
}
|
|
1864
|
+
get pageInfo() {
|
|
1865
|
+
return this.ngxDatatable;
|
|
1866
|
+
}
|
|
1464
1867
|
filterStates = this._filterStatesSubject.asObservable();
|
|
1465
1868
|
static pageDefaults(dt, defaultPageSize = 20) {
|
|
1466
1869
|
return {
|
|
@@ -1573,7 +1976,10 @@ function checkRecordsHaveValue(arr, indices, testValueCheckProp = 'name', onlyCh
|
|
|
1573
1976
|
}
|
|
1574
1977
|
else {
|
|
1575
1978
|
if (!onlyCheckProvidedIndices) {
|
|
1576
|
-
|
|
1979
|
+
const isItemDefined = notNullOrUndefined(testValueCheckProp)
|
|
1980
|
+
? notNullOrUndefined(item?.[testValueCheckProp])
|
|
1981
|
+
: notNullOrUndefined(item);
|
|
1982
|
+
if (isItemDefined) {
|
|
1577
1983
|
throw Error(`Record at index '${i}' should not be defined.`);
|
|
1578
1984
|
}
|
|
1579
1985
|
}
|
|
@@ -1603,133 +2009,61 @@ function graphQLLink(options) {
|
|
|
1603
2009
|
return new ApolloLink((operation, forward) => {
|
|
1604
2010
|
return new Observable$1((subscriber) => {
|
|
1605
2011
|
// console.log('graphQLLink', operation.variables)
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
2012
|
+
const execute = () => {
|
|
2013
|
+
const response = graphqlSync({
|
|
2014
|
+
schema: options.schema,
|
|
2015
|
+
source: print(operation.query),
|
|
2016
|
+
rootValue: options.rootValue,
|
|
2017
|
+
contextValue: operation.getContext(),
|
|
2018
|
+
variableValues: operation.variables,
|
|
2019
|
+
operationName: operation.operationName,
|
|
2020
|
+
// fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
|
|
2021
|
+
// typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
|
|
2022
|
+
});
|
|
2023
|
+
// console.log('graphQLLink response', response)
|
|
2024
|
+
operation.setContext({ response });
|
|
2025
|
+
subscriber.next(response);
|
|
2026
|
+
subscriber.complete();
|
|
2027
|
+
};
|
|
2028
|
+
if (options.delay && options.delay > 0) {
|
|
2029
|
+
const timeoutId = setTimeout(execute, options.delay);
|
|
2030
|
+
return () => clearTimeout(timeoutId);
|
|
2031
|
+
}
|
|
2032
|
+
execute();
|
|
1620
2033
|
return () => { };
|
|
1621
2034
|
});
|
|
1622
2035
|
});
|
|
1623
2036
|
}
|
|
1624
2037
|
|
|
1625
|
-
const queryProcessingLink = new ApolloLink((operation, forward) => {
|
|
1626
|
-
// console.log('~link operation', operation)
|
|
1627
|
-
const context = operation.getContext();
|
|
1628
|
-
const queryProcessingConfig = context.queryProcessingConfig || {};
|
|
1629
|
-
// console.log(operation.query)
|
|
1630
|
-
// const rules = parseHints(operation.query)
|
|
1631
|
-
let _ast = parseAst(operation.query);
|
|
1632
|
-
const rules = parseHints(_ast);
|
|
1633
|
-
// console.log('rules', rules)
|
|
1634
|
-
operation.query = _ast;
|
|
1635
|
-
const removeNotDefined = hintsTokensContainingHint(rules, removeNotDefinedHintDef.name);
|
|
1636
|
-
// console.log('removeNotDefined', removeNotDefined)
|
|
1637
|
-
for (const rulesToken of removeNotDefined) {
|
|
1638
|
-
// _ast = removeVariableDefinitionsNotDefined(_ast, rulesToken.node as OperationDefinitionNode, operation.variables)
|
|
1639
|
-
if (!removeNotDefinedHintDef.transformer) {
|
|
1640
|
-
continue;
|
|
1641
|
-
}
|
|
1642
|
-
const result = removeNotDefinedHintDef.transformer({
|
|
1643
|
-
query: operation.query,
|
|
1644
|
-
variables: operation.variables,
|
|
1645
|
-
}, rulesToken);
|
|
1646
|
-
operation.query = result.query;
|
|
1647
|
-
operation.variables = result.variables;
|
|
1648
|
-
}
|
|
1649
|
-
const inlineVariableRulesTokens = hintsTokensContainingHint(rules, inlineVariableHintDef.name);
|
|
1650
|
-
// console.log('inlineVariableRulesTokens', inlineVariableRulesTokens)
|
|
1651
|
-
for (const rulesToken of inlineVariableRulesTokens) {
|
|
1652
|
-
let varName = null;
|
|
1653
|
-
let varDefaultValue;
|
|
1654
|
-
if (rulesToken.kind === HintsKind.VariableDefinition) {
|
|
1655
|
-
varName = rulesToken.node.variable.name.value;
|
|
1656
|
-
varDefaultValue = rulesToken.node.defaultValue;
|
|
1657
|
-
}
|
|
1658
|
-
else if (rulesToken.kind === HintsKind.Variable) {
|
|
1659
|
-
varName = rulesToken.node.name.value;
|
|
1660
|
-
}
|
|
1661
|
-
if (varName === null) {
|
|
1662
|
-
// TODO: Throw error here?
|
|
1663
|
-
continue;
|
|
1664
|
-
}
|
|
1665
|
-
const varValue = operation.variables[varName];
|
|
1666
|
-
operation.variables = withoutProperty(operation.variables, varName);
|
|
1667
|
-
_ast = removeVariableDefinition(_ast, varName);
|
|
1668
|
-
const varValueNode = (_ast = inlineVariable(_ast, varName, parseValue(toGQL(varValue))));
|
|
1669
|
-
}
|
|
1670
|
-
// const removeIfNotDefined = hintsTokensContainingHint(rules, GQL_HINT_REMOVE_IF_NOT_USED)
|
|
1671
|
-
// console.log('removeIfNotDefined', removeIfNotDefined)
|
|
1672
|
-
// const _operation = operation
|
|
1673
|
-
// const removeIdNotDefined = (queryProcessingConfig?.variables?.removeIfNotDefined || [])
|
|
1674
|
-
// for (const varName of removeIdNotDefined) {
|
|
1675
|
-
// if (isNullOrUndefined(operation.variables[varName])) {
|
|
1676
|
-
// _operation.query = removeVariable(_operation.query, varName)
|
|
1677
|
-
// }
|
|
1678
|
-
// }
|
|
1679
|
-
// const removeIfNotUsed = (queryProcessingConfig?.variables?.removeIfNotUsed || [])
|
|
1680
|
-
// for (const varName of removeIfNotUsed) {
|
|
1681
|
-
// if (!containsVariable(_operation.query, varName)) {
|
|
1682
|
-
// _operation.query = removeVariable(_operation.query, varName)
|
|
1683
|
-
// }
|
|
1684
|
-
// }
|
|
1685
|
-
// const inlineVariables = (queryProcessingConfig?.variables?.inline || [])
|
|
1686
|
-
// for (const varName of inlineVariables) {
|
|
1687
|
-
// const varValue = _operation.variables[varName]
|
|
1688
|
-
// _operation.variables = withoutProperty(_operation.variables, varName)
|
|
1689
|
-
// _operation.query = removeVariableDefinition(_operation.query, varName)
|
|
1690
|
-
// _operation.query = inlineVariable(_operation.query, varName, parseValue(toGQL(varValue)))
|
|
1691
|
-
// }
|
|
1692
|
-
operation.query = _ast;
|
|
1693
|
-
return forward(operation);
|
|
1694
|
-
});
|
|
1695
|
-
|
|
1696
2038
|
/**
|
|
1697
|
-
* Creates
|
|
1698
|
-
*
|
|
1699
|
-
*
|
|
1700
|
-
*
|
|
1701
|
-
* necessary for tests. Since we use a C# GraphQL api, it would be a lot of
|
|
1702
|
-
* overhead to use that implementation in this project's tests, but you can
|
|
1703
|
-
* check our `graphQLLink` to find what features have been implemented to
|
|
1704
|
-
* hopefully match the result our api would return.
|
|
2039
|
+
* Creates Apollo providers configured like our apps, except with the Apollo
|
|
2040
|
+
* HttpLink replaced with a custom GraphQL link that queries from the schema
|
|
2041
|
+
* and root value provided. Responses match what our real API returns, but
|
|
2042
|
+
* query features are limited to what `graphQLLink` implements.
|
|
1705
2043
|
*
|
|
1706
2044
|
* NOTE: This was created because `ApolloTestingModule` is very limited. We
|
|
1707
2045
|
* mostly use queries intended to emit more than once, but `ApolloTestingModule`
|
|
1708
2046
|
* can only emit a query response once.
|
|
1709
2047
|
*/
|
|
1710
|
-
function createApolloTestingProvider(schema, rootValue) {
|
|
1711
|
-
return {
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
errorPolicy: 'all',
|
|
1728
|
-
},
|
|
1729
|
-
},
|
|
1730
|
-
};
|
|
2048
|
+
function createApolloTestingProvider(schema, rootValue, delay) {
|
|
2049
|
+
return provideApollo(() => ({
|
|
2050
|
+
cache: new InMemoryCache(),
|
|
2051
|
+
link: concat(queryProcessingLink, graphQLLink({
|
|
2052
|
+
schema,
|
|
2053
|
+
rootValue,
|
|
2054
|
+
delay,
|
|
2055
|
+
})),
|
|
2056
|
+
defaultOptions: {
|
|
2057
|
+
watchQuery: {
|
|
2058
|
+
fetchPolicy: 'cache-and-network',
|
|
2059
|
+
errorPolicy: 'ignore',
|
|
2060
|
+
},
|
|
2061
|
+
query: {
|
|
2062
|
+
fetchPolicy: 'network-only',
|
|
2063
|
+
errorPolicy: 'all',
|
|
2064
|
+
},
|
|
1731
2065
|
},
|
|
1732
|
-
};
|
|
2066
|
+
}));
|
|
1733
2067
|
}
|
|
1734
2068
|
|
|
1735
2069
|
function createSimpleGqlTestRecord(num) {
|
|
@@ -1756,6 +2090,11 @@ const simpleGqlTestSchema = buildSchema(print(gql `
|
|
|
1756
2090
|
name: StringOperationFilterInput
|
|
1757
2091
|
}
|
|
1758
2092
|
|
|
2093
|
+
input SimpleGqlTestRecordSortInput {
|
|
2094
|
+
id: SortEnumType
|
|
2095
|
+
name: SortEnumType
|
|
2096
|
+
}
|
|
2097
|
+
|
|
1759
2098
|
type SimpleGqlTestRecord {
|
|
1760
2099
|
id: Int
|
|
1761
2100
|
name: String
|
|
@@ -1765,6 +2104,7 @@ const simpleGqlTestSchema = buildSchema(print(gql `
|
|
|
1765
2104
|
simpleGqlTestRecords(
|
|
1766
2105
|
skip: Int
|
|
1767
2106
|
take: Int
|
|
2107
|
+
order: [SimpleGqlTestRecordSortInput!]
|
|
1768
2108
|
where: SimpleGqlTestRecordFilterInput
|
|
1769
2109
|
): SimpleGqlTestRecordCollectionSegment
|
|
1770
2110
|
}
|
|
@@ -1782,9 +2122,42 @@ const SIMPLE_GQL_TEST_QUERY = gql `
|
|
|
1782
2122
|
query ExampleQuery(
|
|
1783
2123
|
$skip: Int
|
|
1784
2124
|
$take: Int
|
|
2125
|
+
$order: [SimpleGqlTestRecordSortInput!]
|
|
2126
|
+
$where: SimpleGqlTestRecordFilterInput
|
|
2127
|
+
) {
|
|
2128
|
+
simpleGqlTestRecords(
|
|
2129
|
+
skip: $skip
|
|
2130
|
+
take: $take
|
|
2131
|
+
order: $order
|
|
2132
|
+
where: $where
|
|
2133
|
+
) {
|
|
2134
|
+
items {
|
|
2135
|
+
id
|
|
2136
|
+
name
|
|
2137
|
+
}
|
|
2138
|
+
totalCount
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
`;
|
|
2142
|
+
/**
|
|
2143
|
+
* Like {@link SIMPLE_GQL_TEST_QUERY} but includes `$search: String` for use
|
|
2144
|
+
* with search filters that inline a `gqlVar('search')` reference via
|
|
2145
|
+
* `queryProcessingConfig.variables.inline`.
|
|
2146
|
+
*/
|
|
2147
|
+
const SIMPLE_GQL_TEST_SEARCH_QUERY = gql `
|
|
2148
|
+
query ExampleQuery(
|
|
2149
|
+
$skip: Int
|
|
2150
|
+
$take: Int
|
|
2151
|
+
$order: [SimpleGqlTestRecordSortInput!]
|
|
1785
2152
|
$where: SimpleGqlTestRecordFilterInput
|
|
2153
|
+
$search: String
|
|
1786
2154
|
) {
|
|
1787
|
-
simpleGqlTestRecords(
|
|
2155
|
+
simpleGqlTestRecords(
|
|
2156
|
+
skip: $skip
|
|
2157
|
+
take: $take
|
|
2158
|
+
order: $order
|
|
2159
|
+
where: $where
|
|
2160
|
+
) {
|
|
1788
2161
|
items {
|
|
1789
2162
|
id
|
|
1790
2163
|
name
|
|
@@ -1798,5 +2171,5 @@ const SIMPLE_GQL_TEST_QUERY = gql `
|
|
|
1798
2171
|
* Generated bundle index. Do not edit.
|
|
1799
2172
|
*/
|
|
1800
2173
|
|
|
1801
|
-
export { DATATABLE_GRAPHQL_SERVICE_CONFIG, DEFAULT_PAGE_SIZE, DEFAULT_TO_REMOVE_ON_UNDEFINED, DatatableGraphQLQueryRef, DatatableGraphqlService, GQLDirection, GQLVariable, HINT_NAMES_CAPTURE_REGEX, HINT_PREFIX_REGEX, HintsKind, MockDatatable, SIMPLE_GQL_TEST_QUERY, baseSchemaFragment, checkRecordsHaveValue, containsVariable, createApolloTestingProvider, createHintsToken, createSimpleGqlTestRecord, createSimpleGqlTestRoot, filterWhere, filteredResults, getHintsToken, getPageInfo, getTokenAppliesTo, gqlVar, hintNamesFromHintToken, hintsTokensContainingHint, inlineVariable, inlineVariableHintDef, inlineVariableTransformer, isCommentToken, isHintToken, isInlineComment, mapFilterStates, mapSearchDateColumnsDataFilterStateToGql, mapSearchNumericColumnsDataFilterStateToGql, mapSearchTextColumnsDataFilterStateToGql, observeRowsWithGqlInputsHandling, parseAst, parseComments, parseHints, removeNotDefinedHintDef, removeNotDefinedTransformer, removeVariable, removeVariableDefinition, removeVariableDefinitionsNotDefined, simpleGqlTestSchema, skipAndTake, toGQL };
|
|
2174
|
+
export { DATATABLE_GRAPHQL_SERVICE_CONFIG, DEFAULT_PAGE_SIZE, DEFAULT_TO_REMOVE_ON_UNDEFINED, DatatableGraphQLQueryRef, DatatableGraphqlService, GQLDirection, GQLVariable, HINT_NAMES_CAPTURE_REGEX, HINT_PREFIX_REGEX, HintsKind, MAX_ERROR_RECOVERY_ATTEMPTS, MockDatatable, SIMPLE_GQL_TEST_QUERY, SIMPLE_GQL_TEST_SEARCH_QUERY, baseSchemaFragment, checkRecordsHaveValue, containsVariable, createApolloTestingProvider, createHintsToken, createSimpleGqlTestRecord, createSimpleGqlTestRoot, createSortsMapper, filterWhere, filteredResults, getHintsToken, getPageInfo, getTokenAppliesTo, gqlVar, hintNamesFromHintToken, hintsTokensContainingHint, inlineVariable, inlineVariableHintDef, inlineVariableTransformer, isCommentToken, isHintToken, isInlineComment, logQueryLink, mapFilterStates, mapSearchDateColumnsDataFilterStateToGql, mapSearchNumericColumnsDataFilterStateToGql, mapSearchTextColumnsDataFilterStateToGql, observeRowsWithGqlInputsHandling, parseAst, parseComments, parseHints, queryProcessingLink, removeNotDefinedHintDef, removeNotDefinedTransformer, removeVariable, removeVariableDefinition, removeVariableDefinitionsNotDefined, simpleGqlTestSchema, skipAndTake, sortItems, toGQL };
|
|
1802
2175
|
//# sourceMappingURL=theseam-ui-common-graphql.mjs.map
|