beanbagdb 0.0.1 → 0.5.1

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.
@@ -0,0 +1,184 @@
1
+ const schema_schema = {
2
+ name: "schema",
3
+ description:"Meta-schema or schema for defining other schemas",
4
+ system_generated:true,
5
+ schema: {
6
+ type: "object",
7
+ additionalProperties: false,
8
+ properties: {
9
+ system_generated:{
10
+ type:"boolean",
11
+ default:false
12
+ },
13
+ name: {
14
+ type: "string",
15
+ minLength: 5,
16
+ maxLength: 50,
17
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
18
+ },
19
+ description:{
20
+ type:"string",
21
+ minLength:1,
22
+ maxLength:1000
23
+ },
24
+ schema: {
25
+ type: "object",
26
+ additionalProperties: true,
27
+ minProperties: 1,
28
+ maxProperties: 50,
29
+ },
30
+ settings: {
31
+ type: "object",
32
+ additionalProperties: true,
33
+ properties: {
34
+ primary_keys: {
35
+ type: "array",
36
+ default: [],
37
+ items: {
38
+ type: "string",
39
+ },
40
+ maxItems: 10,
41
+ },
42
+ editable_fields: {
43
+ type: "array",
44
+ default: [],
45
+ items: {
46
+ type: "string",
47
+ },
48
+ maxItems: 20,
49
+ },
50
+ encrypted_fields: {
51
+ type: "array",
52
+ default: [],
53
+ items: {
54
+ type: "string",
55
+ },
56
+ maxItems: 10,
57
+ },
58
+ single_record: {
59
+ type: "boolean",
60
+ default: false,
61
+ description:
62
+ "If set, only a single records with this schema will be allowed to insert in the database",
63
+ },
64
+ },
65
+ },
66
+ },
67
+ required: ["name","description","schema", "settings"],
68
+ },
69
+ settings: {
70
+ primary_key: ["name"],
71
+ editable_fields: ["schema", "settings"],
72
+ },
73
+ };
74
+
75
+ 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
+ keys: {
98
+ system_generated:true,
99
+ 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
+ name: "system_keys",
101
+ schema: {
102
+ type: "object",
103
+ additionalProperties: true,
104
+ required:["name","value"],
105
+ properties: {
106
+ name: {
107
+ type: "string",
108
+ minLength: 5,
109
+ maxLength: 50,
110
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
111
+ },
112
+ value: {
113
+ type: "string",
114
+ minLength: 5,
115
+ maxLength: 5000,
116
+ pattern: "^[^\n\r]*$",
117
+ description:"Must be a single line string"
118
+ },
119
+ note: {
120
+ type: "string",
121
+ minLength: 1,
122
+ maxLength: 5000,
123
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
124
+ },
125
+ },
126
+ },
127
+ settings: {
128
+ primary_keys: ["name"],
129
+ encrypted_fields:["value"]
130
+ },
131
+ },
132
+ settings: {
133
+ system_generated:true,
134
+ description:"The system relies on these settings for proper functioning or enabling optional features.",
135
+ name: "system_settings",
136
+ schema: {
137
+ required:["name","value"],
138
+ type: "object",
139
+ additionalProperties: true,
140
+ properties: {
141
+ name: {
142
+ type: "string",
143
+ minLength: 5,
144
+ maxLength: 250,
145
+ pattern: "^[a-zA-Z][a-zA-Z0-9_]*$",
146
+ },
147
+ 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",
159
+ },
160
+ },
161
+ },
162
+ settings: {
163
+ primary_keys: ["name"],
164
+ editable_fields: ["value"],
165
+ },
166
+ },
167
+ };
168
+
169
+ // this is not stored in the DB. only for validating the metadata during doc update
170
+ const editable_metadata_schema = {
171
+ additionalProperties: false,
172
+ properties:{
173
+ tags:{
174
+ type:"array",
175
+ items:{type:"string"},
176
+ default:[],
177
+ maxItems: 40,
178
+ }
179
+ }
180
+ }
181
+
182
+ module.exports.system_schemas = system_schemas;
183
+ module.exports.schema_schema = schema_schema;
184
+ module.exports.editable_metadata_schema = editable_metadata_schema;
package/test/helper.js ADDED
@@ -0,0 +1,12 @@
1
+ // helper file for testing
2
+
3
+ async function on_load(db){
4
+ console.log(db.name)
5
+ console.log("loading...done")
6
+ }
7
+
8
+ let print = async (db,msg)=>{
9
+ console.log(msg)
10
+ }
11
+
12
+ module.exports = {on_load,print}
@@ -0,0 +1,219 @@
1
+ // to test initialization of the BeanBagDB class. using in memory pouch db for testing to avoid additional setup.
2
+ const PouchDB = require("pouchdb");
3
+ PouchDB.plugin(require("pouchdb-find"));
4
+ const crypto = require('crypto');
5
+ const db_name = "test_database_24"
6
+ const pdb = new PouchDB(db_name);
7
+ const doc_obj = {
8
+ name: db_name,
9
+ encryption_key: "qwertyuiopaqwsde1254",
10
+ api: {
11
+ insert: async (doc) => {
12
+ const result = await pdb.post(doc);
13
+ return result;
14
+ },
15
+ // delete: ()=>{db1.destroy},
16
+ update: async (doc) => {
17
+ const result = await pdb.put(doc);
18
+ return result;
19
+ },
20
+ search: async (query) => {
21
+ const results = await pdb.find(query);
22
+ return results; // of the form {docs:[],...}
23
+ },
24
+ get: async (id) => {
25
+ const data = await pdb.get(id);
26
+ return data;
27
+ },
28
+ delete: async (id) => {
29
+ const doc = await pdb.get(id);
30
+ const resp = await pdb.remove(doc);
31
+ return resp;
32
+ },
33
+ createIndex: async (filter) => {
34
+ const data = await pdb.createIndex(filter);
35
+ return data;
36
+ },
37
+ },
38
+ utils: {
39
+ encrypt: (text, encryptionKey) => {
40
+ const key = crypto.scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
41
+ const iv = crypto.randomBytes(16); // Initialization vector
42
+ const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
43
+ let encrypted = cipher.update(text, "utf8", "hex");
44
+ encrypted += cipher.final("hex");
45
+ return iv.toString("hex") + ":" + encrypted; // Prepend the IV for later use
46
+ },
47
+ decrypt: (encryptedText, encryptionKey) => {
48
+ const key = crypto.scryptSync(encryptionKey, "salt", 32); // Derive a 256-bit key
49
+ const [iv, encrypted] = encryptedText
50
+ .split(":")
51
+ .map((part) => Buffer.from(part, "hex"));
52
+ const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
53
+ let decrypted = decipher.update(encrypted, "hex", "utf8");
54
+ decrypted += decipher.final("utf8");
55
+ return decrypted;
56
+ },
57
+ ping: () => {
58
+ // @TODO ping the database to check connectivity when class is ready to use
59
+ },
60
+ },
61
+ }
62
+
63
+ the_correct_object = {};
64
+
65
+ const assert = require("assert");
66
+
67
+ const BeanBagDB = require("../src/index");
68
+
69
+ /**
70
+ * Initial setup
71
+ * database is the global var where the beanbag class is initialized
72
+ */
73
+ const test_set1 = [
74
+ ["name", "sample"],
75
+ ["encryption_key", "sample_key"],
76
+ ["utils", {}],
77
+ ["api", {}],
78
+ ];
79
+ const test_obj = { name: "bla", encryption_key: "1234567890opuityerqwas" };
80
+ const full_util = {util: { encrypt: () => {}, decrypt: () => {}, ping: () => {} },}
81
+ const full_api = {api: { update: () => {}, delete: () => {}, get: () => {}, search: () => {}, createIndex: () => {}}}
82
+ const test_set_api = [
83
+ [
84
+ "api.insert missing",
85
+ {
86
+ ...test_obj,
87
+ ...full_util,
88
+ api: {update: () => {}, delete: () => {}, get: () => {}, search: () => {}, createIndex: () => {}},
89
+ },
90
+ ],
91
+ [
92
+ "api.update missing",
93
+ {
94
+ ...test_obj,
95
+ ...full_util,
96
+ api: {insert: () => {}, delete: () => {}, get: () => {},search: () => {},createIndex: () => {}},
97
+ },
98
+ ],
99
+ [
100
+ "api.delete missing",
101
+ {
102
+ ...test_obj,
103
+ ...full_util,
104
+ api: { insert: () => {}, update: () => {}, get: () => {}, search: () => {}, createIndex: () => {}},
105
+ },
106
+ ],
107
+ [
108
+ "api.get missing",
109
+ {
110
+ ...test_obj,
111
+ ...full_util,
112
+ api: { insert: () => {}, update: () => {}, delete: () => {}, search: () => {}, createIndex: () => {}}
113
+ },
114
+ ],
115
+ [
116
+ "api.search missing",
117
+ {
118
+ ...test_obj,
119
+ ...full_util,
120
+ api: { insert: () => {}, update: () => {}, delete: () => {}, get: () => {}, createIndex: () => {}}
121
+ },
122
+ ],
123
+ [
124
+ "api.createIndex missing",
125
+ {
126
+ ...test_obj,
127
+ ...full_util,
128
+ api: { insert: () => {}, update: () => {}, delete: () => {}, get: () => {}, search: () => {}},
129
+ },
130
+ ],
131
+ [
132
+ "util.encrypt missing",
133
+ { ...test_obj, ...full_api, util: { decrypt: () => {}, ping: () => {} } },
134
+ ],
135
+ [
136
+ "util.decrypt missing",
137
+ { ...test_obj, ...full_api, util: { encrypt: () => {}, ping: () => {} } },
138
+ ],
139
+ [
140
+ "util.ping missing",
141
+ {
142
+ ...test_obj,
143
+ ...full_api,
144
+ util: { encrypt: () => {}, decrypt: () => {} },
145
+ },
146
+ ],
147
+ ];
148
+
149
+ let database;
150
+
151
+ describe("Tests initialization of the BeanBagDB class without no init object", async () => {
152
+ it("Throws error", () => {
153
+ assert.throws(() => {
154
+ database = new BeanBagDB();
155
+ }, Error);
156
+ });
157
+ });
158
+
159
+ describe("Tests initialization of the BeanBagDB class with incomplete init object", async () => {
160
+ /*
161
+ * Proper object : {name,encryption_key,api:{insert,updated,delete,search,get,createIndex},utils:{encrypt,decrypt,ping}}
162
+ */
163
+ it("Blank object throw error", () => {
164
+ assert.throws(() => {
165
+ database = new BeanBagDB({});
166
+ }, Error);
167
+ });
168
+ it("some invalid field throws error", () => {
169
+ assert.throws(() => {
170
+ database = new BeanBagDB({ dbname: "sample" });
171
+ }, Error);
172
+ });
173
+ test_set1.forEach((item) => {
174
+ it(`only ${item[0]} throws error`, () => {
175
+ assert.throws(() => {
176
+ let key = item[0];
177
+ database = new BeanBagDB({ key: item[1] });
178
+ }, Error);
179
+ });
180
+ });
181
+ test_set_api.forEach((item) => {
182
+ it(`${item[0]} throws error`, () => {
183
+ assert.throws(() => {
184
+ let obj = { ...item[1] };
185
+ database = new BeanBagDB(obj);
186
+ }, Error);
187
+ });
188
+ });
189
+ });
190
+
191
+ describe("Successful database class init", async () => {
192
+ it("global database variable not yet initialized", () => {
193
+ assert.strictEqual(
194
+ database instanceof BeanBagDB,
195
+ false,
196
+ "The variable is not yet initialized"
197
+ );
198
+ });
199
+
200
+ it("DB init successful", () => {
201
+ database = new BeanBagDB(doc_obj);
202
+ assert.strictEqual(
203
+ database instanceof BeanBagDB,
204
+ true,
205
+ "The variable is initialized successfully"
206
+ );
207
+ });
208
+ });
209
+
210
+ /**
211
+ * process is : create a new obj with all required inputs, run ready() , then initialize_db , then ready to perform operations
212
+ *
213
+ * to test:
214
+ * - no object
215
+ * - incomplete objects and inner objects
216
+ * - good example
217
+ *
218
+ * initialized by ready not called ,
219
+ */
@@ -0,0 +1 @@
1
+ // to test database operations. assuming the class is initialized successfully
package/test/test1.js CHANGED
@@ -1,37 +1,140 @@
1
- require('dotenv').config()
2
- console.log(process.env)
3
- const SDB = require("../index.js")
4
- const cdb = require("nano")(process.env.cdburl)
5
- const doc_obj = {
6
- name: process.env.cdbname,
7
- api:{
8
- insert: async (doc)=>{
9
- const result = await cdb.insert(doc)
10
- return result
11
- },
12
- // delete: ()=>{db1.destroy},
13
- update: async (doc)=>{
14
- const result = await cdb.insert(doc)
15
- return result
16
- },
17
- search: async (query)=>{
18
- const results = await cdb.find(query)
19
- return results
20
- },
21
- get: async (id)=>{
22
- const data = await cdb.get(id)
23
- return data
24
- },
25
- createIndex: async (filter)=>{
26
- const data = await cdb.createIndex(filter)
27
- return data
1
+ require("dotenv").config();
2
+ const cbbdb = require("../src/couchdb.js");
3
+ const pbbdb = require("../src/pouchdb.js");
4
+ const pl1 = require("./helper.js")
5
+ async function main1() {
6
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
7
+
8
+ try {
9
+ await db.ready();
10
+ await db.initialize_db();
11
+ await db.update_indexes()
12
+ } catch (error) {
13
+ console.log(error);
14
+ }
15
+ }
16
+
17
+ async function main2() {
18
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
19
+ await db.ready();
20
+ // await db.update_indexes()
21
+ sample_setting_docs = [
22
+ {
23
+ name: "sample1",
24
+ value: "sample thing here",
25
+ },
26
+ {
27
+ name: "no_value",
28
+ },
29
+ {
30
+ value: "incorrect name",
31
+ },
32
+ {
33
+ name: "sample1",
34
+ value: "sample thing here",
35
+ },
36
+ {
37
+ name: "sample1",
38
+ value: "primary key check",
39
+ },
40
+ {
41
+ name: "Sample2",
42
+ value: "normal insert",
43
+ },
44
+ {
45
+ value: "No name provided",
46
+ },
47
+ ];
48
+ let t = sample_setting_docs.length
49
+ for (let i = 0; i < t; i++) {
50
+ try {
51
+ let newid = await db.insert("system_settings",sample_setting_docs[i])
52
+ console.log(newid)
53
+ } catch (error) {
54
+ console.log("Error "+i)
55
+ console.error(error)
56
+ continue
57
+ }
58
+ }
59
+ }
60
+
61
+ async function main3() {
62
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
63
+ await db.ready();
64
+ // await db.update_indexes()
65
+ sample_setting_docs = [
66
+ {
67
+ name: "sample1",
68
+ value: "sample thing here",
69
+ },
70
+ {
71
+ name: "sample2",
72
+ value: "sample thing here again",
73
+ },
74
+
75
+ {
76
+ name: "sample1",
77
+ value: "sample thing here again",
78
+ },
79
+ ];
80
+ let t = sample_setting_docs.length
81
+ for (let i = 0; i < t; i++) {
82
+ try {
83
+ let newid = await db.insert("system_keys",sample_setting_docs[i])
84
+ console.log(newid)
85
+ } catch (error) {
86
+ console.log("Error "+i)
87
+ console.error(error)
88
+ continue
28
89
  }
29
90
  }
30
91
  }
31
92
 
32
- let db = new SDB.InfoDB(doc_obj)
33
- try {
34
- db.initialize_db()
35
- } catch (error) {
36
- console.log(error)
93
+
94
+ async function main4(){
95
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
96
+ await db.ready();
97
+ let doc1 = await db.get("e94b5eebe6b3c6dab8e2508d5908717c")
98
+ console.log(doc1)
99
+ let doc2 = await db.get_doc("system_keys",{"name":"sample2"})
100
+ console.log(doc2)
101
+ let doc3 = await db.get_doc("system_logs")
102
+ console.log(doc3)
103
+ }
104
+
105
+ async function main5(){
106
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
107
+ await db.ready();
108
+ let id = "e94b5eebe6b3c6dab8e2508d5908717c"
109
+ let rec_1 = await db.get(id)
110
+ let rec1 = rec_1.doc
111
+ console.log(rec1)
112
+ let rec1u = await db.update(id,"",{data:{"value":"secret key updated"},meta:{tags:["testing1","testing2","money"]}})
113
+ console.log(rec1u)
114
+ let r1 = await db.get(id)
115
+ console.log(r1)
116
+ }
117
+
118
+ async function main6(){
119
+ let db = new pbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
120
+ await db.ready()
121
+ //await db.initialize_db()
122
+
37
123
  }
124
+
125
+ // main1().then(() => {console.log("Bye.");}).catch();
126
+
127
+ // main3().then(() => {console.log("Bye.");}).catch();
128
+
129
+ // main4().then(() => {console.log("Bye.");}).catch();
130
+
131
+ // main5().then(() => {console.log("Bye.");}).catch();
132
+
133
+ // main6().then(() => {console.log("Bye.");}).catch();
134
+
135
+
136
+ (async ()=>{
137
+ let db = new cbbdb(process.env.cdburl, process.env.cdbname, "sample_key");
138
+ await db.ready();
139
+ db.load_plugin("sample",pl1)
140
+ })();
package/couchdb.js DELETED
@@ -1,35 +0,0 @@
1
- const SDB = require("./index.js")
2
- class InfoDB_CouchDB extends SDB.InfoDB {
3
- constructor(db_url,db_name){
4
- const cdb = require("nano")(db_url)
5
- const doc_obj = {
6
- name: db_name,
7
- api:{
8
- insert: async (doc)=>{
9
- const result = await cdb.insert(doc)
10
- return result
11
- },
12
- // delete: ()=>{db1.destroy},
13
- update: async (doc)=>{
14
- const result = await cdb.insert(doc)
15
- return result
16
- },
17
- search: async (query)=>{
18
- const results = await cdb.find(query)
19
- return results
20
- },
21
- get: async (id)=>{
22
- const data = await cdb.get(id)
23
- return data
24
- },
25
- createIndex: async (filter)=>{
26
- const data = await cdb.createIndex(filter)
27
- return data
28
- }
29
- }
30
- }
31
- super(doc_obj)
32
- }
33
- }
34
-
35
- module.exports = InfoDB_CouchDB