beanbagdb 0.5.77 → 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 +227 -122
- package/src/system_schema.js +208 -141
- package/test/operations.test.js +264 -581
- package/test/pouchdb.js +74 -25
- package/test/test1.js +106 -147
- 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
|
|
@@ -451,6 +548,11 @@ export class BeanBagDB {
|
|
|
451
548
|
// }
|
|
452
549
|
// }
|
|
453
550
|
|
|
551
|
+
// system generated schemas cannot be edited
|
|
552
|
+
if(full_doc.schema=="schema"&&full_doc.data.system_generated==true){
|
|
553
|
+
throw new DocUpdateError("System schemas cannot be updated using this API");
|
|
554
|
+
}
|
|
555
|
+
|
|
454
556
|
// update new value depending on settings.non_editable_fields (if does not exists, all fields are editable)
|
|
455
557
|
let all_fields = Object.keys(schema.schema.properties);
|
|
456
558
|
let unedit_fields = schema.settings["non_editable_fields"];
|
|
@@ -462,7 +564,7 @@ export class BeanBagDB {
|
|
|
462
564
|
// todo : what if additionalField are allowed ??
|
|
463
565
|
let updated_data = { ...full_doc.data, ...allowed_updates };
|
|
464
566
|
|
|
465
|
-
this.util_validate_data(schema.schema, updated_data);
|
|
567
|
+
updated_data = this.util_validate_data({schema:schema.schema,data: updated_data});
|
|
466
568
|
|
|
467
569
|
// primary key check if multiple records can be created
|
|
468
570
|
if (schema.settings["primary_keys"].length > 0) {
|
|
@@ -489,7 +591,7 @@ export class BeanBagDB {
|
|
|
489
591
|
let m_sch = sys_sch.editable_metadata_schema;
|
|
490
592
|
let editable_fields = Object.keys(m_sch["properties"]);
|
|
491
593
|
let allowed_meta = this.util_filter_object(updates.meta, editable_fields);
|
|
492
|
-
this.util_validate_data(m_sch, allowed_meta);
|
|
594
|
+
allowed_meta = this.util_validate_data({schema:m_sch, data:allowed_meta});
|
|
493
595
|
// if update has a link ,then check if it already exists
|
|
494
596
|
if (allowed_meta.link){
|
|
495
597
|
let search = await this.search({ selector: {"meta.link":allowed_meta.link} })
|
|
@@ -520,50 +622,7 @@ export class BeanBagDB {
|
|
|
520
622
|
}
|
|
521
623
|
|
|
522
624
|
|
|
523
|
-
/**
|
|
524
|
-
* 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
|
|
525
|
-
* If existing value is an array, and update_mode is append "value" is appended to the current value array.
|
|
526
|
-
* if existing value is an object, update_mode "append" will update fields that exists in the new object,
|
|
527
|
-
* for both data types, new value is replaced in update_mode : "replace"
|
|
528
|
-
* @param {string} name The name of the setting
|
|
529
|
-
* @param {object} value Value to be modified
|
|
530
|
-
* @param {string} mode
|
|
531
|
-
*/
|
|
532
|
-
async modify_setting(name,value,update_mode){
|
|
533
|
-
if(!name||!value||!update_mode){
|
|
534
|
-
throw new DocUpdateError("All 3 inputs (setting name, value and update_mode) are required")
|
|
535
|
-
}
|
|
536
|
-
let doc_search = await this.db_api.search({
|
|
537
|
-
selector: { schema: "system_setting", "data.name": name },
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
if (!["append", "update"].includes(update_mode)) {
|
|
541
|
-
throw new DocUpdateError("Invalid update_mode");
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
if (doc_search.docs.length > 0) {
|
|
545
|
-
// doc already exists,
|
|
546
|
-
let doc = { ...doc_search.docs[0] };
|
|
547
|
-
if (Array.isArray(value)) {
|
|
548
|
-
doc.data.value = update_mode === "append" ? [...doc.data.value, value] : value; // "update" mode replaces the value
|
|
549
|
-
} else {
|
|
550
|
-
doc.data.value = update_mode === "append" ? { ...doc.data.value, ...value } : value; // "update" mode replaces the value
|
|
551
|
-
}
|
|
552
|
-
// finally update it
|
|
553
|
-
doc["meta"]["updated_on"] = this.util_get_now_unix_timestamp();
|
|
554
|
-
// caution : db api is being used directly
|
|
555
|
-
await this.db_api.update(doc);
|
|
556
|
-
return doc;
|
|
557
625
|
|
|
558
|
-
} else {
|
|
559
|
-
// doc does not exists, generate a new one
|
|
560
|
-
let new_log_doc = this._get_blank_doc("system_setting")
|
|
561
|
-
new_log_doc.data = {value, name}
|
|
562
|
-
await this.db_api.insert(new_log_doc);
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
626
|
|
|
568
627
|
/**
|
|
569
628
|
* Deletes a document from the database by its ID.
|
|
@@ -575,7 +634,7 @@ export class BeanBagDB {
|
|
|
575
634
|
async delete(criteria) {
|
|
576
635
|
this._check_ready_to_use();
|
|
577
636
|
let doc = await this.read(criteria)
|
|
578
|
-
const delete_blocked = ["schema","
|
|
637
|
+
const delete_blocked = ["schema","system_setting","system_log"]
|
|
579
638
|
if (delete_blocked.includes(doc.schema)){
|
|
580
639
|
throw new Error(`Deletion of ${doc.schema} doc is not support yet.`)
|
|
581
640
|
}
|
|
@@ -611,9 +670,11 @@ export class BeanBagDB {
|
|
|
611
670
|
* for a given schema. It handles system-related data and throws errors for invalid document types
|
|
612
671
|
* or if the document is not found.
|
|
613
672
|
*
|
|
614
|
-
* @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:
|
|
615
676
|
* - 'schema': Retrieves a schema document based on the criteria provided.
|
|
616
|
-
* @param {Object} [criteria={}] - Criteria used to search for the special document.
|
|
677
|
+
* @param {Object} [input.criteria={}] - Criteria used to search for the special document.
|
|
617
678
|
* For example, to search for a schema, the criteria should include the name.
|
|
618
679
|
*
|
|
619
680
|
* @throws {ValidationError} Throws if the `special_doc_type` is not recognized.
|
|
@@ -621,7 +682,8 @@ export class BeanBagDB {
|
|
|
621
682
|
*
|
|
622
683
|
* @returns {Object} The fetched special document based on the type and criteria.
|
|
623
684
|
*/
|
|
624
|
-
async get(
|
|
685
|
+
async get(input){
|
|
686
|
+
|
|
625
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
|
|
626
688
|
const fetch_docs = {
|
|
627
689
|
// to return schema object for the given name
|
|
@@ -652,6 +714,7 @@ export class BeanBagDB {
|
|
|
652
714
|
system_defined : doc.data.system_generated,
|
|
653
715
|
description: doc.data.description,
|
|
654
716
|
link: doc.meta.link,
|
|
717
|
+
title:doc.data.title,
|
|
655
718
|
_id:doc._id
|
|
656
719
|
})
|
|
657
720
|
})
|
|
@@ -660,14 +723,19 @@ export class BeanBagDB {
|
|
|
660
723
|
|
|
661
724
|
}
|
|
662
725
|
}
|
|
663
|
-
if(Object.keys(fetch_docs).
|
|
664
|
-
|
|
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||{})
|
|
665
729
|
return data
|
|
666
730
|
}else{
|
|
667
|
-
throw new ValidationError("Invalid
|
|
731
|
+
throw new ValidationError("Invalid name. Must be : "+Object.keys(fetch_docs).join(","))
|
|
668
732
|
}
|
|
669
733
|
}
|
|
670
734
|
|
|
735
|
+
|
|
736
|
+
//////////////////// methods for special use , requires to be called using the class (rather than the rest api)
|
|
737
|
+
|
|
738
|
+
|
|
671
739
|
/**
|
|
672
740
|
* To load a plugin in the current BeanBagDB instance.
|
|
673
741
|
* Plug_module has to be loaded manually first. It must export an object containing fields: `actions` and `schema`.
|
|
@@ -699,6 +767,51 @@ export class BeanBagDB {
|
|
|
699
767
|
// }
|
|
700
768
|
}
|
|
701
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
|
+
|
|
702
815
|
///////////////////////////////////////////////////////////
|
|
703
816
|
//////////////// simple directed graph ////////////////////////
|
|
704
817
|
//////////////////////////////////////////////////////////
|
|
@@ -711,7 +824,8 @@ export class BeanBagDB {
|
|
|
711
824
|
* @param {*} edge_label
|
|
712
825
|
* @returns {Object}
|
|
713
826
|
*/
|
|
714
|
-
async create_edge(
|
|
827
|
+
async create_edge(input){
|
|
828
|
+
const {node1,node2,edge_name,edge_label=""} = input
|
|
715
829
|
this._check_ready_to_use();
|
|
716
830
|
if(!edge_name){throw new ValidationError("edge_name required")}
|
|
717
831
|
if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
|
|
@@ -756,7 +870,7 @@ async create_edge(node1,node2,edge_name,edge_label=""){
|
|
|
756
870
|
}
|
|
757
871
|
|
|
758
872
|
if(errors.length==0){
|
|
759
|
-
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 }})
|
|
760
874
|
return edge
|
|
761
875
|
}else{
|
|
762
876
|
throw new RelationError(errors)
|
|
@@ -765,8 +879,8 @@ async create_edge(node1,node2,edge_name,edge_label=""){
|
|
|
765
879
|
} catch (error) {
|
|
766
880
|
if(error instanceof DocNotFoundError){
|
|
767
881
|
let doc = {node1:"*",node2:"*",name:edge_name,label:edge_label}
|
|
768
|
-
let new_doc = await this.create("system_edge_constraint",doc)
|
|
769
|
-
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 }})
|
|
770
884
|
return edge
|
|
771
885
|
}else{
|
|
772
886
|
throw error
|
|
@@ -835,7 +949,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
835
949
|
const schema_data = schemas[index]
|
|
836
950
|
steps.push(`checking.${schema_name}`)
|
|
837
951
|
try {
|
|
838
|
-
let schema1 = await this.get("schema",{name:schema_name})
|
|
952
|
+
let schema1 = await this.get({type:"schema",criteria:{name:schema_name}})
|
|
839
953
|
if (schema1["data"]["version"] != schema_data.version) {
|
|
840
954
|
steps.push(`old.${schema_name}.v.${schema1["data"]["version"]}`);
|
|
841
955
|
let full_doc = await this.db_api.get(schema1["_id"]);
|
|
@@ -951,9 +1065,9 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
951
1065
|
* @returns {Object}
|
|
952
1066
|
*/
|
|
953
1067
|
_get_blank_schema_doc(schema_name, schema_object, data) {
|
|
954
|
-
this.util_validate_data(schema_object, data);
|
|
1068
|
+
let new_data = this.util_validate_data({schema:schema_object, data});
|
|
955
1069
|
let obj = this._get_blank_doc(schema_name);
|
|
956
|
-
obj["data"] =
|
|
1070
|
+
obj["data"] = new_data;
|
|
957
1071
|
return obj;
|
|
958
1072
|
}
|
|
959
1073
|
|
|
@@ -1024,11 +1138,13 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1024
1138
|
if (sch_search.docs.length == 0) {throw new DocCreationError(`The schema "${schema}" does not exists`)}
|
|
1025
1139
|
let schemaDoc = sch_search.docs[0]["data"];
|
|
1026
1140
|
// validate data
|
|
1027
|
-
|
|
1141
|
+
if(!schemaDoc.active){throw new DocCreationError(`The schema "${schema}" is not active`)}
|
|
1142
|
+
|
|
1143
|
+
let new_data = this.util_validate_data({schema:schemaDoc.schema, data});
|
|
1028
1144
|
|
|
1029
1145
|
// validate meta
|
|
1030
|
-
if(Object.keys.length>0){
|
|
1031
|
-
this.util_validate_data(sys_sch.editable_metadata_schema, meta)
|
|
1146
|
+
if(Object.keys(meta).length>0){
|
|
1147
|
+
meta = this.util_validate_data({schema:sys_sch.editable_metadata_schema, data:meta})
|
|
1032
1148
|
}
|
|
1033
1149
|
|
|
1034
1150
|
|
|
@@ -1042,25 +1158,25 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1042
1158
|
// @TODO : for schema dos: settings fields must be in schema field
|
|
1043
1159
|
if (schema == "schema") {
|
|
1044
1160
|
//more checks are required
|
|
1045
|
-
this.util_validate_schema_object(
|
|
1161
|
+
this.util_validate_schema_object(new_data);
|
|
1046
1162
|
}
|
|
1047
1163
|
// @TODO : check if single record setting is set to true
|
|
1048
1164
|
//console.log(schemaDoc)
|
|
1049
1165
|
// duplicate check
|
|
1050
1166
|
if (schemaDoc.settings["primary_keys"].length > 0) {
|
|
1051
1167
|
let primary_obj = { schema: schema };
|
|
1052
|
-
schemaDoc.settings["primary_keys"].map((ky) => {primary_obj["data." + ky] =
|
|
1168
|
+
schemaDoc.settings["primary_keys"].map((ky) => {primary_obj["data." + ky] = new_data[ky];});
|
|
1053
1169
|
let prim_search = await this.search({ selector: primary_obj });
|
|
1054
1170
|
if (prim_search.docs.length > 0) {
|
|
1055
1171
|
throw new DocCreationError(`Document with the given primary key (${schemaDoc.settings["primary_keys"].join(",")}) already exists in the schema "${schema}"`);
|
|
1056
1172
|
}
|
|
1057
1173
|
}
|
|
1058
1174
|
// encrypt if required
|
|
1059
|
-
|
|
1175
|
+
|
|
1060
1176
|
if (schemaDoc.settings["encrypted_fields"].length > 0) {
|
|
1061
1177
|
// todo test if encryption is successful
|
|
1062
1178
|
for (let itm of schemaDoc.settings["encrypted_fields"]) {
|
|
1063
|
-
new_data[itm] = await this.utils.encrypt(
|
|
1179
|
+
new_data[itm] = await this.utils.encrypt(new_data[itm], this.encryption_key);
|
|
1064
1180
|
}
|
|
1065
1181
|
}
|
|
1066
1182
|
|
|
@@ -1086,29 +1202,14 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1086
1202
|
*/
|
|
1087
1203
|
util_get_now_unix_timestamp() {return Math.floor(Date.now() / 1000)}
|
|
1088
1204
|
|
|
1089
|
-
|
|
1090
|
-
* Validates that the required fields are present in the provided object.
|
|
1091
|
-
*
|
|
1092
|
-
* @param {string[]} requiredFields - An array of field names that are required.
|
|
1093
|
-
* @param {object} obj - The object to check for the required fields.
|
|
1094
|
-
* @throws {ValidationError} If any of the required fields are missing, an error is thrown.
|
|
1095
|
-
*/
|
|
1205
|
+
|
|
1096
1206
|
util_check_required_fields(requiredFields, obj) {
|
|
1097
1207
|
for (const field of requiredFields) {
|
|
1098
1208
|
if (!obj[field]) {throw new ValidationError(`The field ${field} is required.`)}
|
|
1099
1209
|
}
|
|
1100
1210
|
}
|
|
1101
1211
|
|
|
1102
|
-
/**
|
|
1103
|
-
* Filters an object, returning a new object that only contains the specified fields.
|
|
1104
|
-
*
|
|
1105
|
-
* @param {Object} obj - The object to filter.
|
|
1106
|
-
* @param {Array<String>} fields - An array of field names to retain in the filtered object.
|
|
1107
|
-
*
|
|
1108
|
-
* @returns {Object} - A new object containing only the fields that exist in `obj` from the `fields` array.
|
|
1109
|
-
*
|
|
1110
|
-
* **Example**:
|
|
1111
|
-
*
|
|
1212
|
+
/** Filters an object, returning a new object that only contains the specified fields.
|
|
1112
1213
|
* const data = { name: "Alice", age: 25, location: "NY" };
|
|
1113
1214
|
* const result = util_filter_object(data, ["name", "age"]);
|
|
1114
1215
|
* // result: { name: "Alice", age: 25 }
|
|
@@ -1122,18 +1223,22 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1122
1223
|
}, {});
|
|
1123
1224
|
}
|
|
1124
1225
|
|
|
1125
|
-
|
|
1126
1226
|
/**
|
|
1127
|
-
* Validates a data object against a provided JSON schema
|
|
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 )
|
|
1128
1228
|
* It relies on the external API provided by the user
|
|
1129
1229
|
* @param {Object} schema_obj - The JSON schema object to validate against
|
|
1130
1230
|
* @param {Object} data_obj - The data object to validate
|
|
1131
1231
|
* @throws {Error} If the data object does not conform to the schema
|
|
1132
1232
|
*/
|
|
1133
|
-
util_validate_data(
|
|
1134
|
-
|
|
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)
|
|
1135
1238
|
if (!valid) {
|
|
1136
1239
|
throw new ValidationError(validate.errors);
|
|
1240
|
+
}else{
|
|
1241
|
+
return data
|
|
1137
1242
|
}
|
|
1138
1243
|
}
|
|
1139
1244
|
|
|
@@ -1248,7 +1353,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1248
1353
|
* "banana-earth-rain".
|
|
1249
1354
|
*
|
|
1250
1355
|
*/
|
|
1251
|
-
util_generate_random_link(type
|
|
1356
|
+
util_generate_random_link(input={type:1}) {
|
|
1252
1357
|
const options = {
|
|
1253
1358
|
0:()=>{
|
|
1254
1359
|
// prettier-ignore
|
|
@@ -1262,7 +1367,7 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1262
1367
|
|
|
1263
1368
|
}
|
|
1264
1369
|
}
|
|
1265
|
-
return options[type]()
|
|
1370
|
+
return options[input.type]()
|
|
1266
1371
|
}
|
|
1267
1372
|
}
|
|
1268
1373
|
|