ng-qubee 2.0.5 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +432 -30
- package/fesm2022/ng-qubee.mjs +1264 -261
- package/fesm2022/ng-qubee.mjs.map +1 -1
- package/package.json +9 -4
- package/types/ng-qubee.d.ts +1302 -0
- package/index.d.ts +0 -5
- package/lib/enums/sort.enum.d.ts +0 -4
- package/lib/errors/invalid-limit.error.d.ts +0 -3
- package/lib/errors/invalid-model-name.error.d.ts +0 -3
- package/lib/errors/invalid-page-number.error.d.ts +0 -3
- package/lib/errors/key-not-found.error.d.ts +0 -3
- package/lib/errors/unselectable-model.error.d.ts +0 -3
- package/lib/interfaces/config.interface.d.ts +0 -6
- package/lib/interfaces/fields.interface.d.ts +0 -3
- package/lib/interfaces/filters.interface.d.ts +0 -3
- package/lib/interfaces/normalized.interface.d.ts +0 -3
- package/lib/interfaces/paginated-object.interface.d.ts +0 -3
- package/lib/interfaces/pagination-config.interface.d.ts +0 -14
- package/lib/interfaces/query-builder-config.interface.d.ts +0 -9
- package/lib/interfaces/query-builder-state.interface.d.ts +0 -13
- package/lib/interfaces/sort.interface.d.ts +0 -5
- package/lib/models/paginated-collection.d.ts +0 -30
- package/lib/models/query-builder-options.d.ts +0 -11
- package/lib/models/response-options.d.ts +0 -16
- package/lib/ng-qubee.module.d.ts +0 -9
- package/lib/provide-ngqubee.d.ts +0 -21
- package/lib/services/nest.service.d.ts +0 -182
- package/lib/services/ng-qubee.service.d.ts +0 -147
- package/lib/services/pagination.service.d.ts +0 -13
- package/public-api.d.ts +0 -10
package/fesm2022/ng-qubee.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { signal, computed, Injectable, Inject, Optional, NgModule, makeEnvironmentProviders } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject, filter, throwError } from 'rxjs';
|
|
3
4
|
import * as qs from 'qs';
|
|
4
|
-
import { BehaviorSubject, filter } from 'rxjs';
|
|
5
5
|
|
|
6
6
|
class KeyNotFoundError extends Error {
|
|
7
7
|
constructor(key) {
|
|
@@ -66,18 +66,178 @@ class PaginatedCollection {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Enum representing the available pagination driver types
|
|
71
|
+
*
|
|
72
|
+
* Each driver encapsulates the full format knowledge for both
|
|
73
|
+
* request building (URI generation) and response parsing.
|
|
74
|
+
*/
|
|
75
|
+
var DriverEnum;
|
|
76
|
+
(function (DriverEnum) {
|
|
77
|
+
DriverEnum["LARAVEL"] = "laravel";
|
|
78
|
+
DriverEnum["NESTJS"] = "nestjs";
|
|
79
|
+
DriverEnum["SPATIE"] = "spatie";
|
|
80
|
+
})(DriverEnum || (DriverEnum = {}));
|
|
74
81
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Resolved response field key names with defaults applied
|
|
84
|
+
*
|
|
85
|
+
* Maps logical pagination concepts to the actual key names
|
|
86
|
+
* used in the API response. Unset values fall back to Laravel defaults.
|
|
87
|
+
*
|
|
88
|
+
* For NestJS responses, use dot-notation paths:
|
|
89
|
+
* ```typescript
|
|
90
|
+
* new ResponseOptions({
|
|
91
|
+
* currentPage: 'meta.currentPage',
|
|
92
|
+
* total: 'meta.totalItems'
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
class ResponseOptions {
|
|
97
|
+
currentPage;
|
|
98
|
+
data;
|
|
99
|
+
firstPageUrl;
|
|
100
|
+
from;
|
|
101
|
+
lastPage;
|
|
102
|
+
lastPageUrl;
|
|
103
|
+
nextPageUrl;
|
|
104
|
+
path;
|
|
105
|
+
perPage;
|
|
106
|
+
prevPageUrl;
|
|
107
|
+
to;
|
|
108
|
+
total;
|
|
109
|
+
constructor(options) {
|
|
110
|
+
this.currentPage = options.currentPage || 'current_page';
|
|
111
|
+
this.data = options.data || 'data';
|
|
112
|
+
this.firstPageUrl = options.firstPageUrl || 'first_page_url';
|
|
113
|
+
this.from = options.from || 'from';
|
|
114
|
+
this.lastPage = options.lastPage || 'last_page';
|
|
115
|
+
this.lastPageUrl = options.lastPageUrl || 'last_page_url';
|
|
116
|
+
this.nextPageUrl = options.nextPageUrl || 'next_page_url';
|
|
117
|
+
this.path = options.path || 'path';
|
|
118
|
+
this.perPage = options.perPage || 'per_page';
|
|
119
|
+
this.prevPageUrl = options.prevPageUrl || 'prev_page_url';
|
|
120
|
+
this.to = options.to || 'to';
|
|
121
|
+
this.total = options.total || 'total';
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Pre-configured ResponseOptions for the NestJS driver
|
|
126
|
+
*
|
|
127
|
+
* Uses dot-notation paths to access nested values in the NestJS response format.
|
|
128
|
+
*/
|
|
129
|
+
class NestjsResponseOptions extends ResponseOptions {
|
|
130
|
+
constructor(options) {
|
|
131
|
+
super({
|
|
132
|
+
currentPage: options.currentPage || 'meta.currentPage',
|
|
133
|
+
data: options.data || 'data',
|
|
134
|
+
firstPageUrl: options.firstPageUrl || 'links.first',
|
|
135
|
+
from: options.from || 'meta.from',
|
|
136
|
+
lastPage: options.lastPage || 'meta.totalPages',
|
|
137
|
+
lastPageUrl: options.lastPageUrl || 'links.last',
|
|
138
|
+
nextPageUrl: options.nextPageUrl || 'links.next',
|
|
139
|
+
path: options.path || 'path',
|
|
140
|
+
perPage: options.perPage || 'meta.itemsPerPage',
|
|
141
|
+
prevPageUrl: options.prevPageUrl || 'links.previous',
|
|
142
|
+
to: options.to || 'meta.to',
|
|
143
|
+
total: options.total || 'meta.totalItems'
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Error thrown when per-model field selection is attempted with a driver that does not support it
|
|
150
|
+
*
|
|
151
|
+
* Per-model field selection is only supported by the Spatie driver.
|
|
152
|
+
* Use `addSelect()` for NestJS flat field selection.
|
|
153
|
+
*/
|
|
154
|
+
class UnsupportedFieldSelectionError extends Error {
|
|
155
|
+
constructor() {
|
|
156
|
+
super('Per-model field selection is only supported by the Spatie driver. Use addSelect() for NestJS.');
|
|
157
|
+
this.name = 'UnsupportedFieldSelectionError';
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Error thrown when filters are attempted with a driver that does not support them
|
|
163
|
+
*
|
|
164
|
+
* Filters are only supported by the Spatie and NestJS drivers.
|
|
165
|
+
*/
|
|
166
|
+
class UnsupportedFilterError extends Error {
|
|
167
|
+
constructor() {
|
|
168
|
+
super('Filters are only supported by the Spatie and NestJS drivers.');
|
|
169
|
+
this.name = 'UnsupportedFilterError';
|
|
78
170
|
}
|
|
79
171
|
}
|
|
80
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Error thrown when filter operators are attempted with a driver that does not support them
|
|
175
|
+
*
|
|
176
|
+
* Filter operators are only supported by the NestJS driver.
|
|
177
|
+
* Use `addFilter()` for Spatie implicit equality filters.
|
|
178
|
+
*/
|
|
179
|
+
class UnsupportedFilterOperatorError extends Error {
|
|
180
|
+
constructor() {
|
|
181
|
+
super('Filter operators are only supported by the NestJS driver. Use addFilter() for Spatie.');
|
|
182
|
+
this.name = 'UnsupportedFilterOperatorError';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Error thrown when includes are attempted with a driver that does not support them
|
|
188
|
+
*
|
|
189
|
+
* Includes are only supported by the Spatie driver.
|
|
190
|
+
*/
|
|
191
|
+
class UnsupportedIncludesError extends Error {
|
|
192
|
+
constructor() {
|
|
193
|
+
super('Includes are only supported by the Spatie driver.');
|
|
194
|
+
this.name = 'UnsupportedIncludesError';
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Error thrown when search is attempted with a driver that does not support it
|
|
200
|
+
*
|
|
201
|
+
* Search is only supported by the NestJS driver.
|
|
202
|
+
*/
|
|
203
|
+
class UnsupportedSearchError extends Error {
|
|
204
|
+
constructor() {
|
|
205
|
+
super('Search is only supported by the NestJS driver.');
|
|
206
|
+
this.name = 'UnsupportedSearchError';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Error thrown when flat field selection is attempted with a driver that does not support it
|
|
212
|
+
*
|
|
213
|
+
* Flat field selection is only supported by the NestJS driver.
|
|
214
|
+
* Use `addFields()` for Spatie per-model field selection.
|
|
215
|
+
*/
|
|
216
|
+
class UnsupportedSelectError extends Error {
|
|
217
|
+
constructor() {
|
|
218
|
+
super('Flat field selection is only supported by the NestJS driver. Use addFields() for Spatie.');
|
|
219
|
+
this.name = 'UnsupportedSelectError';
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Error thrown when sorts are attempted with a driver that does not support them
|
|
225
|
+
*
|
|
226
|
+
* Sorts are only supported by the Spatie and NestJS drivers.
|
|
227
|
+
*/
|
|
228
|
+
class UnsupportedSortError extends Error {
|
|
229
|
+
constructor() {
|
|
230
|
+
super('Sorts are only supported by the Spatie and NestJS drivers.');
|
|
231
|
+
this.name = 'UnsupportedSortError';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Resolved query parameter key names with defaults applied
|
|
237
|
+
*
|
|
238
|
+
* Maps logical query concepts to the actual query parameter names
|
|
239
|
+
* used in the generated URI. Unset values fall back to defaults.
|
|
240
|
+
*/
|
|
81
241
|
class QueryBuilderOptions {
|
|
82
242
|
appends;
|
|
83
243
|
fields;
|
|
@@ -85,7 +245,10 @@ class QueryBuilderOptions {
|
|
|
85
245
|
includes;
|
|
86
246
|
limit;
|
|
87
247
|
page;
|
|
248
|
+
search;
|
|
249
|
+
select;
|
|
88
250
|
sort;
|
|
251
|
+
sortBy;
|
|
89
252
|
constructor(options) {
|
|
90
253
|
this.appends = options.appends || 'append';
|
|
91
254
|
this.fields = options.fields || 'fields';
|
|
@@ -93,14 +256,29 @@ class QueryBuilderOptions {
|
|
|
93
256
|
this.includes = options.includes || 'include';
|
|
94
257
|
this.limit = options.limit || 'limit';
|
|
95
258
|
this.page = options.page || 'page';
|
|
259
|
+
this.search = options.search || 'search';
|
|
260
|
+
this.select = options.select || 'select';
|
|
96
261
|
this.sort = options.sort || 'sort';
|
|
262
|
+
this.sortBy = options.sortBy || 'sortBy';
|
|
97
263
|
}
|
|
98
264
|
}
|
|
99
265
|
|
|
100
|
-
class
|
|
101
|
-
constructor(
|
|
102
|
-
super(`Invalid
|
|
103
|
-
this.name = '
|
|
266
|
+
class InvalidLimitError extends Error {
|
|
267
|
+
constructor(limit) {
|
|
268
|
+
super(`Invalid limit value: Limit must be a positive integer greater than 0. Received: ${limit}`);
|
|
269
|
+
this.name = 'InvalidLimitError';
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Error thrown when an invalid resource name is provided
|
|
275
|
+
*
|
|
276
|
+
* Resource name must be a non-empty string.
|
|
277
|
+
*/
|
|
278
|
+
class InvalidResourceNameError extends Error {
|
|
279
|
+
constructor(resource) {
|
|
280
|
+
super(`Invalid resource name: Resource name must be a non-empty string. Received: ${JSON.stringify(resource)}`);
|
|
281
|
+
this.name = 'InvalidResourceNameError';
|
|
104
282
|
}
|
|
105
283
|
}
|
|
106
284
|
|
|
@@ -111,21 +289,17 @@ class InvalidPageNumberError extends Error {
|
|
|
111
289
|
}
|
|
112
290
|
}
|
|
113
291
|
|
|
114
|
-
class InvalidLimitError extends Error {
|
|
115
|
-
constructor(limit) {
|
|
116
|
-
super(`Invalid limit value: Limit must be a positive integer greater than 0. Received: ${limit}`);
|
|
117
|
-
this.name = 'InvalidLimitError';
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
292
|
const INITIAL_STATE = {
|
|
122
293
|
baseUrl: '',
|
|
123
294
|
fields: {},
|
|
124
295
|
filters: {},
|
|
125
296
|
includes: [],
|
|
126
297
|
limit: 15,
|
|
127
|
-
|
|
298
|
+
operatorFilters: [],
|
|
128
299
|
page: 1,
|
|
300
|
+
resource: '',
|
|
301
|
+
search: '',
|
|
302
|
+
select: [],
|
|
129
303
|
sorts: []
|
|
130
304
|
};
|
|
131
305
|
class NestService {
|
|
@@ -134,15 +308,15 @@ class NestService {
|
|
|
134
308
|
*
|
|
135
309
|
* @type {IQueryBuilderState}
|
|
136
310
|
*/
|
|
137
|
-
_nest = signal(this._clone(INITIAL_STATE));
|
|
311
|
+
_nest = signal(this._clone(INITIAL_STATE), ...(ngDevMode ? [{ debugName: "_nest" }] : []));
|
|
138
312
|
/**
|
|
139
313
|
* A computed signal that makes readonly the writable signal _nest
|
|
140
314
|
*
|
|
141
315
|
* @type {Signal<IQueryBuilderState>}
|
|
142
316
|
*/
|
|
143
|
-
nest = computed(() => this._clone(this._nest()));
|
|
317
|
+
nest = computed(() => this._clone(this._nest()), ...(ngDevMode ? [{ debugName: "nest" }] : []));
|
|
144
318
|
constructor() {
|
|
145
|
-
// Nothing to see here
|
|
319
|
+
// Nothing to see here
|
|
146
320
|
}
|
|
147
321
|
/**
|
|
148
322
|
* Set the base URL for the API
|
|
@@ -173,22 +347,6 @@ class NestService {
|
|
|
173
347
|
limit
|
|
174
348
|
}));
|
|
175
349
|
}
|
|
176
|
-
/**
|
|
177
|
-
* Set the model name for the query
|
|
178
|
-
* Must be a non-empty string
|
|
179
|
-
*
|
|
180
|
-
* @param {string} model - The model/resource name (e.g., 'users', 'posts')
|
|
181
|
-
* @throws {InvalidModelNameError} If model is not a non-empty string
|
|
182
|
-
* @example
|
|
183
|
-
* service.model = 'users';
|
|
184
|
-
*/
|
|
185
|
-
set model(model) {
|
|
186
|
-
this._validateModelName(model);
|
|
187
|
-
this._nest.update(nest => ({
|
|
188
|
-
...nest,
|
|
189
|
-
model
|
|
190
|
-
}));
|
|
191
|
-
}
|
|
192
350
|
/**
|
|
193
351
|
* Set the page number for pagination
|
|
194
352
|
* Must be a positive integer greater than 0
|
|
@@ -205,19 +363,35 @@ class NestService {
|
|
|
205
363
|
page
|
|
206
364
|
}));
|
|
207
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Set the resource name for the query
|
|
368
|
+
* Must be a non-empty string
|
|
369
|
+
*
|
|
370
|
+
* @param {string} resource - The API resource name (e.g., 'users', 'posts')
|
|
371
|
+
* @throws {InvalidResourceNameError} If resource is not a non-empty string
|
|
372
|
+
* @example
|
|
373
|
+
* service.resource = 'users';
|
|
374
|
+
*/
|
|
375
|
+
set resource(resource) {
|
|
376
|
+
this._validateResourceName(resource);
|
|
377
|
+
this._nest.update(nest => ({
|
|
378
|
+
...nest,
|
|
379
|
+
resource
|
|
380
|
+
}));
|
|
381
|
+
}
|
|
208
382
|
_clone(obj) {
|
|
209
383
|
return JSON.parse(JSON.stringify(obj));
|
|
210
384
|
}
|
|
211
385
|
/**
|
|
212
|
-
* Validates that the
|
|
386
|
+
* Validates that the limit is a positive integer
|
|
213
387
|
*
|
|
214
|
-
* @param {
|
|
215
|
-
* @throws {
|
|
388
|
+
* @param {number} limit - The limit value to validate
|
|
389
|
+
* @throws {InvalidLimitError} If limit is not a positive integer
|
|
216
390
|
* @private
|
|
217
391
|
*/
|
|
218
|
-
|
|
219
|
-
if (!
|
|
220
|
-
throw new
|
|
392
|
+
_validateLimit(limit) {
|
|
393
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
394
|
+
throw new InvalidLimitError(limit);
|
|
221
395
|
}
|
|
222
396
|
}
|
|
223
397
|
/**
|
|
@@ -233,15 +407,15 @@ class NestService {
|
|
|
233
407
|
}
|
|
234
408
|
}
|
|
235
409
|
/**
|
|
236
|
-
* Validates that the
|
|
410
|
+
* Validates that the resource name is a non-empty string
|
|
237
411
|
*
|
|
238
|
-
* @param {
|
|
239
|
-
* @throws {
|
|
412
|
+
* @param {string} resource - The resource name to validate
|
|
413
|
+
* @throws {InvalidResourceNameError} If resource is not a non-empty string
|
|
240
414
|
* @private
|
|
241
415
|
*/
|
|
242
|
-
|
|
243
|
-
if (!
|
|
244
|
-
throw new
|
|
416
|
+
_validateResourceName(resource) {
|
|
417
|
+
if (!resource || typeof resource !== 'string' || resource.trim().length === 0) {
|
|
418
|
+
throw new InvalidResourceNameError(resource);
|
|
245
419
|
}
|
|
246
420
|
}
|
|
247
421
|
/**
|
|
@@ -300,7 +474,7 @@ class NestService {
|
|
|
300
474
|
* Add resources to include with the request
|
|
301
475
|
* Automatically prevents duplicate includes
|
|
302
476
|
*
|
|
303
|
-
* @param {string[]} includes - Array of
|
|
477
|
+
* @param {string[]} includes - Array of resource names to include in the response
|
|
304
478
|
* @return {void}
|
|
305
479
|
* @example
|
|
306
480
|
* service.addIncludes(['profile', 'posts']);
|
|
@@ -316,6 +490,56 @@ class NestService {
|
|
|
316
490
|
};
|
|
317
491
|
});
|
|
318
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* Add filters with explicit operators (NestJS only)
|
|
495
|
+
* Automatically prevents duplicate operator filters for the same field + operator combination
|
|
496
|
+
*
|
|
497
|
+
* @param {IOperatorFilter[]} filters - Array of operator filter configurations
|
|
498
|
+
* @return {void}
|
|
499
|
+
* @example
|
|
500
|
+
* import { FilterOperatorEnum } from 'ng-qubee';
|
|
501
|
+
* service.addOperatorFilters([{ field: 'age', operator: FilterOperatorEnum.GTE, values: [18] }]);
|
|
502
|
+
*/
|
|
503
|
+
addOperatorFilters(filters) {
|
|
504
|
+
this._nest.update(nest => {
|
|
505
|
+
const merged = [...nest.operatorFilters];
|
|
506
|
+
filters.forEach(newFilter => {
|
|
507
|
+
const existingIdx = merged.findIndex(f => f.field === newFilter.field && f.operator === newFilter.operator);
|
|
508
|
+
if (existingIdx > -1) {
|
|
509
|
+
const existingValues = merged[existingIdx].values;
|
|
510
|
+
merged[existingIdx] = {
|
|
511
|
+
...merged[existingIdx],
|
|
512
|
+
values: Array.from(new Set([...existingValues, ...newFilter.values]))
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
else {
|
|
516
|
+
merged.push({ ...newFilter });
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
return {
|
|
520
|
+
...nest,
|
|
521
|
+
operatorFilters: merged
|
|
522
|
+
};
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Add flat field selection columns (NestJS only)
|
|
527
|
+
* Automatically prevents duplicate select fields
|
|
528
|
+
*
|
|
529
|
+
* @param {string[]} fields - Array of column names to select
|
|
530
|
+
* @return {void}
|
|
531
|
+
* @example
|
|
532
|
+
* service.addSelect(['id', 'name', 'email']);
|
|
533
|
+
*/
|
|
534
|
+
addSelect(fields) {
|
|
535
|
+
this._nest.update(nest => {
|
|
536
|
+
const uniqueSelect = Array.from(new Set([...nest.select, ...fields]));
|
|
537
|
+
return {
|
|
538
|
+
...nest,
|
|
539
|
+
select: uniqueSelect
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
}
|
|
319
543
|
/**
|
|
320
544
|
* Add a field that should be used for sorting data
|
|
321
545
|
*
|
|
@@ -390,6 +614,49 @@ class NestService {
|
|
|
390
614
|
includes: nest.includes.filter(v => !includes.includes(v))
|
|
391
615
|
}));
|
|
392
616
|
}
|
|
617
|
+
/**
|
|
618
|
+
* Remove operator filters by field name (NestJS only)
|
|
619
|
+
*
|
|
620
|
+
* @param {...string[]} fields - Field names of operator filters to remove
|
|
621
|
+
* @return {void}
|
|
622
|
+
* @example
|
|
623
|
+
* service.deleteOperatorFilters('age');
|
|
624
|
+
* service.deleteOperatorFilters('price', 'quantity');
|
|
625
|
+
*/
|
|
626
|
+
deleteOperatorFilters(...fields) {
|
|
627
|
+
this._nest.update(nest => ({
|
|
628
|
+
...nest,
|
|
629
|
+
operatorFilters: nest.operatorFilters.filter(f => !fields.includes(f.field))
|
|
630
|
+
}));
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Remove the search term from the state (NestJS only)
|
|
634
|
+
*
|
|
635
|
+
* @return {void}
|
|
636
|
+
* @example
|
|
637
|
+
* service.deleteSearch();
|
|
638
|
+
*/
|
|
639
|
+
deleteSearch() {
|
|
640
|
+
this._nest.update(nest => ({
|
|
641
|
+
...nest,
|
|
642
|
+
search: ''
|
|
643
|
+
}));
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Remove flat field selections from the state (NestJS only)
|
|
647
|
+
*
|
|
648
|
+
* @param {...string[]} fields - Field names to remove from selection
|
|
649
|
+
* @return {void}
|
|
650
|
+
* @example
|
|
651
|
+
* service.deleteSelect('email');
|
|
652
|
+
* service.deleteSelect('name', 'email');
|
|
653
|
+
*/
|
|
654
|
+
deleteSelect(...fields) {
|
|
655
|
+
this._nest.update(nest => ({
|
|
656
|
+
...nest,
|
|
657
|
+
select: nest.select.filter(f => !fields.includes(f))
|
|
658
|
+
}));
|
|
659
|
+
}
|
|
393
660
|
/**
|
|
394
661
|
* Remove sorts from the request by field name
|
|
395
662
|
*
|
|
@@ -402,7 +669,7 @@ class NestService {
|
|
|
402
669
|
deleteSorts(...sorts) {
|
|
403
670
|
const s = [...this._nest().sorts];
|
|
404
671
|
sorts.forEach(field => {
|
|
405
|
-
const p =
|
|
672
|
+
const p = s.findIndex(sort => sort.field === field);
|
|
406
673
|
if (p > -1) {
|
|
407
674
|
s.splice(p, 1);
|
|
408
675
|
}
|
|
@@ -412,6 +679,20 @@ class NestService {
|
|
|
412
679
|
sorts: s
|
|
413
680
|
}));
|
|
414
681
|
}
|
|
682
|
+
/**
|
|
683
|
+
* Set the full-text search term (NestJS only)
|
|
684
|
+
*
|
|
685
|
+
* @param {string} search - The search term
|
|
686
|
+
* @return {void}
|
|
687
|
+
* @example
|
|
688
|
+
* service.setSearch('john doe');
|
|
689
|
+
*/
|
|
690
|
+
setSearch(search) {
|
|
691
|
+
this._nest.update(nest => ({
|
|
692
|
+
...nest,
|
|
693
|
+
search
|
|
694
|
+
}));
|
|
695
|
+
}
|
|
415
696
|
/**
|
|
416
697
|
* Reset the query builder state to initial values
|
|
417
698
|
* Clears all fields, filters, includes, sorts, and resets pagination
|
|
@@ -419,158 +700,121 @@ class NestService {
|
|
|
419
700
|
* @return {void}
|
|
420
701
|
* @example
|
|
421
702
|
* service.reset();
|
|
422
|
-
* // State is now: { baseUrl: '', fields: {}, filters: {}, includes: [], limit: 15, model: '', page: 1, sorts: [] }
|
|
423
703
|
*/
|
|
424
704
|
reset() {
|
|
425
705
|
this._nest.update(_ => this._clone(INITIAL_STATE));
|
|
426
706
|
}
|
|
427
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
428
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
707
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NestService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
708
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NestService });
|
|
429
709
|
}
|
|
430
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
710
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NestService, decorators: [{
|
|
431
711
|
type: Injectable
|
|
432
712
|
}], ctorParameters: () => [] });
|
|
433
713
|
|
|
434
714
|
class NgQubeeService {
|
|
435
715
|
_nestService;
|
|
716
|
+
/**
|
|
717
|
+
* The active pagination driver
|
|
718
|
+
*/
|
|
719
|
+
_driver;
|
|
720
|
+
/**
|
|
721
|
+
* Resolved query parameter key name options
|
|
722
|
+
*/
|
|
436
723
|
_options;
|
|
437
724
|
/**
|
|
438
|
-
*
|
|
725
|
+
* The request strategy that builds URIs for the active driver
|
|
726
|
+
*/
|
|
727
|
+
_requestStrategy;
|
|
728
|
+
/**
|
|
729
|
+
* Internal BehaviorSubject that holds the latest generated URI
|
|
439
730
|
*/
|
|
440
|
-
_uri = '';
|
|
441
731
|
_uri$ = new BehaviorSubject('');
|
|
732
|
+
/**
|
|
733
|
+
* Observable that emits non-empty generated URIs
|
|
734
|
+
*/
|
|
442
735
|
uri$ = this._uri$.asObservable().pipe(filter(uri => !!uri));
|
|
443
|
-
constructor(_nestService, options = {}) {
|
|
736
|
+
constructor(_nestService, requestStrategy, driver, options = {}) {
|
|
444
737
|
this._nestService = _nestService;
|
|
738
|
+
this._driver = driver;
|
|
445
739
|
this._options = new QueryBuilderOptions(options);
|
|
740
|
+
this._requestStrategy = requestStrategy;
|
|
446
741
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
const f = {};
|
|
458
|
-
for (const k in s.fields) {
|
|
459
|
-
if (s.fields.hasOwnProperty(k)) {
|
|
460
|
-
// Check if the key is the model or is declared in "includes".
|
|
461
|
-
// If not, it means that has not been selected anywhere and that will cause an error on the API
|
|
462
|
-
if (k !== s.model && !s.includes.includes(k)) {
|
|
463
|
-
throw new UnselectableModelError(k);
|
|
464
|
-
}
|
|
465
|
-
Object.assign(f, { [`${this._options.fields}[${k}]`]: s.fields[k].join(',') });
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
const param = `${this._prepend(s.model)}${qs.stringify(f, { encode: false })}`;
|
|
469
|
-
this._uri += param;
|
|
470
|
-
return param;
|
|
471
|
-
}
|
|
472
|
-
_parseFilters(s) {
|
|
473
|
-
const keys = Object.keys(s.filters);
|
|
474
|
-
if (!keys.length) {
|
|
475
|
-
return this._uri;
|
|
742
|
+
/**
|
|
743
|
+
* Assert that the active driver is one of the allowed drivers
|
|
744
|
+
*
|
|
745
|
+
* @param allowed - The allowed drivers
|
|
746
|
+
* @param error - The error to throw if the driver is not allowed
|
|
747
|
+
* @throws The provided error if the active driver is not in the allowed list
|
|
748
|
+
*/
|
|
749
|
+
_assertDriver(allowed, error) {
|
|
750
|
+
if (!allowed.includes(this._driver)) {
|
|
751
|
+
throw error;
|
|
476
752
|
}
|
|
477
|
-
const f = {
|
|
478
|
-
[`${this._options.filters}`]: keys.reduce((acc, key) => {
|
|
479
|
-
return Object.assign(acc, { [key]: s.filters[key].join(',') });
|
|
480
|
-
}, {})
|
|
481
|
-
};
|
|
482
|
-
const param = `${this._prepend(s.model)}${qs.stringify(f, { encode: false })}`;
|
|
483
|
-
this._uri += param;
|
|
484
|
-
return param;
|
|
485
753
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
754
|
+
/**
|
|
755
|
+
* Add fields to the select statement for the given model (Spatie only)
|
|
756
|
+
*
|
|
757
|
+
* @param model - Model that holds the fields
|
|
758
|
+
* @param fields - Fields to select
|
|
759
|
+
* @returns {this}
|
|
760
|
+
* @throws {UnsupportedFieldSelectionError} If the active driver does not support per-model field selection
|
|
761
|
+
*/
|
|
762
|
+
addFields(model, fields) {
|
|
763
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedFieldSelectionError());
|
|
764
|
+
if (!fields.length) {
|
|
765
|
+
return this;
|
|
489
766
|
}
|
|
490
|
-
|
|
491
|
-
this
|
|
492
|
-
return param;
|
|
493
|
-
}
|
|
494
|
-
_parseLimit(s) {
|
|
495
|
-
const param = `${this._prepend(s.model)}${this._options.limit}=${s.limit}`;
|
|
496
|
-
this._uri += param;
|
|
497
|
-
return param;
|
|
498
|
-
}
|
|
499
|
-
_parsePage(s) {
|
|
500
|
-
const param = `${this._prepend(s.model)}${this._options.page}=${s.page}`;
|
|
501
|
-
this._uri += param;
|
|
502
|
-
return param;
|
|
503
|
-
}
|
|
504
|
-
_parseSort(s) {
|
|
505
|
-
let param = '';
|
|
506
|
-
if (!s.sorts.length) {
|
|
507
|
-
return param;
|
|
508
|
-
}
|
|
509
|
-
param = `${this._prepend(s.model)}${this._options.sort}=`;
|
|
510
|
-
s.sorts.forEach((sort, idx) => {
|
|
511
|
-
param += `${sort.order === SortEnum.DESC ? '-' : ''}${sort.field}`;
|
|
512
|
-
if (idx < s.sorts.length - 1) {
|
|
513
|
-
param += ',';
|
|
514
|
-
}
|
|
515
|
-
});
|
|
516
|
-
this._uri += param;
|
|
517
|
-
return param;
|
|
518
|
-
}
|
|
519
|
-
_parse(s) {
|
|
520
|
-
if (!s.model) {
|
|
521
|
-
throw new Error('Set the model property BEFORE adding filters or calling the url() / get() methods');
|
|
522
|
-
}
|
|
523
|
-
// Cleanup the previously generated URI
|
|
524
|
-
this._uri = '';
|
|
525
|
-
this._parseIncludes(s);
|
|
526
|
-
this._parseFields(s);
|
|
527
|
-
this._parseFilters(s);
|
|
528
|
-
this._parseLimit(s);
|
|
529
|
-
this._parsePage(s);
|
|
530
|
-
this._parseSort(s);
|
|
531
|
-
return this._uri;
|
|
532
|
-
}
|
|
533
|
-
_prepend(model) {
|
|
534
|
-
return this._uri ? '&' : `/${model}?`;
|
|
767
|
+
this._nestService.addFields({ [model]: fields });
|
|
768
|
+
return this;
|
|
535
769
|
}
|
|
536
770
|
/**
|
|
537
|
-
* Add
|
|
771
|
+
* Add a filter with the given value(s) (Spatie and NestJS only)
|
|
538
772
|
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
773
|
+
* Produces: `filter[field]=value` (Spatie) or `filter.field=value` (NestJS)
|
|
774
|
+
*
|
|
775
|
+
* @param {string} field - Name of the field to filter
|
|
776
|
+
* @param {(string | number | boolean)[]} values - The needle(s)
|
|
541
777
|
* @returns {this}
|
|
778
|
+
* @throws {UnsupportedFilterError} If the active driver does not support filters
|
|
542
779
|
*/
|
|
543
|
-
|
|
544
|
-
|
|
780
|
+
addFilter(field, ...values) {
|
|
781
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedFilterError());
|
|
782
|
+
if (!values.length) {
|
|
545
783
|
return this;
|
|
546
784
|
}
|
|
547
|
-
this._nestService.
|
|
785
|
+
this._nestService.addFilters({
|
|
786
|
+
[field]: values
|
|
787
|
+
});
|
|
548
788
|
return this;
|
|
549
789
|
}
|
|
550
790
|
/**
|
|
551
|
-
* Add a filter with
|
|
552
|
-
*
|
|
791
|
+
* Add a filter with an explicit operator (NestJS only)
|
|
792
|
+
*
|
|
793
|
+
* Produces: `filter.field=$operator:value`
|
|
553
794
|
*
|
|
554
|
-
* @param {string} field Name of the field to filter
|
|
555
|
-
* @param {
|
|
795
|
+
* @param {string} field - Name of the field to filter
|
|
796
|
+
* @param {FilterOperatorEnum} operator - The filter operator to apply
|
|
797
|
+
* @param {(string | number | boolean)[]} values - The value(s) for the filter
|
|
556
798
|
* @returns {this}
|
|
799
|
+
* @throws {UnsupportedFilterOperatorError} If the active driver does not support filter operators
|
|
557
800
|
*/
|
|
558
|
-
|
|
801
|
+
addFilterOperator(field, operator, ...values) {
|
|
802
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedFilterOperatorError());
|
|
559
803
|
if (!values.length) {
|
|
560
804
|
return this;
|
|
561
805
|
}
|
|
562
|
-
this._nestService.
|
|
563
|
-
[field]: values
|
|
564
|
-
});
|
|
806
|
+
this._nestService.addOperatorFilters([{ field, operator, values }]);
|
|
565
807
|
return this;
|
|
566
808
|
}
|
|
567
809
|
/**
|
|
568
|
-
* Add related entities to include in the request
|
|
810
|
+
* Add related entities to include in the request (Spatie only)
|
|
569
811
|
*
|
|
570
|
-
* @param {string[]} models
|
|
571
|
-
* @returns
|
|
812
|
+
* @param {string[]} models - Models to include
|
|
813
|
+
* @returns {this}
|
|
814
|
+
* @throws {UnsupportedIncludesError} If the active driver does not support includes
|
|
572
815
|
*/
|
|
573
816
|
addIncludes(...models) {
|
|
817
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedIncludesError());
|
|
574
818
|
if (!models.length) {
|
|
575
819
|
return this;
|
|
576
820
|
}
|
|
@@ -578,13 +822,32 @@ class NgQubeeService {
|
|
|
578
822
|
return this;
|
|
579
823
|
}
|
|
580
824
|
/**
|
|
581
|
-
* Add
|
|
825
|
+
* Add flat field selection (NestJS only)
|
|
826
|
+
*
|
|
827
|
+
* Produces: `select=col1,col2`
|
|
582
828
|
*
|
|
583
|
-
* @param
|
|
584
|
-
* @param {SortEnum} order A value from the SortEnum enumeration
|
|
829
|
+
* @param {string[]} fields - Fields to select
|
|
585
830
|
* @returns {this}
|
|
831
|
+
* @throws {UnsupportedSelectError} If the active driver does not support flat field selection
|
|
832
|
+
*/
|
|
833
|
+
addSelect(...fields) {
|
|
834
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedSelectError());
|
|
835
|
+
if (!fields.length) {
|
|
836
|
+
return this;
|
|
837
|
+
}
|
|
838
|
+
this._nestService.addSelect(fields);
|
|
839
|
+
return this;
|
|
840
|
+
}
|
|
841
|
+
/**
|
|
842
|
+
* Add a field with a sort criteria (Spatie and NestJS only)
|
|
843
|
+
*
|
|
844
|
+
* @param field - Field to use for sorting
|
|
845
|
+
* @param {SortEnum} order - A value from the SortEnum enumeration
|
|
846
|
+
* @returns {this}
|
|
847
|
+
* @throws {UnsupportedSortError} If the active driver does not support sorts
|
|
586
848
|
*/
|
|
587
849
|
addSort(field, order) {
|
|
850
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedSortError());
|
|
588
851
|
this._nestService.addSort({
|
|
589
852
|
field,
|
|
590
853
|
order
|
|
@@ -592,7 +855,7 @@ class NgQubeeService {
|
|
|
592
855
|
return this;
|
|
593
856
|
}
|
|
594
857
|
/**
|
|
595
|
-
* Delete selected fields for the given models in the current query builder state
|
|
858
|
+
* Delete selected fields for the given models in the current query builder state (Spatie only)
|
|
596
859
|
*
|
|
597
860
|
* ```
|
|
598
861
|
* ngQubeeService.deleteFields({
|
|
@@ -601,25 +864,29 @@ class NgQubeeService {
|
|
|
601
864
|
* });
|
|
602
865
|
* ```
|
|
603
866
|
*
|
|
604
|
-
* @param {IFields} fields
|
|
605
|
-
* @returns
|
|
867
|
+
* @param {IFields} fields - Object mapping model names to field arrays to remove
|
|
868
|
+
* @returns {this}
|
|
869
|
+
* @throws {UnsupportedFieldSelectionError} If the active driver does not support per-model field selection
|
|
606
870
|
*/
|
|
607
871
|
deleteFields(fields) {
|
|
872
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedFieldSelectionError());
|
|
608
873
|
this._nestService.deleteFields(fields);
|
|
609
874
|
return this;
|
|
610
875
|
}
|
|
611
876
|
/**
|
|
612
|
-
* Delete selected fields for the given model in the current query builder state
|
|
877
|
+
* Delete selected fields for the given model in the current query builder state (Spatie only)
|
|
613
878
|
*
|
|
614
879
|
* ```
|
|
615
|
-
* ngQubeeService.deleteFieldsByModel('users', 'email', 'password'
|
|
880
|
+
* ngQubeeService.deleteFieldsByModel('users', 'email', 'password');
|
|
616
881
|
* ```
|
|
617
882
|
*
|
|
618
|
-
* @param model Model that holds the fields
|
|
619
|
-
* @param {string[]} fields Fields to delete from the state
|
|
883
|
+
* @param model - Model that holds the fields
|
|
884
|
+
* @param {string[]} fields - Fields to delete from the state
|
|
620
885
|
* @returns {this}
|
|
886
|
+
* @throws {UnsupportedFieldSelectionError} If the active driver does not support per-model field selection
|
|
621
887
|
*/
|
|
622
888
|
deleteFieldsByModel(model, ...fields) {
|
|
889
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedFieldSelectionError());
|
|
623
890
|
if (!fields.length) {
|
|
624
891
|
return this;
|
|
625
892
|
}
|
|
@@ -629,12 +896,14 @@ class NgQubeeService {
|
|
|
629
896
|
return this;
|
|
630
897
|
}
|
|
631
898
|
/**
|
|
632
|
-
* Remove given filters from the query builder state
|
|
899
|
+
* Remove given filters from the query builder state (Spatie and NestJS only)
|
|
633
900
|
*
|
|
634
|
-
* @param {string[]} filters Filters to remove
|
|
901
|
+
* @param {string[]} filters - Filters to remove
|
|
635
902
|
* @returns {this}
|
|
903
|
+
* @throws {UnsupportedFilterError} If the active driver does not support filters
|
|
636
904
|
*/
|
|
637
905
|
deleteFilters(...filters) {
|
|
906
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedFilterError());
|
|
638
907
|
if (!filters.length) {
|
|
639
908
|
return this;
|
|
640
909
|
}
|
|
@@ -642,12 +911,14 @@ class NgQubeeService {
|
|
|
642
911
|
return this;
|
|
643
912
|
}
|
|
644
913
|
/**
|
|
645
|
-
* Remove selected related models from the query builder state
|
|
914
|
+
* Remove selected related models from the query builder state (Spatie only)
|
|
646
915
|
*
|
|
647
|
-
* @param {string[]} includes Models to remove
|
|
648
|
-
* @returns
|
|
916
|
+
* @param {string[]} includes - Models to remove
|
|
917
|
+
* @returns {this}
|
|
918
|
+
* @throws {UnsupportedIncludesError} If the active driver does not support includes
|
|
649
919
|
*/
|
|
650
920
|
deleteIncludes(...includes) {
|
|
921
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedIncludesError());
|
|
651
922
|
if (!includes.length) {
|
|
652
923
|
return this;
|
|
653
924
|
}
|
|
@@ -655,23 +926,71 @@ class NgQubeeService {
|
|
|
655
926
|
return this;
|
|
656
927
|
}
|
|
657
928
|
/**
|
|
658
|
-
* Remove
|
|
929
|
+
* Remove operator filters by field name (NestJS only)
|
|
930
|
+
*
|
|
931
|
+
* @param {string[]} fields - Field names of operator filters to remove
|
|
932
|
+
* @returns {this}
|
|
933
|
+
* @throws {UnsupportedFilterOperatorError} If the active driver does not support filter operators
|
|
934
|
+
*/
|
|
935
|
+
deleteOperatorFilters(...fields) {
|
|
936
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedFilterOperatorError());
|
|
937
|
+
if (!fields.length) {
|
|
938
|
+
return this;
|
|
939
|
+
}
|
|
940
|
+
this._nestService.deleteOperatorFilters(...fields);
|
|
941
|
+
return this;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Remove search term from the query builder state (NestJS only)
|
|
945
|
+
*
|
|
946
|
+
* @returns {this}
|
|
947
|
+
* @throws {UnsupportedSearchError} If the active driver does not support search
|
|
948
|
+
*/
|
|
949
|
+
deleteSearch() {
|
|
950
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedSearchError());
|
|
951
|
+
this._nestService.deleteSearch();
|
|
952
|
+
return this;
|
|
953
|
+
}
|
|
954
|
+
/**
|
|
955
|
+
* Remove flat field selections from the query builder state (NestJS only)
|
|
956
|
+
*
|
|
957
|
+
* @param {string[]} fields - Fields to remove from selection
|
|
958
|
+
* @returns {this}
|
|
959
|
+
* @throws {UnsupportedSelectError} If the active driver does not support flat field selection
|
|
960
|
+
*/
|
|
961
|
+
deleteSelect(...fields) {
|
|
962
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedSelectError());
|
|
963
|
+
if (!fields.length) {
|
|
964
|
+
return this;
|
|
965
|
+
}
|
|
966
|
+
this._nestService.deleteSelect(...fields);
|
|
967
|
+
return this;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Remove sort rules from the query builder state (Spatie and NestJS only)
|
|
659
971
|
*
|
|
660
|
-
* @param sorts Fields used for sorting to remove
|
|
972
|
+
* @param sorts - Fields used for sorting to remove
|
|
661
973
|
* @returns {this}
|
|
974
|
+
* @throws {UnsupportedSortError} If the active driver does not support sorts
|
|
662
975
|
*/
|
|
663
976
|
deleteSorts(...sorts) {
|
|
977
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedSortError());
|
|
664
978
|
this._nestService.deleteSorts(...sorts);
|
|
665
979
|
return this;
|
|
666
980
|
}
|
|
667
981
|
/**
|
|
668
|
-
* Generate
|
|
982
|
+
* Generate a URI accordingly to the given data and active driver
|
|
669
983
|
*
|
|
670
|
-
* @returns {Observable<string>} An observable that emits the generated
|
|
984
|
+
* @returns {Observable<string>} An observable that emits the generated URI
|
|
671
985
|
*/
|
|
672
986
|
generateUri() {
|
|
673
|
-
|
|
674
|
-
|
|
987
|
+
try {
|
|
988
|
+
this._uri$.next(this._requestStrategy.buildUri(this._nestService.nest(), this._options));
|
|
989
|
+
return this.uri$;
|
|
990
|
+
}
|
|
991
|
+
catch (error) {
|
|
992
|
+
return throwError(() => error);
|
|
993
|
+
}
|
|
675
994
|
}
|
|
676
995
|
/**
|
|
677
996
|
* Clear the current state and reset the Query Builder to a fresh, clean condition
|
|
@@ -683,9 +1002,9 @@ class NgQubeeService {
|
|
|
683
1002
|
return this;
|
|
684
1003
|
}
|
|
685
1004
|
/**
|
|
686
|
-
* Set the base
|
|
1005
|
+
* Set the base URL to use for composing the address
|
|
687
1006
|
*
|
|
688
|
-
* @param {string} baseUrl
|
|
1007
|
+
* @param {string} baseUrl - The base URL
|
|
689
1008
|
* @returns {this}
|
|
690
1009
|
*/
|
|
691
1010
|
setBaseUrl(baseUrl) {
|
|
@@ -695,7 +1014,7 @@ class NgQubeeService {
|
|
|
695
1014
|
/**
|
|
696
1015
|
* Set the items per page number
|
|
697
1016
|
*
|
|
698
|
-
* @param limit
|
|
1017
|
+
* @param limit - Number of items per page
|
|
699
1018
|
* @returns {this}
|
|
700
1019
|
*/
|
|
701
1020
|
setLimit(limit) {
|
|
@@ -703,91 +1022,698 @@ class NgQubeeService {
|
|
|
703
1022
|
return this;
|
|
704
1023
|
}
|
|
705
1024
|
/**
|
|
706
|
-
* Set the
|
|
707
|
-
* - I.e. the model "users" will return /users
|
|
1025
|
+
* Set the page that the backend will use to paginate the result set
|
|
708
1026
|
*
|
|
709
|
-
* @param
|
|
1027
|
+
* @param page - Page number
|
|
710
1028
|
* @returns {this}
|
|
711
1029
|
*/
|
|
712
|
-
|
|
713
|
-
this._nestService.
|
|
1030
|
+
setPage(page) {
|
|
1031
|
+
this._nestService.page = page;
|
|
714
1032
|
return this;
|
|
715
1033
|
}
|
|
716
1034
|
/**
|
|
717
|
-
* Set the
|
|
1035
|
+
* Set the API resource to run the query against
|
|
718
1036
|
*
|
|
719
|
-
* @param
|
|
1037
|
+
* @param {string} resource - Resource name (e.g. 'users' produces /users)
|
|
720
1038
|
* @returns {this}
|
|
721
1039
|
*/
|
|
722
|
-
|
|
723
|
-
this._nestService.
|
|
1040
|
+
setResource(resource) {
|
|
1041
|
+
this._nestService.resource = resource;
|
|
724
1042
|
return this;
|
|
725
1043
|
}
|
|
726
|
-
|
|
727
|
-
|
|
1044
|
+
/**
|
|
1045
|
+
* Set the search term for full-text search (NestJS only)
|
|
1046
|
+
*
|
|
1047
|
+
* Produces: `search=term`
|
|
1048
|
+
*
|
|
1049
|
+
* @param {string} search - The search term
|
|
1050
|
+
* @returns {this}
|
|
1051
|
+
* @throws {UnsupportedSearchError} If the active driver does not support search
|
|
1052
|
+
*/
|
|
1053
|
+
setSearch(search) {
|
|
1054
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedSearchError());
|
|
1055
|
+
this._nestService.setSearch(search);
|
|
1056
|
+
return this;
|
|
1057
|
+
}
|
|
1058
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeService, deps: [{ token: NestService }, { token: 'REQUEST_STRATEGY' }, { token: 'DRIVER' }, { token: 'QUERY_PARAMS_CONFIG', optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1059
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeService });
|
|
728
1060
|
}
|
|
729
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1061
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeService, decorators: [{
|
|
730
1062
|
type: Injectable
|
|
731
1063
|
}], ctorParameters: () => [{ type: NestService }, { type: undefined, decorators: [{
|
|
1064
|
+
type: Inject,
|
|
1065
|
+
args: ['REQUEST_STRATEGY']
|
|
1066
|
+
}] }, { type: DriverEnum, decorators: [{
|
|
1067
|
+
type: Inject,
|
|
1068
|
+
args: ['DRIVER']
|
|
1069
|
+
}] }, { type: undefined, decorators: [{
|
|
732
1070
|
type: Inject,
|
|
733
1071
|
args: ['QUERY_PARAMS_CONFIG']
|
|
734
1072
|
}, {
|
|
735
1073
|
type: Optional
|
|
736
1074
|
}] }] });
|
|
737
1075
|
|
|
738
|
-
class ResponseOptions {
|
|
739
|
-
currentPage;
|
|
740
|
-
data;
|
|
741
|
-
firstPageUrl;
|
|
742
|
-
from;
|
|
743
|
-
lastPage;
|
|
744
|
-
lastPageUrl;
|
|
745
|
-
nextPageUrl;
|
|
746
|
-
path;
|
|
747
|
-
perPage;
|
|
748
|
-
prevPageUrl;
|
|
749
|
-
to;
|
|
750
|
-
total;
|
|
751
|
-
constructor(options) {
|
|
752
|
-
this.currentPage = options.currentPage || 'current_page';
|
|
753
|
-
this.data = options.data || 'data';
|
|
754
|
-
this.firstPageUrl = options.firstPageUrl || 'first_page_url';
|
|
755
|
-
this.from = options.from || 'from';
|
|
756
|
-
this.lastPage = options.lastPage || 'last_page';
|
|
757
|
-
this.lastPageUrl = options.lastPageUrl || 'last_page_url';
|
|
758
|
-
this.nextPageUrl = options.nextPageUrl || 'next_page_url';
|
|
759
|
-
this.path = options.path || 'path';
|
|
760
|
-
this.perPage = options.perPage || 'per_page';
|
|
761
|
-
this.prevPageUrl = options.prevPageUrl || 'prev_page_url';
|
|
762
|
-
this.to = options.to || 'to';
|
|
763
|
-
this.total = options.total || 'total';
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
1076
|
class PaginationService {
|
|
1077
|
+
/**
|
|
1078
|
+
* Resolved response key name options
|
|
1079
|
+
*/
|
|
768
1080
|
_options;
|
|
769
|
-
|
|
1081
|
+
/**
|
|
1082
|
+
* The response strategy that parses responses for the active driver
|
|
1083
|
+
*/
|
|
1084
|
+
_responseStrategy;
|
|
1085
|
+
constructor(responseStrategy, options = {}) {
|
|
770
1086
|
this._options = new ResponseOptions(options);
|
|
1087
|
+
this._responseStrategy = responseStrategy;
|
|
771
1088
|
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Transform a raw API response into a typed PaginatedCollection
|
|
1091
|
+
*
|
|
1092
|
+
* Delegates to the active driver's response strategy for parsing.
|
|
1093
|
+
*
|
|
1094
|
+
* @param response - The raw API response object
|
|
1095
|
+
* @returns A typed PaginatedCollection instance
|
|
1096
|
+
*/
|
|
772
1097
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
773
1098
|
paginate(response) {
|
|
774
|
-
return
|
|
1099
|
+
return this._responseStrategy.paginate(response, this._options);
|
|
775
1100
|
}
|
|
776
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
777
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "
|
|
1101
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PaginationService, deps: [{ token: 'RESPONSE_STRATEGY' }, { token: 'RESPONSE_OPTIONS', optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1102
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PaginationService });
|
|
778
1103
|
}
|
|
779
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1104
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PaginationService, decorators: [{
|
|
780
1105
|
type: Injectable
|
|
781
1106
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1107
|
+
type: Inject,
|
|
1108
|
+
args: ['RESPONSE_STRATEGY']
|
|
1109
|
+
}] }, { type: undefined, decorators: [{
|
|
782
1110
|
type: Inject,
|
|
783
1111
|
args: ['RESPONSE_OPTIONS']
|
|
784
1112
|
}, {
|
|
785
1113
|
type: Optional
|
|
786
1114
|
}] }] });
|
|
787
1115
|
|
|
1116
|
+
/**
|
|
1117
|
+
* Request strategy for the Laravel (pagination-only) driver
|
|
1118
|
+
*
|
|
1119
|
+
* Generates simple pagination URIs:
|
|
1120
|
+
* - `/{resource}?limit=N&page=N`
|
|
1121
|
+
*
|
|
1122
|
+
* Filters, sorts, fields, includes, search, and select in state are ignored.
|
|
1123
|
+
*/
|
|
1124
|
+
class LaravelRequestStrategy {
|
|
1125
|
+
/**
|
|
1126
|
+
* Build a pagination-only URI from the given state
|
|
1127
|
+
*
|
|
1128
|
+
* @param state - The current query builder state
|
|
1129
|
+
* @param options - The query parameter key name configuration
|
|
1130
|
+
* @returns The composed URI string
|
|
1131
|
+
* @throws Error if resource is not set
|
|
1132
|
+
*/
|
|
1133
|
+
buildUri(state, options) {
|
|
1134
|
+
if (!state.resource) {
|
|
1135
|
+
throw new Error('Set the resource property BEFORE calling the url() / get() methods');
|
|
1136
|
+
}
|
|
1137
|
+
const base = state.baseUrl ? `${state.baseUrl}/${state.resource}` : `/${state.resource}`;
|
|
1138
|
+
return `${base}?${options.limit}=${state.limit}&${options.page}=${state.page}`;
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
/**
|
|
1143
|
+
* Response strategy for the Laravel (pagination-only) driver
|
|
1144
|
+
*
|
|
1145
|
+
* Parses flat Laravel pagination responses:
|
|
1146
|
+
* ```json
|
|
1147
|
+
* {
|
|
1148
|
+
* "data": [...],
|
|
1149
|
+
* "current_page": 1,
|
|
1150
|
+
* "total": 100,
|
|
1151
|
+
* "per_page": 15,
|
|
1152
|
+
* "from": 1,
|
|
1153
|
+
* "to": 15,
|
|
1154
|
+
* ...
|
|
1155
|
+
* }
|
|
1156
|
+
* ```
|
|
1157
|
+
*/
|
|
1158
|
+
class LaravelResponseStrategy {
|
|
1159
|
+
/**
|
|
1160
|
+
* Parse a flat Laravel pagination response into a PaginatedCollection
|
|
1161
|
+
*
|
|
1162
|
+
* @param response - The raw API response object
|
|
1163
|
+
* @param options - The response key name configuration
|
|
1164
|
+
* @returns A typed PaginatedCollection instance
|
|
1165
|
+
*/
|
|
1166
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1167
|
+
paginate(response, options) {
|
|
1168
|
+
return new PaginatedCollection(response[options.data], response[options.currentPage], response[options.from], response[options.to], response[options.total], response[options.perPage], response[options.prevPageUrl], response[options.nextPageUrl], response[options.lastPage], response[options.firstPageUrl], response[options.lastPageUrl]);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
var SortEnum;
|
|
1173
|
+
(function (SortEnum) {
|
|
1174
|
+
SortEnum["ASC"] = "asc";
|
|
1175
|
+
SortEnum["DESC"] = "desc";
|
|
1176
|
+
})(SortEnum || (SortEnum = {}));
|
|
1177
|
+
|
|
1178
|
+
/**
|
|
1179
|
+
* Request strategy for the NestJS (nestjs-paginate) driver
|
|
1180
|
+
*
|
|
1181
|
+
* Generates URIs in the NestJS paginate format:
|
|
1182
|
+
* - Simple filters: `filter.field=value`
|
|
1183
|
+
* - Operator filters: `filter.field=$operator:value`
|
|
1184
|
+
* - Sorts: `sortBy=field1:DESC,field2:ASC`
|
|
1185
|
+
* - Select: `select=col1,col2`
|
|
1186
|
+
* - Search: `search=term`
|
|
1187
|
+
* - Pagination: `limit=N&page=N`
|
|
1188
|
+
*
|
|
1189
|
+
* @see https://github.com/ppetzold/nestjs-paginate
|
|
1190
|
+
*/
|
|
1191
|
+
class NestjsRequestStrategy {
|
|
1192
|
+
/**
|
|
1193
|
+
* Accumulator for composing the URI string
|
|
1194
|
+
*/
|
|
1195
|
+
_uri = '';
|
|
1196
|
+
/**
|
|
1197
|
+
* Build a URI string from the given state using the NestJS paginate format
|
|
1198
|
+
*
|
|
1199
|
+
* @param state - The current query builder state
|
|
1200
|
+
* @param options - The query parameter key name configuration
|
|
1201
|
+
* @returns The composed URI string
|
|
1202
|
+
* @throws Error if model is not set
|
|
1203
|
+
*/
|
|
1204
|
+
buildUri(state, options) {
|
|
1205
|
+
if (!state.resource) {
|
|
1206
|
+
throw new Error('Set the resource property BEFORE adding filters or calling the url() / get() methods');
|
|
1207
|
+
}
|
|
1208
|
+
this._uri = '';
|
|
1209
|
+
this._parseFilters(state, options);
|
|
1210
|
+
this._parseOperatorFilters(state, options);
|
|
1211
|
+
this._parseSort(state, options);
|
|
1212
|
+
this._parseSelect(state, options);
|
|
1213
|
+
this._parseSearch(state, options);
|
|
1214
|
+
this._parseLimit(state, options);
|
|
1215
|
+
this._parsePage(state, options);
|
|
1216
|
+
return this._uri;
|
|
1217
|
+
}
|
|
1218
|
+
/**
|
|
1219
|
+
* Parse and append simple filter parameters
|
|
1220
|
+
*
|
|
1221
|
+
* Generates: `filter.field=value1,value2` for each filter
|
|
1222
|
+
*
|
|
1223
|
+
* @param state - The current query builder state
|
|
1224
|
+
* @param options - The query parameter key name configuration
|
|
1225
|
+
*/
|
|
1226
|
+
_parseFilters(state, options) {
|
|
1227
|
+
const keys = Object.keys(state.filters);
|
|
1228
|
+
if (!keys.length) {
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
keys.forEach(key => {
|
|
1232
|
+
const values = state.filters[key].join(',');
|
|
1233
|
+
const param = `${this._prepend(state)}${options.filters}.${key}=${values}`;
|
|
1234
|
+
this._uri += param;
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Parse and append the limit parameter
|
|
1239
|
+
*
|
|
1240
|
+
* @param state - The current query builder state
|
|
1241
|
+
* @param options - The query parameter key name configuration
|
|
1242
|
+
*/
|
|
1243
|
+
_parseLimit(state, options) {
|
|
1244
|
+
const param = `${this._prepend(state)}${options.limit}=${state.limit}`;
|
|
1245
|
+
this._uri += param;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Parse and append operator filter parameters
|
|
1249
|
+
*
|
|
1250
|
+
* Groups operator filters by field and generates:
|
|
1251
|
+
* - Single value: `filter.field=$operator:value`
|
|
1252
|
+
* - Multiple values ($in, $btw): `filter.field=$operator:val1,val2`
|
|
1253
|
+
*
|
|
1254
|
+
* @param state - The current query builder state
|
|
1255
|
+
* @param options - The query parameter key name configuration
|
|
1256
|
+
*/
|
|
1257
|
+
_parseOperatorFilters(state, options) {
|
|
1258
|
+
if (!state.operatorFilters.length) {
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
state.operatorFilters.forEach((opFilter) => {
|
|
1262
|
+
const values = opFilter.values.join(',');
|
|
1263
|
+
const param = `${this._prepend(state)}${options.filters}.${opFilter.field}=${opFilter.operator}:${values}`;
|
|
1264
|
+
this._uri += param;
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Parse and append the page parameter
|
|
1269
|
+
*
|
|
1270
|
+
* @param state - The current query builder state
|
|
1271
|
+
* @param options - The query parameter key name configuration
|
|
1272
|
+
*/
|
|
1273
|
+
_parsePage(state, options) {
|
|
1274
|
+
const param = `${this._prepend(state)}${options.page}=${state.page}`;
|
|
1275
|
+
this._uri += param;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Parse and append the search parameter
|
|
1279
|
+
*
|
|
1280
|
+
* Generates: `search=term`
|
|
1281
|
+
*
|
|
1282
|
+
* @param state - The current query builder state
|
|
1283
|
+
* @param options - The query parameter key name configuration
|
|
1284
|
+
*/
|
|
1285
|
+
_parseSearch(state, options) {
|
|
1286
|
+
if (!state.search) {
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
1289
|
+
const param = `${this._prepend(state)}${options.search}=${state.search}`;
|
|
1290
|
+
this._uri += param;
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Parse and append the select parameter
|
|
1294
|
+
*
|
|
1295
|
+
* Generates: `select=col1,col2`
|
|
1296
|
+
*
|
|
1297
|
+
* @param state - The current query builder state
|
|
1298
|
+
* @param options - The query parameter key name configuration
|
|
1299
|
+
*/
|
|
1300
|
+
_parseSelect(state, options) {
|
|
1301
|
+
if (!state.select.length) {
|
|
1302
|
+
return;
|
|
1303
|
+
}
|
|
1304
|
+
const param = `${this._prepend(state)}${options.select}=${state.select.join(',')}`;
|
|
1305
|
+
this._uri += param;
|
|
1306
|
+
}
|
|
1307
|
+
/**
|
|
1308
|
+
* Parse and append sort parameters
|
|
1309
|
+
*
|
|
1310
|
+
* Generates: `sortBy=field1:DESC,field2:ASC`
|
|
1311
|
+
*
|
|
1312
|
+
* @param state - The current query builder state
|
|
1313
|
+
* @param options - The query parameter key name configuration
|
|
1314
|
+
*/
|
|
1315
|
+
_parseSort(state, options) {
|
|
1316
|
+
if (!state.sorts.length) {
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const sortPairs = state.sorts.map(sort => `${sort.field}:${sort.order === SortEnum.DESC ? 'DESC' : 'ASC'}`);
|
|
1320
|
+
const param = `${this._prepend(state)}${options.sortBy}=${sortPairs.join(',')}`;
|
|
1321
|
+
this._uri += param;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Determine the appropriate URI prefix based on the current accumulator state
|
|
1325
|
+
*
|
|
1326
|
+
* Returns the full base path with `?` for the first parameter,
|
|
1327
|
+
* or `&` for subsequent parameters.
|
|
1328
|
+
*
|
|
1329
|
+
* @param state - The current query builder state
|
|
1330
|
+
* @returns The prefix string to prepend to the next parameter
|
|
1331
|
+
*/
|
|
1332
|
+
_prepend(state) {
|
|
1333
|
+
if (this._uri) {
|
|
1334
|
+
return '&';
|
|
1335
|
+
}
|
|
1336
|
+
return state.baseUrl ? `${state.baseUrl}/${state.resource}?` : `/${state.resource}?`;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
/**
|
|
1341
|
+
* Response strategy for the NestJS (nestjs-paginate) driver
|
|
1342
|
+
*
|
|
1343
|
+
* Parses nested NestJS pagination responses:
|
|
1344
|
+
* ```json
|
|
1345
|
+
* {
|
|
1346
|
+
* "data": [...],
|
|
1347
|
+
* "meta": {
|
|
1348
|
+
* "currentPage": 1,
|
|
1349
|
+
* "totalItems": 100,
|
|
1350
|
+
* "itemsPerPage": 10,
|
|
1351
|
+
* "totalPages": 10
|
|
1352
|
+
* },
|
|
1353
|
+
* "links": {
|
|
1354
|
+
* "first": "url",
|
|
1355
|
+
* "previous": "url",
|
|
1356
|
+
* "next": "url",
|
|
1357
|
+
* "last": "url",
|
|
1358
|
+
* "current": "url"
|
|
1359
|
+
* }
|
|
1360
|
+
* }
|
|
1361
|
+
* ```
|
|
1362
|
+
*
|
|
1363
|
+
* @see https://github.com/ppetzold/nestjs-paginate
|
|
1364
|
+
*/
|
|
1365
|
+
class NestjsResponseStrategy {
|
|
1366
|
+
/**
|
|
1367
|
+
* Parse a nested NestJS pagination response into a PaginatedCollection
|
|
1368
|
+
*
|
|
1369
|
+
* Supports dot-notation key paths for accessing nested values.
|
|
1370
|
+
* Computes `from` and `to` from `currentPage` and `itemsPerPage` when
|
|
1371
|
+
* they are not directly available in the response.
|
|
1372
|
+
*
|
|
1373
|
+
* @param response - The raw API response object
|
|
1374
|
+
* @param options - The response key name configuration
|
|
1375
|
+
* @returns A typed PaginatedCollection instance
|
|
1376
|
+
*/
|
|
1377
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1378
|
+
paginate(response, options) {
|
|
1379
|
+
const data = this._resolve(response, options.data);
|
|
1380
|
+
const currentPage = this._resolve(response, options.currentPage);
|
|
1381
|
+
const total = this._resolve(response, options.total);
|
|
1382
|
+
const perPage = this._resolve(response, options.perPage);
|
|
1383
|
+
const lastPage = this._resolve(response, options.lastPage);
|
|
1384
|
+
// Compute from/to if not directly available
|
|
1385
|
+
const from = this._resolveFrom(response, options, currentPage, perPage);
|
|
1386
|
+
const to = this._resolveTo(response, options, currentPage, perPage, total);
|
|
1387
|
+
const prevPageUrl = this._resolve(response, options.prevPageUrl);
|
|
1388
|
+
const nextPageUrl = this._resolve(response, options.nextPageUrl);
|
|
1389
|
+
const firstPageUrl = this._resolve(response, options.firstPageUrl);
|
|
1390
|
+
const lastPageUrl = this._resolve(response, options.lastPageUrl);
|
|
1391
|
+
return new PaginatedCollection(data, currentPage, from, to, total, perPage, prevPageUrl, nextPageUrl, lastPage, firstPageUrl, lastPageUrl);
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* Resolve a value from a response object using a dot-notation path
|
|
1395
|
+
*
|
|
1396
|
+
* Supports both flat keys ('data') and nested paths ('meta.currentPage').
|
|
1397
|
+
*
|
|
1398
|
+
* @param response - The raw response object
|
|
1399
|
+
* @param path - The dot-notation path to resolve
|
|
1400
|
+
* @returns The resolved value, or undefined if not found
|
|
1401
|
+
*/
|
|
1402
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1403
|
+
_resolve(response, path) {
|
|
1404
|
+
return path.split('.').reduce((obj, key) => obj?.[key], response);
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* Resolve the "from" index value
|
|
1408
|
+
*
|
|
1409
|
+
* If the path resolves to a value in the response, use it.
|
|
1410
|
+
* Otherwise, compute it from currentPage and perPage:
|
|
1411
|
+
* `(currentPage - 1) * perPage + 1`
|
|
1412
|
+
*
|
|
1413
|
+
* @param response - The raw response object
|
|
1414
|
+
* @param options - The response key name configuration
|
|
1415
|
+
* @param currentPage - The current page number
|
|
1416
|
+
* @param perPage - The number of items per page
|
|
1417
|
+
* @returns The computed "from" index
|
|
1418
|
+
*/
|
|
1419
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1420
|
+
_resolveFrom(response, options, currentPage, perPage) {
|
|
1421
|
+
const direct = this._resolve(response, options.from);
|
|
1422
|
+
if (direct !== undefined) {
|
|
1423
|
+
return direct;
|
|
1424
|
+
}
|
|
1425
|
+
if (currentPage && perPage) {
|
|
1426
|
+
return (currentPage - 1) * perPage + 1;
|
|
1427
|
+
}
|
|
1428
|
+
return undefined;
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Resolve the "to" index value
|
|
1432
|
+
*
|
|
1433
|
+
* If the path resolves to a value in the response, use it.
|
|
1434
|
+
* Otherwise, compute it from currentPage, perPage, and total:
|
|
1435
|
+
* `Math.min(currentPage * perPage, total)`
|
|
1436
|
+
*
|
|
1437
|
+
* @param response - The raw response object
|
|
1438
|
+
* @param options - The response key name configuration
|
|
1439
|
+
* @param currentPage - The current page number
|
|
1440
|
+
* @param perPage - The number of items per page
|
|
1441
|
+
* @param total - The total number of items
|
|
1442
|
+
* @returns The computed "to" index
|
|
1443
|
+
*/
|
|
1444
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1445
|
+
_resolveTo(response, options, currentPage, perPage, total) {
|
|
1446
|
+
const direct = this._resolve(response, options.to);
|
|
1447
|
+
if (direct !== undefined) {
|
|
1448
|
+
return direct;
|
|
1449
|
+
}
|
|
1450
|
+
if (currentPage && perPage && total) {
|
|
1451
|
+
return Math.min(currentPage * perPage, total);
|
|
1452
|
+
}
|
|
1453
|
+
return undefined;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
class UnselectableModelError extends Error {
|
|
1458
|
+
constructor(model) {
|
|
1459
|
+
super(`Unselectable Model: the selected model (${model}) is not present neither in the "model" property, nor in the includes object.`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Request strategy for the Spatie Query Builder driver
|
|
1465
|
+
*
|
|
1466
|
+
* Generates URIs in the Spatie format:
|
|
1467
|
+
* - Fields: `fields[model]=col1,col2`
|
|
1468
|
+
* - Filters: `filter[field]=value`
|
|
1469
|
+
* - Includes: `include=model1,model2`
|
|
1470
|
+
* - Sorts: `sort=-field1,field2` (- prefix = DESC)
|
|
1471
|
+
* - Pagination: `limit=N&page=N`
|
|
1472
|
+
*
|
|
1473
|
+
* @see https://spatie.be/docs/laravel-query-builder
|
|
1474
|
+
*/
|
|
1475
|
+
class SpatieRequestStrategy {
|
|
1476
|
+
/**
|
|
1477
|
+
* Accumulator for composing the URI string
|
|
1478
|
+
*/
|
|
1479
|
+
_uri = '';
|
|
1480
|
+
/**
|
|
1481
|
+
* Build a URI string from the given state using the Spatie format
|
|
1482
|
+
*
|
|
1483
|
+
* @param state - The current query builder state
|
|
1484
|
+
* @param options - The query parameter key name configuration
|
|
1485
|
+
* @returns The composed URI string
|
|
1486
|
+
* @throws Error if resource is not set
|
|
1487
|
+
*/
|
|
1488
|
+
buildUri(state, options) {
|
|
1489
|
+
if (!state.resource) {
|
|
1490
|
+
throw new Error('Set the resource property BEFORE adding filters or calling the url() / get() methods');
|
|
1491
|
+
}
|
|
1492
|
+
this._uri = '';
|
|
1493
|
+
this._parseIncludes(state, options);
|
|
1494
|
+
this._parseFields(state, options);
|
|
1495
|
+
this._parseFilters(state, options);
|
|
1496
|
+
this._parseLimit(state, options);
|
|
1497
|
+
this._parsePage(state, options);
|
|
1498
|
+
this._parseSort(state, options);
|
|
1499
|
+
return this._uri;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Parse and append field selection parameters
|
|
1503
|
+
*
|
|
1504
|
+
* Validates that each field model exists either as the main resource
|
|
1505
|
+
* or in the includes list. Fields are grouped by model in bracket notation.
|
|
1506
|
+
*
|
|
1507
|
+
* @param state - The current query builder state
|
|
1508
|
+
* @param options - The query parameter key name configuration
|
|
1509
|
+
* @returns The generated field selection parameter string
|
|
1510
|
+
* @throws Error if resource is required but not set
|
|
1511
|
+
* @throws UnselectableModelError if a field model is not in resource or includes
|
|
1512
|
+
*/
|
|
1513
|
+
_parseFields(state, options) {
|
|
1514
|
+
if (!Object.keys(state.fields).length) {
|
|
1515
|
+
return this._uri;
|
|
1516
|
+
}
|
|
1517
|
+
if (!state.resource) {
|
|
1518
|
+
throw new Error('While selecting fields, the -> resource <- is required');
|
|
1519
|
+
}
|
|
1520
|
+
if (!(state.resource in state.fields)) {
|
|
1521
|
+
throw new Error(`Key ${state.resource} is missing in the fields object`);
|
|
1522
|
+
}
|
|
1523
|
+
const f = {};
|
|
1524
|
+
for (const k in state.fields) {
|
|
1525
|
+
if (state.fields.hasOwnProperty(k)) {
|
|
1526
|
+
if (k !== state.resource && !state.includes.includes(k)) {
|
|
1527
|
+
throw new UnselectableModelError(k);
|
|
1528
|
+
}
|
|
1529
|
+
Object.assign(f, { [`${options.fields}[${k}]`]: state.fields[k].join(',') });
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
const param = `${this._prepend(state)}${qs.stringify(f, { encode: false })}`;
|
|
1533
|
+
this._uri += param;
|
|
1534
|
+
return param;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Parse and append filter parameters
|
|
1538
|
+
*
|
|
1539
|
+
* Generates filter parameters in bracket notation: `filter[key]=value1,value2`
|
|
1540
|
+
*
|
|
1541
|
+
* @param state - The current query builder state
|
|
1542
|
+
* @param options - The query parameter key name configuration
|
|
1543
|
+
* @returns The generated filter parameter string
|
|
1544
|
+
*/
|
|
1545
|
+
_parseFilters(state, options) {
|
|
1546
|
+
const keys = Object.keys(state.filters);
|
|
1547
|
+
if (!keys.length) {
|
|
1548
|
+
return this._uri;
|
|
1549
|
+
}
|
|
1550
|
+
const f = {
|
|
1551
|
+
[`${options.filters}`]: keys.reduce((acc, key) => {
|
|
1552
|
+
return Object.assign(acc, { [key]: state.filters[key].join(',') });
|
|
1553
|
+
}, {})
|
|
1554
|
+
};
|
|
1555
|
+
const param = `${this._prepend(state)}${qs.stringify(f, { encode: false })}`;
|
|
1556
|
+
this._uri += param;
|
|
1557
|
+
return param;
|
|
1558
|
+
}
|
|
1559
|
+
/**
|
|
1560
|
+
* Parse and append include parameters
|
|
1561
|
+
*
|
|
1562
|
+
* Generates: `include=model1,model2`
|
|
1563
|
+
*
|
|
1564
|
+
* @param state - The current query builder state
|
|
1565
|
+
* @param options - The query parameter key name configuration
|
|
1566
|
+
* @returns The generated include parameter string
|
|
1567
|
+
*/
|
|
1568
|
+
_parseIncludes(state, options) {
|
|
1569
|
+
if (!state.includes.length) {
|
|
1570
|
+
return this._uri;
|
|
1571
|
+
}
|
|
1572
|
+
const param = `${this._prepend(state)}${options.includes}=${state.includes}`;
|
|
1573
|
+
this._uri += param;
|
|
1574
|
+
return param;
|
|
1575
|
+
}
|
|
1576
|
+
/**
|
|
1577
|
+
* Parse and append the limit parameter
|
|
1578
|
+
*
|
|
1579
|
+
* @param state - The current query builder state
|
|
1580
|
+
* @param options - The query parameter key name configuration
|
|
1581
|
+
* @returns The generated limit parameter string
|
|
1582
|
+
*/
|
|
1583
|
+
_parseLimit(state, options) {
|
|
1584
|
+
const param = `${this._prepend(state)}${options.limit}=${state.limit}`;
|
|
1585
|
+
this._uri += param;
|
|
1586
|
+
return param;
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Parse and append the page parameter
|
|
1590
|
+
*
|
|
1591
|
+
* @param state - The current query builder state
|
|
1592
|
+
* @param options - The query parameter key name configuration
|
|
1593
|
+
* @returns The generated page parameter string
|
|
1594
|
+
*/
|
|
1595
|
+
_parsePage(state, options) {
|
|
1596
|
+
const param = `${this._prepend(state)}${options.page}=${state.page}`;
|
|
1597
|
+
this._uri += param;
|
|
1598
|
+
return param;
|
|
1599
|
+
}
|
|
1600
|
+
/**
|
|
1601
|
+
* Parse and append sort parameters
|
|
1602
|
+
*
|
|
1603
|
+
* Generates: `sort=-field1,field2` where `-` prefix indicates DESC order
|
|
1604
|
+
*
|
|
1605
|
+
* @param state - The current query builder state
|
|
1606
|
+
* @param options - The query parameter key name configuration
|
|
1607
|
+
* @returns The generated sort parameter string
|
|
1608
|
+
*/
|
|
1609
|
+
_parseSort(state, options) {
|
|
1610
|
+
let param = '';
|
|
1611
|
+
if (!state.sorts.length) {
|
|
1612
|
+
return param;
|
|
1613
|
+
}
|
|
1614
|
+
param = `${this._prepend(state)}${options.sort}=`;
|
|
1615
|
+
state.sorts.forEach((sort, idx) => {
|
|
1616
|
+
param += `${sort.order === SortEnum.DESC ? '-' : ''}${sort.field}`;
|
|
1617
|
+
if (idx < state.sorts.length - 1) {
|
|
1618
|
+
param += ',';
|
|
1619
|
+
}
|
|
1620
|
+
});
|
|
1621
|
+
this._uri += param;
|
|
1622
|
+
return param;
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* Determine the appropriate URI prefix based on the current accumulator state
|
|
1626
|
+
*
|
|
1627
|
+
* Returns the full base path with `?` for the first parameter,
|
|
1628
|
+
* or `&` for subsequent parameters.
|
|
1629
|
+
*
|
|
1630
|
+
* @param state - The current query builder state
|
|
1631
|
+
* @returns The prefix string to prepend to the next parameter
|
|
1632
|
+
*/
|
|
1633
|
+
_prepend(state) {
|
|
1634
|
+
if (this._uri) {
|
|
1635
|
+
return '&';
|
|
1636
|
+
}
|
|
1637
|
+
return state.baseUrl ? `${state.baseUrl}/${state.resource}?` : `/${state.resource}?`;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/**
|
|
1642
|
+
* Response strategy for the Spatie Query Builder driver
|
|
1643
|
+
*
|
|
1644
|
+
* Parses flat Laravel pagination responses:
|
|
1645
|
+
* ```json
|
|
1646
|
+
* {
|
|
1647
|
+
* "data": [...],
|
|
1648
|
+
* "current_page": 1,
|
|
1649
|
+
* "total": 100,
|
|
1650
|
+
* "per_page": 15,
|
|
1651
|
+
* "from": 1,
|
|
1652
|
+
* "to": 15,
|
|
1653
|
+
* ...
|
|
1654
|
+
* }
|
|
1655
|
+
* ```
|
|
1656
|
+
*
|
|
1657
|
+
* @see https://spatie.be/docs/laravel-query-builder
|
|
1658
|
+
*/
|
|
1659
|
+
class SpatieResponseStrategy {
|
|
1660
|
+
/**
|
|
1661
|
+
* Parse a flat Laravel pagination response into a PaginatedCollection
|
|
1662
|
+
*
|
|
1663
|
+
* @param response - The raw API response object
|
|
1664
|
+
* @param options - The response key name configuration
|
|
1665
|
+
* @returns A typed PaginatedCollection instance
|
|
1666
|
+
*/
|
|
1667
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1668
|
+
paginate(response, options) {
|
|
1669
|
+
return new PaginatedCollection(response[options.data], response[options.currentPage], response[options.from], response[options.to], response[options.total], response[options.perPage], response[options.prevPageUrl], response[options.nextPageUrl], response[options.lastPage], response[options.firstPageUrl], response[options.lastPageUrl]);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/**
|
|
1674
|
+
* Resolve the request strategy instance for the given driver
|
|
1675
|
+
*
|
|
1676
|
+
* @param driver - The pagination driver
|
|
1677
|
+
* @returns The corresponding request strategy
|
|
1678
|
+
*/
|
|
1679
|
+
function resolveRequestStrategy$1(driver) {
|
|
1680
|
+
switch (driver) {
|
|
1681
|
+
case DriverEnum.NESTJS:
|
|
1682
|
+
return new NestjsRequestStrategy();
|
|
1683
|
+
case DriverEnum.SPATIE:
|
|
1684
|
+
return new SpatieRequestStrategy();
|
|
1685
|
+
case DriverEnum.LARAVEL:
|
|
1686
|
+
return new LaravelRequestStrategy();
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Resolve the response strategy instance for the given driver
|
|
1691
|
+
*
|
|
1692
|
+
* @param driver - The pagination driver
|
|
1693
|
+
* @returns The corresponding response strategy
|
|
1694
|
+
*/
|
|
1695
|
+
function resolveResponseStrategy$1(driver) {
|
|
1696
|
+
switch (driver) {
|
|
1697
|
+
case DriverEnum.NESTJS:
|
|
1698
|
+
return new NestjsResponseStrategy();
|
|
1699
|
+
case DriverEnum.SPATIE:
|
|
1700
|
+
return new SpatieResponseStrategy();
|
|
1701
|
+
case DriverEnum.LARAVEL:
|
|
1702
|
+
return new LaravelResponseStrategy();
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
788
1705
|
// @dynamic
|
|
789
1706
|
class NgQubeeModule {
|
|
790
|
-
|
|
1707
|
+
/**
|
|
1708
|
+
* Configure NgQubee for the root module
|
|
1709
|
+
*
|
|
1710
|
+
* @param config - Configuration object with driver, and optional request and response settings
|
|
1711
|
+
* @returns Module with providers configured for the specified driver
|
|
1712
|
+
*/
|
|
1713
|
+
static forRoot(config) {
|
|
1714
|
+
const driver = config.driver;
|
|
1715
|
+
const requestStrategy = resolveRequestStrategy$1(driver);
|
|
1716
|
+
const responseStrategy = resolveResponseStrategy$1(driver);
|
|
791
1717
|
return {
|
|
792
1718
|
ngModule: NgQubeeModule,
|
|
793
1719
|
providers: [
|
|
@@ -795,52 +1721,99 @@ class NgQubeeModule {
|
|
|
795
1721
|
{
|
|
796
1722
|
deps: [NestService],
|
|
797
1723
|
provide: NgQubeeService,
|
|
798
|
-
useFactory: (nestService) => new NgQubeeService(nestService, Object.assign({}, config.request))
|
|
799
|
-
},
|
|
1724
|
+
useFactory: (nestService) => new NgQubeeService(nestService, requestStrategy, driver, Object.assign({}, config.request))
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
800
1727
|
provide: PaginationService,
|
|
801
|
-
useFactory: () =>
|
|
1728
|
+
useFactory: () => {
|
|
1729
|
+
const responseConfig = Object.assign({}, config.response);
|
|
1730
|
+
return driver === DriverEnum.NESTJS
|
|
1731
|
+
? new PaginationService(responseStrategy, new NestjsResponseOptions(responseConfig))
|
|
1732
|
+
: new PaginationService(responseStrategy, responseConfig);
|
|
1733
|
+
}
|
|
802
1734
|
}
|
|
803
1735
|
]
|
|
804
1736
|
};
|
|
805
1737
|
}
|
|
806
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
807
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
808
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
809
|
-
deps: [NestService],
|
|
810
|
-
provide: NgQubeeService,
|
|
811
|
-
useFactory: (nestService) => new NgQubeeService(nestService, {})
|
|
812
|
-
}] });
|
|
1738
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
1739
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeModule });
|
|
1740
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeModule });
|
|
813
1741
|
}
|
|
814
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1742
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeModule, decorators: [{
|
|
815
1743
|
type: NgModule,
|
|
816
|
-
args: [{
|
|
817
|
-
providers: [{
|
|
818
|
-
deps: [NestService],
|
|
819
|
-
provide: NgQubeeService,
|
|
820
|
-
useFactory: (nestService) => new NgQubeeService(nestService, {})
|
|
821
|
-
}]
|
|
822
|
-
}]
|
|
1744
|
+
args: [{}]
|
|
823
1745
|
}] });
|
|
824
1746
|
|
|
1747
|
+
/**
|
|
1748
|
+
* Resolve the request strategy instance for the given driver
|
|
1749
|
+
*
|
|
1750
|
+
* @param driver - The pagination driver
|
|
1751
|
+
* @returns The corresponding request strategy
|
|
1752
|
+
*/
|
|
1753
|
+
function resolveRequestStrategy(driver) {
|
|
1754
|
+
switch (driver) {
|
|
1755
|
+
case DriverEnum.NESTJS:
|
|
1756
|
+
return new NestjsRequestStrategy();
|
|
1757
|
+
case DriverEnum.SPATIE:
|
|
1758
|
+
return new SpatieRequestStrategy();
|
|
1759
|
+
case DriverEnum.LARAVEL:
|
|
1760
|
+
return new LaravelRequestStrategy();
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
/**
|
|
1764
|
+
* Resolve the response strategy instance for the given driver
|
|
1765
|
+
*
|
|
1766
|
+
* @param driver - The pagination driver
|
|
1767
|
+
* @returns The corresponding response strategy
|
|
1768
|
+
*/
|
|
1769
|
+
function resolveResponseStrategy(driver) {
|
|
1770
|
+
switch (driver) {
|
|
1771
|
+
case DriverEnum.NESTJS:
|
|
1772
|
+
return new NestjsResponseStrategy();
|
|
1773
|
+
case DriverEnum.SPATIE:
|
|
1774
|
+
return new SpatieResponseStrategy();
|
|
1775
|
+
case DriverEnum.LARAVEL:
|
|
1776
|
+
return new LaravelResponseStrategy();
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
825
1779
|
/**
|
|
826
1780
|
* Sets up providers necessary to enable `NgQubee` functionality for the application.
|
|
827
1781
|
*
|
|
828
1782
|
* @usageNotes
|
|
829
1783
|
*
|
|
830
|
-
* Basic example
|
|
1784
|
+
* Basic example with the Laravel driver:
|
|
831
1785
|
* ```
|
|
832
|
-
*
|
|
1786
|
+
* bootstrapApplication(AppComponent, {
|
|
1787
|
+
* providers: [provideNgQubee({ driver: DriverEnum.LARAVEL })]
|
|
1788
|
+
* });
|
|
1789
|
+
* ```
|
|
1790
|
+
*
|
|
1791
|
+
* Spatie driver example:
|
|
1792
|
+
* ```
|
|
1793
|
+
* import { DriverEnum } from 'ng-qubee';
|
|
833
1794
|
*
|
|
834
1795
|
* bootstrapApplication(AppComponent, {
|
|
835
|
-
* providers: [provideNgQubee(
|
|
1796
|
+
* providers: [provideNgQubee({ driver: DriverEnum.SPATIE })]
|
|
1797
|
+
* });
|
|
1798
|
+
* ```
|
|
1799
|
+
*
|
|
1800
|
+
* NestJS driver example:
|
|
1801
|
+
* ```
|
|
1802
|
+
* import { DriverEnum } from 'ng-qubee';
|
|
1803
|
+
*
|
|
1804
|
+
* bootstrapApplication(AppComponent, {
|
|
1805
|
+
* providers: [provideNgQubee({ driver: DriverEnum.NESTJS })]
|
|
836
1806
|
* });
|
|
837
1807
|
* ```
|
|
838
1808
|
*
|
|
839
1809
|
* @publicApi
|
|
840
|
-
* @param config Configuration object compliant to the IConfig interface
|
|
1810
|
+
* @param config - Configuration object compliant to the IConfig interface
|
|
841
1811
|
* @returns A set of providers to setup NgQubee
|
|
842
1812
|
*/
|
|
843
|
-
function provideNgQubee(config
|
|
1813
|
+
function provideNgQubee(config) {
|
|
1814
|
+
const driver = config.driver;
|
|
1815
|
+
const requestStrategy = resolveRequestStrategy(driver);
|
|
1816
|
+
const responseStrategy = resolveResponseStrategy(driver);
|
|
844
1817
|
return makeEnvironmentProviders([
|
|
845
1818
|
{
|
|
846
1819
|
provide: NestService,
|
|
@@ -849,14 +1822,44 @@ function provideNgQubee(config = {}) {
|
|
|
849
1822
|
{
|
|
850
1823
|
deps: [NestService],
|
|
851
1824
|
provide: NgQubeeService,
|
|
852
|
-
useFactory: (nestService) => new NgQubeeService(nestService, Object.assign({}, config.request))
|
|
853
|
-
},
|
|
1825
|
+
useFactory: (nestService) => new NgQubeeService(nestService, requestStrategy, driver, Object.assign({}, config.request))
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
854
1828
|
provide: PaginationService,
|
|
855
|
-
useFactory: () =>
|
|
1829
|
+
useFactory: () => {
|
|
1830
|
+
const responseConfig = Object.assign({}, config.response);
|
|
1831
|
+
return driver === DriverEnum.NESTJS
|
|
1832
|
+
? new PaginationService(responseStrategy, new NestjsResponseOptions(responseConfig))
|
|
1833
|
+
: new PaginationService(responseStrategy, responseConfig);
|
|
1834
|
+
}
|
|
856
1835
|
}
|
|
857
1836
|
]);
|
|
858
1837
|
}
|
|
859
1838
|
|
|
1839
|
+
/**
|
|
1840
|
+
* Enum representing the available filter operators for the NestJS driver
|
|
1841
|
+
*
|
|
1842
|
+
* These operators map to the nestjs-paginate filter syntax:
|
|
1843
|
+
* `filter.field=$operator:value`
|
|
1844
|
+
*
|
|
1845
|
+
* @see https://github.com/ppetzold/nestjs-paginate
|
|
1846
|
+
*/
|
|
1847
|
+
var FilterOperatorEnum;
|
|
1848
|
+
(function (FilterOperatorEnum) {
|
|
1849
|
+
FilterOperatorEnum["BTW"] = "$btw";
|
|
1850
|
+
FilterOperatorEnum["CONTAINS"] = "$contains";
|
|
1851
|
+
FilterOperatorEnum["EQ"] = "$eq";
|
|
1852
|
+
FilterOperatorEnum["GT"] = "$gt";
|
|
1853
|
+
FilterOperatorEnum["GTE"] = "$gte";
|
|
1854
|
+
FilterOperatorEnum["ILIKE"] = "$ilike";
|
|
1855
|
+
FilterOperatorEnum["IN"] = "$in";
|
|
1856
|
+
FilterOperatorEnum["LT"] = "$lt";
|
|
1857
|
+
FilterOperatorEnum["LTE"] = "$lte";
|
|
1858
|
+
FilterOperatorEnum["NOT"] = "$not";
|
|
1859
|
+
FilterOperatorEnum["NULL"] = "$null";
|
|
1860
|
+
FilterOperatorEnum["SW"] = "$sw";
|
|
1861
|
+
})(FilterOperatorEnum || (FilterOperatorEnum = {}));
|
|
1862
|
+
|
|
860
1863
|
/*
|
|
861
1864
|
* Public API Surface of angular-query-builder
|
|
862
1865
|
*/
|
|
@@ -865,5 +1868,5 @@ function provideNgQubee(config = {}) {
|
|
|
865
1868
|
* Generated bundle index. Do not edit.
|
|
866
1869
|
*/
|
|
867
1870
|
|
|
868
|
-
export {
|
|
1871
|
+
export { DriverEnum, FilterOperatorEnum, InvalidLimitError, InvalidPageNumberError, InvalidResourceNameError, KeyNotFoundError, LaravelRequestStrategy, LaravelResponseStrategy, NestjsRequestStrategy, NestjsResponseStrategy, NgQubeeModule, NgQubeeService, PaginatedCollection, PaginationService, SortEnum, SpatieRequestStrategy, SpatieResponseStrategy, UnselectableModelError, UnsupportedFieldSelectionError, UnsupportedFilterError, UnsupportedFilterOperatorError, UnsupportedIncludesError, UnsupportedSearchError, UnsupportedSelectError, UnsupportedSortError, provideNgQubee };
|
|
869
1872
|
//# sourceMappingURL=ng-qubee.mjs.map
|