@recursyve/nestjs-data-filter 11.4.1 → 11.5.1
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/lib/filter/controllers/dynamic-filter.controller.js +1 -1
- package/lib/filter/controllers/filter.controller.js +1 -1
- package/lib/filter/filter.service.d.ts +3 -1
- package/lib/filter/filter.service.js +33 -16
- package/lib/filter/filters/filter.d.ts +9 -4
- package/lib/filter/filters/filter.js +1 -1
- package/lib/filter/filters/options.filter.d.ts +1 -1
- package/lib/filter/filters/options.filter.js +8 -5
- package/lib/sequelize.utils.d.ts +1 -0
- package/lib/sequelize.utils.js +11 -0
- package/package.json +5 -2
|
@@ -33,7 +33,7 @@ class DynamicFilterController {
|
|
|
33
33
|
async filterCount(query, request) {
|
|
34
34
|
query = this.transformQuery(query, request);
|
|
35
35
|
const user = await this.getUser(request);
|
|
36
|
-
return this.filterService.count({ options: query, user });
|
|
36
|
+
return this.filterService.count({ options: query, request, user });
|
|
37
37
|
}
|
|
38
38
|
async downloadData(query, type, request) {
|
|
39
39
|
query = this.transformQuery(query, request);
|
|
@@ -31,7 +31,7 @@ class FilterController {
|
|
|
31
31
|
}
|
|
32
32
|
async filterCount(query, request) {
|
|
33
33
|
const user = await this.getUser(request);
|
|
34
|
-
return this.filterService.count({ options: query, user });
|
|
34
|
+
return this.filterService.count({ options: query, request, user });
|
|
35
35
|
}
|
|
36
36
|
async downloadData(query, type, request) {
|
|
37
37
|
const user = await this.getUser(request);
|
|
@@ -15,6 +15,7 @@ import { FilterConfigurationModel } from "./models/filter-configuration.model";
|
|
|
15
15
|
import { FilterResourceValueModel } from "./models/filter-resource-value.model";
|
|
16
16
|
interface CountParams<Users extends DataFilterUserModel> {
|
|
17
17
|
options: FilterQueryModel;
|
|
18
|
+
request: Request;
|
|
18
19
|
user?: Users | null;
|
|
19
20
|
}
|
|
20
21
|
interface DownloadDataParams<Users extends DataFilterUserModel, Request> {
|
|
@@ -49,7 +50,7 @@ export declare class FilterService<Data> {
|
|
|
49
50
|
count<Users extends DataFilterUserModel>(params: CountParams<Users>): Promise<number>;
|
|
50
51
|
filter<Request, Users extends DataFilterUserModel>(params: FilterParams<DataFilterUserModel, Request>): Promise<FilterResultModel<Data>>;
|
|
51
52
|
downloadData<Request>(params: DownloadDataParams<DataFilterUserModel, Request>): Promise<Buffer | string>;
|
|
52
|
-
getFindOptions(model: typeof M, query: QueryModel | undefined, data?: object): Promise<FindOptions>;
|
|
53
|
+
getFindOptions<Request>(model: typeof M, query: QueryModel | undefined, request: Request, user: DataFilterUserModel | null, data?: object): Promise<FindOptions>;
|
|
53
54
|
private convertNumber;
|
|
54
55
|
private init;
|
|
55
56
|
private getInclude;
|
|
@@ -58,6 +59,7 @@ export declare class FilterService<Data> {
|
|
|
58
59
|
private generateHavingOptions;
|
|
59
60
|
private addSearchCondition;
|
|
60
61
|
private addOrderCondition;
|
|
62
|
+
private getOptionsForCountAndIds;
|
|
61
63
|
private countTotalValues;
|
|
62
64
|
private findValues;
|
|
63
65
|
private getAccessControlWhereCondition;
|
|
@@ -90,7 +90,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
90
90
|
var _a, _b;
|
|
91
91
|
const { options } = params;
|
|
92
92
|
const user = (_a = params.user) !== null && _a !== void 0 ? _a : null;
|
|
93
|
-
const countOptions = await this.getFindOptions(this.repository.model, options.query);
|
|
93
|
+
const countOptions = await this.getFindOptions(this.repository.model, options.query, params.request, user);
|
|
94
94
|
if (options.search) {
|
|
95
95
|
this.addSearchCondition(options.search, countOptions, (_b = user === null || user === void 0 ? void 0 : user.language) !== null && _b !== void 0 ? _b : null);
|
|
96
96
|
}
|
|
@@ -103,7 +103,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
103
103
|
if (options.order) {
|
|
104
104
|
options.order = this.normalizeOrder(options.order);
|
|
105
105
|
}
|
|
106
|
-
const countOptions = await this.getFindOptions(this.repository.model, options.query, options.data);
|
|
106
|
+
const countOptions = await this.getFindOptions(this.repository.model, options.query, request, user, options.data);
|
|
107
107
|
if (options.search) {
|
|
108
108
|
this.addSearchCondition(options.search, countOptions, (_b = user === null || user === void 0 ? void 0 : user.language) !== null && _b !== void 0 ? _b : null);
|
|
109
109
|
}
|
|
@@ -113,8 +113,9 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
113
113
|
if (options.groupBy) {
|
|
114
114
|
countOptions.group = [sequelize_utils_1.SequelizeUtils.getGroupLiteral(this.repository.model, options.groupBy)];
|
|
115
115
|
}
|
|
116
|
-
const
|
|
117
|
-
const
|
|
116
|
+
const optionsForCountAndIds = this.getOptionsForCountAndIds(countOptions);
|
|
117
|
+
const total = await this.countTotalValues({ options: optionsForCountAndIds, user });
|
|
118
|
+
const values = await this.findValues({ filter: options, options: optionsForCountAndIds, request, user });
|
|
118
119
|
return {
|
|
119
120
|
total,
|
|
120
121
|
page: options.page,
|
|
@@ -124,7 +125,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
124
125
|
async downloadData(params) {
|
|
125
126
|
var _a, _b, _c;
|
|
126
127
|
const { exportOptions, options, request, type, user } = params;
|
|
127
|
-
const findOptions = await this.getFindOptions(this.exportRepository.model, options.query);
|
|
128
|
+
const findOptions = await this.getFindOptions(this.exportRepository.model, options.query, request, user);
|
|
128
129
|
if (options.order) {
|
|
129
130
|
options.order = this.normalizeOrder(options.order);
|
|
130
131
|
}
|
|
@@ -151,7 +152,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
151
152
|
return await this.repository.downloadData(values, type, (_c = user === null || user === void 0 ? void 0 : user.language) !== null && _c !== void 0 ? _c : "fr", exportOptions);
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
|
-
async getFindOptions(model, query, data) {
|
|
155
|
+
async getFindOptions(model, query, request, user, data) {
|
|
155
156
|
/**
|
|
156
157
|
* Reset Geo localization filter state
|
|
157
158
|
*/
|
|
@@ -163,7 +164,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
163
164
|
const havingConditions = [];
|
|
164
165
|
option = {
|
|
165
166
|
...option,
|
|
166
|
-
include: await this.getInclude(model, query, data),
|
|
167
|
+
include: await this.getInclude(model, query, request, user, data),
|
|
167
168
|
where: query.condition === "and" ? { [sequelize_1.Op.and]: whereConditions } : { [sequelize_1.Op.or]: whereConditions },
|
|
168
169
|
having: query.condition === "and" ? { [sequelize_1.Op.and]: havingConditions } : { [sequelize_1.Op.or]: havingConditions }
|
|
169
170
|
};
|
|
@@ -203,7 +204,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
203
204
|
this.definitions[key] = this.model[key];
|
|
204
205
|
}
|
|
205
206
|
}
|
|
206
|
-
async getInclude(model, query, data) {
|
|
207
|
+
async getInclude(model, query, request, user, data) {
|
|
207
208
|
if (!query.rules) {
|
|
208
209
|
return [];
|
|
209
210
|
}
|
|
@@ -211,7 +212,7 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
211
212
|
for (const rule of query.rules) {
|
|
212
213
|
const m = rule;
|
|
213
214
|
if (m.condition) {
|
|
214
|
-
includes.push(await this.getInclude(model, m, data));
|
|
215
|
+
includes.push(await this.getInclude(model, m, request, user, data));
|
|
215
216
|
continue;
|
|
216
217
|
}
|
|
217
218
|
const r = rule;
|
|
@@ -221,19 +222,19 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
221
222
|
const filter = this.definitions[r.id];
|
|
222
223
|
if (filter.rootFilter) {
|
|
223
224
|
const f = filter;
|
|
224
|
-
includes.push(...this.getFilterInclude(model, f.rootFilter, r, data));
|
|
225
|
+
includes.push(...this.getFilterInclude(model, f.rootFilter, r, request, user, data));
|
|
225
226
|
if (!f.lazyLoading) {
|
|
226
227
|
if (f.valueFilter) {
|
|
227
|
-
includes.push(...this.getFilterInclude(model, f.valueFilter, r, data));
|
|
228
|
+
includes.push(...this.getFilterInclude(model, f.valueFilter, r, request, user, data));
|
|
228
229
|
}
|
|
229
230
|
}
|
|
230
231
|
else if (f.getValueFilter && Array.isArray(r.value)) {
|
|
231
232
|
const valueFiler = await f.getValueFilter(r.value[0]);
|
|
232
|
-
includes.push(...this.getFilterInclude(model, valueFiler, r, data));
|
|
233
|
+
includes.push(...this.getFilterInclude(model, valueFiler, r, request, user, data));
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
else {
|
|
236
|
-
includes.push(...this.getFilterInclude(model, filter, r, data));
|
|
237
|
+
includes.push(...this.getFilterInclude(model, filter, r, request, user, data));
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
return sequelize_utils_1.SequelizeUtils.reduceIncludes(includes, true);
|
|
@@ -346,6 +347,16 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
346
347
|
options.include = sequelize_utils_1.SequelizeUtils.mergeIncludes(options.include, includes);
|
|
347
348
|
}
|
|
348
349
|
}
|
|
350
|
+
getOptionsForCountAndIds(options) {
|
|
351
|
+
const include = options.include;
|
|
352
|
+
if (!include || (Array.isArray(include) && include.length === 0)) {
|
|
353
|
+
return options;
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
...options,
|
|
357
|
+
include: sequelize_utils_1.SequelizeUtils.stripIncludeAttributes(include)
|
|
358
|
+
};
|
|
359
|
+
}
|
|
349
360
|
async countTotalValues(params) {
|
|
350
361
|
var _a;
|
|
351
362
|
const { options } = params;
|
|
@@ -354,9 +365,12 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
354
365
|
options.where = await this.getAccessControlWhereCondition(options.where, user);
|
|
355
366
|
}
|
|
356
367
|
if (options.having) {
|
|
368
|
+
const modelName = this.repository.model.name;
|
|
357
369
|
const data = await this.repository.model.findAll({
|
|
358
370
|
...options,
|
|
359
|
-
|
|
371
|
+
attributes: [[sequelize_typescript_1.Sequelize.literal(`DISTINCT \`${modelName}\`.\`id\``), "id"]],
|
|
372
|
+
subQuery: false,
|
|
373
|
+
raw: true
|
|
360
374
|
});
|
|
361
375
|
return data.length;
|
|
362
376
|
}
|
|
@@ -428,9 +442,9 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
428
442
|
}
|
|
429
443
|
return where;
|
|
430
444
|
}
|
|
431
|
-
getFilterInclude(model, filter, rule, data) {
|
|
445
|
+
getFilterInclude(model, filter, rule, request, user, data) {
|
|
432
446
|
const includes = [];
|
|
433
|
-
const paths = filter.getIncludePaths(rule, data);
|
|
447
|
+
const paths = filter.getIncludePaths(rule, request, user, data);
|
|
434
448
|
for (const path of paths) {
|
|
435
449
|
includes.push(this.sequelizeModelScanner.getIncludes(model, path, [], [], true));
|
|
436
450
|
}
|
|
@@ -440,6 +454,9 @@ let FilterService = FilterService_1 = class FilterService {
|
|
|
440
454
|
}
|
|
441
455
|
return includes;
|
|
442
456
|
}
|
|
457
|
+
if (typeof filter.where === "function") {
|
|
458
|
+
filter.where = filter.where({ request, user });
|
|
459
|
+
}
|
|
443
460
|
return [
|
|
444
461
|
...includes,
|
|
445
462
|
this.sequelizeModelScanner.getIncludes(model, {
|
|
@@ -14,8 +14,13 @@ export interface FilterConditionRule {
|
|
|
14
14
|
path?: string;
|
|
15
15
|
operation: FilterOperators;
|
|
16
16
|
value?: (unknown | unknown[]) | ((value: unknown | unknown[]) => unknown | unknown[]);
|
|
17
|
-
where?: IncludeWhereModel;
|
|
17
|
+
where?: IncludeWhereModel | IncludeWhereModalCallback;
|
|
18
18
|
}
|
|
19
|
+
export interface IncludeWhereModelContext<Request = unknown> {
|
|
20
|
+
request: Request;
|
|
21
|
+
user: DataFilterUserModel | null;
|
|
22
|
+
}
|
|
23
|
+
type IncludeWhereModalCallback = (context: IncludeWhereModelContext) => IncludeWhereModel;
|
|
19
24
|
export interface FilterCondition {
|
|
20
25
|
condition: Condition;
|
|
21
26
|
rules: (FilterConditionRule | FilterCondition)[];
|
|
@@ -39,7 +44,7 @@ export interface BaseFilterDefinition {
|
|
|
39
44
|
paranoid?: boolean;
|
|
40
45
|
condition?: FilterCondition;
|
|
41
46
|
group?: string;
|
|
42
|
-
where?: IncludeWhereModel;
|
|
47
|
+
where?: IncludeWhereModel | IncludeWhereModalCallback;
|
|
43
48
|
pathCondition?: PathCondition;
|
|
44
49
|
json?: JsonConfig;
|
|
45
50
|
enabled?: ({ user, request }: EnabledConfig) => Promise<boolean>;
|
|
@@ -52,7 +57,7 @@ export interface FilterDefinition extends BaseFilterDefinition, IRule {
|
|
|
52
57
|
type: FilterType;
|
|
53
58
|
operators: (FilterOperatorTypes | CustomOperator)[];
|
|
54
59
|
getConfig<Request>(key: string, req: Request, user?: DataFilterUserModel): Promise<FilterBaseConfigurationModel | null>;
|
|
55
|
-
getIncludePaths(rule: QueryRuleModel, data?: object): PathModel[];
|
|
60
|
+
getIncludePaths<Request>(rule: QueryRuleModel, request: Request, user: DataFilterUserModel | null, data?: object): PathModel[];
|
|
56
61
|
getWhereOptions(rule: QueryRuleModel): Promise<WhereOptions | undefined>;
|
|
57
62
|
getHavingOptions(rule: QueryRuleModel): Promise<WhereOptions | undefined>;
|
|
58
63
|
usePathCondition(query: QueryModel): boolean;
|
|
@@ -79,7 +84,7 @@ export declare abstract class Filter implements FilterDefinition {
|
|
|
79
84
|
removeOperators(...operator: (FilterOperatorTypes | CustomOperator)[]): Filter;
|
|
80
85
|
setOperators(...operator: (FilterOperatorTypes | CustomOperator)[]): Filter;
|
|
81
86
|
getConfig<Request = any>(key: string, request: Request, user?: DataFilterUserModel): Promise<FilterBaseConfigurationModel | null>;
|
|
82
|
-
getIncludePaths(rule: QueryRuleModel, data?: object): PathModel[];
|
|
87
|
+
getIncludePaths<Request>(rule: QueryRuleModel, request: Request, user: DataFilterUserModel | null, data?: object): PathModel[];
|
|
83
88
|
getWhereOptions(rule: QueryRuleModel, name?: string): Promise<WhereOptions | undefined>;
|
|
84
89
|
getHavingOptions(rule: QueryRuleModel): Promise<WhereOptions | undefined>;
|
|
85
90
|
usePathCondition(query: QueryModel): boolean;
|
|
@@ -33,7 +33,7 @@ export declare class OptionsFilter extends Filter implements OptionsFilterDefini
|
|
|
33
33
|
options: OptionsFilterOption[];
|
|
34
34
|
constructor(definition: BaseFilterDefinition & OptionsFilterDefinition);
|
|
35
35
|
getConfig<Request>(key: string, request: Request, user?: DataFilterUserModel): Promise<OptionsFilterConfigurationModel | null>;
|
|
36
|
-
getIncludePaths(rule: QueryRuleModel, data?: object): PathModel[];
|
|
36
|
+
getIncludePaths<Request>(rule: QueryRuleModel, request: Request, user: DataFilterUserModel | null, data?: object): PathModel[];
|
|
37
37
|
getWhereOptions(rule: QueryRuleModel): Promise<WhereOptions | undefined>;
|
|
38
38
|
getHavingOptions(rule: QueryRuleModel): Promise<WhereOptions | undefined>;
|
|
39
39
|
private getRulePaths;
|
|
@@ -51,13 +51,13 @@ class OptionsFilter extends filter_1.Filter {
|
|
|
51
51
|
}))
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
getIncludePaths(rule, data) {
|
|
54
|
+
getIncludePaths(rule, request, user, data) {
|
|
55
55
|
const option = this.options.find(x => x.key === rule.value);
|
|
56
56
|
const condition = option === null || option === void 0 ? void 0 : option.condition;
|
|
57
57
|
if (!condition) {
|
|
58
|
-
return super.getIncludePaths(rule);
|
|
58
|
+
return super.getIncludePaths(rule, request, user);
|
|
59
59
|
}
|
|
60
|
-
return this.getRulePaths(condition.rules, data);
|
|
60
|
+
return this.getRulePaths(condition.rules, request, user, data);
|
|
61
61
|
}
|
|
62
62
|
async getWhereOptions(rule) {
|
|
63
63
|
const option = this.options.find(x => x.key === rule.value);
|
|
@@ -93,11 +93,11 @@ class OptionsFilter extends filter_1.Filter {
|
|
|
93
93
|
operation: (_a = option.operator) !== null && _a !== void 0 ? _a : rule.operation
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
-
getRulePaths(rules, data) {
|
|
96
|
+
getRulePaths(rules, request, user, data) {
|
|
97
97
|
const paths = [];
|
|
98
98
|
for (const rule of rules) {
|
|
99
99
|
if (rule.rules) {
|
|
100
|
-
paths.push(...this.getRulePaths(rule.rules, data));
|
|
100
|
+
paths.push(...this.getRulePaths(rule.rules, request, user, data));
|
|
101
101
|
}
|
|
102
102
|
else if (rule.path) {
|
|
103
103
|
if (paths.every((path) => path.path !== rule.path)) {
|
|
@@ -105,6 +105,9 @@ class OptionsFilter extends filter_1.Filter {
|
|
|
105
105
|
if (!r.path) {
|
|
106
106
|
continue;
|
|
107
107
|
}
|
|
108
|
+
if (typeof r.where === "function") {
|
|
109
|
+
r.where = r.where({ request, user });
|
|
110
|
+
}
|
|
108
111
|
paths.push({
|
|
109
112
|
path: r.path,
|
|
110
113
|
where: filter_utils_1.FilterUtils.generateWhereConditions(r.where, data)
|
package/lib/sequelize.utils.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare class M extends Model {
|
|
|
11
11
|
}
|
|
12
12
|
export declare class SequelizeUtils {
|
|
13
13
|
static reduceIncludes(includes: Array<IncludeOptions | IncludeOptions[]>, ignoreAttributes?: boolean): Includeable[];
|
|
14
|
+
static stripIncludeAttributes(includes: IncludeOptions[] | IncludeOptions): IncludeOptions[];
|
|
14
15
|
static mergeIncludes(a?: IncludeOptions | IncludeOptions[], b?: IncludeOptions | IncludeOptions[], ignoreAttributes?: boolean): IncludeOptions[];
|
|
15
16
|
static mergeAttributes(a: FindAttributeOptions | undefined, b: FindAttributeOptions | undefined): FindAttributeOptions | undefined;
|
|
16
17
|
static ensureAttributesValidity(attribute: FindAttributeOptions): FindAttributeOptions;
|
package/lib/sequelize.utils.js
CHANGED
|
@@ -15,6 +15,17 @@ class SequelizeUtils {
|
|
|
15
15
|
}
|
|
16
16
|
return include;
|
|
17
17
|
}
|
|
18
|
+
static stripIncludeAttributes(includes) {
|
|
19
|
+
const arr = !includes ? [] : Array.isArray(includes) ? includes : [includes];
|
|
20
|
+
if (!arr.length) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return arr.map((include) => ({
|
|
24
|
+
...include,
|
|
25
|
+
attributes: [],
|
|
26
|
+
include: include.include ? this.stripIncludeAttributes(include.include) : [],
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
18
29
|
static mergeIncludes(a = [], b = [], ignoreAttributes = false) {
|
|
19
30
|
var _a, _b, _c, _d;
|
|
20
31
|
if (!Array.isArray(a)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recursyve/nestjs-data-filter",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.5.1",
|
|
4
4
|
"description": "NestJs DataFilter library",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
"pack:lib": "npm run build:lib && cd dist && npm pack",
|
|
11
11
|
"publish-package": "npm run build:lib && npm run copy:lib && npm publish ./dist --access public",
|
|
12
12
|
"publish-package:beta": "npm run publish-package -- --tag beta --access public",
|
|
13
|
-
"publish-package:dry-run": "npm run publish-package -- --dry-run"
|
|
13
|
+
"publish-package:dry-run": "npm run publish-package -- --dry-run",
|
|
14
|
+
"test": "jest",
|
|
15
|
+
"test:watch": "jest --watch",
|
|
16
|
+
"test:coverage": "jest --coverage"
|
|
14
17
|
},
|
|
15
18
|
"repository": {
|
|
16
19
|
"type": "git",
|