orange-dragonfly-model 0.10.0 → 0.11.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.
- package/.eslintrc.json +18 -0
- package/components/model.js +329 -285
- package/index.js +1 -1
- package/package.json +11 -6
- package/tests/lookup-query.test.js +9 -8
- package/tests/output.test.js +32 -30
- package/tests/test-model.js +31 -31
- package/babel.config.json +0 -10
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"browser": true,
|
|
4
|
+
"node": true,
|
|
5
|
+
"commonjs": true,
|
|
6
|
+
"es2021": true
|
|
7
|
+
},
|
|
8
|
+
"extends": [
|
|
9
|
+
"standard"
|
|
10
|
+
],
|
|
11
|
+
"parserOptions": {
|
|
12
|
+
"ecmaVersion": 12
|
|
13
|
+
},
|
|
14
|
+
"rules": {
|
|
15
|
+
"camelcase": 0,
|
|
16
|
+
"no-prototype-builtins": 0
|
|
17
|
+
}
|
|
18
|
+
}
|
package/components/model.js
CHANGED
|
@@ -1,285 +1,329 @@
|
|
|
1
|
-
const ORM = require('orange-dragonfly-orm')
|
|
2
|
-
const validate = require('orange-dragonfly-validator')
|
|
3
|
-
|
|
4
|
-
class ValidationException extends Error {
|
|
5
|
-
info
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
await this.
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
*
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
*
|
|
261
|
-
* @param
|
|
262
|
-
* @
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
|
|
1
|
+
const ORM = require('orange-dragonfly-orm')
|
|
2
|
+
const validate = require('orange-dragonfly-validator')
|
|
3
|
+
|
|
4
|
+
class ValidationException extends Error {
|
|
5
|
+
get info () {
|
|
6
|
+
return {}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
class Model extends ORM.ActiveRecord {
|
|
11
|
+
static get IGNORE_EXTRA_FIELDS () {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Returns list of unique keys
|
|
17
|
+
* @returns Array[] List of unique keys
|
|
18
|
+
*/
|
|
19
|
+
static get UNIQUE_KEYS () {
|
|
20
|
+
return []
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Returns schema for the model (Orange Dragonfly Validator format)
|
|
25
|
+
* @return object
|
|
26
|
+
*/
|
|
27
|
+
static get validation_rules () {
|
|
28
|
+
return {
|
|
29
|
+
id: {
|
|
30
|
+
required: false,
|
|
31
|
+
type: 'integer',
|
|
32
|
+
min: 1
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Overridden method of ActiveRecord. Returns special fields list based on validation rules
|
|
39
|
+
* @return {Array}
|
|
40
|
+
*/
|
|
41
|
+
static get special_fields () {
|
|
42
|
+
const rules = this.validation_rules
|
|
43
|
+
const fields = []
|
|
44
|
+
for (const field of ['created_at', 'updated_at', 'deleted_at']) {
|
|
45
|
+
rules.hasOwnProperty(field) && fields.push(field)
|
|
46
|
+
}
|
|
47
|
+
return fields
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns list of relations restricted for extended output
|
|
52
|
+
* @return {Array}
|
|
53
|
+
*/
|
|
54
|
+
static get restricted_for_output () {
|
|
55
|
+
return []
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns list of fields restricted for lookup
|
|
60
|
+
* @return {Array}
|
|
61
|
+
*/
|
|
62
|
+
static get restricted_for_lookup () {
|
|
63
|
+
return []
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Returns list of fields restricted for create method
|
|
68
|
+
* @return {Array}
|
|
69
|
+
*/
|
|
70
|
+
static get restricted_for_create () {
|
|
71
|
+
return ['id'].concat(this.special_fields)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Returns list of fields restricted for update method
|
|
76
|
+
* @return {Array}
|
|
77
|
+
*/
|
|
78
|
+
static get restricted_for_update () {
|
|
79
|
+
return ['id'].concat(this.special_fields)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Lookup method
|
|
84
|
+
* @param data
|
|
85
|
+
* @return {SelectQuery}
|
|
86
|
+
*/
|
|
87
|
+
static lookupQuery (data) {
|
|
88
|
+
const rules = this.validation_rules
|
|
89
|
+
const q = this.selectQuery()
|
|
90
|
+
const filtered_rules = {}
|
|
91
|
+
for (const field of Object.keys(data)) {
|
|
92
|
+
if (!rules.hasOwnProperty(field)) {
|
|
93
|
+
if (this.IGNORE_EXTRA_FIELDS) {
|
|
94
|
+
continue
|
|
95
|
+
}
|
|
96
|
+
const ex = new ValidationException('Parameters error')
|
|
97
|
+
ex.info[field] = `Field "${field}" is not described for model ${this.name}`
|
|
98
|
+
throw ex
|
|
99
|
+
}
|
|
100
|
+
if (this.restricted_for_lookup.includes(field)) {
|
|
101
|
+
const ex = new ValidationException('Parameters error')
|
|
102
|
+
ex.info[field] = `Field "${field}" is restricted for searching model ${this.name}`
|
|
103
|
+
throw ex
|
|
104
|
+
}
|
|
105
|
+
q.where(field, data[field])
|
|
106
|
+
filtered_rules[field] = Array.isArray(data[field]) ? { type: 'array', children: { '*': rules[field] } } : rules[field]
|
|
107
|
+
}
|
|
108
|
+
validate(filtered_rules, data)
|
|
109
|
+
return q
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates object
|
|
114
|
+
* @param data
|
|
115
|
+
* @return {Promise<ActiveRecord>}
|
|
116
|
+
*/
|
|
117
|
+
static async create (data) {
|
|
118
|
+
const rules = this.validation_rules
|
|
119
|
+
const new_data = {}
|
|
120
|
+
for (const field of Object.keys(data)) {
|
|
121
|
+
if (!rules.hasOwnProperty(field)) {
|
|
122
|
+
if (this.IGNORE_EXTRA_FIELDS) {
|
|
123
|
+
continue
|
|
124
|
+
}
|
|
125
|
+
const ex = new ValidationException('Parameters error')
|
|
126
|
+
ex.info[field] = `Field "${field}" is not described for model ${this.name}`
|
|
127
|
+
throw ex
|
|
128
|
+
}
|
|
129
|
+
if (this.restricted_for_create.includes(field)) {
|
|
130
|
+
const ex = new ValidationException('Parameters error')
|
|
131
|
+
ex.info[field] = `Field "${field}" is restricted for creating model ${this.name}`
|
|
132
|
+
throw ex
|
|
133
|
+
}
|
|
134
|
+
new_data[field] = data[field]
|
|
135
|
+
}
|
|
136
|
+
return await (new this(new_data)).save()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Updates object
|
|
141
|
+
* @param data
|
|
142
|
+
* @return {Promise<ActiveRecord>}
|
|
143
|
+
*/
|
|
144
|
+
async update (data) {
|
|
145
|
+
if (!this.id) {
|
|
146
|
+
throw new Error('You can update saved object only')
|
|
147
|
+
}
|
|
148
|
+
const rules = this.constructor.validation_rules
|
|
149
|
+
const new_data = {}
|
|
150
|
+
for (const field of Object.keys(data)) {
|
|
151
|
+
if (!rules.hasOwnProperty(field)) {
|
|
152
|
+
if (this.constructor.IGNORE_EXTRA_FIELDS) {
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
const ex = new ValidationException('Parameters error')
|
|
156
|
+
ex.info[field] = `Field "${field}" is not described for model ${this.constructor.name}`
|
|
157
|
+
throw ex
|
|
158
|
+
}
|
|
159
|
+
if (this.constructor.restricted_for_update.includes(field)) {
|
|
160
|
+
const ex = new ValidationException('Parameters error')
|
|
161
|
+
ex.info[field] = `Field "${field}" is restricted for updating model ${this.constructor.name}`
|
|
162
|
+
throw ex
|
|
163
|
+
}
|
|
164
|
+
new_data[field] = data[field]
|
|
165
|
+
}
|
|
166
|
+
return await this.save(new_data)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Checks uniqueness of the object based on UNIQUE_KEYS
|
|
171
|
+
*/
|
|
172
|
+
async checkUniqueness (exception_mode = false) {
|
|
173
|
+
for (const fields of this.constructor.UNIQUE_KEYS) {
|
|
174
|
+
if (!await this.isUnique(fields)) {
|
|
175
|
+
if (exception_mode) {
|
|
176
|
+
const ex = new ValidationException('Object is not unique')
|
|
177
|
+
for (const field of fields) {
|
|
178
|
+
ex.info[field] = 'Part of the unique key'
|
|
179
|
+
}
|
|
180
|
+
throw ex
|
|
181
|
+
}
|
|
182
|
+
return false
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return true
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async _preSave () {
|
|
189
|
+
if (this.constructor.IGNORE_EXTRA_FIELDS) {
|
|
190
|
+
const rules = this.constructor.validation_rules
|
|
191
|
+
for (const key of Object.keys(this.data).filter(v => !rules.hasOwnProperty(v))) {
|
|
192
|
+
delete this.data[key]
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
await super._preSave()
|
|
196
|
+
await this.checkUniqueness(true)
|
|
197
|
+
await this.validate()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Performs custom validation - returns null (empty object) in case of successful validation or object of validation issues
|
|
202
|
+
* @return {Promise<null|Object>}
|
|
203
|
+
*/
|
|
204
|
+
async custom_validation () {
|
|
205
|
+
return null
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validate object's data
|
|
210
|
+
* @return {Promise<void>}
|
|
211
|
+
*/
|
|
212
|
+
async validate () {
|
|
213
|
+
const rules = this.constructor.validation_rules
|
|
214
|
+
if (!rules) {
|
|
215
|
+
throw new Error(`Validation rules are not defined for mode ${this.constructor.name}`)
|
|
216
|
+
}
|
|
217
|
+
// Convert integer 1 or 0 to boolean
|
|
218
|
+
let types
|
|
219
|
+
for (const rule_name of Object.keys(rules)) {
|
|
220
|
+
if (this.data.hasOwnProperty(rule_name)) {
|
|
221
|
+
if (rules[rule_name].hasOwnProperty('type')) {
|
|
222
|
+
types = Array.isArray(rules[rule_name].type) ? rules[rule_name].type : [rules[rule_name].type]
|
|
223
|
+
if (types.includes('boolean')) {
|
|
224
|
+
if ((this.data[rule_name] === 1) || (this.data[rule_name] === 0)) {
|
|
225
|
+
this.data[rule_name] = this.data[rule_name] === 1
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
validate(rules, this.data)
|
|
232
|
+
const custom_validation_errors = await this.custom_validation()
|
|
233
|
+
if (custom_validation_errors && Object.keys(custom_validation_errors).length) {
|
|
234
|
+
const ex = new ValidationException('Validation failed')
|
|
235
|
+
for (const [param, message] of Object.entries(custom_validation_errors)) ex.info[param] = message
|
|
236
|
+
throw ex
|
|
237
|
+
}
|
|
238
|
+
let rel
|
|
239
|
+
const relation_errors = []
|
|
240
|
+
for (const rel_name of Object.keys(this.constructor.available_relations)) {
|
|
241
|
+
rel = this.constructor.available_relations[rel_name]
|
|
242
|
+
if (rel.mode === 'parent') {
|
|
243
|
+
if (this.data.hasOwnProperty(rel._a_key_by_mode) && (this.data[rel._a_key_by_mode] !== null) && (this.data[rel._a_key_by_mode] !== 0)) {
|
|
244
|
+
if ((await this.rel(rel_name, true)) === null) {
|
|
245
|
+
relation_errors.push(rel._a_key_by_mode)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (relation_errors.length) {
|
|
250
|
+
const ex = new ValidationException(`Some relations of the ${this.constructor.name} are not found`)
|
|
251
|
+
for (const param of relation_errors) ex.info[param] = 'Parent object not found'
|
|
252
|
+
throw ex
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Returns object by ID if it exists and accessible by user
|
|
259
|
+
* @param id
|
|
260
|
+
* @param user
|
|
261
|
+
* @param mode
|
|
262
|
+
* @return {Promise<ActiveRecord>}
|
|
263
|
+
*/
|
|
264
|
+
static async findAndCheckAccessOrDie (id, user, mode = null) {
|
|
265
|
+
const obj = await this.find(id)
|
|
266
|
+
if (!obj) {
|
|
267
|
+
throw new Error(`${this.name} #${id} not found`)
|
|
268
|
+
}
|
|
269
|
+
if (!(await obj.accessible(user, mode))) {
|
|
270
|
+
throw new Error(`${this.name} #${id} is not accessible${mode ? ` for ${mode}` : ''}`)
|
|
271
|
+
}
|
|
272
|
+
return obj
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Returns is object accessible by user
|
|
277
|
+
* @param user
|
|
278
|
+
* @param mode
|
|
279
|
+
* @return {Promise<boolean>}
|
|
280
|
+
*/
|
|
281
|
+
async accessible (user, mode = null) {
|
|
282
|
+
return mode === null
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Returns public data of the object
|
|
287
|
+
* @return Object
|
|
288
|
+
*/
|
|
289
|
+
get output () {
|
|
290
|
+
return {
|
|
291
|
+
id: this.id
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Format output
|
|
297
|
+
* @param mode
|
|
298
|
+
* @return {Object}
|
|
299
|
+
*/
|
|
300
|
+
formatOutput (mode) {
|
|
301
|
+
return this.output
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Returns public data of the object with relations
|
|
306
|
+
* @param required_relations
|
|
307
|
+
* @param mode
|
|
308
|
+
* @return {Promise<Object>}
|
|
309
|
+
*/
|
|
310
|
+
async getExtendedOutput (required_relations = [], mode = null) {
|
|
311
|
+
const output = this.formatOutput(mode)
|
|
312
|
+
let rel_data, rel_mode, rel_relations
|
|
313
|
+
for (const name of required_relations) {
|
|
314
|
+
if (name.split(':').length > 1) continue
|
|
315
|
+
if (this.constructor.restricted_for_output.includes(name)) {
|
|
316
|
+
throw new Error(`Relation "${name}" is not allowed for extended output of model ${this.constructor.name}`)
|
|
317
|
+
}
|
|
318
|
+
rel_data = await this.rel(name)
|
|
319
|
+
rel_mode = `relation:${this.constructor.name}.${name}`
|
|
320
|
+
rel_relations = required_relations.filter(v => v.startsWith(`${name}:`)).map(v => v.substr(name.length + 1))
|
|
321
|
+
output[`:${name}`] = Array.isArray(rel_data)
|
|
322
|
+
? await Promise.all(rel_data.map(v => v.getExtendedOutput(rel_relations, rel_mode)))
|
|
323
|
+
: (rel_data ? await rel_data.getExtendedOutput(rel_relations, rel_mode) : null)
|
|
324
|
+
}
|
|
325
|
+
return output
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
module.exports = Model
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orange-dragonfly-model",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Orange Dragonfly Model",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"url": "git+https://github.com/charger88/orange-dragonfly-model.git"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
-
"test": "jest"
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"lint": "eslint ./"
|
|
12
13
|
},
|
|
13
14
|
"keywords": [
|
|
14
15
|
"framework"
|
|
@@ -24,11 +25,15 @@
|
|
|
24
25
|
},
|
|
25
26
|
"homepage": "https://github.com/charger88/orange-dragonfly-model#readme",
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"orange-dragonfly-orm": "^0.7.
|
|
28
|
-
"orange-dragonfly-validator": "^0.7.
|
|
28
|
+
"orange-dragonfly-orm": "^0.7.21",
|
|
29
|
+
"orange-dragonfly-validator": "^0.7.8"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"
|
|
32
|
-
"
|
|
32
|
+
"eslint": "^7.32.0",
|
|
33
|
+
"eslint-config-standard": "^16.0.3",
|
|
34
|
+
"eslint-plugin-import": "^2.24.1",
|
|
35
|
+
"eslint-plugin-node": "^11.1.0",
|
|
36
|
+
"eslint-plugin-promise": "^5.1.0",
|
|
37
|
+
"jest": "^25.5.4"
|
|
33
38
|
}
|
|
34
39
|
}
|
|
@@ -1,41 +1,42 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
|
|
1
3
|
const TestModel = require('./test-model')
|
|
2
4
|
|
|
3
5
|
test('select-id', () => {
|
|
4
6
|
const data = [1]
|
|
5
|
-
const lookup_data = {
|
|
7
|
+
const lookup_data = { id: data[0] }
|
|
6
8
|
const q = TestModel.lookupQuery(lookup_data).buildRawSQL()
|
|
7
9
|
expect(q.sql).toBe('SELECT * FROM test_model WHERE test_model.id = ?')
|
|
8
10
|
expect(q.params).toEqual(data)
|
|
9
11
|
})
|
|
10
12
|
|
|
11
13
|
test('select-id-multiple', () => {
|
|
12
|
-
const data = [1,2,3]
|
|
13
|
-
const lookup_data = {
|
|
14
|
+
const data = [1, 2, 3]
|
|
15
|
+
const lookup_data = { id: data }
|
|
14
16
|
const q = TestModel.lookupQuery(lookup_data).buildRawSQL()
|
|
15
17
|
expect(q.sql).toBe('SELECT * FROM test_model WHERE test_model.id IN (?, ?, ?)')
|
|
16
18
|
expect(q.params).toEqual(data)
|
|
17
19
|
})
|
|
18
20
|
|
|
19
|
-
|
|
20
21
|
test('select-username', () => {
|
|
21
22
|
const data = ['qwerty']
|
|
22
|
-
const lookup_data = {
|
|
23
|
+
const lookup_data = { username: data[0] }
|
|
23
24
|
const q = TestModel.lookupQuery(lookup_data).buildRawSQL()
|
|
24
25
|
expect(q.sql).toBe('SELECT * FROM test_model WHERE test_model.username = ?')
|
|
25
26
|
expect(q.params).toEqual(data)
|
|
26
27
|
})
|
|
27
28
|
|
|
28
29
|
test('select-username-incorrect', () => {
|
|
29
|
-
const lookup_data = {
|
|
30
|
+
const lookup_data = { username: 123 }
|
|
30
31
|
expect(() => TestModel.lookupQuery(lookup_data)).toThrow()
|
|
31
32
|
})
|
|
32
33
|
|
|
33
34
|
test('select-username-wrong-field', () => {
|
|
34
|
-
const lookup_data = {'wrong-field': 123}
|
|
35
|
+
const lookup_data = { 'wrong-field': 123 }
|
|
35
36
|
expect(() => TestModel.lookupQuery(lookup_data)).toThrow()
|
|
36
37
|
})
|
|
37
38
|
|
|
38
39
|
test('select-username-restricted-field', () => {
|
|
39
|
-
const lookup_data = {
|
|
40
|
+
const lookup_data = { uuid: '1234567890123456789012345678901234567890' }
|
|
40
41
|
expect(() => TestModel.lookupQuery(lookup_data)).toThrow()
|
|
41
42
|
})
|
package/tests/output.test.js
CHANGED
|
@@ -1,56 +1,58 @@
|
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
|
|
1
3
|
const TestModel = require('./test-model')
|
|
2
4
|
|
|
3
5
|
const data = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
6
|
+
id: 1,
|
|
7
|
+
username: 'tester',
|
|
8
|
+
uuid: '1234567890123456789012345678901234567890'
|
|
9
|
+
}
|
|
8
10
|
|
|
9
11
|
test('output', () => {
|
|
10
12
|
const t = new TestModel(data)
|
|
11
13
|
const o = t.output
|
|
12
|
-
expect(o
|
|
13
|
-
expect(o
|
|
14
|
-
expect(o
|
|
15
|
-
expect(o
|
|
14
|
+
expect(o.id).toBe(data.id)
|
|
15
|
+
expect(o.username).toBe(data.username)
|
|
16
|
+
expect(o.uuid).toBe(data.uuid)
|
|
17
|
+
expect(o.constant_value).toBe('QWERTY')
|
|
16
18
|
})
|
|
17
19
|
|
|
18
20
|
test('output-extended', async () => {
|
|
19
21
|
const t = new TestModel(data)
|
|
20
22
|
const o = await t.getExtendedOutput()
|
|
21
|
-
expect(o
|
|
22
|
-
expect(o
|
|
23
|
-
expect(o
|
|
24
|
-
expect(o
|
|
23
|
+
expect(o.id).toBe(data.id)
|
|
24
|
+
expect(o.username).toBe(data.username)
|
|
25
|
+
expect(o.uuid).toBe(data.uuid)
|
|
26
|
+
expect(o.constant_value).toBe('QWERTY')
|
|
25
27
|
})
|
|
26
28
|
|
|
27
29
|
test('output-extended-rel-empty', async () => {
|
|
28
30
|
const t = new TestModel(data)
|
|
29
|
-
t.relations
|
|
31
|
+
t.relations.child_test = null
|
|
30
32
|
const o = await t.getExtendedOutput(['child_test'])
|
|
31
|
-
expect(o
|
|
32
|
-
expect(o
|
|
33
|
-
expect(o
|
|
34
|
-
expect(o
|
|
33
|
+
expect(o.id).toBe(data.id)
|
|
34
|
+
expect(o.username).toBe(data.username)
|
|
35
|
+
expect(o.uuid).toBe(data.uuid)
|
|
36
|
+
expect(o.constant_value).toBe('QWERTY')
|
|
35
37
|
expect(o[':child_test']).toEqual(null)
|
|
36
38
|
})
|
|
37
39
|
|
|
38
40
|
test('output-extended-rel-data', async () => {
|
|
39
41
|
const data2 = {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
42
|
+
id: 2,
|
|
43
|
+
username: 'admin',
|
|
44
|
+
uuid: '0987654321098765432109876543210987654321'
|
|
45
|
+
}
|
|
44
46
|
const t = new TestModel(data)
|
|
45
47
|
const t2 = new TestModel(data2)
|
|
46
|
-
t.relations
|
|
48
|
+
t.relations.child_test = t2
|
|
47
49
|
const o = await t.getExtendedOutput(['child_test'])
|
|
48
|
-
expect(o
|
|
49
|
-
expect(o
|
|
50
|
-
expect(o
|
|
51
|
-
expect(o
|
|
52
|
-
expect(o[':child_test']
|
|
53
|
-
expect(o[':child_test']
|
|
54
|
-
expect(o[':child_test']
|
|
55
|
-
expect(o[':child_test']
|
|
50
|
+
expect(o.id).toBe(data.id)
|
|
51
|
+
expect(o.username).toBe(data.username)
|
|
52
|
+
expect(o.uuid).toBe(data.uuid)
|
|
53
|
+
expect(o.constant_value).toBe('QWERTY')
|
|
54
|
+
expect(o[':child_test'].id).toBe(data2.id)
|
|
55
|
+
expect(o[':child_test'].username).toBe(data2.username)
|
|
56
|
+
expect(o[':child_test'].uuid).toBe(data2.uuid)
|
|
57
|
+
expect(o[':child_test'].constant_value).toBe('QWERTY')
|
|
56
58
|
})
|
package/tests/test-model.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
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
|
-
module.exports = TestModel
|
|
1
|
+
/* eslint-disable no-undef */
|
|
2
|
+
|
|
3
|
+
const Model = require('../components/model')
|
|
4
|
+
const ORM = require('orange-dragonfly-orm')
|
|
5
|
+
|
|
6
|
+
class TestModel extends Model {
|
|
7
|
+
static get available_relations () {
|
|
8
|
+
return {
|
|
9
|
+
child_test: ORM.Relation.parent(this, this)
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
static get validation_rules () {
|
|
14
|
+
return require('./test-model-schema.json')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static get restricted_for_lookup () {
|
|
18
|
+
return ['uuid']
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get output () {
|
|
22
|
+
return {
|
|
23
|
+
id: this.id,
|
|
24
|
+
username: this.data.username,
|
|
25
|
+
uuid: this.data.uuid,
|
|
26
|
+
constant_value: 'QWERTY'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = TestModel
|