functional-models 1.0.8 → 1.0.14
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/README.md +36 -21
- package/features/arrayFields.feature +15 -15
- package/features/model.feature +2 -2
- package/features/stepDefinitions/steps.js +26 -26
- package/package.json +2 -1
- package/src/index.js +1 -1
- package/src/models.js +78 -29
- package/src/{fields.js → properties.js} +98 -66
- package/src/serialization.js +4 -4
- package/src/validation.js +78 -23
- package/test/src/models.test.js +124 -47
- package/test/src/{fields.test.js → properties.test.js} +204 -166
- package/test/src/serialization.test.js +15 -15
- package/test/src/validation.test.js +81 -14
package/README.md
CHANGED
|
@@ -10,29 +10,36 @@ This library empowers the creation of pure JavaScript function based models that
|
|
|
10
10
|
## Example Usage
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
field,
|
|
14
|
+
constantValueField,
|
|
15
|
+
textField,
|
|
16
|
+
dateField,
|
|
17
|
+
integerField,
|
|
16
18
|
uniqueId,
|
|
19
|
+
createModel,
|
|
20
|
+
validation,
|
|
17
21
|
}= require('functional-models')
|
|
18
22
|
|
|
19
|
-
const Truck = ({
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
const Truck = createModel({
|
|
24
|
+
type: constantValueField('truck'),
|
|
25
|
+
id: uniqueId({required: true}),
|
|
26
|
+
make: textField({ maxLength: 20, minLength: 3, required: true}),
|
|
27
|
+
model: textField({ maxLength: 20, minLength: 3, required: true}),
|
|
28
|
+
color: textField({ maxLength: 10, minLength: 3, validators: [
|
|
29
|
+
validation.meetsRegex(/Red/),
|
|
30
|
+
]}),
|
|
31
|
+
year: integerField({ maxValue: 2500, minValue: 1900}),
|
|
32
|
+
lastModified: dateField({ autoNow: true}),
|
|
33
|
+
})
|
|
27
34
|
|
|
28
35
|
|
|
29
36
|
const myTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 2013})
|
|
30
37
|
|
|
31
|
-
console.log(myTruck.getId()) // a random uuid
|
|
32
|
-
console.log(myTruck.getMake()) // 'Ford'
|
|
33
|
-
console.log(myTruck.getModel()) // 'F-150'
|
|
34
|
-
console.log(myTruck.getColor()) // 'White'
|
|
35
|
-
console.log(myTruck.getYear()) // 2013
|
|
38
|
+
console.log(await myTruck.getId()) // a random uuid
|
|
39
|
+
console.log(await myTruck.getMake()) // 'Ford'
|
|
40
|
+
console.log(await myTruck.getModel()) // 'F-150'
|
|
41
|
+
console.log(await myTruck.getColor()) // 'White'
|
|
42
|
+
console.log(await myTruck.getYear()) // 2013
|
|
36
43
|
|
|
37
44
|
const asJson = await myTruck.functions.toJson()
|
|
38
45
|
console.log(asJson)
|
|
@@ -47,8 +54,16 @@ This library empowers the creation of pure JavaScript function based models that
|
|
|
47
54
|
*/
|
|
48
55
|
|
|
49
56
|
const sameTruck = Truck(asJson)
|
|
50
|
-
console.log(sameTruck.getId()) // same uuid as above
|
|
51
|
-
console.log(sameTruck.getMake()) // 'Ford'
|
|
52
|
-
console.log(sameTruck.getModel()) // 'F-150'
|
|
53
|
-
console.log(sameTruck.getColor()) // 'White'
|
|
54
|
-
console.log(sameTruck.getYear()) // 2013
|
|
57
|
+
console.log(await sameTruck.getId()) // same uuid as above
|
|
58
|
+
console.log(await sameTruck.getMake()) // 'Ford'
|
|
59
|
+
console.log(await sameTruck.getModel()) // 'F-150'
|
|
60
|
+
console.log(await sameTruck.getColor()) // 'White'
|
|
61
|
+
console.log(await sameTruck.getYear()) // 2013
|
|
62
|
+
|
|
63
|
+
// Validation
|
|
64
|
+
const errors = await sameTruck.functions.validate.model() // {}
|
|
65
|
+
|
|
66
|
+
const newTruck = Truck({ make: 'Ford', model: 'F-150', color: 'White', year: 20130})
|
|
67
|
+
const errors2 = await newTruck.functions.validate.model()
|
|
68
|
+
console.log(errors2)
|
|
69
|
+
// {"year": 'Value is too long'}
|
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
Feature: Array
|
|
1
|
+
Feature: Array Property
|
|
2
2
|
|
|
3
|
-
Scenario: A model that has an array
|
|
3
|
+
Scenario: A model that has an array property is created holding an array of integers and is checked and validated
|
|
4
4
|
Given ArrayModel1 model is used
|
|
5
5
|
When ArrayModelData1 data is inserted
|
|
6
|
-
Then the
|
|
6
|
+
Then the getArrayProperty property is called on the model
|
|
7
7
|
Then the array values match
|
|
8
8
|
| array | [1,2,3,4,5]|
|
|
9
9
|
Then functions.validate is called
|
|
10
10
|
Then an array of 0 errors is shown
|
|
11
11
|
|
|
12
|
-
Scenario: A model that has an array
|
|
12
|
+
Scenario: A model that has an array property but has a non-array inserted into it fails validation
|
|
13
13
|
Given ArrayModel1 model is used
|
|
14
14
|
When ArrayModelData2 data is inserted
|
|
15
|
-
Then the
|
|
15
|
+
Then the getArrayProperty property is called on the model
|
|
16
16
|
Then functions.validate is called
|
|
17
17
|
Then an array of 1 errors is shown
|
|
18
18
|
|
|
19
|
-
Scenario: A model that has an array
|
|
19
|
+
Scenario: A model that has an array property for integers but an array of strings is inserted into it, it should fail validation
|
|
20
20
|
Given ArrayModel1 model is used
|
|
21
21
|
When ArrayModelData3 data is inserted
|
|
22
|
-
Then the
|
|
22
|
+
Then the getArrayProperty property is called on the model
|
|
23
23
|
Then functions.validate is called
|
|
24
24
|
Then an array of 1 errors is shown
|
|
25
25
|
|
|
26
|
-
Scenario: A model that has an array
|
|
26
|
+
Scenario: A model that has an array property that has mixed values it should not fail validation.
|
|
27
27
|
Given ArrayModel2 model is used
|
|
28
28
|
When ArrayModelData4 data is inserted
|
|
29
|
-
Then the
|
|
29
|
+
Then the getArrayProperty property is called on the model
|
|
30
30
|
Then functions.validate is called
|
|
31
31
|
Then an array of 0 errors is shown
|
|
32
32
|
|
|
33
|
-
Scenario: A model that uses the
|
|
33
|
+
Scenario: A model that uses the arrayProperty property that has mixed values it should not fail validation.
|
|
34
34
|
Given ArrayModel3 model is used
|
|
35
35
|
When ArrayModelData4 data is inserted
|
|
36
|
-
Then the
|
|
36
|
+
Then the getArrayProperty property is called on the model
|
|
37
37
|
Then functions.validate is called
|
|
38
38
|
Then an array of 0 errors is shown
|
|
39
39
|
|
|
40
|
-
Scenario: A model that uses the
|
|
40
|
+
Scenario: A model that uses the arrayProperty with the choice validator should pass validation with no errors.
|
|
41
41
|
Given ArrayModel4 model is used
|
|
42
42
|
When ArrayModelData5 data is inserted
|
|
43
|
-
Then the
|
|
43
|
+
Then the getArrayProperty property is called on the model
|
|
44
44
|
Then functions.validate is called
|
|
45
45
|
Then an array of 0 errors is shown
|
|
46
46
|
|
|
47
|
-
Scenario: A model that uses the
|
|
47
|
+
Scenario: A model that uses the arrayProperty with the choice validator should fail validation when one value is outside the choices
|
|
48
48
|
Given ArrayModel4 model is used
|
|
49
49
|
When ArrayModelData6 data is inserted
|
|
50
|
-
Then the
|
|
50
|
+
Then the getArrayProperty property is called on the model
|
|
51
51
|
Then functions.validate is called
|
|
52
52
|
Then an array of 1 errors is shown
|
package/features/model.feature
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
const assert = require('chai').assert
|
|
2
2
|
const flatMap = require('lodash/flatMap')
|
|
3
3
|
const { Given, When, Then } = require('@cucumber/cucumber')
|
|
4
|
-
const {
|
|
4
|
+
const { Model, Property, ArrayProperty, validation } = require('../../index')
|
|
5
5
|
|
|
6
6
|
const MODEL_DEFINITIONS = {
|
|
7
|
-
TestModel1:
|
|
8
|
-
name:
|
|
9
|
-
type:
|
|
10
|
-
flag:
|
|
7
|
+
TestModel1: Model('TestModel1', {
|
|
8
|
+
name: Property({ required: true }),
|
|
9
|
+
type: Property({ required: true, isString: true }),
|
|
10
|
+
flag: Property({ required: true, isNumber: true }),
|
|
11
11
|
}),
|
|
12
|
-
ArrayModel1:
|
|
13
|
-
|
|
12
|
+
ArrayModel1: Model('ArrayModel1', {
|
|
13
|
+
ArrayProperty: Property({
|
|
14
14
|
isArray: true,
|
|
15
15
|
validators: [validation.arrayType(validation.TYPE_PRIMATIVES.integer)],
|
|
16
16
|
}),
|
|
17
17
|
}),
|
|
18
|
-
ArrayModel2:
|
|
19
|
-
|
|
18
|
+
ArrayModel2: Model('ArrayModel2', {
|
|
19
|
+
ArrayProperty: Property({ isArray: true }),
|
|
20
20
|
}),
|
|
21
|
-
ArrayModel3:
|
|
22
|
-
|
|
21
|
+
ArrayModel3: Model('ArrayModel3', {
|
|
22
|
+
ArrayProperty: ArrayProperty({}),
|
|
23
23
|
}),
|
|
24
|
-
ArrayModel4:
|
|
25
|
-
|
|
24
|
+
ArrayModel4: Model('ArrayModel4', {
|
|
25
|
+
ArrayProperty: ArrayProperty({
|
|
26
26
|
choices: [4, 5, 6],
|
|
27
27
|
validators: [validation.arrayType(validation.TYPE_PRIMATIVES.integer)],
|
|
28
28
|
}),
|
|
@@ -41,22 +41,22 @@ const MODEL_INPUT_VALUES = {
|
|
|
41
41
|
flag: 1,
|
|
42
42
|
},
|
|
43
43
|
ArrayModelData1: {
|
|
44
|
-
|
|
44
|
+
ArrayProperty: [1, 2, 3, 4, 5],
|
|
45
45
|
},
|
|
46
46
|
ArrayModelData2: {
|
|
47
|
-
|
|
47
|
+
ArrayProperty: 'a-string',
|
|
48
48
|
},
|
|
49
49
|
ArrayModelData3: {
|
|
50
|
-
|
|
50
|
+
ArrayProperty: ['a-string', 'a-string2'],
|
|
51
51
|
},
|
|
52
52
|
ArrayModelData4: {
|
|
53
|
-
|
|
53
|
+
ArrayProperty: ['a-string', 1, {}, true],
|
|
54
54
|
},
|
|
55
55
|
ArrayModelData5: {
|
|
56
|
-
|
|
56
|
+
ArrayProperty: [4, 5, 5, 5, 6],
|
|
57
57
|
},
|
|
58
58
|
ArrayModelData6: {
|
|
59
|
-
|
|
59
|
+
ArrayProperty: [4, 5, 5, 5, 6, 1],
|
|
60
60
|
},
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -75,7 +75,7 @@ Given(
|
|
|
75
75
|
if (!input) {
|
|
76
76
|
throw new Error(`${modelInputValues} did not result in an input`)
|
|
77
77
|
}
|
|
78
|
-
this.instance = def(input)
|
|
78
|
+
this.instance = def.create(input)
|
|
79
79
|
}
|
|
80
80
|
)
|
|
81
81
|
|
|
@@ -106,13 +106,13 @@ When('{word} data is inserted', function (modelInputValues) {
|
|
|
106
106
|
if (!input) {
|
|
107
107
|
throw new Error(`${modelInputValues} did not result in an input`)
|
|
108
108
|
}
|
|
109
|
-
this.instance = this.modelDefinition(input)
|
|
109
|
+
this.instance = this.modelDefinition.create(input)
|
|
110
110
|
})
|
|
111
111
|
|
|
112
|
-
Then('{word} expected
|
|
113
|
-
const propertyArray = EXPECTED_FIELDS[
|
|
112
|
+
Then('{word} expected property is found', function (properties) {
|
|
113
|
+
const propertyArray = EXPECTED_FIELDS[properties]
|
|
114
114
|
if (!propertyArray) {
|
|
115
|
-
throw new Error(`${
|
|
115
|
+
throw new Error(`${properties} did not result in properties`)
|
|
116
116
|
}
|
|
117
117
|
propertyArray.forEach(key => {
|
|
118
118
|
if (!(key in this.instance)) {
|
|
@@ -121,8 +121,8 @@ Then('{word} expected fields are found', function (fields) {
|
|
|
121
121
|
})
|
|
122
122
|
})
|
|
123
123
|
|
|
124
|
-
Then('the {word}
|
|
125
|
-
return this.instance[
|
|
124
|
+
Then('the {word} property is called on the model', function (property) {
|
|
125
|
+
return this.instance[property]().then(result => {
|
|
126
126
|
this.results = result
|
|
127
127
|
})
|
|
128
128
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functional-models",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "A library for creating JavaScript function based models.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"nyc": {
|
|
30
30
|
"all": true,
|
|
31
31
|
"exclude": [
|
|
32
|
+
"coverage/",
|
|
32
33
|
"features/stepDefinitions/*",
|
|
33
34
|
"test/*"
|
|
34
35
|
]
|
package/src/index.js
CHANGED
package/src/models.js
CHANGED
|
@@ -1,55 +1,104 @@
|
|
|
1
1
|
const merge = require('lodash/merge')
|
|
2
|
-
const
|
|
3
|
-
const { toJson } = require('./serialization')
|
|
2
|
+
const { toObj } = require('./serialization')
|
|
4
3
|
const { createPropertyTitle } = require('./utils')
|
|
5
4
|
const { createModelValidator } = require('./validation')
|
|
6
5
|
|
|
7
|
-
const
|
|
8
|
-
|
|
6
|
+
const MODEL_DEF_KEYS = ['meta', 'functions']
|
|
9
7
|
const PROTECTED_KEYS = ['model']
|
|
10
8
|
|
|
11
|
-
const
|
|
9
|
+
const Model = (
|
|
10
|
+
modelName,
|
|
11
|
+
keyToProperty,
|
|
12
|
+
modelExtensions = {},
|
|
13
|
+
{ instanceCreatedCallback = null } = {}
|
|
14
|
+
) => {
|
|
15
|
+
/*
|
|
16
|
+
* This non-functional approach is specifically used to
|
|
17
|
+
* allow instances to be able to refer back to its parent without
|
|
18
|
+
* having to duplicate it for every instance.
|
|
19
|
+
* This is set at the very end and returned, so it can be referenced
|
|
20
|
+
* throughout instance methods.
|
|
21
|
+
*/
|
|
22
|
+
// eslint-disable-next-line functional/no-let
|
|
23
|
+
let model = null
|
|
12
24
|
PROTECTED_KEYS.forEach(key => {
|
|
13
|
-
if (key in
|
|
25
|
+
if (key in keyToProperty) {
|
|
14
26
|
throw new Error(`Cannot use ${key}. This is a protected value.`)
|
|
15
27
|
}
|
|
16
28
|
})
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
const instanceProperties = Object.entries(keyToProperty).filter(
|
|
30
|
+
([key, _]) => MODEL_DEF_KEYS.includes(key) === false
|
|
31
|
+
)
|
|
32
|
+
const specialProperties1 = Object.entries(keyToProperty).filter(([key, _]) =>
|
|
33
|
+
MODEL_DEF_KEYS.includes(key)
|
|
34
|
+
)
|
|
35
|
+
const properties = instanceProperties.reduce((acc, [key, property]) => {
|
|
36
|
+
return merge(acc, { [key]: property })
|
|
20
37
|
}, {})
|
|
21
|
-
const
|
|
22
|
-
([key,
|
|
38
|
+
const specialProperties = specialProperties1.reduce(
|
|
39
|
+
(acc, [key, property]) => {
|
|
40
|
+
return merge(acc, { [key]: property })
|
|
41
|
+
},
|
|
42
|
+
{}
|
|
23
43
|
)
|
|
24
44
|
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const getFieldKey = createPropertyTitle(key)
|
|
30
|
-
const fleshedOutField = {
|
|
31
|
-
[getFieldKey]: fieldGetter,
|
|
32
|
-
functions: {
|
|
33
|
-
validate: {
|
|
34
|
-
[key]: fieldValidator,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
45
|
+
const create = (instanceValues = {}) => {
|
|
46
|
+
const specialInstanceProperties1 = MODEL_DEF_KEYS.reduce((acc, key) => {
|
|
47
|
+
if (key in instanceValues) {
|
|
48
|
+
return { ...acc, [key]: instanceValues[key] }
|
|
37
49
|
}
|
|
38
|
-
return
|
|
50
|
+
return acc
|
|
39
51
|
}, {})
|
|
40
|
-
const
|
|
41
|
-
|
|
52
|
+
const loadedInternals = instanceProperties.reduce(
|
|
53
|
+
(acc, [key, property]) => {
|
|
54
|
+
const propertyGetter = property.createGetter(instanceValues[key])
|
|
55
|
+
const propertyValidator = property.getValidator(propertyGetter)
|
|
56
|
+
const getPropertyKey = createPropertyTitle(key)
|
|
57
|
+
const fleshedOutInstanceProperties = {
|
|
58
|
+
[getPropertyKey]: propertyGetter,
|
|
59
|
+
functions: {
|
|
60
|
+
validate: {
|
|
61
|
+
[key]: propertyValidator,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
}
|
|
65
|
+
return merge(acc, fleshedOutInstanceProperties)
|
|
66
|
+
},
|
|
67
|
+
{}
|
|
68
|
+
)
|
|
69
|
+
const frameworkProperties = {
|
|
70
|
+
meta: {
|
|
71
|
+
getModel: () => model,
|
|
72
|
+
},
|
|
42
73
|
functions: {
|
|
43
|
-
|
|
74
|
+
toObj: toObj(loadedInternals),
|
|
44
75
|
validate: {
|
|
45
76
|
model: createModelValidator(loadedInternals),
|
|
46
77
|
},
|
|
47
78
|
},
|
|
48
79
|
}
|
|
49
|
-
|
|
80
|
+
const instance = merge(
|
|
81
|
+
{},
|
|
82
|
+
loadedInternals,
|
|
83
|
+
specialProperties,
|
|
84
|
+
frameworkProperties,
|
|
85
|
+
specialInstanceProperties1
|
|
86
|
+
)
|
|
87
|
+
if (instanceCreatedCallback) {
|
|
88
|
+
instanceCreatedCallback(instance)
|
|
89
|
+
}
|
|
90
|
+
return instance
|
|
50
91
|
}
|
|
92
|
+
|
|
93
|
+
// This sets the model that is used by the instances later.
|
|
94
|
+
model = merge({}, modelExtensions, {
|
|
95
|
+
create,
|
|
96
|
+
getName: () => modelName,
|
|
97
|
+
getProperties: () => properties,
|
|
98
|
+
})
|
|
99
|
+
return model
|
|
51
100
|
}
|
|
52
101
|
|
|
53
102
|
module.exports = {
|
|
54
|
-
|
|
103
|
+
Model,
|
|
55
104
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
const identity = require('lodash/identity')
|
|
2
|
+
const isFunction = require('lodash/isFunction')
|
|
2
3
|
const merge = require('lodash/merge')
|
|
3
4
|
const {
|
|
4
|
-
|
|
5
|
+
createPropertyValidator,
|
|
5
6
|
emptyValidator,
|
|
6
7
|
maxTextLength,
|
|
7
8
|
minTextLength,
|
|
8
9
|
minNumber,
|
|
9
10
|
maxNumber,
|
|
10
11
|
isType,
|
|
12
|
+
referenceTypeMatch,
|
|
11
13
|
meetsRegex,
|
|
12
14
|
} = require('./validation')
|
|
13
15
|
const { createUuid } = require('./utils')
|
|
@@ -23,7 +25,7 @@ const _getValidatorFromConfigElseEmpty = (config, key, validatorGetter) => {
|
|
|
23
25
|
return emptyValidator
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
const
|
|
28
|
+
const Property = (config = {}, additionalMetadata = {}) => {
|
|
27
29
|
const value = config.value || undefined
|
|
28
30
|
const defaultValue = config.defaultValue || undefined
|
|
29
31
|
const lazyLoadMethod = config.lazyLoadMethod || false
|
|
@@ -33,6 +35,7 @@ const field = (config = {}) => {
|
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
return {
|
|
38
|
+
...additionalMetadata,
|
|
36
39
|
createGetter: instanceValue => {
|
|
37
40
|
if (value !== undefined) {
|
|
38
41
|
return () => value
|
|
@@ -53,15 +56,17 @@ const field = (config = {}) => {
|
|
|
53
56
|
}
|
|
54
57
|
},
|
|
55
58
|
getValidator: valueGetter => {
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const validator = createPropertyValidator(config)
|
|
60
|
+
const _propertyValidatorWrapper = async () => {
|
|
61
|
+
return validator(await valueGetter())
|
|
58
62
|
}
|
|
63
|
+
return _propertyValidatorWrapper
|
|
59
64
|
},
|
|
60
65
|
}
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
const
|
|
64
|
-
|
|
68
|
+
const UniqueId = (config = {}) =>
|
|
69
|
+
Property({
|
|
65
70
|
...config,
|
|
66
71
|
lazyLoadMethod: value => {
|
|
67
72
|
if (!value) {
|
|
@@ -71,8 +76,8 @@ const uniqueId = config =>
|
|
|
71
76
|
},
|
|
72
77
|
})
|
|
73
78
|
|
|
74
|
-
const
|
|
75
|
-
|
|
79
|
+
const DateProperty = (config = {}) =>
|
|
80
|
+
Property({
|
|
76
81
|
...config,
|
|
77
82
|
lazyLoadMethod: value => {
|
|
78
83
|
if (!value && config.autoNow) {
|
|
@@ -82,58 +87,85 @@ const dateField = config =>
|
|
|
82
87
|
},
|
|
83
88
|
})
|
|
84
89
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
...(objToUse.functions ? objToUse.functions : {}),
|
|
104
|
-
toJson: _getId,
|
|
105
|
-
},
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
const valueIsSmartObj = smartObj && smartObj.functions
|
|
109
|
-
if (valueIsSmartObj) {
|
|
110
|
-
return _getSmartObjReturn(smartObj)
|
|
111
|
-
}
|
|
112
|
-
if (config.fetcher) {
|
|
113
|
-
const obj = await config.fetcher(smartObj)
|
|
114
|
-
return _getSmartObjReturn(obj)
|
|
90
|
+
const ReferenceProperty = (model, config = {}) => {
|
|
91
|
+
if (!model) {
|
|
92
|
+
throw new Error('Must include the referenced model')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const _getModel = () => {
|
|
96
|
+
if (isFunction(model)) {
|
|
97
|
+
return model()
|
|
98
|
+
}
|
|
99
|
+
return model
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const validators = [...(config.validators || []), referenceTypeMatch(model)]
|
|
103
|
+
|
|
104
|
+
const lazyLoadMethod = async instanceValues => {
|
|
105
|
+
const _getId = () => {
|
|
106
|
+
if (!instanceValues) {
|
|
107
|
+
return null
|
|
115
108
|
}
|
|
116
|
-
return
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
return instanceValues && instanceValues.id
|
|
110
|
+
? instanceValues.id
|
|
111
|
+
: instanceValues.getId
|
|
112
|
+
? instanceValues.getId()
|
|
113
|
+
: instanceValues
|
|
114
|
+
}
|
|
115
|
+
const valueIsModelInstance =
|
|
116
|
+
Boolean(instanceValues) && Boolean(instanceValues.functions)
|
|
117
|
+
|
|
118
|
+
const _getInstanceReturn = objToUse => {
|
|
119
|
+
const instance = valueIsModelInstance
|
|
120
|
+
? objToUse
|
|
121
|
+
: _getModel().create(objToUse)
|
|
122
|
+
return merge({}, instance, {
|
|
123
|
+
functions: {
|
|
124
|
+
toObj: _getId,
|
|
125
|
+
},
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (valueIsModelInstance) {
|
|
130
|
+
return _getInstanceReturn(instanceValues)
|
|
131
|
+
}
|
|
132
|
+
if (config.fetcher) {
|
|
133
|
+
const id = await _getId()
|
|
134
|
+
const obj = await config.fetcher(_getModel(), id)
|
|
135
|
+
return _getInstanceReturn(obj)
|
|
136
|
+
}
|
|
137
|
+
return _getId(instanceValues)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return Property(
|
|
141
|
+
merge({}, config, {
|
|
142
|
+
validators,
|
|
143
|
+
lazyLoadMethod,
|
|
144
|
+
}),
|
|
145
|
+
{
|
|
146
|
+
meta: {
|
|
147
|
+
getReferencedModel: _getModel,
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
)
|
|
119
151
|
}
|
|
120
152
|
|
|
121
|
-
const
|
|
122
|
-
|
|
153
|
+
const ArrayProperty = (config = {}) =>
|
|
154
|
+
Property({
|
|
123
155
|
defaultValue: [],
|
|
124
156
|
...config,
|
|
125
157
|
isArray: true,
|
|
126
158
|
})
|
|
127
159
|
|
|
128
|
-
const
|
|
129
|
-
|
|
160
|
+
const ObjectProperty = (config = {}) =>
|
|
161
|
+
Property(
|
|
130
162
|
merge(config, {
|
|
131
163
|
validators: [isType('object')],
|
|
132
164
|
})
|
|
133
165
|
)
|
|
134
166
|
|
|
135
|
-
const
|
|
136
|
-
|
|
167
|
+
const TextProperty = (config = {}) =>
|
|
168
|
+
Property(
|
|
137
169
|
merge(config, {
|
|
138
170
|
isString: true,
|
|
139
171
|
validators: [
|
|
@@ -147,8 +179,8 @@ const textField = (config = {}) =>
|
|
|
147
179
|
})
|
|
148
180
|
)
|
|
149
181
|
|
|
150
|
-
const
|
|
151
|
-
|
|
182
|
+
const IntegerProperty = (config = {}) =>
|
|
183
|
+
Property(
|
|
152
184
|
merge(config, {
|
|
153
185
|
isInteger: true,
|
|
154
186
|
validators: [
|
|
@@ -162,8 +194,8 @@ const integerField = (config = {}) =>
|
|
|
162
194
|
})
|
|
163
195
|
)
|
|
164
196
|
|
|
165
|
-
const
|
|
166
|
-
|
|
197
|
+
const NumberProperty = (config = {}) =>
|
|
198
|
+
Property(
|
|
167
199
|
merge(config, {
|
|
168
200
|
isNumber: true,
|
|
169
201
|
validators: [
|
|
@@ -177,30 +209,30 @@ const numberField = (config = {}) =>
|
|
|
177
209
|
})
|
|
178
210
|
)
|
|
179
211
|
|
|
180
|
-
const
|
|
181
|
-
|
|
212
|
+
const ConstantValueProperty = (value, config = {}) =>
|
|
213
|
+
TextProperty(
|
|
182
214
|
merge(config, {
|
|
183
215
|
value,
|
|
184
216
|
})
|
|
185
217
|
)
|
|
186
218
|
|
|
187
|
-
const
|
|
188
|
-
|
|
219
|
+
const EmailProperty = (config = {}) =>
|
|
220
|
+
TextProperty(
|
|
189
221
|
merge(config, {
|
|
190
222
|
validators: [meetsRegex(EMAIL_REGEX)],
|
|
191
223
|
})
|
|
192
224
|
)
|
|
193
225
|
|
|
194
226
|
module.exports = {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
227
|
+
Property,
|
|
228
|
+
UniqueId,
|
|
229
|
+
DateProperty,
|
|
230
|
+
ArrayProperty,
|
|
231
|
+
ReferenceProperty,
|
|
232
|
+
IntegerProperty,
|
|
233
|
+
TextProperty,
|
|
234
|
+
ConstantValueProperty,
|
|
235
|
+
NumberProperty,
|
|
236
|
+
ObjectProperty,
|
|
237
|
+
EmailProperty,
|
|
206
238
|
}
|