@jsreport/jsreport-core 3.0.1 → 3.1.2-test.2
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/LICENSE +166 -166
- package/README.md +298 -284
- package/index.js +29 -27
- package/lib/main/blobStorage/blobStorage.js +52 -47
- package/lib/main/blobStorage/inMemoryProvider.js +27 -27
- package/lib/main/blobStorage/mainActions.js +24 -24
- package/lib/main/createDefaultLoggerFormat.js +17 -17
- package/lib/main/defaults.js +14 -14
- package/lib/main/extensions/discover.js +20 -20
- package/lib/main/extensions/extensionsManager.js +264 -265
- package/lib/main/extensions/fileUtils.js +56 -55
- package/lib/main/extensions/findVersion.js +49 -53
- package/lib/main/extensions/locationCache.js +103 -97
- package/lib/main/extensions/sorter.js +10 -10
- package/lib/main/extensions/validateMinimalVersion.js +50 -50
- package/lib/main/folders/cascadeFolderRemove.js +25 -25
- package/lib/main/folders/getEntitiesInFolder.js +53 -53
- package/lib/main/folders/index.js +42 -42
- package/lib/main/folders/moveBetweenFolders.js +354 -354
- package/lib/main/folders/validateDuplicatedName.js +107 -107
- package/lib/main/folders/validateReservedName.js +53 -53
- package/lib/main/logger.js +244 -244
- package/lib/main/migration/resourcesToAssets.js +230 -210
- package/lib/main/migration/xlsxTemplatesToAssets.js +128 -118
- package/lib/main/monitoring.js +91 -91
- package/lib/main/optionsLoad.js +237 -237
- package/lib/main/optionsSchema.js +237 -237
- package/lib/main/reporter.js +579 -578
- package/lib/main/schemaValidator.js +252 -252
- package/lib/main/settings.js +154 -154
- package/lib/main/store/checkDuplicatedId.js +27 -27
- package/lib/main/store/collection.js +329 -329
- package/lib/main/store/documentStore.js +469 -469
- package/lib/main/store/mainActions.js +28 -28
- package/lib/main/store/memoryStoreProvider.js +99 -99
- package/lib/main/store/queue.js +48 -48
- package/lib/main/store/referenceUtils.js +251 -251
- package/lib/main/store/setupValidateId.js +43 -43
- package/lib/main/store/setupValidateShortid.js +71 -71
- package/lib/main/store/transaction.js +69 -69
- package/lib/main/store/typeUtils.js +180 -180
- package/lib/main/templates.js +34 -34
- package/lib/main/validateEntityName.js +62 -62
- package/lib/shared/createError.js +36 -36
- package/lib/shared/encryption.js +114 -114
- package/lib/shared/folders/index.js +11 -11
- package/lib/shared/folders/normalizeEntityPath.js +15 -15
- package/lib/shared/folders/resolveEntityFromPath.js +88 -88
- package/lib/shared/folders/resolveEntityPath.js +46 -46
- package/lib/shared/folders/resolveFolderFromPath.js +38 -38
- package/lib/shared/generateRequestId.js +4 -4
- package/lib/shared/listenerCollection.js +169 -0
- package/lib/shared/normalizeMetaFromLogs.js +30 -30
- package/lib/shared/reporter.js +123 -123
- package/lib/shared/request.js +64 -64
- package/lib/shared/tempFilesHandler.js +81 -81
- package/lib/shared/templates.js +82 -82
- package/lib/static/helpers.js +33 -33
- package/lib/worker/blobStorage.js +34 -34
- package/lib/worker/defaultProxyExtend.js +46 -46
- package/lib/worker/documentStore.js +49 -49
- package/lib/worker/extensionsManager.js +17 -17
- package/lib/worker/logger.js +48 -48
- package/lib/worker/render/diff.js +138 -138
- package/lib/worker/render/executeEngine.js +207 -200
- package/lib/worker/render/htmlRecipe.js +10 -10
- package/lib/worker/render/moduleHelper.js +43 -43
- package/lib/worker/render/noneEngine.js +12 -12
- package/lib/worker/render/profiler.js +158 -158
- package/lib/worker/render/render.js +205 -209
- package/lib/worker/render/resolveReferences.js +60 -60
- package/lib/worker/reporter.js +191 -187
- package/lib/worker/sandbox/runInSandbox.js +13 -4
- package/lib/worker/sandbox/safeSandbox.js +828 -822
- package/lib/worker/templates.js +78 -78
- package/lib/worker/workerHandler.js +54 -54
- package/package.json +92 -92
- package/test/blobStorage/common.js +21 -21
- package/test/store/common.js +1449 -1449
|
@@ -1,469 +1,469 @@
|
|
|
1
|
-
const events = require('events')
|
|
2
|
-
const { nanoid } = require('nanoid')
|
|
3
|
-
const { v4: uuidv4 } = require('uuid')
|
|
4
|
-
const
|
|
5
|
-
const { resolvePropDefinition, typeDefToJSONSchema } = require('./typeUtils')
|
|
6
|
-
const { findReferencePropertiesInType, findLinkedEntitiesForReferenceValue, existsReferenceValue, updateReferenceValue } = require('./referenceUtils')
|
|
7
|
-
const collection = require('./collection')
|
|
8
|
-
const checkDuplicatedId = require('./checkDuplicatedId')
|
|
9
|
-
|
|
10
|
-
// factory function
|
|
11
|
-
const DocumentStore = (options, validator, encryption) => {
|
|
12
|
-
const entitySchemasToGenerate = []
|
|
13
|
-
const generateSchemaEntityTypeConfig = {}
|
|
14
|
-
const defaultGenerateSchemaForEntityType = true
|
|
15
|
-
|
|
16
|
-
// internal sets are not listed in normal documentStore.model.entitySets, or available in
|
|
17
|
-
// documentStore.collection(), instead they are available in documentStore.internalCollection()
|
|
18
|
-
// and entity set definitions of these internals are only available in store provider implementations.
|
|
19
|
-
//
|
|
20
|
-
// this allows having collections that are available for specific uses cases (playground, jsreportonline)
|
|
21
|
-
// which needs to save/load data using jsreport store abstraction but they don't need to be visible
|
|
22
|
-
// from extensions. (to avoid adding permissions, attributes or other logic that modifies these internal entities from extensions)
|
|
23
|
-
const internalEntitySets = {}
|
|
24
|
-
const transactions = new Map()
|
|
25
|
-
const fileExtensionResolvers = []
|
|
26
|
-
|
|
27
|
-
transactions.getActiveTransaction = function (req) {
|
|
28
|
-
if (req && req.context && req.context.storeTransaction) {
|
|
29
|
-
if (!transactions.has(req.context.storeTransaction)) {
|
|
30
|
-
throw new Error('transaction does not exists or is no longer active, make sure you are not closing the transaction previously using store.commitTransaction or store.rollbackTransaction')
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return transactions.get(req.context.storeTransaction)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
let initialized = false
|
|
38
|
-
|
|
39
|
-
const store = {
|
|
40
|
-
options,
|
|
41
|
-
model: {
|
|
42
|
-
namespace: 'jsreport',
|
|
43
|
-
complexTypes: {},
|
|
44
|
-
entitySets: {}
|
|
45
|
-
},
|
|
46
|
-
internalAfterInitListeners:
|
|
47
|
-
emitter: new events.EventEmitter(),
|
|
48
|
-
|
|
49
|
-
registerProvider (provider) {
|
|
50
|
-
this.provider = provider
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
async init () {
|
|
54
|
-
initialized = true
|
|
55
|
-
|
|
56
|
-
if (!this.provider && this.options.store.provider === 'memory') {
|
|
57
|
-
this.provider = require('./memoryStoreProvider')()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (!this.provider) {
|
|
61
|
-
throw new Error(`The document store provider ${this.options.store.provider} was not registered.`)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this.emit('before-init', this)
|
|
65
|
-
|
|
66
|
-
this.collections = {}
|
|
67
|
-
this.internalCollections = {}
|
|
68
|
-
|
|
69
|
-
const entitySetsLinkedReferenceProperties = {}
|
|
70
|
-
|
|
71
|
-
Object.entries(this.model.entitySets).forEach((e) => {
|
|
72
|
-
const eName = e[0]
|
|
73
|
-
const es = e[1]
|
|
74
|
-
es.normalizedEntityTypeName = es.entityType.replace(this.model.namespace + '.', '')
|
|
75
|
-
es.entityTypeDef = this.model.entityTypes[es.normalizedEntityTypeName]
|
|
76
|
-
const entityType = es.entityTypeDef
|
|
77
|
-
|
|
78
|
-
if (!entityType._id) {
|
|
79
|
-
entityType._id = { type: 'Edm.String' }
|
|
80
|
-
|
|
81
|
-
if (!entityTypeHasKey(entityType)) {
|
|
82
|
-
// add key: true only if there is no other field defined already
|
|
83
|
-
// key: true is used for ODATA XML generation, it includes new information to the final XML
|
|
84
|
-
entityType._id.key = true
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (!entityType.creationDate) {
|
|
89
|
-
entityType.creationDate = { type: 'Edm.DateTimeOffset' }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (!entityType.modificationDate) {
|
|
93
|
-
entityType.modificationDate = { type: 'Edm.DateTimeOffset' }
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!entityType.shortid) {
|
|
97
|
-
entityType.shortid = { type: 'Edm.String' }
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const referenceProperties = findReferencePropertiesInType(this.model, entityType)
|
|
101
|
-
|
|
102
|
-
referenceProperties.forEach((property) => {
|
|
103
|
-
const entitySetName = property.referenceTo
|
|
104
|
-
entitySetsLinkedReferenceProperties[entitySetName] = entitySetsLinkedReferenceProperties[entitySetName] || []
|
|
105
|
-
|
|
106
|
-
entitySetsLinkedReferenceProperties[entitySetName].push({
|
|
107
|
-
name: property.name,
|
|
108
|
-
entitySet: eName
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
es.referenceProperties = referenceProperties
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
Object.keys(this.model.entitySets).forEach((eName) => {
|
|
116
|
-
const es = this.model.entitySets[eName]
|
|
117
|
-
|
|
118
|
-
if (entitySetsLinkedReferenceProperties[eName]) {
|
|
119
|
-
es.linkedReferenceProperties = entitySetsLinkedReferenceProperties[eName]
|
|
120
|
-
} else {
|
|
121
|
-
es.linkedReferenceProperties = []
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
const col = collection(eName, this.provider, this.model, validator, encryption, transactions)
|
|
125
|
-
|
|
126
|
-
const addDefaultFields = (doc) => {
|
|
127
|
-
doc.creationDate = new Date()
|
|
128
|
-
doc.modificationDate = new Date()
|
|
129
|
-
doc.shortid = doc.shortid || nanoid(7)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
col.beforeInsertListeners.add('core-default-fields', (doc, req) => {
|
|
133
|
-
addDefaultFields(doc)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
col.beforeUpdateListeners.add('core-default-fields', (q, u, o, req) => {
|
|
137
|
-
if (u.$set && o && o.upsert === true) {
|
|
138
|
-
addDefaultFields(u.$set)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (req && req.context.skipModificationDateUpdate === true) {
|
|
142
|
-
return
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
if (u.$set) {
|
|
146
|
-
u.$set.modificationDate = new Date()
|
|
147
|
-
}
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
this.collections[eName] = col
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
Object.keys(internalEntitySets).forEach((e) => (this.internalCollections[e] = collection(e, this.provider, this.model, undefined, undefined, transactions)))
|
|
154
|
-
|
|
155
|
-
if (this.provider.load) {
|
|
156
|
-
// we combine internal and public entity sets in order for the store provider
|
|
157
|
-
// be able to recognize both set of entities and be able to work with them
|
|
158
|
-
const modelToLoad = Object.assign({}, this.model)
|
|
159
|
-
|
|
160
|
-
modelToLoad.entitySets = Object.assign({}, modelToLoad.entitySets, internalEntitySets)
|
|
161
|
-
|
|
162
|
-
await this.provider.load(modelToLoad)
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
entitySchemasToGenerate.forEach((entityType) => {
|
|
166
|
-
const schema = typeDefToJSONSchema(this.model, this.model.entityTypes[entityType])
|
|
167
|
-
|
|
168
|
-
if (schema == null) {
|
|
169
|
-
return
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (initialized && validator.getSchema(entityType) != null) {
|
|
173
|
-
validator.addSchema(entityType, schema, true)
|
|
174
|
-
} else {
|
|
175
|
-
validator.addSchema(entityType, schema)
|
|
176
|
-
}
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
this.emit('after-init', this)
|
|
180
|
-
return this.internalAfterInitListeners.fire()
|
|
181
|
-
},
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Register type for odata.
|
|
185
|
-
* Example:
|
|
186
|
-
* documentStore.registerEntityType('UserType', {
|
|
187
|
-
* _id: {type: 'Edm.String', key: true}
|
|
188
|
-
* })
|
|
189
|
-
*
|
|
190
|
-
* @param {String} type
|
|
191
|
-
* @param {Object} def
|
|
192
|
-
*/
|
|
193
|
-
registerEntityType (type, def, generateJSONSchema = defaultGenerateSchemaForEntityType) {
|
|
194
|
-
generateSchemaEntityTypeConfig[type] = generateJSONSchema === true
|
|
195
|
-
this.model.entityTypes[type] = def
|
|
196
|
-
},
|
|
197
|
-
|
|
198
|
-
addFileExtensionResolver (fn) {
|
|
199
|
-
fileExtensionResolvers.push(fn)
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
resolveFileExtension (doc, entitySetName, propertyName) {
|
|
203
|
-
const model = this.model
|
|
204
|
-
const entitySets = { ...model.entitySets, ...internalEntitySets }
|
|
205
|
-
const es = entitySets[entitySetName]
|
|
206
|
-
|
|
207
|
-
if (es == null) {
|
|
208
|
-
throw new Error(`Entity set "${entitySetName}" does not exists`)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const entityTypeName = es.entityType
|
|
212
|
-
const entityType = es.entityTypeDef
|
|
213
|
-
const propTypeParts = propertyName.split('.')
|
|
214
|
-
const propTypePartsLastIndex = propTypeParts.length - 1
|
|
215
|
-
let propType = entityType
|
|
216
|
-
|
|
217
|
-
propTypeParts.forEach((propName, idx) => {
|
|
218
|
-
if (propType == null || propType[propName] == null) {
|
|
219
|
-
throw new Error(`Property "${propertyName}" does not exists in entity type "${entityTypeName}"`)
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
propType = propType[propName]
|
|
223
|
-
|
|
224
|
-
const resolveResult = this.resolvePropertyDefinition(propType)
|
|
225
|
-
|
|
226
|
-
if (!resolveResult) {
|
|
227
|
-
throw new Error(`Property "${propertyName}" does not have a valid type`)
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (resolveResult.def.type.startsWith('Collection(') && resolveResult.subType == null) {
|
|
231
|
-
if (propTypePartsLastIndex !== idx) {
|
|
232
|
-
propType = null
|
|
233
|
-
}
|
|
234
|
-
} else if (resolveResult.subType) {
|
|
235
|
-
propType = resolveResult.subType
|
|
236
|
-
}
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
if (!propType || propType.document == null) {
|
|
240
|
-
return
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
for (const resolver of fileExtensionResolvers) {
|
|
244
|
-
const extension = resolver(doc, entitySetName, entityType, propType)
|
|
245
|
-
|
|
246
|
-
if (extension) {
|
|
247
|
-
return extension
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return propType.document.extension
|
|
252
|
-
},
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Register complex type for odata.
|
|
256
|
-
* Example:
|
|
257
|
-
* documentStore.registerComplexType('DataItemRefType', {
|
|
258
|
-
* name: {type: 'Edm.String' }
|
|
259
|
-
* })
|
|
260
|
-
*
|
|
261
|
-
* @param {String} name
|
|
262
|
-
* @param {Object} def
|
|
263
|
-
*/
|
|
264
|
-
registerComplexType (name, def) {
|
|
265
|
-
this.model.complexTypes[name] = def
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Register complete entity set for odata. The first parameter is then use as a collection name
|
|
270
|
-
* Example:
|
|
271
|
-
* documentStore.registerEntitySet('users', {
|
|
272
|
-
* entityType: 'jsreport.UserType'
|
|
273
|
-
* })
|
|
274
|
-
*
|
|
275
|
-
* @param {String} name
|
|
276
|
-
* @param {Object} def
|
|
277
|
-
*/
|
|
278
|
-
registerEntitySet (name, def) {
|
|
279
|
-
const isInternal = def.internal === true
|
|
280
|
-
|
|
281
|
-
if (def.exportable == null || def.exportable === true) {
|
|
282
|
-
def.exportable = true
|
|
283
|
-
} else {
|
|
284
|
-
def.exportable = false
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
isInternal &&
|
|
289
|
-
this.model.entitySets[name] != null
|
|
290
|
-
) {
|
|
291
|
-
throw new Error(
|
|
292
|
-
`Entity set "${name}" can not be registered as internal entity because it was register as public entity previously`
|
|
293
|
-
)
|
|
294
|
-
} else if (
|
|
295
|
-
!isInternal &&
|
|
296
|
-
internalEntitySets[name] != null
|
|
297
|
-
) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
`Entity set "${name}" can not be registered as public entity because it was register as internal entity previously`
|
|
300
|
-
)
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
if (!isInternal) {
|
|
304
|
-
this.model.entitySets[name] = def
|
|
305
|
-
} else {
|
|
306
|
-
def.normalizedEntityTypeName = def.entityType.replace(this.model.namespace + '.', '')
|
|
307
|
-
def.entityTypeDef = this.model.entityTypes[def.normalizedEntityTypeName]
|
|
308
|
-
internalEntitySets[name] = def
|
|
309
|
-
}
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* Resolves the passed definition to contain subtype definition
|
|
314
|
-
* (useful when dealing with complex types or collection types)
|
|
315
|
-
* @param {Object} def
|
|
316
|
-
* @return {Object}
|
|
317
|
-
*/
|
|
318
|
-
resolvePropertyDefinition (def) {
|
|
319
|
-
return resolvePropDefinition(this.model, def)
|
|
320
|
-
},
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* Get the document Collection by the name provided in registerEntitySet
|
|
324
|
-
* @param {String} name
|
|
325
|
-
* @returns {Collection}
|
|
326
|
-
*/
|
|
327
|
-
collection (name) {
|
|
328
|
-
return this.collections[name]
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
/**
|
|
332
|
-
* Get the document internal Collection by the name provided in registerEntitySet
|
|
333
|
-
* @param {String} name
|
|
334
|
-
* @returns {Collection}
|
|
335
|
-
*/
|
|
336
|
-
internalCollection (name) {
|
|
337
|
-
return this.internalCollections[name]
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
findLinkedEntitiesForReference (entitiesByCollection, collectionReferenceTargetName, referenceValue) {
|
|
341
|
-
return findLinkedEntitiesForReferenceValue(this, entitiesByCollection, collectionReferenceTargetName, referenceValue)
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
existsReference (collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceValue) {
|
|
345
|
-
return existsReferenceValue(this, collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceValue)
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
updateReference (collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceOpts, newReferenceValue) {
|
|
349
|
-
return updateReferenceValue(this, collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceOpts, newReferenceValue)
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
checkDuplicatedId (collectionName, idValue, req) {
|
|
353
|
-
return checkDuplicatedId(this, collectionName, idValue, req)
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
async close () {
|
|
357
|
-
transactions.clear()
|
|
358
|
-
|
|
359
|
-
if (this.provider && this.provider.close) {
|
|
360
|
-
await this.provider.close()
|
|
361
|
-
}
|
|
362
|
-
},
|
|
363
|
-
|
|
364
|
-
/**
|
|
365
|
-
* Drop the whole document store
|
|
366
|
-
* @returns {Promise}
|
|
367
|
-
*/
|
|
368
|
-
async drop (req) {
|
|
369
|
-
return this.provider.drop({
|
|
370
|
-
transaction: transactions.getActiveTransaction(req)
|
|
371
|
-
})
|
|
372
|
-
},
|
|
373
|
-
|
|
374
|
-
async beginTransaction (req) {
|
|
375
|
-
if (req.context.storeTransaction && transactions.has(req.context.storeTransaction)) {
|
|
376
|
-
throw new Error('Can not call store.beginTransaction when an active transaction already exists, make sure you are not calling store.beginTransaction more than once')
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
const tran = await this.provider.beginTransaction()
|
|
380
|
-
|
|
381
|
-
const tranId = uuidv4()
|
|
382
|
-
|
|
383
|
-
transactions.set(tranId, tran)
|
|
384
|
-
|
|
385
|
-
req.context.storeTransaction = tranId
|
|
386
|
-
},
|
|
387
|
-
|
|
388
|
-
async commitTransaction (req) {
|
|
389
|
-
const tranId = req.context.storeTransaction
|
|
390
|
-
const tran = transactions.get(tranId)
|
|
391
|
-
|
|
392
|
-
if (!tran) {
|
|
393
|
-
throw new Error('Can not call store.commitTransaction without an active transaction, make sure you are calling store.beginTransaction previously or that you are not calling store.commitTransaction, store.rollbackTransaction more than once')
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
await this.provider.commitTransaction(tran)
|
|
397
|
-
|
|
398
|
-
transactions.delete(tranId)
|
|
399
|
-
delete req.context.storeTransaction
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
async rollbackTransaction (req) {
|
|
403
|
-
const tranId = req.context.storeTransaction
|
|
404
|
-
const tran = transactions.get(tranId)
|
|
405
|
-
|
|
406
|
-
if (!tran) {
|
|
407
|
-
throw new Error('Can not call store.rollbackTransaction without an active transaction, make sure you are calling store.beginTransaction previously or that you are not calling store.rollbackTransaction, store.commitTransaction more than once')
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
await this.provider.rollbackTransaction(tran)
|
|
411
|
-
|
|
412
|
-
transactions.delete(tranId)
|
|
413
|
-
delete req.context.storeTransaction
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
store.model.entityTypes = proxyTypeCollection({
|
|
418
|
-
toGenerate: entitySchemasToGenerate,
|
|
419
|
-
config: generateSchemaEntityTypeConfig,
|
|
420
|
-
generateSchemaDefault: defaultGenerateSchemaForEntityType
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
return store
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function entityTypeHasKey (entityType) {
|
|
427
|
-
let hasKey = false
|
|
428
|
-
|
|
429
|
-
Object.entries(entityType).forEach(([field, fieldDef]) => {
|
|
430
|
-
if (hasKey === true) {
|
|
431
|
-
return
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (fieldDef.key === true) {
|
|
435
|
-
hasKey = true
|
|
436
|
-
}
|
|
437
|
-
})
|
|
438
|
-
|
|
439
|
-
return hasKey
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function proxyTypeCollection ({ toGenerate, config, generateSchemaDefault }) {
|
|
443
|
-
return new Proxy({}, {
|
|
444
|
-
set: (target, property, value, receiver) => {
|
|
445
|
-
let shouldGenerate = config[property]
|
|
446
|
-
|
|
447
|
-
if (shouldGenerate == null) {
|
|
448
|
-
shouldGenerate = generateSchemaDefault
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
if (shouldGenerate === true) {
|
|
452
|
-
toGenerate.push(property)
|
|
453
|
-
} else {
|
|
454
|
-
const index = toGenerate.indexOf(property)
|
|
455
|
-
|
|
456
|
-
if (index !== -1) {
|
|
457
|
-
toGenerate.splice(index, 1)
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ensure clean config for next call
|
|
462
|
-
delete config[property]
|
|
463
|
-
|
|
464
|
-
return Reflect.set(target, property, value, receiver)
|
|
465
|
-
}
|
|
466
|
-
})
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
module.exports = (...args) => Object.assign(DocumentStore(...args), events.EventEmitter.prototype)
|
|
1
|
+
const events = require('events')
|
|
2
|
+
const { nanoid } = require('nanoid')
|
|
3
|
+
const { v4: uuidv4 } = require('uuid')
|
|
4
|
+
const createListenerCollection = require('../../shared/listenerCollection')
|
|
5
|
+
const { resolvePropDefinition, typeDefToJSONSchema } = require('./typeUtils')
|
|
6
|
+
const { findReferencePropertiesInType, findLinkedEntitiesForReferenceValue, existsReferenceValue, updateReferenceValue } = require('./referenceUtils')
|
|
7
|
+
const collection = require('./collection')
|
|
8
|
+
const checkDuplicatedId = require('./checkDuplicatedId')
|
|
9
|
+
|
|
10
|
+
// factory function
|
|
11
|
+
const DocumentStore = (options, validator, encryption) => {
|
|
12
|
+
const entitySchemasToGenerate = []
|
|
13
|
+
const generateSchemaEntityTypeConfig = {}
|
|
14
|
+
const defaultGenerateSchemaForEntityType = true
|
|
15
|
+
|
|
16
|
+
// internal sets are not listed in normal documentStore.model.entitySets, or available in
|
|
17
|
+
// documentStore.collection(), instead they are available in documentStore.internalCollection()
|
|
18
|
+
// and entity set definitions of these internals are only available in store provider implementations.
|
|
19
|
+
//
|
|
20
|
+
// this allows having collections that are available for specific uses cases (playground, jsreportonline)
|
|
21
|
+
// which needs to save/load data using jsreport store abstraction but they don't need to be visible
|
|
22
|
+
// from extensions. (to avoid adding permissions, attributes or other logic that modifies these internal entities from extensions)
|
|
23
|
+
const internalEntitySets = {}
|
|
24
|
+
const transactions = new Map()
|
|
25
|
+
const fileExtensionResolvers = []
|
|
26
|
+
|
|
27
|
+
transactions.getActiveTransaction = function (req) {
|
|
28
|
+
if (req && req.context && req.context.storeTransaction) {
|
|
29
|
+
if (!transactions.has(req.context.storeTransaction)) {
|
|
30
|
+
throw new Error('transaction does not exists or is no longer active, make sure you are not closing the transaction previously using store.commitTransaction or store.rollbackTransaction')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return transactions.get(req.context.storeTransaction)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let initialized = false
|
|
38
|
+
|
|
39
|
+
const store = {
|
|
40
|
+
options,
|
|
41
|
+
model: {
|
|
42
|
+
namespace: 'jsreport',
|
|
43
|
+
complexTypes: {},
|
|
44
|
+
entitySets: {}
|
|
45
|
+
},
|
|
46
|
+
internalAfterInitListeners: createListenerCollection('DocumentStore@internalAfterInit'),
|
|
47
|
+
emitter: new events.EventEmitter(),
|
|
48
|
+
|
|
49
|
+
registerProvider (provider) {
|
|
50
|
+
this.provider = provider
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async init () {
|
|
54
|
+
initialized = true
|
|
55
|
+
|
|
56
|
+
if (!this.provider && this.options.store.provider === 'memory') {
|
|
57
|
+
this.provider = require('./memoryStoreProvider')()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!this.provider) {
|
|
61
|
+
throw new Error(`The document store provider ${this.options.store.provider} was not registered.`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.emit('before-init', this)
|
|
65
|
+
|
|
66
|
+
this.collections = {}
|
|
67
|
+
this.internalCollections = {}
|
|
68
|
+
|
|
69
|
+
const entitySetsLinkedReferenceProperties = {}
|
|
70
|
+
|
|
71
|
+
Object.entries(this.model.entitySets).forEach((e) => {
|
|
72
|
+
const eName = e[0]
|
|
73
|
+
const es = e[1]
|
|
74
|
+
es.normalizedEntityTypeName = es.entityType.replace(this.model.namespace + '.', '')
|
|
75
|
+
es.entityTypeDef = this.model.entityTypes[es.normalizedEntityTypeName]
|
|
76
|
+
const entityType = es.entityTypeDef
|
|
77
|
+
|
|
78
|
+
if (!entityType._id) {
|
|
79
|
+
entityType._id = { type: 'Edm.String' }
|
|
80
|
+
|
|
81
|
+
if (!entityTypeHasKey(entityType)) {
|
|
82
|
+
// add key: true only if there is no other field defined already
|
|
83
|
+
// key: true is used for ODATA XML generation, it includes new information to the final XML
|
|
84
|
+
entityType._id.key = true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!entityType.creationDate) {
|
|
89
|
+
entityType.creationDate = { type: 'Edm.DateTimeOffset' }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!entityType.modificationDate) {
|
|
93
|
+
entityType.modificationDate = { type: 'Edm.DateTimeOffset' }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!entityType.shortid) {
|
|
97
|
+
entityType.shortid = { type: 'Edm.String' }
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const referenceProperties = findReferencePropertiesInType(this.model, entityType)
|
|
101
|
+
|
|
102
|
+
referenceProperties.forEach((property) => {
|
|
103
|
+
const entitySetName = property.referenceTo
|
|
104
|
+
entitySetsLinkedReferenceProperties[entitySetName] = entitySetsLinkedReferenceProperties[entitySetName] || []
|
|
105
|
+
|
|
106
|
+
entitySetsLinkedReferenceProperties[entitySetName].push({
|
|
107
|
+
name: property.name,
|
|
108
|
+
entitySet: eName
|
|
109
|
+
})
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
es.referenceProperties = referenceProperties
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
Object.keys(this.model.entitySets).forEach((eName) => {
|
|
116
|
+
const es = this.model.entitySets[eName]
|
|
117
|
+
|
|
118
|
+
if (entitySetsLinkedReferenceProperties[eName]) {
|
|
119
|
+
es.linkedReferenceProperties = entitySetsLinkedReferenceProperties[eName]
|
|
120
|
+
} else {
|
|
121
|
+
es.linkedReferenceProperties = []
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const col = collection(eName, this.provider, this.model, validator, encryption, transactions)
|
|
125
|
+
|
|
126
|
+
const addDefaultFields = (doc) => {
|
|
127
|
+
doc.creationDate = new Date()
|
|
128
|
+
doc.modificationDate = new Date()
|
|
129
|
+
doc.shortid = doc.shortid || nanoid(7)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
col.beforeInsertListeners.add('core-default-fields', (doc, req) => {
|
|
133
|
+
addDefaultFields(doc)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
col.beforeUpdateListeners.add('core-default-fields', (q, u, o, req) => {
|
|
137
|
+
if (u.$set && o && o.upsert === true) {
|
|
138
|
+
addDefaultFields(u.$set)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (req && req.context.skipModificationDateUpdate === true) {
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (u.$set) {
|
|
146
|
+
u.$set.modificationDate = new Date()
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
this.collections[eName] = col
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
Object.keys(internalEntitySets).forEach((e) => (this.internalCollections[e] = collection(e, this.provider, this.model, undefined, undefined, transactions)))
|
|
154
|
+
|
|
155
|
+
if (this.provider.load) {
|
|
156
|
+
// we combine internal and public entity sets in order for the store provider
|
|
157
|
+
// be able to recognize both set of entities and be able to work with them
|
|
158
|
+
const modelToLoad = Object.assign({}, this.model)
|
|
159
|
+
|
|
160
|
+
modelToLoad.entitySets = Object.assign({}, modelToLoad.entitySets, internalEntitySets)
|
|
161
|
+
|
|
162
|
+
await this.provider.load(modelToLoad)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
entitySchemasToGenerate.forEach((entityType) => {
|
|
166
|
+
const schema = typeDefToJSONSchema(this.model, this.model.entityTypes[entityType])
|
|
167
|
+
|
|
168
|
+
if (schema == null) {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (initialized && validator.getSchema(entityType) != null) {
|
|
173
|
+
validator.addSchema(entityType, schema, true)
|
|
174
|
+
} else {
|
|
175
|
+
validator.addSchema(entityType, schema)
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
this.emit('after-init', this)
|
|
180
|
+
return this.internalAfterInitListeners.fire()
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Register type for odata.
|
|
185
|
+
* Example:
|
|
186
|
+
* documentStore.registerEntityType('UserType', {
|
|
187
|
+
* _id: {type: 'Edm.String', key: true}
|
|
188
|
+
* })
|
|
189
|
+
*
|
|
190
|
+
* @param {String} type
|
|
191
|
+
* @param {Object} def
|
|
192
|
+
*/
|
|
193
|
+
registerEntityType (type, def, generateJSONSchema = defaultGenerateSchemaForEntityType) {
|
|
194
|
+
generateSchemaEntityTypeConfig[type] = generateJSONSchema === true
|
|
195
|
+
this.model.entityTypes[type] = def
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
addFileExtensionResolver (fn) {
|
|
199
|
+
fileExtensionResolvers.push(fn)
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
resolveFileExtension (doc, entitySetName, propertyName) {
|
|
203
|
+
const model = this.model
|
|
204
|
+
const entitySets = { ...model.entitySets, ...internalEntitySets }
|
|
205
|
+
const es = entitySets[entitySetName]
|
|
206
|
+
|
|
207
|
+
if (es == null) {
|
|
208
|
+
throw new Error(`Entity set "${entitySetName}" does not exists`)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const entityTypeName = es.entityType
|
|
212
|
+
const entityType = es.entityTypeDef
|
|
213
|
+
const propTypeParts = propertyName.split('.')
|
|
214
|
+
const propTypePartsLastIndex = propTypeParts.length - 1
|
|
215
|
+
let propType = entityType
|
|
216
|
+
|
|
217
|
+
propTypeParts.forEach((propName, idx) => {
|
|
218
|
+
if (propType == null || propType[propName] == null) {
|
|
219
|
+
throw new Error(`Property "${propertyName}" does not exists in entity type "${entityTypeName}"`)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
propType = propType[propName]
|
|
223
|
+
|
|
224
|
+
const resolveResult = this.resolvePropertyDefinition(propType)
|
|
225
|
+
|
|
226
|
+
if (!resolveResult) {
|
|
227
|
+
throw new Error(`Property "${propertyName}" does not have a valid type`)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (resolveResult.def.type.startsWith('Collection(') && resolveResult.subType == null) {
|
|
231
|
+
if (propTypePartsLastIndex !== idx) {
|
|
232
|
+
propType = null
|
|
233
|
+
}
|
|
234
|
+
} else if (resolveResult.subType) {
|
|
235
|
+
propType = resolveResult.subType
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
if (!propType || propType.document == null) {
|
|
240
|
+
return
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const resolver of fileExtensionResolvers) {
|
|
244
|
+
const extension = resolver(doc, entitySetName, entityType, propType)
|
|
245
|
+
|
|
246
|
+
if (extension) {
|
|
247
|
+
return extension
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return propType.document.extension
|
|
252
|
+
},
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Register complex type for odata.
|
|
256
|
+
* Example:
|
|
257
|
+
* documentStore.registerComplexType('DataItemRefType', {
|
|
258
|
+
* name: {type: 'Edm.String' }
|
|
259
|
+
* })
|
|
260
|
+
*
|
|
261
|
+
* @param {String} name
|
|
262
|
+
* @param {Object} def
|
|
263
|
+
*/
|
|
264
|
+
registerComplexType (name, def) {
|
|
265
|
+
this.model.complexTypes[name] = def
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Register complete entity set for odata. The first parameter is then use as a collection name
|
|
270
|
+
* Example:
|
|
271
|
+
* documentStore.registerEntitySet('users', {
|
|
272
|
+
* entityType: 'jsreport.UserType'
|
|
273
|
+
* })
|
|
274
|
+
*
|
|
275
|
+
* @param {String} name
|
|
276
|
+
* @param {Object} def
|
|
277
|
+
*/
|
|
278
|
+
registerEntitySet (name, def) {
|
|
279
|
+
const isInternal = def.internal === true
|
|
280
|
+
|
|
281
|
+
if (def.exportable == null || def.exportable === true) {
|
|
282
|
+
def.exportable = true
|
|
283
|
+
} else {
|
|
284
|
+
def.exportable = false
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
isInternal &&
|
|
289
|
+
this.model.entitySets[name] != null
|
|
290
|
+
) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Entity set "${name}" can not be registered as internal entity because it was register as public entity previously`
|
|
293
|
+
)
|
|
294
|
+
} else if (
|
|
295
|
+
!isInternal &&
|
|
296
|
+
internalEntitySets[name] != null
|
|
297
|
+
) {
|
|
298
|
+
throw new Error(
|
|
299
|
+
`Entity set "${name}" can not be registered as public entity because it was register as internal entity previously`
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (!isInternal) {
|
|
304
|
+
this.model.entitySets[name] = def
|
|
305
|
+
} else {
|
|
306
|
+
def.normalizedEntityTypeName = def.entityType.replace(this.model.namespace + '.', '')
|
|
307
|
+
def.entityTypeDef = this.model.entityTypes[def.normalizedEntityTypeName]
|
|
308
|
+
internalEntitySets[name] = def
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Resolves the passed definition to contain subtype definition
|
|
314
|
+
* (useful when dealing with complex types or collection types)
|
|
315
|
+
* @param {Object} def
|
|
316
|
+
* @return {Object}
|
|
317
|
+
*/
|
|
318
|
+
resolvePropertyDefinition (def) {
|
|
319
|
+
return resolvePropDefinition(this.model, def)
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Get the document Collection by the name provided in registerEntitySet
|
|
324
|
+
* @param {String} name
|
|
325
|
+
* @returns {Collection}
|
|
326
|
+
*/
|
|
327
|
+
collection (name) {
|
|
328
|
+
return this.collections[name]
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get the document internal Collection by the name provided in registerEntitySet
|
|
333
|
+
* @param {String} name
|
|
334
|
+
* @returns {Collection}
|
|
335
|
+
*/
|
|
336
|
+
internalCollection (name) {
|
|
337
|
+
return this.internalCollections[name]
|
|
338
|
+
},
|
|
339
|
+
|
|
340
|
+
findLinkedEntitiesForReference (entitiesByCollection, collectionReferenceTargetName, referenceValue) {
|
|
341
|
+
return findLinkedEntitiesForReferenceValue(this, entitiesByCollection, collectionReferenceTargetName, referenceValue)
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
existsReference (collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceValue) {
|
|
345
|
+
return existsReferenceValue(this, collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceValue)
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
updateReference (collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceOpts, newReferenceValue) {
|
|
349
|
+
return updateReferenceValue(this, collectionReferenceOriginName, entity, collectionReferenceTargetName, referenceOpts, newReferenceValue)
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
checkDuplicatedId (collectionName, idValue, req) {
|
|
353
|
+
return checkDuplicatedId(this, collectionName, idValue, req)
|
|
354
|
+
},
|
|
355
|
+
|
|
356
|
+
async close () {
|
|
357
|
+
transactions.clear()
|
|
358
|
+
|
|
359
|
+
if (this.provider && this.provider.close) {
|
|
360
|
+
await this.provider.close()
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Drop the whole document store
|
|
366
|
+
* @returns {Promise}
|
|
367
|
+
*/
|
|
368
|
+
async drop (req) {
|
|
369
|
+
return this.provider.drop({
|
|
370
|
+
transaction: transactions.getActiveTransaction(req)
|
|
371
|
+
})
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
async beginTransaction (req) {
|
|
375
|
+
if (req.context.storeTransaction && transactions.has(req.context.storeTransaction)) {
|
|
376
|
+
throw new Error('Can not call store.beginTransaction when an active transaction already exists, make sure you are not calling store.beginTransaction more than once')
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const tran = await this.provider.beginTransaction()
|
|
380
|
+
|
|
381
|
+
const tranId = uuidv4()
|
|
382
|
+
|
|
383
|
+
transactions.set(tranId, tran)
|
|
384
|
+
|
|
385
|
+
req.context.storeTransaction = tranId
|
|
386
|
+
},
|
|
387
|
+
|
|
388
|
+
async commitTransaction (req) {
|
|
389
|
+
const tranId = req.context.storeTransaction
|
|
390
|
+
const tran = transactions.get(tranId)
|
|
391
|
+
|
|
392
|
+
if (!tran) {
|
|
393
|
+
throw new Error('Can not call store.commitTransaction without an active transaction, make sure you are calling store.beginTransaction previously or that you are not calling store.commitTransaction, store.rollbackTransaction more than once')
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
await this.provider.commitTransaction(tran)
|
|
397
|
+
|
|
398
|
+
transactions.delete(tranId)
|
|
399
|
+
delete req.context.storeTransaction
|
|
400
|
+
},
|
|
401
|
+
|
|
402
|
+
async rollbackTransaction (req) {
|
|
403
|
+
const tranId = req.context.storeTransaction
|
|
404
|
+
const tran = transactions.get(tranId)
|
|
405
|
+
|
|
406
|
+
if (!tran) {
|
|
407
|
+
throw new Error('Can not call store.rollbackTransaction without an active transaction, make sure you are calling store.beginTransaction previously or that you are not calling store.rollbackTransaction, store.commitTransaction more than once')
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
await this.provider.rollbackTransaction(tran)
|
|
411
|
+
|
|
412
|
+
transactions.delete(tranId)
|
|
413
|
+
delete req.context.storeTransaction
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
store.model.entityTypes = proxyTypeCollection({
|
|
418
|
+
toGenerate: entitySchemasToGenerate,
|
|
419
|
+
config: generateSchemaEntityTypeConfig,
|
|
420
|
+
generateSchemaDefault: defaultGenerateSchemaForEntityType
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
return store
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function entityTypeHasKey (entityType) {
|
|
427
|
+
let hasKey = false
|
|
428
|
+
|
|
429
|
+
Object.entries(entityType).forEach(([field, fieldDef]) => {
|
|
430
|
+
if (hasKey === true) {
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (fieldDef.key === true) {
|
|
435
|
+
hasKey = true
|
|
436
|
+
}
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
return hasKey
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function proxyTypeCollection ({ toGenerate, config, generateSchemaDefault }) {
|
|
443
|
+
return new Proxy({}, {
|
|
444
|
+
set: (target, property, value, receiver) => {
|
|
445
|
+
let shouldGenerate = config[property]
|
|
446
|
+
|
|
447
|
+
if (shouldGenerate == null) {
|
|
448
|
+
shouldGenerate = generateSchemaDefault
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (shouldGenerate === true) {
|
|
452
|
+
toGenerate.push(property)
|
|
453
|
+
} else {
|
|
454
|
+
const index = toGenerate.indexOf(property)
|
|
455
|
+
|
|
456
|
+
if (index !== -1) {
|
|
457
|
+
toGenerate.splice(index, 1)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// ensure clean config for next call
|
|
462
|
+
delete config[property]
|
|
463
|
+
|
|
464
|
+
return Reflect.set(target, property, value, receiver)
|
|
465
|
+
}
|
|
466
|
+
})
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
module.exports = (...args) => Object.assign(DocumentStore(...args), events.EventEmitter.prototype)
|