@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.
Files changed (79) hide show
  1. package/LICENSE +166 -166
  2. package/README.md +298 -284
  3. package/index.js +29 -27
  4. package/lib/main/blobStorage/blobStorage.js +52 -47
  5. package/lib/main/blobStorage/inMemoryProvider.js +27 -27
  6. package/lib/main/blobStorage/mainActions.js +24 -24
  7. package/lib/main/createDefaultLoggerFormat.js +17 -17
  8. package/lib/main/defaults.js +14 -14
  9. package/lib/main/extensions/discover.js +20 -20
  10. package/lib/main/extensions/extensionsManager.js +264 -265
  11. package/lib/main/extensions/fileUtils.js +56 -55
  12. package/lib/main/extensions/findVersion.js +49 -53
  13. package/lib/main/extensions/locationCache.js +103 -97
  14. package/lib/main/extensions/sorter.js +10 -10
  15. package/lib/main/extensions/validateMinimalVersion.js +50 -50
  16. package/lib/main/folders/cascadeFolderRemove.js +25 -25
  17. package/lib/main/folders/getEntitiesInFolder.js +53 -53
  18. package/lib/main/folders/index.js +42 -42
  19. package/lib/main/folders/moveBetweenFolders.js +354 -354
  20. package/lib/main/folders/validateDuplicatedName.js +107 -107
  21. package/lib/main/folders/validateReservedName.js +53 -53
  22. package/lib/main/logger.js +244 -244
  23. package/lib/main/migration/resourcesToAssets.js +230 -210
  24. package/lib/main/migration/xlsxTemplatesToAssets.js +128 -118
  25. package/lib/main/monitoring.js +91 -91
  26. package/lib/main/optionsLoad.js +237 -237
  27. package/lib/main/optionsSchema.js +237 -237
  28. package/lib/main/reporter.js +579 -578
  29. package/lib/main/schemaValidator.js +252 -252
  30. package/lib/main/settings.js +154 -154
  31. package/lib/main/store/checkDuplicatedId.js +27 -27
  32. package/lib/main/store/collection.js +329 -329
  33. package/lib/main/store/documentStore.js +469 -469
  34. package/lib/main/store/mainActions.js +28 -28
  35. package/lib/main/store/memoryStoreProvider.js +99 -99
  36. package/lib/main/store/queue.js +48 -48
  37. package/lib/main/store/referenceUtils.js +251 -251
  38. package/lib/main/store/setupValidateId.js +43 -43
  39. package/lib/main/store/setupValidateShortid.js +71 -71
  40. package/lib/main/store/transaction.js +69 -69
  41. package/lib/main/store/typeUtils.js +180 -180
  42. package/lib/main/templates.js +34 -34
  43. package/lib/main/validateEntityName.js +62 -62
  44. package/lib/shared/createError.js +36 -36
  45. package/lib/shared/encryption.js +114 -114
  46. package/lib/shared/folders/index.js +11 -11
  47. package/lib/shared/folders/normalizeEntityPath.js +15 -15
  48. package/lib/shared/folders/resolveEntityFromPath.js +88 -88
  49. package/lib/shared/folders/resolveEntityPath.js +46 -46
  50. package/lib/shared/folders/resolveFolderFromPath.js +38 -38
  51. package/lib/shared/generateRequestId.js +4 -4
  52. package/lib/shared/listenerCollection.js +169 -0
  53. package/lib/shared/normalizeMetaFromLogs.js +30 -30
  54. package/lib/shared/reporter.js +123 -123
  55. package/lib/shared/request.js +64 -64
  56. package/lib/shared/tempFilesHandler.js +81 -81
  57. package/lib/shared/templates.js +82 -82
  58. package/lib/static/helpers.js +33 -33
  59. package/lib/worker/blobStorage.js +34 -34
  60. package/lib/worker/defaultProxyExtend.js +46 -46
  61. package/lib/worker/documentStore.js +49 -49
  62. package/lib/worker/extensionsManager.js +17 -17
  63. package/lib/worker/logger.js +48 -48
  64. package/lib/worker/render/diff.js +138 -138
  65. package/lib/worker/render/executeEngine.js +207 -200
  66. package/lib/worker/render/htmlRecipe.js +10 -10
  67. package/lib/worker/render/moduleHelper.js +43 -43
  68. package/lib/worker/render/noneEngine.js +12 -12
  69. package/lib/worker/render/profiler.js +158 -158
  70. package/lib/worker/render/render.js +205 -209
  71. package/lib/worker/render/resolveReferences.js +60 -60
  72. package/lib/worker/reporter.js +191 -187
  73. package/lib/worker/sandbox/runInSandbox.js +13 -4
  74. package/lib/worker/sandbox/safeSandbox.js +828 -822
  75. package/lib/worker/templates.js +78 -78
  76. package/lib/worker/workerHandler.js +54 -54
  77. package/package.json +92 -92
  78. package/test/blobStorage/common.js +21 -21
  79. package/test/store/common.js +1449 -1449
