@autofleet/sheilta 1.2.3-beta1 → 1.2.3-beta2

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.
@@ -1,6 +1,18 @@
1
1
  declare type OrderItem = string | [string, string];
2
2
  declare type SequelizeOrder = string | OrderItem[];
3
- export declare const formatOrder: ({ order, }: {
4
- order?: any[];
5
- }) => SequelizeOrder[] | undefined;
6
- export {};
3
+ declare const formatPayload: ({ order, page, perPage, include, query, attributes, searchTerm, }: {
4
+ order: any;
5
+ page?: number;
6
+ perPage?: number;
7
+ include?: any[];
8
+ query?: {};
9
+ attributes?: any;
10
+ searchTerm?: any;
11
+ }, model?: any) => {
12
+ query: {};
13
+ order: SequelizeOrder[];
14
+ page: any;
15
+ perPage: any;
16
+ include: any;
17
+ };
18
+ export default formatPayload;
@@ -1,15 +1,67 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.formatOrder = void 0;
6
+ const lodash_1 = __importDefault(require("lodash"));
4
7
  const utils_1 = require("../utils");
5
8
  const DESCENDING_KEY = 'DESC';
6
- exports.formatOrder = ({ order = [], }) => (order.length === 0 ? undefined :
9
+ const formatOrder = ({ order = [], associationModels = [], }) => (order.length === 0 ? undefined :
7
10
  order.map((o) => {
11
+ const formattedOrder = [utils_1.extractAttributeNameFromOrder(o, associationModels)];
8
12
  const isOrderDescOrder = utils_1.isOrderDesc(o);
9
- let column = o;
13
+ const isOrderAssociation = utils_1.isAttributeByAssociation(isOrderDescOrder
14
+ ? o.split(utils_1.ORDER_PREFIX)[1]
15
+ : o, associationModels);
16
+ if (isOrderAssociation) {
17
+ formattedOrder.push(utils_1.extractAssociatedAttributeNameFromOrder(o));
18
+ }
10
19
  if (isOrderDescOrder) {
11
- column = utils_1.extractAttributeNameFromOrder(o);
12
- return [column, DESCENDING_KEY];
20
+ formattedOrder.push(DESCENDING_KEY);
13
21
  }
14
- return [column];
22
+ return formattedOrder;
15
23
  }));
24
+ const formatPage = page => page || utils_1.PAGE_DEFAULT;
25
+ const formatPerPage = perPage => perPage || utils_1.PER_PAGE_DEFAULT;
26
+ const formatInclude = (include, associationsMap = {}) => {
27
+ let formattedInclude = include.map(i => (Object.assign(Object.assign({}, i), { association: associationsMap[i.association || i.model], required: i.required !== 'false' })));
28
+ formattedInclude = formattedInclude.map(i => lodash_1.default.omit(i, ['model']));
29
+ return formattedInclude;
30
+ };
31
+ const formatQuery = (query, associationModels) => Object.keys(query).reduce((acc, queryItemKey) => (Object.assign(Object.assign({}, acc), { [utils_1.isAttributeByAssociation(queryItemKey, associationModels)
32
+ ? utils_1.wrapAttributeWithOperator(queryItemKey) : queryItemKey]: query[queryItemKey] })), {});
33
+ const formatSearchTerm = (searchTerm, attributesToSend) => ({
34
+ $or: attributesToSend.map(attr => ({
35
+ [attr]: {
36
+ $ilike: searchTerm,
37
+ },
38
+ })),
39
+ });
40
+ const formatPayload = ({ order, page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, include = [], query = {}, attributes = null, searchTerm = null, }, model) => {
41
+ const associationModels = Object.keys((model === null || model === void 0 ? void 0 : model.associations) || {});
42
+ const formattedOrder = formatOrder({
43
+ order,
44
+ associationModels,
45
+ });
46
+ const formattedInclude = formatInclude(include, model === null || model === void 0 ? void 0 : model.associations);
47
+ const formattedPage = formatPage(page);
48
+ const formattedPerPage = formatPerPage(perPage);
49
+ let formattedQuery = formatQuery(query, associationModels);
50
+ if (searchTerm) {
51
+ const attributesToSend = (attributes === null || attributes === void 0 ? void 0 : attributes.length) ? attributes : Object.keys(model.rawAttributes);
52
+ formattedQuery = {
53
+ $and: [
54
+ formattedQuery,
55
+ formatSearchTerm(searchTerm, attributesToSend),
56
+ ],
57
+ };
58
+ }
59
+ return {
60
+ query: formattedQuery,
61
+ order: formattedOrder,
62
+ page: formattedPage,
63
+ perPage: formattedPerPage,
64
+ include: formattedInclude,
65
+ };
66
+ };
67
+ exports.default = formatPayload;
@@ -1,17 +1,135 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const _1 = require(".");
6
+ const _1 = __importDefault(require("."));
4
7
  describe('formatting test', () => {
5
- it('check order formatting', () => {
6
- const payload = _1.formatOrder({
7
- order: ['startTime', '-endTime'],
8
+ describe('query formatting', () => {
9
+ it('query json field', () => {
10
+ const { query } = _1.default({
11
+ order: [],
12
+ query: {
13
+ 'json.field': {
14
+ $gt: 4,
15
+ },
16
+ },
17
+ }, {
18
+ rawAttributes: {
19
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', json: '',
20
+ },
21
+ });
22
+ expect(JSON.stringify(query)).toBe(JSON.stringify({ 'json.field': { $gt: 4 } }));
23
+ });
24
+ it('query included model field', () => {
25
+ const { query } = _1.default({
26
+ order: [],
27
+ query: {
28
+ 'status.field': {
29
+ $gt: 4,
30
+ },
31
+ },
32
+ }, {
33
+ rawAttributes: {
34
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
35
+ },
36
+ associations: {
37
+ status: {},
38
+ },
39
+ });
40
+ expect(JSON.stringify(query)).toBe(JSON.stringify({ '$status.field$': { $gt: 4 } }));
41
+ });
42
+ it('query with searchTerm', () => {
43
+ const searchTerm = 'aviv';
44
+ const { query } = _1.default({
45
+ order: [],
46
+ query: {
47
+ 'json.field': {
48
+ $gt: 4,
49
+ },
50
+ },
51
+ searchTerm,
52
+ }, {
53
+ rawAttributes: {
54
+ startTime: '', endTime: '',
55
+ },
56
+ });
57
+ expect(JSON.stringify(query)).toBe(JSON.stringify({
58
+ $and: [{ 'json.field': { $gt: 4 } }, {
59
+ $or: [
60
+ {
61
+ startTime: {
62
+ $ilike: searchTerm,
63
+ },
64
+ },
65
+ {
66
+ endTime: {
67
+ $ilike: searchTerm,
68
+ },
69
+ },
70
+ ],
71
+ }],
72
+ }));
8
73
  });
9
- expect(JSON.stringify(payload)).toBe(JSON.stringify([['startTime'], ['endTime', 'DESC']]));
10
74
  });
11
- it('check order formatting', () => {
12
- const payload = _1.formatOrder({
13
- order: ['startTime', 'endTime'],
75
+ describe('order formatting', () => {
76
+ it('simple check', () => {
77
+ const { order } = _1.default({
78
+ order: ['startTime', '-endTime'],
79
+ });
80
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['startTime'], ['endTime', 'DESC']]));
81
+ });
82
+ it('simple check asc', () => {
83
+ const { order } = _1.default({
84
+ order: ['startTime', 'endTime'],
85
+ });
86
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['startTime'], ['endTime']]));
87
+ });
88
+ it('by included model', () => {
89
+ const { order } = _1.default({
90
+ order: ['status.name'],
91
+ }, {
92
+ rawAttributes: {
93
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
94
+ },
95
+ associations: {
96
+ status: {},
97
+ },
98
+ });
99
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status', 'name']]));
100
+ });
101
+ it('by included model descending', () => {
102
+ const { order } = _1.default({
103
+ order: ['-status.name'],
104
+ }, {
105
+ rawAttributes: {
106
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
107
+ },
108
+ associations: {
109
+ status: {},
110
+ },
111
+ });
112
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status', 'name', 'DESC']]));
113
+ });
114
+ it('by json field', () => {
115
+ const { order } = _1.default({
116
+ order: ['status.name'],
117
+ }, {
118
+ rawAttributes: {
119
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', json: '',
120
+ },
121
+ });
122
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status.name']]));
123
+ });
124
+ it('by json field descending', () => {
125
+ const { order } = _1.default({
126
+ order: ['-status.name'],
127
+ }, {
128
+ rawAttributes: {
129
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', json: '',
130
+ },
131
+ });
132
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status.name', 'DESC']]));
14
133
  });
15
- expect(JSON.stringify(payload)).toBe(JSON.stringify([['startTime'], ['endTime']]));
16
134
  });
17
135
  });
@@ -1,2 +1,4 @@
1
+ export declare const newQueryValidationMiddleware: (inner?: string) => (model: any) => (req: any, res: any, next: any) => Promise<any>;
2
+ export declare const newQueryFormatMiddleware: (inner?: string) => (model: any) => (req: any, res: any, next: any) => Promise<any>;
1
3
  export declare const queryValidationMiddleware: (model: any) => (req: any, res: any, next: any) => Promise<any>;
2
- export declare const queryFormatMiddleware: () => (req: any, res: any, next: any) => Promise<any>;
4
+ export declare const queryFormatMiddleware: (model: any) => (req: any, res: any, next: any) => Promise<any>;
@@ -8,18 +8,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
11
14
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.queryFormatMiddleware = exports.queryValidationMiddleware = void 0;
15
+ exports.queryFormatMiddleware = exports.queryValidationMiddleware = exports.newQueryFormatMiddleware = exports.newQueryValidationMiddleware = void 0;
13
16
  const errors_1 = require("@autofleet/errors");
14
- const formatter_1 = require("../formatter");
17
+ const formatter_1 = __importDefault(require("../formatter"));
15
18
  const validations_1 = require("../validations");
16
- exports.queryValidationMiddleware = model => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
17
- const { query, attributes, order, } = req.body;
19
+ exports.newQueryValidationMiddleware = (inner = 'body') => model => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const { query, attributes, order, page, perPage, include, } = req[inner];
18
21
  try {
19
22
  validations_1.validatePayload({
20
23
  query,
21
24
  attributes,
22
25
  order,
26
+ page,
27
+ perPage,
28
+ include,
23
29
  }, model);
24
30
  return next();
25
31
  }
@@ -35,11 +41,23 @@ exports.queryValidationMiddleware = model => (req, res, next) => __awaiter(void
35
41
  });
36
42
  }
37
43
  });
