beanbagdb 0.5.80 → 0.6.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/package.json +1 -1
- package/src/index.js +210 -115
- package/src/system_schema.js +207 -153
- package/test/operations.test.js +264 -587
- package/test/pouchdb.js +69 -22
- package/src/plugins/text_command.js +0 -191
- package/test/plugin.test.js +0 -52
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -85,6 +85,68 @@ export class BeanBagDB {
|
|
|
85
85
|
//////////////////// Setup methods /////////////////////////////////////
|
|
86
86
|
////////////////////////////////////////////////////////////////////////
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* This is the list of methods that are compatible with JSON-REST API. Each command either has no params or takes just one json param as input
|
|
90
|
+
*/
|
|
91
|
+
static rest_enabled = {
|
|
92
|
+
"ready":{
|
|
93
|
+
use: "Makes the database ready to use"
|
|
94
|
+
},
|
|
95
|
+
"metadata":{
|
|
96
|
+
use: "Returns metadata related to the current BeanBagDB instance "
|
|
97
|
+
},
|
|
98
|
+
"initialize_app":{
|
|
99
|
+
use:"To install/initialize an external app",
|
|
100
|
+
input: "{...app_data}"
|
|
101
|
+
},
|
|
102
|
+
"update_indexes":{
|
|
103
|
+
use:"Updates the indexes in the database for better searching"
|
|
104
|
+
},
|
|
105
|
+
"create":{
|
|
106
|
+
input:"{schema,data,meta}",
|
|
107
|
+
use:"Creates a new doc"
|
|
108
|
+
|
|
109
|
+
},
|
|
110
|
+
"read":{
|
|
111
|
+
input:"{criteria,include_schema:false}",
|
|
112
|
+
use:"Returns a doc. 3 ways to search for a doc : by _id, by link or by the primary key of the schema "
|
|
113
|
+
},
|
|
114
|
+
"update":{
|
|
115
|
+
input:"{criteria,updates}",
|
|
116
|
+
use:"Updates a document"
|
|
117
|
+
},
|
|
118
|
+
"delete":{
|
|
119
|
+
input:"{criteria}",
|
|
120
|
+
use:"Deletes a doc"
|
|
121
|
+
},
|
|
122
|
+
"search":{
|
|
123
|
+
input:"{criteria:{selector:{...}}}",
|
|
124
|
+
use:"To search in the database"
|
|
125
|
+
},
|
|
126
|
+
"get":{
|
|
127
|
+
input:"{type,criteria}",
|
|
128
|
+
use:"Returns special types of documents "
|
|
129
|
+
},
|
|
130
|
+
"create_edge":{
|
|
131
|
+
input:"{node1:{..criteria},node2:{..criteria},edge_name,edge_label}",
|
|
132
|
+
use:"Creates a new edge in the system's simple directed graph "
|
|
133
|
+
},
|
|
134
|
+
"util_get_now_unix_timestamp":{
|
|
135
|
+
use:"Returns the current UNIX timestamp"
|
|
136
|
+
},
|
|
137
|
+
"util_validate_data":{
|
|
138
|
+
input:"{schema:{},data:{}}",
|
|
139
|
+
use:"Validate the given data against the given schema and returns errors/validated doc"
|
|
140
|
+
},
|
|
141
|
+
"util_validate_schema_object":{
|
|
142
|
+
input:"{...schema_object...}",
|
|
143
|
+
use:"Validated the schema document without inserting it in the DB"
|
|
144
|
+
|
|
145
|
+
},
|
|
146
|
+
"util_generate_random_link":{
|
|
147
|
+
use:"Returns a random link"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
88
150
|
/**
|
|
89
151
|
* Database object metadata
|
|
90
152
|
* @typedef {Object} DBMetaData
|
|
@@ -162,8 +224,8 @@ export class BeanBagDB {
|
|
|
162
224
|
// this works on its own but is usually called by ready automatically if required
|
|
163
225
|
// check for schema_scehma : if yes, check if latest and upgrade if required, if no create a new schema doc
|
|
164
226
|
try {
|
|
165
|
-
let app_data = await this.
|
|
166
|
-
console.log(app_data)
|
|
227
|
+
let app_data = await this.initialize_app(sys_sch.default_app)
|
|
228
|
+
// console.log(app_data)
|
|
167
229
|
this.meta.beanbagdb_version_db = this._version;
|
|
168
230
|
this.active = true;
|
|
169
231
|
return app_data
|
|
@@ -175,14 +237,23 @@ export class BeanBagDB {
|
|
|
175
237
|
|
|
176
238
|
}
|
|
177
239
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
240
|
+
/**
|
|
241
|
+
* Install/Updates an app in the database.
|
|
242
|
+
* This should be called before using any
|
|
243
|
+
* @param {Object} app_data
|
|
244
|
+
*/
|
|
245
|
+
async initialize_app(app_data_input){
|
|
181
246
|
// calculate the app_version
|
|
247
|
+
if(!app_data_input){throw new Error("app_data_input is required")}
|
|
248
|
+
let app_data = this.util_validate_data({schema:sys_sch.app_data_schema,data:app_data_input})
|
|
182
249
|
let latest_version = 0
|
|
183
|
-
app_data.schemas.map(sch=>{
|
|
250
|
+
app_data.schemas.map(sch=>{
|
|
251
|
+
latest_version = latest_version + sch.version
|
|
252
|
+
// update the source of schemas to the app
|
|
253
|
+
sch["settings"]["install_source"] = `app:${app_data.app_id}`
|
|
254
|
+
})
|
|
184
255
|
app_data.records.map(sch=>{latest_version = latest_version + sch.version})
|
|
185
|
-
|
|
256
|
+
|
|
186
257
|
|
|
187
258
|
// check if app setting record exists
|
|
188
259
|
let version_search = await this.db_api.search({
|
|
@@ -191,6 +262,7 @@ export class BeanBagDB {
|
|
|
191
262
|
|
|
192
263
|
let update_required = true
|
|
193
264
|
let doc
|
|
265
|
+
|
|
194
266
|
if (version_search.docs.length > 0) {
|
|
195
267
|
doc = version_search.docs[0];
|
|
196
268
|
if(doc["data"]["value"]["version"] == latest_version){
|
|
@@ -212,7 +284,7 @@ export class BeanBagDB {
|
|
|
212
284
|
steps.push(`checking.${schema_name}`)
|
|
213
285
|
try {
|
|
214
286
|
// console.log(schema_name)
|
|
215
|
-
let schema1 = await this.get("schema",{name:schema_name})
|
|
287
|
+
let schema1 = await this.get({type:"schema",criteria:{name:schema_name}})
|
|
216
288
|
if (schema1["data"]["version"] != schema_data.version) {
|
|
217
289
|
steps.push(`old.${schema_name}.v.${schema1["data"]["version"]}`);
|
|
218
290
|
let full_doc = await this.db_api.get(schema1["_id"]);
|
|
@@ -257,7 +329,7 @@ export class BeanBagDB {
|
|
|
257
329
|
const record_title = app_data.records[index]["title"]
|
|
258
330
|
steps.push(`checking.records.${record_title}`)
|
|
259
331
|
try {
|
|
260
|
-
let new_doc = await this.create(schema_name,schema_data,app_data.records[index]["meta"])
|
|
332
|
+
let new_doc = await this.create({schema:schema_name,data:schema_data,meta:app_data.records[index]["meta"]})
|
|
261
333
|
steps.push(`doc.${record_title}.created`)
|
|
262
334
|
} catch (error) {
|
|
263
335
|
if(!(error instanceof DocCreationError)){
|
|
@@ -266,10 +338,11 @@ export class BeanBagDB {
|
|
|
266
338
|
}
|
|
267
339
|
}
|
|
268
340
|
|
|
269
|
-
let app_doc = { ... app_data.meta, version: latest_version}
|
|
341
|
+
let app_doc = { ... app_data.meta,app_id: app_data.app_id ,version: latest_version}
|
|
270
342
|
try {
|
|
271
343
|
// modify the app setting doc
|
|
272
|
-
|
|
344
|
+
// console.log(app_doc,)
|
|
345
|
+
await this.modify_setting(app_data.app_id,app_doc,"update")
|
|
273
346
|
|
|
274
347
|
// add a new log
|
|
275
348
|
let new_log_doc = this._get_blank_doc("system_log")
|
|
@@ -316,20 +389,28 @@ export class BeanBagDB {
|
|
|
316
389
|
* This method validates the input data and schema before inserting a new document into the database.
|
|
317
390
|
*
|
|
318
391
|
* @async
|
|
319
|
-
|
|
320
|
-
* @param {object}
|
|
321
|
-
* @param {
|
|
322
|
-
* @param {object} [
|
|
392
|
+
|
|
393
|
+
* @param {object} input - The document details, e.g.,{ schema:"name",data: { "name": "", "mobile": "", ... }}.
|
|
394
|
+
* @param {string} [input.schema] - The schema name for the document, e.g., "contact".
|
|
395
|
+
* @param {object} [input.data={}] - the data for the document.
|
|
396
|
+
* @param {object} [input.meta={}] - Optional metadata associated with the document.
|
|
323
397
|
* @returns {Promise<{id: string}>} - A promise that resolves with the newly inserted document's ID.
|
|
324
398
|
* @throws {Error} - Throws an error if insertion checks fail or if there is an issue with the database operation.
|
|
325
399
|
*/
|
|
326
|
-
async create(
|
|
400
|
+
async create(input) {
|
|
327
401
|
this._check_ready_to_use();
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
if(
|
|
402
|
+
|
|
403
|
+
let v_errors = []
|
|
404
|
+
if(!input.schema){v_errors.push("schema is required")}
|
|
405
|
+
if(!input.data){v_errors.push("data is required")}
|
|
406
|
+
if(v_errors.length>0){
|
|
407
|
+
throw new DocCreationError(`${v_errors.join(",")}.`)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if(input.schema=="setting_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
|
|
411
|
+
if(Object.keys(input.data).length==0){throw new DocCreationError(`No data provided`)}
|
|
331
412
|
try {
|
|
332
|
-
let doc_obj = await this._insert_pre_checks(schema, data,meta, settings);
|
|
413
|
+
let doc_obj = await this._insert_pre_checks(input.schema, input.data,input?.meta||{}, input?.settings||{});
|
|
333
414
|
let new_rec = await this.db_api.insert(doc_obj);
|
|
334
415
|
return {_id:new_rec["id"],_rev : new_rec["rev"] ,...doc_obj};
|
|
335
416
|
} catch (error) {
|
|
@@ -352,15 +433,14 @@ export class BeanBagDB {
|
|
|
352
433
|
* @param {string} [criteria.link] - A unique link identifier for the document.
|
|
353
434
|
* @param {string} [criteria.schema] - The schema name used when searching by primary keys.
|
|
354
435
|
* @param {Object} [criteria.data] - Data object containing the schema's primary keys for search.
|
|
355
|
-
*
|
|
356
|
-
* @param {boolean} [include_schema=false] - Whether to include the schema object in the returned result.
|
|
436
|
+
* @param {string} [criteria.include_schema] - Whether to include the schema object in the returned result.
|
|
357
437
|
*
|
|
358
438
|
* @returns {Promise<Object>} - Returns an object with the document (`doc`) and optionally the schema (`schema`).
|
|
359
439
|
*
|
|
360
440
|
* @throws {DocNotFoundError} If no document is found for the given criteria.
|
|
361
441
|
* @throws {ValidationError} If invalid search criteria are provided.
|
|
362
442
|
*/
|
|
363
|
-
async read(criteria
|
|
443
|
+
async read(criteria) {
|
|
364
444
|
// todo : decrypt doc
|
|
365
445
|
this._check_ready_to_use()
|
|
366
446
|
let obj = { doc: null }
|
|
@@ -376,7 +456,7 @@ export class BeanBagDB {
|
|
|
376
456
|
if (linkSearch.docs.length == 0) {throw new DocNotFoundError(BeanBagDB.error_codes.doc_not_found)}
|
|
377
457
|
obj.doc = linkSearch.docs[0];
|
|
378
458
|
} else if (criteria.hasOwnProperty("schema") & criteria.hasOwnProperty("data")) {
|
|
379
|
-
data_schema = await this.get("schema",{"name":criteria.schema})
|
|
459
|
+
data_schema = await this.get({type:"schema",criteria:{"name":criteria.schema}})
|
|
380
460
|
let A = data_schema["data"]["settings"]["primary_keys"];
|
|
381
461
|
let search_criteria = { schema: criteria.schema };
|
|
382
462
|
A.forEach((itm) => {
|
|
@@ -392,11 +472,14 @@ export class BeanBagDB {
|
|
|
392
472
|
throw new ValidationError(`Invalid criteria to read a document. Valid ways : {"schema":"schema_name","data":{...primary key}} or {"_id":""} or {"link":""} `)
|
|
393
473
|
}
|
|
394
474
|
if (!data_schema){
|
|
395
|
-
data_schema = await this.get("schema",{"name":obj.doc.schema})
|
|
475
|
+
data_schema = await this.get({type:"schema",criteria:{"name":obj.doc.schema}})
|
|
396
476
|
}
|
|
397
|
-
|
|
477
|
+
|
|
478
|
+
if(criteria.include_schema) {obj.schema = data_schema["data"]}
|
|
479
|
+
|
|
398
480
|
// decrypt the document
|
|
399
481
|
obj.doc = await this._decrypt_doc(data_schema["data"], obj.doc)
|
|
482
|
+
|
|
400
483
|
return obj;
|
|
401
484
|
}
|
|
402
485
|
|
|
@@ -413,11 +496,12 @@ export class BeanBagDB {
|
|
|
413
496
|
* - Yes, but a validation check ensures that primary key policies are not violated before the update is applied.
|
|
414
497
|
*
|
|
415
498
|
*
|
|
416
|
-
* @param {Object}
|
|
417
|
-
* @param {
|
|
418
|
-
* @param {Object} updates - The updated values for the document, structured as `{data: {}, meta: {}}`. Only the fields to be updated need to be provided.
|
|
419
|
-
* @param {String} [
|
|
420
|
-
* @param {
|
|
499
|
+
* @param {Object} params - Object to fetch and update data
|
|
500
|
+
* @param {Object} [params.criteria] - The criteria used to search for the document (e.g., {"_id": "document_id"}, {"link": "some_link"}, {"schema": "schema_name", "data": {primary_key_fields}}).
|
|
501
|
+
* @param {Object} [params.updates] - The updated values for the document, structured as `{data: {}, meta: {}}`. Only the fields to be updated need to be provided.
|
|
502
|
+
* @param {String} [params.rev_id] - The document's revision ID (`_rev`) used for version control and conflict detection.
|
|
503
|
+
* @param {String} [params.update_source="api"] - Identifies the source of the update (default: "api").
|
|
504
|
+
* @param {Boolean} [params.save_conflict=true] - If `true`, conflicting updates will be saved separately in case of revision mismatches.
|
|
421
505
|
*
|
|
422
506
|
* **Behavior**:
|
|
423
507
|
* - Retrieves the document based on the provided search criteria.
|
|
@@ -436,11 +520,24 @@ export class BeanBagDB {
|
|
|
436
520
|
* @throws {DocUpdateError} - If a document with conflicting primary keys already exists.
|
|
437
521
|
* @throws {ValidationError} - If the provided data or metadata is invalid according to the schema.
|
|
438
522
|
*/
|
|
439
|
-
async update(
|
|
523
|
+
async update(params) {
|
|
524
|
+
|
|
440
525
|
this._check_ready_to_use();
|
|
526
|
+
|
|
527
|
+
const {
|
|
528
|
+
criteria,
|
|
529
|
+
updates,
|
|
530
|
+
rev_id = "",
|
|
531
|
+
update_source = "api",
|
|
532
|
+
save_conflict = true
|
|
533
|
+
} = params;
|
|
534
|
+
|
|
535
|
+
if(!criteria){throw new DocUpdateError("Doc search criteria not provided")}
|
|
536
|
+
if(!updates){throw new DocUpdateError("No updates provided")}
|
|
537
|
+
|
|
441
538
|
// making a big assumption here : primary key fields cannot be edited
|
|
442
539
|
// so updating the doc will not generate primary key conflicts
|
|
443
|
-
let req_data = await this.read(
|
|
540
|
+
let req_data = await this.read({...criteria, include_schema: true});
|
|
444
541
|
let schema = req_data.schema;
|
|
445
542
|
let full_doc = req_data.doc;
|
|
446
543
|
// @TODO fix this : what to do if the rev id does not match
|
|
@@ -467,7 +564,7 @@ export class BeanBagDB {
|
|
|
467
564
|
// todo : what if additionalField are allowed ??
|
|
468
565
|
let updated_data = { ...full_doc.data, ...allowed_updates };
|
|
469
566
|
|
|
470
|
-
updated_data = this.util_validate_data(schema.schema, updated_data);
|
|
567
|
+
updated_data = this.util_validate_data({schema:schema.schema,data: updated_data});
|
|
471
568
|
|
|
472
569
|
// primary key check if multiple records can be created
|
|
473
570
|
if (schema.settings["primary_keys"].length > 0) {
|
|
@@ -494,7 +591,7 @@ export class BeanBagDB {
|
|
|
494
591
|
let m_sch = sys_sch.editable_metadata_schema;
|
|
495
592
|
let editable_fields = Object.keys(m_sch["properties"]);
|
|
496
593
|
let allowed_meta = this.util_filter_object(updates.meta, editable_fields);
|
|
497
|
-
allowed_meta = this.util_validate_data(m_sch, allowed_meta);
|
|
594
|
+
allowed_meta = this.util_validate_data({schema:m_sch, data:allowed_meta});
|
|
498
595
|
// if update has a link ,then check if it already exists
|
|
499
596
|
if (allowed_meta.link){
|
|
500
597
|
let search = await this.search({ selector: {"meta.link":allowed_meta.link} })
|
|
@@ -525,50 +622,7 @@ export class BeanBagDB {
|
|
|
525
622
|
}
|
|
526
623
|
|
|
527
624
|
|
|
528
|
-
/**
|
|
529
|
-
* Check if the setting with the given name exists. New record created if not found. If found data is updated based on the updated_mode and the data type of the existing data
|
|
530
|
-
* If existing value is an array, and update_mode is append "value" is appended to the current value array.
|
|
531
|
-
* if existing value is an object, update_mode "append" will update fields that exists in the new object,
|
|
532
|
-
* for both data types, new value is replaced in update_mode : "replace"
|
|
533
|
-
* @param {string} name The name of the setting
|
|
534
|
-
* @param {object} value Value to be modified
|
|
535
|
-
* @param {string} mode
|
|
536
|
-
*/
|
|
537
|
-
async modify_setting(name,value,update_mode){
|
|
538
|
-
if(!name||!value||!update_mode){
|
|
539
|
-
throw new DocUpdateError("All 3 inputs (setting name, value and update_mode) are required")
|
|
540
|
-
}
|
|
541
|
-
let doc_search = await this.db_api.search({
|
|
542
|
-
selector: { schema: "system_setting", "data.name": name },
|
|
543
|
-
});
|
|
544
625
|
|
|
545
|
-
if (!["append", "update"].includes(update_mode)) {
|
|
546
|
-
throw new DocUpdateError("Invalid update_mode");
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
if (doc_search.docs.length > 0) {
|
|
550
|
-
// doc already exists,
|
|
551
|
-
let doc = { ...doc_search.docs[0] };
|
|
552
|
-
if (Array.isArray(value)) {
|
|
553
|
-
doc.data.value = update_mode === "append" ? [...doc.data.value, value] : value; // "update" mode replaces the value
|
|
554
|
-
} else {
|
|
555
|
-
doc.data.value = update_mode === "append" ? { ...doc.data.value, ...value } : value; // "update" mode replaces the value
|
|
556
|
-
}
|
|
557
|
-
// finally update it
|
|
558
|
-
doc["meta"]["updated_on"] = this.util_get_now_unix_timestamp();
|
|
559
|
-
// caution : db api is being used directly
|
|
560
|
-
await this.db_api.update(doc);
|
|
561
|
-
return doc;
|
|
562
|
-
|
|
563
|
-
} else {
|
|
564
|
-
// doc does not exists, generate a new one
|
|
565
|
-
let new_log_doc = this._get_blank_doc("system_setting")
|
|
566
|
-
new_log_doc.data = {value, name}
|
|
567
|
-
await this.db_api.insert(new_log_doc);
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
626
|
|
|
573
627
|
/**
|
|
574
628
|
* Deletes a document from the database by its ID.
|
|
@@ -580,7 +634,7 @@ export class BeanBagDB {
|
|
|
580
634
|
async delete(criteria) {
|
|
581
635
|
this._check_ready_to_use();
|
|
582
636
|
let doc = await this.read(criteria)
|
|
583
|
-
const delete_blocked = ["schema","
|
|
637
|
+
const delete_blocked = ["schema","system_setting","system_log"]
|
|
584
638
|
if (delete_blocked.includes(doc.schema)){
|
|
585
639
|
throw new Error(`Deletion of ${doc.schema} doc is not support yet.`)
|
|
586
640
|
}
|
|
@@ -616,9 +670,11 @@ export class BeanBagDB {
|
|
|
616
670
|
* for a given schema. It handles system-related data and throws errors for invalid document types
|
|
617
671
|
* or if the document is not found.
|
|
618
672
|
*
|
|
619
|
-
* @param {
|
|
673
|
+
* @param {Object} [input={}] - Criteria used to search for the special document.
|
|
674
|
+
|
|
675
|
+
* @param {String} input.type - The type of special document to fetch. Supported types include:
|
|
620
676
|
* - 'schema': Retrieves a schema document based on the criteria provided.
|
|
621
|
-
* @param {Object} [criteria={}] - Criteria used to search for the special document.
|
|
677
|
+
* @param {Object} [input.criteria={}] - Criteria used to search for the special document.
|
|
622
678
|
* For example, to search for a schema, the criteria should include the name.
|
|
623
679
|
*
|
|
624
680
|
* @throws {ValidationError} Throws if the `special_doc_type` is not recognized.
|
|
@@ -626,7 +682,8 @@ export class BeanBagDB {
|
|
|
626
682
|
*
|
|
627
683
|
* @returns {Object} The fetched special document based on the type and criteria.
|
|
628
684
|
*/
|
|
629
|
-
async get(
|
|
685
|
+
async get(input){
|
|
686
|
+
|
|
630
687
|
// this method returns special types of documents such as schema doc, or a blank doc for a given schema and other system related things
|
|
631
688
|
const fetch_docs = {
|
|
632
689
|
// to return schema object for the given name
|
|
@@ -666,14 +723,19 @@ export class BeanBagDB {
|
|
|
666
723
|
|
|
667
724
|
}
|
|
668
725
|
}
|
|
669
|
-
if(Object.keys(fetch_docs).
|
|
670
|
-
|
|
726
|
+
if(!input.type){throw new ValidationError("No type provided. Must be: "+Object.keys(fetch_docs).join(","))}
|
|
727
|
+
if(Object.keys(fetch_docs).includes(input.type)){
|
|
728
|
+
let data = await fetch_docs[input.type](input?.criteria||{})
|
|
671
729
|
return data
|
|
672
730
|
}else{
|
|
673
|
-
throw new ValidationError("Invalid
|
|
731
|
+
throw new ValidationError("Invalid name. Must be : "+Object.keys(fetch_docs).join(","))
|
|
674
732
|
}
|
|
675
733
|
}
|
|
676
734
|
|
|
735
|
+
|
|
736
|
+
//////////////////// methods for special use , requires to be called using the class (rather than the rest api)
|
|
737
|
+
|
|
738
|
+
|
|
677
739
|
/**
|
|
678
740
|
* To load a plugin in the current BeanBagDB instance.
|
|
679
741
|
* Plug_module has to be loaded manually first. It must export an object containing fields: `actions` and `schema`.
|
|
@@ -705,6 +767,51 @@ export class BeanBagDB {
|
|
|
705
767
|
// }
|
|
706
768
|
}
|
|
707
769
|
|
|
770
|
+
/**
|
|
771
|
+
* Check if the setting with the given name exists. New record created if not found. If found data is updated based on the updated_mode and the data type of the existing data
|
|
772
|
+
* If existing value is an array, and update_mode is append "value" is appended to the current value array.
|
|
773
|
+
* if existing value is an object, update_mode "append" will update fields that exists in the new object,
|
|
774
|
+
* for both data types, new value is replaced in update_mode : "replace"
|
|
775
|
+
* @param {string} name The name of the setting
|
|
776
|
+
* @param {object} value Value to be modified
|
|
777
|
+
* @param {string} mode
|
|
778
|
+
*/
|
|
779
|
+
async modify_setting(name,value,update_mode){
|
|
780
|
+
if(!name||!value||!update_mode){
|
|
781
|
+
throw new DocUpdateError("All 3 inputs (setting name, value and update_mode) are required")
|
|
782
|
+
}
|
|
783
|
+
let doc_search = await this.db_api.search({
|
|
784
|
+
selector: { schema: "system_setting", "data.name": name },
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
if (!["append", "update"].includes(update_mode)) {
|
|
788
|
+
throw new DocUpdateError("Invalid update_mode");
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (doc_search.docs.length > 0) {
|
|
792
|
+
// doc already exists,
|
|
793
|
+
let doc = { ...doc_search.docs[0] };
|
|
794
|
+
if (Array.isArray(value)) {
|
|
795
|
+
doc.data.value = update_mode === "append" ? [...doc.data.value, value] : value; // "update" mode replaces the value
|
|
796
|
+
} else {
|
|
797
|
+
doc.data.value = update_mode === "append" ? { ...doc.data.value, ...value } : value; // "update" mode replaces the value
|
|
798
|
+
}
|
|
799
|
+
// finally update it
|
|
800
|
+
doc["meta"]["updated_on"] = this.util_get_now_unix_timestamp();
|
|
801
|
+
// caution : db api is being used directly
|
|
802
|
+
await this.db_api.update(doc);
|
|
803
|
+
return doc;
|
|
804
|
+
|
|
805
|
+
} else {
|
|
806
|
+
// doc does not exists, generate a new one
|
|
807
|
+
let new_log_doc = this._get_blank_doc("system_setting")
|
|
808
|
+
new_log_doc.data = {value, name}
|
|
809
|
+
await this.db_api.insert(new_log_doc);
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
708
815
|
///////////////////////////////////////////////////////////
|
|
709
816
|
//////////////// simple directed graph ////////////////////////
|
|
710
817
|
//////////////////////////////////////////////////////////
|
|
@@ -717,7 +824,8 @@ export class BeanBagDB {
|
|
|
717
824
|
* @param {*} edge_label
|
|
718
825
|
* @returns {Object}
|
|
719
826
|
*/
|
|
720
|
-
async create_edge(
|
|
827
|
+
async create_edge(input){
|
|
828
|
+
const {node1,node2,edge_name,edge_label=""} = input
|
|
721
829
|
this._check_ready_to_use();
|
|
722
830
|
if(!edge_name){throw new ValidationError("edge_name required")}
|
|
723
831
|
if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
|
|
@@ -762,7 +870,7 @@ async create_edge(node1,node2,edge_name,edge_label=""){
|
|
|
762
870
|
}
|
|
763
871
|
|
|
764
872
|
if(errors.length==0){
|
|
765
|
-
let edge = await this.create("system_edge",{node1: node1id , node2: node1id ,edge_name:edge_name })
|
|
873
|
+
let edge = await this.create({schema:"system_edge",data:{node1: node1id , node2: node1id ,edge_name:edge_name }})
|
|
766
874
|
return edge
|
|
767
875
|
}else{
|
|
768
876
|
throw new RelationError(errors)
|
|
@@ -771,8 +879,8 @@ async create_edge(node1,node2,edge_name,edge_label=""){
|
|
|
771
879
|
} catch (error) {
|
|
772
880
|
if(error instanceof DocNotFoundError){
|
|
773
881
|
let doc = {node1:"*",node2:"*",name:edge_name,label:edge_label}
|
|
774
|
-
let new_doc = await this.create("system_edge_constraint",doc)
|
|
775
|
-
let edge = await this.create("system_edge",{node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name })
|
|
882
|
+
let new_doc = await this.create({schema:"system_edge_constraint",data:doc})
|
|
883
|
+
let edge = await this.create({schema:"system_edge",data:{node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name }})
|
|
776
884
|
return edge
|
|
777
885
|
}else{
|
|
778
886
|
throw error
|
|
@@ -841,7 +949,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
841
949
|
const schema_data = schemas[index]
|
|
842
950
|
steps.push(`checking.${schema_name}`)
|
|
843
951
|
try {
|
|
844
|
-
let schema1 = await this.get("schema",{name:schema_name})
|
|
952
|
+
let schema1 = await this.get({type:"schema",criteria:{name:schema_name}})
|
|
845
953
|
if (schema1["data"]["version"] != schema_data.version) {
|
|
846
954
|
steps.push(`old.${schema_name}.v.${schema1["data"]["version"]}`);
|
|
847
955
|
let full_doc = await this.db_api.get(schema1["_id"]);
|
|
@@ -957,7 +1065,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
957
1065
|
* @returns {Object}
|
|
958
1066
|
*/
|
|
959
1067
|
_get_blank_schema_doc(schema_name, schema_object, data) {
|
|
960
|
-
let new_data = this.util_validate_data(schema_object, data);
|
|
1068
|
+
let new_data = this.util_validate_data({schema:schema_object, data});
|
|
961
1069
|
let obj = this._get_blank_doc(schema_name);
|
|
962
1070
|
obj["data"] = new_data;
|
|
963
1071
|
return obj;
|
|
@@ -1032,11 +1140,11 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1032
1140
|
// validate data
|
|
1033
1141
|
if(!schemaDoc.active){throw new DocCreationError(`The schema "${schema}" is not active`)}
|
|
1034
1142
|
|
|
1035
|
-
let new_data = this.util_validate_data(schemaDoc.schema, data);
|
|
1143
|
+
let new_data = this.util_validate_data({schema:schemaDoc.schema, data});
|
|
1036
1144
|
|
|
1037
1145
|
// validate meta
|
|
1038
1146
|
if(Object.keys(meta).length>0){
|
|
1039
|
-
meta = this.util_validate_data(sys_sch.editable_metadata_schema, meta)
|
|
1147
|
+
meta = this.util_validate_data({schema:sys_sch.editable_metadata_schema, data:meta})
|
|
1040
1148
|
}
|
|
1041
1149
|
|
|
1042
1150
|
|
|
@@ -1094,29 +1202,14 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1094
1202
|
*/
|
|
1095
1203
|
util_get_now_unix_timestamp() {return Math.floor(Date.now() / 1000)}
|
|
1096
1204
|
|
|
1097
|
-
|
|
1098
|
-
* Validates that the required fields are present in the provided object.
|
|
1099
|
-
*
|
|
1100
|
-
* @param {string[]} requiredFields - An array of field names that are required.
|
|
1101
|
-
* @param {object} obj - The object to check for the required fields.
|
|
1102
|
-
* @throws {ValidationError} If any of the required fields are missing, an error is thrown.
|
|
1103
|
-
*/
|
|
1205
|
+
|
|
1104
1206
|
util_check_required_fields(requiredFields, obj) {
|
|
1105
1207
|
for (const field of requiredFields) {
|
|
1106
1208
|
if (!obj[field]) {throw new ValidationError(`The field ${field} is required.`)}
|
|
1107
1209
|
}
|
|
1108
1210
|
}
|
|
1109
1211
|
|
|
1110
|
-
/**
|
|
1111
|
-
* Filters an object, returning a new object that only contains the specified fields.
|
|
1112
|
-
*
|
|
1113
|
-
* @param {Object} obj - The object to filter.
|
|
1114
|
-
* @param {Array<String>} fields - An array of field names to retain in the filtered object.
|
|
1115
|
-
*
|
|
1116
|
-
* @returns {Object} - A new object containing only the fields that exist in `obj` from the `fields` array.
|
|
1117
|
-
*
|
|
1118
|
-
* **Example**:
|
|
1119
|
-
*
|
|
1212
|
+
/** Filters an object, returning a new object that only contains the specified fields.
|
|
1120
1213
|
* const data = { name: "Alice", age: 25, location: "NY" };
|
|
1121
1214
|
* const result = util_filter_object(data, ["name", "age"]);
|
|
1122
1215
|
* // result: { name: "Alice", age: 25 }
|
|
@@ -1130,7 +1223,6 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1130
1223
|
}, {});
|
|
1131
1224
|
}
|
|
1132
1225
|
|
|
1133
|
-
|
|
1134
1226
|
/**
|
|
1135
1227
|
* Validates a data object against a provided JSON schema and returns a valid data object (with default value for missing field for which default values are defined in the schema )
|
|
1136
1228
|
* It relies on the external API provided by the user
|
|
@@ -1138,8 +1230,11 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1138
1230
|
* @param {Object} data_obj - The data object to validate
|
|
1139
1231
|
* @throws {Error} If the data object does not conform to the schema
|
|
1140
1232
|
*/
|
|
1141
|
-
util_validate_data(
|
|
1142
|
-
|
|
1233
|
+
util_validate_data(input) {
|
|
1234
|
+
if(!input.schema){throw new ValidationError("schema is required")}
|
|
1235
|
+
if(!input.data){throw new ValidationError("data is required")}
|
|
1236
|
+
|
|
1237
|
+
const { valid, validate , data} = this.utils.validate_schema(input.schema,input.data)
|
|
1143
1238
|
if (!valid) {
|
|
1144
1239
|
throw new ValidationError(validate.errors);
|
|
1145
1240
|
}else{
|
|
@@ -1258,7 +1353,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1258
1353
|
* "banana-earth-rain".
|
|
1259
1354
|
*
|
|
1260
1355
|
*/
|
|
1261
|
-
util_generate_random_link(type
|
|
1356
|
+
util_generate_random_link(input={type:1}) {
|
|
1262
1357
|
const options = {
|
|
1263
1358
|
0:()=>{
|
|
1264
1359
|
// prettier-ignore
|
|
@@ -1272,7 +1367,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1272
1367
|
|
|
1273
1368
|
}
|
|
1274
1369
|
}
|
|
1275
|
-
return options[type]()
|
|
1370
|
+
return options[input.type]()
|
|
1276
1371
|
}
|
|
1277
1372
|
}
|
|
1278
1373
|
|