-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
147 lines (131 loc) · 3.97 KB
/
index.js
File metadata and controls
147 lines (131 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
'use strict';
/**
* Translate direction string to SQL keyword.
*
* @param {string} str - target string.
* @return {string} "ASC" or "DESC"
* @throws Error if the target string is not supported.
*/
const translateDirection = (str) => {
if (str.length === 0 || str === '+') {
return "ASC";
} else if (str === '-') {
return "DESC";
}
throw new Error(`Unsupported Order Direction: ${str}`);
};
const ComponentPattern = /^([+|-]?)(\w+)/;
/**
* Parse order component.
*
* "order component" is the parts of sort string,
* such like "propA", "+propB", "-propC"
*
* @param {string} str - order component string.
* @return {Object} parsed result.
*/
const parseOrderComponent = (str) => {
const matches = str.match(ComponentPattern);
if (matches === null) {
throw new Error(`Invalid format: ${str}`);
}
const direction = translateDirection(matches[1]);
const key = matches[2].toLowerCase();
return {key, direction};
};
/**
* Parse order string
*
* This function parse the sort order string (ex: "propA,-propB,+propC")
* to sort order object for sequelize.
* (ex: [ ["propA", "ASC"], ["propB", "DESC"], ["propC", "ASC"] ])
*
* @param {string} sortValue - sort order string (ex: "propA,-propB,+propC")
* @param {Array<string>} sortableKeys - acceptable sort keys.
* @return {Array<Array<string>>} parsed result.
*/
const parseOrder = (sortValue, sortableKeys) => {
return sortValue.split(',').map((component) => {
try {
const parsed = parseOrderComponent(component);
if (sortableKeys.indexOf(parsed.key) === -1) {
// Ignore the order component.
return [];
}
return [parsed.key, parsed.direction];
} catch (err) {
return [];
}
}).filter((v) => (v.length !== 0));
};
/**
* Select sortableKeys
*
* @param {Sequelize.Model} cls - subclass of Sequelize.Model.
* @param {Array<string>|undefined} acceptableKeys - acceptable attribute names.
* @return {Array<string>} attribute names which can use to sort order keys.
*/
const selectSortableKeys = (cls, acceptableKeys) => {
const attrs = Object.keys(cls.attributes);
if (acceptableKeys && Array.isArray(acceptableKeys)) {
return acceptableKeys.filter((key) => {
return (attrs.indexOf(key) === -1);
});
}
return attrs;
};
const Sorter = {
/**
* Extend the class as "Sortable"
*
* @param {Sequelize.Model} cls - subclass of Sequelize model.
* @param {object} options - options
* @param {Array<string>} options.sortableKeys - default sortable keys.
* @return {Sequelize.Model} Extended subclass of Sequelize Model.
*/
sortable: (cls, options) => {
/**
* Sortable keys (whilte list)
*/
const defaultKeys = (options ? options.sortableKeys : undefined);
cls.sortableKeys = selectSortableKeys(cls, defaultKeys);
/**
* sortable Scope
*
* ```
* // NOTE: users table has "propA", "propB", "propC" columns.
*
* try {
* const params = {sort: "propA,+propB,-propC"};
*
* // If accept all keys as sort column.
* User.scope({method: ["sortable", params]}).findAll();
*
* // If you want to filter the sort keys.
* User.scope({method: ["sortable", params, ["propA"]]}).findAll();
* } catch (err) {
* // when the params.sort is invalid sort order vaule.
* throw err;
* }
* ```
*
* @param {Object} params - query params
* @param {string} params.sort - sort string.
* @param {Array<string>|undefined} acceptableKeys - Acceptable sort keys. (accept all attrs in this Model if undefined.)
*/
cls.addScope('sortable', function(params, acceptableKeys) {
if (!params || !params.sort || params.sort.length === 0) {
return {};
}
const keys = selectSortableKeys(cls, acceptableKeys);
if (keys.length === 0) {
return {};
}
return {
order: parseOrder(params.sort, keys)
};
}, {override: true});
return cls;
}
};
module.exports = Sorter;