38
- exports.queryFormatMiddleware = () => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
39
- const { order, } = req.body;
40
- const formattedOrder = formatter_1.formatOrder({
44
+ exports.newQueryFormatMiddleware = (inner = 'body') => model => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
45
+ const { order, page, perPage, include, query, attributes, searchTerm, } = req[inner];
46
+ const { query: formattedQuery, order: formattedOrder, page: formattedPage, perPage: formattedPerPage, include: formattedInclude, } = formatter_1.default({
47
+ query,
41
48
  order,
42
- });
43
- req.body.order = formattedOrder;
49
+ page,
50
+ perPage,
51
+ include,
52
+ attributes,
53
+ searchTerm,
54
+ }, model);
55
+ req[inner].query = formattedQuery;
56
+ req[inner].order = formattedOrder;
57
+ req[inner].page = formattedPage;
58
+ req[inner].perPage = formattedPerPage;
59
+ req[inner].include = formattedInclude;
44
60
  return next();
45
61
  });
62
+ exports.queryValidationMiddleware = exports.newQueryValidationMiddleware();
63
+ exports.queryFormatMiddleware = exports.newQueryFormatMiddleware();
@@ -13,10 +13,13 @@ exports.OPERATORS = [
13
13
  'notIn',
14
14
  'is',
15
15
  'like',
16
+ 'ilike',
16
17
  'notLike',
17
18
  'between',
18
19
  'and',
19
20
  'or',
21
+ 'overlap',
22
+ 'contains',
20
23
  ];
