@autofleet/sheilta 1.1.1 → 1.2.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.
@@ -1,12 +1,14 @@
1
1
  declare type OrderItem = string | [string, string];
2
2
  declare type SequelizeOrder = string | OrderItem[];
3
- declare const formatPayload: ({ order, page, perPage, }: {
3
+ declare const formatPayload: ({ order, page, perPage, include, }: {
4
4
  order: any;
5
5
  page?: number;
6
6
  perPage?: number;
7
- }) => {
7
+ include?: any[];
8
+ }, model?: any) => {
8
9
  order: SequelizeOrder[];
9
10
  page: any;
10
11
  perPage: any;
12
+ include: any;
11
13
  };
12
14
  export default formatPayload;
@@ -2,28 +2,38 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const utils_1 = require("../utils");
4
4
  const DESCENDING_KEY = 'DESC';
5
- const formatOrder = ({ order = [], }) => (order.length === 0 ? undefined :
5
+ const formatOrder = ({ order = [], associationModels = [], }) => (order.length === 0 ? undefined :
6
6
  order.map((o) => {
7
+ const formattedOrder = [utils_1.extractAttributeNameFromOrder(o, associationModels)];
7
8
  const isOrderDescOrder = utils_1.isOrderDesc(o);
8
- let column = o;
9
+ const isOrderAssociation = utils_1.isOrderByAssociation(isOrderDescOrder
10
+ ? o.split(utils_1.ORDER_PREFIX)[1]
11
+ : o, associationModels);
12
+ if (isOrderAssociation) {
13
+ formattedOrder.push(utils_1.extractAssociatedAttributeNameFromOrder(o));
14
+ }
9
15
  if (isOrderDescOrder) {
10
- column = utils_1.extractAttributeNameFromOrder(o);
11
- return [column, DESCENDING_KEY];
16
+ formattedOrder.push(DESCENDING_KEY);
12
17
  }
13
- return [column];
18
+ return formattedOrder;
14
19
  }));
15
20
  const formatPage = page => page || utils_1.PAGE_DEFAULT;
16
21
  const formatPerPage = perPage => perPage || utils_1.PER_PAGE_DEFAULT;
17
- const formatPayload = ({ order, page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, }) => {
22
+ const formatInclude = (include, associationsMap = {}) => include.map(i => associationsMap[i.model]);
23
+ const formatPayload = ({ order, page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, include = [], }, model) => {
24
+ const associationModels = Object.keys((model === null || model === void 0 ? void 0 : model.associations) || {});
18
25
  const formattedOrder = formatOrder({
19
26
  order,
27
+ associationModels,
20
28
  });
29
+ const formattedInclude = formatInclude(include, model === null || model === void 0 ? void 0 : model.associations);
21
30
  const formattedPage = formatPage(page);
22
31
  const formattedPerPage = formatPerPage(perPage);
23
32
  return {
24
33
  order: formattedOrder,
25
34
  page: formattedPage,
26
35
  perPage: formattedPerPage,
36
+ include: formattedInclude,
27
37
  };
28
38
  };
29
39
  exports.default = formatPayload;
@@ -17,4 +17,50 @@ describe('formatting test', () => {
17
17
  });
18
18
  expect(JSON.stringify(order)).toBe(JSON.stringify([['startTime'], ['endTime']]));
19
19
  });
20
+ it('check order formatting - by included model', () => {
21
+ const { order } = _1.default({
22
+ order: ['status.name'],
23
+ }, {
24
+ rawAttributes: {
25
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
26
+ },
27
+ associations: {
28
+ status: {},
29
+ },
30
+ });
31
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status', 'name']]));
32
+ });
33
+ it('check order formatting - by included model descending', () => {
34
+ const { order } = _1.default({
35
+ order: ['-status.name'],
36
+ }, {
37
+ rawAttributes: {
38
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
39
+ },
40
+ associations: {
41
+ status: {},
42
+ },
43
+ });
44
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status', 'name', 'DESC']]));
45
+ });
46
+ it('check order formatting - by json field', () => {
47
+ const { order } = _1.default({
48
+ order: ['status.name'],
49
+ }, {
50
+ rawAttributes: {
51
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', json: '',
52
+ },
53
+ });
54
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status.name']]));
55
+ });
56
+ it('check order formatting - by json field descending', () => {
57
+ const { order } = _1.default({
58
+ order: ['-status.name'],
59
+ }, {
60
+ rawAttributes: {
61
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', json: '',
62
+ },
63
+ });
64
+ expect(JSON.stringify(order)).toBe(JSON.stringify([['status.name', 'DESC']]));
65
+ });
20
66
  });
