-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmerge.js
99 lines (82 loc) · 2.3 KB
/
merge.js
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
const extend = require('lodash/extend')
const clone = require('lodash/clone')
const omit = require('lodash/omit')
const cloneDeep = require('lodash/cloneDeep')
const { TYPE } = require('@tradle/constants')
const validate = require('@tradle/validate-model')
const LENS_TYPE = 'tradle.Lens'
const override = (fromModel, fromLens) => fromLens.slice()
const mergeUniq = (fromModel=[], fromLens=[]) => uniqStrings(fromModel.concat(fromLens))
const groupCombiner = {
required: mergeUniq,
hidden: mergeUniq,
viewCols: override,
gridCols: override,
editCols: override
}
const FORBIDDEN_METADATA = [
'type',
'range',
'ref',
'inlined'
]
module.exports = {
merge,
mergeProperties,
mergeProperty
}
function merge ({ models, model, lens }) {
if (!model) {
model = models[lens.model]
}
if (!model) {
throw new Error(`model not found: ${lens.model}`)
}
expect('lens.model', model.id, lens.model)
expect(TYPE, LENS_TYPE, lens[TYPE])
const merged = omit(model, ['properties'])
if (lens.properties) {
merged.properties = mergeProperties({ model, lens })
} else {
merged.properties = cloneDeep(model.properties)
}
for (let group in groupCombiner) {
if (!lens[group]) continue
const combine = groupCombiner[group]
merged[group] = combine(model[group], lens[group])
}
validate.model(merged)
validate.refs({ models, model: merged })
return merged
}
function mergeProperties ({ model, lens }) {
const properties = {}
for (let propertyName in model.properties) {
properties[propertyName] = mergeProperty({ model, lens, propertyName })
}
return properties
}
function mergeProperty ({ model, lens, propertyName }) {
const prop = model.properties[propertyName]
const propLens = lens.properties[propertyName]
if (propLens) {
const forbidden = FORBIDDEN_METADATA.filter(mProp => propLens[mProp])
if (forbidden.length) {
throw new Error(`lens cannot override property metadata: ${forbidden.join(', ')}`)
}
return extend({}, prop, propLens)
}
return clone(prop)
}
function uniqStrings (arr) {
const map = {}
for (const item of arr) {
map[item] = true
}
return Object.keys(map)
}
function expect (name, expected, actual) {
if (expected !== actual) {
throw new Error(`expected ${name} to be: ${expected}, got: ${actual}`)
}
}