lemon-core 4.1.14 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/common/test-helper.d.ts +2 -2
- package/dist/common/test-helper.js +24 -26
- package/dist/common/test-helper.js.map +1 -1
- package/dist/controllers/dummy-controller.js +39 -46
- package/dist/controllers/dummy-controller.js.map +1 -1
- package/dist/controllers/general-api-controller.js +95 -100
- package/dist/controllers/general-api-controller.js.map +1 -1
- package/dist/controllers/general-controller.js +81 -82
- package/dist/controllers/general-controller.js.map +1 -1
- package/dist/cores/api/api-service.d.ts +1 -1
- package/dist/cores/api/api-service.js +228 -269
- package/dist/cores/api/api-service.js.map +1 -1
- package/dist/cores/aws/aws-kms-service.d.ts +1 -2
- package/dist/cores/aws/aws-kms-service.js +143 -153
- package/dist/cores/aws/aws-kms-service.js.map +1 -1
- package/dist/cores/aws/aws-s3-service.d.ts +2 -4
- package/dist/cores/aws/aws-s3-service.js +306 -330
- package/dist/cores/aws/aws-s3-service.js.map +1 -1
- package/dist/cores/aws/aws-sns-service.js +147 -153
- package/dist/cores/aws/aws-sns-service.js.map +1 -1
- package/dist/cores/aws/aws-sqs-service.js +149 -170
- package/dist/cores/aws/aws-sqs-service.js.map +1 -1
- package/dist/cores/aws/index.js +10 -20
- package/dist/cores/aws/index.js.map +1 -1
- package/dist/cores/cache/cache-service.d.ts +2 -2
- package/dist/cores/cache/cache-service.js +435 -499
- package/dist/cores/cache/cache-service.js.map +1 -1
- package/dist/cores/config/config-service.d.ts +1 -1
- package/dist/cores/config/config-service.js +56 -63
- package/dist/cores/config/config-service.js.map +1 -1
- package/dist/cores/config/index.js +14 -24
- package/dist/cores/config/index.js.map +1 -1
- package/dist/cores/core-services.d.ts +1 -1
- package/dist/cores/dynamo/dynamo-query-service.js +37 -51
- package/dist/cores/dynamo/dynamo-query-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-scan-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-scan-service.js +29 -40
- package/dist/cores/dynamo/dynamo-scan-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-service.js +528 -601
- package/dist/cores/dynamo/dynamo-service.js.map +1 -1
- package/dist/cores/dynamo/tools/expressions.js +17 -7
- package/dist/cores/dynamo/tools/expressions.js.map +1 -1
- package/dist/cores/dynamo/tools/query.js +142 -127
- package/dist/cores/dynamo/tools/query.js.map +1 -1
- package/dist/cores/dynamo/tools/scan.js +111 -97
- package/dist/cores/dynamo/tools/scan.js.map +1 -1
- package/dist/cores/dynamo/tools/serializer.js +17 -7
- package/dist/cores/dynamo/tools/serializer.js.map +1 -1
- package/dist/cores/dynamo/tools/utils.d.ts +0 -2
- package/dist/cores/dynamo/tools/utils.js.map +1 -1
- package/dist/cores/elastic/elastic6-query-service.js +307 -324
- package/dist/cores/elastic/elastic6-query-service.js.map +1 -1
- package/dist/cores/elastic/elastic6-service.d.ts +3 -3
- package/dist/cores/elastic/elastic6-service.js +568 -647
- package/dist/cores/elastic/elastic6-service.js.map +1 -1
- package/dist/cores/elastic/hangul-service.js +52 -54
- package/dist/cores/elastic/hangul-service.js.map +1 -1
- package/dist/cores/lambda/index.js +42 -36
- package/dist/cores/lambda/index.js.map +1 -1
- package/dist/cores/lambda/lambda-alb-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-alb-handler.js +59 -72
- package/dist/cores/lambda/lambda-alb-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cognito-handler.js +10 -19
- package/dist/cores/lambda/lambda-cognito-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cron-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-cron-handler.js +14 -23
- package/dist/cores/lambda/lambda-cron-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-dynamo-stream-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js +57 -67
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-handler.d.ts +22 -22
- package/dist/cores/lambda/lambda-handler.js +93 -106
- package/dist/cores/lambda/lambda-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-notification-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-notification-handler.js +39 -50
- package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sns-handler.js +79 -88
- package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sqs-handler.js +79 -88
- package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-web-handler.d.ts +21 -43
- package/dist/cores/lambda/lambda-web-handler.js +392 -482
- package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-wss-handler.js +23 -32
- package/dist/cores/lambda/lambda-wss-handler.js.map +1 -1
- package/dist/cores/protocol/index.js +10 -20
- package/dist/cores/protocol/index.js.map +1 -1
- package/dist/cores/protocol/protocol-service.d.ts +3 -3
- package/dist/cores/protocol/protocol-service.js +235 -227
- package/dist/cores/protocol/protocol-service.js.map +1 -1
- package/dist/cores/storage/http-storage-service.js +65 -85
- package/dist/cores/storage/http-storage-service.js.map +1 -1
- package/dist/cores/storage/model-manager.js +66 -85
- package/dist/cores/storage/model-manager.js.map +1 -1
- package/dist/cores/storage/proxy-storage-service.d.ts +2 -2
- package/dist/cores/storage/proxy-storage-service.js +562 -599
- package/dist/cores/storage/proxy-storage-service.js.map +1 -1
- package/dist/cores/storage/redis-storage-service.js +163 -177
- package/dist/cores/storage/redis-storage-service.js.map +1 -1
- package/dist/cores/storage/storage-service.js +281 -322
- package/dist/cores/storage/storage-service.js.map +1 -1
- package/dist/engine/builder.d.ts +1 -1
- package/dist/engine/builder.js +59 -63
- package/dist/engine/builder.js.map +1 -1
- package/dist/engine/engine.d.ts +4 -4
- package/dist/engine/engine.js +9 -18
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/types.d.ts +1 -1
- package/dist/engine/utilities.d.ts +14 -10
- package/dist/engine/utilities.js +301 -280
- package/dist/engine/utilities.js.map +1 -1
- package/dist/environ.js +4 -6
- package/dist/environ.js.map +1 -1
- package/dist/extended/abstract-service.js +595 -645
- package/dist/extended/abstract-service.js.map +1 -1
- package/dist/extended/libs/sig-v4.js.map +1 -1
- package/dist/generated/field-registry.d.ts +10 -0
- package/dist/generated/field-registry.js +17 -0
- package/dist/generated/field-registry.js.map +1 -0
- package/dist/helpers/helpers.d.ts +17 -9
- package/dist/helpers/helpers.js +88 -78
- package/dist/helpers/helpers.js.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/dynamodb-value.js +2 -3
- package/dist/lib/dynamodb-value.js.map +1 -1
- package/dist/tools/express.js +4 -5
- package/dist/tools/express.js.map +1 -1
- package/dist/tools/tools.d.ts +3 -1
- package/dist/tools/tools.js +14 -21
- package/dist/tools/tools.js.map +1 -1
- package/package.json +19 -18
- package/dist/exec-cli.d.ts +0 -2
- package/dist/exec-cli.js +0 -211
- package/dist/exec-cli.js.map +0 -1
- package/dist/lib/dynamo/expressions.d.ts +0 -14
- package/dist/lib/dynamo/expressions.js +0 -212
- package/dist/lib/dynamo/expressions.js.map +0 -1
- package/dist/lib/dynamo/query.d.ts +0 -43
- package/dist/lib/dynamo/query.js +0 -246
- package/dist/lib/dynamo/query.js.map +0 -1
- package/dist/lib/dynamo/scan.d.ts +0 -33
- package/dist/lib/dynamo/scan.js +0 -172
- package/dist/lib/dynamo/scan.js.map +0 -1
- package/dist/lib/dynamo/serializer.d.ts +0 -12
- package/dist/lib/dynamo/serializer.js +0 -243
- package/dist/lib/dynamo/serializer.js.map +0 -1
- package/dist/lib/dynamo/utils.d.ts +0 -15
- package/dist/lib/dynamo/utils.js +0 -129
- package/dist/lib/dynamo/utils.js.map +0 -1
- package/dist/tools/shared.d.ts +0 -37
- package/dist/tools/shared.js +0 -130
- package/dist/tools/shared.js.map +0 -1
|
@@ -15,39 +15,30 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
|
36
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
37
|
-
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
|
38
|
-
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
|
39
|
-
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
|
40
|
-
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
|
41
|
-
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
|
42
|
-
function fulfill(value) { resume("next", value); }
|
|
43
|
-
function reject(value) { resume("throw", value); }
|
|
44
|
-
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
|
45
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
46
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
37
|
};
|
|
49
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
-
exports.$ES6 = exports._ES6 = exports.
|
|
39
|
+
exports.$ES6 = exports._ES6 = exports.Elastic6Instance = exports.AbstractElastic6Instance = exports.Elastic6Synchronizer = exports.AbstractProxy = exports.ManagerProxy = exports.CoreManager = exports.CoreService = exports.filterFields = void 0;
|
|
40
|
+
exports.asIdentityId = asIdentityId;
|
|
41
|
+
exports.sourceToItem = sourceToItem;
|
|
51
42
|
/**
|
|
52
43
|
* `abstract-service.ts`
|
|
53
44
|
* - common service design pattern to build micro-service backend.
|
|
@@ -79,10 +70,8 @@ const NS = engine_1.$U.NS('back', 'blue'); // NAMESPACE TO BE PRINTED.
|
|
|
79
70
|
* @param context the current request context.
|
|
80
71
|
*/
|
|
81
72
|
function asIdentityId(context) {
|
|
82
|
-
|
|
83
|
-
return (_a = context === null || context === void 0 ? void 0 : context.identity) === null || _a === void 0 ? void 0 : _a.identityId;
|
|
73
|
+
return context?.identity?.identityId;
|
|
84
74
|
}
|
|
85
|
-
exports.asIdentityId = asIdentityId;
|
|
86
75
|
/**
|
|
87
76
|
* extract field names from models
|
|
88
77
|
* - only fields start with lowercase, or all upper.
|
|
@@ -102,6 +91,12 @@ exports.filterFields = filterFields;
|
|
|
102
91
|
* @abstract
|
|
103
92
|
*/
|
|
104
93
|
class CoreService extends cores_1.GeneralKeyMaker {
|
|
94
|
+
/** dynamo table name */
|
|
95
|
+
tableName;
|
|
96
|
+
/** global index name of elasticsearch */
|
|
97
|
+
idName;
|
|
98
|
+
/** (optional) current timestamp */
|
|
99
|
+
current = 0; // for unit-test. set the current time-stamp.
|
|
105
100
|
/**
|
|
106
101
|
* constructor
|
|
107
102
|
* @param tableName target table-name (or .yml dummy file-name)
|
|
@@ -110,15 +105,13 @@ class CoreService extends cores_1.GeneralKeyMaker {
|
|
|
110
105
|
*/
|
|
111
106
|
constructor(tableName, ns, idName) {
|
|
112
107
|
super(ns || engine_1.$U.env('NS', 'TT'));
|
|
113
|
-
/** (optional) current timestamp */
|
|
114
|
-
this.current = 0; // for unit-test. set the current time-stamp.
|
|
115
|
-
/**
|
|
116
|
-
* override current time
|
|
117
|
-
*/
|
|
118
|
-
this.setCurrent = (current) => (this.current = current);
|
|
119
108
|
this.tableName = tableName || engine_1.$U.env('MY_DYNAMO_TABLE', 'Test');
|
|
120
109
|
this.idName = idName || '_id';
|
|
121
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* override current time
|
|
113
|
+
*/
|
|
114
|
+
setCurrent = (current) => (this.current = current);
|
|
122
115
|
/**
|
|
123
116
|
* get the current dynamo-options.
|
|
124
117
|
*/
|
|
@@ -152,44 +145,38 @@ class CoreManager extends cores_1.AbstractManager {
|
|
|
152
145
|
*/
|
|
153
146
|
constructor(type, parent, fields, uniqueField) {
|
|
154
147
|
super(type, parent, fields, uniqueField);
|
|
155
|
-
/**
|
|
156
|
-
* say hello()
|
|
157
|
-
*/
|
|
158
|
-
this.hello = () => `${this.storage.hello()}`;
|
|
159
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* say hello()
|
|
151
|
+
*/
|
|
152
|
+
hello = () => `${this.storage.hello()}`;
|
|
160
153
|
/**
|
|
161
154
|
* get existence of model
|
|
162
155
|
* @param id
|
|
163
156
|
*/
|
|
164
|
-
exists(id) {
|
|
165
|
-
return
|
|
166
|
-
return (yield this.find(id)) !== null;
|
|
167
|
-
});
|
|
157
|
+
async exists(id) {
|
|
158
|
+
return (await this.find(id)) !== null;
|
|
168
159
|
}
|
|
169
160
|
/**
|
|
170
161
|
* find model - retrieve or null
|
|
171
162
|
* @param id model-id
|
|
172
163
|
*/
|
|
173
|
-
find(id) {
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
throw e;
|
|
179
|
-
});
|
|
164
|
+
async find(id) {
|
|
165
|
+
return this.retrieve(id).catch(e => {
|
|
166
|
+
if ((0, test_helper_1.GETERR)(e).startsWith('404 NOT FOUND'))
|
|
167
|
+
return null;
|
|
168
|
+
throw e;
|
|
180
169
|
});
|
|
181
170
|
}
|
|
182
171
|
/**
|
|
183
172
|
* get model by key
|
|
184
173
|
* @param key global id(like primary-key)
|
|
185
174
|
*/
|
|
186
|
-
findByKey(key) {
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
throw e;
|
|
192
|
-
});
|
|
175
|
+
async findByKey(key) {
|
|
176
|
+
return this.storage.storage.read(key).catch(e => {
|
|
177
|
+
if ((0, test_helper_1.GETERR)(e).startsWith('404 NOT FOUND'))
|
|
178
|
+
return null;
|
|
179
|
+
throw e;
|
|
193
180
|
});
|
|
194
181
|
}
|
|
195
182
|
/**
|
|
@@ -200,70 +187,60 @@ class CoreManager extends cores_1.AbstractManager {
|
|
|
200
187
|
* @param idList list of id
|
|
201
188
|
* @param parrallel (optional) in parrallel size
|
|
202
189
|
*/
|
|
203
|
-
getMulti(idList, parrallel) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
return idList.map(id => { var _a; return (_a = $map[id]) !== null && _a !== void 0 ? _a : null; }).map(N => { var _a; return (((_a = N === null || N === void 0 ? void 0 : N.error) === null || _a === void 0 ? void 0 : _a.startsWith('404 NOT FOUND')) ? null : N); });
|
|
207
|
-
});
|
|
190
|
+
async getMulti(idList, parrallel) {
|
|
191
|
+
const $map = await this.getMulti$(idList, 'id', parrallel);
|
|
192
|
+
return idList.map(id => $map[id] ?? null).map(N => (N?.error?.startsWith('404 NOT FOUND') ? null : N));
|
|
208
193
|
}
|
|
209
194
|
/**
|
|
210
195
|
* batch get models in map by idName
|
|
211
196
|
*/
|
|
212
|
-
getMulti$(idList, idName = 'id', parrallel) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return helpers_1.$T.asMap(list, idName);
|
|
224
|
-
});
|
|
197
|
+
async getMulti$(idList, idName = 'id', parrallel) {
|
|
198
|
+
// 1. find items in unique
|
|
199
|
+
const ids = idList.reduce((L, id) => {
|
|
200
|
+
if (id && !L.includes(id))
|
|
201
|
+
L.push(id);
|
|
202
|
+
return L;
|
|
203
|
+
}, []);
|
|
204
|
+
// 2. find from storage.
|
|
205
|
+
const list = await (0, helpers_1.my_parrallel)(ids.map(id => ({ id })), N => this.retrieve(N.id), parrallel);
|
|
206
|
+
// 3. convert to map
|
|
207
|
+
return helpers_1.$T.asMap(list, idName);
|
|
225
208
|
}
|
|
226
209
|
/**
|
|
227
210
|
* get by unique field value
|
|
228
211
|
* @param uniqueValue
|
|
229
212
|
*/
|
|
230
|
-
getByUniqueField(uniqueValue) {
|
|
231
|
-
return
|
|
232
|
-
return this.$unique.findOrCreate(uniqueValue);
|
|
233
|
-
});
|
|
213
|
+
async getByUniqueField(uniqueValue) {
|
|
214
|
+
return this.$unique.findOrCreate(uniqueValue);
|
|
234
215
|
}
|
|
235
216
|
/**
|
|
236
217
|
* find model by unique field value - retrieve or null
|
|
237
218
|
* @param uniqueValue
|
|
238
219
|
*/
|
|
239
|
-
findByUniqueField(uniqueValue) {
|
|
240
|
-
return
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
throw e;
|
|
245
|
-
});
|
|
220
|
+
async findByUniqueField(uniqueValue) {
|
|
221
|
+
return this.getByUniqueField(uniqueValue).catch(e => {
|
|
222
|
+
if ((0, test_helper_1.GETERR)(e).startsWith('404 NOT FOUND'))
|
|
223
|
+
return null;
|
|
224
|
+
throw e;
|
|
246
225
|
});
|
|
247
226
|
}
|
|
248
227
|
/**
|
|
249
228
|
* prepare model
|
|
250
229
|
* - override `AbstractManager.prepare()`
|
|
251
230
|
*/
|
|
252
|
-
prepare(id, $def, isCreate = true) {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return Object.assign(Object.assign(Object.assign({}, model), $saved), { id });
|
|
266
|
-
});
|
|
231
|
+
async prepare(id, $def, isCreate = true) {
|
|
232
|
+
const $org = await this.find(id);
|
|
233
|
+
if ($org)
|
|
234
|
+
return $org;
|
|
235
|
+
if (!isCreate || $def === undefined)
|
|
236
|
+
throw new Error(`404 NOT FOUND - ${this.type}:${id}`);
|
|
237
|
+
const model = this.prepareDefault($def);
|
|
238
|
+
// create or update lookup
|
|
239
|
+
if (this.$unique)
|
|
240
|
+
await this.updateLookup(id, model, $org);
|
|
241
|
+
// save target model
|
|
242
|
+
const $saved = await this.storage.save(id, { ...model, id });
|
|
243
|
+
return { ...model, ...$saved, id };
|
|
267
244
|
}
|
|
268
245
|
/**
|
|
269
246
|
* update model
|
|
@@ -271,115 +248,98 @@ class CoreManager extends cores_1.AbstractManager {
|
|
|
271
248
|
*
|
|
272
249
|
* @deprecated use `AbstractProxy`
|
|
273
250
|
*/
|
|
274
|
-
insert(model, initSeq) {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
return this.save(id, model);
|
|
278
|
-
});
|
|
251
|
+
async insert(model, initSeq) {
|
|
252
|
+
const id = helpers_1.$T.S(await this.storage.storage.nextSeq(this.type, initSeq));
|
|
253
|
+
return this.save(id, model);
|
|
279
254
|
}
|
|
280
255
|
/**
|
|
281
256
|
* create or update model
|
|
282
257
|
* @param id model id
|
|
283
258
|
* @param model model data
|
|
284
259
|
*/
|
|
285
|
-
save(id, model) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
return Object.assign(Object.assign(Object.assign({}, $org), $saved), { id });
|
|
298
|
-
});
|
|
260
|
+
async save(id, model) {
|
|
261
|
+
if (!id)
|
|
262
|
+
throw new Error(`@id is requird - save()`);
|
|
263
|
+
const $org = await this.find(id);
|
|
264
|
+
if (!$org)
|
|
265
|
+
model = this.prepareDefault(model);
|
|
266
|
+
// create or update lookup
|
|
267
|
+
if (this.$unique)
|
|
268
|
+
await this.updateLookup(id, model, $org);
|
|
269
|
+
// save target model
|
|
270
|
+
const $saved = await this.storage.save(id, model);
|
|
271
|
+
return { ...$org, ...$saved, id };
|
|
299
272
|
}
|
|
300
273
|
/**
|
|
301
274
|
* update model
|
|
302
275
|
* - override 'AbstractManager.update()'
|
|
303
276
|
*/
|
|
304
|
-
update(id, model, $inc) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return Object.assign(Object.assign(Object.assign({}, $org), $updated), { id });
|
|
316
|
-
});
|
|
277
|
+
async update(id, model, $inc) {
|
|
278
|
+
if (!id)
|
|
279
|
+
throw new Error(`@id is requird - update()`);
|
|
280
|
+
const $org = await this.retrieve(id);
|
|
281
|
+
// update lookup
|
|
282
|
+
if (this.$unique)
|
|
283
|
+
await this.updateLookup(id, model, $org);
|
|
284
|
+
// update target model
|
|
285
|
+
model = this.beforeSave(model, $org);
|
|
286
|
+
const $updated = await this.storage.update(id, model, $inc);
|
|
287
|
+
return { ...$org, ...$updated, id };
|
|
317
288
|
}
|
|
318
289
|
/**
|
|
319
290
|
* update or create model
|
|
320
291
|
* - override 'AbstractManager.updateOrCreate()'
|
|
321
292
|
*/
|
|
322
|
-
updateOrCreate(id, model, $inc) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return Object.assign(Object.assign(Object.assign({}, $org), $updated), { id });
|
|
334
|
-
});
|
|
293
|
+
async updateOrCreate(id, model, $inc) {
|
|
294
|
+
if (!id)
|
|
295
|
+
throw new Error(`@id is requird - updateOrCreate()`);
|
|
296
|
+
const $org = await this.prepare(id, model);
|
|
297
|
+
// update lookup
|
|
298
|
+
if (this.$unique)
|
|
299
|
+
await this.updateLookup(id, model, $org);
|
|
300
|
+
// update target model
|
|
301
|
+
model = this.beforeSave(model, $org);
|
|
302
|
+
const $updated = await this.storage.update(id, model, $inc);
|
|
303
|
+
return { ...$org, ...$updated, id };
|
|
335
304
|
}
|
|
336
305
|
/**
|
|
337
306
|
* delete model
|
|
338
307
|
* - override 'AbstractManager.delete()'
|
|
339
308
|
*/
|
|
340
|
-
delete(id, destroy) {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
const uniqueField = (_a = this.$unique) === null || _a === void 0 ? void 0 : _a.field;
|
|
352
|
-
const uniqueValue = uniqueField && $org[uniqueField];
|
|
353
|
-
if (uniqueValue)
|
|
354
|
-
yield this.storage.delete(this.$unique.asLookupId(uniqueValue));
|
|
355
|
-
return $org;
|
|
356
|
-
});
|
|
309
|
+
async delete(id, destroy) {
|
|
310
|
+
if (!id)
|
|
311
|
+
throw new Error(`@id is requird - delete()`);
|
|
312
|
+
// delete target model
|
|
313
|
+
const $org = await super.delete(id, destroy);
|
|
314
|
+
// delete lookup
|
|
315
|
+
const uniqueField = this.$unique?.field;
|
|
316
|
+
const uniqueValue = uniqueField && $org[uniqueField];
|
|
317
|
+
if (uniqueValue)
|
|
318
|
+
await this.storage.delete(this.$unique.asLookupId(uniqueValue));
|
|
319
|
+
return $org;
|
|
357
320
|
}
|
|
358
321
|
/**
|
|
359
322
|
* prepare default-model when creation
|
|
360
323
|
* @param $def base-model
|
|
361
324
|
*/
|
|
362
325
|
prepareDefault($def) {
|
|
363
|
-
return
|
|
326
|
+
return { ...$def };
|
|
364
327
|
}
|
|
365
328
|
/**
|
|
366
329
|
* update lookup and delete old one if exists
|
|
367
330
|
*/
|
|
368
|
-
updateLookup(id, model, $org) {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (oldUniqueValue) {
|
|
379
|
-
yield this.storage.delete(this.$unique.asLookupId(oldUniqueValue));
|
|
380
|
-
}
|
|
331
|
+
async updateLookup(id, model, $org) {
|
|
332
|
+
const uniqueField = this.$unique?.field;
|
|
333
|
+
const newUniqueValue = uniqueField && model[uniqueField];
|
|
334
|
+
const oldUniqueValue = uniqueField && $org?.[uniqueField];
|
|
335
|
+
// update lookup.
|
|
336
|
+
if (newUniqueValue && newUniqueValue !== oldUniqueValue) {
|
|
337
|
+
await this.$unique.updateLookup({ id, ...model });
|
|
338
|
+
// remove old lookup
|
|
339
|
+
if (oldUniqueValue) {
|
|
340
|
+
await this.storage.delete(this.$unique.asLookupId(oldUniqueValue));
|
|
381
341
|
}
|
|
382
|
-
}
|
|
342
|
+
}
|
|
383
343
|
}
|
|
384
344
|
}
|
|
385
345
|
exports.CoreManager = CoreManager;
|
|
@@ -390,46 +350,20 @@ exports.CoreManager = CoreManager;
|
|
|
390
350
|
*/
|
|
391
351
|
// export class ManagerProxy<T, U extends CoreManager<T, any, any>> {
|
|
392
352
|
class ManagerProxy {
|
|
353
|
+
$mgr;
|
|
393
354
|
constructor(proxy, mgr) {
|
|
394
|
-
/**
|
|
395
|
-
* store the origin model.
|
|
396
|
-
* - `null` means `404 not found`
|
|
397
|
-
*/
|
|
398
|
-
this._org = {};
|
|
399
|
-
/**
|
|
400
|
-
* store the updated one.
|
|
401
|
-
*/
|
|
402
|
-
this._new = {};
|
|
403
|
-
/**
|
|
404
|
-
* 객체 정규화 시킴.
|
|
405
|
-
* - null 에 대해서는 특별히 처리.
|
|
406
|
-
*/
|
|
407
|
-
this.normal = (N) => Object.keys(N || {}).reduce((M, k) => {
|
|
408
|
-
if (k.startsWith('_') || k.startsWith('$'))
|
|
409
|
-
return M;
|
|
410
|
-
const v = N[k];
|
|
411
|
-
//* `null` 은 DynamoDB에서 비어있는 문자임.
|
|
412
|
-
M[k] = v === null ? '' : v;
|
|
413
|
-
return M;
|
|
414
|
-
}, {});
|
|
415
|
-
/**
|
|
416
|
-
* override w/ model
|
|
417
|
-
* @param $org the origin model by `.get(id)`
|
|
418
|
-
* @param model the new model.
|
|
419
|
-
*/
|
|
420
|
-
this.override = ($org, model) => {
|
|
421
|
-
const fields = this.$mgr.FIELDS;
|
|
422
|
-
//* update(set) all properties.
|
|
423
|
-
Object.entries(model).forEach(([key, val]) => {
|
|
424
|
-
if (!fields || fields.includes(key)) {
|
|
425
|
-
$org[key] = val;
|
|
426
|
-
}
|
|
427
|
-
});
|
|
428
|
-
return $org;
|
|
429
|
-
};
|
|
430
355
|
this.$mgr = mgr;
|
|
431
356
|
proxy.register(this);
|
|
432
357
|
}
|
|
358
|
+
/**
|
|
359
|
+
* store the origin model.
|
|
360
|
+
* - `null` means `404 not found`
|
|
361
|
+
*/
|
|
362
|
+
_org = {};
|
|
363
|
+
/**
|
|
364
|
+
* store the updated one.
|
|
365
|
+
*/
|
|
366
|
+
_new = {};
|
|
433
367
|
/**
|
|
434
368
|
* get storage linked.
|
|
435
369
|
*/
|
|
@@ -441,7 +375,7 @@ class ManagerProxy {
|
|
|
441
375
|
*/
|
|
442
376
|
org(id, raw = false) {
|
|
443
377
|
const O = this._org[id];
|
|
444
|
-
return O === undefined ? null : raw ? O :
|
|
378
|
+
return O === undefined ? null : raw ? O : { ...O };
|
|
445
379
|
}
|
|
446
380
|
/**
|
|
447
381
|
* check if already read.
|
|
@@ -454,47 +388,70 @@ class ManagerProxy {
|
|
|
454
388
|
* @param id object-id
|
|
455
389
|
* @param defaultOrThrow (optional) create if not exists, or flag to throw error
|
|
456
390
|
*/
|
|
457
|
-
get(id, defaultOrThrow) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
391
|
+
async get(id, defaultOrThrow) {
|
|
392
|
+
const err404 = `404 NOT FOUND - proxy/${this.$mgr.type}/id:${id}`;
|
|
393
|
+
const throwable = typeof defaultOrThrow === 'boolean' ? defaultOrThrow : true;
|
|
394
|
+
const $def = typeof defaultOrThrow === 'boolean' ? null : defaultOrThrow;
|
|
395
|
+
// STEP.0 validate if null (404 error)
|
|
396
|
+
if (this._org[id] === null && !$def) {
|
|
397
|
+
if (throwable)
|
|
398
|
+
throw new Error(err404);
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
// STEP.1 find from `new`
|
|
402
|
+
const N = this._new[id];
|
|
403
|
+
if (N !== undefined)
|
|
404
|
+
return N;
|
|
405
|
+
// OR, READ (or CREATE) from storage.
|
|
406
|
+
const M = !$def ? await this.$mgr.retrieve(id).catch(test_helper_1.NUL404) : await this.$mgr.prepare(id, $def, true);
|
|
407
|
+
if (M === null) {
|
|
408
|
+
this._org[id] = null; //* null 로 저장해두고, 다음에 호출할때 에러 발생.
|
|
409
|
+
if (throwable)
|
|
410
|
+
throw new Error(err404);
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
const M2 = this.normal(M);
|
|
414
|
+
this._org[id] = M2; //* 원본 저장.
|
|
415
|
+
// const M3 = { ...M2 }; //* 클론 생성.
|
|
416
|
+
const M3 = JSON.parse(engine_1.$U.json(M2)); //* deep clone.
|
|
417
|
+
this._new[id] = M3; //* 클론 저장.
|
|
418
|
+
return M3;
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* 객체 정규화 시킴.
|
|
422
|
+
* - null 에 대해서는 특별히 처리.
|
|
423
|
+
*/
|
|
424
|
+
normal = (N) => Object.keys(N || {}).reduce((M, k) => {
|
|
425
|
+
if (k.startsWith('_') || k.startsWith('$'))
|
|
426
|
+
return M;
|
|
427
|
+
const v = N[k];
|
|
428
|
+
//* `null` 은 DynamoDB에서 비어있는 문자임.
|
|
429
|
+
M[k] = v === null ? '' : v;
|
|
430
|
+
return M;
|
|
431
|
+
}, {});
|
|
432
|
+
/**
|
|
433
|
+
* override w/ model
|
|
434
|
+
* @param $org the origin model by `.get(id)`
|
|
435
|
+
* @param model the new model.
|
|
436
|
+
*/
|
|
437
|
+
override = ($org, model) => {
|
|
438
|
+
const fields = this.$mgr.FIELDS;
|
|
439
|
+
//* update(set) all properties.
|
|
440
|
+
Object.entries(model).forEach(([key, val]) => {
|
|
441
|
+
if (!fields || fields.includes(key)) {
|
|
442
|
+
$org[key] = val;
|
|
479
443
|
}
|
|
480
|
-
const M2 = this.normal(M);
|
|
481
|
-
this._org[id] = M2; //* 원본 저장.
|
|
482
|
-
// const M3 = { ...M2 }; //* 클론 생성.
|
|
483
|
-
const M3 = JSON.parse(engine_1.$U.json(M2)); //* deep clone.
|
|
484
|
-
this._new[id] = M3; //* 클론 저장.
|
|
485
|
-
return M3;
|
|
486
444
|
});
|
|
487
|
-
|
|
445
|
+
return $org;
|
|
446
|
+
};
|
|
488
447
|
/**
|
|
489
448
|
* update the node.
|
|
490
449
|
*/
|
|
491
|
-
set(id, model) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
return this.override(O, model);
|
|
497
|
-
});
|
|
450
|
+
async set(id, model) {
|
|
451
|
+
if (!model)
|
|
452
|
+
throw new Error(`@model (object) is required - proxy/${this.$mgr.type}/id:${id}!`);
|
|
453
|
+
const O = await this.get(id);
|
|
454
|
+
return this.override(O, model);
|
|
498
455
|
}
|
|
499
456
|
/**
|
|
500
457
|
* read multiple nodes.
|
|
@@ -503,121 +460,114 @@ class ManagerProxy {
|
|
|
503
460
|
* @param options (optional) options for batch read error reporting
|
|
504
461
|
* @param options.errNoti flag to notify error via Slack if error occurs
|
|
505
462
|
*/
|
|
506
|
-
mget(ids, throwable = true, options) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
463
|
+
async mget(ids, throwable = true, options) {
|
|
464
|
+
if (!ids || ids.length === 0)
|
|
465
|
+
return [];
|
|
466
|
+
const errNoti = options?.errNoti ?? false;
|
|
467
|
+
const result = [];
|
|
468
|
+
const toFetch = [];
|
|
469
|
+
const indexMap = {};
|
|
470
|
+
//* STEP.1 check cached items
|
|
471
|
+
ids.forEach((id, index) => {
|
|
472
|
+
const err404 = `404 NOT FOUND - proxy/${this.$mgr.type}/id:${id}`;
|
|
473
|
+
//* already marked as null (404)
|
|
474
|
+
if (this._org[id] === null) {
|
|
475
|
+
if (throwable)
|
|
476
|
+
throw new Error(err404);
|
|
477
|
+
result[index] = null;
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
//* found in _new
|
|
481
|
+
const N = this._new[id];
|
|
482
|
+
if (N !== undefined) {
|
|
483
|
+
result[index] = N;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
//* need to fetch
|
|
487
|
+
if (!indexMap[id]) {
|
|
488
|
+
indexMap[id] = [];
|
|
489
|
+
toFetch.push(id);
|
|
490
|
+
}
|
|
491
|
+
indexMap[id].push(index);
|
|
492
|
+
});
|
|
493
|
+
//* STEP.2 batch read from storage
|
|
494
|
+
if (toFetch.length > 0) {
|
|
495
|
+
const res = await this.$mgr.storage.storage.doReadMulti(this.$mgr.type, toFetch, {
|
|
496
|
+
context: options?.context,
|
|
497
|
+
throwable: errNoti,
|
|
498
|
+
});
|
|
499
|
+
const successMap = {};
|
|
500
|
+
(res.success || []).forEach((model) => {
|
|
501
|
+
const id = model.id;
|
|
502
|
+
if (id)
|
|
503
|
+
successMap[id] = model;
|
|
504
|
+
});
|
|
505
|
+
//* STEP.3 save to cache and build result
|
|
506
|
+
toFetch.forEach(id => {
|
|
507
|
+
const M = successMap[id];
|
|
508
|
+
if (M) {
|
|
509
|
+
const M2 = this.normal(M);
|
|
510
|
+
this._org[id] = M2;
|
|
511
|
+
const M3 = JSON.parse(engine_1.$U.json(M2));
|
|
512
|
+
this._new[id] = M3;
|
|
513
|
+
indexMap[id].forEach(index => {
|
|
514
|
+
result[index] = M3;
|
|
515
|
+
});
|
|
530
516
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
517
|
+
else {
|
|
518
|
+
this._org[id] = null;
|
|
519
|
+
const err404 = `404 NOT FOUND - proxy/${this.$mgr.type}/id:${id}`;
|
|
520
|
+
indexMap[id].forEach(index => {
|
|
521
|
+
if (throwable)
|
|
522
|
+
throw new Error(err404);
|
|
523
|
+
result[index] = null;
|
|
524
|
+
});
|
|
535
525
|
}
|
|
536
|
-
indexMap[id].push(index);
|
|
537
526
|
});
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
const res = yield this.$mgr.storage.storage.doReadMulti(this.$mgr.type, toFetch, {
|
|
541
|
-
context: options === null || options === void 0 ? void 0 : options.context,
|
|
542
|
-
throwable: errNoti,
|
|
543
|
-
});
|
|
544
|
-
const successMap = {};
|
|
545
|
-
(res.success || []).forEach((model) => {
|
|
546
|
-
const id = model.id;
|
|
547
|
-
if (id)
|
|
548
|
-
successMap[id] = model;
|
|
549
|
-
});
|
|
550
|
-
//* STEP.3 save to cache and build result
|
|
551
|
-
toFetch.forEach(id => {
|
|
552
|
-
const M = successMap[id];
|
|
553
|
-
if (M) {
|
|
554
|
-
const M2 = this.normal(M);
|
|
555
|
-
this._org[id] = M2;
|
|
556
|
-
const M3 = JSON.parse(engine_1.$U.json(M2));
|
|
557
|
-
this._new[id] = M3;
|
|
558
|
-
indexMap[id].forEach(index => {
|
|
559
|
-
result[index] = M3;
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
else {
|
|
563
|
-
this._org[id] = null;
|
|
564
|
-
const err404 = `404 NOT FOUND - proxy/${this.$mgr.type}/id:${id}`;
|
|
565
|
-
indexMap[id].forEach(index => {
|
|
566
|
-
if (throwable)
|
|
567
|
-
throw new Error(err404);
|
|
568
|
-
result[index] = null;
|
|
569
|
-
});
|
|
570
|
-
}
|
|
571
|
-
});
|
|
572
|
-
}
|
|
573
|
-
return result;
|
|
574
|
-
});
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
575
529
|
}
|
|
576
530
|
/**
|
|
577
531
|
* update multiple nodes.
|
|
578
532
|
* @param updates list of models (should include id)
|
|
579
533
|
*/
|
|
580
|
-
mset(updates) {
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return result;
|
|
593
|
-
});
|
|
534
|
+
async mset(updates) {
|
|
535
|
+
if (!updates || updates.length === 0)
|
|
536
|
+
return [];
|
|
537
|
+
const result = [];
|
|
538
|
+
for (const model of updates) {
|
|
539
|
+
const id = model.id;
|
|
540
|
+
if (!id)
|
|
541
|
+
throw new Error(`@id is required - proxy/${this.$mgr.type}/mset()!`);
|
|
542
|
+
const O = await this.get(id);
|
|
543
|
+
result.push(this.override(O, model));
|
|
544
|
+
}
|
|
545
|
+
return result;
|
|
594
546
|
}
|
|
595
547
|
/**
|
|
596
548
|
* increment the field of Object[id]
|
|
597
549
|
* !WARN! this incremented properties should NOT be updated later.
|
|
598
550
|
*/
|
|
599
|
-
inc(id, model) {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
}, $new);
|
|
620
|
-
});
|
|
551
|
+
async inc(id, model) {
|
|
552
|
+
if (!model)
|
|
553
|
+
throw new Error(`@model (object) is required - proxy/${this.$mgr.type}/id:${id}!`);
|
|
554
|
+
const $inc = Object.entries(model).reduce((M, [k, v]) => {
|
|
555
|
+
if (typeof v === 'number')
|
|
556
|
+
M[k] = v;
|
|
557
|
+
return M;
|
|
558
|
+
}, {});
|
|
559
|
+
const keys = Object.keys($inc);
|
|
560
|
+
if (!keys.length)
|
|
561
|
+
throw new Error(`@model (object) is empty to inc() - proxy/${this.$mgr.type}/id:${id}!`);
|
|
562
|
+
//* try to increment, and update the latest to both org and new.
|
|
563
|
+
const $res = await this.$mgr.storage.update(id, null, $inc);
|
|
564
|
+
const $new = await this.get(id);
|
|
565
|
+
const $org = this.org(id, true);
|
|
566
|
+
return keys.reduce((N, k) => {
|
|
567
|
+
const key = k;
|
|
568
|
+
N[key] = $org[key] = $res[key];
|
|
569
|
+
return N;
|
|
570
|
+
}, $new);
|
|
621
571
|
}
|
|
622
572
|
/**
|
|
623
573
|
* get all the updated node.
|
|
@@ -629,7 +579,7 @@ class ManagerProxy {
|
|
|
629
579
|
return ids.reduce((M, id) => {
|
|
630
580
|
const O = this._org[id];
|
|
631
581
|
const N = this._new[id];
|
|
632
|
-
const N2 = this.$mgr.onBeforeSave(
|
|
582
|
+
const N2 = this.$mgr.onBeforeSave({ ...N }, O);
|
|
633
583
|
const N3 = onlyUpdated ? helpers_1.$T.diff(O, N2, onlyValid) : N2;
|
|
634
584
|
M[id] = N3;
|
|
635
585
|
return M;
|
|
@@ -642,6 +592,14 @@ exports.ManagerProxy = ManagerProxy;
|
|
|
642
592
|
* - common abstract based class for Proxy
|
|
643
593
|
*/
|
|
644
594
|
class AbstractProxy {
|
|
595
|
+
/** parrallel factor */
|
|
596
|
+
parrallel;
|
|
597
|
+
/** (internal) current context */
|
|
598
|
+
context;
|
|
599
|
+
/** (internal) backend-service */
|
|
600
|
+
service;
|
|
601
|
+
/** (internal) cache service instance */
|
|
602
|
+
cache;
|
|
645
603
|
/**
|
|
646
604
|
* constructor of proxy.
|
|
647
605
|
* @param service user service instance
|
|
@@ -649,27 +607,6 @@ class AbstractProxy {
|
|
|
649
607
|
* @param cacheScope prefix of cache-key (like `lemon:SS:` or `lemon:SS:user`)
|
|
650
608
|
*/
|
|
651
609
|
constructor(context, service, parrallel = 2, cacheScope) {
|
|
652
|
-
/**
|
|
653
|
-
* say hello().
|
|
654
|
-
*/
|
|
655
|
-
this.hello = () => `manager-proxy:${this.service.NS}/${this.service.tableName}`;
|
|
656
|
-
/**
|
|
657
|
-
* list of manager-proxy
|
|
658
|
-
*/
|
|
659
|
-
this._proxies = [];
|
|
660
|
-
/**
|
|
661
|
-
* report via slack.
|
|
662
|
-
*/
|
|
663
|
-
this.report = (title, data) => __awaiter(this, void 0, void 0, function* () {
|
|
664
|
-
const context = this.context;
|
|
665
|
-
return (0, helpers_1.$slack)(title, data, null, { context }).catch(e => `ERR:${(0, test_helper_1.GETERR)(e)}`);
|
|
666
|
-
});
|
|
667
|
-
/**
|
|
668
|
-
* the cached identity model
|
|
669
|
-
*
|
|
670
|
-
* @deprecated useless anymore since 3.2.10
|
|
671
|
-
*/
|
|
672
|
-
this._identity = {};
|
|
673
610
|
this.context = context;
|
|
674
611
|
this.service = service;
|
|
675
612
|
this.parrallel = parrallel;
|
|
@@ -679,6 +616,14 @@ class AbstractProxy {
|
|
|
679
616
|
this.cache = cores_1.CacheService.create({ type: 'redis', endpoint, ns: cacheScope });
|
|
680
617
|
}
|
|
681
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* say hello().
|
|
621
|
+
*/
|
|
622
|
+
hello = () => `manager-proxy:${this.service.NS}/${this.service.tableName}`;
|
|
623
|
+
/**
|
|
624
|
+
* list of manager-proxy
|
|
625
|
+
*/
|
|
626
|
+
_proxies = [];
|
|
682
627
|
/**
|
|
683
628
|
* get all proxies in list.
|
|
684
629
|
*/
|
|
@@ -700,104 +645,109 @@ class AbstractProxy {
|
|
|
700
645
|
*
|
|
701
646
|
* @param options running parameters.
|
|
702
647
|
*/
|
|
703
|
-
saveAllUpdates(options) {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
this.name = 'MyError';
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* factory to create custom error
|
|
723
|
-
*/
|
|
724
|
-
const $err = (e, M) => {
|
|
725
|
-
var _a, _b, _c, _d, _e;
|
|
726
|
-
return new MyError(`Failed to update ${(_c = (_a = M === null || M === void 0 ? void 0 : M.type) !== null && _a !== void 0 ? _a : (_b = M === null || M === void 0 ? void 0 : M.storage) === null || _b === void 0 ? void 0 : _b.type) !== null && _c !== void 0 ? _c : 'unknown'}/${(_d = M === null || M === void 0 ? void 0 : M.id) !== null && _d !== void 0 ? _d : ''}: ${(_e = e === null || e === void 0 ? void 0 : e.message) !== null && _e !== void 0 ? _e : '-'} @${errScope}`, M === null || M === void 0 ? void 0 : M.N);
|
|
727
|
-
};
|
|
728
|
-
// STEP.1 prepare the list of updater (collect type and storage info for batch mode)
|
|
729
|
-
const list = this.allProxies.reduce((L, $p) => {
|
|
730
|
-
// batch mode needs full model, legacy mode needs only diff
|
|
731
|
-
const $set = $p.alls(!useBatch, options === null || options === void 0 ? void 0 : options.onlyValid);
|
|
732
|
-
return Object.entries($set).reduce((L, [id, N]) => {
|
|
733
|
-
const hasUpdate = Object.keys(N).length > 0;
|
|
734
|
-
if (hasUpdate) {
|
|
735
|
-
(0, engine_1._log)(NS, `>> ${$p.$mgr.type}/${id} =`, engine_1.$U.json(N));
|
|
736
|
-
//* callback updater
|
|
737
|
-
const _ = (M, i) => {
|
|
738
|
-
var _a, _b;
|
|
739
|
-
const storage = (_a = M === null || M === void 0 ? void 0 : M.storage) !== null && _a !== void 0 ? _a : $p.$mgr.storage;
|
|
740
|
-
const type = (_b = M === null || M === void 0 ? void 0 : M.type) !== null && _b !== void 0 ? _b : $p.$mgr.type;
|
|
741
|
-
M = Object.assign(Object.assign({}, M), { type, storage });
|
|
742
|
-
try {
|
|
743
|
-
return storage
|
|
744
|
-
.update(id, M.N, undefined, { onlyValid })
|
|
745
|
-
.catch(e => Promise.reject($err(e, M)));
|
|
746
|
-
}
|
|
747
|
-
catch (e) {
|
|
748
|
-
throw $err(e, M);
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
L.push({ id, N, _, type: $p.$mgr.type, storage: $p.$mgr.storage });
|
|
752
|
-
}
|
|
753
|
-
return L;
|
|
754
|
-
}, L);
|
|
755
|
-
}, []);
|
|
756
|
-
// STEP.2 finally update storage.
|
|
757
|
-
if (useBatch) {
|
|
758
|
-
//* NEW: batch update mode
|
|
759
|
-
(0, engine_1._log)(NS, `> ${errScope}: using batch mode (${list.length} items)`);
|
|
760
|
-
//* group by storage instance
|
|
761
|
-
const grouped = new Map();
|
|
762
|
-
list.forEach(({ id, N, storage }) => {
|
|
763
|
-
const group = grouped.get(storage);
|
|
764
|
-
group ? group.push(Object.assign(Object.assign({}, N), { id })) : grouped.set(storage, [Object.assign(Object.assign({}, N), { id })]);
|
|
765
|
-
});
|
|
766
|
-
//* execute batch updates (slack notification is handled in doUpdateMulti)
|
|
767
|
-
const results = yield Promise.all(Array.from(grouped.entries()).map(([storage, items]) => storage.mupdate(items, { onlyValid, context: this.context, throwable: options === null || options === void 0 ? void 0 : options.throwable })));
|
|
768
|
-
//* flatten BatchResult[] to Model[]
|
|
769
|
-
const allItems = results.reduce((acc, result) => { var _a; return [...acc, ...((_a = result === null || result === void 0 ? void 0 : result.success) !== null && _a !== void 0 ? _a : [])]; }, []);
|
|
770
|
-
return allItems;
|
|
648
|
+
async saveAllUpdates(options) {
|
|
649
|
+
const parrallel = engine_1.$U.N(options?.parrallel, this.parrallel);
|
|
650
|
+
const useBatch = options?.useBatch ?? false;
|
|
651
|
+
const onlyValid = options?.onlyValid ?? true;
|
|
652
|
+
const errScope = `saveAllUpdates(${parrallel})`;
|
|
653
|
+
/**
|
|
654
|
+
* custom error class
|
|
655
|
+
* - to wrap the root cause error
|
|
656
|
+
*/
|
|
657
|
+
class MyError extends Error {
|
|
658
|
+
cause;
|
|
659
|
+
constructor(message, cause) {
|
|
660
|
+
super(message);
|
|
661
|
+
this.cause = cause;
|
|
662
|
+
this.name = 'MyError';
|
|
771
663
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
});
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* factory to create custom error
|
|
667
|
+
*/
|
|
668
|
+
const $err = (e, M) => new MyError(`Failed to update ${M?.type ?? M?.storage?.type ?? 'unknown'}/${M?.id ?? ''}: ${e?.message ?? '-'} @${errScope}`, M?.N);
|
|
669
|
+
// STEP.1 prepare the list of updater (collect type and storage info for batch mode)
|
|
670
|
+
const list = this.allProxies.reduce((L, $p) => {
|
|
671
|
+
// batch mode needs full model, legacy mode needs only diff
|
|
672
|
+
const $set = $p.alls(!useBatch, options?.onlyValid);
|
|
673
|
+
return Object.entries($set).reduce((L, [id, N]) => {
|
|
674
|
+
const hasUpdate = Object.keys(N).length > 0;
|
|
675
|
+
if (hasUpdate) {
|
|
676
|
+
(0, engine_1._log)(NS, `>> ${$p.$mgr.type}/${id} =`, engine_1.$U.json(N));
|
|
677
|
+
//* callback updater
|
|
678
|
+
const _ = (M, i) => {
|
|
679
|
+
const storage = M?.storage ?? $p.$mgr.storage;
|
|
680
|
+
const type = M?.type ?? $p.$mgr.type;
|
|
681
|
+
M = { ...M, type, storage };
|
|
682
|
+
try {
|
|
683
|
+
return storage
|
|
684
|
+
.update(id, M.N, undefined, { onlyValid })
|
|
685
|
+
.catch(e => Promise.reject($err(e, M)));
|
|
686
|
+
}
|
|
687
|
+
catch (e) {
|
|
688
|
+
throw $err(e, M);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
L.push({ id, N, _, type: $p.$mgr.type, storage: $p.$mgr.storage });
|
|
692
|
+
}
|
|
693
|
+
return L;
|
|
694
|
+
}, L);
|
|
695
|
+
}, []);
|
|
696
|
+
// STEP.2 finally update storage.
|
|
697
|
+
if (useBatch) {
|
|
698
|
+
//* NEW: batch update mode
|
|
699
|
+
(0, engine_1._log)(NS, `> ${errScope}: using batch mode (${list.length} items)`);
|
|
700
|
+
//* group by storage instance
|
|
701
|
+
const grouped = new Map();
|
|
702
|
+
list.forEach(({ id, N, storage }) => {
|
|
703
|
+
const group = grouped.get(storage);
|
|
704
|
+
group ? group.push({ ...N, id }) : grouped.set(storage, [{ ...N, id }]);
|
|
705
|
+
});
|
|
706
|
+
//* execute batch updates (slack notification is handled in doUpdateMulti)
|
|
707
|
+
const results = await Promise.all(Array.from(grouped.entries()).map(([storage, items]) => storage.mupdate(items, { onlyValid, context: this.context, throwable: options?.throwable })));
|
|
708
|
+
//* flatten BatchResult[] to Model[]
|
|
709
|
+
const allItems = results.reduce((acc, result) => [...acc, ...(result?.success ?? [])], []);
|
|
710
|
+
return allItems;
|
|
711
|
+
}
|
|
712
|
+
//* LEGACY: original parallel update (default behavior)
|
|
713
|
+
return (0, helpers_1.my_parrallel)(list, async (M, i) => {
|
|
714
|
+
return typeof M._ === 'function' ? M._(M, i) : null;
|
|
715
|
+
}, parrallel);
|
|
777
716
|
}
|
|
717
|
+
/**
|
|
718
|
+
* report via slack.
|
|
719
|
+
*/
|
|
720
|
+
report = async (title, data) => {
|
|
721
|
+
const context = this.context;
|
|
722
|
+
return (0, helpers_1.$slack)(title, data, null, { context }).catch(e => `ERR:${(0, test_helper_1.GETERR)(e)}`);
|
|
723
|
+
};
|
|
778
724
|
/**
|
|
779
725
|
* featch identity-acess from `lemon-accounts-api`
|
|
780
726
|
*
|
|
781
727
|
* @deprecated useless anymore since 3.2.10
|
|
782
728
|
*/
|
|
783
|
-
fetchIdentityAccess(identityId, domain) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
return { identityId, $identity };
|
|
799
|
-
});
|
|
729
|
+
async fetchIdentityAccess(identityId, domain) {
|
|
730
|
+
domain = helpers_1.$T.S(domain, this.context.domain);
|
|
731
|
+
if (!identityId)
|
|
732
|
+
throw new Error(`.identityId (string) is required - fetchAccess(${domain})`);
|
|
733
|
+
// 1. get user detail by invoking 'lemon-accounts-api/pack-context'
|
|
734
|
+
const service = '//lemon-accounts-api/oauth/0/pack-context';
|
|
735
|
+
const body = { domain, identityId };
|
|
736
|
+
const $identity = await (0, helpers_1.$protocol)(this.context, service)
|
|
737
|
+
.execute({}, body, 'POST')
|
|
738
|
+
.catch(test_helper_1.NUL404);
|
|
739
|
+
(0, engine_1._log)(NS, `> identity[${domain}] =`, engine_1.$U.json($identity));
|
|
740
|
+
//WARN! - $identity can be null (or .Account can be null)
|
|
741
|
+
// if (!$identity?.Account)
|
|
742
|
+
// throw new Error(`.Account(NextIdentityAccess) is invalid - fetchAccess(${domain}/${identityId})`);
|
|
743
|
+
return { identityId, $identity };
|
|
800
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* the cached identity model
|
|
747
|
+
*
|
|
748
|
+
* @deprecated useless anymore since 3.2.10
|
|
749
|
+
*/
|
|
750
|
+
_identity = {};
|
|
801
751
|
/**
|
|
802
752
|
* fetch(or load) identity.
|
|
803
753
|
*
|
|
@@ -807,43 +757,37 @@ class AbstractProxy {
|
|
|
807
757
|
*
|
|
808
758
|
* @deprecated useless anymore since 3.2.10
|
|
809
759
|
*/
|
|
810
|
-
getIdentity$(identityId, force) {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
return $identity;
|
|
822
|
-
});
|
|
760
|
+
async getIdentity$(identityId, force) {
|
|
761
|
+
if (!identityId)
|
|
762
|
+
return null;
|
|
763
|
+
// STEP.1 check if in stock.
|
|
764
|
+
const val = this._identity[identityId];
|
|
765
|
+
if (val !== undefined && !force)
|
|
766
|
+
return val;
|
|
767
|
+
// STEP.2 fetch remotely, and save in cache.
|
|
768
|
+
const { $identity } = await this.fetchIdentityAccess(identityId);
|
|
769
|
+
this._identity[identityId] = $identity ? $identity : null; //* mark as 'null' not to fetch futher
|
|
770
|
+
return $identity;
|
|
823
771
|
}
|
|
824
772
|
/**
|
|
825
773
|
* get current identity-id
|
|
826
774
|
*/
|
|
827
|
-
getCurrentIdentityId(throwable = true) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
return identityId;
|
|
833
|
-
});
|
|
775
|
+
async getCurrentIdentityId(throwable = true) {
|
|
776
|
+
const identityId = asIdentityId(this.context) || '';
|
|
777
|
+
if (!identityId && throwable)
|
|
778
|
+
throw new Error(`400 NOT ALLOWED - getCurrentIdentity(${identityId || ''})`);
|
|
779
|
+
return identityId;
|
|
834
780
|
}
|
|
835
781
|
/**
|
|
836
782
|
* get the current identity object (or throw access-error)
|
|
837
783
|
*
|
|
838
784
|
* @deprecated useless anymore since 3.2.10
|
|
839
785
|
*/
|
|
840
|
-
getCurrentIdentity$(throwable = true) {
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
return this.getIdentity$(identityId);
|
|
846
|
-
});
|
|
786
|
+
async getCurrentIdentity$(throwable = true) {
|
|
787
|
+
const identityId = await this.getCurrentIdentityId(throwable);
|
|
788
|
+
if (!identityId && !throwable)
|
|
789
|
+
return null;
|
|
790
|
+
return this.getIdentity$(identityId);
|
|
847
791
|
}
|
|
848
792
|
}
|
|
849
793
|
exports.AbstractProxy = AbstractProxy;
|
|
@@ -852,6 +796,16 @@ exports.AbstractProxy = AbstractProxy;
|
|
|
852
796
|
* - listen DynamoDBStream events and index into Elasticsearch
|
|
853
797
|
*/
|
|
854
798
|
class Elastic6Synchronizer {
|
|
799
|
+
/**
|
|
800
|
+
* model synchronizer map
|
|
801
|
+
* @private
|
|
802
|
+
*/
|
|
803
|
+
synchronizerMap;
|
|
804
|
+
/**
|
|
805
|
+
* default model synchronizer
|
|
806
|
+
* @private
|
|
807
|
+
*/
|
|
808
|
+
defModelSynchronizer;
|
|
855
809
|
/**
|
|
856
810
|
* constructor
|
|
857
811
|
* @param elastic (optional) elastic6-service instance
|
|
@@ -861,39 +815,11 @@ class Elastic6Synchronizer {
|
|
|
861
815
|
//NOTE - can be created without elastic instance (just to prepare synchronizer map)
|
|
862
816
|
// if (!elastic) throw new Error(`@elastic (elastic-service) is required!`);
|
|
863
817
|
// if (!dynamoOptions) throw new Error(`@dynamoOptions (object) is required!`);
|
|
864
|
-
/**
|
|
865
|
-
* internal callback for filtering
|
|
866
|
-
* @private
|
|
867
|
-
*/
|
|
868
|
-
this.filter = (id, item) => {
|
|
869
|
-
var _a, _b;
|
|
870
|
-
const handler = this.synchronizerMap.get(item.type);
|
|
871
|
-
if (handler)
|
|
872
|
-
return (_b = (_a = handler.filter) === null || _a === void 0 ? void 0 : _a.call(handler, id, item)) !== null && _b !== void 0 ? _b : true; // 핸들러를 등록했다면 filter 메서드를 정의하지 않았더라도 sync 한다.
|
|
873
|
-
return false; // 핸들러가 등록되지 않았다면 sync하지 않는다.
|
|
874
|
-
};
|
|
875
|
-
/**
|
|
876
|
-
* internal callback on before synchronization
|
|
877
|
-
* @private
|
|
878
|
-
*/
|
|
879
|
-
this.onBeforeSync = (id, eventName, item, diff, prev) => __awaiter(this, void 0, void 0, function* () {
|
|
880
|
-
var _a;
|
|
881
|
-
const handler = this.synchronizerMap.get(item.type);
|
|
882
|
-
if (handler)
|
|
883
|
-
yield ((_a = handler.onBeforeSync) === null || _a === void 0 ? void 0 : _a.call(handler, id, eventName, item, diff, prev));
|
|
884
|
-
});
|
|
885
|
-
/**
|
|
886
|
-
* internal callback on after synchronization
|
|
887
|
-
* @private
|
|
888
|
-
*/
|
|
889
|
-
this.onAfterSync = (id, eventName, item, diff, prev) => __awaiter(this, void 0, void 0, function* () {
|
|
890
|
-
var _b;
|
|
891
|
-
const handler = this.synchronizerMap.get(item.type);
|
|
892
|
-
if (handler)
|
|
893
|
-
yield ((_b = handler.onAfterSync) === null || _b === void 0 ? void 0 : _b.call(handler, id, eventName, item, diff, prev));
|
|
894
|
-
});
|
|
895
818
|
//* build dynamo-options as default.
|
|
896
|
-
const options = (0, test_helper_1.onlyDefined)(
|
|
819
|
+
const options = (0, test_helper_1.onlyDefined)({
|
|
820
|
+
...dynamoOptions,
|
|
821
|
+
idName: dynamoOptions?.idName || '_id', // id (global) of dynamo-table. should be `_id`.
|
|
822
|
+
});
|
|
897
823
|
//* create sync-handler w/ this.
|
|
898
824
|
const listener = cores_1.LambdaDynamoStreamHandler.createSyncToElastic6(options, elastic, this.filter.bind(this), this.onBeforeSync.bind(this), this.onAfterSync.bind(this));
|
|
899
825
|
cores_1.default.lambda.dynamos.addListener(listener); // register DynamoStream event listener
|
|
@@ -901,9 +827,8 @@ class Elastic6Synchronizer {
|
|
|
901
827
|
this.synchronizerMap = new Map();
|
|
902
828
|
this.defModelSynchronizer = new (class {
|
|
903
829
|
filter(id, item) {
|
|
904
|
-
|
|
905
|
-
const
|
|
906
|
-
const stereo = `${(_b = item === null || item === void 0 ? void 0 : item.stereo) !== null && _b !== void 0 ? _b : ''}`;
|
|
830
|
+
const type = `${item?.type ?? ''}`;
|
|
831
|
+
const stereo = `${item?.stereo ?? ''}`;
|
|
907
832
|
return !(type.startsWith('#') || stereo.startsWith('#')); // special purpose item. do not index.
|
|
908
833
|
}
|
|
909
834
|
})();
|
|
@@ -914,8 +839,36 @@ class Elastic6Synchronizer {
|
|
|
914
839
|
* @param handler (optional) custom synchronizer.
|
|
915
840
|
*/
|
|
916
841
|
enableSynchronization(type, handler) {
|
|
917
|
-
this.synchronizerMap.set(type, handler
|
|
842
|
+
this.synchronizerMap.set(type, handler ?? this.defModelSynchronizer);
|
|
918
843
|
}
|
|
844
|
+
/**
|
|
845
|
+
* internal callback for filtering
|
|
846
|
+
* @private
|
|
847
|
+
*/
|
|
848
|
+
filter = (id, item) => {
|
|
849
|
+
const handler = this.synchronizerMap.get(item.type);
|
|
850
|
+
if (handler)
|
|
851
|
+
return handler.filter?.(id, item) ?? true; // 핸들러를 등록했다면 filter 메서드를 정의하지 않았더라도 sync 한다.
|
|
852
|
+
return false; // 핸들러가 등록되지 않았다면 sync하지 않는다.
|
|
853
|
+
};
|
|
854
|
+
/**
|
|
855
|
+
* internal callback on before synchronization
|
|
856
|
+
* @private
|
|
857
|
+
*/
|
|
858
|
+
onBeforeSync = async (id, eventName, item, diff, prev) => {
|
|
859
|
+
const handler = this.synchronizerMap.get(item.type);
|
|
860
|
+
if (handler)
|
|
861
|
+
await handler.onBeforeSync?.(id, eventName, item, diff, prev);
|
|
862
|
+
};
|
|
863
|
+
/**
|
|
864
|
+
* internal callback on after synchronization
|
|
865
|
+
* @private
|
|
866
|
+
*/
|
|
867
|
+
onAfterSync = async (id, eventName, item, diff, prev) => {
|
|
868
|
+
const handler = this.synchronizerMap.get(item.type);
|
|
869
|
+
if (handler)
|
|
870
|
+
await handler.onAfterSync?.(id, eventName, item, diff, prev);
|
|
871
|
+
};
|
|
919
872
|
}
|
|
920
873
|
exports.Elastic6Synchronizer = Elastic6Synchronizer;
|
|
921
874
|
/**
|
|
@@ -923,6 +876,22 @@ exports.Elastic6Synchronizer = Elastic6Synchronizer;
|
|
|
923
876
|
* - to manipulate the shared Elasticsearch resources.
|
|
924
877
|
*/
|
|
925
878
|
class AbstractElastic6Instance {
|
|
879
|
+
/**
|
|
880
|
+
* Elasticsearch client
|
|
881
|
+
*/
|
|
882
|
+
client;
|
|
883
|
+
/**
|
|
884
|
+
* Elastic6Service instance
|
|
885
|
+
*/
|
|
886
|
+
elastic;
|
|
887
|
+
/**
|
|
888
|
+
* Elastic6QueryService instance
|
|
889
|
+
*/
|
|
890
|
+
query;
|
|
891
|
+
/**
|
|
892
|
+
* Elastic6Synchronizer instance
|
|
893
|
+
*/
|
|
894
|
+
synchronizer;
|
|
926
895
|
/**
|
|
927
896
|
* default constructor
|
|
928
897
|
*/
|
|
@@ -955,93 +924,86 @@ class AbstractElastic6Instance {
|
|
|
955
924
|
/**
|
|
956
925
|
* create Elasticsearch index w/ custom settings
|
|
957
926
|
*/
|
|
958
|
-
createIndex() {
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
}
|
|
971
|
-
else {
|
|
972
|
-
settings.mappings.properties = Object.assign(Object.assign({}, (((_b = settings.mappings) === null || _b === void 0 ? void 0 : _b.properties) || {})), $set);
|
|
973
|
-
}
|
|
974
|
-
return this.elastic.createIndex(settings);
|
|
927
|
+
async createIndex() {
|
|
928
|
+
if (this.elastic) {
|
|
929
|
+
const { docType, idName } = this.options;
|
|
930
|
+
const settings = cores_1.Elastic6Service.prepareSettings({ docType, idName }); // default setting
|
|
931
|
+
const { version } = this.elastic.options;
|
|
932
|
+
// force set type of 'score_' field
|
|
933
|
+
const ver = engine_1.$U.F(version, 7.0);
|
|
934
|
+
const $set = { score_: { type: 'half_float' } };
|
|
935
|
+
if (ver < 7.0) {
|
|
936
|
+
settings.mappings[docType].properties = {
|
|
937
|
+
...(settings.mappings[docType]?.properties || {}),
|
|
938
|
+
...$set,
|
|
939
|
+
};
|
|
975
940
|
}
|
|
976
|
-
|
|
977
|
-
|
|
941
|
+
else {
|
|
942
|
+
settings.mappings.properties = {
|
|
943
|
+
...(settings.mappings?.properties || {}),
|
|
944
|
+
...$set,
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
return this.elastic.createIndex(settings);
|
|
948
|
+
}
|
|
949
|
+
return null;
|
|
978
950
|
}
|
|
979
951
|
/**
|
|
980
952
|
* destroy Elasticsearch index
|
|
981
953
|
*/
|
|
982
|
-
destroyIndex() {
|
|
983
|
-
return
|
|
984
|
-
return this.elastic && (yield this.elastic.destroyIndex());
|
|
985
|
-
});
|
|
954
|
+
async destroyIndex() {
|
|
955
|
+
return this.elastic && (await this.elastic.destroyIndex());
|
|
986
956
|
}
|
|
987
957
|
/**
|
|
988
958
|
* display index settings and mappings
|
|
989
959
|
*/
|
|
990
|
-
describeIndex() {
|
|
991
|
-
return
|
|
992
|
-
return this.elastic && (yield this.elastic.describe());
|
|
993
|
-
});
|
|
960
|
+
async describeIndex() {
|
|
961
|
+
return this.elastic && (await this.elastic.describe());
|
|
994
962
|
}
|
|
995
963
|
/**
|
|
996
964
|
* multi get
|
|
997
965
|
* @param _ids _id list
|
|
998
966
|
*/
|
|
999
|
-
mget(_ids) {
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
},
|
|
1007
|
-
});
|
|
1008
|
-
// _log(NS, `> res =`, $U.json({ ...$res, meta: undefined }));
|
|
1009
|
-
const { docs } = $res.body;
|
|
1010
|
-
const idName = this.options.idName;
|
|
1011
|
-
return docs.map((doc) => (doc.found ? sourceToItem(doc._source, idName) : null));
|
|
967
|
+
async mget(_ids) {
|
|
968
|
+
const $res = await this.client.mget({
|
|
969
|
+
index: this.options.indexName,
|
|
970
|
+
type: this.options.docType,
|
|
971
|
+
body: {
|
|
972
|
+
docs: _ids.map(_id => ({ _id })),
|
|
973
|
+
},
|
|
1012
974
|
});
|
|
975
|
+
// _log(NS, `> res =`, $U.json({ ...$res, meta: undefined }));
|
|
976
|
+
const { docs } = $res.body;
|
|
977
|
+
const idName = this.options.idName;
|
|
978
|
+
return docs.map((doc) => (doc.found ? sourceToItem(doc._source, idName) : null));
|
|
1013
979
|
}
|
|
1014
980
|
/**
|
|
1015
981
|
* search raw query
|
|
1016
982
|
* @param body Elasticsearch Query DSL
|
|
1017
983
|
* @param params see 'search_type' in Elasticsearch documentation
|
|
1018
984
|
*/
|
|
1019
|
-
search(body, params) {
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
return elastic.search(body, searchType);
|
|
1028
|
-
});
|
|
985
|
+
async search(body, params) {
|
|
986
|
+
if (!this.elastic)
|
|
987
|
+
throw new Error(`Could not read Elasticsearch endpoint or index setting.`);
|
|
988
|
+
const searchType = params?.searchType;
|
|
989
|
+
const elastic = params?.indexName
|
|
990
|
+
? new cores_1.Elastic6Service({ ...this.options, indexName: params.indexName })
|
|
991
|
+
: this.elastic;
|
|
992
|
+
return elastic.search(body, searchType);
|
|
1029
993
|
}
|
|
1030
994
|
/**
|
|
1031
995
|
* create async generator that yields items queried until last
|
|
1032
996
|
* @param body Elasticsearch Query DSL
|
|
1033
997
|
* @param searchType see 'search_type' in Elasticsearch documentation
|
|
1034
998
|
*/
|
|
1035
|
-
generateSearchResult(body, searchType) {
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
} while (body.search_after);
|
|
1044
|
-
});
|
|
999
|
+
async *generateSearchResult(body, searchType) {
|
|
1000
|
+
if (!body.sort)
|
|
1001
|
+
body.sort = '_doc';
|
|
1002
|
+
do {
|
|
1003
|
+
const { list, last } = await this.search(body, { searchType });
|
|
1004
|
+
body.search_after = last;
|
|
1005
|
+
yield list;
|
|
1006
|
+
} while (body.search_after);
|
|
1045
1007
|
}
|
|
1046
1008
|
}
|
|
1047
1009
|
exports.AbstractElastic6Instance = AbstractElastic6Instance;
|
|
@@ -1069,6 +1031,7 @@ exports.Elastic6Instance = Elastic6Instance;
|
|
|
1069
1031
|
* - proxied agent to handle `search()`
|
|
1070
1032
|
*/
|
|
1071
1033
|
class Elastic6Proxy extends Elastic6Instance {
|
|
1034
|
+
proxy;
|
|
1072
1035
|
constructor(params, proxy) {
|
|
1073
1036
|
super(params);
|
|
1074
1037
|
this.proxy = proxy;
|
|
@@ -1081,23 +1044,17 @@ class Elastic6Proxy extends Elastic6Instance {
|
|
|
1081
1044
|
*
|
|
1082
1045
|
* @override
|
|
1083
1046
|
*/
|
|
1084
|
-
search(body, params) {
|
|
1085
|
-
const
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
const signature = (_e = params === null || params === void 0 ? void 0 : params.signature) !== null && _e !== void 0 ? _e : _hmac([_hmac(service), indexName]);
|
|
1096
|
-
const $body = { body, service, index: indexName, signature };
|
|
1097
|
-
return this.proxy.doProxy('POST', null, null, params, $body);
|
|
1098
|
-
}
|
|
1099
|
-
return _super.search.call(this, body, params);
|
|
1100
|
-
});
|
|
1047
|
+
async search(body, params) {
|
|
1048
|
+
const _hmac = (s, delim = ':', prefix = 'v1') => [prefix, engine_1.$U.hmac(Array.isArray(s) ? s.join(delim) : s, 'base64')].join(delim);
|
|
1049
|
+
//* use proxy if possible.
|
|
1050
|
+
if (this.proxy) {
|
|
1051
|
+
const service = (0, helpers_1.$info)()?.service;
|
|
1052
|
+
const indexName = params?.indexName ?? this.options?.indexName ?? '';
|
|
1053
|
+
const signature = params?.signature ?? _hmac([_hmac(service), indexName]);
|
|
1054
|
+
const $body = { body, service, index: indexName, signature };
|
|
1055
|
+
return this.proxy.doProxy('POST', null, null, params, $body);
|
|
1056
|
+
}
|
|
1057
|
+
return super.search(body, params);
|
|
1101
1058
|
}
|
|
1102
1059
|
}
|
|
1103
1060
|
/**
|
|
@@ -1108,14 +1065,13 @@ class Elastic6Proxy extends Elastic6Instance {
|
|
|
1108
1065
|
* @param idName (optional) global id of elastic. (default is `$id`)
|
|
1109
1066
|
*/
|
|
1110
1067
|
function sourceToItem(_source, idName = '$id') {
|
|
1111
|
-
const item =
|
|
1068
|
+
const item = { ..._source };
|
|
1112
1069
|
if (idName in item) {
|
|
1113
1070
|
item._id = item[idName];
|
|
1114
1071
|
delete item[idName];
|
|
1115
1072
|
}
|
|
1116
1073
|
return item;
|
|
1117
1074
|
}
|
|
1118
|
-
exports.sourceToItem = sourceToItem;
|
|
1119
1075
|
/**
|
|
1120
1076
|
* internal helper function.
|
|
1121
1077
|
*/
|
|
@@ -1128,21 +1084,20 @@ const $X = {
|
|
|
1128
1084
|
* @param url
|
|
1129
1085
|
*/
|
|
1130
1086
|
describeEndpointUrl: (url, options) => {
|
|
1131
|
-
|
|
1132
|
-
const
|
|
1133
|
-
const throwable = (_b = options === null || options === void 0 ? void 0 : options.throwable) !== null && _b !== void 0 ? _b : true;
|
|
1087
|
+
const errScope = options?.errScope ?? `describeEndpointUrl()`;
|
|
1088
|
+
const throwable = options?.throwable ?? true;
|
|
1134
1089
|
url = /^\/\/[a-z0-9]+/.test(url) ? `https:${url}` : url;
|
|
1135
1090
|
if (throwable) {
|
|
1136
1091
|
if (!url)
|
|
1137
1092
|
throw new Error(`@url(string) is required - ${errScope}`);
|
|
1138
|
-
if (!(
|
|
1139
|
-
throw new Error(`@url[${url
|
|
1093
|
+
if (!(url?.startsWith('http://') || url?.startsWith('https://')))
|
|
1094
|
+
throw new Error(`@url[${url ?? ''}] is invalid (no http) - ${errScope}`);
|
|
1140
1095
|
}
|
|
1141
1096
|
const $url = new URL(url);
|
|
1142
1097
|
const host = engine_1.$U.S($url.hostname || $url.host);
|
|
1143
1098
|
const isTunnel = $url.hostname === 'localhost';
|
|
1144
|
-
const protocol =
|
|
1145
|
-
const port = engine_1.$U.N($url.port,
|
|
1099
|
+
const protocol = engine_1.$U.S($url.protocol).split(':')[0] ?? '';
|
|
1100
|
+
const port = engine_1.$U.N($url.port, url?.startsWith('https') ? 443 : 80);
|
|
1146
1101
|
const hosts = host.split('.');
|
|
1147
1102
|
//* use `/search/0/proxy` working with deployed enpoint (requires access-key)
|
|
1148
1103
|
const isProxy = host.endsWith('.amazonaws.com') && hosts[hosts.length - 4] == 'execute-api';
|
|
@@ -1174,29 +1129,27 @@ const $X = {
|
|
|
1174
1129
|
* @param options optionals parameters.
|
|
1175
1130
|
*/
|
|
1176
1131
|
createHttpSearchProxy: (endpoint, options) => {
|
|
1177
|
-
|
|
1178
|
-
const
|
|
1179
|
-
const errScope = `createHttpSearchProxy(${name !== null && name !== void 0 ? name : ''})`;
|
|
1132
|
+
const name = options?.name ?? engine_1.$U.env('NAME');
|
|
1133
|
+
const errScope = `createHttpSearchProxy(${name ?? ''})`;
|
|
1180
1134
|
if (!endpoint)
|
|
1181
1135
|
throw new Error(`@endpoint (url) is required - ${errScope}`);
|
|
1182
1136
|
const NS = engine_1.$U.NS(`X${name}`, 'magenta'); // NAMESPACE TO BE PRINTED.
|
|
1183
|
-
const headers = options
|
|
1184
|
-
const encoder =
|
|
1185
|
-
const relayHeaderKey =
|
|
1186
|
-
const resultKey =
|
|
1137
|
+
const headers = options?.headers;
|
|
1138
|
+
const encoder = options?.encoder ?? ((name, path) => path);
|
|
1139
|
+
const relayHeaderKey = options?.relayHeaderKey || '';
|
|
1140
|
+
const resultKey = options?.resultKey ?? '';
|
|
1187
1141
|
// initialize AWS SigV4 Client
|
|
1188
1142
|
const _client = () => {
|
|
1189
|
-
|
|
1190
|
-
if (!(options === null || options === void 0 ? void 0 : options.credentials))
|
|
1143
|
+
if (!options?.credentials)
|
|
1191
1144
|
return null;
|
|
1192
|
-
const $cred = options
|
|
1145
|
+
const $cred = options?.credentials;
|
|
1193
1146
|
const $info = $X.describeEndpointUrl(endpoint, { throwable: false });
|
|
1194
1147
|
const config = {
|
|
1195
|
-
accessKey: $cred
|
|
1196
|
-
secretKey: $cred
|
|
1148
|
+
accessKey: $cred?.accessKeyId,
|
|
1149
|
+
secretKey: $cred?.secretAccessKey,
|
|
1197
1150
|
serviceName: 'execute-api',
|
|
1198
|
-
host: $info
|
|
1199
|
-
region:
|
|
1151
|
+
host: $info?.host,
|
|
1152
|
+
region: options?.region ?? $info?.region,
|
|
1200
1153
|
endpoint,
|
|
1201
1154
|
};
|
|
1202
1155
|
return (0, sig_v4_1.sigV4Client)(config);
|
|
@@ -1208,9 +1161,8 @@ const $X = {
|
|
|
1208
1161
|
*/
|
|
1209
1162
|
return new (class {
|
|
1210
1163
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1211
|
-
constructor() {
|
|
1212
|
-
|
|
1213
|
-
}
|
|
1164
|
+
constructor() { }
|
|
1165
|
+
hello = () => `http-search-proxy:${name}`;
|
|
1214
1166
|
doProxy(method, path1, path2, $param, $body, ctx) {
|
|
1215
1167
|
if (!method)
|
|
1216
1168
|
throw new Error(`@method is required - ${errScope}`);
|
|
@@ -1229,7 +1181,7 @@ const $X = {
|
|
|
1229
1181
|
const options = {
|
|
1230
1182
|
method,
|
|
1231
1183
|
uri: url,
|
|
1232
|
-
headers:
|
|
1184
|
+
headers: { ...headers },
|
|
1233
1185
|
body: $body === null ? undefined : $body,
|
|
1234
1186
|
json: typeof $body === 'string' ? false : true,
|
|
1235
1187
|
};
|
|
@@ -1243,7 +1195,7 @@ const $X = {
|
|
|
1243
1195
|
headers: options.headers,
|
|
1244
1196
|
body: $body,
|
|
1245
1197
|
});
|
|
1246
|
-
options.headers =
|
|
1198
|
+
options.headers = { ...options.headers, ...signedRequest.headers };
|
|
1247
1199
|
options.uri = signedRequest.url;
|
|
1248
1200
|
}
|
|
1249
1201
|
//* relay HEADERS to `WEB-API`
|
|
@@ -1251,7 +1203,7 @@ const $X = {
|
|
|
1251
1203
|
options.headers = Object.keys(headers).reduce((H, key) => {
|
|
1252
1204
|
const val = headers[key];
|
|
1253
1205
|
const name = `${relayHeaderKey}${key}`;
|
|
1254
|
-
const text = `${val
|
|
1206
|
+
const text = `${val ?? ''}`;
|
|
1255
1207
|
H[name] = text;
|
|
1256
1208
|
return H;
|
|
1257
1209
|
}, options.headers);
|
|
@@ -1313,28 +1265,26 @@ const $X = {
|
|
|
1313
1265
|
* @param options (optional) for debugging
|
|
1314
1266
|
*/
|
|
1315
1267
|
const _ES6 = (options) => {
|
|
1316
|
-
var _a, _b, _c;
|
|
1317
1268
|
// 0. load from env configuration.
|
|
1318
|
-
const endpoint =
|
|
1319
|
-
const indexName =
|
|
1269
|
+
const endpoint = options?.endpoint ?? engine_1.$U.env('ES6_ENDPOINT', '');
|
|
1270
|
+
const indexName = options?.indexName ?? engine_1.$U.env('ES6_INDEX', 'test-v1');
|
|
1320
1271
|
const esVersion = engine_1.$U.env('ES6_VERSION', '6.8'); //* version of elastic server (default 6.8)
|
|
1321
1272
|
const esDocType = engine_1.$U.env('ES6_DOCTYPE', ''); //* version of elastic server (default `_doc`)
|
|
1322
1273
|
const tableName = engine_1.$U.env('MY_DYNAMO_TABLE', 'Test');
|
|
1323
1274
|
const autocompleteFields = helpers_1.$T.SS(engine_1.$U.env('ES6_AUTOCOMPLETE_FIELDS', ''));
|
|
1324
1275
|
// use search-proxy by detectiong automatically.
|
|
1325
1276
|
const _isProxy = () => {
|
|
1326
|
-
|
|
1327
|
-
const isProxy = (_a = engine_1.$U.env('ES6_IS_PROXY', '')) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
1277
|
+
const isProxy = engine_1.$U.env('ES6_IS_PROXY', '')?.toLowerCase();
|
|
1328
1278
|
if (isProxy == '1' || isProxy == 'y' || isProxy == 'true')
|
|
1329
1279
|
return true;
|
|
1330
1280
|
if (endpoint) {
|
|
1331
1281
|
const $res = $X.describeEndpointUrl(endpoint, { throwable: false });
|
|
1332
|
-
if ($res
|
|
1282
|
+
if ($res?.isProxy)
|
|
1333
1283
|
return true;
|
|
1334
1284
|
}
|
|
1335
1285
|
return false;
|
|
1336
1286
|
};
|
|
1337
|
-
const useProxy =
|
|
1287
|
+
const useProxy = options?.useProxy ?? _isProxy();
|
|
1338
1288
|
// prepare constructor parameters.
|
|
1339
1289
|
const params = {
|
|
1340
1290
|
endpoint,
|
|
@@ -1346,7 +1296,7 @@ const _ES6 = (options) => {
|
|
|
1346
1296
|
};
|
|
1347
1297
|
//* use proxy.
|
|
1348
1298
|
if (useProxy) {
|
|
1349
|
-
const credentials = options
|
|
1299
|
+
const credentials = options?.credentials;
|
|
1350
1300
|
const proxy = $X.createHttpSearchProxy(endpoint, { credentials });
|
|
1351
1301
|
return new Elastic6Proxy(params, proxy);
|
|
1352
1302
|
}
|