@restorecommerce/resource-base-interface 1.6.5 → 1.7.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/lib/core/GraphResourcesServiceBase.d.ts +9 -5
- package/lib/core/GraphResourcesServiceBase.d.ts.map +1 -0
- package/lib/core/GraphResourcesServiceBase.js +69 -100
- package/lib/core/GraphResourcesServiceBase.js.map +1 -1
- package/lib/core/ResourcesAPI.d.ts +26 -19
- package/lib/core/ResourcesAPI.d.ts.map +1 -0
- package/lib/core/ResourcesAPI.js +273 -388
- package/lib/core/ResourcesAPI.js.map +1 -1
- package/lib/core/ServiceBase.d.ts +29 -10
- package/lib/core/ServiceBase.d.ts.map +1 -0
- package/lib/core/ServiceBase.js +137 -242
- package/lib/core/ServiceBase.js.map +1 -1
- package/lib/core/index.d.ts +5 -0
- package/lib/core/index.d.ts.map +1 -0
- package/lib/core/index.js +21 -0
- package/lib/core/index.js.map +1 -0
- package/lib/core/interfaces.d.ts +6 -72
- package/lib/core/interfaces.d.ts.map +1 -0
- package/lib/core/interfaces.js +18 -31
- package/lib/core/interfaces.js.map +1 -1
- package/lib/core/utils.d.ts +3 -1
- package/lib/core/utils.d.ts.map +1 -0
- package/lib/core/utils.js +22 -16
- package/lib/core/utils.js.map +1 -1
- package/lib/experimental/AccessControlledServiceBase.d.ts +30 -0
- package/lib/experimental/AccessControlledServiceBase.d.ts.map +1 -0
- package/lib/experimental/AccessControlledServiceBase.js +232 -0
- package/lib/experimental/AccessControlledServiceBase.js.map +1 -0
- package/lib/experimental/ClientRegister.d.ts +22 -0
- package/lib/experimental/ClientRegister.d.ts.map +1 -0
- package/lib/experimental/ClientRegister.js +33 -0
- package/lib/experimental/ClientRegister.js.map +1 -0
- package/lib/experimental/Pipe.d.ts +6 -0
- package/lib/experimental/Pipe.d.ts.map +1 -0
- package/lib/experimental/Pipe.js +14 -0
- package/lib/experimental/Pipe.js.map +1 -0
- package/lib/experimental/ResourceAggregator.d.ts +35 -0
- package/lib/experimental/ResourceAggregator.d.ts.map +1 -0
- package/lib/experimental/ResourceAggregator.js +94 -0
- package/lib/experimental/ResourceAggregator.js.map +1 -0
- package/lib/experimental/ResourceAwaitQueue.d.ts +12 -0
- package/lib/experimental/ResourceAwaitQueue.d.ts.map +1 -0
- package/lib/experimental/ResourceAwaitQueue.js +34 -0
- package/lib/experimental/ResourceAwaitQueue.js.map +1 -0
- package/lib/experimental/ResourceMap.d.ts +16 -0
- package/lib/experimental/ResourceMap.d.ts.map +1 -0
- package/lib/experimental/ResourceMap.js +51 -0
- package/lib/experimental/ResourceMap.js.map +1 -0
- package/lib/{core → experimental}/WorkerBase.d.ts +10 -14
- package/lib/experimental/WorkerBase.d.ts.map +1 -0
- package/lib/{core → experimental}/WorkerBase.js +44 -30
- package/lib/experimental/WorkerBase.js.map +1 -0
- package/lib/experimental/index.d.ts +7 -0
- package/lib/experimental/index.d.ts.map +1 -0
- package/lib/experimental/index.js +23 -0
- package/lib/experimental/index.js.map +1 -0
- package/lib/index.d.ts +5 -7
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +81 -141
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +22 -20
- package/tsconfig.json +11 -25
- package/lib/core/WorkerBase.js.map +0 -1
package/lib/core/ResourcesAPI.js
CHANGED
|
@@ -1,104 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
3
|
exports.ResourcesAPIBase = void 0;
|
|
27
|
-
const
|
|
28
|
-
const chassis_srv_1 = require("@restorecommerce/chassis-srv");
|
|
29
|
-
const uuid = __importStar(require("uuid"));
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
30
5
|
const utils_1 = require("./utils");
|
|
31
|
-
let redisClient;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
};
|
|
41
|
-
const isEmptyObject = (obj) => {
|
|
42
|
-
return !Object.keys(obj).length;
|
|
43
|
-
};
|
|
44
|
-
const setDefaults = async (obj, collectionName, subject) => {
|
|
45
|
-
const o = obj;
|
|
46
|
-
if (_.isEmpty(o.meta)) {
|
|
47
|
-
throw new chassis_srv_1.errors.InvalidArgument('Object does not contain ownership information');
|
|
48
|
-
}
|
|
49
|
-
if (redisClient) {
|
|
50
|
-
const values = await redisClient.hGetAll(collectionName);
|
|
51
|
-
if (values) {
|
|
52
|
-
for (const field in values) {
|
|
53
|
-
const strategy = values[field];
|
|
54
|
-
let key;
|
|
55
|
-
switch (strategy) {
|
|
56
|
-
case Strategies.INCREMENT:
|
|
57
|
-
key = collectionName + ':' + field;
|
|
58
|
-
o[field] = await redisClient.get(key);
|
|
59
|
-
await redisClient.incr(key);
|
|
60
|
-
break;
|
|
61
|
-
case Strategies.UUID:
|
|
62
|
-
o[field] = uuidGen();
|
|
63
|
-
break;
|
|
64
|
-
case Strategies.RANDOM:
|
|
65
|
-
o[field] = uuidGen();
|
|
66
|
-
break;
|
|
67
|
-
case Strategies.TIMESTAMP:
|
|
68
|
-
o[field] = await redisClient.time()[0];
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (_.isNil(o.meta.created) || o.meta.created === 0) {
|
|
75
|
-
o.meta.created = new Date();
|
|
76
|
-
}
|
|
77
|
-
o.meta.created_by = subject?.id;
|
|
78
|
-
o.meta.modified_by = subject?.id;
|
|
79
|
-
o.meta.modified = new Date();
|
|
80
|
-
if (_.isNil(o.id) || o.id === 0 || isEmptyObject(o.id)) {
|
|
81
|
-
o.id = uuidGen();
|
|
82
|
-
}
|
|
83
|
-
return o;
|
|
84
|
-
};
|
|
85
|
-
const updateMetadata = (docMeta, newDoc, subject) => {
|
|
86
|
-
if (_.isEmpty(newDoc.meta)) {
|
|
87
|
-
throw new chassis_srv_1.errors.InvalidArgument(`Update request holds no valid metadata for document ${newDoc.id}`);
|
|
88
|
-
}
|
|
89
|
-
if (!_.isEmpty(newDoc.meta?.owners)) {
|
|
90
|
-
// if ownership is meant to be updated
|
|
91
|
-
docMeta.owners = newDoc.meta.owners;
|
|
92
|
-
}
|
|
93
|
-
docMeta.modified_by = subject?.id;
|
|
94
|
-
docMeta.modified = new Date();
|
|
95
|
-
newDoc.meta = docMeta;
|
|
96
|
-
return newDoc;
|
|
97
|
-
};
|
|
6
|
+
// let redisClient: any;
|
|
7
|
+
var Strategies;
|
|
8
|
+
(function (Strategies) {
|
|
9
|
+
Strategies["INCREMENT"] = "increment";
|
|
10
|
+
Strategies["UUID"] = "uuid";
|
|
11
|
+
Strategies["RANDOM"] = "random";
|
|
12
|
+
Strategies["TIMESTAMP"] = "timestamp";
|
|
13
|
+
})(Strategies || (Strategies = {}));
|
|
14
|
+
const uuidGen = () => (0, crypto_1.randomUUID)().replace(/-/g, '');
|
|
98
15
|
/**
|
|
99
16
|
* Resource API base provides functions for CRUD operations.
|
|
100
17
|
*/
|
|
101
18
|
class ResourcesAPIBase {
|
|
19
|
+
db;
|
|
20
|
+
collectionName;
|
|
21
|
+
edgeCfg;
|
|
22
|
+
graphName;
|
|
23
|
+
logger;
|
|
24
|
+
resourceName;
|
|
25
|
+
bufferFields;
|
|
26
|
+
requiredFields;
|
|
27
|
+
timeStampFields;
|
|
28
|
+
redisClient;
|
|
102
29
|
/**
|
|
103
30
|
* @constructor
|
|
104
31
|
* @param {object} db Chassis arangodb provider.
|
|
@@ -116,29 +43,20 @@ class ResourcesAPIBase {
|
|
|
116
43
|
if (!fieldHandlerConf) {
|
|
117
44
|
return;
|
|
118
45
|
}
|
|
119
|
-
const strategyCfg = fieldHandlerConf
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.bufferFields = fieldHandlerConf.bufferFields;
|
|
125
|
-
}
|
|
126
|
-
// config fix to be removed after ts-proto is used
|
|
127
|
-
if (fieldHandlerConf.timeStampFields) {
|
|
128
|
-
this.timeStampFields = fieldHandlerConf.timeStampFields;
|
|
129
|
-
}
|
|
130
|
-
if (fieldHandlerConf.requiredFields) {
|
|
131
|
-
this.requiredFields = fieldHandlerConf.requiredFields[this.resourceName] ?? fieldHandlerConf.requiredFields;
|
|
132
|
-
}
|
|
46
|
+
const strategyCfg = fieldHandlerConf?.strategies ?? [];
|
|
47
|
+
this.redisClient = fieldHandlerConf?.redisClient;
|
|
48
|
+
this.bufferFields = fieldHandlerConf?.bufferFields;
|
|
49
|
+
this.timeStampFields = fieldHandlerConf?.timeStampFields;
|
|
50
|
+
this.requiredFields = fieldHandlerConf?.requiredFields?.[this.resourceName] ?? fieldHandlerConf?.requiredFields;
|
|
133
51
|
// values for Redis hash set
|
|
134
52
|
for (const field in strategyCfg) {
|
|
135
53
|
const strategy = strategyCfg[field].strategy;
|
|
136
|
-
redisClient.hSet(collectionName, field, strategy);
|
|
137
|
-
let startingValue;
|
|
54
|
+
this.redisClient.hSet(collectionName, field, strategy);
|
|
138
55
|
switch (strategy) {
|
|
139
|
-
case Strategies.INCREMENT:
|
|
56
|
+
case Strategies.INCREMENT: {
|
|
140
57
|
// check if value already exists in redis
|
|
141
|
-
startingValue
|
|
58
|
+
let startingValue;
|
|
59
|
+
startingValue = this.redisClient.get(`${collectionName}:${field}`).then((val) => val);
|
|
142
60
|
if (!startingValue) {
|
|
143
61
|
if (strategyCfg[field].startingValue) {
|
|
144
62
|
startingValue = Number.isNaN(strategyCfg[field].startingValue) ?
|
|
@@ -147,42 +65,91 @@ class ResourcesAPIBase {
|
|
|
147
65
|
else {
|
|
148
66
|
startingValue = '0';
|
|
149
67
|
}
|
|
150
|
-
redisClient.set(`${collectionName}:${field}`, startingValue).then((val) => val);
|
|
68
|
+
this.redisClient.set(`${collectionName}:${field}`, startingValue).then((val) => val);
|
|
151
69
|
}
|
|
152
70
|
break;
|
|
71
|
+
}
|
|
153
72
|
default:
|
|
154
73
|
break;
|
|
155
74
|
}
|
|
156
75
|
}
|
|
157
76
|
}
|
|
77
|
+
catchOperationError(msg, err) {
|
|
78
|
+
this.logger?.error(msg, err);
|
|
79
|
+
return {
|
|
80
|
+
code: Number.isInteger(err.code) ? err.code : 500,
|
|
81
|
+
message: err.message ?? 'Unknown Error!',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catchStatusError(msg, err) {
|
|
85
|
+
this.logger?.error(msg, err);
|
|
86
|
+
return {
|
|
87
|
+
code: Number.isInteger(err.code) ? err.code : 500,
|
|
88
|
+
message: err.message ?? 'Unknown Error!',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
setMeta(o, subject, create = false) {
|
|
92
|
+
o.meta ??= {};
|
|
93
|
+
if (create) {
|
|
94
|
+
o.meta.created ??= new Date();
|
|
95
|
+
o.meta.created_by ??= subject?.id;
|
|
96
|
+
}
|
|
97
|
+
o.meta.modified_by ??= subject?.id;
|
|
98
|
+
o.meta.modified ??= new Date();
|
|
99
|
+
if (!o.id?.length || o.id?.toString() === '0') {
|
|
100
|
+
o.id = uuidGen();
|
|
101
|
+
}
|
|
102
|
+
return o;
|
|
103
|
+
}
|
|
104
|
+
async setDefaults(o, collectionName, subject, create = false) {
|
|
105
|
+
if (create && this.redisClient) {
|
|
106
|
+
const values = await this.redisClient.hGetAll(collectionName);
|
|
107
|
+
if (values) {
|
|
108
|
+
for (const field in values) {
|
|
109
|
+
const strategy = values[field];
|
|
110
|
+
switch (strategy) {
|
|
111
|
+
case Strategies.INCREMENT: {
|
|
112
|
+
const key = `${collectionName}:${field}`;
|
|
113
|
+
o[field] = await this.redisClient.get(key);
|
|
114
|
+
await this.redisClient.incr(key);
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case Strategies.TIMESTAMP:
|
|
118
|
+
o[field] = (await this.redisClient.time()).getTime();
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
case Strategies.UUID:
|
|
122
|
+
case Strategies.RANDOM:
|
|
123
|
+
o[field] = uuidGen();
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return this.setMeta(o, subject, create);
|
|
130
|
+
}
|
|
158
131
|
/**
|
|
159
132
|
* Finds documents based on provided filters and options
|
|
160
133
|
* @param {object} filter key value filter using mongodb/nedb filter format.
|
|
161
134
|
* @param {number} limit
|
|
162
135
|
* @param {number} offset
|
|
163
136
|
* @param {object} sort key value, key=field value: 1=ASCENDING, -1=DESCENDING, 0=UNSORTED
|
|
164
|
-
* @param {object}
|
|
137
|
+
* @param {object} fields key value, key=field value: 0=exclude, 1=include
|
|
165
138
|
* @returns {an Object that contains an items field}
|
|
166
139
|
*/
|
|
167
|
-
async read(filter = {}, limit = 1000, offset = 0, sort = {},
|
|
140
|
+
async read(filter = {}, limit = 1000, offset = 0, sort = {}, fields = {}, customQueries = [], customArgs = {}, search) {
|
|
168
141
|
const options = {
|
|
169
142
|
limit: Math.min(limit, 1000),
|
|
170
143
|
offset,
|
|
171
144
|
sort,
|
|
172
|
-
fields
|
|
145
|
+
fields,
|
|
173
146
|
customQueries,
|
|
174
|
-
customArguments: customArgs
|
|
147
|
+
customArguments: customArgs?.value ? JSON.parse(customArgs.value.toString()) : {},
|
|
175
148
|
search
|
|
176
149
|
};
|
|
177
150
|
let entities = await this.db.find(this.collectionName, filter, options);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
entities = this.encodeOrDecode(entities, this.bufferFields, 'encode');
|
|
181
|
-
}
|
|
182
|
-
if (this.timeStampFields && entities?.length > 0) {
|
|
183
|
-
// convert number to Date Object
|
|
184
|
-
entities = this.encodeOrDecode(entities, this.timeStampFields, 'convertMilisecToDateObj');
|
|
185
|
-
}
|
|
151
|
+
entities = this.encodeOrDecode(entities, this.bufferFields, 'encode');
|
|
152
|
+
entities = this.encodeOrDecode(entities, this.timeStampFields, 'convertMilisecToDateObj');
|
|
186
153
|
return entities;
|
|
187
154
|
}
|
|
188
155
|
/**
|
|
@@ -190,95 +157,68 @@ class ResourcesAPIBase {
|
|
|
190
157
|
*
|
|
191
158
|
* @param {array.object} documents
|
|
192
159
|
*/
|
|
193
|
-
async create(documents, subject) {
|
|
160
|
+
async create(documents, subject, events) {
|
|
194
161
|
const collection = this.collectionName;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
const toIDkey = eachEdgeCfg.to;
|
|
223
|
-
const to_id = document[toIDkey];
|
|
224
|
-
// edges are created outbound, if it is inbound - check for direction
|
|
225
|
-
const direction = eachEdgeCfg.direction;
|
|
226
|
-
let fromVerticeName = collection;
|
|
227
|
-
let toVerticeName = eachEdgeCfg.toVerticeName;
|
|
228
|
-
if (direction === 'inbound') {
|
|
229
|
-
fromVerticeName = eachEdgeCfg.fromVerticeName;
|
|
230
|
-
toVerticeName = collection;
|
|
231
|
-
}
|
|
232
|
-
if (from_id && to_id) {
|
|
233
|
-
if (Array.isArray(to_id)) {
|
|
234
|
-
for (const toID of to_id) {
|
|
235
|
-
await this.db.createEdge(eachEdgeCfg.edgeName, null, `${fromVerticeName}/${from_id}`, `${toVerticeName}/${toID}`);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
else {
|
|
239
|
-
await this.db.createEdge(eachEdgeCfg.edgeName, null, `${fromVerticeName}/${from_id}`, `${toVerticeName}/${to_id}`);
|
|
240
|
-
}
|
|
162
|
+
const result = new Array();
|
|
163
|
+
// check if all the required fields are present
|
|
164
|
+
if (this.requiredFields) {
|
|
165
|
+
documents = this.checkRequiredFields(this.requiredFields, documents, result);
|
|
166
|
+
}
|
|
167
|
+
documents = await Promise.all(documents.map(async (doc) => await this.setDefaults(doc, collection, subject, true)));
|
|
168
|
+
documents = this.encodeOrDecode(documents, this.bufferFields, 'decode');
|
|
169
|
+
documents = this.encodeOrDecode(documents, this.timeStampFields, 'convertDateObjToMilisec');
|
|
170
|
+
if (this.isGraphDB(this.db)) {
|
|
171
|
+
const db = this.db;
|
|
172
|
+
await db.addVertexCollection(collection);
|
|
173
|
+
const createVertexResp = await this.db.createVertex(collection, documents);
|
|
174
|
+
await Promise.all(documents.map(async (document) => {
|
|
175
|
+
try {
|
|
176
|
+
for (const eachEdgeCfg of this.edgeCfg) {
|
|
177
|
+
const fromIDkey = eachEdgeCfg.from;
|
|
178
|
+
const from_id = document[fromIDkey];
|
|
179
|
+
const toIDkey = eachEdgeCfg.to;
|
|
180
|
+
const to_id = document[toIDkey];
|
|
181
|
+
// edges are created outbound, if it is inbound - check for direction
|
|
182
|
+
const inbound = eachEdgeCfg.direction === 'inbound';
|
|
183
|
+
const fromVerticeName = inbound ? eachEdgeCfg.fromVerticeName : collection;
|
|
184
|
+
const toVerticeName = inbound ? collection : eachEdgeCfg.toVerticeName;
|
|
185
|
+
const ids = Array.isArray(to_id) ? to_id : [to_id];
|
|
186
|
+
if (from_id && to_id) {
|
|
187
|
+
for (const id of ids) {
|
|
188
|
+
await db.createEdge(eachEdgeCfg.edgeName, null, `${fromVerticeName}/${from_id}`, `${toVerticeName}/${id}`);
|
|
241
189
|
}
|
|
242
190
|
}
|
|
243
191
|
}
|
|
244
192
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (this.timeStampFields && result?.length > 0) {
|
|
252
|
-
// convert number to Date Object
|
|
253
|
-
result = this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
193
|
+
catch (error) {
|
|
194
|
+
result.push({
|
|
195
|
+
error: true,
|
|
196
|
+
errorNum: error?.code,
|
|
197
|
+
errorMessage: error?.details ?? error?.message
|
|
198
|
+
});
|
|
254
199
|
}
|
|
255
|
-
|
|
200
|
+
}));
|
|
201
|
+
if (Array.isArray(createVertexResp)) {
|
|
202
|
+
result.push(...createVertexResp);
|
|
256
203
|
}
|
|
257
204
|
else {
|
|
258
|
-
|
|
259
|
-
if (!_.isEmpty(result)) {
|
|
260
|
-
checkReqFieldResult = result;
|
|
261
|
-
}
|
|
262
|
-
result = await this.db.insert(collection, documents);
|
|
263
|
-
if (!_.isEmpty(checkReqFieldResult)) {
|
|
264
|
-
checkReqFieldResult.forEach((reqFieldResult) => result.push(reqFieldResult));
|
|
265
|
-
}
|
|
266
|
-
if (this.timeStampFields && result?.length > 0) {
|
|
267
|
-
// convert number to Date Object
|
|
268
|
-
result = this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
269
|
-
}
|
|
270
|
-
return result;
|
|
205
|
+
result.push(createVertexResp);
|
|
271
206
|
}
|
|
207
|
+
this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
272
208
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
result.push(
|
|
276
|
-
|
|
277
|
-
errorNum: error?.code,
|
|
278
|
-
errorMessage: error?.details ? error?.details : error?.message
|
|
279
|
-
});
|
|
280
|
-
return result;
|
|
209
|
+
else {
|
|
210
|
+
const inserts = await this.db.insert(collection, documents);
|
|
211
|
+
result.push(...inserts);
|
|
212
|
+
this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
281
213
|
}
|
|
214
|
+
if (events) {
|
|
215
|
+
await Promise.all(result?.map(async (item) => {
|
|
216
|
+
if (!item?.error) {
|
|
217
|
+
await events.emit(`${this.resourceName}Created`, item);
|
|
218
|
+
}
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
282
222
|
}
|
|
283
223
|
isGraphDB(db) {
|
|
284
224
|
return !!this.edgeCfg;
|
|
@@ -288,77 +228,67 @@ class ResourcesAPIBase {
|
|
|
288
228
|
* @param requiredFields
|
|
289
229
|
* @param documents
|
|
290
230
|
*/
|
|
291
|
-
checkRequiredFields(requiredFields, documents,
|
|
292
|
-
|
|
293
|
-
return requiredFields.every((
|
|
294
|
-
if (document[
|
|
295
|
-
|
|
231
|
+
checkRequiredFields(requiredFields, documents, errors) {
|
|
232
|
+
const valid = documents.filter((document) => {
|
|
233
|
+
return requiredFields.every((field) => {
|
|
234
|
+
if (document[field] === undefined || (Array.isArray(document[field]) && document[field].length === 0)) {
|
|
235
|
+
errors.push({
|
|
236
|
+
id: document.id,
|
|
296
237
|
error: true,
|
|
297
238
|
errorNum: 400,
|
|
298
|
-
errorMessage: `Field ${
|
|
239
|
+
errorMessage: `Field ${field} is necessary for ${this.resourceName} in document ${document.id}`
|
|
299
240
|
});
|
|
300
241
|
return false;
|
|
301
242
|
}
|
|
302
243
|
return true;
|
|
303
244
|
});
|
|
304
245
|
});
|
|
305
|
-
return
|
|
246
|
+
return valid;
|
|
306
247
|
}
|
|
307
248
|
/**
|
|
308
249
|
* Removes documents found by id.
|
|
309
250
|
*
|
|
310
251
|
* @param [array.string] ids List of document IDs.
|
|
311
252
|
*/
|
|
312
|
-
async delete(ids) {
|
|
313
|
-
let
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return `${this.collectionName}/${id}`;
|
|
323
|
-
});
|
|
324
|
-
deleteResponse = await this.db.removeVertex(this.collectionName, ids);
|
|
325
|
-
return deleteResponse;
|
|
326
|
-
}
|
|
253
|
+
async delete(ids, events) {
|
|
254
|
+
let response;
|
|
255
|
+
if (!Array.isArray(ids)) {
|
|
256
|
+
ids = [ids];
|
|
257
|
+
}
|
|
258
|
+
if (this.isGraphDB(this.db)) {
|
|
259
|
+
// Modify the Ids to include documentHandle
|
|
260
|
+
if (ids.length > 0) {
|
|
261
|
+
ids = ids?.map((id) => `${this.collectionName}/${id}`);
|
|
262
|
+
response = await this.db.removeVertex(this.collectionName, ids);
|
|
327
263
|
}
|
|
328
|
-
deleteResponse = await this.db.delete(this.collectionName, ids);
|
|
329
|
-
return deleteResponse;
|
|
330
264
|
}
|
|
331
|
-
|
|
332
|
-
this.
|
|
333
|
-
deleteResponse.push({
|
|
334
|
-
error: true,
|
|
335
|
-
errorNum: error?.code,
|
|
336
|
-
errorMessage: error?.details ? error?.details : error?.message
|
|
337
|
-
});
|
|
338
|
-
return deleteResponse;
|
|
265
|
+
else {
|
|
266
|
+
response = await this.db.delete(this.collectionName, ids);
|
|
339
267
|
}
|
|
268
|
+
if (events) {
|
|
269
|
+
await Promise.all(response?.map(async (id) => {
|
|
270
|
+
if (typeof id === 'string') {
|
|
271
|
+
id = { id };
|
|
272
|
+
}
|
|
273
|
+
if (!id.error) {
|
|
274
|
+
await events?.emit(`${this.resourceName}Deleted`, id);
|
|
275
|
+
}
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
return response;
|
|
340
279
|
}
|
|
341
280
|
/**
|
|
342
281
|
* Delete all documents in the collection.
|
|
343
282
|
*/
|
|
344
|
-
async deleteCollection() {
|
|
283
|
+
async deleteCollection(events) {
|
|
284
|
+
await this.db.truncate(this.collectionName);
|
|
345
285
|
if (this.isGraphDB(this.db)) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
fields: {
|
|
350
|
-
id: 1
|
|
351
|
-
}
|
|
352
|
-
});
|
|
353
|
-
await this.delete(_.map(ids, (doc) => {
|
|
354
|
-
return doc.id;
|
|
355
|
-
}));
|
|
356
|
-
return ids;
|
|
286
|
+
const db = this.db;
|
|
287
|
+
const edges = await db.getGraphDB().get().then((info) => info.edgeDefinitions);
|
|
288
|
+
await Promise.all(edges?.filter(edge => Object.values(edge).flatMap(edge => edge).includes(this.collectionName)).map(edge => db.truncate(edge.collection)) ?? []);
|
|
357
289
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
await this.db.truncate(this.collectionName);
|
|
361
|
-
return entities;
|
|
290
|
+
if (events) {
|
|
291
|
+
await events?.emit(`${this.resourceName}DeletedAll`, { collection: this.collectionName });
|
|
362
292
|
}
|
|
363
293
|
}
|
|
364
294
|
/**
|
|
@@ -366,73 +296,39 @@ class ResourcesAPIBase {
|
|
|
366
296
|
*
|
|
367
297
|
* @param [array.object] documents
|
|
368
298
|
*/
|
|
369
|
-
async upsert(documents,
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
299
|
+
async upsert(documents, subject, events) {
|
|
300
|
+
const createDocuments = new Array();
|
|
301
|
+
const updateDocuments = new Array();
|
|
302
|
+
documents = this.encodeOrDecode(documents, this.bufferFields, 'decode');
|
|
303
|
+
const orgs = new Set(await this.db.find(this.collectionName, {
|
|
304
|
+
_key: {
|
|
305
|
+
$in: [...new Set(documents?.map(doc => doc.id).filter(id => id))],
|
|
306
|
+
},
|
|
307
|
+
}, {
|
|
308
|
+
fields: {
|
|
309
|
+
id: 1
|
|
378
310
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
meta: 1
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
let eventName;
|
|
389
|
-
if (_.isEmpty(foundDocs)) {
|
|
390
|
-
// insert
|
|
391
|
-
setDefaults(doc, this.collectionName, subject);
|
|
392
|
-
createDocuments.push(doc);
|
|
393
|
-
eventName = 'Created';
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
// convert dateTimeStamp fields
|
|
397
|
-
if (this.timeStampFields) {
|
|
398
|
-
foundDocs = this.encodeOrDecode(foundDocs, this.timeStampFields, 'convertMilisecToDateObj');
|
|
399
|
-
}
|
|
400
|
-
// update
|
|
401
|
-
const dbDoc = foundDocs[0];
|
|
402
|
-
updateMetadata(dbDoc.meta, doc, subject);
|
|
403
|
-
updateDocuments.push(doc);
|
|
404
|
-
eventName = 'Modified';
|
|
405
|
-
}
|
|
406
|
-
if (events) {
|
|
407
|
-
return events.emit(`${resourceName}${eventName}`, doc);
|
|
408
|
-
}
|
|
409
|
-
}));
|
|
410
|
-
if (createDocuments?.length > 0) {
|
|
411
|
-
createDocsResult = await this.create(createDocuments, subject);
|
|
412
|
-
}
|
|
413
|
-
if (updateDocuments?.length > 0) {
|
|
414
|
-
updateDocsResult = await this.update(updateDocuments, subject);
|
|
311
|
+
}).then(resp => resp.map(doc => doc.id)));
|
|
312
|
+
documents?.forEach((doc) => {
|
|
313
|
+
if (orgs.has(doc?.id)) {
|
|
314
|
+
// update
|
|
315
|
+
updateDocuments.push(doc);
|
|
415
316
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
result = this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
420
|
-
}
|
|
421
|
-
await Promise.all(dispatch);
|
|
422
|
-
if (this.bufferFields && result?.length > 0) {
|
|
423
|
-
result = this.encodeOrDecode(result, this.bufferFields, 'encode');
|
|
317
|
+
else {
|
|
318
|
+
// insert
|
|
319
|
+
createDocuments.push(doc);
|
|
424
320
|
}
|
|
425
|
-
|
|
321
|
+
});
|
|
322
|
+
if (updateDocuments?.length > 0) {
|
|
323
|
+
await this.update(updateDocuments, subject, events);
|
|
426
324
|
}
|
|
427
|
-
|
|
428
|
-
this.
|
|
429
|
-
result.push({
|
|
430
|
-
error: true,
|
|
431
|
-
errorNum: error?.code,
|
|
432
|
-
errorMessage: error?.details ? error?.details : error?.message
|
|
433
|
-
});
|
|
434
|
-
return result;
|
|
325
|
+
if (createDocuments?.length > 0) {
|
|
326
|
+
await this.create(createDocuments, subject, events);
|
|
435
327
|
}
|
|
328
|
+
const result = [...updateDocuments, ...createDocuments];
|
|
329
|
+
this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
330
|
+
this.encodeOrDecode(result, this.bufferFields, 'encode');
|
|
331
|
+
return result;
|
|
436
332
|
}
|
|
437
333
|
/**
|
|
438
334
|
* Finds documents by id and updates them.
|
|
@@ -440,96 +336,85 @@ class ResourcesAPIBase {
|
|
|
440
336
|
* @param [array.object] documents
|
|
441
337
|
* A list of documents or partial documents. Each document must contain an id field.
|
|
442
338
|
*/
|
|
443
|
-
async update(documents, subject) {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
documents = this.encodeOrDecode(documents, this.bufferFields, 'decode');
|
|
449
|
-
}
|
|
450
|
-
let docsWithUpMetadata = await Promise.all(documents.map(async (doc) => {
|
|
451
|
-
const foundDocs = await this.db.find(collectionName, { id: doc.id }, { limit: 1 });
|
|
452
|
-
let dbDoc;
|
|
453
|
-
if (foundDocs && foundDocs.length === 1) {
|
|
454
|
-
dbDoc = foundDocs[0];
|
|
455
|
-
doc = updateMetadata(dbDoc.meta, doc, subject);
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
dbDoc = doc; // doc not existing assigning to generate error message in response
|
|
459
|
-
}
|
|
339
|
+
async update(documents, subject, events) {
|
|
340
|
+
documents = this.encodeOrDecode(documents, this.bufferFields, 'decode');
|
|
341
|
+
documents = documents.map((doc) => this.setMeta(doc, subject));
|
|
342
|
+
documents = await Promise.all(documents.map(async (doc) => {
|
|
343
|
+
try {
|
|
460
344
|
if (this.isGraphDB(this.db)) {
|
|
461
345
|
const db = this.db;
|
|
462
|
-
await Promise.all(this.edgeCfg.map(async (
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (Array.isArray(modified_to_idValues)) {
|
|
467
|
-
modified_to_idValues = _.sortBy(modified_to_idValues);
|
|
468
|
-
}
|
|
469
|
-
if (Array.isArray(db_to_idValues)) {
|
|
470
|
-
db_to_idValues = _.sortBy(db_to_idValues);
|
|
471
|
-
}
|
|
346
|
+
await Promise.all(this.edgeCfg.map(async (edgeCfg) => {
|
|
347
|
+
const to_id = doc[edgeCfg.to];
|
|
348
|
+
const from_id = doc[edgeCfg.from];
|
|
349
|
+
const edgeCollectionName = edgeCfg.edgeName;
|
|
472
350
|
// delete and recreate only if there is a difference in references
|
|
473
|
-
if (
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const edgeCollectionName = eachEdgeCfg.edgeName;
|
|
485
|
-
const outgoingEdges = await db.getOutEdges(edgeCollectionName, `${collectionName}/${dbDoc.id}`);
|
|
486
|
-
if (Array.isArray(outgoingEdges.edges)) {
|
|
487
|
-
await Promise.all(outgoingEdges.edges.map((outgoingEdge) => db.removeEdge(edgeCollectionName, outgoingEdge._id)));
|
|
488
|
-
}
|
|
489
|
-
const incomingEdges = await db.getInEdges(edgeCollectionName, `${collectionName}/${dbDoc.id}`);
|
|
490
|
-
if (Array.isArray(incomingEdges.edges)) {
|
|
491
|
-
await Promise.all(incomingEdges.edges.map((incomingEdge) => db.removeEdge(edgeCollectionName, incomingEdge._id)));
|
|
351
|
+
if (edgeCfg.direction === 'inbound' && from_id) {
|
|
352
|
+
const from_ids = Array.isArray(from_id) ? from_id : [from_id];
|
|
353
|
+
// if (!from_ids?.length) return;
|
|
354
|
+
if (typeof to_id !== 'string')
|
|
355
|
+
throw Error('Inbound value `to` has to be a single string!');
|
|
356
|
+
const fromVerticeName = edgeCfg.fromVerticeName;
|
|
357
|
+
const toVerticeName = edgeCfg.toVerticeName ?? this.collectionName;
|
|
358
|
+
const incoming = await db.getInEdges(edgeCollectionName, `${fromVerticeName}/${to_id}`);
|
|
359
|
+
// Remove edges that are no longer defined
|
|
360
|
+
if (Array.isArray(incoming.edges)) {
|
|
361
|
+
await Promise.all(incoming.edges?.filter((edge) => !from_ids.includes(edge._from)).map((edge) => db.removeEdge(edgeCollectionName, edge._id)));
|
|
492
362
|
}
|
|
493
363
|
// Create new edges
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
364
|
+
await Promise.all(from_ids.filter(id => !incoming.edges?.includes(id)).map(id => db.createEdge(edgeCfg.edgeName, null, `${fromVerticeName}/${from_id}`, `${toVerticeName}/${id}`)));
|
|
365
|
+
}
|
|
366
|
+
else if (to_id) {
|
|
367
|
+
const to_ids = Array.isArray(to_id) ? to_id : [to_id];
|
|
368
|
+
// if (!to_ids?.length) return;
|
|
369
|
+
if (typeof from_id !== 'string')
|
|
370
|
+
throw Error('Outbound value `from` has to be a single string!');
|
|
371
|
+
const fromVerticeName = edgeCfg.fromVerticeName ?? this.collectionName;
|
|
372
|
+
const toVerticeName = edgeCfg.toVerticeName;
|
|
373
|
+
const outgoing = await db.getOutEdges(edgeCollectionName, `${fromVerticeName}/${from_id}`);
|
|
374
|
+
// Remove edges that are no longer defined
|
|
375
|
+
if (Array.isArray(outgoing.edges)) {
|
|
376
|
+
await Promise.all(outgoing.edges?.filter((edge) => !to_ids.includes(edge._to)).map((edge) => db.removeEdge(edgeCollectionName, edge._id)));
|
|
501
377
|
}
|
|
378
|
+
// Create new edges
|
|
379
|
+
await Promise.all(to_ids.filter(id => !outgoing.edges?.includes(id)).map(id => db.createEdge(edgeCfg.edgeName, null, `${fromVerticeName}/${from_id}`, `${toVerticeName}/${id}`)));
|
|
502
380
|
}
|
|
503
381
|
}));
|
|
504
382
|
}
|
|
505
383
|
return doc;
|
|
506
|
-
}));
|
|
507
|
-
if (this.timeStampFields && docsWithUpMetadata?.length > 0) {
|
|
508
|
-
docsWithUpMetadata = this.encodeOrDecode(docsWithUpMetadata, this.timeStampFields, 'convertDateObjToMilisec');
|
|
509
384
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
385
|
+
catch (error) {
|
|
386
|
+
this.logger?.error(`Error updating document ${doc.id}`, error);
|
|
387
|
+
return {
|
|
388
|
+
...doc,
|
|
389
|
+
error: true,
|
|
390
|
+
errorNum: error?.code,
|
|
391
|
+
errorMessage: `On graph update: ${error?.details ?? error?.message}`
|
|
392
|
+
};
|
|
513
393
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
394
|
+
}));
|
|
395
|
+
const errors = documents.filter(doc => doc.error);
|
|
396
|
+
const updates = documents.filter(doc => !doc.error);
|
|
397
|
+
this.encodeOrDecode(updates, this.timeStampFields, 'convertDateObjToMilisec');
|
|
398
|
+
const results = await this.db.update(this.collectionName, updates);
|
|
399
|
+
results.push(...errors);
|
|
400
|
+
this.encodeOrDecode(results, this.timeStampFields, 'convertMilisecToDateObj');
|
|
401
|
+
if (events) {
|
|
402
|
+
await Promise.all(results?.map(async (item) => {
|
|
403
|
+
if (!item.error) {
|
|
404
|
+
await events.emit(`${this.resourceName}Modified`, item);
|
|
405
|
+
}
|
|
406
|
+
}));
|
|
527
407
|
}
|
|
408
|
+
this.encodeOrDecode(results, this.bufferFields, 'encode');
|
|
409
|
+
return results;
|
|
528
410
|
}
|
|
529
|
-
encodeOrDecode(documents, fieldPaths,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
411
|
+
encodeOrDecode(documents, fieldPaths, mode) {
|
|
412
|
+
const arr = Array.isArray(documents) ? documents : [documents];
|
|
413
|
+
if (fieldPaths?.length && arr?.length) {
|
|
414
|
+
for (const doc of arr) {
|
|
415
|
+
for (const fieldPath of fieldPaths) {
|
|
416
|
+
(0, utils_1.fieldHandler)(doc, fieldPath, mode);
|
|
417
|
+
}
|
|
533
418
|
}
|
|
534
419
|
}
|
|
535
420
|
return documents;
|