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