@@ -1,4 +1,4 @@
1
1
  export declare const newQueryValidationMiddleware: (inner?: string) => (model: any) => (req: any, res: any, next: any) => Promise<any>;
2
- export declare const newQueryFormatMiddleware: (inner?: string) => () => (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>;
3
3
  export declare const queryValidationMiddleware: (model: any) => (req: any, res: any, next: any) => Promise<any>;
4
- 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>;
@@ -17,7 +17,7 @@ const errors_1 = require("@autofleet/errors");
17
17
  const formatter_1 = __importDefault(require("../formatter"));
18
18
  const validations_1 = require("../validations");
19
19
  exports.newQueryValidationMiddleware = (inner = 'body') => model => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
20
- const { query, attributes, order, page, perPage, } = req[inner];
20
+ const { query, attributes, order, page, perPage, include, } = req[inner];
21
21
  try {
22
22
  validations_1.validatePayload({
23
23
  query,
@@ -25,6 +25,7 @@ exports.newQueryValidationMiddleware = (inner = 'body') => model => (req, res, n
25
25
  order,
26
26
  page,
27
27
  perPage,
28
+ include,
28
29
  }, model);
29
30
  return next();
30
31
  }
@@ -40,16 +41,18 @@ exports.newQueryValidationMiddleware = (inner = 'body') => model => (req, res, n
40
41
  });
41
42
  }
42
43
  });
43
- exports.newQueryFormatMiddleware = (inner = 'body') => () => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
44
- const { order, page, perPage, } = req[inner];
45
- const { order: formattedOrder, page: formattedPage, perPage: formattedPerPage, } = formatter_1.default({
44
+ exports.newQueryFormatMiddleware = (inner = 'body') => model => (req, res, next) => __awaiter(void 0, void 0, void 0, function* () {
45
+ const { order, page, perPage, include, } = req[inner];
46
+ const { order: formattedOrder, page: formattedPage, perPage: formattedPerPage, include: formattedInclude, } = formatter_1.default({
46
47
  order,
47
48
  page,
48
49
  perPage,
49
- });
50
+ include,
51
+ }, model);
50
52
  req[inner].order = formattedOrder;
51
53
  req[inner].page = formattedPage;
52
54
  req[inner].perPage = formattedPerPage;
55
+ req[inner].include = formattedInclude;
53
56
  return next();
54
57
  });
55
58
  exports.queryValidationMiddleware = exports.newQueryValidationMiddleware();
package/lib/utils.d.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  export declare const ORDER_PREFIX = "-";
2
+ export declare const ASSOCIATION_PREFIX = ".";
2
3
  export declare const PER_PAGE_DEFAULT = 20;
3
4
  export declare const PAGE_DEFAULT = 1;
4
5
  export declare const PER_PAGE_MAX_LIMIT = 100;
5
6
  export declare const PER_PAGE_MIN_LIMIT = 1;
6
7
  export declare const PAGE_MIN = 1;
7
- export declare const extractAttributeNameFromOrder: (order: any) => string;
8
+ export declare const isOrderByAssociation: (attributeName: any, associatedModels: any) => boolean;
9
+ export declare const extractAttributeNameFromOrder: (order: any, associationModels: any) => string;
8
10
  export declare const isOrderDesc: (order: any) => boolean;
9
11
  export declare const throwBadRequestError: (message: string) => never;
12
+ export declare const extractAssociatedAttributeNameFromOrder: (order: any) => string;
package/lib/utils.js CHANGED
@@ -1,13 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.throwBadRequestError = exports.isOrderDesc = exports.extractAttributeNameFromOrder = exports.PAGE_MIN = exports.PER_PAGE_MIN_LIMIT = exports.PER_PAGE_MAX_LIMIT = exports.PAGE_DEFAULT = exports.PER_PAGE_DEFAULT = exports.ORDER_PREFIX = void 0;
3
+ exports.extractAssociatedAttributeNameFromOrder = exports.throwBadRequestError = exports.isOrderDesc = exports.extractAttributeNameFromOrder = exports.isOrderByAssociation = 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
4
  const errors_1 = require("@autofleet/errors");
5
5
  exports.ORDER_PREFIX = '-';
6
+ exports.ASSOCIATION_PREFIX = '.';
6
7
  exports.PER_PAGE_DEFAULT = 20;
7
8
  exports.PAGE_DEFAULT = 1;