@@ -1,329 +1,329 @@
1
- const ListenerCollection = require('listener-collection')
2
- const { resolvePropDefinition } = require('./typeUtils')
3
- const createError = require('../../shared/createError')
4
- const validateEntityName = require('../validateEntityName')
5
-
6
- module.exports = (entitySet, provider, model, validator, encryption, transactions) => ({
7
- name: entitySet,
8
- beforeFindListeners: new ListenerCollection(),
9
- beforeUpdateListeners: new ListenerCollection(),
10
- beforeInsertListeners: new ListenerCollection(),
11
- beforeRemoveListeners: new ListenerCollection(),
12
- beforeUpdateValidationListeners: new ListenerCollection(),
13
- beforeInsertValidationListeners: new ListenerCollection(),
14
- entitySet,
15
-
16
- load: (...args) => {
17
- provider.load(entitySet, ...args)
18
- },
19
-
20
- find (q, p, req) {
21
- if (p && p.__isJsreportRequest__ === true) {
22
- req = p
23
- p = {}
24
- }
25
-
26
- p = p || {}
27
-
28
- const listenerPromise = this.beforeFindListeners.fire(q, p, req)
29
-
30
- // the jsreport back-compatible API for find returns promise with the result array
31
- // the new API returns a cursor like mongo uses
32
- // to make it working for both way of calling, we return
33
- // an object which is a promise and in the same time a cursor
34
- const cursorCalls = []
35
- const functions = ['skip', 'limit', 'sort', 'toArray', 'count']
36
- const fakeCursor = {}
37
- functions.forEach((f) => {
38
- fakeCursor[f] = (...args) => {
39
- cursorCalls.push({ f: f, args: args })
40
- return fakeCursor
41
- }
42
- })
43
-
44
- const replay = (cursor) => {
45
- cursorCalls.filter((c) => c.f !== 'toArray' && c.f !== 'count').forEach((c) => cursor[c.f].apply(cursor, c.args))
46
-
47
- if (cursorCalls.find((c) => c.f === 'count')) {
48
- return cursor.count()
49
- }
50
-
51
- return cursor.toArray()
52
- }
53
-
54
- return Object.assign(fakeCursor, {
55
- then: (onFulfilled, onRejected) => {
56
- // the node A compatible promise expects then to be called with two functions
57
- let promise = listenerPromise.then(() => {
58
- return replay(provider.find(entitySet, q, p, {
59
- transaction: transactions.getActiveTransaction(req)
60
- }))
61
- })
62
-
63
- // the node A compatible promise expects then to be called with two functions
64
- if (typeof onFulfilled === 'function') {
65
- promise = promise.then(onFulfilled)
66
- }
67
-
68
- if (typeof onRejected === 'function') {
69
- promise = promise.catch(onRejected)
70
- }
71
-
72
- return promise
73
- }
74
- })
75
- },
76
-
77
- count (...args) {
78
- return this.find(...args).count()
79
- },
80
-
81
- async insert (data, req) {
82
- await this.beforeInsertValidationListeners.fire(data, req)
83
-
84
- // internal entities are not in the model
85
- if (model.entitySets[entitySet] && model.entitySets[entitySet].entityTypeDef.name) {
86
- validateEntityName(data.name)
87
- }
88
-
89
- const entityType = model.entitySets[entitySet] ? model.entitySets[entitySet].normalizedEntityTypeName : null
90
-
91
- if (entityType != null && validator.getSchema(entityType) != null) {
92
- const validationResult = validator.validate(entityType, data)
93
-
94
- if (!validationResult.valid) {
95
- throw createError(`Error when trying to insert into "${entitySet}" collection. input contain values that does not match the schema. ${validationResult.fullErrorMessage}`, {
96
- weak: true,
97
- statusCode: 400
98
- })
99
- }
100
- }
101
-
102
- await this.beforeInsertListeners.fire(data, req)
103
-
104
- return provider.insert(entitySet, data, {
105
- transaction: transactions.getActiveTransaction(req)
106
- })
107
- },
108
-
109
- async update (q, u, o, req) {
110
- if (o && o.__isJsreportRequest__) {
111
- req = o
112
- o = {}
113
- }
114
-
115
- await this.beforeUpdateValidationListeners.fire(q, u, o, req)
116
-
117
- // internal entities are not in the model
118
- if (model.entitySets[entitySet] && model.entitySets[entitySet].entityTypeDef.name && u.$set.name !== undefined) {
119
- validateEntityName(u.$set.name)
120
- }
121
-
122
- const entityType = model.entitySets[entitySet] ? model.entitySets[entitySet].normalizedEntityTypeName : null
123
-
124
- if (entityType != null && validator.getSchema(entityType) != null) {
125
- const validationResult = validator.validate(entityType, u.$set)
126
-
127
- if (!validationResult.valid) {
128
- throw createError(`Error when trying to update "${entitySet}" collection. input contain values that does not match the schema. ${validationResult.fullErrorMessage}`, {
129
- weak: true,
130
- statusCode: 400
131
- })
132
- }
133
- }
134
-
135
- await this.beforeUpdateListeners.fire(q, u, o, req)
136
-
137
- const updateOpts = Object.assign({}, o)
138
- const activeTran = transactions.getActiveTransaction(req)
139
-
140
- if (activeTran) {
141
- updateOpts.transaction = activeTran
142
- }
143
-
144
- return provider.update(entitySet, q, u, updateOpts)
145
- },
146
-
147
- async remove (q, req) {
148
- await this.beforeRemoveListeners.fire(q, req)
149
-
150
- return provider.remove(entitySet, q, {
151
- transaction: transactions.getActiveTransaction(req)
152
- })
153
- },
154
-
155
- async findOne (...args) {
156
- const res = await this.find(...args)
157
- if (res.length > 0) {
158
- return res[0]
159
- }
160
-
161
- return null
162
- },
163
-
164
- async serializeProperties (docs, customTypeDef) {
165
- let typeDef
166
-
167
- if (customTypeDef == null) {
168
- const entitySetInfo = model.entitySets[entitySet]
169
- const entityType = entitySetInfo.entityTypeDef
170
- typeDef = entityType
171
- } else {
172
- typeDef = customTypeDef
173
- }
174
-
175
- if (!typeDef) {
176
- return
177
- }
178
-
179
- return Promise.all(docs.map(async (doc) => {
180
- if (doc == null) {
181
- return doc
182
- }
183
-
184
- const newDoc = Object.assign({}, doc)
185
-
186
- for (const prop in newDoc) {
187
- if (!prop) {
188
- continue
189
- }
190
-
191
- const propDef = typeDef[prop]
192
-
193
- if (!propDef || propDef.type == null) {
194
- continue
195
- }
196
-
197
- const resolveResult = resolvePropDefinition(model, propDef)
198
-
199
- if (!resolveResult) {
200
- continue
201
- }
202
-
203
- const isCollection = resolveResult.def.type.startsWith('Collection(')
204
-
205
- if (propDef.encrypted === true) {
206
- if (isCollection && resolveResult.subType == null) {
207
- if (Array.isArray(newDoc[prop])) {
208
- for (const [idx, value] of Object.entries(newDoc[prop])) {
209
- newDoc[prop][idx] = await encryption.decrypt(value)
210
- }
211
- }
212
- } else if (resolveResult.def.type.startsWith('Edm')) {
213
- newDoc[prop] = await encryption.decrypt(newDoc[prop])
214
- }
215
- }
216
-
217
- if (
218
- isCollection ||
219
- resolveResult.subType
220
- ) {
221
- if (isCollection) {
222
- if (!Array.isArray(newDoc[prop])) {
223
- continue
224
- }
225
-
226
- if (resolveResult.subType) {
227
- const result = await this.serializeProperties(newDoc[prop], resolveResult.subType)
228
- newDoc[prop] = result
229
- }
230
- } else {
231
- const result = await this.serializeProperties([newDoc[prop]], resolveResult.subType)
232
- newDoc[prop] = result[0]
233
- }
234
- } else if (propDef.type === 'Edm.Binary') {
235
- newDoc[prop] = Buffer.from(newDoc[prop]).toString('base64') // eslint-disable-line
236
- }
237
- }
238
-
239
- return newDoc
240
- }))
241
- },
242
-
243
- async deserializeProperties (docs, customTypeDef) {
244
- let typeDef
245
-
246
- if (customTypeDef == null) {
247
- const entitySetInfo = model.entitySets[entitySet]
248
-
249
- if (!entitySetInfo) {
250
- return []
251
- }
252
-
253
- const entityType = entitySetInfo.entityTypeDef
254
-
255
- if (!entityType) {
256
- return []
257
- }
258
-
259
- typeDef = entityType
260
- } else {
261
- typeDef = customTypeDef
262
- }
263
-
264
- return Promise.all(docs.map(async (doc) => {
265
- if (doc == null) {
266
- return doc
267
- }
268
-
269
- const newDoc = Object.assign({}, doc)
270
-
271
- for (const prop in newDoc) {
272
- if (!prop) {
273
- continue
274
- }
275
-
276
- const propDef = typeDef[prop]
277
-
278
- if (!propDef || propDef.type == null) {
279
- continue
280
- }
281
-
282
- const resolveResult = resolvePropDefinition(model, propDef)
283
-
284
- if (!resolveResult) {
285
- continue
286
- }
287
-
288
- const isCollection = resolveResult.def.type.startsWith('Collection(')
289
-
290
- if (
291
- isCollection ||
292
- resolveResult.subType
293
- ) {
294
- if (isCollection) {
295
- if (!Array.isArray(newDoc[prop])) {
296
- continue
297
- }
298
-
299
- if (resolveResult.subType) {
300
- const result = await this.deserializeProperties(newDoc[prop], resolveResult.subType)
301
- newDoc[prop] = result
302
- }
303
- } else {
304
- const result = await this.deserializeProperties([newDoc[prop]], resolveResult.subType)
305
- newDoc[prop] = result[0]
306
- }
307
- } else if (propDef.type === 'Edm.Binary') {
308
- newDoc[prop] = Buffer.from(newDoc[prop], 'base64') // eslint-disable-line
309
- } else if (propDef.type === 'Edm.DateTimeOffset') {
310
- newDoc[prop] = new Date(newDoc[prop])
311
- }
312
-
313
- if (propDef.encrypted === true) {
314
- if (isCollection && resolveResult.subType == null) {
315
- if (Array.isArray(newDoc[prop])) {
316
- for (const [idx, value] of Object.entries(newDoc[prop])) {
317
- newDoc[prop][idx] = await encryption.encrypt(value)
318
- }
319
- }
320
- } else if (resolveResult.def.type.startsWith('Edm')) {
321
- newDoc[prop] = await encryption.encrypt(newDoc[prop])
322
- }
323
- }
324
- }
325
-
326
- return newDoc
327
- }))
328
- }
329
- })
1
+ const createListenerCollection = require('../../shared/listenerCollection')
2
+ const { resolvePropDefinition } = require('./typeUtils')
3
+ const createError = require('../../shared/createError')
4
+ const validateEntityName = require('../validateEntityName')
5
+
6
+ module.exports = (entitySet, provider, model, validator, encryption, transactions) => ({
7
+ name: entitySet,
8
+ beforeFindListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeFind`),
9
+ beforeUpdateListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeUpdate`),
10
+ beforeInsertListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeInsert`),
11
+ beforeRemoveListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeRemove`),
12
+ beforeUpdateValidationListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeUpdateValidation`),
13
+ beforeInsertValidationListeners: createListenerCollection(`DocumentStoreCollection(${entitySet})@beforeInsertValidation`),
14
+ entitySet,
15
+
16
+ load: (...args) => {
17
+ provider.load(entitySet, ...args)
18
+ },
19
+
20
+ find (q, p, req) {
21
+ if (p && p.__isJsreportRequest__ === true) {
22
+ req = p
23
+ p = {}
24
+ }
25
+
26
+ p = p || {}
27
+
28
+ const listenerPromise = this.beforeFindListeners.fire(q, p, req)
29
+
30
+ // the jsreport back-compatible API for find returns promise with the result array
31
+ // the new API returns a cursor like mongo uses
32
+ // to make it working for both way of calling, we return
33
+ // an object which is a promise and in the same time a cursor
34
+ const cursorCalls = []
35
+ const functions = ['skip', 'limit', 'sort', 'toArray', 'count']
36
+ const fakeCursor = {}
37
+ functions.forEach((f) => {
38
+ fakeCursor[f] = (...args) => {
39
+ cursorCalls.push({ f: f, args: args })
40
+ return fakeCursor
41
+ }
42
+ })
43
+
44
+ const replay = (cursor) => {
45
+ cursorCalls.filter((c) => c.f !== 'toArray' && c.f !== 'count').forEach((c) => cursor[c.f].apply(cursor, c.args))
46
+
47
+ if (cursorCalls.find((c) => c.f === 'count')) {
48
+ return cursor.count()
49
+ }
50
+
51
+ return cursor.toArray()
52
+ }
53
+
54
+ return Object.assign(fakeCursor, {
55
+ then: (onFulfilled, onRejected) => {
56
+ // the node A compatible promise expects then to be called with two functions
57
+ let promise = listenerPromise.then(() => {
58
+ return replay(provider.find(entitySet, q, p, {
59
+ transaction: transactions.getActiveTransaction(req)
60
+ }))
61
+ })
62
+
63
+ // the node A compatible promise expects then to be called with two functions
64
+ if (typeof onFulfilled === 'function') {
65
+ promise = promise.then(onFulfilled)
66
+ }
67
+
68
+ if (typeof onRejected === 'function') {
69
+ promise = promise.catch(onRejected)
70
+ }
71
+
72
+ return promise
73
+ }
74
+ })
75
+ },
76
+
77
+ count (...args) {
78
+ return this.find(...args).count()
79
+ },
80
+
81
+ async insert (data, req) {
82
+ await this.beforeInsertValidationListeners.fire(data, req)
83
+
84
+ // internal entities are not in the model
85
+ if (model.entitySets[entitySet] && model.entitySets[entitySet].entityTypeDef.name) {
86
+ validateEntityName(data.name)
87
+ }
88
+
89
+ const entityType = model.entitySets[entitySet] ? model.entitySets[entitySet].normalizedEntityTypeName : null
90
+
91
+ if (entityType != null && validator.getSchema(entityType) != null) {
92
+ const validationResult = validator.validate(entityType, data)
93
+
94
+ if (!validationResult.valid) {
95
+ throw createError(`Error when trying to insert into "${entitySet}" collection. input contain values that does not match the schema. ${validationResult.fullErrorMessage}`, {
96
+ weak: true,
97
+ statusCode: 400
98
+ })
99
+ }
100
+ }
101
+
102
+ await this.beforeInsertListeners.fire(data, req)
103
+
104
+ return provider.insert(entitySet, data, {
105
+ transaction: transactions.getActiveTransaction(req)
106
+ })
107
+ },
108
+
109
+ async update (q, u, o, req) {
110
+ if (o && o.__isJsreportRequest__) {
111
+ req = o
112
+ o = {}
113
+ }
114
+
115
+ await this.beforeUpdateValidationListeners.fire(q, u, o, req)
116
+
117
+ // internal entities are not in the model
118
+ if (model.entitySets[entitySet] && model.entitySets[entitySet].entityTypeDef.name && u.$set.name !== undefined) {
119
+ validateEntityName(u.$set.name)
120
+ }
121
+
122
+ const entityType = model.entitySets[entitySet] ? model.entitySets[entitySet].normalizedEntityTypeName : null
123
+
124
+ if (entityType != null && validator.getSchema(entityType) != null) {
125
+ const validationResult = validator.validate(entityType, u.$set)
126
+
127
+ if (!validationResult.valid) {
128
+ throw createError(`Error when trying to update "${entitySet}" collection. input contain values that does not match the schema. ${validationResult.fullErrorMessage}`, {
129
+ weak: true,
130
+ statusCode: 400
131
+ })
132
+ }
133
+ }
134
+
135
+ await this.beforeUpdateListeners.fire(q, u, o, req)
136
+
137
+ const updateOpts = Object.assign({}, o)
138
+ const activeTran = transactions.getActiveTransaction(req)
139
+
140
+ if (activeTran) {
141
+ updateOpts.transaction = activeTran
142
+ }
143
+
144
+ return provider.update(entitySet, q, u, updateOpts)
145
+ },
146
+
147
+ async remove (q, req) {
148
+ await this.beforeRemoveListeners.fire(q, req)
149
+
150
+ return provider.remove(entitySet, q, {
151
+ transaction: transactions.getActiveTransaction(req)
152
+ })
153
+ },
154
+
155
+ async findOne (...args) {
156
+ const res = await this.find(...args)
157
+ if (res.length > 0) {
158
+ return res[0]
159
+ }
160
+
161
+ return null
162
+ },
163
+
164
+ async serializeProperties (docs, customTypeDef) {
165
+ let typeDef
166
+
167
+ if (customTypeDef == null) {
168
+ const entitySetInfo = model.entitySets[entitySet]
169
+ const entityType = entitySetInfo.entityTypeDef
170
+ typeDef = entityType
171
+ } else {
172
+ typeDef = customTypeDef
173
+ }
174
+
175
+ if (!typeDef) {
176
+ return
177
+ }
178
+
179
+ return Promise.all(docs.map(async (doc) => {
180
+ if (doc == null) {
181
+ return doc
182
+ }
183
+
184
+ const newDoc = Object.assign({}, doc)
185
+
186
+ for (const prop in newDoc) {
187
+ if (!prop) {
188
+ continue
189
+ }
190
+
191
+ const propDef = typeDef[prop]
192
+
193
+ if (!propDef || propDef.type == null) {
194
+ continue
195
+ }
196
+
197
+ const resolveResult = resolvePropDefinition(model, propDef)
198
+
199
+ if (!resolveResult) {
200
+ continue
201
+ }
202
+
203
+ const isCollection = resolveResult.def.type.startsWith('Collection(')
204
+
205
+ if (propDef.encrypted === true) {
206
+ if (isCollection && resolveResult.subType == null) {
207
+ if (Array.isArray(newDoc[prop])) {
208
+ for (const [idx, value] of Object.entries(newDoc[prop])) {
209
+ newDoc[prop][idx] = await encryption.decrypt(value)
210
+ }
211
+ }
212
+ } else if (resolveResult.def.type.startsWith('Edm')) {
213
+ newDoc[prop] = await encryption.decrypt(newDoc[prop])
214
+ }
215
+ }
216
+
217
+ if (
218
+ isCollection ||
219
+ resolveResult.subType
220
+ ) {
221
+ if (isCollection) {
222
+ if (!Array.isArray(newDoc[prop])) {
223
+ continue
224
+ }
225
+
226
+ if (resolveResult.subType) {
227
+ const result = await this.serializeProperties(newDoc[prop], resolveResult.subType)
228
+ newDoc[prop] = result
229
+ }
230
+ } else {
231
+ const result = await this.serializeProperties([newDoc[prop]], resolveResult.subType)
232
+ newDoc[prop] = result[0]
233
+ }
234
+ } else if (propDef.type === 'Edm.Binary') {
235
+ newDoc[prop] = Buffer.from(newDoc[prop]).toString('base64') // eslint-disable-line
236
+ }
237
+ }
238
+
239
+ return newDoc
240
+ }))
241
+ },
242
+
243
+ async deserializeProperties (docs, customTypeDef) {
244
+ let typeDef
245
+
246
+ if (customTypeDef == null) {
247
+ const entitySetInfo = model.entitySets[entitySet]
248
+
249
+ if (!entitySetInfo) {
250
+ return []
251
+ }
252
+
253
+ const entityType = entitySetInfo.entityTypeDef
254
+
255
+ if (!entityType) {
256
+ return []
257
+ }
258
+
259
+ typeDef = entityType
260
+ } else {
261
+ typeDef = customTypeDef
262
+ }
263
+
264
+ return Promise.all(docs.map(async (doc) => {
265
+ if (doc == null) {
266
+ return doc
267
+ }
268
+
269
+ const newDoc = Object.assign({}, doc)
270
+
271
+ for (const prop in newDoc) {
272
+ if (!prop) {
273
+ continue
274
+ }
275
+
276
+ const propDef = typeDef[prop]
277
+
278
+ if (!propDef || propDef.type == null) {
279
+ continue
280
+ }
281
+
282
+ const resolveResult = resolvePropDefinition(model, propDef)
283
+
284
+ if (!resolveResult) {
285
+ continue
286
+ }
287
+
288
+ const isCollection = resolveResult.def.type.startsWith('Collection(')
289
+
290
+ if (
291
+ isCollection ||
292
+ resolveResult.subType
293
+ ) {
294
+ if (isCollection) {
295
+ if (!Array.isArray(newDoc[prop])) {
296
+ continue
297
+ }
298
+
299
+ if (resolveResult.subType) {
300
+ const result = await this.deserializeProperties(newDoc[prop], resolveResult.subType)
301
+ newDoc[prop] = result
302
+ }
303
+ } else {
304
+ const result = await this.deserializeProperties([newDoc[prop]], resolveResult.subType)
305
+ newDoc[prop] = result[0]
306
+ }
307
+ } else if (propDef.type === 'Edm.Binary') {
308
+ newDoc[prop] = Buffer.from(newDoc[prop], 'base64') // eslint-disable-line
309
+ } else if (propDef.type === 'Edm.DateTimeOffset') {
310
+ newDoc[prop] = new Date(newDoc[prop])
311
+ }
312
+
313
+ if (propDef.encrypted === true) {
314
+ if (isCollection && resolveResult.subType == null) {
315
+ if (Array.isArray(newDoc[prop])) {
316
+ for (const [idx, value] of Object.entries(newDoc[prop])) {
317
+ newDoc[prop][idx] = await encryption.encrypt(value)
318
+ }
319
+ }
320
+ } else if (resolveResult.def.type.startsWith('Edm')) {
321
+ newDoc[prop] = await encryption.encrypt(newDoc[prop])
322
+ }
323
+ }
324
+ }
325
+
326
+ return newDoc
327
+ }))
328
+ }
329
+ })