beanbagdb 0.6.4 → 0.6.5
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 +33 -16
- package/src/system_schema.js +14 -10
- package/test/couchdb.js +76 -19
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -407,7 +407,7 @@ export class BeanBagDB {
|
|
|
407
407
|
throw new DocCreationError(`${v_errors.join(",")}.`)
|
|
408
408
|
}
|
|
409
409
|
|
|
410
|
-
if(input.schema=="
|
|
410
|
+
// if(input.schema=="system_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
|
|
411
411
|
if(Object.keys(input.data).length==0){throw new DocCreationError(`No data provided`)}
|
|
412
412
|
try {
|
|
413
413
|
let doc_obj = await this._insert_pre_checks(input.schema, input.data,input?.meta||{}, input?.settings||{});
|
|
@@ -564,6 +564,10 @@ export class BeanBagDB {
|
|
|
564
564
|
// todo : what if additionalField are allowed ??
|
|
565
565
|
let updated_data = { ...full_doc.data, ...allowed_updates };
|
|
566
566
|
|
|
567
|
+
if(full_doc.schema=="system_edge"){
|
|
568
|
+
// extra checks required. if everything is correct
|
|
569
|
+
updated_data = await this._create_edge(updated_data)
|
|
570
|
+
}
|
|
567
571
|
updated_data = this.util_validate_data({schema:schema.schema,data: updated_data});
|
|
568
572
|
|
|
569
573
|
// primary key check if multiple records can be created
|
|
@@ -622,8 +626,6 @@ export class BeanBagDB {
|
|
|
622
626
|
}
|
|
623
627
|
|
|
624
628
|
|
|
625
|
-
|
|
626
|
-
|
|
627
629
|
/**
|
|
628
630
|
* Deletes a document from the database by its ID.
|
|
629
631
|
*
|
|
@@ -821,21 +823,31 @@ export class BeanBagDB {
|
|
|
821
823
|
* @param {object} node1
|
|
822
824
|
* @param {object} node2
|
|
823
825
|
* @param {string} edge_name
|
|
824
|
-
* @param {
|
|
826
|
+
* @param {string} note
|
|
825
827
|
* @returns {Object}
|
|
826
828
|
*/
|
|
827
|
-
async
|
|
828
|
-
|
|
829
|
+
async _create_edge(input){
|
|
830
|
+
console.log(input)
|
|
831
|
+
let {node1,node2,edge_name,note=""} = input
|
|
829
832
|
this._check_ready_to_use();
|
|
830
833
|
if(!edge_name){throw new ValidationError("edge_name required")}
|
|
831
|
-
if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
|
|
832
|
-
if(Object.keys(node2)==0){throw new ValidationError("node2 required")}
|
|
833
|
-
|
|
834
|
+
if(!node1|| Object.keys(node1).length==0){throw new ValidationError("node1 required")}
|
|
835
|
+
if(!node2|| Object.keys(node2).length==0){throw new ValidationError("node2 required")}
|
|
836
|
+
// if nodes are of type string, they are assumed to be ids since they are stored in the DB as ids
|
|
837
|
+
if(typeof(node1)=="string"){node1 = {_id:node1}}
|
|
838
|
+
if(typeof(node2)=="string"){node2 = {_id:node2}}
|
|
834
839
|
let n1 = await this.read(node1)
|
|
835
840
|
let n2 = await this.read(node2)
|
|
841
|
+
if(n1.doc._id==n2.doc._id){
|
|
842
|
+
throw new ValidationError("Both nodes cannot be the same")
|
|
843
|
+
}
|
|
844
|
+
if(n1.doc.schema=="system_edge"|| n2.doc.schema=="system_edge"){
|
|
845
|
+
throw new ValidationError("A node cannot be an existing edge document")
|
|
846
|
+
}
|
|
836
847
|
let edges_constraint
|
|
837
848
|
|
|
838
849
|
try {
|
|
850
|
+
// check if edge_name has a related edge_constraint
|
|
839
851
|
let d = await this.read({schema:"system_edge_constraint",data:{name:edge_name}})
|
|
840
852
|
edges_constraint = d["doc"]["data"]
|
|
841
853
|
let errors = []
|
|
@@ -850,7 +862,7 @@ async create_edge(input){
|
|
|
850
862
|
node2id = n1.doc._id
|
|
851
863
|
}
|
|
852
864
|
}else{
|
|
853
|
-
|
|
865
|
+
errors.push("Invalid nodes.This config of nodes not allowed")
|
|
854
866
|
}
|
|
855
867
|
|
|
856
868
|
let records = await this.search({selector:{schema:"system_edge","data.edge_name":edge_name}})
|
|
@@ -870,18 +882,17 @@ async create_edge(input){
|
|
|
870
882
|
}
|
|
871
883
|
|
|
872
884
|
if(errors.length==0){
|
|
873
|
-
let edge = await this.create({schema:"system_edge",data:
|
|
874
|
-
return
|
|
885
|
+
// let edge = await this.create({schema:"system_edge",data:})
|
|
886
|
+
return {node1: node1id , node2: node2id ,edge_name:edge_name ,note:note}
|
|
875
887
|
}else{
|
|
876
888
|
throw new RelationError(errors)
|
|
877
889
|
}
|
|
878
890
|
|
|
879
891
|
} catch (error) {
|
|
880
892
|
if(error instanceof DocNotFoundError){
|
|
881
|
-
let doc = {node1:"*",node2:"*",name:edge_name,
|
|
893
|
+
let doc = {node1:"*",node2:"*",name:edge_name,note:note}
|
|
882
894
|
let new_doc = await this.create({schema:"system_edge_constraint",data:doc})
|
|
883
|
-
|
|
884
|
-
return edge
|
|
895
|
+
return {node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name ,note:note}
|
|
885
896
|
}else{
|
|
886
897
|
throw error
|
|
887
898
|
}
|
|
@@ -1142,7 +1153,10 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1142
1153
|
// validate data
|
|
1143
1154
|
if(!schemaDoc.active){throw new DocCreationError(`The schema "${schema}" is not active`)}
|
|
1144
1155
|
|
|
1145
|
-
let new_data
|
|
1156
|
+
let new_data
|
|
1157
|
+
if(schema!="system_edge"){
|
|
1158
|
+
new_data = this.util_validate_data({schema:schemaDoc.schema, data});
|
|
1159
|
+
}
|
|
1146
1160
|
|
|
1147
1161
|
// validate meta
|
|
1148
1162
|
if(Object.keys(meta).length>0){
|
|
@@ -1161,6 +1175,9 @@ async _upgrade_schema_in_bulk(schemas,log_upgrade=false,log_message="Schema Upgr
|
|
|
1161
1175
|
if (schema == "schema") {
|
|
1162
1176
|
//more checks are required
|
|
1163
1177
|
this.util_validate_schema_object(new_data);
|
|
1178
|
+
}else if(schema == "system_edge"){
|
|
1179
|
+
let create_edge_after_checks = await this._create_edge(data)
|
|
1180
|
+
new_data = create_edge_after_checks
|
|
1164
1181
|
}
|
|
1165
1182
|
// @TODO : check if single record setting is set to true
|
|
1166
1183
|
//console.log(schemaDoc)
|
package/src/system_schema.js
CHANGED
|
@@ -11,7 +11,7 @@ export const default_app = {
|
|
|
11
11
|
active: true,
|
|
12
12
|
description: "Meta-schema or the schema for defining other schemas",
|
|
13
13
|
system_generated: true,
|
|
14
|
-
version:
|
|
14
|
+
version: 1,
|
|
15
15
|
title: "Schema document",
|
|
16
16
|
schema: {
|
|
17
17
|
type: "object",
|
|
@@ -146,7 +146,7 @@ export const default_app = {
|
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
148
|
system_generated: true,
|
|
149
|
-
version:
|
|
149
|
+
version: 1,
|
|
150
150
|
description:
|
|
151
151
|
"To store user defined key. this can include anything like API tokens etc. There is a special method to fetch this. The values are encrypted",
|
|
152
152
|
name: "system_key",
|
|
@@ -184,7 +184,7 @@ export const default_app = {
|
|
|
184
184
|
},
|
|
185
185
|
},
|
|
186
186
|
{
|
|
187
|
-
version:
|
|
187
|
+
version: 1,
|
|
188
188
|
system_generated: true,
|
|
189
189
|
description:
|
|
190
190
|
"The system relies on these settings for proper functioning or enabling optional features.",
|
|
@@ -218,7 +218,7 @@ export const default_app = {
|
|
|
218
218
|
title: "Edge constraint",
|
|
219
219
|
system_generated: true,
|
|
220
220
|
active: true,
|
|
221
|
-
version:
|
|
221
|
+
version: 1,
|
|
222
222
|
description:
|
|
223
223
|
"To define edge constraints for simple directed graph of records.",
|
|
224
224
|
schema: {
|
|
@@ -275,12 +275,12 @@ export const default_app = {
|
|
|
275
275
|
title: "Edge in the system graph",
|
|
276
276
|
active: true,
|
|
277
277
|
system_generated: true,
|
|
278
|
-
version:
|
|
278
|
+
version: 1,
|
|
279
279
|
description: "To define edges in the simple directed graph of records.",
|
|
280
280
|
schema: {
|
|
281
281
|
type: "object",
|
|
282
282
|
additionalProperties: true,
|
|
283
|
-
required: ["node1", "node2", "
|
|
283
|
+
required: ["node1", "node2", "edge_name"],
|
|
284
284
|
properties: {
|
|
285
285
|
node1: {
|
|
286
286
|
type: "string",
|
|
@@ -291,10 +291,14 @@ export const default_app = {
|
|
|
291
291
|
edge_name: {
|
|
292
292
|
type: "string",
|
|
293
293
|
},
|
|
294
|
+
note:{
|
|
295
|
+
type:"string",
|
|
296
|
+
default:" "
|
|
297
|
+
}
|
|
294
298
|
},
|
|
295
299
|
},
|
|
296
300
|
settings: {
|
|
297
|
-
primary_keys: ["node1", "node2", "
|
|
301
|
+
primary_keys: ["node1", "node2", "edge_name"],
|
|
298
302
|
non_editable_fields: ["edge_type"],
|
|
299
303
|
encrypted_fields: [],
|
|
300
304
|
},
|
|
@@ -304,7 +308,7 @@ export const default_app = {
|
|
|
304
308
|
title: "Media content",
|
|
305
309
|
active: true,
|
|
306
310
|
system_generated: true,
|
|
307
|
-
version:
|
|
311
|
+
version: 1,
|
|
308
312
|
description: "To store images as Base64",
|
|
309
313
|
schema: {
|
|
310
314
|
type: "object",
|
|
@@ -336,7 +340,7 @@ export const default_app = {
|
|
|
336
340
|
system_generated: true,
|
|
337
341
|
title: "System log",
|
|
338
342
|
active: true,
|
|
339
|
-
version:
|
|
343
|
+
version: 1,
|
|
340
344
|
description: "To define edges in the simple directed graph of records.",
|
|
341
345
|
schema: {
|
|
342
346
|
type: "object",
|
|
@@ -369,7 +373,7 @@ export const default_app = {
|
|
|
369
373
|
system_generated: true,
|
|
370
374
|
title: "Executable script",
|
|
371
375
|
active: true,
|
|
372
|
-
version:
|
|
376
|
+
version: 1,
|
|
373
377
|
description: "To create scripts that implement some logic. Can run both on browser and client.",
|
|
374
378
|
schema: {
|
|
375
379
|
type: "object",
|
package/test/couchdb.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import {BeanBagDB} from '../src/index.js';
|
|
2
|
+
import crypto from 'crypto'
|
|
2
3
|
// const SDB = require("beanbagdb")
|
|
3
4
|
|
|
4
5
|
import Ajv from 'ajv';
|
|
5
6
|
import nano from "nano";
|
|
6
|
-
import BeanBagDB from '../src/index.js';
|
|
7
7
|
|
|
8
8
|
export class BeanBagDB_CouchDB extends BeanBagDB {
|
|
9
9
|
constructor(db_url,db_name,encryption_key){
|
|
@@ -41,30 +41,87 @@ export class BeanBagDB_CouchDB extends BeanBagDB {
|
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
43
|
utils:{
|
|
44
|
-
encrypt: async
|
|
45
|
-
const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
encrypt: async (text, encryptionKey) => {
|
|
45
|
+
const encoder = new TextEncoder();
|
|
46
|
+
const data = encoder.encode(text); // Encode the text into bytes
|
|
47
|
+
|
|
48
|
+
// Ensure the encryption key is of valid length (16, 24, or 32 bytes for AES-GCM)
|
|
49
|
+
const keyBytes = encoder.encode(encryptionKey);
|
|
50
|
+
if (
|
|
51
|
+
keyBytes.length !== 16 &&
|
|
52
|
+
keyBytes.length !== 24 &&
|
|
53
|
+
keyBytes.length !== 32
|
|
54
|
+
) {
|
|
55
|
+
throw new Error("Encryption key must be 16, 24, or 32 bytes long.");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Convert encryptionKey to CryptoKey
|
|
59
|
+
const key = await crypto.subtle.importKey(
|
|
60
|
+
"raw",
|
|
61
|
+
keyBytes,
|
|
62
|
+
{ name: "AES-GCM" },
|
|
63
|
+
false,
|
|
64
|
+
["encrypt"]
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Create a random initialization vector (IV)
|
|
68
|
+
const iv = crypto.getRandomValues(new Uint8Array(12)); // 12 bytes for AES-GCM
|
|
69
|
+
|
|
70
|
+
// Encrypt the data
|
|
71
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
72
|
+
{ name: "AES-GCM", iv: iv },
|
|
73
|
+
key,
|
|
74
|
+
data
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Convert encrypted data and IV to base64 for storage
|
|
78
|
+
const encryptedArray = new Uint8Array(encrypted);
|
|
79
|
+
const encryptedText = btoa(String.fromCharCode(...encryptedArray));
|
|
80
|
+
const ivText = btoa(String.fromCharCode(...iv));
|
|
81
|
+
|
|
82
|
+
return ivText + ":" + encryptedText; // Store IV and encrypted text together
|
|
51
83
|
},
|
|
52
|
-
decrypt
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
84
|
+
decrypt: async (encryptedText, encryptionKey) => {
|
|
85
|
+
const [ivText, encryptedData] = encryptedText.split(":");
|
|
86
|
+
|
|
87
|
+
// Convert IV and encrypted data from base64 to byte arrays
|
|
88
|
+
const iv = Uint8Array.from(atob(ivText), (c) => c.charCodeAt(0));
|
|
89
|
+
const encryptedArray = Uint8Array.from(atob(encryptedData), (c) =>
|
|
90
|
+
c.charCodeAt(0)
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const encoder = new TextEncoder();
|
|
94
|
+
|
|
95
|
+
// Convert encryptionKey to CryptoKey
|
|
96
|
+
const key = await crypto.subtle.importKey(
|
|
97
|
+
"raw",
|
|
98
|
+
encoder.encode(encryptionKey),
|
|
99
|
+
{ name: "AES-GCM" },
|
|
100
|
+
false,
|
|
101
|
+
["decrypt"]
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
// Decrypt the data
|
|
105
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
106
|
+
{ name: "AES-GCM", iv: iv },
|
|
107
|
+
key,
|
|
108
|
+
encryptedArray
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// Convert decrypted data back to a string
|
|
112
|
+
const decoder = new TextDecoder();
|
|
113
|
+
return decoder.decode(decrypted);
|
|
59
114
|
},
|
|
60
115
|
ping : ()=>{
|
|
61
116
|
// @TODO ping the database to check connectivity when class is ready to use
|
|
62
117
|
},
|
|
63
118
|
validate_schema: (schema_obj, data_obj)=>{
|
|
64
|
-
const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
|
|
119
|
+
const ajv = new Ajv({code: {esm: true},strict:false,useDefaults:true}) // options can be passed, e.g. {allErrors: true}
|
|
120
|
+
const data_copy = {...data_obj}
|
|
65
121
|
const validate = ajv.compile(schema_obj);
|
|
66
|
-
const valid = validate(
|
|
67
|
-
|
|
122
|
+
const valid = validate(data_copy);
|
|
123
|
+
|
|
124
|
+
return {valid,validate,data:data_copy}
|
|
68
125
|
}
|
|
69
126
|
}
|
|
70
127
|
}
|