corebasic 1.0.205 → 1.0.208
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/libs/dip/suffix/date.js +103 -0
- package/libs/dip/suffix/index.js +249 -0
- package/libs/dip/suffix/policy.js +156 -0
- package/libs/dip/suffix/query.js +289 -0
- package/libs/dip/suffix/suffix.js +109 -0
- package/libs/dip/suffix/tests/date.test.js +416 -0
- package/libs/dip/suffix/tests/index.test.js +653 -0
- package/libs/dip/suffix/tests/policy.applySuffixPolicy.test.js +642 -0
- package/libs/dip/suffix/tests/policy.test.js +675 -0
- package/libs/dip/suffix/tests/suffix.test.js +622 -0
- package/libs/dip.js +5 -3
- package/libs/elabase.js +43 -31
- package/libs/features.js +23 -13
- package/package.json +1 -1
package/libs/elabase.js
CHANGED
|
@@ -2,7 +2,7 @@ import axios from 'axios'
|
|
|
2
2
|
import {default as dipper, shard_stats as ShardStats} from './dipper.js'
|
|
3
3
|
import * as Utils from './utils.js'
|
|
4
4
|
import * as Cpp from './cpp.js'
|
|
5
|
-
|
|
5
|
+
import {applySuffixPolicy} from './dip/suffix/policy.js'
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -74,17 +74,17 @@ export const globalMeta = args => {
|
|
|
74
74
|
}
|
|
75
75
|
export const companyMeta = (company, args) => {
|
|
76
76
|
if (typeof company !== "string" && !Array.isArray(company))
|
|
77
|
-
throw
|
|
77
|
+
throw new Error("Error: Invalid company provided in Dip.companyMeta()")
|
|
78
78
|
if (!company || (typeof company === "string" && company.trim() === "") || company === "GLOBAL" || (Array.isArray(company) && (!company.length || company.some(item => typeof item !== "string" || item.trim() === "" || item === "GLOBAL")) ))
|
|
79
|
-
throw
|
|
79
|
+
throw new Error("Error: Invalid company provided in Dip.companyMeta()")
|
|
80
80
|
|
|
81
81
|
return new DipMeta({...args, outlet: "GLOBAL", company})
|
|
82
82
|
}
|
|
83
83
|
export const outletMeta = (outlet, args) => {
|
|
84
84
|
if (typeof outlet !== "string" && !Array.isArray(outlet))
|
|
85
|
-
throw
|
|
85
|
+
throw new Error("Error: Invalid outlet provided in Dip.outletMeta()")
|
|
86
86
|
if (!outlet || (typeof outlet === "string" && outlet.trim() === "") || outlet === "GLOBAL" || (Array.isArray(outlet) && (!outlet.length || outlet.some(item => typeof item !== "string" || item.trim() === "" || item === "GLOBAL")) ))
|
|
87
|
-
throw
|
|
87
|
+
throw new Error("Error: Invalid outlet provided in Dip.outletMeta()")
|
|
88
88
|
|
|
89
89
|
return new DipMeta({...args, company: "GLOBAL", outlet})
|
|
90
90
|
}
|
|
@@ -92,38 +92,38 @@ export const outletMeta = (outlet, args) => {
|
|
|
92
92
|
export const customMeta = (company, outlet, args) => {
|
|
93
93
|
|
|
94
94
|
if (typeof company !== "string" && !Array.isArray(company))
|
|
95
|
-
throw
|
|
95
|
+
throw new Error("Error: Invalid company provided in Dip.customMeta()")
|
|
96
96
|
if (typeof outlet !== "string" && !Array.isArray(outlet))
|
|
97
|
-
throw
|
|
97
|
+
throw new Error("Error: Invalid outlet provided in Dip.customMeta()")
|
|
98
98
|
|
|
99
99
|
if (!company || (typeof company === "string" && company.trim() === "") || company === "GLOBAL" || (Array.isArray(company) && (!company.length || company.some(item => typeof item !== "string" || item.trim() === "" || item === "GLOBAL")) ))
|
|
100
|
-
throw
|
|
100
|
+
throw new Error("Error: Invalid company provided in Dip.customMeta()"}
|
|
101
101
|
if (!outlet || (typeof outlet === "string" && outlet.trim() === "") || outlet === "GLOBAL" || (Array.isArray(outlet) && (!outlet.length || outlet.some(item => typeof item !== "string" || item.trim() === "" || item === "GLOBAL")) ))
|
|
102
|
-
throw
|
|
102
|
+
throw new Error("Error: Invalid outlet provided in Dip.customMeta()")
|
|
103
103
|
|
|
104
104
|
return new DipMeta({...args, company, outlet})
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function validate_collection(collection, fn_name) {
|
|
108
108
|
if ((typeof collection === 'string' && collection.trim() === "") || (typeof collection !== 'string' && !Array.isArray(collection)) || (Array.isArray(collection) && (!collection.length || collection.some(item => typeof item !== "string" || item.trim() === "")))) {
|
|
109
|
-
throw
|
|
109
|
+
throw new Error(`Error: Invalid collection ${fn_name}`)
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
function validate_meta(meta, fn_name) {
|
|
114
114
|
if (!meta || typeof meta !== 'object') {
|
|
115
|
-
throw
|
|
115
|
+
throw new Error(`Error: Invalid meta ${fn_name}`)
|
|
116
116
|
}
|
|
117
117
|
if ((typeof meta.company === 'string' && meta.company.trim() === "") || (typeof meta.company !== 'string' && !Array.isArray(meta.company)) || (Array.isArray(meta.company) && (!meta.company.length || meta.company.some(item => typeof item !== "string"|| item.trim() === "")))) {
|
|
118
|
-
throw
|
|
118
|
+
throw new Error(`Error: Invalid meta.company ${fn_name}`)
|
|
119
119
|
}
|
|
120
120
|
if ((typeof meta.outlet === 'string' && meta.outlet.trim() === "") || (typeof meta.outlet !== 'string' && !Array.isArray(meta.outlet)) || (Array.isArray(meta.outlet) && (!meta.outlet.length || meta.outlet.some(item => typeof item !== "string" || item.trim() === "")))) {
|
|
121
|
-
throw
|
|
121
|
+
throw new Error(`Error: Invalid meta.outlet ${fn_name}`)
|
|
122
122
|
}
|
|
123
123
|
if ((meta.suffix !== undefined && typeof meta.suffix !== 'string' && !Array.isArray(meta.suffix)) || (Array.isArray(meta.suffix) && meta.suffix.some(item => typeof item !== "string" || item.trim() === ""))) {
|
|
124
|
-
throw
|
|
124
|
+
throw new Error(`Error: Invalid meta.suffix ${fn_name}`)
|
|
125
125
|
} else if (typeof meta.suffix === 'string' && meta.suffix.trim() === "")
|
|
126
|
-
throw
|
|
126
|
+
throw new Error(`Error: Invalid meta.suffix ${fn_name}`)
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function generate_suffix(meta) {
|
|
@@ -202,7 +202,7 @@ export const batch = async (callback, arg) => { // Convenience function with aut
|
|
|
202
202
|
// Ported
|
|
203
203
|
export function batchSubmit(batch_id, arg) {
|
|
204
204
|
if (!batch_id || !batches[batch_id] || !batches[batch_id].length)
|
|
205
|
-
throw
|
|
205
|
+
throw new Error(`Error: Invalid batch in Dip.batchSubmit()`)
|
|
206
206
|
let txns = batches[batch_id]
|
|
207
207
|
const executor = arg?.executor ?? batches[batch_id].find(txn => txn.executor)?.executor
|
|
208
208
|
const syncMode = arg?.syncMode ?? batches[batch_id].find(txn => txn.syncMode)?.syncMode
|
|
@@ -231,7 +231,7 @@ export const schema = async (name) => {
|
|
|
231
231
|
} catch (e) {
|
|
232
232
|
// Delete the broken promise from cache so a retry can occur later
|
|
233
233
|
delete SCHEMAS_JSON_PROMISES[name]
|
|
234
|
-
throw
|
|
234
|
+
throw new Error(`Error: Failed to load schema ${name} in Dip`)
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -248,13 +248,20 @@ function init_collections_config() {
|
|
|
248
248
|
for (let collection in COLLECTIONS_JSON) {
|
|
249
249
|
let config = COLLECTIONS_JSON[collection]
|
|
250
250
|
if (config.suffix !== undefined && (typeof config.suffix !== "object" || Array.isArray(config.suffix)) ) {
|
|
251
|
-
throw
|
|
251
|
+
throw new Error(`Error: Initiating collection configs failed. Invalid suffix in collection: ${collection}`)
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
if (COLLECTIONS_JSON)
|
|
256
256
|
init_collections_config()
|
|
257
257
|
|
|
258
|
+
const EXCLUDED_COLLECTIONS_CONFIG = {}
|
|
259
|
+
export function excludeCollectionConfig(collection) {
|
|
260
|
+
EXCLUDED_COLLECTIONS_CONFIG[collection] = true
|
|
261
|
+
}
|
|
262
|
+
export function includeCollectionConfig(collection) {
|
|
263
|
+
delete EXCLUDED_COLLECTIONS_CONFIG[collection]
|
|
264
|
+
}
|
|
258
265
|
|
|
259
266
|
function validate_topology(meta, collection, fn_name) {
|
|
260
267
|
|
|
@@ -262,15 +269,16 @@ function validate_topology(meta, collection, fn_name) {
|
|
|
262
269
|
|
|
263
270
|
let config = COLLECTIONS_JSON?.[collection]
|
|
264
271
|
if (!config) {
|
|
265
|
-
|
|
272
|
+
if (!EXCLUDED_COLLECTIONS_CONFIG[collection])
|
|
273
|
+
console.warn(`Warn: Missing config for collection: ${collection}${fn_name ? ` ${fn_name}` : ''}`)
|
|
266
274
|
return new_meta
|
|
267
275
|
}
|
|
268
276
|
|
|
269
277
|
function check_core_meta(config, meta, suffix) {
|
|
270
278
|
if (config.company !== meta.company && typeof config.company !== "object")
|
|
271
|
-
throw
|
|
279
|
+
throw new Error(`Error: Incompatible meta.company config for collection: ${collection}${suffix ? ' suffix: ' + suffix : ''}${fn_name ? ` ${fn_name}` : ''}`)
|
|
272
280
|
if (config.outlet !== meta.outlet && typeof config.outlet !== "object")
|
|
273
|
-
throw
|
|
281
|
+
throw new Error(`Error: Incompatible meta.outlet config for collection: ${collection}${suffix ? ' suffix: ' + suffix : ''}${fn_name ? ` ${fn_name}` : ''}`)
|
|
274
282
|
}
|
|
275
283
|
|
|
276
284
|
check_core_meta(config, meta)
|
|
@@ -315,7 +323,8 @@ export const insert = async (meta, collection, value, options, extras) => {
|
|
|
315
323
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
316
324
|
}
|
|
317
325
|
arg = Object.assign(arg, extras)
|
|
318
|
-
arg.suffix =
|
|
326
|
+
arg.suffix = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.insert, true, arg)
|
|
327
|
+
arg.suffix = (arg.suffix ?? []).concat(generate_suffix(meta))
|
|
319
328
|
arg.executor = meta.executor ?? "native"
|
|
320
329
|
arg = Object.assign({}, topology_meta, arg)
|
|
321
330
|
if (options?._id) {
|
|
@@ -325,7 +334,7 @@ export const insert = async (meta, collection, value, options, extras) => {
|
|
|
325
334
|
|
|
326
335
|
if (meta.batch) {
|
|
327
336
|
if (!batches[meta.batch])
|
|
328
|
-
throw
|
|
337
|
+
throw new Error('Error: Invalid batch in Dip.insert()')
|
|
329
338
|
batches[meta.batch].push(arg)
|
|
330
339
|
return
|
|
331
340
|
}
|
|
@@ -357,12 +366,13 @@ export const query = async (meta, collection, query, options, extras) => {
|
|
|
357
366
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
358
367
|
}
|
|
359
368
|
arg = Object.assign(arg, extras)
|
|
360
|
-
arg.suffix =
|
|
369
|
+
arg.suffix = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
370
|
+
arg.suffix = (arg.suffix ?? []).concat(generate_suffix(meta))
|
|
361
371
|
arg.executor = meta.executor ?? "native"
|
|
362
372
|
arg = Object.assign({}, topology_meta, arg)
|
|
363
373
|
if (meta.batch) {
|
|
364
374
|
if (!batches[meta.batch])
|
|
365
|
-
throw
|
|
375
|
+
throw new Error('Error: Invalid batch in Dip.query()')
|
|
366
376
|
batches[meta.batch].push(arg)
|
|
367
377
|
return
|
|
368
378
|
}
|
|
@@ -408,12 +418,13 @@ export const update = async (meta, collection, query, update, options, extras) =
|
|
|
408
418
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
409
419
|
}
|
|
410
420
|
arg = Object.assign(arg, extras)
|
|
411
|
-
arg.suffix =
|
|
421
|
+
arg.suffix = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
422
|
+
arg.suffix = (arg.suffix ?? []).concat(generate_suffix(meta))
|
|
412
423
|
arg.executor = meta.executor ?? "native"
|
|
413
424
|
arg = Object.assign({}, topology_meta, arg)
|
|
414
425
|
if (meta.batch) {
|
|
415
426
|
if (!batches[meta.batch])
|
|
416
|
-
throw
|
|
427
|
+
throw new Error('Error: Invalid batch in Dip.update()')
|
|
417
428
|
batches[meta.batch].push(arg)
|
|
418
429
|
return
|
|
419
430
|
}
|
|
@@ -433,7 +444,7 @@ export const remove = async (meta, collection, query, options, extras) => {
|
|
|
433
444
|
validate_collection(collection, 'in Dip.remove()')
|
|
434
445
|
|
|
435
446
|
if (Utils.isEmpty(query._id) && !meta.allowDangerousRemove)
|
|
436
|
-
throw
|
|
447
|
+
throw new Error("Error: Dip.remove() without _id blocked by default (Dangerous Operation). Set meta.allowDangerousRemove if intentional.")
|
|
437
448
|
|
|
438
449
|
validate_meta(meta, 'in Dip.remove()')
|
|
439
450
|
|
|
@@ -449,12 +460,13 @@ export const remove = async (meta, collection, query, options, extras) => {
|
|
|
449
460
|
...(meta.DIP_DB ? {db: meta.DIP_DB} : {}),
|
|
450
461
|
}
|
|
451
462
|
arg = Object.assign(arg, extras)
|
|
452
|
-
arg.suffix =
|
|
463
|
+
arg.suffix = await applySuffixPolicy(COLLECTIONS_JSON, collection, arg.query, false, arg)
|
|
464
|
+
arg.suffix = (arg.suffix ?? []).concat(generate_suffix(meta))
|
|
453
465
|
arg.executor = meta.executor ?? "native"
|
|
454
466
|
arg = Object.assign({}, topology_meta, arg)
|
|
455
467
|
if (meta.batch) {
|
|
456
468
|
if (!batches[meta.batch])
|
|
457
|
-
throw
|
|
469
|
+
throw new Error('Error: Invalid batch in Dip.query()')
|
|
458
470
|
batches[meta.batch].push(arg)
|
|
459
471
|
return
|
|
460
472
|
}
|
|
@@ -551,7 +563,7 @@ function parseBinaryResponse(result) {
|
|
|
551
563
|
return {metadata: result.body, content: result.body.items}
|
|
552
564
|
}
|
|
553
565
|
if (!contentType.includes("application/dip")) {
|
|
554
|
-
throw
|
|
566
|
+
throw new Error("Error: Dip unsupported media. Invalid content-type.")
|
|
555
567
|
}
|
|
556
568
|
|
|
557
569
|
const arrayBuffer = result.body
|
package/libs/features.js
CHANGED
|
@@ -43,13 +43,13 @@ let SERVICE_ADDRESS = process.env.APP_ENDPOINT || 'http://127.0.0.1:3000'
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
export const get = feature => {
|
|
46
|
-
const throwError = () => {throw
|
|
46
|
+
const throwError = () => {throw new Error(`Feature ${feature} not found in internal or external list during Features.subscribe call`)}
|
|
47
47
|
let {api, service = SERVICE_ADDRESS, topic} = getFeature(feature) ?? SLYP_FEATURES_LIST[feature] ?? throwError()
|
|
48
48
|
return {api, service, topic}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
export const send = async (meta, feature, data, params) => {
|
|
52
|
-
const throwError = () => {throw
|
|
52
|
+
const throwError = () => {throw new Error(`Feature ${feature} not found in internal or external list during inter feature call`)}
|
|
53
53
|
let {api, service = SERVICE_ADDRESS} = getFeature(feature) ?? SLYP_FEATURES_LIST[feature] ?? throwError()
|
|
54
54
|
let method = getFeatureMethod(api)
|
|
55
55
|
let url = getFeatureUrl(api)
|
|
@@ -58,7 +58,7 @@ export const send = async (meta, feature, data, params) => {
|
|
|
58
58
|
for (let key in params)
|
|
59
59
|
url = url.replace(`:${key}`, params[key])
|
|
60
60
|
if (url.includes(':'))
|
|
61
|
-
throw {feature
|
|
61
|
+
throw new Error(`Error: Internal feature call send on feature: ${feature} when api/params/substitution`)
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
let payload = { ...meta, data, feature, txn: Utils.uid() }
|
|
@@ -217,6 +217,9 @@ function registerApi() {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
export const start = async (app, url, file) => {
|
|
220
|
+
const globalMeta = Dip.globalMeta()
|
|
221
|
+
Dip.excludeCollectionConfig("users.txns")
|
|
222
|
+
Dip.excludeCollectionConfig("Features.txns")
|
|
220
223
|
ExpressApp = app
|
|
221
224
|
PROJECT_ROOT_URL = url
|
|
222
225
|
features = await Utils.fileToJson(url, file)
|
|
@@ -257,9 +260,12 @@ export const start = async (app, url, file) => {
|
|
|
257
260
|
topic = topic.replace(/^.*Features./,'')
|
|
258
261
|
while (true) {
|
|
259
262
|
try {
|
|
260
|
-
|
|
263
|
+
// const iso_date = new Date(message.date).toISOString()
|
|
264
|
+
// // TODO: use $useChunks: [] once support is added in dip insert
|
|
265
|
+
// await Dip.insert(globalMeta, `users.txns`, { _id: `${message.user}_${iso_date}_${message.txn}`, user: message.user, feature: message.feature, date: message.date, created: message.date, updated: message.date, status: "Queued" }, {idempotent: true}) // Can always update the status later
|
|
266
|
+
// TODO: Avoid duplicate processing
|
|
261
267
|
await features[message.feature].handler(topic, message)
|
|
262
|
-
await Dip.insert(
|
|
268
|
+
await Dip.insert(globalMeta, "Features.txns", {_id: message.txn, status: "Processed"}, {idempotent: true})
|
|
263
269
|
break
|
|
264
270
|
} catch(err) {
|
|
265
271
|
if (process.env.DEBUG_MODE)
|
|
@@ -276,7 +282,7 @@ export const start = async (app, url, file) => {
|
|
|
276
282
|
// Transaction status api
|
|
277
283
|
app.get('/transactions/:id', async (req, res) => { // Front end calls this as a regular feature call. so `req.meta` exists
|
|
278
284
|
try {
|
|
279
|
-
let items = await Dip.query(
|
|
285
|
+
let items = await Dip.query(globalMeta, "Features.txns", {_id: req.params.id})
|
|
280
286
|
if (items.length)
|
|
281
287
|
res.json({ data: { ...items[0], txn: items._id } })
|
|
282
288
|
else
|
|
@@ -285,13 +291,17 @@ export const start = async (app, url, file) => {
|
|
|
285
291
|
res.status(500).json({ message: "Failed to retreive data" })
|
|
286
292
|
}
|
|
287
293
|
})
|
|
288
|
-
app.get('/users/:id/transactions', async (req, res) => {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
})
|
|
294
|
+
// app.get('/users/:id/transactions', async (req, res) => {
|
|
295
|
+
// try {
|
|
296
|
+
// const idPrefix = `${req.params.id}_`
|
|
297
|
+
// // TODO: use $useChunks: [] once support is added in dip insert
|
|
298
|
+
// // TODO: remove $orderby: { date: -1 } and remove $orderby: { _id: -1 }
|
|
299
|
+
// // res.json({ data: await Dip.query(globalMeta, `users.txns`, {}, { $rangeFrom: idPrefix, $rangeTo: idPrefix, $rangeDirection: -1, $rangeInclusive: true, $orderby: { date: -1 } }) })
|
|
300
|
+
// res.json({ data: await Dip.query(globalMeta, `users.txns`, {}, { $rangeFrom: idPrefix, $rangeTo: idPrefix, $rangeDirection: -1, $rangeInclusive: true, $orderby: { _id: -1 } }) })
|
|
301
|
+
// } catch {
|
|
302
|
+
// res.status(500).json({ message: "Failed to retreive data" })
|
|
303
|
+
// }
|
|
304
|
+
// })
|
|
295
305
|
|
|
296
306
|
try { await Messaging.start(app) } catch { console.log('Messaging failed to start. Maybe missing redis') }
|
|
297
307
|
}
|