functional-models 1.0.18 → 1.0.23
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/package.json +2 -3
- package/src/models.js +10 -6
- package/src/properties.js +20 -9
- package/src/validation.js +11 -9
- package/test/src/models.test.js +87 -0
- package/test/src/validation.test.js +4 -1
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functional-models",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"description": "A library for creating JavaScript function based models.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "nyc --all mocha --recursive ./test/**/*.test.js",
|
|
7
|
+
"test": "nyc --all mocha --recursive './test/**/*.test.js'",
|
|
8
8
|
"feature-tests": "./node_modules/@cucumber/cucumber/bin/cucumber-js",
|
|
9
9
|
"coverage": "nyc --all --reporter=lcov npm test"
|
|
10
10
|
},
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"get-random-values": "^1.2.2",
|
|
52
|
-
"lazy-property": "^1.0.0",
|
|
53
52
|
"lodash": "^4.17.21"
|
|
54
53
|
}
|
|
55
54
|
}
|
package/src/models.js
CHANGED
|
@@ -2,14 +2,16 @@ const merge = require('lodash/merge')
|
|
|
2
2
|
const { toObj } = require('./serialization')
|
|
3
3
|
const { createPropertyTitle } = require('./utils')
|
|
4
4
|
const { createModelValidator } = require('./validation')
|
|
5
|
+
const { UniqueId } = require('./properties')
|
|
5
6
|
|
|
6
7
|
const MODEL_DEF_KEYS = ['meta', 'functions']
|
|
7
|
-
const PROTECTED_KEYS = ['model']
|
|
8
8
|
|
|
9
9
|
const Model = (
|
|
10
10
|
modelName,
|
|
11
11
|
keyToProperty,
|
|
12
12
|
{
|
|
13
|
+
primaryKey = 'id',
|
|
14
|
+
getPrimaryKeyProperty=() => UniqueId({required: true}),
|
|
13
15
|
instanceCreatedCallback = null,
|
|
14
16
|
modelFunctions = {},
|
|
15
17
|
instanceFunctions = {},
|
|
@@ -25,11 +27,11 @@ const Model = (
|
|
|
25
27
|
*/
|
|
26
28
|
// eslint-disable-next-line functional/no-let
|
|
27
29
|
let model = null
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
30
|
+
keyToProperty = {
|
|
31
|
+
// this key exists over keyToProperty, so it can be overrided if desired.
|
|
32
|
+
[primaryKey]: getPrimaryKeyProperty(),
|
|
33
|
+
...keyToProperty,
|
|
34
|
+
}
|
|
33
35
|
const instanceProperties = Object.entries(keyToProperty).filter(
|
|
34
36
|
([key, _]) => MODEL_DEF_KEYS.includes(key) === false
|
|
35
37
|
)
|
|
@@ -78,6 +80,7 @@ const Model = (
|
|
|
78
80
|
},
|
|
79
81
|
functions: {
|
|
80
82
|
toObj: toObj(loadedInternals),
|
|
83
|
+
getPrimaryKey: loadedInternals[createPropertyTitle(primaryKey)],
|
|
81
84
|
validate: {
|
|
82
85
|
model: () =>
|
|
83
86
|
createModelValidator(loadedInternals, modelValidators)(instance),
|
|
@@ -125,6 +128,7 @@ const Model = (
|
|
|
125
128
|
create,
|
|
126
129
|
getName: () => modelName,
|
|
127
130
|
getProperties: () => properties,
|
|
131
|
+
getPrimaryKeyName: () => primaryKey,
|
|
128
132
|
})
|
|
129
133
|
return model
|
|
130
134
|
}
|
package/src/properties.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const identity = require('lodash/identity')
|
|
2
|
+
const get = require('lodash/get')
|
|
2
3
|
const isFunction = require('lodash/isFunction')
|
|
3
4
|
const merge = require('lodash/merge')
|
|
4
5
|
const {
|
|
@@ -57,8 +58,8 @@ const Property = (config = {}, additionalMetadata = {}) => {
|
|
|
57
58
|
},
|
|
58
59
|
getValidator: valueGetter => {
|
|
59
60
|
const validator = createPropertyValidator(config)
|
|
60
|
-
const _propertyValidatorWrapper = async () => {
|
|
61
|
-
return validator(await valueGetter())
|
|
61
|
+
const _propertyValidatorWrapper = async (instance, instanceData) => {
|
|
62
|
+
return validator(await valueGetter(), instance, instanceData)
|
|
62
63
|
}
|
|
63
64
|
return _propertyValidatorWrapper
|
|
64
65
|
},
|
|
@@ -106,17 +107,26 @@ const ReferenceProperty = (model, config = {}) => {
|
|
|
106
107
|
if (!instanceValues) {
|
|
107
108
|
return null
|
|
108
109
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
110
|
+
const theModel = _getModel()
|
|
111
|
+
const primaryKey = theModel.getPrimaryKeyName()
|
|
112
|
+
if (instanceValues[primaryKey]) {
|
|
113
|
+
return instanceValues[primaryKey]
|
|
114
|
+
}
|
|
115
|
+
const primaryKeyFunc = get(instanceValues, 'functions.getPrimaryKey')
|
|
116
|
+
if (primaryKeyFunc) {
|
|
117
|
+
return primaryKeyFunc()
|
|
118
|
+
}
|
|
119
|
+
return instanceValues
|
|
114
120
|
}
|
|
121
|
+
|
|
115
122
|
const valueIsModelInstance =
|
|
116
123
|
Boolean(instanceValues) && Boolean(instanceValues.functions)
|
|
117
124
|
|
|
118
125
|
const _getInstanceReturn = objToUse => {
|
|
119
|
-
|
|
126
|
+
// We need to determine if the object we just go is an actual model instance to determine if we need to make one.
|
|
127
|
+
const objIsModelInstance =
|
|
128
|
+
Boolean(objToUse) && Boolean(objToUse.functions)
|
|
129
|
+
const instance = objIsModelInstance
|
|
120
130
|
? objToUse
|
|
121
131
|
: _getModel().create(objToUse)
|
|
122
132
|
return merge({}, instance, {
|
|
@@ -131,7 +141,8 @@ const ReferenceProperty = (model, config = {}) => {
|
|
|
131
141
|
}
|
|
132
142
|
if (config.fetcher) {
|
|
133
143
|
const id = await _getId()
|
|
134
|
-
const
|
|
144
|
+
const model = _getModel()
|
|
145
|
+
const obj = await config.fetcher(model, id)
|
|
135
146
|
return _getInstanceReturn(obj)
|
|
136
147
|
}
|
|
137
148
|
return _getId(instanceValues)
|
package/src/validation.js
CHANGED
|
@@ -208,11 +208,11 @@ const createPropertyValidator = config => {
|
|
|
208
208
|
: validators.includes(isRequired)
|
|
209
209
|
const validator =
|
|
210
210
|
validators.length > 0 ? aggregateValidator(validators) : emptyValidator
|
|
211
|
-
const _propertyValidator = async value => {
|
|
211
|
+
const _propertyValidator = async (value, instance, instanceData) => {
|
|
212
212
|
if (!value && !isRequiredValue) {
|
|
213
213
|
return []
|
|
214
214
|
}
|
|
215
|
-
const errors = await validator(value)
|
|
215
|
+
const errors = await validator(value, instance, instanceData)
|
|
216
216
|
return [...new Set(flatMap(errors))]
|
|
217
217
|
}
|
|
218
218
|
return _propertyValidator
|
|
@@ -223,20 +223,22 @@ const createModelValidator = (properties, modelValidators = []) => {
|
|
|
223
223
|
const keysAndFunctions = Object.entries(
|
|
224
224
|
get(properties, 'functions.validate', {})
|
|
225
225
|
)
|
|
226
|
+
const instanceData = await (modelValidators.length > 0
|
|
227
|
+
? instance.functions.toObj()
|
|
228
|
+
: {})
|
|
226
229
|
const data = await Promise.all(
|
|
227
230
|
keysAndFunctions.map(async ([key, validator]) => {
|
|
228
231
|
if (key === 'model') {
|
|
229
232
|
return [key, []]
|
|
230
233
|
}
|
|
231
|
-
return [key, await validator()]
|
|
234
|
+
return [key, await validator(instance, instanceData)]
|
|
232
235
|
})
|
|
233
236
|
)
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
)).filter(x=>x)
|
|
237
|
+
const modelValidationErrors = (
|
|
238
|
+
await Promise.all(
|
|
239
|
+
modelValidators.map(validator => validator(instance, instanceData))
|
|
240
|
+
)
|
|
241
|
+
).filter(x => x)
|
|
240
242
|
const propertyErrors = data
|
|
241
243
|
.filter(([_, errors]) => Boolean(errors) && errors.length > 0)
|
|
242
244
|
.reduce((acc, [key, errors]) => {
|
package/test/src/models.test.js
CHANGED
|
@@ -60,7 +60,57 @@ describe('/src/models.js', () => {
|
|
|
60
60
|
)
|
|
61
61
|
assert.isFunction(model.myString)
|
|
62
62
|
})
|
|
63
|
+
describe('#getPrimaryKeyName()', () => {
|
|
64
|
+
it('should return "primaryKey" when this value is passed in as the primaryKey', () => {
|
|
65
|
+
const expected = 'primaryKey'
|
|
66
|
+
const model = Model(
|
|
67
|
+
'ModelName',
|
|
68
|
+
{},
|
|
69
|
+
{
|
|
70
|
+
primaryKey: expected,
|
|
71
|
+
modelFunctions: {
|
|
72
|
+
myString: model => () => {
|
|
73
|
+
return 'To String'
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
const actual = model.getPrimaryKeyName()
|
|
79
|
+
assert.equal(actual, expected)
|
|
80
|
+
})
|
|
81
|
+
})
|
|
63
82
|
describe('#create()', () => {
|
|
83
|
+
it('should have an "getId" field when no primaryKey is passed', () => {
|
|
84
|
+
const model = Model(
|
|
85
|
+
'ModelName',
|
|
86
|
+
{},
|
|
87
|
+
{
|
|
88
|
+
instanceFunctions: {
|
|
89
|
+
toString: instance => () => {
|
|
90
|
+
return 'An instance'
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
const instance = model.create({})
|
|
96
|
+
assert.isFunction(instance.getId)
|
|
97
|
+
})
|
|
98
|
+
it('should have an "getMyPrimaryKeyId" field when "myPrimaryKeyId" is passed as the "primaryKey" is passed', () => {
|
|
99
|
+
const model = Model(
|
|
100
|
+
'ModelName',
|
|
101
|
+
{},
|
|
102
|
+
{
|
|
103
|
+
primaryKey: 'myPrimaryKeyId',
|
|
104
|
+
instanceFunctions: {
|
|
105
|
+
toString: instance => () => {
|
|
106
|
+
return 'An instance'
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
const instance = model.create({})
|
|
112
|
+
assert.isFunction(instance.getMyPrimaryKeyId)
|
|
113
|
+
})
|
|
64
114
|
it('should find instance.functions.toString when in instanceFunctions', () => {
|
|
65
115
|
const model = Model(
|
|
66
116
|
'ModelName',
|
|
@@ -234,5 +284,42 @@ describe('/src/models.js', () => {
|
|
|
234
284
|
Model('name', { model: 'weeee' }).create()
|
|
235
285
|
})
|
|
236
286
|
})
|
|
287
|
+
describe('#functions.getPrimaryKey()', () => {
|
|
288
|
+
it('should return the id field when no primaryKey is passed', async () => {
|
|
289
|
+
const model = Model(
|
|
290
|
+
'ModelName',
|
|
291
|
+
{},
|
|
292
|
+
{
|
|
293
|
+
instanceFunctions: {
|
|
294
|
+
toString: instance => () => {
|
|
295
|
+
return 'An instance'
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
const expected = 'my-primary-key'
|
|
301
|
+
const instance = model.create({id: expected})
|
|
302
|
+
const actual = await instance.functions.getPrimaryKey()
|
|
303
|
+
assert.equal(actual, expected)
|
|
304
|
+
})
|
|
305
|
+
it('should return the primaryKey field when "primaryKey" is passed as primaryKey', async () => {
|
|
306
|
+
const model = Model(
|
|
307
|
+
'ModelName',
|
|
308
|
+
{},
|
|
309
|
+
{
|
|
310
|
+
primaryKey: 'primaryKey',
|
|
311
|
+
instanceFunctions: {
|
|
312
|
+
toString: instance => () => {
|
|
313
|
+
return 'An instance'
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
const expected = 'my-primary-key'
|
|
319
|
+
const instance = model.create({primaryKey: expected})
|
|
320
|
+
const actual = await instance.functions.getPrimaryKey()
|
|
321
|
+
assert.equal(actual, expected)
|
|
322
|
+
})
|
|
323
|
+
})
|
|
237
324
|
})
|
|
238
325
|
})
|
|
@@ -408,7 +408,10 @@ describe('/src/validation.js', () => {
|
|
|
408
408
|
},
|
|
409
409
|
},
|
|
410
410
|
}
|
|
411
|
-
const validator = createModelValidator(properties, [
|
|
411
|
+
const validator = createModelValidator(properties, [
|
|
412
|
+
modelValidator1,
|
|
413
|
+
modelValidator2,
|
|
414
|
+
])
|
|
412
415
|
const instance = testModel3.create({
|
|
413
416
|
id: 'test-id',
|
|
414
417
|
name: 'my-name',
|