@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.
@@ -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 total = await this.countTotalValues({ options: countOptions, user });
117
- const values = await this.findValues({ filter: options, options: countOptions, request, user });
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
- subQuery: false
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;
@@ -63,7 +63,7 @@ class Filter {
63
63
  }
64
64
  return config;
65
65
  }
66
- getIncludePaths(rule, data) {
66
+ getIncludePaths(rule, request, user, data) {
67
67
  return [];
68
68
  }
69
69
  async getWhereOptions(rule, name) {
@@ -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)
@@ -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;
@@ -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.4.1",
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",