8
9
  exports.PER_PAGE_MAX_LIMIT = 100;
9
10
  exports.PER_PAGE_MIN_LIMIT = 1;
10
11
  exports.PAGE_MIN = 1;
11
- exports.extractAttributeNameFromOrder = (order) => order.split(exports.ORDER_PREFIX)[1];
12
+ exports.isOrderByAssociation = (attributeName, associatedModels) => attributeName.includes(exports.ASSOCIATION_PREFIX)
13
+ && associatedModels.includes(attributeName.split(exports.ASSOCIATION_PREFIX)[0]);
14
+ exports.extractAttributeNameFromOrder = (order, associationModels) => {
15
+ let formattedOrder = order;
16
+ if (order.includes(exports.ORDER_PREFIX)) {
17
+ // eslint-disable-next-line prefer-destructuring
18
+ formattedOrder = formattedOrder.split(exports.ORDER_PREFIX)[1];
19
+ }
20
+ if (exports.isOrderByAssociation(formattedOrder, associationModels)) {
21
+ [formattedOrder] = formattedOrder.split(exports.ASSOCIATION_PREFIX);
22
+ }
23
+ return formattedOrder;
24
+ };
12
25
  exports.isOrderDesc = (order) => order.includes(exports.ORDER_PREFIX);
13
- exports.throwBadRequestError = (message) => { throw new errors_1.BadRequest([{ message }], null); };
26
+ exports.throwBadRequestError = (message) => {
27
+ throw new errors_1.BadRequest([{ message }], null);
28
+ };
29
+ exports.extractAssociatedAttributeNameFromOrder = (order) => order.split(exports.ASSOCIATION_PREFIX)[1];
@@ -1,7 +1,8 @@
1
- export declare const validatePayload: ({ query, order, attributes, page, perPage, }: {
1
+ export declare const validatePayload: ({ query, order, attributes, include, page, perPage, }: {
2
2
  query?: {};
3
3
  order?: any[];
4
4
  attributes?: any[];
5
+ include?: any[];
5
6
  page?: number;
6
7
  perPage?: number;
7
- }, model: any) => boolean;
8
+ }, model?: any) => boolean;
@@ -9,15 +9,19 @@ const utils_1 = require("../utils");
9
9
  const operators_1 = require("../operators");
10
10
  const validateOperator = (operator) => operators_1.OPERATORS.includes(operator.split(operators_1.OPERATOR_PREFIX)[1]);
11
11
  const validateQueryAttribute = (attribute, modelAttributes) => modelAttributes.includes(attribute);
