adminmate-express-mongoose 1.3.3 → 1.3.5
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/index.js +41 -28
- package/package.json +2 -2
- package/src/controllers/chart-pie.js +78 -56
- package/src/controllers/chart-ranking.js +99 -78
- package/src/controllers/chart-time.js +143 -127
- package/src/controllers/chart-value.js +104 -69
- package/src/controllers/model-autocomplete.js +52 -47
- package/src/controllers/model-deletesome.js +21 -18
- package/src/controllers/model-getall.js +123 -96
- package/src/controllers/model-getin.js +16 -12
- package/src/controllers/model-getone.js +41 -38
- package/src/controllers/model-getrefs.js +54 -50
- package/src/controllers/model-postone.js +25 -22
- package/src/controllers/model-putone.js +43 -38
- package/src/controllers/model-query.js +53 -49
- package/src/helpers/functions.js +453 -420
package/src/helpers/functions.js
CHANGED
|
@@ -2,494 +2,527 @@ const mongoose = require('mongoose');
|
|
|
2
2
|
const { serializeError } = require('serialize-error');
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
const moment = require('moment');
|
|
5
|
-
|
|
6
5
|
const pjson = require('../../package.json');
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
module.exports = _conf => {
|
|
8
|
+
const getModelProperties = model => {
|
|
9
|
+
let modelFields = [];
|
|
10
|
+
const modelProps = model.schema.paths;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
Object.keys(modelProps).forEach(key => {
|
|
13
|
+
if (key === '__v') {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
let property = {
|
|
17
|
+
path: key,
|
|
18
|
+
type: modelProps[key].instance
|
|
19
|
+
};
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
if (property.type === 'Array') {
|
|
22
|
+
const optionsTypes = modelProps[key].options.type;
|
|
23
|
+
if (optionsTypes && optionsTypes[0] && typeof optionsTypes[0] === 'function') {
|
|
24
|
+
property.type = `ArrayOf${optionsTypes[0].name}`;
|
|
25
|
+
}
|
|
26
|
+
else if (optionsTypes && optionsTypes[0] && optionsTypes[0].type && typeof optionsTypes[0].type === 'function') {
|
|
27
|
+
property.type = `ArrayOf${optionsTypes[0].type.name}`;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
property.type = 'ArrayOfObject';
|
|
31
|
+
}
|
|
25
32
|
}
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
|
|
34
|
+
// Required option
|
|
35
|
+
if (modelProps[key].options.required) {
|
|
36
|
+
property.required = true;
|
|
28
37
|
}
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
|
|
39
|
+
// Default value option
|
|
40
|
+
if (typeof modelProps[key].options.default !== 'undefined') {
|
|
41
|
+
if (typeof modelProps[key].options.default === 'function') {
|
|
42
|
+
property.default = modelProps[key].options.default();
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
property.default = modelProps[key].options.default;
|
|
46
|
+
}
|
|
31
47
|
}
|
|
32
|
-
}
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
// Enum option
|
|
50
|
+
if (modelProps[key].options.enum) {
|
|
51
|
+
if (modelProps[key].enumValues) {
|
|
52
|
+
property.enum = modelProps[key].enumValues;
|
|
53
|
+
}
|
|
54
|
+
else if (modelProps[key].options.enum.values) {
|
|
55
|
+
property.enum = modelProps[key].options.enum.values
|
|
56
|
+
}
|
|
57
|
+
}
|
|
38
58
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
property.default = modelProps[key].options.default();
|
|
59
|
+
// Ref option
|
|
60
|
+
if (modelProps[key].options.ref) {
|
|
61
|
+
property.ref = modelProps[key].options.ref;
|
|
43
62
|
}
|
|
44
|
-
|
|
45
|
-
|
|
63
|
+
|
|
64
|
+
// RefPath option
|
|
65
|
+
if (modelProps[key].options.refPath) {
|
|
66
|
+
property.refPath = modelProps[key].options.refPath;
|
|
46
67
|
}
|
|
47
|
-
}
|
|
48
68
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (modelProps[key].enumValues) {
|
|
52
|
-
property.enum = modelProps[key].enumValues;
|
|
69
|
+
if (key === '_id') {
|
|
70
|
+
modelFields.unshift(property);
|
|
53
71
|
}
|
|
54
|
-
else
|
|
55
|
-
property
|
|
72
|
+
else {
|
|
73
|
+
modelFields.push(property);
|
|
56
74
|
}
|
|
57
|
-
}
|
|
75
|
+
});
|
|
58
76
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
property.ref = modelProps[key].options.ref;
|
|
62
|
-
}
|
|
77
|
+
return modelFields;
|
|
78
|
+
};
|
|
63
79
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
80
|
+
// Return real mongoose model name
|
|
81
|
+
const getModelRealname = model => {
|
|
82
|
+
return model.modelName;
|
|
83
|
+
};
|
|
68
84
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
modelFields.push(property);
|
|
85
|
+
// To be used in this file
|
|
86
|
+
const permutations = list => {
|
|
87
|
+
if (list.length <= 1) {
|
|
88
|
+
return list.slice();
|
|
74
89
|
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
return modelFields;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
module.exports.getModelProperties = getModelProperties;
|
|
81
90
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
current,
|
|
99
|
-
rest;
|
|
100
|
-
|
|
101
|
-
for(; i < list.length; i++) {
|
|
102
|
-
rest = list.slice(); // make a copy of list
|
|
103
|
-
current = rest.splice(i, 1);
|
|
104
|
-
permutationsRest = permutations(rest);
|
|
105
|
-
for(j = 0; j < permutationsRest.length; j++) {
|
|
106
|
-
result.push(current.concat(permutationsRest[j]));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return result;
|
|
110
|
-
};
|
|
91
|
+
let result = [],
|
|
92
|
+
i = 0,
|
|
93
|
+
j,
|
|
94
|
+
current,
|
|
95
|
+
rest;
|
|
96
|
+
|
|
97
|
+
for(; i < list.length; i++) {
|
|
98
|
+
rest = list.slice(); // make a copy of list
|
|
99
|
+
current = rest.splice(i, 1);
|
|
100
|
+
permutationsRest = permutations(rest);
|
|
101
|
+
for(j = 0; j < permutationsRest.length; j++) {
|
|
102
|
+
result.push(current.concat(permutationsRest[j]));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
111
107
|
|
|
112
|
-
|
|
108
|
+
// To be used in this file
|
|
109
|
+
const cleanString = string => {
|
|
110
|
+
return string.toLowerCase().replace(/\W/g, '');
|
|
111
|
+
};
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
113
|
+
const queryRule = rule => {
|
|
114
|
+
if (rule.type === 'group') {
|
|
115
|
+
return queryRuleSet(rule);
|
|
116
|
+
}
|
|
117
|
+
let q = {};
|
|
118
|
+
if (rule.operator === 'is') {
|
|
119
|
+
// In order that aggregate queries to work well
|
|
120
|
+
const value = mongoose.isValidObjectId(rule.value) ? new mongoose.Types.ObjectId(rule.value) : rule.value;
|
|
121
|
+
q[rule.field] = { $eq: value };
|
|
122
|
+
}
|
|
123
|
+
else if (rule.operator === 'is_not') {
|
|
124
|
+
// In order that aggregate queries to work well
|
|
125
|
+
const value = mongoose.isValidObjectId(rule.value) ? new mongoose.Types.ObjectId(rule.value) : rule.value;
|
|
126
|
+
q[rule.field] = { $ne: value };
|
|
127
|
+
}
|
|
128
|
+
// Date
|
|
129
|
+
else if (rule.operator === 'is_before') {
|
|
130
|
+
// In order that aggregate queries to work well
|
|
131
|
+
q[rule.field] = { $lt: new Date(moment(rule.value)) };
|
|
132
|
+
}
|
|
133
|
+
else if (rule.operator === 'is_after') {
|
|
134
|
+
// In order that aggregate queries to work well
|
|
135
|
+
q[rule.field] = { $gt: new Date(moment(rule.value)) };
|
|
136
|
+
}
|
|
137
|
+
else if (rule.operator === 'is_today') {
|
|
138
|
+
q[rule.field] = {
|
|
139
|
+
$gte: moment().startOf('day'),
|
|
140
|
+
$lte: moment().endOf('day')
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
else if (rule.operator === 'was_yesterday') {
|
|
144
|
+
q[rule.field] = {
|
|
145
|
+
$gte: moment().startOf('day').subtract(1, 'day'),
|
|
146
|
+
$lte: moment().endOf('day').subtract(1, 'day')
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
else if (rule.operator === 'was_in_previous_week') {
|
|
150
|
+
q[rule.field] = {
|
|
151
|
+
$gte: moment().subtract(1, 'week').startOf('week'),
|
|
152
|
+
$lte: moment().subtract(1, 'week').endOf('week')
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
else if (rule.operator === 'was_in_previous_month') {
|
|
156
|
+
q[rule.field] = {
|
|
157
|
+
$gte: moment().subtract(1, 'month').startOf('month'),
|
|
158
|
+
$lte: moment().subtract(1, 'month').endOf('month')
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
else if (rule.operator === 'was_in_previous_year') {
|
|
162
|
+
q[rule.field] = {
|
|
163
|
+
$gte: moment().subtract(1, 'year').startOf('year'),
|
|
164
|
+
$lte: moment().subtract(1, 'year').endOf('year')
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Number
|
|
168
|
+
else if (rule.operator === 'is_greater_than') {
|
|
169
|
+
q[rule.field] = { $gt: rule.value };
|
|
170
|
+
}
|
|
171
|
+
else if (rule.operator === 'is_less_than') {
|
|
172
|
+
q[rule.field] = { $lt: rule.value };
|
|
173
|
+
}
|
|
174
|
+
// Boolean
|
|
175
|
+
else if (rule.operator === 'is_true') {
|
|
176
|
+
q[rule.field] = { $eq: true };
|
|
177
|
+
}
|
|
178
|
+
else if (rule.operator === 'is_false') {
|
|
179
|
+
q[rule.field] = { $eq: false };
|
|
180
|
+
}
|
|
181
|
+
// Exists
|
|
182
|
+
else if (rule.operator === 'is_present') {
|
|
183
|
+
q[rule.field] = { $exists: true };
|
|
184
|
+
}
|
|
185
|
+
else if (rule.operator === 'is_blank') {
|
|
186
|
+
q[rule.field] = { $exists: false };
|
|
187
|
+
}
|
|
188
|
+
// String comparison
|
|
189
|
+
else if (rule.operator === 'starts_with') {
|
|
190
|
+
const regexp = new RegExp(`^${rule.value}`);
|
|
191
|
+
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
192
|
+
}
|
|
193
|
+
else if (rule.operator === 'ends_with') {
|
|
194
|
+
const regexp = new RegExp(`${rule.value}$`);
|
|
195
|
+
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
196
|
+
}
|
|
197
|
+
else if (rule.operator === 'contains') {
|
|
198
|
+
const regexp = new RegExp(`${rule.value}`);
|
|
199
|
+
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
200
|
+
}
|
|
201
|
+
else if (rule.operator === 'not_contains') {
|
|
202
|
+
const regexp = new RegExp(`^((?!${rule.value}).)*$`);
|
|
203
|
+
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
204
|
+
}
|
|
205
|
+
return q;
|
|
206
|
+
};
|
|
118
207
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return queryRuleSet(rule);
|
|
124
|
-
}
|
|
125
|
-
let q = {};
|
|
126
|
-
if (rule.operator === 'is') {
|
|
127
|
-
q[rule.field] = { $eq: rule.value };
|
|
128
|
-
}
|
|
129
|
-
else if (rule.operator === 'is_not') {
|
|
130
|
-
q[rule.field] = { $ne: rule.value };
|
|
131
|
-
}
|
|
132
|
-
// Date
|
|
133
|
-
else if (rule.operator === 'is_before') {
|
|
134
|
-
q[rule.field] = { $lt: rule.value };
|
|
135
|
-
}
|
|
136
|
-
else if (rule.operator === 'is_after') {
|
|
137
|
-
q[rule.field] = { $gt: rule.value };
|
|
138
|
-
}
|
|
139
|
-
else if (rule.operator === 'is_today') {
|
|
140
|
-
q[rule.field] = {
|
|
141
|
-
$gte: moment().startOf('day'),
|
|
142
|
-
$lte: moment().endOf('day')
|
|
208
|
+
const queryRuleSet = ruleSet => {
|
|
209
|
+
const conditions = {
|
|
210
|
+
'and': '$and',
|
|
211
|
+
'or': '$or'
|
|
143
212
|
};
|
|
144
|
-
}
|
|
145
|
-
else if (rule.operator === 'was_yesterday') {
|
|
146
|
-
q[rule.field] = {
|
|
147
|
-
$gte: moment().startOf('day').subtract(1, 'day'),
|
|
148
|
-
$lte: moment().endOf('day').subtract(1, 'day')
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
else if (rule.operator === 'was_in_previous_week') {
|
|
152
|
-
q[rule.field] = {
|
|
153
|
-
$gte: moment().subtract(1, 'week').startOf('week'),
|
|
154
|
-
$lte: moment().subtract(1, 'week').endOf('week')
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
else if (rule.operator === 'was_in_previous_month') {
|
|
158
|
-
q[rule.field] = {
|
|
159
|
-
$gte: moment().subtract(1, 'month').startOf('month'),
|
|
160
|
-
$lte: moment().subtract(1, 'month').endOf('month')
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
else if (rule.operator === 'was_in_previous_year') {
|
|
164
|
-
q[rule.field] = {
|
|
165
|
-
$gte: moment().subtract(1, 'year').startOf('year'),
|
|
166
|
-
$lte: moment().subtract(1, 'year').endOf('year')
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
// Number
|
|
170
|
-
else if (rule.operator === 'is_greater_than') {
|
|
171
|
-
q[rule.field] = { $gt: rule.value };
|
|
172
|
-
}
|
|
173
|
-
else if (rule.operator === 'is_less_than') {
|
|
174
|
-
q[rule.field] = { $lt: rule.value };
|
|
175
|
-
}
|
|
176
|
-
// Boolean
|
|
177
|
-
else if (rule.operator === 'is_true') {
|
|
178
|
-
q[rule.field] = { $eq: true };
|
|
179
|
-
}
|
|
180
|
-
else if (rule.operator === 'is_false') {
|
|
181
|
-
q[rule.field] = { $eq: false };
|
|
182
|
-
}
|
|
183
|
-
// Exists
|
|
184
|
-
else if (rule.operator === 'is_present') {
|
|
185
|
-
q[rule.field] = { $exists: true };
|
|
186
|
-
}
|
|
187
|
-
else if (rule.operator === 'is_blank') {
|
|
188
|
-
q[rule.field] = { $exists: false };
|
|
189
|
-
}
|
|
190
|
-
// String comparison
|
|
191
|
-
else if (rule.operator === 'starts_with') {
|
|
192
|
-
const regexp = new RegExp(`^${rule.value}`);
|
|
193
|
-
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
194
|
-
}
|
|
195
|
-
else if (rule.operator === 'ends_with') {
|
|
196
|
-
const regexp = new RegExp(`${rule.value}$`);
|
|
197
|
-
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
198
|
-
}
|
|
199
|
-
else if (rule.operator === 'contains') {
|
|
200
|
-
const regexp = new RegExp(`${rule.value}`);
|
|
201
|
-
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
202
|
-
}
|
|
203
|
-
else if (rule.operator === 'not_contains') {
|
|
204
|
-
const regexp = new RegExp(`^((?!${rule.value}).)*$`);
|
|
205
|
-
q[rule.field] = { $regex: regexp, $options: 'i' };
|
|
206
|
-
}
|
|
207
|
-
return q;
|
|
208
|
-
};
|
|
209
213
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
return {
|
|
215
|
+
[conditions[ruleSet.operator]]: ruleSet.list.map(
|
|
216
|
+
rule => rule.list ? queryRuleSet(rule) : queryRule(rule)
|
|
217
|
+
)
|
|
218
|
+
}
|
|
214
219
|
};
|
|
215
220
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
module.exports.toFixedIfNecessary = (value, dp) => {
|
|
224
|
-
return +parseFloat(value).toFixed(dp);
|
|
225
|
-
};
|
|
221
|
+
const toFixedIfNecessary = (value, dp) => {
|
|
222
|
+
return +parseFloat(value).toFixed(dp);
|
|
223
|
+
};
|
|
226
224
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
};
|
|
225
|
+
const constructQuery = jsonQuery => {
|
|
226
|
+
if (jsonQuery.operator && jsonQuery.list && jsonQuery.list.length) {
|
|
227
|
+
return queryRuleSet(jsonQuery);
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
};
|
|
233
231
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
232
|
+
const fieldsToValues = (string, values) => {
|
|
233
|
+
return string.replace(/[a-z._]+/gi, word => {
|
|
234
|
+
return _.get(values, word);
|
|
235
|
+
});
|
|
236
|
+
};
|
|
239
237
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
238
|
+
const refFields = (item, fieldsToPopulate) => {
|
|
239
|
+
const attributes = Object.keys(item);
|
|
240
|
+
attributes.forEach(attr => {
|
|
243
241
|
|
|
244
|
-
|
|
245
|
-
|
|
242
|
+
// Set to empty instead of undefined
|
|
243
|
+
item[attr] = typeof item[attr] === 'undefined' ? '' : item[attr];
|
|
246
244
|
|
|
247
|
-
|
|
248
|
-
|
|
245
|
+
// Manage populate fields
|
|
246
|
+
const matchingField = fieldsToPopulate.find(field => field.path === attr);
|
|
249
247
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
248
|
+
if (matchingField) {
|
|
249
|
+
let fieldsList = '';
|
|
250
|
+
if (matchingField.multipleRefField && matchingField.multipleValues) {
|
|
251
|
+
const modelToCheck = item[matchingField.multipleRefField];
|
|
252
|
+
if (modelToCheck && matchingField.multipleValues[modelToCheck]) {
|
|
253
|
+
fieldsList = matchingField.multipleValues[modelToCheck];
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
fieldsList = '_id';
|
|
257
|
+
}
|
|
256
258
|
}
|
|
257
259
|
else {
|
|
258
|
-
fieldsList =
|
|
260
|
+
fieldsList = matchingField.select;
|
|
259
261
|
}
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
fieldsList = matchingField.select;
|
|
263
|
-
}
|
|
264
262
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
263
|
+
const label = fieldsList.replace(/[a-z._]+/gi, word => {
|
|
264
|
+
return _.get(item, `${attr}.${word}`);
|
|
265
|
+
});
|
|
268
266
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
267
|
+
if (item[attr]) {
|
|
268
|
+
item[attr] = {
|
|
269
|
+
type: 'ref',
|
|
270
|
+
id: item[attr]._id,
|
|
271
|
+
label
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
item[attr] = '(deleted)';
|
|
276
|
+
}
|
|
278
277
|
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
};
|
|
278
|
+
});
|
|
279
|
+
return item;
|
|
280
|
+
};
|
|
283
281
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
282
|
+
const getFieldsToPopulate = (keys, fieldsToFetch, refFields = {}) => {
|
|
283
|
+
// Create query populate config
|
|
284
|
+
let fieldsToPopulate = [];
|
|
285
|
+
fieldsToFetch.forEach(field => {
|
|
286
|
+
const matchingField = keys.find(k => k.path === field);
|
|
287
|
+
if (matchingField && matchingField.type === 'ObjectID' && (matchingField.ref || matchingField.refPath)) {
|
|
288
|
+
|
|
289
|
+
let fieldToSelect = '_id';
|
|
290
|
+
let toPush = {
|
|
291
|
+
path: field,
|
|
292
|
+
select: '_id'
|
|
293
|
+
};
|
|
296
294
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
295
|
+
// For ref attributes
|
|
296
|
+
if (matchingField.ref) {
|
|
297
|
+
const matchingModel = _conf.models.find(m => m.model.modelName === matchingField.ref);
|
|
298
|
+
if (matchingModel && matchingModel.slug && refFields[matchingModel.slug]) {
|
|
299
|
+
toPush.select = refFields[matchingModel.slug];
|
|
300
|
+
}
|
|
302
301
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
302
|
+
// For refPath attributes
|
|
303
|
+
else if (matchingField.refPath) {
|
|
304
|
+
const multipleValues = {};
|
|
305
|
+
const refPathField = keys.find(k => k.path === matchingField.refPath);
|
|
306
|
+
if (refPathField && refPathField.enum) {
|
|
307
|
+
refPathField.enum.forEach(modelName => {
|
|
308
|
+
multipleValues[modelName] = '_id';
|
|
309
|
+
const matchingModel = _conf.models.find(m => m.model.modelName === modelName);
|
|
310
|
+
if (matchingModel && matchingModel.slug && refFields[matchingModel.slug]) {
|
|
311
|
+
// Merge all ref models fields
|
|
312
|
+
fieldToSelect += ` ${refFields[matchingModel.slug]}`;
|
|
313
|
+
multipleValues[modelName] = refFields[matchingModel.slug];
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
toPush.select = fieldToSelect || '_id';
|
|
317
|
+
toPush.multipleRefField = matchingField.refPath;
|
|
318
|
+
toPush.multipleValues = multipleValues;
|
|
319
|
+
}
|
|
321
320
|
}
|
|
321
|
+
|
|
322
|
+
fieldsToPopulate.push(toPush);
|
|
322
323
|
}
|
|
324
|
+
});
|
|
323
325
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
326
|
+
return fieldsToPopulate;
|
|
327
|
+
};
|
|
327
328
|
|
|
328
|
-
|
|
329
|
-
};
|
|
329
|
+
const constructSearch = (search, fieldsToSearchIn, fieldsToPopulate = []) => {
|
|
330
|
+
params = { $or: [] };
|
|
330
331
|
|
|
331
|
-
|
|
332
|
-
|
|
332
|
+
fieldsToSearchIn.map(field => {
|
|
333
|
+
params.$or.push({ [field]: { '$regex': `${search}`, '$options': 'i' } });
|
|
334
|
+
});
|
|
333
335
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
336
|
+
// If the search is a valid mongodb _id
|
|
337
|
+
// An object id's only defining feature is that its 12 bytes long
|
|
338
|
+
if (mongoose.isValidObjectId(search)) {
|
|
339
|
+
params.$or.push({ _id: search });
|
|
340
|
+
fieldsToPopulate.map(field => {
|
|
341
|
+
params.$or.push({ [field.path]: search });
|
|
342
|
+
});
|
|
343
|
+
}
|
|
337
344
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
params.$or.push({
|
|
357
|
-
$expr: {
|
|
358
|
-
$regexMatch: {
|
|
359
|
-
input: {
|
|
360
|
-
$concat: concatFields
|
|
361
|
-
},
|
|
362
|
-
regex: new RegExp(searchCombinaisons),
|
|
363
|
-
options: 'i'
|
|
345
|
+
// If the search terms contains multiple words and there is multiple fields to search in
|
|
346
|
+
if (/\s/.test(search) && fieldsToSearchIn.length > 1) {
|
|
347
|
+
// Create all search combinaisons for $regexMatch
|
|
348
|
+
const searchPieces = search.split(' ');
|
|
349
|
+
const searchCombinaisons = permutations(searchPieces)
|
|
350
|
+
.map(comb => cleanString(comb.join('')))
|
|
351
|
+
.join('|');
|
|
352
|
+
const concatFields = fieldsToSearchIn.map(field => `$${field}`);
|
|
353
|
+
|
|
354
|
+
params.$or.push({
|
|
355
|
+
$expr: {
|
|
356
|
+
$regexMatch: {
|
|
357
|
+
input: {
|
|
358
|
+
$concat: concatFields
|
|
359
|
+
},
|
|
360
|
+
regex: new RegExp(searchCombinaisons),
|
|
361
|
+
options: 'i'
|
|
362
|
+
}
|
|
364
363
|
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
364
|
+
});
|
|
365
|
+
}
|
|
368
366
|
|
|
369
|
-
|
|
370
|
-
};
|
|
367
|
+
return params;
|
|
368
|
+
};
|
|
371
369
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
};
|
|
370
|
+
const getModelWhereClause = (model, idsArray) => {
|
|
371
|
+
return { _id: idsArray };
|
|
372
|
+
};
|
|
375
373
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
};
|
|
374
|
+
const getModelPrimaryKeys = model => {
|
|
375
|
+
return ['_id'];
|
|
376
|
+
};
|
|
379
377
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (!currentModelRealName) {
|
|
385
|
-
return [];
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// List all the models that reference the current model
|
|
389
|
-
const associationsList = [];
|
|
390
|
-
global._amConfig.models
|
|
391
|
-
.filter(mc => getModelRealname(mc.model) !== currentModelRealName)
|
|
392
|
-
.forEach(mc => {
|
|
393
|
-
const modelProperties = getModelProperties(mc.model);
|
|
394
|
-
if (modelProperties && modelProperties.length) {
|
|
395
|
-
modelProperties.forEach(mp => {
|
|
396
|
-
if (mp.ref === currentModelRealName) {
|
|
397
|
-
associationsList.push({
|
|
398
|
-
model: mc.model,
|
|
399
|
-
model_slug: mc.slug,
|
|
400
|
-
slug: `${mc.slug}_${mp.path}`,
|
|
401
|
-
ref_field: mp.path
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
})
|
|
405
|
-
}
|
|
406
|
-
});
|
|
378
|
+
const getModelAssociations = model => {
|
|
379
|
+
// Get current model mongoose realname
|
|
380
|
+
const currentModelRealName = getModelRealname(model);
|
|
407
381
|
|
|
408
|
-
|
|
409
|
-
|
|
382
|
+
if (!currentModelRealName) {
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
410
385
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
386
|
+
// List all the models that reference the current model
|
|
387
|
+
const associationsList = [];
|
|
388
|
+
_conf.models
|
|
389
|
+
.filter(mc => !!mc.model && getModelRealname(mc.model) !== currentModelRealName)
|
|
390
|
+
.forEach(mc => {
|
|
391
|
+
const modelProperties = getModelProperties(mc.model);
|
|
392
|
+
if (modelProperties && modelProperties.length) {
|
|
393
|
+
modelProperties.forEach(mp => {
|
|
394
|
+
if (mp.ref === currentModelRealName) {
|
|
395
|
+
associationsList.push({
|
|
396
|
+
model: mc.model,
|
|
397
|
+
model_slug: mc.slug,
|
|
398
|
+
slug: `${mc.slug}_${mp.path}`,
|
|
399
|
+
ref_field: mp.path
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
});
|
|
415
405
|
|
|
416
|
-
|
|
406
|
+
return associationsList;
|
|
407
|
+
};
|
|
417
408
|
|
|
418
|
-
|
|
419
|
-
|
|
409
|
+
const getModel = modelCode => {
|
|
410
|
+
if (!modelCode) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
420
413
|
|
|
421
|
-
|
|
422
|
-
const currentModel = getModel(modelCode);
|
|
423
|
-
if (!currentModel) {
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
414
|
+
const currentModel = _conf.models.find(m => m.slug === modelCode);
|
|
426
415
|
|
|
427
|
-
|
|
428
|
-
};
|
|
416
|
+
return currentModel;
|
|
417
|
+
};
|
|
429
418
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
419
|
+
const getModelObject = modelCode => {
|
|
420
|
+
const currentModel = getModel(modelCode);
|
|
421
|
+
if (!currentModel) {
|
|
422
|
+
return null;
|
|
423
|
+
}
|
|
435
424
|
|
|
436
|
-
|
|
437
|
-
};
|
|
425
|
+
return currentModel.model;
|
|
426
|
+
};
|
|
438
427
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
428
|
+
const getModelSegments = modelCode => {
|
|
429
|
+
const currentModel = getModel(modelCode);
|
|
430
|
+
if (!currentModel) {
|
|
431
|
+
return null;
|
|
432
|
+
}
|
|
444
433
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
};
|
|
434
|
+
return currentModel.segments;
|
|
435
|
+
};
|
|
448
436
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
});
|
|
455
|
-
return { message: defaultMessage, error_details: arr };
|
|
456
|
-
}
|
|
457
|
-
else if (e && e.message) {
|
|
458
|
-
const errorObject = serializeError(e);
|
|
459
|
-
const arr = [{
|
|
460
|
-
message: errorObject.stack
|
|
461
|
-
}];
|
|
462
|
-
return { message: defaultMessage, error_details: arr };
|
|
463
|
-
}
|
|
464
|
-
return { message: defaultMessage };
|
|
465
|
-
};
|
|
437
|
+
const getModelActions = modelCode => {
|
|
438
|
+
const currentModel = getModel(modelCode);
|
|
439
|
+
if (!currentModel) {
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
466
442
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
443
|
+
return currentModel.actions || [];
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const getModelSegment = (modelCode, segmentCode) => {
|
|
447
|
+
const currentModel = getModel(modelCode);
|
|
448
|
+
if (!currentModel || !currentModel.segments || currentModel.segments.length === 0) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return currentModel.segments
|
|
453
|
+
.find(s => s.code === segmentCode);
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
const buildError = (e, defaultMessage) => {
|
|
457
|
+
if (e && e.errors) {
|
|
458
|
+
let arr = [];
|
|
459
|
+
Object.entries(e.errors).forEach(value => {
|
|
460
|
+
arr.push({ field: value[0], message: value[1].message });
|
|
461
|
+
});
|
|
462
|
+
return { message: defaultMessage, error_details: arr };
|
|
463
|
+
}
|
|
464
|
+
else if (e && e.message) {
|
|
465
|
+
const errorObject = serializeError(e);
|
|
466
|
+
const arr = [{
|
|
467
|
+
message: errorObject.stack
|
|
468
|
+
}];
|
|
469
|
+
return { message: defaultMessage, error_details: arr };
|
|
470
|
+
}
|
|
471
|
+
return { message: defaultMessage };
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
const validateOrderStructure = orderConfig => {
|
|
475
|
+
let bool = true;
|
|
476
|
+
if (orderConfig && Array.isArray(orderConfig)) {
|
|
477
|
+
orderConfig.forEach(oc => {
|
|
478
|
+
if (!Array.isArray(oc) || oc.length !== 2 && !['ASC', 'DESC'].includes(oc[1])) {
|
|
479
|
+
bool = false;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
bool = false;
|
|
485
|
+
}
|
|
486
|
+
return bool;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
const getCleanOrderStructure = orderConfig => {
|
|
490
|
+
const order = {};
|
|
470
491
|
orderConfig.forEach(oc => {
|
|
471
|
-
|
|
472
|
-
bool = false;
|
|
473
|
-
}
|
|
492
|
+
order[oc[0]] = oc[1];
|
|
474
493
|
});
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
bool = false;
|
|
478
|
-
}
|
|
479
|
-
return bool;
|
|
480
|
-
};
|
|
494
|
+
return order;
|
|
495
|
+
};
|
|
481
496
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
};
|
|
497
|
+
const getAppConfig = () => {
|
|
498
|
+
return {
|
|
499
|
+
package: pjson.name,
|
|
500
|
+
version: pjson.version
|
|
501
|
+
};
|
|
502
|
+
};
|
|
489
503
|
|
|
490
|
-
module.exports.getAppConfig = () => {
|
|
491
504
|
return {
|
|
492
|
-
|
|
493
|
-
|
|
505
|
+
getAppConfig,
|
|
506
|
+
getCleanOrderStructure,
|
|
507
|
+
validateOrderStructure,
|
|
508
|
+
getModelSegment,
|
|
509
|
+
getModelSegments,
|
|
510
|
+
getModelActions,
|
|
511
|
+
getModelObject,
|
|
512
|
+
getModelAssociations,
|
|
513
|
+
getModelPrimaryKeys,
|
|
514
|
+
getModelWhereClause,
|
|
515
|
+
getModelRealname,
|
|
516
|
+
getModelProperties,
|
|
517
|
+
constructSearch,
|
|
518
|
+
getFieldsToPopulate,
|
|
519
|
+
getFieldsToPopulate,
|
|
520
|
+
refFields,
|
|
521
|
+
fieldsToValues,
|
|
522
|
+
constructQuery,
|
|
523
|
+
toFixedIfNecessary,
|
|
524
|
+
cleanString,
|
|
525
|
+
buildError,
|
|
526
|
+
permutations
|
|
494
527
|
};
|
|
495
|
-
};
|
|
528
|
+
};
|