@slickgrid-universal/graphql 5.14.0 → 9.0.2

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 (122) hide show
  1. package/dist/index.d.ts.map +1 -0
  2. package/dist/index.js.map +1 -0
  3. package/dist/interfaces/graphqlCursorPaginationOption.interface.d.ts.map +1 -0
  4. package/dist/interfaces/graphqlCursorPaginationOption.interface.js.map +1 -0
  5. package/dist/interfaces/graphqlDatasetFilter.interface.d.ts.map +1 -0
  6. package/dist/interfaces/graphqlDatasetFilter.interface.js.map +1 -0
  7. package/dist/interfaces/graphqlFilteringOption.interface.d.ts.map +1 -0
  8. package/dist/interfaces/graphqlFilteringOption.interface.js.map +1 -0
  9. package/dist/interfaces/graphqlPaginatedResult.interface.d.ts.map +1 -0
  10. package/dist/interfaces/graphqlPaginatedResult.interface.js.map +1 -0
  11. package/dist/interfaces/graphqlPaginationOption.interface.d.ts.map +1 -0
  12. package/dist/interfaces/graphqlPaginationOption.interface.js.map +1 -0
  13. package/dist/interfaces/graphqlResult.interface.d.ts.map +1 -0
  14. package/dist/interfaces/graphqlResult.interface.js.map +1 -0
  15. package/dist/interfaces/graphqlServiceApi.interface.d.ts.map +1 -0
  16. package/dist/interfaces/graphqlServiceApi.interface.js.map +1 -0
  17. package/dist/interfaces/graphqlServiceOption.interface.d.ts.map +1 -0
  18. package/dist/interfaces/graphqlServiceOption.interface.js.map +1 -0
  19. package/dist/interfaces/graphqlSortingOption.interface.d.ts.map +1 -0
  20. package/dist/interfaces/graphqlSortingOption.interface.js.map +1 -0
  21. package/dist/interfaces/index.d.ts.map +1 -0
  22. package/dist/{cjs/interfaces → interfaces}/index.js.map +1 -1
  23. package/dist/interfaces/queryArgument.interface.d.ts.map +1 -0
  24. package/dist/interfaces/queryArgument.interface.js.map +1 -0
  25. package/dist/{types/services → services}/graphql.service.d.ts +1 -1
  26. package/dist/services/graphql.service.d.ts.map +1 -0
  27. package/dist/{esm/services → services}/graphql.service.js +8 -8
  28. package/dist/services/graphql.service.js.map +1 -0
  29. package/dist/services/graphqlQueryBuilder.d.ts.map +1 -0
  30. package/dist/services/graphqlQueryBuilder.js.map +1 -0
  31. package/dist/services/index.d.ts.map +1 -0
  32. package/dist/services/index.js.map +1 -0
  33. package/package.json +9 -11
  34. package/src/services/graphql.service.ts +9 -9
  35. package/dist/cjs/index.js +0 -8
  36. package/dist/cjs/index.js.map +0 -1
  37. package/dist/cjs/interfaces/graphqlCursorPaginationOption.interface.js +0 -3
  38. package/dist/cjs/interfaces/graphqlCursorPaginationOption.interface.js.map +0 -1
  39. package/dist/cjs/interfaces/graphqlDatasetFilter.interface.js +0 -3
  40. package/dist/cjs/interfaces/graphqlDatasetFilter.interface.js.map +0 -1
  41. package/dist/cjs/interfaces/graphqlFilteringOption.interface.js +0 -3
  42. package/dist/cjs/interfaces/graphqlFilteringOption.interface.js.map +0 -1
  43. package/dist/cjs/interfaces/graphqlPaginatedResult.interface.js +0 -3
  44. package/dist/cjs/interfaces/graphqlPaginatedResult.interface.js.map +0 -1
  45. package/dist/cjs/interfaces/graphqlPaginationOption.interface.js +0 -3
  46. package/dist/cjs/interfaces/graphqlPaginationOption.interface.js.map +0 -1
  47. package/dist/cjs/interfaces/graphqlResult.interface.js +0 -3
  48. package/dist/cjs/interfaces/graphqlResult.interface.js.map +0 -1
  49. package/dist/cjs/interfaces/graphqlServiceApi.interface.js +0 -3
  50. package/dist/cjs/interfaces/graphqlServiceApi.interface.js.map +0 -1
  51. package/dist/cjs/interfaces/graphqlServiceOption.interface.js +0 -3
  52. package/dist/cjs/interfaces/graphqlServiceOption.interface.js.map +0 -1
  53. package/dist/cjs/interfaces/graphqlSortingOption.interface.js +0 -3
  54. package/dist/cjs/interfaces/graphqlSortingOption.interface.js.map +0 -1
  55. package/dist/cjs/interfaces/index.js +0 -3
  56. package/dist/cjs/interfaces/queryArgument.interface.js +0 -3
  57. package/dist/cjs/interfaces/queryArgument.interface.js.map +0 -1
  58. package/dist/cjs/services/graphql.service.js +0 -674
  59. package/dist/cjs/services/graphql.service.js.map +0 -1
  60. package/dist/cjs/services/graphqlQueryBuilder.js +0 -142
  61. package/dist/cjs/services/graphqlQueryBuilder.js.map +0 -1
  62. package/dist/cjs/services/index.js +0 -21
  63. package/dist/cjs/services/index.js.map +0 -1
  64. package/dist/esm/index.js.map +0 -1
  65. package/dist/esm/interfaces/graphqlCursorPaginationOption.interface.js.map +0 -1
  66. package/dist/esm/interfaces/graphqlDatasetFilter.interface.js.map +0 -1
  67. package/dist/esm/interfaces/graphqlFilteringOption.interface.js.map +0 -1
  68. package/dist/esm/interfaces/graphqlPaginatedResult.interface.js.map +0 -1
  69. package/dist/esm/interfaces/graphqlPaginationOption.interface.js.map +0 -1
  70. package/dist/esm/interfaces/graphqlResult.interface.js.map +0 -1
  71. package/dist/esm/interfaces/graphqlServiceApi.interface.js.map +0 -1
  72. package/dist/esm/interfaces/graphqlServiceOption.interface.js.map +0 -1
  73. package/dist/esm/interfaces/graphqlSortingOption.interface.js.map +0 -1
  74. package/dist/esm/interfaces/index.js.map +0 -1
  75. package/dist/esm/interfaces/queryArgument.interface.js.map +0 -1
  76. package/dist/esm/services/graphql.service.js.map +0 -1
  77. package/dist/esm/services/graphqlQueryBuilder.js.map +0 -1
  78. package/dist/esm/services/index.js.map +0 -1
  79. package/dist/tsconfig.tsbuildinfo +0 -1
  80. package/dist/types/index.d.ts.map +0 -1
  81. package/dist/types/interfaces/graphqlCursorPaginationOption.interface.d.ts.map +0 -1
  82. package/dist/types/interfaces/graphqlDatasetFilter.interface.d.ts.map +0 -1
  83. package/dist/types/interfaces/graphqlFilteringOption.interface.d.ts.map +0 -1
  84. package/dist/types/interfaces/graphqlPaginatedResult.interface.d.ts.map +0 -1
  85. package/dist/types/interfaces/graphqlPaginationOption.interface.d.ts.map +0 -1
  86. package/dist/types/interfaces/graphqlResult.interface.d.ts.map +0 -1
  87. package/dist/types/interfaces/graphqlServiceApi.interface.d.ts.map +0 -1
  88. package/dist/types/interfaces/graphqlServiceOption.interface.d.ts.map +0 -1
  89. package/dist/types/interfaces/graphqlSortingOption.interface.d.ts.map +0 -1
  90. package/dist/types/interfaces/index.d.ts.map +0 -1
  91. package/dist/types/interfaces/queryArgument.interface.d.ts.map +0 -1
  92. package/dist/types/services/graphql.service.d.ts.map +0 -1
  93. package/dist/types/services/graphqlQueryBuilder.d.ts.map +0 -1
  94. package/dist/types/services/index.d.ts.map +0 -1
  95. /package/dist/{types/index.d.ts → index.d.ts} +0 -0
  96. /package/dist/{esm/index.js → index.js} +0 -0
  97. /package/dist/{types/interfaces → interfaces}/graphqlCursorPaginationOption.interface.d.ts +0 -0
  98. /package/dist/{esm/interfaces → interfaces}/graphqlCursorPaginationOption.interface.js +0 -0
  99. /package/dist/{types/interfaces → interfaces}/graphqlDatasetFilter.interface.d.ts +0 -0
  100. /package/dist/{esm/interfaces → interfaces}/graphqlDatasetFilter.interface.js +0 -0
  101. /package/dist/{types/interfaces → interfaces}/graphqlFilteringOption.interface.d.ts +0 -0
  102. /package/dist/{esm/interfaces → interfaces}/graphqlFilteringOption.interface.js +0 -0
  103. /package/dist/{types/interfaces → interfaces}/graphqlPaginatedResult.interface.d.ts +0 -0
  104. /package/dist/{esm/interfaces → interfaces}/graphqlPaginatedResult.interface.js +0 -0
  105. /package/dist/{types/interfaces → interfaces}/graphqlPaginationOption.interface.d.ts +0 -0
  106. /package/dist/{esm/interfaces → interfaces}/graphqlPaginationOption.interface.js +0 -0
  107. /package/dist/{types/interfaces → interfaces}/graphqlResult.interface.d.ts +0 -0
  108. /package/dist/{esm/interfaces → interfaces}/graphqlResult.interface.js +0 -0
  109. /package/dist/{types/interfaces → interfaces}/graphqlServiceApi.interface.d.ts +0 -0
  110. /package/dist/{esm/interfaces → interfaces}/graphqlServiceApi.interface.js +0 -0
  111. /package/dist/{types/interfaces → interfaces}/graphqlServiceOption.interface.d.ts +0 -0
  112. /package/dist/{esm/interfaces → interfaces}/graphqlServiceOption.interface.js +0 -0
  113. /package/dist/{types/interfaces → interfaces}/graphqlSortingOption.interface.d.ts +0 -0
  114. /package/dist/{esm/interfaces → interfaces}/graphqlSortingOption.interface.js +0 -0
  115. /package/dist/{types/interfaces → interfaces}/index.d.ts +0 -0
  116. /package/dist/{esm/interfaces → interfaces}/index.js +0 -0
  117. /package/dist/{types/interfaces → interfaces}/queryArgument.interface.d.ts +0 -0
  118. /package/dist/{esm/interfaces → interfaces}/queryArgument.interface.js +0 -0
  119. /package/dist/{types/services → services}/graphqlQueryBuilder.d.ts +0 -0
  120. /package/dist/{esm/services → services}/graphqlQueryBuilder.js +0 -0
  121. /package/dist/{types/services → services}/index.d.ts +0 -0
  122. /package/dist/{esm/services → services}/index.js +0 -0