12
- const validateSingleOrder = (currentOrder, rawAttributes) => {
12
+ const validateSingleOrder = (currentOrder, rawAttributes, associationModels) => {
13
13
  const isOrderDescOrder = utils_1.isOrderDesc(currentOrder);
14
14
  if (isOrderDescOrder && currentOrder[0] !== utils_1.ORDER_PREFIX) {
15
15
  utils_1.throwBadRequestError(`${utils_1.ORDER_PREFIX} must be only at the beginning of the word`);
16
16
  }
17
- const formattedOrderString = isOrderDescOrder ?
18
- utils_1.extractAttributeNameFromOrder(currentOrder) :
19
- currentOrder;
20
- if (!rawAttributes.includes(formattedOrderString)) {
17
+ const orderStringWithoutDesc = isOrderDescOrder ?
18
+ currentOrder.split(utils_1.ORDER_PREFIX)[1] : currentOrder;
19
+ const isOrderAssociation = utils_1.isOrderByAssociation(orderStringWithoutDesc, associationModels);
20
+ let formattedOrderString = utils_1.extractAttributeNameFromOrder(currentOrder, associationModels);
21
+ if (!isOrderAssociation && formattedOrderString.includes(utils_1.ASSOCIATION_PREFIX)) {
22
+ [formattedOrderString] = formattedOrderString.split(utils_1.ASSOCIATION_PREFIX);
23
+ }
24
+ if (!(rawAttributes.includes(formattedOrderString) || isOrderAssociation)) {
21
25
  utils_1.throwBadRequestError(`${currentOrder} is invalid`);
22
26
  }
23
27
  };
@@ -26,8 +30,8 @@ const validateSingleAttribute = (currentAttribute, rawAttributes) => {
26
30
  utils_1.throwBadRequestError(`${currentAttribute} is invalid`);
27
31
  }
28
32
  };
29
- const validateOrderAttributes = (order, rawAttributes) => {
30
- order.map(o => validateSingleOrder(o, rawAttributes));
33
+ const validateOrderAttributes = (order, rawAttributes, associationModels = []) => {
34
+ order.map(o => validateSingleOrder(o, rawAttributes, associationModels));
31
35
  };
32
36
  const validateAttributes = (attributes, rawAttributes) => {
33
37
  attributes.map(a => validateSingleAttribute(a, rawAttributes));
@@ -59,8 +63,25 @@ const validatePagination = ({ page, perPage, }) => {
59
63
  utils_1.throwBadRequestError(`PerPage must be between ${utils_1.PER_PAGE_MIN_LIMIT} to ${utils_1.PER_PAGE_MAX_LIMIT}`);
60
64
  }
61
65
  };
62
- exports.validatePayload = ({ query = {}, order = [], attributes = [], page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, }, model) => {
66
+ const validateIncludePayload = (include, associations) => {
67
+ const associationsKeys = Object.keys(associations);
68
+ include.forEach((i) => {
69
+ validateQueryAttribute(i.model, associationsKeys);
70
+ const { rawAttributes } = associations[i.model].target;
71
+ if (include.where) {
72
+ validateQueryPayload(i.where, rawAttributes);
73
+ }
74
+ if (include.order) {
75
+ validateOrderAttributes(i.order, rawAttributes);
76
+ }
77
+ if (!lodash_1.default.isNil(include.required) && !lodash_1.default.isBoolean(include.required)) {
78
+ utils_1.throwBadRequestError('include.required must be a boolean');
79
+ }
80
+ });
81
+ };
82
+ exports.validatePayload = ({ query = {}, order = [], attributes = [], include = [], page = utils_1.PAGE_DEFAULT, perPage = utils_1.PER_PAGE_DEFAULT, }, model) => {
63
83
  const rawAttributes = Object.keys(model.rawAttributes);
84
+ const associationModels = Object.keys((model === null || model === void 0 ? void 0 : model.associations) || {});
64
85
  if (!attributes || attributes.length === 0) {
65
86
  // eslint-disable-next-line no-param-reassign
66
87
  attributes = rawAttributes;
@@ -68,8 +89,11 @@ exports.validatePayload = ({ query = {}, order = [], attributes = [], page = uti
68
89
  else {
69
90
  validateAttributes(attributes, rawAttributes);
70
91
  }
71
- validateOrderAttributes(order, rawAttributes);
92
+ validateOrderAttributes(order, rawAttributes, associationModels);
72
93
  validateQueryPayload(query, rawAttributes);
94
+ if (include.length) {
95
+ validateIncludePayload(include, model === null || model === void 0 ? void 0 : model.associations);
96
+ }
73
97
  validatePagination({
74
98
  page,
75
99
  perPage,
@@ -78,4 +78,54 @@ describe('validations test', () => {
78
78
  expect(error.statusCode).toBe(400);
79
79
  }
80
80
  });
81
+ it('check order validation - order by included model', () => {
82
+ const payload = _1.validatePayload({ order: ['status.title'] }, {
83
+ rawAttributes: {
84
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
85
+ },
86
+ associations: {
87
+ status: {},
88
+ },
89
+ });
90
+ expect(payload).toBe(true);
91
+ });
92
+ it('check order validation - order by included model descending', () => {
93
+ const payload = _1.validatePayload({ order: ['-status.title'] }, {
94
+ rawAttributes: {
95
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '',
96
+ },
97
+ associations: {
98
+ status: {},
99
+ },
100
+ });
101
+ expect(payload).toBe(true);
102
+ });
103
+ it('check order validation - order by json field', () => {
104
+ const payload = _1.validatePayload({ order: ['status.title'] }, {
105
+ rawAttributes: {
106
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
107
+ },
108
+ });
109
+ expect(payload).toBe(true);
110
+ });
111
+ it('check order validation - order by json field desc', () => {
112
+ const payload = _1.validatePayload({ order: ['-status.title'] }, {
113
+ rawAttributes: {
114
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
115
+ },
116
+ });
117
+ expect(payload).toBe(true);
118
+ });
119
+ it('check order validation - try order by json field that doesnt exist', () => {
120
+ try {
121
+ _1.validatePayload({ order: ['-stas.title'] }, {
122
+ rawAttributes: {
123
+ startTime: '', endTime: '', currencySymbol: '', currencyCode: '', startStationId: '', status: '',
124
+ },
125
+ });
126
+ }
127
+ catch (error) {
128
+ expect(error.statusCode).toBe(400);
129
+ }
130
+ });
81
131
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/sheilta",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "manage cache",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",