21
24
  exports.OPERATOR_PREFIX = '$';
22
25
  exports.formatOperators = (Sequelize) => {
package/lib/utils.d.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  export declare const ORDER_PREFIX = "-";
2
- export declare const extractAttributeNameFromOrder: (order: any) => string;
2
+ export declare const ASSOCIATION_PREFIX = ".";
3
+ export declare const PER_PAGE_DEFAULT = 20;
4
+ export declare const PAGE_DEFAULT = 1;
5
+ export declare const PER_PAGE_MAX_LIMIT = 100;
6
+ export declare const PER_PAGE_MIN_LIMIT = 1;
7
+ export declare const PAGE_MIN = 1;
8
+ export declare const wrapAttributeWithOperator: (attribute: any) => string;
9
+ export declare const isAttributeByAssociation: (attributeName: any, associatedModels: any) => boolean;
10
+ export declare const extractAttributeNameFromOrder: (order: any, associationModels: any) => string;
3
11
  export declare const isOrderDesc: (order: any) => boolean;
12
+ export declare const throwBadRequestError: (message: string) => never;
13
+ export declare const extractAssociatedAttributeNameFromOrder: (order: any) => string;
package/lib/utils.js CHANGED
@@ -1,6 +1,31 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isOrderDesc = exports.extractAttributeNameFromOrder = exports.ORDER_PREFIX = void 0;
3
+ exports.extractAssociatedAttributeNameFromOrder = exports.throwBadRequestError = exports.isOrderDesc = exports.extractAttributeNameFromOrder = exports.isAttributeByAssociation = exports.wrapAttributeWithOperator = exports.PAGE_MIN = exports.PER_PAGE_MIN_LIMIT = exports.PER_PAGE_MAX_LIMIT = exports.PAGE_DEFAULT = exports.PER_PAGE_DEFAULT = exports.ASSOCIATION_PREFIX = exports.ORDER_PREFIX = void 0;
4
+ const errors_1 = require("@autofleet/errors");
5
+ const operators_1 = require("./operators");
4
6
  exports.ORDER_PREFIX = '-';
5
- exports.extractAttributeNameFromOrder = (order) => order.split(exports.ORDER_PREFIX)[1];
7
+ exports.ASSOCIATION_PREFIX = '.';
8
+ exports.PER_PAGE_DEFAULT = 20;
9
+ exports.PAGE_DEFAULT = 1;
10
+ exports.PER_PAGE_MAX_LIMIT = 100;
11
+ exports.PER_PAGE_MIN_LIMIT = 1;
12
+ exports.PAGE_MIN = 1;
13
+ exports.wrapAttributeWithOperator = attribute => `${operators_1.OPERATOR_PREFIX}${attribute}${operators_1.OPERATOR_PREFIX}`;
14
+ exports.isAttributeByAssociation = (attributeName, associatedModels) => attributeName.includes(exports.ASSOCIATION_PREFIX)
15
+ && associatedModels.includes(attributeName.split(exports.ASSOCIATION_PREFIX)[0]);
16
+ exports.extractAttributeNameFromOrder = (order, associationModels) => {
17
+ let formattedOrder = order;
18
+ if (order.includes(exports.ORDER_PREFIX)) {
19
+ // eslint-disable-next-line prefer-destructuring
20
+ formattedOrder = formattedOrder.split(exports.ORDER_PREFIX)[1];
21
+ }
22
+ if (exports.isAttributeByAssociation(formattedOrder, associationModels)) {
23
+ [formattedOrder] = formattedOrder.split(exports.ASSOCIATION_PREFIX);
24
+ }
25
+ return formattedOrder;
26
+ };
6
27
  exports.isOrderDesc = (order) => order.includes(exports.ORDER_PREFIX);
28
+ exports.throwBadRequestError = (message) => {
29
+ throw new errors_1.BadRequest([{ message }], null);
30
+ };
31
+ exports.extractAssociatedAttributeNameFromOrder = (order) => order.split(exports.ASSOCIATION_PREFIX)[1];
@@ -1,5 +1,8 @@
1
- export declare const validatePayload: ({ query, order, attributes, }: {
1
+ export declare const validatePayload: ({ query, order, attributes, include, page, perPage, }: {
2
2
  query?: {};
3
3
  order?: any[];
4
4
  attributes?: any[];
5
- }, model: any) => boolean;
5
+ include?: any[];
6
+ page?: number;
7
+ perPage?: number;
8
+ }, model?: any) => boolean;
@@ -4,56 +4,90 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.validatePayload = void 0;
7
- const errors_1 = require("@autofleet/errors");
8
7
  const lodash_1 = __importDefault(require("lodash"));
9
8
  const utils_1 = require("../utils");
10
9
  const operators_1 = require("../operators");
11
10
  const validateOperator = (operator) => operators_1.OPERATORS.includes(operator.split(operators_1.OPERATOR_PREFIX)[1]);
12
- const validateQueryAttribute = (attribute, modelAttributes) => modelAttributes.includes(attribute);
13
- const validateSingleOrder = (currentOrder, rawAttributes) => {
11
+ const validateQueryAttribute = (attribute, modelAttributes = [], associationModels = []) => [...modelAttributes, ...associationModels]
12
+ .includes(attribute.includes(utils_1.ASSOCIATION_PREFIX)
13
+ ? attribute.split(utils_1.ASSOCIATION_PREFIX)[0] : attribute);
14
+ const validateSingleOrder = (currentOrder, rawAttributes, associationModels) => {
14
15
  const isOrderDescOrder = utils_1.isOrderDesc(currentOrder);
15
16
  if (isOrderDescOrder && currentOrder[0] !== utils_1.ORDER_PREFIX) {
16
- throw new errors_1.BadRequest(`${utils_1.ORDER_PREFIX} must be only at the beginning of the word`, null);
17
+ utils_1.throwBadRequestError(`${utils_1.ORDER_PREFIX} must be only at the beginning of the word`);
17
18
  }
18
- const formattedOrderString = isOrderDescOrder ?
19
- utils_1.extractAttributeNameFromOrder(currentOrder) :
20
- currentOrder;
21
- if (!rawAttributes.includes(formattedOrderString)) {
22
- throw new errors_1.BadRequest(`${currentOrder} is invalid`, null);
19
+ const orderStringWithoutDesc = isOrderDescOrder ?
20
+ currentOrder.split(utils_1.ORDER_PREFIX)[1] : currentOrder;
21
+ const isOrderAssociation = utils_1.isAttributeByAssociation(orderStringWithoutDesc, associationModels);
22
+ let formattedOrderString = utils_1.extractAttributeNameFromOrder(currentOrder, associationModels);
23
+ if (!isOrderAssociation && formattedOrderString.includes(utils_1.ASSOCIATION_PREFIX)) {
24
+ [formattedOrderString] = formattedOrderString.split(utils_1.ASSOCIATION_PREFIX);
25
+ }
26
+ if (!(rawAttributes.includes(formattedOrderString) || isOrderAssociation)) {
27
+ utils_1.throwBadRequestError(`${currentOrder} is invalid`);
23
28
  }
24
29
  };
25
30
  const validateSingleAttribute = (currentAttribute, rawAttributes) => {
26
31
  if (!rawAttributes.includes(currentAttribute)) {
27
- throw new errors_1.BadRequest(`${currentAttribute} is invalid`, null);
32
+ utils_1.throwBadRequestError(`${currentAttribute} is invalid`);
28
33
  }
29
34
  };
30
- const validateOrderAttributes = (order, rawAttributes) => {
31
- order.map(o => validateSingleOrder(o, rawAttributes));
35
+ const validateOrderAttributes = (order, rawAttributes, associationModels = []) => {
36
+ order.map(o => validateSingleOrder(o, rawAttributes, associationModels));
32
37
  };
33
38
  const validateAttributes = (attributes, rawAttributes) => {
34
39
  attributes.map(a => validateSingleAttribute(a, rawAttributes));
35
40
  };
36
- const validateQueryPayload = (query, rawAttributes) => {
41
+ const validateQueryPayload = (query, rawAttributes, associationModels = []) => {
37
42
  // eslint-disable-next-line array-callback-return
38
43
  Object.keys(query).map((key) => {
39
44
  const value = query[key];
40
45
  if (lodash_1.default.isArray(value)) {
41
46
  if (lodash_1.default.isObject(value[0])) {
42
- value.map(v => validateQueryPayload(v, rawAttributes));
47
+ value.map(v => validateQueryPayload(v, rawAttributes, associationModels));
43
48
  }
44
49
  }
45
- else if (validateOperator(key) || validateQueryAttribute(key, rawAttributes)) {
50
+ else if (validateOperator(key)
51
+ || validateQueryAttribute(key, rawAttributes, associationModels)) {
46
52
  if (lodash_1.default.isObject(value)) {
47
53
  validateQueryPayload(value, rawAttributes);
48
54
  }
49
55
  }
50
56
  else {
51
- throw new errors_1.BadRequest(`invalid key: ${key}`, null);
57
+ utils_1.throwBadRequestError(`invalid key: ${key}`);
58
+ }
59
+ });
60
+ };
61
+ const validatePagination = ({ page, perPage, }) => {
62
+ if (page < utils_1.PAGE_MIN) {
63
+ utils_1.throwBadRequestError('Page must be greater than 0');
64
+ }
65
+ if (perPage > utils_1.PER_PAGE_MAX_LIMIT || perPage < utils_1.PER_PAGE_MIN_LIMIT) {
66
+ utils_1.throwBadRequestError(`PerPage must be between ${utils_1.PER_PAGE_MIN_LIMIT} to ${utils_1.PER_PAGE_MAX_LIMIT}`);
67
+ }
68
+ };
69
+ const validateIncludePayload = (include, associations) => {
70
+ const associationsKeys = Object.keys(associations);
71
+ include.forEach((i) => {
72
+ validateQueryAttribute(i.model, associationsKeys);
73
+ const { rawAttributes } = associations[i.model].target;
74
+ if (include.where) {
75
+ validateQueryPayload(i.where, rawAttributes);
76
+ }
77
+ if (include.order) {
78
+ validateOrderAttributes(i.order, rawAttributes);
79
+ }
80
+ if (include.attributes) {
81
+ validateAttributes(i.attributes, rawAttributes);
82
+ }
83
+ if (!lodash_1.default.isNil(include.required) && !lodash_1.default.isBoolean(include.required)) {
84
+ utils_1.throwBadRequestError('include.required must be a boolean');
52
85
  }
53
86
  });
54
87
  };
55
- exports.validatePayload = ({ query = {}, order = [], attributes = [], }, model) => {
88
+ exports.validatePayload = ({ query = {}, order = [], attributes = [], include = [], page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, }, model) => {
56
89
  const rawAttributes = Object.keys(model.rawAttributes);
90
+ const associationModels = Object.keys((model === null || model === void 0 ? void 0 : model.associations) || {});
57
91
  if (!attributes || attributes.length === 0) {
58
92
  // eslint-disable-next-line no-param-reassign
59
93
  attributes = rawAttributes;
@@ -61,7 +95,14 @@ exports.validatePayload = ({ query = {}, order = [], attributes = [], }, model)
61
95
  else {
62
96
  validateAttributes(attributes, rawAttributes);
63
97
  }
64
- validateOrderAttributes(order, rawAttributes);
65
- validateQueryPayload(query, rawAttributes);
98
+ validateOrderAttributes(order, rawAttributes, associationModels);
99
+ validateQueryPayload(query, rawAttributes, associationModels);
100
+ if (include.length) {
101
+ validateIncludePayload(include, model === null || model === void 0 ? void 0 : model.associations);
102
+ }
103
+ validatePagination({
104
+ page,
105
+ perPage,
106
+ });
66
107
  return true;
67
108
  };
@@ -38,44 +38,147 @@ const query = {
38
38
  };
39
39
  const order = ['startTime', '-endTime'];
40
40
  describe('validations test', () => {
41
- it('check query validation', () => {
42
- const payload = _1.validatePayload({ query }, {
43
- rawAttributes: {
44
- startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
45
- },
41
+ describe('query validation', () => {
42
+ it('check query validation', () => {
43
+ const payload = _1.validatePayload({ query }, {
44
+ rawAttributes: {
45
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
46
+ },
47
+ });
48
+ expect(payload).toBe(true);
46
49
  });
47
- expect(payload).toBe(true);
48
- });
49
- it('check order validation', () => {
50
- const payload = _1.validatePayload({ order }, {
51
- rawAttributes: {
52
- startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
53
- },
50
+ it('check query by json property field', () => {
51
+ const payload = _1.validatePayload({
52
+ query: {
53
+ 'jsonField.exampleField': {
54
+ $eq: 'a',
55
+ },
56
+ },
57
+ }, {
58
+ rawAttributes: {
59
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', jsonField: {},
60
+ },
61
+ });
62
+ expect(payload).toBe(true);
54
63
  });
55
- expect(payload).toBe(true);
56
- });
57
- it('check order validation - order does not exist', () => {
58
- try {
59
- _1.validatePayload({ order: ['aviv'] }, {
64
+ it('check query by included model field', () => {
65
+ const payload = _1.validatePayload({
66
+ query: {
67
+ 'status.name': {
68
+ $eq: 'a',
69
+ },
70
+ },
71
+ }, {
60
72
  rawAttributes: {
61
73
  startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
62
74
  },
75
+ associations: {
76
+ status: {},
77
+ },
63
78
  });
64
- }
65
- catch (error) {
66
- expect(error.statusCode).toBe(400);
67
- }
79
+ expect(payload).toBe(true);
80
+ });
81
+ it('check query by non existent field', () => {
82
+ try {
83
+ _1.validatePayload({
84
+ query: {
85
+ nonExistent: {
86
+ $eq: 'a',
87
+ },
88
+ },
89
+ }, {
90
+ rawAttributes: {
91
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
92
+ },
93
+ });
94
+ }
95
+ catch (error) {
96
+ expect(error.statusCode).toBe(400);
97
+ }
98
+ });
68
99
  });
69
- it('check order validation - prefix in the wrong place', () => {
70
- try {
71
- _1.validatePayload({ order: ['startTime-', 'currencySymbol'] }, {
100
+ describe('order validation', () => {
101
+ it('check order validation', () => {
102
+ const payload = _1.validatePayload({ order }, {
72
103
  rawAttributes: {
73
104
  startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
74
105
  },
75
106
  });
76
- }
77
- catch (error) {
78
- expect(error.statusCode).toBe(400);
79
- }
107
+ expect(payload).toBe(true);
108
+ });
109
+ it('order does not exist', () => {
110
+ try {
111
+ _1.validatePayload({ order: ['aviv'] }, {
112
+ rawAttributes: {
113
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
114
+ },
115
+ });
116
+ }
117
+ catch (error) {
118
+ expect(error.statusCode).toBe(400);
119
+ }
120
+ });
121
+ it('prefix in the wrong place', () => {
122
+ try {
123
+ _1.validatePayload({ order: ['startTime-', 'currencySymbol'] }, {
124
+ rawAttributes: {
125
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
126
+ },
127
+ });
128
+ }
129
+ catch (error) {
130
+ expect(error.statusCode).toBe(400);
131
+ }
132
+ });
133
+ it('order by included model', () => {
134
+ const payload = _1.validatePayload({ order: ['status.title'] }, {
135
+ rawAttributes: {
136
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
137
+ },
138
+ associations: {
139
+ status: {},
140
+ },
141
+ });
142
+ expect(payload).toBe(true);
143
+ });
144
+ it('order by included model descending', () => {
145
+ const payload = _1.validatePayload({ order: ['-status.title'] }, {
146
+ rawAttributes: {
147
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
148
+ },
149
+ associations: {
150
+ status: {},
151
+ },
152
+ });
153
+ expect(payload).toBe(true);
154
+ });
155
+ it('order by json field', () => {
156
+ const payload = _1.validatePayload({ order: ['status.title'] }, {
157
+ rawAttributes: {
158
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
159
+ },
160
+ });
161
+ expect(payload).toBe(true);
162
+ });
163
+ it('order by json field desc', () => {
164
+ const payload = _1.validatePayload({ order: ['-status.title'] }, {
165
+ rawAttributes: {
166
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
167
+ },
168
+ });
169
+ expect(payload).toBe(true);
170
+ });
171
+ it('try order by json field that doesnt exist', () => {
172
+ try {
173
+ _1.validatePayload({ order: ['-stas.title'] }, {
174
+ rawAttributes: {
175
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
176
+ },
177
+ });
178
+ }
179
+ catch (error) {
180
+ expect(error.statusCode).toBe(400);
181
+ }
182
+ });
80
183
  });
81
184
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sheilta",
3
- "version": "1.2.3-beta1",
3
+ "version": "1.2.3-beta2",
4
4
  "description": "manage cache",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -46,4 +46,4 @@
46
46
  "files": [
47
47
  "lib/**/*"
48
48
  ]
49
- }
49
+ }