@@ -1,674 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GraphqlService = void 0;
4
- const common_1 = require("@slickgrid-universal/common");
5
- const utils_1 = require("@slickgrid-universal/utils");
6
- const graphqlQueryBuilder_js_1 = require("./graphqlQueryBuilder.js");
7
- const DEFAULT_ITEMS_PER_PAGE = 25;
8
- const DEFAULT_PAGE_SIZE = 20;
9
- class GraphqlService {
10
- constructor() {
11
- this._currentFilters = [];
12
- this._currentPagination = null;
13
- this._currentSorters = [];
14
- this._datasetIdPropName = 'id';
15
- this.defaultPaginationOptions = {
16
- first: DEFAULT_ITEMS_PER_PAGE,
17
- offset: 0,
18
- };
19
- }
20
- /** Getter for the Column Definitions */
21
- get columnDefinitions() {
22
- return this._columnDefinitions;
23
- }
24
- /** Getter for the Grid Options pulled through the Grid Object */
25
- get _gridOptions() {
26
- return this._grid?.getOptions() ?? {};
27
- }
28
- /** Initialization of the service, which acts as a constructor */
29
- init(serviceOptions, pagination, grid, sharedService) {
30
- this._grid = grid;
31
- this.options = serviceOptions || { datasetName: '' };
32
- this.pagination = pagination;
33
- this._datasetIdPropName = this._gridOptions.datasetIdPropertyName || 'id';
34
- if (typeof grid?.getColumns === 'function') {
35
- this._columnDefinitions = sharedService?.allColumns ?? grid.getColumns() ?? [];
36
- }
37
- }
38
- /**
39
- * Build the GraphQL query, since the service include/exclude cursor, the output query will be different.
40
- * @param serviceOptions GraphqlServiceOption
41
- */
42
- buildQuery() {
43
- if (!this.options || !this.options.datasetName || !Array.isArray(this._columnDefinitions)) {
44
- throw new Error('GraphQL Service requires the "datasetName" property to properly build the GraphQL query');
45
- }
46
- // get the column definitions and exclude some if they were tagged as excluded
47
- let columnDefinitions = this._columnDefinitions || [];
48
- columnDefinitions = columnDefinitions.filter((column) => !column.excludeFromQuery);
49
- const queryQb = new graphqlQueryBuilder_js_1.default(`query ${this.options.operationName ?? ''}`);
50
- const datasetQb = new graphqlQueryBuilder_js_1.default(this.options.datasetName);
51
- const nodesQb = new graphqlQueryBuilder_js_1.default('nodes');
52
- // get all the columnds Ids for the filters to work
53
- const columnIds = [];
54
- if (Array.isArray(columnDefinitions)) {
55
- for (const column of columnDefinitions) {
56
- if (!column.excludeFieldFromQuery) {
57
- columnIds.push(column.field);
58
- }
59
- // when extra "fields" are provided, also push them to columnIds
60
- if (column.fields) {
61
- columnIds.push(...column.fields);
62
- }
63
- }
64
- }
65
- // Slickgrid also requires the "id" field to be part of DataView
66
- // add it to the GraphQL query if it wasn't already part of the list
67
- if (columnIds.indexOf(this._datasetIdPropName) === -1) {
68
- columnIds.unshift(this._datasetIdPropName);
69
- }
70
- const columnsQuery = this.buildFilterQuery(columnIds);
71
- let graphqlNodeFields = [];
72
- if (this._gridOptions.enablePagination !== false || this.options.infiniteScroll) {
73
- if (this.options.useCursor) {
74
- // ...pageInfo { hasNextPage, endCursor }, edges { cursor, node { _columns_ } }, totalCount: 100
75
- const edgesQb = new graphqlQueryBuilder_js_1.default('edges');
76
- const pageInfoQb = new graphqlQueryBuilder_js_1.default('pageInfo');
77
- pageInfoQb.find('hasNextPage', 'hasPreviousPage', 'endCursor', 'startCursor');
78
- nodesQb.find(columnsQuery);
79
- edgesQb.find(['cursor']);
80
- graphqlNodeFields = ['totalCount', nodesQb, pageInfoQb, edgesQb];
81
- }
82
- else {
83
- // ...nodes { _columns_ }, totalCount: 100
84
- nodesQb.find(columnsQuery);
85
- graphqlNodeFields = ['totalCount', nodesQb];
86
- }
87
- // all properties to be returned by the query
88
- datasetQb.find(graphqlNodeFields);
89
- }
90
- else {
91
- // include all columns to be returned
92
- datasetQb.find(columnsQuery);
93
- }
94
- // add dataset filters, could be Pagination and SortingFilters and/or FieldFilters
95
- let datasetFilters = {};
96
- // only add pagination if it's enabled in the grid options
97
- if (this._gridOptions.enablePagination !== false || this.options.infiniteScroll) {
98
- datasetFilters = {};
99
- if (this.options.useCursor && this.options.paginationOptions) {
100
- datasetFilters = { ...this.options.paginationOptions };
101
- }
102
- else {
103
- const paginationOptions = this.options?.paginationOptions;
104
- datasetFilters.first =
105
- this.options?.infiniteScroll?.fetchSize ??
106
- this.options?.paginationOptions?.first ??
107
- this.pagination?.pageSize ??
108
- this.defaultPaginationOptions.first;
109
- datasetFilters.offset = paginationOptions && 'offset' in paginationOptions ? +paginationOptions.offset : 0;
110
- }
111
- }
112
- if (this.options.sortingOptions && Array.isArray(this.options.sortingOptions) && this.options.sortingOptions.length > 0) {
113
- // orderBy: [{ field:x, direction: 'ASC' }]
114
- datasetFilters.orderBy = this.options.sortingOptions;
115
- }
116
- if (this.options.filteringOptions && Array.isArray(this.options.filteringOptions) && this.options.filteringOptions.length > 0) {
117
- // filterBy: [{ field: date, operator: '>', value: '2000-10-10' }]
118
- datasetFilters.filterBy = this.options.filteringOptions;
119
- }
120
- if (this.options.addLocaleIntoQuery) {
121
- // first: 20, ... locale: "en-CA"
122
- datasetFilters.locale = this._gridOptions.translater?.getCurrentLanguage() || this._gridOptions.locale || 'en';
123
- }
124
- if (this.options.extraQueryArguments) {
125
- // first: 20, ... userId: 123
126
- for (const queryArgument of this.options.extraQueryArguments) {
127
- datasetFilters[queryArgument.field] = queryArgument.value;
128
- }
129
- }
130
- // with pagination:: query { users(first: 20, offset: 0, orderBy: [], filterBy: []) { totalCount: 100, nodes: { _columns_ }}}
131
- // without pagination:: query { users(orderBy: [], filterBy: []) { _columns_ }}
132
- datasetQb.filter(datasetFilters);
133
- queryQb.find(datasetQb);
134
- const enumSearchProperties = ['direction:', 'field:', 'operator:'];
135
- return this.trimDoubleQuotesOnEnumField(queryQb.toString(), enumSearchProperties, this.options.keepArgumentFieldDoubleQuotes || false);
136
- }
137
- postProcess(processResult) {
138
- if (processResult.data && this.pagination) {
139
- this.pagination.totalItems = processResult.data[this.getDatasetName()]?.totalCount || 0;
140
- }
141
- }
142
- /**
143
- * From an input array of strings, we want to build a GraphQL query string.
144
- * The process has to take the dot notation and parse it into a valid GraphQL query
145
- * Following this SO answer https://stackoverflow.com/a/47705476/1212166
146
- *
147
- * INPUT::
148
- * ['firstName', 'lastName', 'billing.address.street', 'billing.address.zip']
149
- * OUTPUT::
150
- * firstName, lastName, billing{address{street, zip}}
151
- * @param inputArray
152
- */
153
- buildFilterQuery(inputArray) {
154
- const set = (o = {}, a) => {
155
- const k = a.shift();
156
- o[k] = a.length ? set(o[k] ?? {}, a) : null;
157
- return o;
158
- };
159
- const output = inputArray.reduce((o, a) => set(o, a.split('.')), {});
160
- return JSON.stringify(output)
161
- .replace(/"|:|null/g, '')
162
- .replace(/^\{/, '')
163
- .replace(/\}$/, '');
164
- }
165
- clearFilters() {
166
- this._currentFilters = [];
167
- this.updateOptions({ filteringOptions: [] });
168
- }
169
- clearSorters() {
170
- this._currentSorters = [];
171
- this.updateOptions({ sortingOptions: [] });
172
- }
173
- /**
174
- * Get default initial Pagination options
175
- * @return Pagination Options
176
- */
177
- getInitPaginationOptions() {
178
- const paginationFirst = this.options?.infiniteScroll?.fetchSize ?? this.pagination?.pageSize ?? DEFAULT_ITEMS_PER_PAGE;
179
- return this.options?.useCursor ? { first: paginationFirst } : { first: paginationFirst, offset: 0 };
180
- }
181
- /** Get the GraphQL dataset name */
182
- getDatasetName() {
183
- return this.options?.datasetName || '';
184
- }
185
- /** Get the Filters that are currently used by the grid */
186
- getCurrentFilters() {
187
- return this._currentFilters;
188
- }
189
- /** Get the Pagination that is currently used by the grid */
190
- getCurrentPagination() {
191
- return this._currentPagination;
192
- }
193
- /** Get the Sorters that are currently used by the grid */
194
- getCurrentSorters() {
195
- return this._currentSorters;
196
- }
197
- /*
198
- * Reset the pagination options
199
- */
200
- resetPaginationOptions() {
201
- let paginationOptions;
202
- if (this.options?.useCursor) {
203
- paginationOptions = this.getInitPaginationOptions();
204
- }
205
- else {
206
- // first, last, offset
207
- paginationOptions = ((this.options && this.options.paginationOptions) || this.getInitPaginationOptions());
208
- paginationOptions.offset = 0;
209
- }
210
- // save current pagination as Page 1 and page size as "first" set size
211
- this._currentPagination = {
212
- pageNumber: 1,
213
- pageSize: paginationOptions.first || DEFAULT_PAGE_SIZE,
214
- };
215
- // unless user specifically set "enablePagination" to False, we'll update pagination options in every other cases
216
- if (this._gridOptions &&
217
- (this._gridOptions.enablePagination || !('enablePagination' in this._gridOptions) || this.options?.infiniteScroll)) {
218
- this.updateOptions({ paginationOptions });
219
- }
220
- }
221
- updateOptions(serviceOptions) {
222
- this.options = { ...this.options, ...serviceOptions };
223
- }
224
- /*
225
- * FILTERING
226
- */
227
- processOnFilterChanged(_event, args) {
228
- const gridOptions = this._gridOptions;
229
- const backendApi = gridOptions.backendServiceApi;
230
- if (backendApi === undefined) {
231
- throw new Error('Something went wrong in the GraphqlService, "backendServiceApi" is not initialized');
232
- }
233
- // keep current filters & always save it as an array (columnFilters can be an object when it is dealt by SlickGrid Filter)
234
- this._currentFilters = this.castFilterToColumnFilters(args.columnFilters);
235
- if (!args || !args.grid) {
236
- throw new Error('Something went wrong when trying create the GraphQL Backend Service, it seems that "args" is not populated correctly');
237
- }
238
- // loop through all columns to inspect filters & set the query
239
- this.updateFilters(args.columnFilters, false);
240
- this.resetPaginationOptions();
241
- return this.buildQuery();
242
- }
243
- /*
244
- * PAGINATION
245
- * With cursor, the query can have 4 arguments (first, after, last, before), for example:
246
- * users (first:20, after:"YXJyYXljb25uZWN0aW9uOjM=") {
247
- * totalCount
248
- * pageInfo {
249
- * hasNextPage
250
- * hasPreviousPage
251
- * endCursor
252
- * startCursor
253
- * }
254
- * edges {
255
- * cursor
256
- * node {
257
- * name
258
- * gender
259
- * }
260
- * }
261
- * }
262
- * Without cursor, the query can have 3 arguments (first, last, offset), for example:
263
- * users (first:20, offset: 10) {
264
- * totalCount
265
- * nodes {
266
- * name
267
- * gender
268
- * }
269
- * }
270
- */
271
- processOnPaginationChanged(_event, args) {
272
- const pageSize = +(this.options?.infiniteScroll?.fetchSize ||
273
- args.pageSize ||
274
- (this.pagination ? this.pagination.pageSize : DEFAULT_PAGE_SIZE));
275
- // if first/last defined on args, then it is a cursor based pagination change
276
- 'first' in args || 'last' in args ? this.updatePagination(args.newPage, pageSize, args) : this.updatePagination(args.newPage, pageSize);
277
- // build the GraphQL query which we will use in the WebAPI callback
278
- return this.buildQuery();
279
- }
280
- /*
281
- * SORTING
282
- * we will use sorting as per a Facebook suggestion on a Github issue (with some small changes)
283
- * https://github.com/graphql/graphql-relay-js/issues/20#issuecomment-220494222
284
- *
285
- * users (first: 20, offset: 10, orderBy: [{field: lastName, direction: ASC}, {field: firstName, direction: DESC}]) {
286
- * totalCount
287
- * nodes {
288
- * name
289
- * gender
290
- * }
291
- * }
292
- */
293
- processOnSortChanged(_event, args) {
294
- const sortColumns = args.multiColumnSort
295
- ? args.sortCols
296
- : new Array({
297
- columnId: args.sortCol?.id ?? '',
298
- sortCol: args.sortCol,
299
- sortAsc: args.sortAsc,
300
- });
301
- // loop through all columns to inspect sorters & set the query
302
- this.updateSorters(sortColumns);
303
- // when using infinite scroll, we need to go back to 1st page
304
- if (this.options?.infiniteScroll) {
305
- this.updateOptions({ paginationOptions: { offset: 0 } });
306
- }
307
- // build the GraphQL query which we will use in the WebAPI callback
308
- return this.buildQuery();
309
- }
310
- /**
311
- * Update column filters by looping through all columns to inspect filters & update backend service filteringOptions
312
- * @param columnFilters
313
- */
314
- updateFilters(columnFilters, isUpdatedByPresetOrDynamically) {
315
- const searchByArray = [];
316
- let searchValue;
317
- // on filter preset load, we need to keep current filters
318
- if (isUpdatedByPresetOrDynamically) {
319
- this._currentFilters = this.castFilterToColumnFilters(columnFilters);
320
- }
321
- for (const columnId in columnFilters) {
322
- if (columnId in columnFilters) {
323
- const columnFilter = columnFilters[columnId];
324
- // if user defined some "presets", then we need to find the filters from the column definitions instead
325
- let columnDef;
326
- if (isUpdatedByPresetOrDynamically && Array.isArray(this._columnDefinitions)) {
327
- columnDef = this._columnDefinitions.find((column) => column.id === columnFilter.columnId);
328
- }
329
- else {
330
- columnDef = columnFilter.columnDef;
331
- }
332
- if (!columnDef) {
333
- throw new Error('[GraphQL Service]: Something went wrong in trying to get the column definition of the specified filter (or preset filters). Did you make a typo on the filter columnId?');
334
- }
335
- let fieldName = columnDef.filter?.queryField || columnDef.queryFieldFilter || columnDef.queryField || columnDef.field || columnDef.name || '';
336
- if (fieldName instanceof HTMLElement) {
337
- fieldName = (0, utils_1.stripTags)(fieldName.innerHTML);
338
- }
339
- const fieldType = columnDef.type || common_1.FieldType.string;
340
- let searchTerms = columnFilter?.searchTerms ?? [];
341
- let fieldSearchValue = Array.isArray(searchTerms) && searchTerms.length === 1 ? searchTerms[0] : '';
342
- if (typeof fieldSearchValue === 'undefined') {
343
- fieldSearchValue = '';
344
- }
345
- if (!fieldName) {
346
- throw new Error(`GraphQL filter could not find the field name to query the search, your column definition must include a valid "field" or "name" (optionally you can also use the "queryfield").`);
347
- }
348
- if (this.options?.useVerbatimSearchTerms || columnFilter.verbatimSearchTerms) {
349
- searchByArray.push({
350
- field: (0, utils_1.getHtmlStringOutput)(fieldName),
351
- operator: columnFilter.operator,
352
- value: JSON.stringify(columnFilter.searchTerms),
353
- });
354
- continue;
355
- }
356
- fieldSearchValue = fieldSearchValue === undefined || fieldSearchValue === null ? '' : `${fieldSearchValue}`; // make sure it's a string
357
- // run regex to find possible filter operators unless the user disabled the feature
358
- const autoParseInputFilterOperator = columnDef.autoParseInputFilterOperator ?? this._gridOptions.autoParseInputFilterOperator;
359
- // group (2): comboStartsWith, (3): comboEndsWith, (4): Operator, (1 or 5): searchValue, (6): last char is '*' (meaning starts with, ex.: abc*)
360
- const matches = autoParseInputFilterOperator !== false
361
- ? fieldSearchValue.match(/^((.*[^\\*\r\n])[*]{1}(.*[^*\r\n]))|^([<>!=*]{0,2})(.*[^<>!=*])([*]?)$/) || []
362
- : [fieldSearchValue, '', '', '', '', fieldSearchValue, ''];
363
- const comboStartsWith = matches?.[2] || '';
364
- const comboEndsWith = matches?.[3] || '';
365
- let operator = columnFilter.operator || matches?.[4];
366
- searchValue = matches?.[1] || matches?.[5] || '';
367
- const lastValueChar = matches?.[6] || operator === '*z' || operator === common_1.OperatorType.endsWith ? '*' : '';
368
- // no need to query if search value is empty
369
- if (fieldName && searchValue === '' && searchTerms.length === 0) {
370
- continue;
371
- }
372
- let filterQueryOverride = undefined;
373
- if (typeof this.options?.filterQueryOverride === 'function') {
374
- filterQueryOverride = this.options?.filterQueryOverride({
375
- fieldName: (0, utils_1.getHtmlStringOutput)(fieldName),
376
- columnDef,
377
- operator,
378
- columnFilterOperator: columnFilter.operator,
379
- searchValues: searchTerms,
380
- grid: this._grid,
381
- });
382
- }
383
- if (filterQueryOverride !== undefined) {
384
- // since this is a Custom Filter, we expect Operator to be a string
385
- // and it is assumed that the developer will implement this custom operator in their GraphQL Schema
386
- // e.g.: https://stackoverflow.com/a/37981802/1212166
387
- searchByArray.push(filterQueryOverride);
388
- }
389
- else {
390
- if (comboStartsWith && comboEndsWith) {
391
- searchTerms = [comboStartsWith, comboEndsWith];
392
- operator = common_1.OperatorType.startsWithEndsWith;
393
- }
394
- else if (Array.isArray(searchTerms) &&
395
- searchTerms.length === 1 &&
396
- typeof searchTerms[0] === 'string' &&
397
- searchTerms[0].indexOf('..') >= 0) {
398
- if (operator !== common_1.OperatorType.rangeInclusive && operator !== common_1.OperatorType.rangeExclusive) {
399
- operator = this._gridOptions.defaultFilterRangeOperator ?? common_1.OperatorType.rangeInclusive;
400
- }
401
- searchTerms = searchTerms[0].split('..', 2);
402
- if (searchTerms[0] === '') {
403
- operator = operator === common_1.OperatorType.rangeInclusive ? '<=' : operator === common_1.OperatorType.rangeExclusive ? '<' : operator;
404
- searchTerms = searchTerms.slice(1);
405
- searchValue = searchTerms[0];
406
- }
407
- else if (searchTerms[1] === '') {
408
- operator = operator === common_1.OperatorType.rangeInclusive ? '>=' : operator === common_1.OperatorType.rangeExclusive ? '>' : operator;
409
- searchTerms = searchTerms.slice(0, 1);
410
- searchValue = searchTerms[0];
411
- }
412
- }
413
- if (typeof searchValue === 'string') {
414
- if (operator === '*' || operator === 'a*' || operator === '*z' || lastValueChar === '*') {
415
- operator = (operator === '*' || operator === '*z' ? 'EndsWith' : 'StartsWith');
416
- }
417
- }
418
- // if we didn't find an Operator but we have a Column Operator inside the Filter (DOM Element), we should use its default Operator
419
- // multipleSelect is "IN", while singleSelect is "EQ", else don't map any operator
420
- if (!operator && columnDef.filter && columnDef.filter.operator) {
421
- operator = columnDef.filter.operator;
422
- }
423
- // No operator and 2 search terms should lead to default range operator.
424
- if (!operator && Array.isArray(searchTerms) && searchTerms.length === 2 && searchTerms[0] && searchTerms[1]) {
425
- operator = this._gridOptions.defaultFilterRangeOperator;
426
- }
427
- // Range with 1 searchterm should lead to equals for a date field.
428
- if ((operator === common_1.OperatorType.rangeInclusive || operator === common_1.OperatorType.rangeExclusive) &&
429
- Array.isArray(searchTerms) &&
430
- searchTerms.length === 1 &&
431
- fieldType === common_1.FieldType.date) {
432
- operator = common_1.OperatorType.equal;
433
- }
434
- // Normalize all search values
435
- searchValue = this.normalizeSearchValue(fieldType, searchValue);
436
- if (Array.isArray(searchTerms)) {
437
- searchTerms.forEach((_part, index) => {
438
- searchTerms[index] = this.normalizeSearchValue(fieldType, searchTerms[index]);
439
- });
440
- }
441
- // StartsWith + EndsWith combo
442
- if (operator === common_1.OperatorType.startsWithEndsWith && Array.isArray(searchTerms) && searchTerms.length === 2) {
443
- // add 2 conditions (StartsWith A + EndsWith B) to the search array
444
- searchByArray.push({
445
- field: (0, utils_1.getHtmlStringOutput)(fieldName),
446
- operator: common_1.OperatorType.startsWith,
447
- value: comboStartsWith,
448
- });
449
- searchByArray.push({ field: (0, utils_1.getHtmlStringOutput)(fieldName), operator: common_1.OperatorType.endsWith, value: comboEndsWith });
450
- continue;
451
- }
452
- // when having more than 1 search term (we need to create a CSV string for GraphQL "IN" or "NOT IN" filter search)
453
- if (searchTerms?.length > 1 && (operator === 'IN' || operator === 'NIN' || operator === 'NOT_IN')) {
454
- searchValue = searchTerms.join(',');
455
- }
456
- else if (searchTerms?.length === 2 && (operator === common_1.OperatorType.rangeExclusive || operator === common_1.OperatorType.rangeInclusive)) {
457
- searchByArray.push({
458
- field: (0, utils_1.getHtmlStringOutput)(fieldName),
459
- operator: operator === common_1.OperatorType.rangeInclusive ? 'GE' : 'GT',
460
- value: searchTerms[0],
461
- });
462
- searchByArray.push({
463
- field: (0, utils_1.getHtmlStringOutput)(fieldName),
464
- operator: operator === common_1.OperatorType.rangeInclusive ? 'LE' : 'LT',
465
- value: searchTerms[1],
466
- });
467
- continue;
468
- }
469
- // if we still don't have an operator find the proper Operator to use according field type
470
- if (!operator) {
471
- operator = (0, common_1.mapOperatorByFieldType)(fieldType);
472
- }
473
- // build the search array
474
- searchByArray.push({ field: (0, utils_1.getHtmlStringOutput)(fieldName), operator: (0, common_1.mapOperatorType)(operator), value: searchValue });
475
- }
476
- }
477
- }
478
- // update the service options with filters for the buildQuery() to work later
479
- this.updateOptions({ filteringOptions: searchByArray });
480
- }
481
- /**
482
- * Update the pagination component with it's new page number and size.
483
- * @param {Number} newPage
484
- * @param {Number} pageSize
485
- * @param {*} [cursorArgs] these should be supplied when using cursor based pagination
486
- */
487
- updatePagination(newPage, pageSize, cursorArgs) {
488
- this._currentPagination = {
489
- pageNumber: newPage,
490
- pageSize,
491
- };
492
- let paginationOptions = {};
493
- if (this.options?.useCursor) {
494
- // use cursor based pagination
495
- // when using cursor pagination, expect to be given a PaginationCursorChangedArgs as arguments,
496
- // but still handle the case where it's not (can happen when initial configuration not pre-configured (automatically corrects itself next setCursorPageInfo() call))
497
- if (cursorArgs && cursorArgs instanceof Object) {
498
- // remove pageSize and newPage from cursorArgs, otherwise they get put on the query input string
499
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
500
- const { pageSize, newPage, ...cursorPaginationOptions } = cursorArgs;
501
- paginationOptions = cursorPaginationOptions;
502
- }
503
- else {
504
- paginationOptions = { first: pageSize };
505
- }
506
- }
507
- else {
508
- // use offset based pagination
509
- paginationOptions = {
510
- first: pageSize,
511
- offset: newPage > 1 ? (newPage - 1) * pageSize : 0, // recalculate offset but make sure the result is always over 0
512
- };
513
- }
514
- this.updateOptions({ paginationOptions });
515
- }
516
- /**
517
- * Update all Sorting by looping through all columns to inspect sorters & update backend service sortingOptions
518
- */
519
- updateSorters(sortColumns, presetSorters) {
520
- let currentSorters = [];
521
- const graphqlSorters = [];
522
- if (!sortColumns && presetSorters) {
523
- // make the presets the current sorters, also make sure that all direction are in uppercase for GraphQL
524
- currentSorters = presetSorters;
525
- currentSorters.forEach((sorter) => (sorter.direction = sorter.direction.toUpperCase()));
526
- // display the correct sorting icons on the UI, for that it requires (columnId, sortAsc) properties
527
- const tmpSorterArray = currentSorters.map((sorter) => {
528
- const columnDef = this._columnDefinitions?.find((column) => column.id === sorter.columnId);
529
- graphqlSorters.push({
530
- field: columnDef ? (columnDef.queryFieldSorter || columnDef.queryField || columnDef.field) + '' : sorter.columnId + '',
531
- direction: sorter.direction,
532
- });
533
- // return only the column(s) found in the Column Definitions ELSE null
534
- if (columnDef) {
535
- return {
536
- columnId: sorter.columnId,
537
- sortAsc: sorter.direction.toUpperCase() === common_1.SortDirection.ASC,
538
- };
539
- }
540
- return null;
541
- });
542
- // set the sort icons, but also make sure to filter out null values (that happens when columnDef is not found)
543
- if (Array.isArray(tmpSorterArray) && this._grid) {
544
- this._grid.setSortColumns(tmpSorterArray.filter((sorter) => sorter) || []);
545
- }
546
- }
547
- else if (sortColumns && !presetSorters) {
548
- // build the orderBy array, it could be multisort, example
549
- // orderBy:[{field: lastName, direction: ASC}, {field: firstName, direction: DESC}]
550
- if (Array.isArray(sortColumns) && sortColumns.length > 0) {
551
- for (const column of sortColumns) {
552
- if (column && column.sortCol) {
553
- currentSorters.push({
554
- columnId: column.sortCol.id + '',
555
- direction: column.sortAsc ? common_1.SortDirection.ASC : common_1.SortDirection.DESC,
556
- });
557
- const fieldName = (column.sortCol.queryFieldSorter || column.sortCol.queryField || column.sortCol.field || '') + '';
558
- if (fieldName) {
559
- graphqlSorters.push({
560
- field: fieldName,
561
- direction: column.sortAsc ? common_1.SortDirection.ASC : common_1.SortDirection.DESC,
562
- });
563
- }
564
- }
565
- }
566
- }
567
- }
568
- // keep current Sorters and update the service options with the new sorting
569
- this._currentSorters = currentSorters;
570
- this.updateOptions({ sortingOptions: graphqlSorters });
571
- }
572
- /**
573
- * A function which takes an input string and removes double quotes only
574
- * on certain fields are identified as GraphQL enums (except fields with dot notation)
575
- * For example let say we identified ("direction:", "sort") as word which are GraphQL enum fields
576
- * then the result will be:
577
- * FROM
578
- * query { users (orderBy:[{field:"firstName", direction:"ASC"} }]) }
579
- * TO
580
- * query { users (orderBy:[{field: firstName, direction: ASC}})}
581
- *
582
- * EXCEPTIONS (fields with dot notation "." which are inside a "field:")
583
- * these fields will keep double quotes while everything else will be stripped of double quotes
584
- * query { users (orderBy:[{field:"billing.street.name", direction: "ASC"} }
585
- * TO
586
- * query { users (orderBy:[{field:"billing.street.name", direction: ASC}}
587
- * @param inputStr input string
588
- * @param enumSearchWords array of enum words to filter
589
- * @param keepArgumentFieldDoubleQuotes - do we keep field double quotes? (i.e.: field: "user.name")
590
- * @returns outputStr output string
591
- */
592
- trimDoubleQuotesOnEnumField(inputStr, enumSearchWords, keepArgumentFieldDoubleQuotes) {
593
- // eslint-disable-next-line
594
- const patternWordInQuotes = `\s?((field:\s*)?".*?")`;
595
- let patternRegex = enumSearchWords.join(patternWordInQuotes + '|');
596
- patternRegex += patternWordInQuotes; // the last one should also have the pattern but without the pipe "|"
597
- // example with (field: & direction:): /field:s?(".*?")|direction:s?(".*?")/
598
- const reg = new RegExp(patternRegex, 'g');
599
- return inputStr.replace(reg, (group1) => {
600
- // remove double quotes except when the string starts with a "field:"
601
- let removeDoubleQuotes = true;
602
- if (group1.startsWith('field:') && keepArgumentFieldDoubleQuotes) {
603
- removeDoubleQuotes = false;
604
- }
605
- const rep = removeDoubleQuotes ? group1.replace(/"/g, '') : group1;
606
- return rep;
607
- });
608
- }
609
- //
610
- // protected functions
611
- // -------------------
612
- /**
613
- * Cast provided filters (could be in multiple formats) into an array of CurrentFilter
614
- * @param columnFilters
615
- */
616
- castFilterToColumnFilters(columnFilters) {
617
- // keep current filters & always save it as an array (columnFilters can be an object when it is dealt by SlickGrid Filter)
618
- const filtersArray = typeof columnFilters === 'object' ? Object.keys(columnFilters).map((key) => columnFilters[key]) : columnFilters;
619
- if (!Array.isArray(filtersArray)) {
620
- return [];
621
- }
622
- return filtersArray.map((filter) => {
623
- const tmpFilter = { columnId: filter.columnId || '' };
624
- if (filter.operator) {
625
- tmpFilter.operator = filter.operator;
626
- }
627
- if (filter.targetSelector) {
628
- tmpFilter.targetSelector = filter.targetSelector;
629
- }
630
- if (Array.isArray(filter.searchTerms)) {
631
- tmpFilter.searchTerms = filter.searchTerms;
632
- }
633
- return tmpFilter;
634
- });
635
- }
636
- /** Normalizes the search value according to field type. */
637
- normalizeSearchValue(fieldType, searchValue) {
638
- switch (fieldType) {
639
- case common_1.FieldType.date:
640
- case common_1.FieldType.string:
641
- case common_1.FieldType.text:
642
- case common_1.FieldType.readonly:
643
- if (typeof searchValue === 'string') {
644
- // escape single quotes by doubling them
645
- searchValue = searchValue.replace(/'/g, `''`);
646
- }
647
- break;
648
- case common_1.FieldType.integer:
649
- case common_1.FieldType.number:
650
- case common_1.FieldType.float:
651
- if (typeof searchValue === 'string') {
652
- // Parse a valid decimal from the string.
653
- // Replace double dots with single dots
654
- searchValue = searchValue.replace(/\.\./g, '.');
655
- // Remove a trailing dot
656
- searchValue = searchValue.replace(/\.+$/g, '');
657
- // Prefix a leading dot with 0
658
- searchValue = searchValue.replace(/^\.+/g, '0.');
659
- // Prefix leading dash dot with -0.
660
- searchValue = searchValue.replace(/^-+\.+/g, '-0.');
661
- // Remove any non valid decimal characters from the search string
662
- searchValue = searchValue.replace(/(?!^-)[^\d.]/g, '');
663
- // if nothing left, search for 0
664
- if (searchValue === '' || searchValue === '-') {
665
- searchValue = '0';
666
- }
667
- }
668
- break;
669
- }
670
- return searchValue;
671
- }
672
- }
673
- exports.GraphqlService = GraphqlService;
674
- //# sourceMappingURL=graphql.service.js.map