beanbagdb 0.5.46 → 0.5.51

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.
@@ -1,7 +1,8 @@
1
1
  export const schema_schema = {
2
2
  name: "schema",
3
- description:"Meta-schema or schema for defining other schemas",
3
+ description:"Meta-schema or the schema for defining other schemas",
4
4
  system_generated:true,
5
+ version:0.5,
5
6
  schema: {
6
7
  type: "object",
7
8
  additionalProperties: false,
@@ -10,22 +11,31 @@ export const schema_schema = {
10
11
  type:"boolean",
11
12
  default:false
12
13
  },
14
+ version: {
15
+ type: "number",
16
+ minimum: 0,
17
+ default: 1,
18
+ description:"This is an optional field.To be used primarily for system schemas"
19
+ },
13
20
  name: {
14
21
  type: "string",
15
22
  minLength: 5,
16
23
  maxLength: 50,
17
24
  pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
25
+ description:"This is the name of the schema.It cannot be changed later"
18
26
  },
19
27
  description:{
20
28
  type:"string",
21
- minLength:1,
22
- maxLength:1000
29
+ minLength:0,
30
+ maxLength:1000,
31
+ description:"A small description of what data in this schema stores."
23
32
  },
24
33
  schema: {
25
34
  type: "object",
26
35
  additionalProperties: true,
27
36
  minProperties: 1,
28
37
  maxProperties: 50,
38
+ description:"This must be a valid JSON Schema which will be used to validate documents created with this schema.See this https://tour.json-schema.org/",
29
39
  },
30
40
  settings: {
31
41
  type: "object",
@@ -38,14 +48,17 @@ export const schema_schema = {
38
48
  type: "string",
39
49
  },
40
50
  maxItems: 10,
51
+ description:"Fields that makes each document unique in the schema.Leave it blank if you do not need it. You can still be able to distinguish documents using the link field and the document id."
41
52
  },
42
- editable_fields: {
53
+ non_editable_fields: {
43
54
  type: "array",
44
55
  default: [],
45
56
  items: {
46
57
  type: "string",
47
58
  },
48
59
  maxItems: 50,
60
+ minItems:0,
61
+ description:"The list of fields whose values are added when the document is created but cannot be edited later in future."
49
62
  },
50
63
  encrypted_fields: {
51
64
  type: "array",
@@ -54,6 +67,7 @@ export const schema_schema = {
54
67
  type: "string",
55
68
  },
56
69
  maxItems: 50,
70
+ description:"Once set, all the data in this field will be encrypted before storing it in the database. Encryption key must be provided during the time of BeanBagDB initialization and must be managed by the user as it is NOT stored in the database"
57
71
  },
58
72
  single_record: {
59
73
  type: "boolean",
@@ -62,6 +76,7 @@ export const schema_schema = {
62
76
  "If set, only a single records with this schema will be allowed to insert in the database",
63
77
  },
64
78
  },
79
+ required :["primary_keys","non_editable_fields","single_record","encrypted_fields"]
65
80
  },
66
81
  },
67
82
  required: ["name","description","schema", "settings"],
@@ -73,29 +88,9 @@ export const schema_schema = {
73
88
  };
74
89
 
75
90
  export const system_schemas = {
76
- logs: {
77
- system_generated:true,
78
- description:"Schema for the log doc. Single log doc for the whole DB to log stuff about the DB",
79
- name: "system_logs",
80
- schema: {
81
- type: "object",
82
- additionalProperties: true,
83
- properties: {
84
- logs: {
85
- type: "array",
86
- items: {
87
- type: "object",
88
- additionalProperties: true,
89
- },
90
- },
91
- },
92
- },
93
- settings: {
94
- single_record: true
95
- },
96
- },
97
91
  keys: {
98
92
  system_generated:true,
93
+ version:0.5,
99
94
  description:"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",
100
95
  name: "system_keys",
101
96
  schema: {
@@ -126,10 +121,13 @@ export const system_schemas = {
126
121
  },
127
122
  settings: {
128
123
  primary_keys: ["name"],
129
- encrypted_fields:["value"]
124
+ encrypted_fields:["value"],
125
+ non_editable_fields:[],
126
+ single_record: false
130
127
  },
131
128
  },
132
129
  settings: {
130
+ version:0.5,
133
131
  system_generated:true,
134
132
  description:"The system relies on these settings for proper functioning or enabling optional features.",
135
133
  name: "system_settings",
@@ -141,27 +139,25 @@ export const system_schemas = {
141
139
  name: {
142
140
  type: "string",
143
141
  minLength: 5,
144
- maxLength: 250,
142
+ maxLength: 1000,
145
143
  pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
146
144
  },
147
145
  value: {
148
- type: "string",
149
- minLength: 5,
150
- maxLength: 5000,
151
- pattern: "^[^\n\r]*$",
152
- description:"Must be a single line string"
153
- },
154
- user_editable: {
155
- type: "boolean",
156
- default: true,
157
- description:
158
- "Whether this setting is editable by the user or only by the system",
146
+ type: ["string", "number", "boolean", "array"]
159
147
  },
148
+ on_update_array:{
149
+ type:"string",
150
+ default:"replace",
151
+ description:"Special operation only for updating Arrays. Either replace it or append new elements to it. Cannot be edited",
152
+ enum:["replace","append"],
153
+ }
160
154
  },
161
155
  },
162
156
  settings: {
163
157
  primary_keys: ["name"],
164
- editable_fields: ["value"],
158
+ non_editable_fields: ["name"],
159
+ encrypted_fields:[],
160
+ single_record:false
165
161
  },
166
162
  },
167
163
  };
@@ -0,0 +1,29 @@
1
+ import * as cdb from "./couchdb.js"
2
+ import "dotenv/config"
3
+
4
+
5
+
6
+
7
+
8
+ (async()=>{
9
+
10
+ let db = new cdb.BeanBagDB_CouchDB(process.env.cdburl, process.env.cdbname, process.env.secret)
11
+
12
+ //db.initialize_db()
13
+ await db.ready()
14
+ console.log(await db.metadata())
15
+ let test_schema = {
16
+ "name":"contact_list",
17
+ "description":"My contact book",
18
+ "schema":{},
19
+ "settings":{}
20
+ }
21
+
22
+ //let sch = await db.insert("schema",test_schema,{"link":"test1"})
23
+ //console.log(sch)
24
+
25
+
26
+ })();
27
+
28
+
29
+
@@ -0,0 +1,75 @@
1
+ // const crypto = require('crypto');
2
+ // const SDB = require("beanbagdb")
3
+
4
+ import Ajv from 'ajv';
5
+ import nano from "nano";
6
+ import BeanBagDB from '../src/index.js';
7
+
8
+ export class BeanBagDB_CouchDB extends BeanBagDB {
9
+ constructor(db_url,db_name,encryption_key){
10
+ //const cdb = import("nano")(db_url)
11
+ const cdb = nano(db_url)
12
+ const doc_obj = {
13
+ db_name:"couchdb",
14
+ name: db_name,
15
+ encryption_key: encryption_key,
16
+ api:{
17
+ insert: async (doc)=>{
18
+ const result = await cdb.insert(doc)
19
+ return result
20
+ },
21
+ // delete: ()=>{db1.destroy},
22
+ update: async (doc)=>{
23
+ const result = await cdb.insert(doc)
24
+ return result
25
+ },
26
+ search: async (query)=>{
27
+ const results = await cdb.find(query)
28
+ return results // of the form {docs:[],...}
29
+ },
30
+ get: async (id)=>{
31
+ const data = await cdb.get(id)
32
+ return data
33
+ },
34
+ createIndex: async (filter)=>{
35
+ const data = await cdb.createIndex(filter)
36
+ return data
37
+ },
38
+ delete: async (doc_id)=>{
39
+ const data = await cdb.get(id)
40
+ await cdb.destroy(data._id,data._rev)
41
+ }
42
+ },
43
+ utils:{
44
+ encrypt: (text,encryptionKey)=>{
45
+ const key = crypto.scryptSync(encryptionKey, 'salt', 32); // Derive a 256-bit key
46
+ const iv = crypto.randomBytes(16); // Initialization vector
47
+ const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
48
+ let encrypted = cipher.update(text, 'utf8', 'hex');
49
+ encrypted += cipher.final('hex');
50
+ return iv.toString('hex') + ':' + encrypted; // Prepend the IV for later use
51
+ },
52
+ decrypt : (encryptedText, encryptionKey)=>{
53
+ const key = crypto.scryptSync(encryptionKey, 'salt', 32); // Derive a 256-bit key
54
+ const [iv, encrypted] = encryptedText.split(':').map(part => Buffer.from(part, 'hex'));
55
+ const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
56
+ let decrypted = decipher.update(encrypted, 'hex', 'utf8');
57
+ decrypted += decipher.final('utf8');
58
+ return decrypted;
59
+ },
60
+ ping : ()=>{
61
+ // @TODO ping the database to check connectivity when class is ready to use
62
+ },
63
+ validate_schema: (schema_obj, data_obj)=>{
64
+ const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
65
+ const validate = ajv.compile(schema_obj);
66
+ const valid = validate(data_obj);
67
+ return {valid,validate}
68
+ }
69
+ }
70
+ }
71
+ super(doc_obj)
72
+ }
73
+ }
74
+
75
+
package/test/init.test.js CHANGED
@@ -1,83 +1,13 @@
1
- // to test initialization of the BeanBagDB class. using in memory pouch db for testing to avoid additional setup.
2
- import PouchDB from 'pouchdb';
3
- import pouchdbFind from 'pouchdb-find';
4
- PouchDB.plugin(pouchdbFind)
5
- import { scryptSync, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
6
- const db_name = "test_database_24"
7
- const pdb = new PouchDB(db_name);
8
- import Ajv from 'ajv';
9
-
10
- const doc_obj = {
11
- name: db_name,
12
- encryption_key: "qwertyuiopaqwsde1254",
13
- api: {
14
- insert: async (doc) => {
15
- const result = await pdb.post(doc);
16
- return result;
17
- },
18
- // delete: ()=>{db1.destroy},
19
- update: async (doc) => {
20
- const result = await pdb.put(doc);
21
- return result;
22
- },
23
- search: async (query) => {
24
- const results = await pdb.find(query);
25
- return results; // of the form {docs:[],...}
26
- },
27
- get: async (id) => {
28
- const data = await pdb.get(id);
29
- return data;
30
- },
31
- delete: async (id) => {
32
- const doc = await pdb.get(id);
33
- const resp = await pdb.remove(doc);
34
- return resp;
35
- },
36
- createIndex: async (filter) => {
37
- const data = await pdb.createIndex(filter);
38
- return data;
39
- },
40
- },
41
- utils: {
42
- encrypt: (text, encryptionKey) => {
43
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
44
- const iv = randomBytes(16); // Initialization vector
45
- const cipher = createCipheriv("aes-256-cbc", key, iv);
46
- let encrypted = cipher.update(text, "utf8", "hex");
47
- encrypted += cipher.final("hex");
48
- return iv.toString("hex") + ":" + encrypted; // Prepend the IV for later use
49
- },
50
- decrypt: (encryptedText, encryptionKey) => {
51
- const key = scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
52
- const [iv, encrypted] = encryptedText
53
- .split(":")
54
- .map((part) => Buffer.from(part, "hex"));
55
- const decipher = createDecipheriv("aes-256-cbc", key, iv);
56
- let decrypted = decipher.update(encrypted, "hex", "utf8");
57
- decrypted += decipher.final("utf8");
58
- return decrypted;
59
- },
60
- ping: () => {
61
- // @TODO ping the database to check connectivity when class is ready to use
62
- },
63
- validate_schema: (schema_obj, data_obj)=>{
64
- const ajv = new Ajv({code: {esm: true}}) // options can be passed, e.g. {allErrors: true}
65
- const validate = ajv.compile(schema_obj);
66
- const valid = validate(data_obj);
67
- return {valid,validate}
68
- }
69
- },
70
- }
71
-
72
- let the_correct_object = {};
73
-
1
+ // to test initialization of the BeanBagDB class
2
+ import { get_pdb_doc } from './pouchdb.js';
74
3
  import { throws, strictEqual } from "assert";
4
+ import {BeanBagDB} from '../src/index.js';
75
5
 
76
- import BeanBagDB from '../src/index.js';
77
6
  /**
78
7
  * Initial setup
79
8
  * database is the global var where the beanbag class is initialized
80
9
  */
10
+
81
11
  const test_set1 = [
82
12
  ["name", "sample"],
83
13
  ["encryption_key", "sample_key"],
@@ -210,6 +140,7 @@ describe("Successful database class init", async () => {
210
140
  });
211
141
 
212
142
  it("DB init successful", () => {
143
+ let doc_obj = get_pdb_doc("test_database_24","qwertyuiopaqwsde1254")
213
144
  database = new BeanBagDB(doc_obj);
214
145
  strictEqual(
215
146
  database instanceof BeanBagDB,
@@ -1 +1,327 @@
1
- // to test database operations. assuming the class is initialized successfully
1
+ // to test database operations. assuming the class is initialized successfully
2
+ // to test initialization of the BeanBagDB class
3
+ import { get_pdb_doc } from './pouchdb.js';
4
+ import { throws, strictEqual,rejects } from "assert";
5
+ import {BeanBagDB,ValidationError} from '../src/index.js';
6
+
7
+ let database // this is the global db object
8
+
9
+ describe("Successful database class init (required for further testing) ", async () => {
10
+ it("DB init successful", () => {
11
+ let doc_obj = get_pdb_doc("test_database_25","qwertyuiopaqwsde1254")
12
+ database = new BeanBagDB(doc_obj);
13
+ strictEqual(
14
+ database instanceof BeanBagDB,
15
+ true,
16
+ "The variable is initialized successfully"
17
+ );
18
+ });
19
+ });
20
+
21
+ describe("Schema doc insertion gives errors when", async () => {
22
+ let schema_docs_invalid = [
23
+ [ "name missing",
24
+ {
25
+ name: "",
26
+ description: "",
27
+ schema: {},
28
+ settings: {},
29
+ }],
30
+ ["name is too short ",
31
+ {
32
+ name: "nos",
33
+ description: "",
34
+ schema: {},
35
+ settings: {},
36
+ }],
37
+ ["schema is blank",
38
+ {
39
+ name: "contact",
40
+ description: "",
41
+ schema: {},
42
+ settings: {},
43
+ }],
44
+ ["schema object missing",
45
+ {
46
+ name: "contact",
47
+ description: "This can be left blank",
48
+ settings: {},
49
+ }],
50
+ [
51
+ "no schema.type",{
52
+ name: "contact",
53
+ description: "This can be left blank",
54
+ schema: {
55
+ "abc":"something"
56
+ },
57
+ settings: {},
58
+ }],
59
+ ["schema.type is invalid",
60
+ {
61
+ name: "contact",
62
+ description: "This can be left blank",
63
+ schema: {
64
+ "type":"something",
65
+ },
66
+ settings: {},
67
+ }],
68
+ ["schema.properties is missing",
69
+ {
70
+ name: "contact",
71
+ description: "This can be left blank",
72
+ schema: {
73
+ "type":"object",
74
+ },
75
+ settings: {},
76
+ }],
77
+ ["schema.properties is invalid",
78
+ {
79
+ name: "contact",
80
+ description: "This can be left blank",
81
+ schema: {
82
+ "type":"object",
83
+ "properties":"something"
84
+ },
85
+ settings: {},
86
+ }],
87
+ ["schema.properties are missing/blank object",
88
+ {
89
+ name: "contact",
90
+ description: "This can be left blank",
91
+ schema: {
92
+ "type":"object",
93
+ "properties":{}
94
+ },
95
+ settings: {},
96
+ }],
97
+ ["schema.additionalProperties is missing",
98
+ {
99
+ name: "contact",
100
+ description: "This can be left blank",
101
+ schema: {
102
+ "type":"object",
103
+ "properties":{"name":{"type":"string"}}
104
+ },
105
+ settings: {},
106
+ }],
107
+ ["schema.additionalProperties is invalid",
108
+ {
109
+ name: "contact",
110
+ description: "This can be left blank",
111
+ schema: {
112
+ "type":"object",
113
+ "properties":{"name":{"type":"string"}},
114
+ "additionalProperties":"no"
115
+ },
116
+ settings: {},
117
+ }],
118
+ [
119
+ "setting is missing",{
120
+ name: "contact",
121
+ description: "This can be left blank",
122
+ schema: {
123
+ "type":"object",
124
+ "properties":{"name":{"type":"string"}},
125
+ "additionalProperties":true
126
+ }
127
+ }],
128
+ [
129
+ "setting is invalid",{
130
+ name: "contact",
131
+ description: "This can be left blank",
132
+ schema: {
133
+ "type":"object",
134
+ "properties":{"name":{"type":"string"}},
135
+ "additionalProperties":true
136
+ },
137
+ settings:"none"
138
+ }],
139
+ // ["settings.primary_keys is missing",
140
+ // {
141
+ // name: "contact",
142
+ // description: "This can be left blank",
143
+ // schema: {
144
+ // "type":"object",
145
+ // "properties":{"name":{"type":"string"}},
146
+ // "additionalProperties":true
147
+ // },
148
+ // settings: {
149
+ // },
150
+ // }],
151
+ ["settings.primary_keys is invalid",
152
+ {
153
+ name: "contact",
154
+ description: "This can be left blank",
155
+ schema: {
156
+ "type":"object",
157
+ "properties":{"name":{"type":"string"}},
158
+ "additionalProperties":true
159
+ },
160
+ settings: {
161
+ primary_keys:"name"
162
+ },
163
+ }],
164
+ ["settings.non_editable_fields is invalid",
165
+ {
166
+ name: "contact",
167
+ description: "This can be left blank",
168
+ schema: {
169
+ "type":"object",
170
+ "properties":{"name":{"type":"string"}},
171
+ "additionalProperties":true
172
+ },
173
+ settings: {
174
+ primary_keys:["name"],
175
+ non_editable_fields:"all"
176
+ },
177
+ }],
178
+ ["settings.single_record is invalid",
179
+ {
180
+ name: "contact",
181
+ description: "This can be left blank",
182
+ schema: {
183
+ "type":"object",
184
+ "properties":{"name":{"type":"string"}},
185
+ "additionalProperties":true
186
+ },
187
+ settings: {
188
+ primary_keys:["name"],
189
+ non_editable_fields:[],
190
+ single_record:"no"
191
+ },
192
+ }],
193
+ ["settings.encrypted_fields is invalid",
194
+ {
195
+ name: "contact",
196
+ description: "This can be left blank",
197
+ schema: {
198
+ "type":"object",
199
+ "properties":{"name":{"type":"string"}},
200
+ "additionalProperties":true
201
+ },
202
+ settings: {
203
+ primary_keys:["name"],
204
+ non_editable_fields:[],
205
+ single_record:false,
206
+ encrypted_fields:"none"
207
+ },
208
+ }],
209
+ ["settings.primary_keys fields are not defined in schema",
210
+ {
211
+ name: "contact",
212
+ description: "This can be left blank",
213
+ schema: {
214
+ "type":"object",
215
+ "properties":{"name":{"type":"string"}},
216
+ "additionalProperties":true
217
+ },
218
+ settings: {
219
+ primary_keys:["name1"],
220
+ non_editable_fields:[],
221
+ single_record:false,
222
+ encrypted_fields:"none"
223
+ },
224
+ }],
225
+ ["settings.primary_keys field is an object",
226
+ {
227
+ name: "contact",
228
+ description: "This can be left blank",
229
+ schema: {
230
+ "type":"object",
231
+ "properties":{"name":{"type":"object"},"address":{type:"string"}},
232
+ "additionalProperties":true
233
+ },
234
+ settings: {
235
+ primary_keys:["name"],
236
+ non_editable_fields:[],
237
+ single_record:false,
238
+ encrypted_fields:[]
239
+ },
240
+ }],
241
+ ["settings.non_editable_fields not defined in the schema",
242
+ {
243
+ name: "contact",
244
+ description: "This can be left blank",
245
+ schema: {
246
+ "type":"object",
247
+ "properties":{"name":{"type":"string"},"address":{type:"string"}},
248
+ "additionalProperties":true
249
+ },
250
+ settings: {
251
+ primary_keys:["name"],
252
+ non_editable_fields:["mobile"],
253
+ single_record:false,
254
+ encrypted_fields:[]
255
+ },
256
+ }],
257
+ ["settings.encrypted_fields not defined in the schema",
258
+ {
259
+ name: "contact",
260
+ description: "This can be left blank",
261
+ schema: {
262
+ "type":"object",
263
+ "properties":{"name":{"type":"string"},"address":{type:"object"},"secret":{"type":"string"}},
264
+ "additionalProperties":true
265
+ },
266
+ settings: {
267
+ primary_keys:["name"],
268
+ non_editable_fields:["mobile"],
269
+ single_record:false,
270
+ encrypted_fields:["password"]
271
+ },
272
+ }],
273
+ ["settings.encrypted_fields is not a string",
274
+ {
275
+ name: "contact",
276
+ description: "This can be left blank",
277
+ schema: {
278
+ "type":"object",
279
+ "properties":{"name":{"type":"string"},"address":{type:"object"},"secret":{"type":"string"}},
280
+ "additionalProperties":true
281
+ },
282
+ settings: {
283
+ primary_keys:["name"],
284
+ non_editable_fields:["mobile"],
285
+ single_record:false,
286
+ encrypted_fields:["address"]
287
+ },
288
+ }],
289
+ ["settings.encrypted_fields is a primary key",
290
+ {
291
+ name: "contact",
292
+ description: "This can be left blank",
293
+ schema: {
294
+ "type":"object",
295
+ "properties":{"name":{"type":"string"},"address":{type:"object"},"secret":{"type":"string"}},
296
+ "additionalProperties":true
297
+ },
298
+ settings: {
299
+ primary_keys:["name"],
300
+ non_editable_fields:["mobile"],
301
+ single_record:false,
302
+ encrypted_fields:["name"]
303
+ },
304
+ }]
305
+ ];
306
+
307
+
308
+ before(async () => {
309
+ let doc_obj = get_pdb_doc("test_database_25", "qwertyuiopaqwsde1254");
310
+ database = new BeanBagDB(doc_obj);
311
+ await database.ready(); // Ensure the database is ready before running tests
312
+ console.log("Ready for more tests...");
313
+ });
314
+
315
+ schema_docs_invalid.forEach((element, index) => {
316
+ it(`${element[0]}`, async () => {
317
+ await rejects(
318
+ async () => {
319
+ await database.insert("schema", element[1]);
320
+ },
321
+ ValidationError
322
+ );
323
+ });
324
+ });
325
+
326
+ });
327
+