beanbagdb 0.5.50 → 0.5.52

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,6 +11,12 @@ 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,
@@ -41,14 +48,17 @@ export const schema_schema = {
41
48
  type: "string",
42
49
  },
43
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."
44
52
  },
45
- editable_fields: {
53
+ non_editable_fields: {
46
54
  type: "array",
47
55
  default: [],
48
56
  items: {
49
57
  type: "string",
50
58
  },
51
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."
52
62
  },
53
63
  encrypted_fields: {
54
64
  type: "array",
@@ -57,6 +67,7 @@ export const schema_schema = {
57
67
  type: "string",
58
68
  },
59
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"
60
71
  },
61
72
  single_record: {
62
73
  type: "boolean",
@@ -65,6 +76,7 @@ export const schema_schema = {
65
76
  "If set, only a single records with this schema will be allowed to insert in the database",
66
77
  },
67
78
  },
79
+ required :["primary_keys","non_editable_fields","single_record","encrypted_fields"]
68
80
  },
69
81
  },
70
82
  required: ["name","description","schema", "settings"],
@@ -76,29 +88,9 @@ export const schema_schema = {
76
88
  };
77
89
 
78
90
  export const system_schemas = {
79
- logs: {
80
- system_generated:true,
81
- description:"Schema for the log doc. Single log doc for the whole DB to log stuff about the DB",
82
- name: "system_logs",
83
- schema: {
84
- type: "object",
85
- additionalProperties: true,
86
- properties: {
87
- logs: {
88
- type: "array",
89
- items: {
90
- type: "object",
91
- additionalProperties: true,
92
- },
93
- },
94
- },
95
- },
96
- settings: {
97
- single_record: true
98
- },
99
- },
100
91
  keys: {
101
92
  system_generated:true,
93
+ version:0.5,
102
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",
103
95
  name: "system_keys",
104
96
  schema: {
@@ -129,10 +121,13 @@ export const system_schemas = {
129
121
  },
130
122
  settings: {
131
123
  primary_keys: ["name"],
132
- encrypted_fields:["value"]
124
+ encrypted_fields:["value"],
125
+ non_editable_fields:[],
126
+ single_record: false
133
127
  },
134
128
  },
135
129
  settings: {
130
+ version:0.5,
136
131
  system_generated:true,
137
132
  description:"The system relies on these settings for proper functioning or enabling optional features.",
138
133
  name: "system_settings",
@@ -144,29 +139,27 @@ export const system_schemas = {
144
139
  name: {
145
140
  type: "string",
146
141
  minLength: 5,
147
- maxLength: 250,
142
+ maxLength: 1000,
148
143
  pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
149
144
  },
150
145
  value: {
151
- type: "string",
152
- minLength: 5,
153
- maxLength: 5000,
154
- pattern: "^[^\n\r]*$",
155
- description:"Must be a single line string"
156
- },
157
- user_editable: {
158
- type: "boolean",
159
- default: true,
160
- description:
161
- "Whether this setting is editable by the user or only by the system",
146
+ type: ["string", "number", "boolean", "array"]
162
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
+ }
163
154
  },
164
155
  },
165
156
  settings: {
166
157
  primary_keys: ["name"],
167
- editable_fields: ["value"],
158
+ non_editable_fields: ["name"],
159
+ encrypted_fields:[],
160
+ single_record:false
168
161
  },
169
- },
162
+ }
170
163
  };
171
164
 
172
165
  // this is not stored in the DB. only for validating the metadata during doc update
@@ -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,7 +1,7 @@
1
1
  // to test initialization of the BeanBagDB class
2
2
  import { get_pdb_doc } from './pouchdb.js';
3
3
  import { throws, strictEqual } from "assert";
4
- import BeanBagDB from '../src/index.js';
4
+ import {BeanBagDB} from '../src/index.js';
5
5
 
