-
Notifications
You must be signed in to change notification settings - Fork 1
/
keep-models-fresh.js
102 lines (86 loc) · 2.38 KB
/
keep-models-fresh.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
100
101
102
const crypto = require('crypto')
const co = require('co').wrap
const buildResource = require('@tradle/build-resource')
const baseModels = require('./base-models')
const { isPromise, stableStringify, shallowClone } = require('./utils')
const defaultPropertyName = 'modelsHash'
function defaultGetIdentifier (req) {
return req.user.id
}
function hashObject (obj) {
return hashString('sha256', stableStringify(obj))
}
function hashString (algorithm, data) {
return crypto.createHash(algorithm).update(data).digest('hex')
}
function modelsToArray (models) {
return Object.keys(models)
.sort(compareAlphabetical)
.map(id => models[id])
}
function compareAlphabetical (a, b) {
return a < b ? -1 : a > b ? 1 : 0
}
module.exports = function keepModelsFresh ({
getModelsForUser,
propertyName=defaultPropertyName,
// unique identifier for counterparty
// which will be used to track freshness.
// defaults to user.id
getIdentifier=defaultGetIdentifier,
send
}) {
// modelsObject => modelsArray
// modelsArray => modelsHash
const objToArray = new Map()
const arrToHash = new Map()
return co(function* (req) {
const identifier = getIdentifier(req)
const { user } = req
if (!user[propertyName] || typeof user[propertyName] !== 'object') {
user[propertyName] = {}
}
const modelsHash = user[propertyName][identifier]
let models = getModelsForUser(user)
if (isPromise(models)) {
models = yield models
}
let modelsArray
if (Array.isArray(models)) {
modelsArray = models
} else {
modelsArray = objToArray.get(models)
if (!modelsArray) {
modelsArray = modelsToArray(models)
objToArray.set(models, modelsArray)
}
}
let hash = arrToHash.get(modelsArray)
if (!hash) {
hash = hashObject(modelsArray)
arrToHash.set(modelsArray, hash)
}
if (hash === modelsHash) return
user[propertyName][identifier] = hash
const pack = buildResource({
models: baseModels,
model: 'tradle.ModelsPack',
resource: {
models: modelsArray
}
})
.toJSON()
yield send({ req, object: pack })
})
}
function toModelsMap (models) {
if (!Array.isArray(models)) return models
const obj = {}
for (const model of models) {
obj[model.id] = model
}
return obj
}
function byteLength (obj) {
return Buffer.byteLength(JSON.stringify(obj))
}