@restorecommerce/resource-base-interface 1.6.4 → 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/eslint.config.mjs +22 -0
- package/lib/core/GraphResourcesServiceBase.d.ts +9 -5
- package/lib/core/GraphResourcesServiceBase.d.ts.map +1 -0
- package/lib/core/GraphResourcesServiceBase.js +79 -110
- 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 +275 -398
- 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 +28 -32
- 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 +84 -144
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/package.json +23 -28
- package/tsconfig.json +11 -25
- package/lib/core/WorkerBase.js.map +0 -1
package/lib/core/ResourcesAPI.js
CHANGED
|
@@ -1,103 +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 (let field in values) {
|
|
53
|
-
const strategy = values[field];
|
|
54
|
-
switch (strategy) {
|
|
55
|
-
case Strategies.INCREMENT:
|
|
56
|
-
const key = collectionName + ':' + field;
|
|
57
|
-
o[field] = await redisClient.get(key);
|
|
58
|
-
await redisClient.incr(key);
|
|
59
|
-
break;
|
|
60
|
-
case Strategies.UUID:
|
|
61
|
-
o[field] = uuidGen();
|
|
62
|
-
break;
|
|
63
|
-
case Strategies.RANDOM:
|
|
64
|
-
o[field] = uuidGen();
|
|
65
|
-
break;
|
|
66
|
-
case Strategies.TIMESTAMP:
|
|
67
|
-
o[field] = await redisClient.time()[0];
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (_.isNil(o.meta.created) || o.meta.created === 0) {
|
|
74
|
-
o.meta.created = new Date();
|
|
75
|
-
}
|
|
76
|
-
o.meta.created_by = subject?.id;
|
|
77
|
-
o.meta.modified_by = subject?.id;
|
|
78
|
-
o.meta.modified = new Date();
|
|
79
|
-
if (_.isNil(o.id) || o.id === 0 || isEmptyObject(o.id)) {
|
|
80
|
-
o.id = uuidGen();
|
|
81
|
-
}
|
|
82
|
-
return o;
|
|
83
|
-
};
|
|
84
|
-
const updateMetadata = (docMeta, newDoc, subject) => {
|
|
85
|
-
if (_.isEmpty(newDoc.meta)) {
|
|
86
|
-
throw new chassis_srv_1.errors.InvalidArgument(`Update request holds no valid metadata for document ${newDoc.id}`);
|
|
87
|
-
}
|
|
88
|
-
if (!_.isEmpty(newDoc.meta?.owners)) {
|
|
89
|
-
// if ownership is meant to be updated
|
|
90
|
-
docMeta.owners = newDoc.meta.owners;
|
|
91
|
-
}
|
|
92
|
-
docMeta.modified_by = subject?.id;
|
|
93
|
-
docMeta.modified = new Date();
|
|
94
|
-
newDoc.meta = docMeta;
|
|
95
|
-
return newDoc;
|
|
96
|
-
};
|
|
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, '');
|
|
97
15
|
/**
|
|
98
16
|
* Resource API base provides functions for CRUD operations.
|
|
99
17
|
*/
|
|
100
18
|
class ResourcesAPIBase {
|
|
19
|
+
db;
|
|
20
|
+
collectionName;
|
|
21
|
+
edgeCfg;
|
|
22
|
+
graphName;
|
|
23
|
+
logger;
|
|
24
|
+
resourceName;
|
|
25
|
+
bufferFields;
|
|
26
|
+
requiredFields;
|
|
27
|
+
timeStampFields;
|
|
28
|
+
redisClient;
|
|
101
29
|
/**
|
|
102
30
|
* @constructor
|
|
103
31
|
* @param {object} db Chassis arangodb provider.
|
|
@@ -115,29 +43,20 @@ class ResourcesAPIBase {
|
|
|
115
43
|
if (!fieldHandlerConf) {
|
|
116
44
|
return;
|
|
117
45
|
}
|
|
118
|
-
const strategyCfg = fieldHandlerConf
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
this.bufferFields = fieldHandlerConf.bufferFields;
|
|
124
|
-
}
|
|
125
|
-
// config fix to be removed after ts-proto is used
|
|
126
|
-
if (fieldHandlerConf.timeStampFields) {
|
|
127
|
-
this.timeStampFields = fieldHandlerConf.timeStampFields;
|
|
128
|
-
}
|
|
129
|
-
if (fieldHandlerConf.requiredFields) {
|
|
130
|
-
this.requiredFields = fieldHandlerConf.requiredFields;
|
|
131
|
-
}
|
|
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;
|
|
132
51
|
// values for Redis hash set
|
|
133
|
-
for (
|
|
52
|
+
for (const field in strategyCfg) {
|
|
134
53
|
const strategy = strategyCfg[field].strategy;
|
|
135
|
-
redisClient.hSet(collectionName, field, strategy);
|
|
54
|
+
this.redisClient.hSet(collectionName, field, strategy);
|
|
136
55
|
switch (strategy) {
|
|
137
|
-
case Strategies.INCREMENT:
|
|
56
|
+
case Strategies.INCREMENT: {
|
|
138
57
|
// check if value already exists in redis
|
|
139
58
|
let startingValue;
|
|
140
|
-
startingValue = redisClient.get(`${collectionName}:${field}`).then((val) => val);
|
|
59
|
+
startingValue = this.redisClient.get(`${collectionName}:${field}`).then((val) => val);
|
|
141
60
|
if (!startingValue) {
|
|
142
61
|
if (strategyCfg[field].startingValue) {
|
|
143
62
|
startingValue = Number.isNaN(strategyCfg[field].startingValue) ?
|
|
@@ -146,42 +65,91 @@ class ResourcesAPIBase {
|
|
|
146
65
|
else {
|
|
147
66
|
startingValue = '0';
|
|
148
67
|
}
|
|
149
|
-
redisClient.set(`${collectionName}:${field}`, startingValue).then((val) => val);
|
|
68
|
+
this.redisClient.set(`${collectionName}:${field}`, startingValue).then((val) => val);
|
|
150
69
|
}
|
|
151
70
|
break;
|
|
71
|
+
}
|
|
152
72
|
default:
|
|
153
73
|
break;
|
|
154
74
|
}
|
|
155
75
|
}
|
|
156
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
|
+
}
|
|
157
131
|
/**
|
|
158
132
|
* Finds documents based on provided filters and options
|
|
159
133
|
* @param {object} filter key value filter using mongodb/nedb filter format.
|
|
160
134
|
* @param {number} limit
|
|
161
135
|
* @param {number} offset
|
|
162
136
|
* @param {object} sort key value, key=field value: 1=ASCENDING, -1=DESCENDING, 0=UNSORTED
|
|
163
|
-
* @param {object}
|
|
137
|
+
* @param {object} fields key value, key=field value: 0=exclude, 1=include
|
|
164
138
|
* @returns {an Object that contains an items field}
|
|
165
139
|
*/
|
|
166
|
-
async read(filter = {}, limit = 1000, offset = 0, sort = {},
|
|
140
|
+
async read(filter = {}, limit = 1000, offset = 0, sort = {}, fields = {}, customQueries = [], customArgs = {}, search) {
|
|
167
141
|
const options = {
|
|
168
142
|
limit: Math.min(limit, 1000),
|
|
169
143
|
offset,
|
|
170
144
|
sort,
|
|
171
|
-
fields
|
|
145
|
+
fields,
|
|
172
146
|
customQueries,
|
|
173
|
-
customArguments: customArgs
|
|
147
|
+
customArguments: customArgs?.value ? JSON.parse(customArgs.value.toString()) : {},
|
|
174
148
|
search
|
|
175
149
|
};
|
|
176
150
|
let entities = await this.db.find(this.collectionName, filter, options);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
entities = this.encodeOrDecode(entities, this.bufferFields, 'encode');
|
|
180
|
-
}
|
|
181
|
-
if (this.timeStampFields && entities?.length > 0) {
|
|
182
|
-
// convert number to Date Object
|
|
183
|
-
entities = this.encodeOrDecode(entities, this.timeStampFields, 'convertMilisecToDateObj');
|
|
184
|
-
}
|
|
151
|
+
entities = this.encodeOrDecode(entities, this.bufferFields, 'encode');
|
|
152
|
+
entities = this.encodeOrDecode(entities, this.timeStampFields, 'convertMilisecToDateObj');
|
|
185
153
|
return entities;
|
|
186
154
|
}
|
|
187
155
|
/**
|
|
@@ -189,96 +157,68 @@ class ResourcesAPIBase {
|
|
|
189
157
|
*
|
|
190
158
|
* @param {array.object} documents
|
|
191
159
|
*/
|
|
192
|
-
async create(documents, subject) {
|
|
160
|
+
async create(documents, subject, events) {
|
|
193
161
|
const collection = this.collectionName;
|
|
194
|
-
|
|
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
|
-
|
|
221
|
-
const from_id = document[fromIDkey];
|
|
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 (_.isArray(to_id)) {
|
|
234
|
-
for (let 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
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
209
|
+
else {
|
|
210
|
+
const inserts = await this.db.insert(collection, documents);
|
|
211
|
+
result.push(...inserts);
|
|
212
|
+
this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
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
|
+
}));
|
|
281
220
|
}
|
|
221
|
+
return result;
|
|
282
222
|
}
|
|
283
223
|
isGraphDB(db) {
|
|
284
224
|
return !!this.edgeCfg;
|
|
@@ -288,85 +228,67 @@ class ResourcesAPIBase {
|
|
|
288
228
|
* @param requiredFields
|
|
289
229
|
* @param documents
|
|
290
230
|
*/
|
|
291
|
-
checkRequiredFields(requiredFields, documents,
|
|
292
|
-
documents.
|
|
293
|
-
requiredFields.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
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,
|
|
297
237
|
error: true,
|
|
298
238
|
errorNum: 400,
|
|
299
|
-
errorMessage: `Field ${
|
|
239
|
+
errorMessage: `Field ${field} is necessary for ${this.resourceName} in document ${document.id}`
|
|
300
240
|
});
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
if ((isArray && document[eachField].length == 0)) {
|
|
304
|
-
result.push({
|
|
305
|
-
error: true,
|
|
306
|
-
errorNum: 400,
|
|
307
|
-
errorMessage: `Field ${eachField} is necessary for ${this.resourceName} for documentID ${document.id}`
|
|
308
|
-
});
|
|
309
|
-
documents = documents.filter(doc => doc.id != document.id);
|
|
241
|
+
return false;
|
|
310
242
|
}
|
|
243
|
+
return true;
|
|
311
244
|
});
|
|
312
245
|
});
|
|
313
|
-
return
|
|
246
|
+
return valid;
|
|
314
247
|
}
|
|
315
248
|
/**
|
|
316
249
|
* Removes documents found by id.
|
|
317
250
|
*
|
|
318
251
|
* @param [array.string] ids List of document IDs.
|
|
319
252
|
*/
|
|
320
|
-
async delete(ids) {
|
|
321
|
-
let
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return `${this.collectionName}/${id}`;
|
|
331
|
-
});
|
|
332
|
-
deleteResponse = await this.db.removeVertex(this.collectionName, ids);
|
|
333
|
-
return deleteResponse;
|
|
334
|
-
}
|
|
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);
|
|
335
263
|
}
|
|
336
|
-
deleteResponse = await this.db.delete(this.collectionName, ids);
|
|
337
|
-
return deleteResponse;
|
|
338
264
|
}
|
|
339
|
-
|
|
340
|
-
this.
|
|
341
|
-
deleteResponse.push({
|
|
342
|
-
error: true,
|
|
343
|
-
errorNum: error?.code,
|
|
344
|
-
errorMessage: error?.details ? error?.details : error?.message
|
|
345
|
-
});
|
|
346
|
-
return deleteResponse;
|
|
265
|
+
else {
|
|
266
|
+
response = await this.db.delete(this.collectionName, ids);
|
|
347
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;
|
|
348
279
|
}
|
|
349
280
|
/**
|
|
350
281
|
* Delete all documents in the collection.
|
|
351
282
|
*/
|
|
352
|
-
async deleteCollection() {
|
|
283
|
+
async deleteCollection(events) {
|
|
284
|
+
await this.db.truncate(this.collectionName);
|
|
353
285
|
if (this.isGraphDB(this.db)) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
fields: {
|
|
358
|
-
id: 1
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
await this.delete(_.map(ids, (doc) => {
|
|
362
|
-
return doc.id;
|
|
363
|
-
}));
|
|
364
|
-
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)) ?? []);
|
|
365
289
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
await this.db.truncate(this.collectionName);
|
|
369
|
-
return entities;
|
|
290
|
+
if (events) {
|
|
291
|
+
await events?.emit(`${this.resourceName}DeletedAll`, { collection: this.collectionName });
|
|
370
292
|
}
|
|
371
293
|
}
|
|
372
294
|
/**
|
|
@@ -374,73 +296,39 @@ class ResourcesAPIBase {
|
|
|
374
296
|
*
|
|
375
297
|
* @param [array.object] documents
|
|
376
298
|
*/
|
|
377
|
-
async upsert(documents,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
|
386
310
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
meta: 1
|
|
393
|
-
}
|
|
394
|
-
});
|
|
395
|
-
}
|
|
396
|
-
let eventName;
|
|
397
|
-
if (_.isEmpty(foundDocs)) {
|
|
398
|
-
// insert
|
|
399
|
-
setDefaults(doc, this.collectionName, subject);
|
|
400
|
-
createDocuments.push(doc);
|
|
401
|
-
eventName = 'Created';
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
// convert dateTimeStamp fields
|
|
405
|
-
if (this.timeStampFields) {
|
|
406
|
-
foundDocs = this.encodeOrDecode(foundDocs, this.timeStampFields, 'convertMilisecToDateObj');
|
|
407
|
-
}
|
|
408
|
-
// update
|
|
409
|
-
const dbDoc = foundDocs[0];
|
|
410
|
-
updateMetadata(dbDoc.meta, doc, subject);
|
|
411
|
-
updateDocuments.push(doc);
|
|
412
|
-
eventName = 'Modified';
|
|
413
|
-
}
|
|
414
|
-
if (events) {
|
|
415
|
-
return events.emit(`${resourceName}${eventName}`, doc);
|
|
416
|
-
}
|
|
417
|
-
}));
|
|
418
|
-
if (createDocuments?.length > 0) {
|
|
419
|
-
createDocsResult = await this.create(createDocuments, subject);
|
|
420
|
-
}
|
|
421
|
-
if (updateDocuments?.length > 0) {
|
|
422
|
-
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);
|
|
423
316
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
result = this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
428
|
-
}
|
|
429
|
-
await Promise.all(dispatch);
|
|
430
|
-
if (this.bufferFields && result?.length > 0) {
|
|
431
|
-
result = this.encodeOrDecode(result, this.bufferFields, 'encode');
|
|
317
|
+
else {
|
|
318
|
+
// insert
|
|
319
|
+
createDocuments.push(doc);
|
|
432
320
|
}
|
|
433
|
-
|
|
321
|
+
});
|
|
322
|
+
if (updateDocuments?.length > 0) {
|
|
323
|
+
await this.update(updateDocuments, subject, events);
|
|
434
324
|
}
|
|
435
|
-
|
|
436
|
-
this.
|
|
437
|
-
result.push({
|
|
438
|
-
error: true,
|
|
439
|
-
errorNum: error?.code,
|
|
440
|
-
errorMessage: error?.details ? error?.details : error?.message
|
|
441
|
-
});
|
|
442
|
-
return result;
|
|
325
|
+
if (createDocuments?.length > 0) {
|
|
326
|
+
await this.create(createDocuments, subject, events);
|
|
443
327
|
}
|
|
328
|
+
const result = [...updateDocuments, ...createDocuments];
|
|
329
|
+
this.encodeOrDecode(result, this.timeStampFields, 'convertMilisecToDateObj');
|
|
330
|
+
this.encodeOrDecode(result, this.bufferFields, 'encode');
|
|
331
|
+
return result;
|
|
444
332
|
}
|
|
445
333
|
/**
|
|
446
334
|
* Finds documents by id and updates them.
|
|
@@ -448,96 +336,85 @@ class ResourcesAPIBase {
|
|
|
448
336
|
* @param [array.object] documents
|
|
449
337
|
* A list of documents or partial documents. Each document must contain an id field.
|
|
450
338
|
*/
|
|
451
|
-
async update(documents, subject) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
documents = this.encodeOrDecode(documents, this.bufferFields, 'decode');
|
|
457
|
-
}
|
|
458
|
-
let docsWithUpMetadata = await Promise.all(documents.map(async (doc) => {
|
|
459
|
-
let foundDocs = await this.db.find(collectionName, { id: doc.id }, { limit: 1 });
|
|
460
|
-
let dbDoc;
|
|
461
|
-
if (foundDocs && foundDocs.length === 1) {
|
|
462
|
-
dbDoc = foundDocs[0];
|
|
463
|
-
doc = updateMetadata(dbDoc.meta, doc, subject);
|
|
464
|
-
}
|
|
465
|
-
else {
|
|
466
|
-
dbDoc = doc; // doc not existing assigning to generate error message in response
|
|
467
|
-
}
|
|
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 {
|
|
468
344
|
if (this.isGraphDB(this.db)) {
|
|
469
345
|
const db = this.db;
|
|
470
|
-
await Promise.all(this.edgeCfg.map(async (
|
|
471
|
-
const
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
if (_.isArray(modified_to_idValues)) {
|
|
475
|
-
modified_to_idValues = _.sortBy(modified_to_idValues);
|
|
476
|
-
}
|
|
477
|
-
if (_.isArray(db_to_idValues)) {
|
|
478
|
-
db_to_idValues = _.sortBy(db_to_idValues);
|
|
479
|
-
}
|
|
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;
|
|
480
350
|
// delete and recreate only if there is a difference in references
|
|
481
|
-
if (
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const edgeCollectionName = eachEdgeCfg.edgeName;
|
|
493
|
-
let outgoingEdges = await db.getOutEdges(edgeCollectionName, `${collectionName}/${dbDoc.id}`);
|
|
494
|
-
if (_.isArray(outgoingEdges.edges)) {
|
|
495
|
-
await Promise.all(outgoingEdges.edges.map((outgoingEdge) => db.removeEdge(edgeCollectionName, outgoingEdge._id)));
|
|
496
|
-
}
|
|
497
|
-
let incomingEdges = await db.getInEdges(edgeCollectionName, `${collectionName}/${dbDoc.id}`);
|
|
498
|
-
if (_.isArray(incomingEdges.edges)) {
|
|
499
|
-
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)));
|
|
500
362
|
}
|
|
501
363
|
// Create new edges
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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)));
|
|
509
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}`)));
|
|
510
380
|
}
|
|
511
381
|
}));
|
|
512
382
|
}
|
|
513
383
|
return doc;
|
|
514
|
-
}));
|
|
515
|
-
if (this.timeStampFields && docsWithUpMetadata?.length > 0) {
|
|
516
|
-
docsWithUpMetadata = this.encodeOrDecode(docsWithUpMetadata, this.timeStampFields, 'convertDateObjToMilisec');
|
|
517
384
|
}
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|
+
};
|
|
521
393
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
+
}));
|
|
535
407
|
}
|
|
408
|
+
this.encodeOrDecode(results, this.bufferFields, 'encode');
|
|
409
|
+
return results;
|
|
536
410
|
}
|
|
537
|
-
encodeOrDecode(documents, fieldPaths,
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
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
|
+
}
|
|
541
418
|
}
|
|
542
419
|
}
|
|
543
420
|
return documents;
|