6
6
  /**
7
7
  * Initial setup
@@ -2,9 +2,7 @@
2
2
  // to test initialization of the BeanBagDB class
3
3
  import { get_pdb_doc } from './pouchdb.js';
4
4
  import { throws, strictEqual,rejects } from "assert";
5
- import BeanBagDB from '../src/index.js';
6
-
7
-
5
+ import {BeanBagDB,ValidationError} from '../src/index.js';
8
6
 
9
7
  let database // this is the global db object
10
8
 
@@ -20,60 +18,64 @@ describe("Successful database class init (required for further testing) ", async
20
18
  });
21
19
  });
22
20
 
23
-
24
-
25
-
26
- describe("Testing creation of schema docs", async () => {
21
+ describe("Schema doc insertion gives errors when", async () => {
27
22
  let schema_docs_invalid = [
28
- {
23
+ [ "name missing",
24
+ {
29
25
  name: "",
30
26
  description: "",
31
27
  schema: {},
32
28
  settings: {},
33
- },
34
- {
29
+ }],
30
+ ["name is too short ",
31
+ {
35
32
  name: "nos",
36
33
  description: "",
37
34
  schema: {},
38
35
  settings: {},
39
- },
40
- {
36
+ }],
37
+ ["schema is blank",
38
+ {
41
39
  name: "contact",
42
40
  description: "",
43
41
  schema: {},
44
42
  settings: {},
45
- },
46
- {
43
+ }],
44
+ ["schema object missing",
45
+ {
47
46
  name: "contact",
48
47
  description: "This can be left blank",
49
- schema: {},
50
48
  settings: {},
51
- },
52
- {
49
+ }],
50
+ [
51
+ "no schema.type",{
53
52
  name: "contact",
54
53
  description: "This can be left blank",
55
54
  schema: {
56
55
  "abc":"something"
57
56
  },
58
57
  settings: {},
59
- },
60
- {
58
+ }],
59
+ ["schema.type is invalid",
60
+ {
61
61
  name: "contact",
62
62
  description: "This can be left blank",
63
63
  schema: {
64
64
  "type":"something",
65
65
  },
66
66
  settings: {},
67
- },
68
- {
67
+ }],
68
+ ["schema.properties is missing",
69
+ {
69
70
  name: "contact",
70
71
  description: "This can be left blank",
71
72
  schema: {
72
73
  "type":"object",
73
74
  },
74
75
  settings: {},
75
- },
76
- {
76
+ }],
77
+ ["schema.properties is invalid",
78
+ {
77
79
  name: "contact",
78
80
  description: "This can be left blank",
79
81
  schema: {
@@ -81,8 +83,9 @@ describe("Testing creation of schema docs", async () => {
81
83
  "properties":"something"
82
84
  },
83
85
  settings: {},
84
- },
85
- {
86
+ }],
87
+ ["schema.properties are missing/blank object",
88
+ {
86
89
  name: "contact",
87
90
  description: "This can be left blank",
88
91
  schema: {
@@ -90,8 +93,9 @@ describe("Testing creation of schema docs", async () => {
90
93
  "properties":{}
91
94
  },
92
95
  settings: {},
93
- },
94
- {
96
+ }],
97
+ ["schema.additionalProperties is missing",
98
+ {
95
99
  name: "contact",
96
100
  description: "This can be left blank",
97
101
  schema: {
@@ -99,8 +103,9 @@ describe("Testing creation of schema docs", async () => {
99
103
  "properties":{"name":{"type":"string"}}
100
104
  },
101
105
  settings: {},
102
- },
103
- {
106
+ }],
107
+ ["schema.additionalProperties is invalid",
108
+ {
104
109
  name: "contact",
105
110
  description: "This can be left blank",
106
111
  schema: {
@@ -109,8 +114,30 @@ describe("Testing creation of schema docs", async () => {
109
114
  "additionalProperties":"no"
110
115
  },
111
116
  settings: {},
112
- },
113
- // {
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
+ // {
114
141
  // name: "contact",
115
142
  // description: "This can be left blank",
116
143
  // schema: {
@@ -118,28 +145,183 @@ describe("Testing creation of schema docs", async () => {
118
145
  // "properties":{"name":{"type":"string"}},
119
146
  // "additionalProperties":true
120
147
  // },
121
- // settings: {},
122
- // },
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
+ }]
123
305
  ];
124
306
 
125
- let doc_obj = get_pdb_doc("test_database_25", "qwertyuiopaqwsde1254");
126
- let database = new BeanBagDB(doc_obj);
127
- database.initialize_db();
128
- database.ready();
129
- // schema_docs_invalid.forEach(doc=>{})
130
- for (let index = 0; index < schema_docs_invalid.length; index++) {
131
- const element = schema_docs_invalid[index];
132
307
 
133
- it("throws error", async () => {
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 () => {
134
317
  await rejects(
135
318
  async () => {
136
- await database.insert("schema", element);
319
+ await database.create("schema", element[1]);
137
320
  },
138
- Error
321
+ ValidationError
139
322
  );
140
323
  });
141
-
142
- }
324
+ });
143
325
 
144
326
  });
145
327
 
package/test/pouchdb.js CHANGED
@@ -10,6 +10,7 @@ export const get_pdb_doc = (dbname,secret)=>{
10
10
  const pdb = new PouchDB(dbname);
11
11
  const doc_obj = {
12
12
  name: dbname,
13
+ db_name:"pouchdb",
13
14
  encryption_key: secret,
14
15
  api: {
15
16
  insert: async (doc) => {
package/test/test1.js CHANGED
@@ -158,21 +158,20 @@
158
158
 
159
159
  import { get_pdb_doc } from './pouchdb.js';
160
160
  import { throws, strictEqual } from "assert";
161
- import BeanBagDB from '../src/index.js';
161
+ import {BeanBagDB} from '../src/index.js';
162
162
 
163
163
  (async()=>{
164
+
164
165
  let schema_docs_invalid = [
165
166
  {
166
- "name":"",
167
- "description":"",
168
- "schema":{},
169
- "settings":{}
167
+ name: "",
168
+ description: "",
169
+ schema: {},
170
+ settings: {},
170
171
  }
171
-
172
172
  ]
173
173
  let doc_obj = get_pdb_doc("test_database_26","qwertyuiopaqwsde1254")
174
174
  let database = new BeanBagDB(doc_obj);
175
- database.initialize_db()
176
- database.ready()
177
- let a = await database.insert("schema",schema_docs_invalid[0])
175
+ await database.ready()
176
+ let a = await database.create("schema",schema_docs_invalid[0])
178
177
  })()
File without changes