ng-qubee 2.1.0 → 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 +301 -54
- package/fesm2022/ng-qubee.mjs +1257 -264
- package/fesm2022/ng-qubee.mjs.map +1 -1
- package/package.json +4 -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/nest-state.interface.d.ts +0 -4
- package/lib/interfaces/normalized.interface.d.ts +0 -3
- package/lib/interfaces/page.interface.d.ts +0 -2
- 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 -20
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 * as qs from 'qs';
|
|
4
3
|
import { BehaviorSubject, filter, throwError } from 'rxjs';
|
|
4
|
+
import * as qs from 'qs';
|
|
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';
|
|
170
|
+
}
|
|
171
|
+
}
|
|
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';
|
|
78
232
|
}
|
|
79
233
|
}
|
|
80
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
|
*
|
|
@@ -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,163 +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
|
-
}
|
|
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;
|
|
467
752
|
}
|
|
468
|
-
const param = `${this._prepend(s.model)}${qs.stringify(f, { encode: false })}`;
|
|
469
|
-
this._uri += param;
|
|
470
|
-
return param;
|
|
471
753
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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;
|
|
476
766
|
}
|
|
477
|
-
|
|
478
|
-
|
|
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
|
-
}
|
|
486
|
-
_parseIncludes(s) {
|
|
487
|
-
if (!s.includes.length) {
|
|
488
|
-
return this._uri;
|
|
489
|
-
}
|
|
490
|
-
const param = `${this._prepend(s.model)}${this._options.includes}=${s.includes}`;
|
|
491
|
-
this._uri += param;
|
|
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
|
-
const state = this._nestService.nest();
|
|
535
|
-
const baseUrl = state.baseUrl;
|
|
536
|
-
if (this._uri) {
|
|
537
|
-
return '&';
|
|
538
|
-
}
|
|
539
|
-
return baseUrl ? `${baseUrl}/${model}?` : `/${model}?`;
|
|
767
|
+
this._nestService.addFields({ [model]: fields });
|
|
768
|
+
return this;
|
|
540
769
|
}
|
|
541
770
|
/**
|
|
542
|
-
* Add
|
|
771
|
+
* Add a filter with the given value(s) (Spatie and NestJS only)
|
|
772
|
+
*
|
|
773
|
+
* Produces: `filter[field]=value` (Spatie) or `filter.field=value` (NestJS)
|
|
543
774
|
*
|
|
544
|
-
* @param
|
|
545
|
-
* @param
|
|
775
|
+
* @param {string} field - Name of the field to filter
|
|
776
|
+
* @param {(string | number | boolean)[]} values - The needle(s)
|
|
546
777
|
* @returns {this}
|
|
778
|
+
* @throws {UnsupportedFilterError} If the active driver does not support filters
|
|
547
779
|
*/
|
|
548
|
-
|
|
549
|
-
|
|
780
|
+
addFilter(field, ...values) {
|
|
781
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedFilterError());
|
|
782
|
+
if (!values.length) {
|
|
550
783
|
return this;
|
|
551
784
|
}
|
|
552
|
-
this._nestService.
|
|
785
|
+
this._nestService.addFilters({
|
|
786
|
+
[field]: values
|
|
787
|
+
});
|
|
553
788
|
return this;
|
|
554
789
|
}
|
|
555
790
|
/**
|
|
556
|
-
* Add a filter with
|
|
557
|
-
*
|
|
791
|
+
* Add a filter with an explicit operator (NestJS only)
|
|
792
|
+
*
|
|
793
|
+
* Produces: `filter.field=$operator:value`
|
|
558
794
|
*
|
|
559
|
-
* @param {string} field Name of the field to filter
|
|
560
|
-
* @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
|
|
561
798
|
* @returns {this}
|
|
799
|
+
* @throws {UnsupportedFilterOperatorError} If the active driver does not support filter operators
|
|
562
800
|
*/
|
|
563
|
-
|
|
801
|
+
addFilterOperator(field, operator, ...values) {
|
|
802
|
+
this._assertDriver([DriverEnum.NESTJS], new UnsupportedFilterOperatorError());
|
|
564
803
|
if (!values.length) {
|
|
565
804
|
return this;
|
|
566
805
|
}
|
|
567
|
-
this._nestService.
|
|
568
|
-
[field]: values
|
|
569
|
-
});
|
|
806
|
+
this._nestService.addOperatorFilters([{ field, operator, values }]);
|
|
570
807
|
return this;
|
|
571
808
|
}
|
|
572
809
|
/**
|
|
573
|
-
* Add related entities to include in the request
|
|
810
|
+
* Add related entities to include in the request (Spatie only)
|
|
574
811
|
*
|
|
575
|
-
* @param {string[]} models
|
|
576
|
-
* @returns
|
|
812
|
+
* @param {string[]} models - Models to include
|
|
813
|
+
* @returns {this}
|
|
814
|
+
* @throws {UnsupportedIncludesError} If the active driver does not support includes
|
|
577
815
|
*/
|
|
578
816
|
addIncludes(...models) {
|
|
817
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedIncludesError());
|
|
579
818
|
if (!models.length) {
|
|
580
819
|
return this;
|
|
581
820
|
}
|
|
@@ -583,13 +822,32 @@ class NgQubeeService {
|
|
|
583
822
|
return this;
|
|
584
823
|
}
|
|
585
824
|
/**
|
|
586
|
-
* Add
|
|
825
|
+
* Add flat field selection (NestJS only)
|
|
826
|
+
*
|
|
827
|
+
* Produces: `select=col1,col2`
|
|
587
828
|
*
|
|
588
|
-
* @param
|
|
589
|
-
* @param {SortEnum} order A value from the SortEnum enumeration
|
|
829
|
+
* @param {string[]} fields - Fields to select
|
|
590
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
|
|
591
848
|
*/
|
|
592
849
|
addSort(field, order) {
|
|
850
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedSortError());
|
|
593
851
|
this._nestService.addSort({
|
|
594
852
|
field,
|
|
595
853
|
order
|
|
@@ -597,7 +855,7 @@ class NgQubeeService {
|
|
|
597
855
|
return this;
|
|
598
856
|
}
|
|
599
857
|
/**
|
|
600
|
-
* 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)
|
|
601
859
|
*
|
|
602
860
|
* ```
|
|
603
861
|
* ngQubeeService.deleteFields({
|
|
@@ -606,25 +864,29 @@ class NgQubeeService {
|
|
|
606
864
|
* });
|
|
607
865
|
* ```
|
|
608
866
|
*
|
|
609
|
-
* @param {IFields} fields
|
|
610
|
-
* @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
|
|
611
870
|
*/
|
|
612
871
|
deleteFields(fields) {
|
|
872
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedFieldSelectionError());
|
|
613
873
|
this._nestService.deleteFields(fields);
|
|
614
874
|
return this;
|
|
615
875
|
}
|
|
616
876
|
/**
|
|
617
|
-
* 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)
|
|
618
878
|
*
|
|
619
879
|
* ```
|
|
620
|
-
* ngQubeeService.deleteFieldsByModel('users', 'email', 'password'
|
|
880
|
+
* ngQubeeService.deleteFieldsByModel('users', 'email', 'password');
|
|
621
881
|
* ```
|
|
622
882
|
*
|
|
623
|
-
* @param model Model that holds the fields
|
|
624
|
-
* @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
|
|
625
885
|
* @returns {this}
|
|
886
|
+
* @throws {UnsupportedFieldSelectionError} If the active driver does not support per-model field selection
|
|
626
887
|
*/
|
|
627
888
|
deleteFieldsByModel(model, ...fields) {
|
|
889
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedFieldSelectionError());
|
|
628
890
|
if (!fields.length) {
|
|
629
891
|
return this;
|
|
630
892
|
}
|
|
@@ -634,12 +896,14 @@ class NgQubeeService {
|
|
|
634
896
|
return this;
|
|
635
897
|
}
|
|
636
898
|
/**
|
|
637
|
-
* Remove given filters from the query builder state
|
|
899
|
+
* Remove given filters from the query builder state (Spatie and NestJS only)
|
|
638
900
|
*
|
|
639
|
-
* @param {string[]} filters Filters to remove
|
|
901
|
+
* @param {string[]} filters - Filters to remove
|
|
640
902
|
* @returns {this}
|
|
903
|
+
* @throws {UnsupportedFilterError} If the active driver does not support filters
|
|
641
904
|
*/
|
|
642
905
|
deleteFilters(...filters) {
|
|
906
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedFilterError());
|
|
643
907
|
if (!filters.length) {
|
|
644
908
|
return this;
|
|
645
909
|
}
|
|
@@ -647,12 +911,14 @@ class NgQubeeService {
|
|
|
647
911
|
return this;
|
|
648
912
|
}
|
|
649
913
|
/**
|
|
650
|
-
* Remove selected related models from the query builder state
|
|
914
|
+
* Remove selected related models from the query builder state (Spatie only)
|
|
651
915
|
*
|
|
652
|
-
* @param {string[]} includes Models to remove
|
|
653
|
-
* @returns
|
|
916
|
+
* @param {string[]} includes - Models to remove
|
|
917
|
+
* @returns {this}
|
|
918
|
+
* @throws {UnsupportedIncludesError} If the active driver does not support includes
|
|
654
919
|
*/
|
|
655
920
|
deleteIncludes(...includes) {
|
|
921
|
+
this._assertDriver([DriverEnum.SPATIE], new UnsupportedIncludesError());
|
|
656
922
|
if (!includes.length) {
|
|
657
923
|
return this;
|
|
658
924
|
}
|
|
@@ -660,23 +926,66 @@ class NgQubeeService {
|
|
|
660
926
|
return this;
|
|
661
927
|
}
|
|
662
928
|
/**
|
|
663
|
-
* Remove
|
|
929
|
+
* Remove operator filters by field name (NestJS only)
|
|
664
930
|
*
|
|
665
|
-
* @param
|
|
931
|
+
* @param {string[]} fields - Field names of operator filters to remove
|
|
666
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)
|
|
971
|
+
*
|
|
972
|
+
* @param sorts - Fields used for sorting to remove
|
|
973
|
+
* @returns {this}
|
|
974
|
+
* @throws {UnsupportedSortError} If the active driver does not support sorts
|
|
667
975
|
*/
|
|
668
976
|
deleteSorts(...sorts) {
|
|
977
|
+
this._assertDriver([DriverEnum.SPATIE, DriverEnum.NESTJS], new UnsupportedSortError());
|
|
669
978
|
this._nestService.deleteSorts(...sorts);
|
|
670
979
|
return this;
|
|
671
980
|
}
|
|
672
981
|
/**
|
|
673
|
-
* Generate
|
|
982
|
+
* Generate a URI accordingly to the given data and active driver
|
|
674
983
|
*
|
|
675
|
-
* @returns {Observable<string>} An observable that emits the generated
|
|
984
|
+
* @returns {Observable<string>} An observable that emits the generated URI
|
|
676
985
|
*/
|
|
677
986
|
generateUri() {
|
|
678
987
|
try {
|
|
679
|
-
this._uri$.next(this.
|
|
988
|
+
this._uri$.next(this._requestStrategy.buildUri(this._nestService.nest(), this._options));
|
|
680
989
|
return this.uri$;
|
|
681
990
|
}
|
|
682
991
|
catch (error) {
|
|
@@ -693,9 +1002,9 @@ class NgQubeeService {
|
|
|
693
1002
|
return this;
|
|
694
1003
|
}
|
|
695
1004
|
/**
|
|
696
|
-
* Set the base
|
|
1005
|
+
* Set the base URL to use for composing the address
|
|
697
1006
|
*
|
|
698
|
-
* @param {string} baseUrl
|
|
1007
|
+
* @param {string} baseUrl - The base URL
|
|
699
1008
|
* @returns {this}
|
|
700
1009
|
*/
|
|
701
1010
|
setBaseUrl(baseUrl) {
|
|
@@ -705,7 +1014,7 @@ class NgQubeeService {
|
|
|
705
1014
|
/**
|
|
706
1015
|
* Set the items per page number
|
|
707
1016
|
*
|
|
708
|
-
* @param limit
|
|
1017
|
+
* @param limit - Number of items per page
|
|
709
1018
|
* @returns {this}
|
|
710
1019
|
*/
|
|
711
1020
|
setLimit(limit) {
|
|
@@ -713,91 +1022,698 @@ class NgQubeeService {
|
|
|
713
1022
|
return this;
|
|
714
1023
|
}
|
|
715
1024
|
/**
|
|
716
|
-
* Set the
|
|
717
|
-
* - I.e. the model "users" will return /users
|
|
1025
|
+
* Set the page that the backend will use to paginate the result set
|
|
718
1026
|
*
|
|
719
|
-
* @param
|
|
1027
|
+
* @param page - Page number
|
|
720
1028
|
* @returns {this}
|
|
721
1029
|
*/
|
|
722
|
-
|
|
723
|
-
this._nestService.
|
|
1030
|
+
setPage(page) {
|
|
1031
|
+
this._nestService.page = page;
|
|
724
1032
|
return this;
|
|
725
1033
|
}
|
|
726
1034
|
/**
|
|
727
|
-
* Set the
|
|
1035
|
+
* Set the API resource to run the query against
|
|
728
1036
|
*
|
|
729
|
-
* @param
|
|
1037
|
+
* @param {string} resource - Resource name (e.g. 'users' produces /users)
|
|
730
1038
|
* @returns {this}
|
|
731
1039
|
*/
|
|
732
|
-
|
|
733
|
-
this._nestService.
|
|
1040
|
+
setResource(resource) {
|
|
1041
|
+
this._nestService.resource = resource;
|
|
734
1042
|
return this;
|
|
735
1043
|
}
|
|
736
|
-
|
|
737
|
-
|
|
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 });
|
|
738
1060
|
}
|
|
739
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1061
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeService, decorators: [{
|
|
740
1062
|
type: Injectable
|
|
741
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: [{
|
|
742
1070
|
type: Inject,
|
|
743
1071
|
args: ['QUERY_PARAMS_CONFIG']
|
|
744
1072
|
}, {
|
|
745
1073
|
type: Optional
|
|
746
1074
|
}] }] });
|
|
747
1075
|
|
|
748
|
-
class ResponseOptions {
|
|
749
|
-
currentPage;
|
|
750
|
-
data;
|
|
751
|
-
firstPageUrl;
|
|
752
|
-
from;
|
|
753
|
-
lastPage;
|
|
754
|
-
lastPageUrl;
|
|
755
|
-
nextPageUrl;
|
|
756
|
-
path;
|
|
757
|
-
perPage;
|
|
758
|
-
prevPageUrl;
|
|
759
|
-
to;
|
|
760
|
-
total;
|
|
761
|
-
constructor(options) {
|
|
762
|
-
this.currentPage = options.currentPage || 'current_page';
|
|
763
|
-
this.data = options.data || 'data';
|
|
764
|
-
this.firstPageUrl = options.firstPageUrl || 'first_page_url';
|
|
765
|
-
this.from = options.from || 'from';
|
|
766
|
-
this.lastPage = options.lastPage || 'last_page';
|
|
767
|
-
this.lastPageUrl = options.lastPageUrl || 'last_page_url';
|
|
768
|
-
this.nextPageUrl = options.nextPageUrl || 'next_page_url';
|
|
769
|
-
this.path = options.path || 'path';
|
|
770
|
-
this.perPage = options.perPage || 'per_page';
|
|
771
|
-
this.prevPageUrl = options.prevPageUrl || 'prev_page_url';
|
|
772
|
-
this.to = options.to || 'to';
|
|
773
|
-
this.total = options.total || 'total';
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
|
|
777
1076
|
class PaginationService {
|
|
1077
|
+
/**
|
|
1078
|
+
* Resolved response key name options
|
|
1079
|
+
*/
|
|
778
1080
|
_options;
|
|
779
|
-
|
|
1081
|
+
/**
|
|
1082
|
+
* The response strategy that parses responses for the active driver
|
|
1083
|
+
*/
|
|
1084
|
+
_responseStrategy;
|
|
1085
|
+
constructor(responseStrategy, options = {}) {
|
|
780
1086
|
this._options = new ResponseOptions(options);
|
|
1087
|
+
this._responseStrategy = responseStrategy;
|
|
781
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
|
+
*/
|
|
782
1097
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
783
1098
|
paginate(response) {
|
|
784
|
-
return
|
|
1099
|
+
return this._responseStrategy.paginate(response, this._options);
|
|
785
1100
|
}
|
|
786
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
787
|
-
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 });
|
|
788
1103
|
}
|
|
789
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1104
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: PaginationService, decorators: [{
|
|
790
1105
|
type: Injectable
|
|
791
1106
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1107
|
+
type: Inject,
|
|
1108
|
+
args: ['RESPONSE_STRATEGY']
|
|
1109
|
+
}] }, { type: undefined, decorators: [{
|
|
792
1110
|
type: Inject,
|
|
793
1111
|
args: ['RESPONSE_OPTIONS']
|
|
794
1112
|
}, {
|
|
795
1113
|
type: Optional
|
|
796
1114
|
}] }] });
|
|
797
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
|
+
}
|
|
798
1705
|
// @dynamic
|
|
799
1706
|
class NgQubeeModule {
|
|
800
|
-
|
|
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);
|
|
801
1717
|
return {
|
|
802
1718
|
ngModule: NgQubeeModule,
|
|
803
1719
|
providers: [
|
|
@@ -805,52 +1721,99 @@ class NgQubeeModule {
|
|
|
805
1721
|
{
|
|
806
1722
|
deps: [NestService],
|
|
807
1723
|
provide: NgQubeeService,
|
|
808
|
-
useFactory: (nestService) => new NgQubeeService(nestService, Object.assign({}, config.request))
|
|
809
|
-
},
|
|
1724
|
+
useFactory: (nestService) => new NgQubeeService(nestService, requestStrategy, driver, Object.assign({}, config.request))
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
810
1727
|
provide: PaginationService,
|
|
811
|
-
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
|
+
}
|
|
812
1734
|
}
|
|
813
1735
|
]
|
|
814
1736
|
};
|
|
815
1737
|
}
|
|
816
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "
|
|
817
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "
|
|
818
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "
|
|
819
|
-
deps: [NestService],
|
|
820
|
-
provide: NgQubeeService,
|
|
821
|
-
useFactory: (nestService) => new NgQubeeService(nestService, {})
|
|
822
|
-
}] });
|
|
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 });
|
|
823
1741
|
}
|
|
824
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "
|
|
1742
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.3", ngImport: i0, type: NgQubeeModule, decorators: [{
|
|
825
1743
|
type: NgModule,
|
|
826
|
-
args: [{
|
|
827
|
-
providers: [{
|
|
828
|
-
deps: [NestService],
|
|
829
|
-
provide: NgQubeeService,
|
|
830
|
-
useFactory: (nestService) => new NgQubeeService(nestService, {})
|
|
831
|
-
}]
|
|
832
|
-
}]
|
|
1744
|
+
args: [{}]
|
|
833
1745
|
}] });
|
|
834
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
|
+
}
|
|
835
1779
|
/**
|
|
836
1780
|
* Sets up providers necessary to enable `NgQubee` functionality for the application.
|
|
837
1781
|
*
|
|
838
1782
|
* @usageNotes
|
|
839
1783
|
*
|
|
840
|
-
* Basic example
|
|
1784
|
+
* Basic example with the Laravel driver:
|
|
841
1785
|
* ```
|
|
842
|
-
*
|
|
1786
|
+
* bootstrapApplication(AppComponent, {
|
|
1787
|
+
* providers: [provideNgQubee({ driver: DriverEnum.LARAVEL })]
|
|
1788
|
+
* });
|
|
1789
|
+
* ```
|
|
1790
|
+
*
|
|
1791
|
+
* Spatie driver example:
|
|
1792
|
+
* ```
|
|
1793
|
+
* import { DriverEnum } from 'ng-qubee';
|
|
843
1794
|
*
|
|
844
1795
|
* bootstrapApplication(AppComponent, {
|
|
845
|
-
* 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 })]
|
|
846
1806
|
* });
|
|
847
1807
|
* ```
|
|
848
1808
|
*
|
|
849
1809
|
* @publicApi
|
|
850
|
-
* @param config Configuration object compliant to the IConfig interface
|
|
1810
|
+
* @param config - Configuration object compliant to the IConfig interface
|
|
851
1811
|
* @returns A set of providers to setup NgQubee
|
|
852
1812
|
*/
|
|
853
|
-
function provideNgQubee(config
|
|
1813
|
+
function provideNgQubee(config) {
|
|
1814
|
+
const driver = config.driver;
|
|
1815
|
+
const requestStrategy = resolveRequestStrategy(driver);
|
|
1816
|
+
const responseStrategy = resolveResponseStrategy(driver);
|
|
854
1817
|
return makeEnvironmentProviders([
|
|
855
1818
|
{
|
|
856
1819
|
provide: NestService,
|
|
@@ -859,14 +1822,44 @@ function provideNgQubee(config = {}) {
|
|
|
859
1822
|
{
|
|
860
1823
|
deps: [NestService],
|
|
861
1824
|
provide: NgQubeeService,
|
|
862
|
-
useFactory: (nestService) => new NgQubeeService(nestService, Object.assign({}, config.request))
|
|
863
|
-
},
|
|
1825
|
+
useFactory: (nestService) => new NgQubeeService(nestService, requestStrategy, driver, Object.assign({}, config.request))
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
864
1828
|
provide: PaginationService,
|
|
865
|
-
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
|
+
}
|
|
866
1835
|
}
|
|
867
1836
|
]);
|
|
868
1837
|
}
|
|
869
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
|
+
|
|
870
1863
|
/*
|
|
871
1864
|
* Public API Surface of angular-query-builder
|
|
872
1865
|
*/
|
|
@@ -875,5 +1868,5 @@ function provideNgQubee(config = {}) {
|
|
|
875
1868
|
* Generated bundle index. Do not edit.
|
|
876
1869
|
*/
|
|
877
1870
|
|
|
878
|
-
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 };
|
|
879
1872
|
//# sourceMappingURL=ng-qubee.mjs.map
|