@jsreport/jsreport-core 3.1.2-test.2 → 3.2.0
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 -298
- package/index.js +29 -29
- package/lib/main/blobStorage/blobStorage.js +52 -52
- 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 -264
- package/lib/main/extensions/fileUtils.js +56 -56
- package/lib/main/extensions/findVersion.js +49 -49
- package/lib/main/extensions/locationCache.js +103 -103
- 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 -230
- package/lib/main/migration/xlsxTemplatesToAssets.js +128 -128
- 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 +574 -579
- 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 -169
- package/lib/shared/normalizeMetaFromLogs.js +30 -30
- package/lib/shared/reporter.js +128 -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 +239 -207
- package/lib/worker/render/htmlRecipe.js +10 -10
- package/lib/worker/render/moduleHelper.js +45 -43
- package/lib/worker/render/noneEngine.js +12 -12
- package/lib/worker/render/profiler.js +158 -158
- package/lib/worker/render/render.js +202 -205
- package/lib/worker/render/resolveReferences.js +60 -60
- package/lib/worker/reporter.js +192 -191
- package/lib/worker/sandbox/runInSandbox.js +16 -9
- package/lib/worker/sandbox/safeSandbox.js +828 -828
- package/lib/worker/templates.js +80 -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,354 +1,354 @@
|
|
|
1
|
-
const omit = require('lodash.omit')
|
|
2
|
-
|
|
3
|
-
async function collectEntitiesInHierarchy (reporter, entities, sourceEntity, onlyChildren, req) {
|
|
4
|
-
if (sourceEntity.__entitySet === 'folders') {
|
|
5
|
-
if (!onlyChildren) {
|
|
6
|
-
entities.push(sourceEntity)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const childEntities = await reporter.folders.getEntitiesInFolder(sourceEntity.shortid, true, req)
|
|
10
|
-
|
|
11
|
-
for (const { entitySet, entity } of childEntities) {
|
|
12
|
-
entities.push(Object.assign(entity, {
|
|
13
|
-
__entitySet: entitySet
|
|
14
|
-
}))
|
|
15
|
-
}
|
|
16
|
-
} else {
|
|
17
|
-
entities.push(sourceEntity)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function throwIfEntityIsNotFolder (reporter, targetShortid, req) {
|
|
22
|
-
const folder = await reporter.documentStore.collection('folders').findOne({
|
|
23
|
-
shortid: targetShortid
|
|
24
|
-
}, req)
|
|
25
|
-
|
|
26
|
-
if (!folder) {
|
|
27
|
-
throw reporter.createError(`Target entity "${targetShortid}" is not a folder, please review that the copy/move is against a valid folder entity`, {
|
|
28
|
-
statusCode: 400
|
|
29
|
-
})
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
module.exports = (reporter) => async ({ source, target, shouldCopy, shouldReplace }, req) => {
|
|
34
|
-
const sourceCol = reporter.documentStore.collection(source.entitySet)
|
|
35
|
-
|
|
36
|
-
if (!sourceCol) {
|
|
37
|
-
throw reporter.createError(`Invalid entity set "${source.entitySet}" for source`, {
|
|
38
|
-
statusCode: 400
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const sourceEntity = await sourceCol.findOne({ _id: source.id }, req)
|
|
43
|
-
|
|
44
|
-
if (!sourceEntity) {
|
|
45
|
-
throw reporter.createError('Source entity with specified id does not exists', {
|
|
46
|
-
statusCode: 400
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const onlyChildren = source.onlyChildren === true
|
|
51
|
-
|
|
52
|
-
if (onlyChildren && source.entitySet !== 'folders') {
|
|
53
|
-
throw reporter.createError('onlyChildren option can only be enabled when source is a folder')
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (target.shortid === undefined) {
|
|
57
|
-
throw reporter.createError('target should specify ".shortid"', {
|
|
58
|
-
statusCode: 400
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (target.shortid != null) {
|
|
63
|
-
await throwIfEntityIsNotFolder(reporter, target.shortid, req)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const targetUpdateReferences = target.updateReferences === true && target.shortid != null
|
|
67
|
-
|
|
68
|
-
let entitiesInHierarchy = []
|
|
69
|
-
|
|
70
|
-
await collectEntitiesInHierarchy(
|
|
71
|
-
reporter,
|
|
72
|
-
entitiesInHierarchy,
|
|
73
|
-
Object.assign(sourceEntity, { __entitySet: source.entitySet }),
|
|
74
|
-
onlyChildren,
|
|
75
|
-
req
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
let rootChildren
|
|
79
|
-
|
|
80
|
-
if (onlyChildren) {
|
|
81
|
-
rootChildren = entitiesInHierarchy.filter((e) => {
|
|
82
|
-
return e.folder.shortid === sourceEntity.shortid
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ignore if we are doing a move at the same level of hierarchy between source and target
|
|
87
|
-
if (
|
|
88
|
-
(sourceEntity.folder == null && target.shortid === null) ||
|
|
89
|
-
(sourceEntity.folder != null &&
|
|
90
|
-
target.shortid != null &&
|
|
91
|
-
sourceEntity.folder.shortid === target.shortid)
|
|
92
|
-
) {
|
|
93
|
-
return []
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (!shouldCopy) {
|
|
97
|
-
// validates that we can't move entities from higher level
|
|
98
|
-
// into lower level of the same hierarchy
|
|
99
|
-
if (entitiesInHierarchy.some((e) => e.shortid === target.shortid)) {
|
|
100
|
-
return []
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
let updateQ
|
|
104
|
-
|
|
105
|
-
if (target.shortid === null) {
|
|
106
|
-
updateQ = {
|
|
107
|
-
$set: {
|
|
108
|
-
folder: null
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
updateQ = {
|
|
113
|
-
$set: {
|
|
114
|
-
folder: {
|
|
115
|
-
shortid: target.shortid
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let sourceEntities
|
|
122
|
-
|
|
123
|
-
if (!onlyChildren) {
|
|
124
|
-
sourceEntities = [sourceEntity]
|
|
125
|
-
} else {
|
|
126
|
-
sourceEntities = rootChildren
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
for (const entity of sourceEntities) {
|
|
130
|
-
try {
|
|
131
|
-
await reporter.documentStore.collection(entity.__entitySet).update({
|
|
132
|
-
_id: entity._id
|
|
133
|
-
}, updateQ, req)
|
|
134
|
-
} catch (e) {
|
|
135
|
-
if (e.code === 'DUPLICATED_ENTITY' && shouldReplace) {
|
|
136
|
-
// replacing is not supported when it generates a conflict with folder
|
|
137
|
-
if (e.existingEntityEntitySet === 'folders') {
|
|
138
|
-
throw e
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const removeFolderQ = target.shortid === null ? { folder: null } : { folder: { shortid: target.shortid } }
|
|
142
|
-
|
|
143
|
-
await reporter.documentStore.collection(e.existingEntityEntitySet).remove({
|
|
144
|
-
_id: e.existingEntity._id,
|
|
145
|
-
...removeFolderQ
|
|
146
|
-
}, req)
|
|
147
|
-
|
|
148
|
-
await reporter.documentStore.collection(entity.__entitySet).update({
|
|
149
|
-
_id: entity._id
|
|
150
|
-
}, updateQ, req)
|
|
151
|
-
} else {
|
|
152
|
-
throw e
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const sourceEntityItems = entitiesInHierarchy.filter((e) => {
|
|
158
|
-
return sourceEntities.find((childE) => childE._id === e._id) != null
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
for (const sourceEntityItem of sourceEntityItems) {
|
|
162
|
-
if (target.shortid === null) {
|
|
163
|
-
sourceEntityItem.folder = null
|
|
164
|
-
} else {
|
|
165
|
-
sourceEntityItem.folder = {
|
|
166
|
-
shortid: target.shortid
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
} else {
|
|
171
|
-
const entitiesInHierarchyByCollection = entitiesInHierarchy.reduce((acu, entity) => {
|
|
172
|
-
acu[entity.__entitySet] = acu[entity.__entitySet] || []
|
|
173
|
-
acu[entity.__entitySet].push(entity)
|
|
174
|
-
return acu
|
|
175
|
-
}, {})
|
|
176
|
-
|
|
177
|
-
const entityReferencesMap = new WeakMap()
|
|
178
|
-
const originalEntitiesNewMap = new WeakMap()
|
|
179
|
-
const entityRecordNewValueMap = new WeakMap()
|
|
180
|
-
const records = []
|
|
181
|
-
|
|
182
|
-
// eslint-disable-next-line
|
|
183
|
-
function createUpdateReferences (record) {
|
|
184
|
-
return async (newEntity) => {
|
|
185
|
-
const { entitySet, entity, originalEntity } = record
|
|
186
|
-
const linkedEntities = entityReferencesMap.get(entity)
|
|
187
|
-
|
|
188
|
-
if (linkedEntities.length === 0) {
|
|
189
|
-
return
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
for (const { properties: linkedProperties, entity: originalLinkedEntity } of linkedEntities) {
|
|
193
|
-
const currentNewLinkedEntity = originalEntitiesNewMap.get(originalLinkedEntity)
|
|
194
|
-
|
|
195
|
-
if (entityRecordNewValueMap.has(currentNewLinkedEntity)) {
|
|
196
|
-
const currentEntityProcessedNew = entityRecordNewValueMap.get(currentNewLinkedEntity)
|
|
197
|
-
const currentEntityUpdate = {}
|
|
198
|
-
|
|
199
|
-
// if we get here it means that the entity was already processed, so we need to
|
|
200
|
-
// execute an update directly to the store
|
|
201
|
-
for (const prop of linkedProperties) {
|
|
202
|
-
// here we care to use the new object result because we want to preserve other values
|
|
203
|
-
// in case the property is array with objects
|
|
204
|
-
reporter.documentStore.updateReference(originalLinkedEntity.__entitySet, currentEntityProcessedNew, entitySet, { referenceProp: prop, referenceValue: originalEntity.shortid }, newEntity.shortid)
|
|
205
|
-
const rootProp = prop.split('.')[0]
|
|
206
|
-
currentEntityUpdate[rootProp] = currentEntityProcessedNew[rootProp]
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
await reporter.documentStore.collection(originalLinkedEntity.__entitySet).update({
|
|
210
|
-
_id: currentEntityProcessedNew._id
|
|
211
|
-
}, { $set: currentEntityUpdate }, req)
|
|
212
|
-
} else {
|
|
213
|
-
// here we care to update all properties to point to old reference value
|
|
214
|
-
reporter.documentStore.updateReference(originalLinkedEntity.__entitySet, currentNewLinkedEntity, entitySet, { referenceValue: originalEntity.shortid }, newEntity.shortid)
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (targetUpdateReferences) {
|
|
221
|
-
const targetEntity = await reporter.documentStore.collection('folders').findOne({
|
|
222
|
-
shortid: target.shortid
|
|
223
|
-
}, req)
|
|
224
|
-
|
|
225
|
-
targetEntity.__entitySet = 'folders'
|
|
226
|
-
entitiesInHierarchyByCollection.folders = entitiesInHierarchyByCollection.folders || []
|
|
227
|
-
entitiesInHierarchyByCollection.folders.push(targetEntity)
|
|
228
|
-
originalEntitiesNewMap.set(targetEntity, targetEntity)
|
|
229
|
-
entityRecordNewValueMap.set(targetEntity, targetEntity)
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
for (const entity of entitiesInHierarchy) {
|
|
233
|
-
const newEntity = {
|
|
234
|
-
...omit(entity, ['_id', 'shortid', '__entitySet'])
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
let isSourceEntityItem
|
|
238
|
-
|
|
239
|
-
if (!onlyChildren) {
|
|
240
|
-
isSourceEntityItem = source.id === entity._id
|
|
241
|
-
} else {
|
|
242
|
-
isSourceEntityItem = rootChildren.find((e) => e._id === entity._id) != null
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
if (isSourceEntityItem) {
|
|
246
|
-
if (target.shortid === null) {
|
|
247
|
-
newEntity.folder = null
|
|
248
|
-
} else {
|
|
249
|
-
newEntity.folder = {
|
|
250
|
-
shortid: target.shortid
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const entitySet = entity.__entitySet
|
|
256
|
-
newEntity.__entitySet = entitySet
|
|
257
|
-
|
|
258
|
-
const linkedEntities = reporter.documentStore.findLinkedEntitiesForReference(
|
|
259
|
-
entitiesInHierarchyByCollection,
|
|
260
|
-
entitySet,
|
|
261
|
-
entity.shortid
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
const record = {
|
|
265
|
-
entitySet,
|
|
266
|
-
originalEntity: entity,
|
|
267
|
-
entity: newEntity
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
record.updateReferences = createUpdateReferences(record)
|
|
271
|
-
|
|
272
|
-
originalEntitiesNewMap.set(entity, newEntity)
|
|
273
|
-
entityReferencesMap.set(newEntity, linkedEntities)
|
|
274
|
-
|
|
275
|
-
records.push(record)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const processNewEntity = async (entitySet, entity) => {
|
|
279
|
-
const newEntityFromStore = await reporter.documentStore.collection(entitySet).insert({
|
|
280
|
-
...omit(entity, ['__entitySet'])
|
|
281
|
-
}, req)
|
|
282
|
-
|
|
283
|
-
entityRecordNewValueMap.set(entity, newEntityFromStore)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
for (const record of records) {
|
|
287
|
-
try {
|
|
288
|
-
await processNewEntity(record.entitySet, record.entity)
|
|
289
|
-
} catch (e) {
|
|
290
|
-
if (e.code === 'DUPLICATED_ENTITY' && shouldReplace) {
|
|
291
|
-
// replacing is not supported when it generates a conflict with folder
|
|
292
|
-
if (e.existingEntityEntitySet === 'folders') {
|
|
293
|
-
throw e
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const removeFolderQ = target.shortid === null ? { folder: null } : { folder: { shortid: target.shortid } }
|
|
297
|
-
|
|
298
|
-
await reporter.documentStore.collection(e.existingEntityEntitySet).remove({
|
|
299
|
-
_id: e.existingEntity._id,
|
|
300
|
-
...removeFolderQ
|
|
301
|
-
}, req)
|
|
302
|
-
|
|
303
|
-
await processNewEntity(record.entitySet, record.entity)
|
|
304
|
-
} else {
|
|
305
|
-
throw e
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
await record.updateReferences(entityRecordNewValueMap.get(record.entity))
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
entitiesInHierarchy = records.map((record) => ({
|
|
313
|
-
...omit(entityRecordNewValueMap.get(record.entity), ['$entitySet', '__entitySet']),
|
|
314
|
-
__entitySet: record.entitySet
|
|
315
|
-
}))
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// return fresh version of creationDate, modificationDate in the records.
|
|
319
|
-
// this helps with concurrent validation on studio
|
|
320
|
-
await Promise.all(entitiesInHierarchy.map(async (entity) => {
|
|
321
|
-
const entitySet = reporter.documentStore.model.entitySets[entity.__entitySet]
|
|
322
|
-
const entityType = entitySet.entityTypeDef
|
|
323
|
-
const projection = {}
|
|
324
|
-
|
|
325
|
-
if (entityType.creationDate != null && entityType.creationDate.type === 'Edm.DateTimeOffset') {
|
|
326
|
-
projection.creationDate = 1
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (entityType.modificationDate != null && entityType.modificationDate.type === 'Edm.DateTimeOffset') {
|
|
330
|
-
projection.modificationDate = 1
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
if (Object.keys(projection).length === 0) {
|
|
334
|
-
return
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
const doc = await reporter.documentStore.collection(entity.__entitySet).findOne({
|
|
338
|
-
_id: entity._id
|
|
339
|
-
}, {
|
|
340
|
-
creationDate: 1,
|
|
341
|
-
modificationDate: 1
|
|
342
|
-
}, req)
|
|
343
|
-
|
|
344
|
-
if (projection.creationDate) {
|
|
345
|
-
entity.creationDate = doc.creationDate
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
if (projection.modificationDate) {
|
|
349
|
-
entity.modificationDate = doc.modificationDate
|
|
350
|
-
}
|
|
351
|
-
}))
|
|
352
|
-
|
|
353
|
-
return entitiesInHierarchy
|
|
354
|
-
}
|
|
1
|
+
const omit = require('lodash.omit')
|
|
2
|
+
|
|
3
|
+
async function collectEntitiesInHierarchy (reporter, entities, sourceEntity, onlyChildren, req) {
|
|
4
|
+
if (sourceEntity.__entitySet === 'folders') {
|
|
5
|
+
if (!onlyChildren) {
|
|
6
|
+
entities.push(sourceEntity)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const childEntities = await reporter.folders.getEntitiesInFolder(sourceEntity.shortid, true, req)
|
|
10
|
+
|
|
11
|
+
for (const { entitySet, entity } of childEntities) {
|
|
12
|
+
entities.push(Object.assign(entity, {
|
|
13
|
+
__entitySet: entitySet
|
|
14
|
+
}))
|
|
15
|
+
}
|
|
16
|
+
} else {
|
|
17
|
+
entities.push(sourceEntity)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function throwIfEntityIsNotFolder (reporter, targetShortid, req) {
|
|
22
|
+
const folder = await reporter.documentStore.collection('folders').findOne({
|
|
23
|
+
shortid: targetShortid
|
|
24
|
+
}, req)
|
|
25
|
+
|
|
26
|
+
if (!folder) {
|
|
27
|
+
throw reporter.createError(`Target entity "${targetShortid}" is not a folder, please review that the copy/move is against a valid folder entity`, {
|
|
28
|
+
statusCode: 400
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = (reporter) => async ({ source, target, shouldCopy, shouldReplace }, req) => {
|
|
34
|
+
const sourceCol = reporter.documentStore.collection(source.entitySet)
|
|
35
|
+
|
|
36
|
+
if (!sourceCol) {
|
|
37
|
+
throw reporter.createError(`Invalid entity set "${source.entitySet}" for source`, {
|
|
38
|
+
statusCode: 400
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const sourceEntity = await sourceCol.findOne({ _id: source.id }, req)
|
|
43
|
+
|
|
44
|
+
if (!sourceEntity) {
|
|
45
|
+
throw reporter.createError('Source entity with specified id does not exists', {
|
|
46
|
+
statusCode: 400
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const onlyChildren = source.onlyChildren === true
|
|
51
|
+
|
|
52
|
+
if (onlyChildren && source.entitySet !== 'folders') {
|
|
53
|
+
throw reporter.createError('onlyChildren option can only be enabled when source is a folder')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (target.shortid === undefined) {
|
|
57
|
+
throw reporter.createError('target should specify ".shortid"', {
|
|
58
|
+
statusCode: 400
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (target.shortid != null) {
|
|
63
|
+
await throwIfEntityIsNotFolder(reporter, target.shortid, req)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const targetUpdateReferences = target.updateReferences === true && target.shortid != null
|
|
67
|
+
|
|
68
|
+
let entitiesInHierarchy = []
|
|
69
|
+
|
|
70
|
+
await collectEntitiesInHierarchy(
|
|
71
|
+
reporter,
|
|
72
|
+
entitiesInHierarchy,
|
|
73
|
+
Object.assign(sourceEntity, { __entitySet: source.entitySet }),
|
|
74
|
+
onlyChildren,
|
|
75
|
+
req
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
let rootChildren
|
|
79
|
+
|
|
80
|
+
if (onlyChildren) {
|
|
81
|
+
rootChildren = entitiesInHierarchy.filter((e) => {
|
|
82
|
+
return e.folder.shortid === sourceEntity.shortid
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ignore if we are doing a move at the same level of hierarchy between source and target
|
|
87
|
+
if (
|
|
88
|
+
(sourceEntity.folder == null && target.shortid === null) ||
|
|
89
|
+
(sourceEntity.folder != null &&
|
|
90
|
+
target.shortid != null &&
|
|
91
|
+
sourceEntity.folder.shortid === target.shortid)
|
|
92
|
+
) {
|
|
93
|
+
return []
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (!shouldCopy) {
|
|
97
|
+
// validates that we can't move entities from higher level
|
|
98
|
+
// into lower level of the same hierarchy
|
|
99
|
+
if (entitiesInHierarchy.some((e) => e.shortid === target.shortid)) {
|
|
100
|
+
return []
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let updateQ
|
|
104
|
+
|
|
105
|
+
if (target.shortid === null) {
|
|
106
|
+
updateQ = {
|
|
107
|
+
$set: {
|
|
108
|
+
folder: null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
updateQ = {
|
|
113
|
+
$set: {
|
|
114
|
+
folder: {
|
|
115
|
+
shortid: target.shortid
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let sourceEntities
|
|
122
|
+
|
|
123
|
+
if (!onlyChildren) {
|
|
124
|
+
sourceEntities = [sourceEntity]
|
|
125
|
+
} else {
|
|
126
|
+
sourceEntities = rootChildren
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
for (const entity of sourceEntities) {
|
|
130
|
+
try {
|
|
131
|
+
await reporter.documentStore.collection(entity.__entitySet).update({
|
|
132
|
+
_id: entity._id
|
|
133
|
+
}, updateQ, req)
|
|
134
|
+
} catch (e) {
|
|
135
|
+
if (e.code === 'DUPLICATED_ENTITY' && shouldReplace) {
|
|
136
|
+
// replacing is not supported when it generates a conflict with folder
|
|
137
|
+
if (e.existingEntityEntitySet === 'folders') {
|
|
138
|
+
throw e
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const removeFolderQ = target.shortid === null ? { folder: null } : { folder: { shortid: target.shortid } }
|
|
142
|
+
|
|
143
|
+
await reporter.documentStore.collection(e.existingEntityEntitySet).remove({
|
|
144
|
+
_id: e.existingEntity._id,
|
|
145
|
+
...removeFolderQ
|
|
146
|
+
}, req)
|
|
147
|
+
|
|
148
|
+
await reporter.documentStore.collection(entity.__entitySet).update({
|
|
149
|
+
_id: entity._id
|
|
150
|
+
}, updateQ, req)
|
|
151
|
+
} else {
|
|
152
|
+
throw e
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sourceEntityItems = entitiesInHierarchy.filter((e) => {
|
|
158
|
+
return sourceEntities.find((childE) => childE._id === e._id) != null
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
for (const sourceEntityItem of sourceEntityItems) {
|
|
162
|
+
if (target.shortid === null) {
|
|
163
|
+
sourceEntityItem.folder = null
|
|
164
|
+
} else {
|
|
165
|
+
sourceEntityItem.folder = {
|
|
166
|
+
shortid: target.shortid
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
const entitiesInHierarchyByCollection = entitiesInHierarchy.reduce((acu, entity) => {
|
|
172
|
+
acu[entity.__entitySet] = acu[entity.__entitySet] || []
|
|
173
|
+
acu[entity.__entitySet].push(entity)
|
|
174
|
+
return acu
|
|
175
|
+
}, {})
|
|
176
|
+
|
|
177
|
+
const entityReferencesMap = new WeakMap()
|
|
178
|
+
const originalEntitiesNewMap = new WeakMap()
|
|
179
|
+
const entityRecordNewValueMap = new WeakMap()
|
|
180
|
+
const records = []
|
|
181
|
+
|
|
182
|
+
// eslint-disable-next-line
|
|
183
|
+
function createUpdateReferences (record) {
|
|
184
|
+
return async (newEntity) => {
|
|
185
|
+
const { entitySet, entity, originalEntity } = record
|
|
186
|
+
const linkedEntities = entityReferencesMap.get(entity)
|
|
187
|
+
|
|
188
|
+
if (linkedEntities.length === 0) {
|
|
189
|
+
return
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
for (const { properties: linkedProperties, entity: originalLinkedEntity } of linkedEntities) {
|
|
193
|
+
const currentNewLinkedEntity = originalEntitiesNewMap.get(originalLinkedEntity)
|
|
194
|
+
|
|
195
|
+
if (entityRecordNewValueMap.has(currentNewLinkedEntity)) {
|
|
196
|
+
const currentEntityProcessedNew = entityRecordNewValueMap.get(currentNewLinkedEntity)
|
|
197
|
+
const currentEntityUpdate = {}
|
|
198
|
+
|
|
199
|
+
// if we get here it means that the entity was already processed, so we need to
|
|
200
|
+
// execute an update directly to the store
|
|
201
|
+
for (const prop of linkedProperties) {
|
|
202
|
+
// here we care to use the new object result because we want to preserve other values
|
|
203
|
+
// in case the property is array with objects
|
|
204
|
+
reporter.documentStore.updateReference(originalLinkedEntity.__entitySet, currentEntityProcessedNew, entitySet, { referenceProp: prop, referenceValue: originalEntity.shortid }, newEntity.shortid)
|
|
205
|
+
const rootProp = prop.split('.')[0]
|
|
206
|
+
currentEntityUpdate[rootProp] = currentEntityProcessedNew[rootProp]
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await reporter.documentStore.collection(originalLinkedEntity.__entitySet).update({
|
|
210
|
+
_id: currentEntityProcessedNew._id
|
|
211
|
+
}, { $set: currentEntityUpdate }, req)
|
|
212
|
+
} else {
|
|
213
|
+
// here we care to update all properties to point to old reference value
|
|
214
|
+
reporter.documentStore.updateReference(originalLinkedEntity.__entitySet, currentNewLinkedEntity, entitySet, { referenceValue: originalEntity.shortid }, newEntity.shortid)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (targetUpdateReferences) {
|
|
221
|
+
const targetEntity = await reporter.documentStore.collection('folders').findOne({
|
|
222
|
+
shortid: target.shortid
|
|
223
|
+
}, req)
|
|
224
|
+
|
|
225
|
+
targetEntity.__entitySet = 'folders'
|
|
226
|
+
entitiesInHierarchyByCollection.folders = entitiesInHierarchyByCollection.folders || []
|
|
227
|
+
entitiesInHierarchyByCollection.folders.push(targetEntity)
|
|
228
|
+
originalEntitiesNewMap.set(targetEntity, targetEntity)
|
|
229
|
+
entityRecordNewValueMap.set(targetEntity, targetEntity)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const entity of entitiesInHierarchy) {
|
|
233
|
+
const newEntity = {
|
|
234
|
+
...omit(entity, ['_id', 'shortid', '__entitySet'])
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
let isSourceEntityItem
|
|
238
|
+
|
|
239
|
+
if (!onlyChildren) {
|
|
240
|
+
isSourceEntityItem = source.id === entity._id
|
|
241
|
+
} else {
|
|
242
|
+
isSourceEntityItem = rootChildren.find((e) => e._id === entity._id) != null
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isSourceEntityItem) {
|
|
246
|
+
if (target.shortid === null) {
|
|
247
|
+
newEntity.folder = null
|
|
248
|
+
} else {
|
|
249
|
+
newEntity.folder = {
|
|
250
|
+
shortid: target.shortid
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const entitySet = entity.__entitySet
|
|
256
|
+
newEntity.__entitySet = entitySet
|
|
257
|
+
|
|
258
|
+
const linkedEntities = reporter.documentStore.findLinkedEntitiesForReference(
|
|
259
|
+
entitiesInHierarchyByCollection,
|
|
260
|
+
entitySet,
|
|
261
|
+
entity.shortid
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
const record = {
|
|
265
|
+
entitySet,
|
|
266
|
+
originalEntity: entity,
|
|
267
|
+
entity: newEntity
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
record.updateReferences = createUpdateReferences(record)
|
|
271
|
+
|
|
272
|
+
originalEntitiesNewMap.set(entity, newEntity)
|
|
273
|
+
entityReferencesMap.set(newEntity, linkedEntities)
|
|
274
|
+
|
|
275
|
+
records.push(record)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const processNewEntity = async (entitySet, entity) => {
|
|
279
|
+
const newEntityFromStore = await reporter.documentStore.collection(entitySet).insert({
|
|
280
|
+
...omit(entity, ['__entitySet'])
|
|
281
|
+
}, req)
|
|
282
|
+
|
|
283
|
+
entityRecordNewValueMap.set(entity, newEntityFromStore)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
for (const record of records) {
|
|
287
|
+
try {
|
|
288
|
+
await processNewEntity(record.entitySet, record.entity)
|
|
289
|
+
} catch (e) {
|
|
290
|
+
if (e.code === 'DUPLICATED_ENTITY' && shouldReplace) {
|
|
291
|
+
// replacing is not supported when it generates a conflict with folder
|
|
292
|
+
if (e.existingEntityEntitySet === 'folders') {
|
|
293
|
+
throw e
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const removeFolderQ = target.shortid === null ? { folder: null } : { folder: { shortid: target.shortid } }
|
|
297
|
+
|
|
298
|
+
await reporter.documentStore.collection(e.existingEntityEntitySet).remove({
|
|
299
|
+
_id: e.existingEntity._id,
|
|
300
|
+
...removeFolderQ
|
|
301
|
+
}, req)
|
|
302
|
+
|
|
303
|
+
await processNewEntity(record.entitySet, record.entity)
|
|
304
|
+
} else {
|
|
305
|
+
throw e
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
await record.updateReferences(entityRecordNewValueMap.get(record.entity))
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
entitiesInHierarchy = records.map((record) => ({
|
|
313
|
+
...omit(entityRecordNewValueMap.get(record.entity), ['$entitySet', '__entitySet']),
|
|
314
|
+
__entitySet: record.entitySet
|
|
315
|
+
}))
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// return fresh version of creationDate, modificationDate in the records.
|
|
319
|
+
// this helps with concurrent validation on studio
|
|
320
|
+
await Promise.all(entitiesInHierarchy.map(async (entity) => {
|
|
321
|
+
const entitySet = reporter.documentStore.model.entitySets[entity.__entitySet]
|
|
322
|
+
const entityType = entitySet.entityTypeDef
|
|
323
|
+
const projection = {}
|
|
324
|
+
|
|
325
|
+
if (entityType.creationDate != null && entityType.creationDate.type === 'Edm.DateTimeOffset') {
|
|
326
|
+
projection.creationDate = 1
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (entityType.modificationDate != null && entityType.modificationDate.type === 'Edm.DateTimeOffset') {
|
|
330
|
+
projection.modificationDate = 1
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (Object.keys(projection).length === 0) {
|
|
334
|
+
return
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const doc = await reporter.documentStore.collection(entity.__entitySet).findOne({
|
|
338
|
+
_id: entity._id
|
|
339
|
+
}, {
|
|
340
|
+
creationDate: 1,
|
|
341
|
+
modificationDate: 1
|
|
342
|
+
}, req)
|
|
343
|
+
|
|
344
|
+
if (projection.creationDate) {
|
|
345
|
+
entity.creationDate = doc.creationDate
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (projection.modificationDate) {
|
|
349
|
+
entity.modificationDate = doc.modificationDate
|
|
350
|
+
}
|
|
351
|
+
}))
|
|
352
|
+
|
|
353
|
+
return entitiesInHierarchy
|
|
354
|
+
}
|