beanbagdb 0.5.53 → 0.5.60
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/doc-src/1_getting-started.md +64 -1
- package/doc-src/3_plugins.md +23 -1
- package/package.json +1 -1
- package/src/index.js +166 -3
- package/src/plugins/text_command.js +193 -0
- package/src/system_schema.js +112 -43
- package/test/edges.test.js +313 -0
- package/test/operations.test.js +2 -2
- package/test/plugin.test.js +52 -0
- package/test/test1.js +146 -127
|
@@ -1 +1,64 @@
|
|
|
1
|
-
# Getting started
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
### Nodes and edges
|
|
6
|
+
|
|
7
|
+
The records stored in BeanBagDB can be organized into a simple directed graph. In in simple directed graph, edges have a direction and there can only be one edge between 2 nodes. Edges can have label based on the context of your data. You can also define rules for these edges based on the schema of the nodes.
|
|
8
|
+
|
|
9
|
+
Example : Consider you have 3 types of nodes : "player", "team", "match" to simulate a game where a teams has multiple players and a player plays a match as part of a team.
|
|
10
|
+
Here are the rules:
|
|
11
|
+
- A player is part of a team (and not vice versa)
|
|
12
|
+
- A team plays a match (and not vice versa)
|
|
13
|
+
- A player cannot be in 2 teams
|
|
14
|
+
- A match can only be between 2 teams (not more than 2)
|
|
15
|
+
- Player does not play a match (but a team does, assuming this is a team sport)
|
|
16
|
+
- A team cannot be part of a team
|
|
17
|
+
- A team cannot have more than 11 players
|
|
18
|
+
|
|
19
|
+
In terms of nodes and edges, the rules translate to:
|
|
20
|
+
|
|
21
|
+
| rule | node 1 | edge label | node 2 | constraint |
|
|
22
|
+
|-------------------------------------------------------|--------|-------------|--------|---------------------------------|
|
|
23
|
+
| 1 A player is part of a team (and not vice versa) | player | is part of | team | player-->team |
|
|
24
|
+
| 2 A team plays a match (and not vice versa) | team | plays | match | |
|
|
25
|
+
| 3 A player cannot be in 2 teams | player | is part of | team | only one such relation |
|
|
26
|
+
| 4 A match can only be between 2 teams (not more than 2) | team | plays | match | only 2 such relation per match |
|
|
27
|
+
| 5 Player does not play a match | player | plays | match | not valid |
|
|
28
|
+
| 6 A team cannot be part of a team | team | is part of | team | not valid |
|
|
29
|
+
| 7 A team cannot have more than 11 players | player | is part of | team | only 11 such relations per team |
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
These rules can be enforced in the database as "edge_constraints" and are automatically checked when new edges are added to the graph.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
- Constraint 1 :
|
|
36
|
+
- node 1 : player
|
|
37
|
+
- edge type : part_of
|
|
38
|
+
- edge label : is part of a
|
|
39
|
+
- node 2 : team
|
|
40
|
+
- max_from_node1 : 1
|
|
41
|
+
- max_to_node2 : 11
|
|
42
|
+
- Constraint 2 :
|
|
43
|
+
- node 1 : team
|
|
44
|
+
- edge type : plays
|
|
45
|
+
- edge label : plays
|
|
46
|
+
- node 2 : match
|
|
47
|
+
- max_from_node1 : None
|
|
48
|
+
- max_to_node2 : 2
|
|
49
|
+
```
|
|
50
|
+
- `max_from_node1` defines the maximum number of edges of a certain type that can exist from node 1 to node 2. For example, player1 is part of team1; now, player1 cannot be part of any other team.
|
|
51
|
+
- `max_to_node2` defines the maximum number of edges of a certain type that can be directed towards a particular node. For example, team1 and team2 can only play match1.
|
|
52
|
+
- Violations of these results in an error and the edge is not created
|
|
53
|
+
- for both node1 and node2, the general approach is to whitelist a set of schemas for the specified type of edge. It is still valid to add other types of edges from these nodes to others.
|
|
54
|
+
- The schema names in node1 and node2 fixes the schema types for this particular edge type. This means creating a relations "player1--(part_of)-->team1" or "team1--(part_of)-->player1" will result in the same relation "player1--(part_of)-->team1".
|
|
55
|
+
- You can also specify nodes with different schema types as : "player,coach".
|
|
56
|
+
- If you want to allow nodes of all schema types, use "*."
|
|
57
|
+
- Eg :
|
|
58
|
+
- Constraint:
|
|
59
|
+
- node 1: *
|
|
60
|
+
- edge label: interacts with
|
|
61
|
+
- node 2: *
|
|
62
|
+
- If you want to include all except for a few types of nodes, use : "* - schema1, schema2"
|
|
63
|
+
- Eg : "*-tag tagged_as tag" means that nodes of all types (except for tag nodes themselves) can be tagged with a tag.
|
|
64
|
+
|
package/doc-src/3_plugins.md
CHANGED
|
@@ -1 +1,23 @@
|
|
|
1
|
-
# How to use plugins
|
|
1
|
+
# How to use plugins
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
// MyPlugin.js
|
|
5
|
+
const MyPlugin = {
|
|
6
|
+
// This function will be bound to the class instance (passed as 'thisArg')
|
|
7
|
+
on_load: async function(instance) {
|
|
8
|
+
console.log("Plugin loaded with instance:", instance);
|
|
9
|
+
// You can access instance properties and methods here
|
|
10
|
+
instance.someMethod();
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Another plugin function
|
|
14
|
+
doSomething: function(instance, arg) {
|
|
15
|
+
console.log("Plugin doing something with arg:", arg);
|
|
16
|
+
// Access the class instance here as 'instance'
|
|
17
|
+
instance.someMethod();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default MyPlugin;
|
|
22
|
+
|
|
23
|
+
```
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -344,6 +344,7 @@ export class BeanBagDB {
|
|
|
344
344
|
async create(schema, data, meta = {}, settings = {}) {
|
|
345
345
|
this._check_ready_to_use();
|
|
346
346
|
if(!schema){throw new DocCreationError(`No schema provided`)}
|
|
347
|
+
if(schema=="setting_edge"){throw new DocCreationError("This type of record can only be created through the create_edge api")}
|
|
347
348
|
if(Object.keys(data).length==0){throw new DocCreationError(`No data provided`)}
|
|
348
349
|
try {
|
|
349
350
|
let doc_obj = await this._insert_pre_checks(schema, data,meta, settings);
|
|
@@ -440,7 +441,6 @@ export class BeanBagDB {
|
|
|
440
441
|
* - Retrieves the document based on the provided search criteria.
|
|
441
442
|
* - Checks the revision ID to detect potential conflicts. (To be implemented: behavior when the `rev_id` does not match).
|
|
442
443
|
* - Validates editable fields against `schema.settings.editable_fields` (or allows editing of all fields if not specified).
|
|
443
|
-
* - Performs primary key conflict checks if multiple records are allowed (`single_record == false`).
|
|
444
444
|
* - Encrypts fields if encryption is required by the schema settings.
|
|
445
445
|
* - Updates the `meta` fields (such as `updated_on` and `updated_by`) and saves the updated document to the database.
|
|
446
446
|
*
|
|
@@ -448,7 +448,7 @@ export class BeanBagDB {
|
|
|
448
448
|
* @returns {Object} The result of the document update operation.
|
|
449
449
|
*
|
|
450
450
|
* **Errors**:
|
|
451
|
-
* - Throws an error if a document with the same primary keys already exists
|
|
451
|
+
* - Throws an error if a document with the same primary keys already exists .
|
|
452
452
|
* - Throws a `DocUpdateError` if a primary key conflict is detected during the update.
|
|
453
453
|
*
|
|
454
454
|
* @throws {DocUpdateError} - If a document with conflicting primary keys already exists.
|
|
@@ -548,7 +548,7 @@ export class BeanBagDB {
|
|
|
548
548
|
async delete(criteria) {
|
|
549
549
|
this._check_ready_to_use();
|
|
550
550
|
let doc = await this.read(criteria)
|
|
551
|
-
const delete_blocked = ["schema","setting",""]
|
|
551
|
+
const delete_blocked = ["schema","setting","key"]
|
|
552
552
|
if (delete_blocked.includes(doc.schema)){
|
|
553
553
|
throw new Error(`Deletion of ${doc.schema} doc is not support yet.`)
|
|
554
554
|
}
|
|
@@ -597,6 +597,7 @@ export class BeanBagDB {
|
|
|
597
597
|
async get(special_doc_type,criteria={}){
|
|
598
598
|
// this method returns special types of documents such as schema doc, or a blank doc for a given schema and other system related things
|
|
599
599
|
const fetch_docs = {
|
|
600
|
+
// to return schema object for the given name
|
|
600
601
|
schema:async (criteria)=>{
|
|
601
602
|
let schemaSearch = await this.db_api.search({
|
|
602
603
|
selector: { schema: "schema", "data.name": criteria.name },
|
|
@@ -606,6 +607,30 @@ export class BeanBagDB {
|
|
|
606
607
|
throw new DocNotFoundError(BeanBagDB.error_codes.schema_not_found);
|
|
607
608
|
}
|
|
608
609
|
return schemaSearch.docs[0];
|
|
610
|
+
},
|
|
611
|
+
// schema list
|
|
612
|
+
schema_list:async (criteria)=>{
|
|
613
|
+
let schemaSearch = await this.db_api.search({
|
|
614
|
+
selector: { schema: "schema" },
|
|
615
|
+
});
|
|
616
|
+
// console.log(schemaSearch)
|
|
617
|
+
if (schemaSearch.docs.length == 0) {
|
|
618
|
+
throw new DocNotFoundError(BeanBagDB.error_codes.schema_not_found);
|
|
619
|
+
}else{
|
|
620
|
+
let schemas = []
|
|
621
|
+
schemaSearch.docs.map(doc=>{
|
|
622
|
+
schemas.push({
|
|
623
|
+
name: doc.data.name,
|
|
624
|
+
version: doc.data.version,
|
|
625
|
+
system_defined : doc.data.system_generated,
|
|
626
|
+
description: doc.data.description,
|
|
627
|
+
link: doc.meta.link,
|
|
628
|
+
_id:doc._id
|
|
629
|
+
})
|
|
630
|
+
})
|
|
631
|
+
return schemas
|
|
632
|
+
}
|
|
633
|
+
|
|
609
634
|
}
|
|
610
635
|
}
|
|
611
636
|
if(Object.keys(fetch_docs).includes(special_doc_type)){
|
|
@@ -630,6 +655,119 @@ export class BeanBagDB {
|
|
|
630
655
|
}
|
|
631
656
|
}
|
|
632
657
|
|
|
658
|
+
///////////////////////////////////////////////////////////
|
|
659
|
+
//////////////// simple directed graph ////////////////////////
|
|
660
|
+
//////////////////////////////////////////////////////////
|
|
661
|
+
|
|
662
|
+
async create_edge(node1,node2,edge_name,edge_label=""){
|
|
663
|
+
this._check_ready_to_use();
|
|
664
|
+
if(!edge_name){throw new ValidationError("edge_name required")}
|
|
665
|
+
if(Object.keys(node1)==0){throw new ValidationError("node1 required")}
|
|
666
|
+
if(Object.keys(node2)==0){throw new ValidationError("node2 required")}
|
|
667
|
+
|
|
668
|
+
let n1 = await this.read(node1)
|
|
669
|
+
let n2 = await this.read(node2)
|
|
670
|
+
let edges_constraint
|
|
671
|
+
|
|
672
|
+
try {
|
|
673
|
+
let d = await this.read({schema:"setting_edge_constraint",data:{name:edge_name}})
|
|
674
|
+
edges_constraint = d["doc"]["data"]
|
|
675
|
+
let errors = []
|
|
676
|
+
let node1id = n1.doc._id
|
|
677
|
+
let node2id = n2.doc._id
|
|
678
|
+
let val_check = this._check_nodes_edge(edges_constraint.node1,edges_constraint.node2,n1.doc.schema,n2.doc.schema)
|
|
679
|
+
|
|
680
|
+
if (val_check.valid){
|
|
681
|
+
if(val_check.swapped){
|
|
682
|
+
// swapping required
|
|
683
|
+
node1id = n2.doc._id
|
|
684
|
+
node2id = n1.doc._id
|
|
685
|
+
}
|
|
686
|
+
}else{
|
|
687
|
+
this.errors.push("Invalid nodes.This config of nodes not allowed")
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
let records = await this.search({selector:{schema:"system_edge","data.edge_name":edge_name}})
|
|
691
|
+
|
|
692
|
+
if(edges_constraint.max_from_node1!=-1){
|
|
693
|
+
let filter1 = records.docs.filter((itm)=>itm.data.node1==node1id)
|
|
694
|
+
if(filter1.length>=edges_constraint.max_from_node1){
|
|
695
|
+
errors.push("max limit reached")
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
if(edges_constraint.max_to_node2!=-1){
|
|
700
|
+
let filter2 = records.docs.filter((itm)=>itm.data.node2==node2id)
|
|
701
|
+
if(filter1.length>=edges_constraint.max_from_node1){
|
|
702
|
+
errors.push("max limit reached")
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
if(errors.length==0){
|
|
707
|
+
let edge = await this.create("system_edge",{node1: node1id , node2: node1id ,edge_name:edge_name })
|
|
708
|
+
return edge
|
|
709
|
+
}else{
|
|
710
|
+
throw new RelationError(errors)
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
} catch (error) {
|
|
714
|
+
if(error instanceof DocNotFoundError){
|
|
715
|
+
let doc = {node1:"*",node2:"*",name:edge_name,label:edge_label}
|
|
716
|
+
let new_doc = await this.create("system_edge_constraint",doc)
|
|
717
|
+
let edge = await this.create("system_edge",{node1: n1.doc._id,node2: n2.doc._id,edge_name:edge_name })
|
|
718
|
+
return edge
|
|
719
|
+
}else{
|
|
720
|
+
throw error
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
_check_node_schema_match(rule, schema) {
|
|
726
|
+
/**
|
|
727
|
+
* Check if the schema matches the rule. The rule can be:
|
|
728
|
+
* - "*" for any schema
|
|
729
|
+
* - "*-n1,n2" for all schemas except n1 and n2
|
|
730
|
+
* - "specific_schema" or "schema1,schema2" for specific schema matches
|
|
731
|
+
*/
|
|
732
|
+
if (rule === "*") {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (rule.startsWith("*-")) {
|
|
737
|
+
// Exclude the schemas listed after "*-"
|
|
738
|
+
const exclusions = rule.slice(2).split(",");
|
|
739
|
+
return !exclusions.includes(schema);
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Otherwise, check if schema matches the specific rule (comma-separated for multiple allowed schemas)
|
|
743
|
+
const allowedSchemas = rule.split(",");
|
|
744
|
+
return allowedSchemas.includes(schema);
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
_check_nodes_edge(node1Rule, node2Rule, schema1, schema2) {
|
|
748
|
+
/**
|
|
749
|
+
* Check if the edge between schema1 (node1) and schema2 (node2) is valid based on the rules
|
|
750
|
+
* node1Rule and node2Rule. Also checks if the nodes should be swapped.
|
|
751
|
+
*
|
|
752
|
+
*/
|
|
753
|
+
// Check if schema1 matches node1Rule and if schema2 matches node2Rule
|
|
754
|
+
const matchesNode1 = this._check_node_schema_match(node1Rule, schema1);
|
|
755
|
+
const matchesNode2 = this._check_node_schema_match(node2Rule, schema2);
|
|
756
|
+
|
|
757
|
+
// Check if schema1 matches node2Rule and schema2 matches node1Rule (for swapping condition)
|
|
758
|
+
const matchesSwappedNode1 = this._check_node_schema_match(node2Rule, schema1);
|
|
759
|
+
const matchesSwappedNode2 = this._check_node_schema_match(node1Rule, schema2);
|
|
760
|
+
|
|
761
|
+
// If the schemas match their respective rules (node1 and node2), the edge is valid
|
|
762
|
+
if (matchesNode1 && matchesNode2) { return { valid: true, swapped: false }}
|
|
763
|
+
|
|
764
|
+
// If swapping makes it valid, indicate that the nodes should be swapped
|
|
765
|
+
if (matchesSwappedNode1 && matchesSwappedNode2) { return { valid: true, swapped: true }}
|
|
766
|
+
// Otherwise, the edge is invalid
|
|
767
|
+
return { valid: false, swapped: false };
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
|
|
633
771
|
///////////////////////////////////////////////////////////
|
|
634
772
|
//////////////// Internal methods ////////////////////////
|
|
635
773
|
//////////////////////////////////////////////////////////
|
|
@@ -1145,4 +1283,29 @@ constructor(errors=[]){
|
|
|
1145
1283
|
this.name = "EncryptionError";
|
|
1146
1284
|
this.errors = errors
|
|
1147
1285
|
}
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Custom error class for relation error.
|
|
1290
|
+
*
|
|
1291
|
+
* @extends {Error}
|
|
1292
|
+
*/
|
|
1293
|
+
export class RelationError extends Error {
|
|
1294
|
+
/**
|
|
1295
|
+
*
|
|
1296
|
+
* @extends {Error}
|
|
1297
|
+
* @param {ErrorItem[]} [errors=[]] - An array of error objects, each containing details about validation failures.
|
|
1298
|
+
*/
|
|
1299
|
+
constructor(errors=[]){
|
|
1300
|
+
let error_messages
|
|
1301
|
+
if(Array.isArray(errors)){
|
|
1302
|
+
error_messages = errors.map(item=>` ${(item.instancePath||" ").replace("/","")} ${item.message} `)
|
|
1303
|
+
}else {
|
|
1304
|
+
error_messages = [errors]
|
|
1305
|
+
}
|
|
1306
|
+
let message = `Error in relation of the simple digraph : ${error_messages.join(",")}`
|
|
1307
|
+
super(message)
|
|
1308
|
+
this.name = "RelationError";
|
|
1309
|
+
this.errors = errors
|
|
1310
|
+
}
|
|
1148
1311
|
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const commands = {
|
|
5
|
+
new: {
|
|
6
|
+
parse: async (instance,parts) => {
|
|
7
|
+
let criteria = {}
|
|
8
|
+
criteria.schema = parts.length==0?"":parts.join("")
|
|
9
|
+
return criteria
|
|
10
|
+
},
|
|
11
|
+
run: async (instance,command) => {
|
|
12
|
+
if (command.criteria.schema==""){
|
|
13
|
+
// return a list of all schemas present in the DB
|
|
14
|
+
let all_schema = await instance.get("schema_list")
|
|
15
|
+
return all_schema
|
|
16
|
+
}else{
|
|
17
|
+
// return the schema object for the given schema if not found throw error
|
|
18
|
+
let schema_obj = await instance.search({"selector":{"schema":"schema","data.name":command.criteria.schema}})
|
|
19
|
+
//console.log(schema_obj)
|
|
20
|
+
if(schema_obj.docs.length==0){
|
|
21
|
+
throw new Error("Schema with this name does not exists")
|
|
22
|
+
}
|
|
23
|
+
return schema_obj.docs[0]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
help: `To create a new record. Format : new/"schema_name(optional)". If no schema name provided, a list of valid schema name is returned`
|
|
27
|
+
},
|
|
28
|
+
open:{
|
|
29
|
+
parse: async (instance,parts) => {
|
|
30
|
+
let criteria = {}
|
|
31
|
+
if (parts.length==0){
|
|
32
|
+
throw new Error("Invalid arguments.open command needs unique id")
|
|
33
|
+
}
|
|
34
|
+
let id_type = parts[0]
|
|
35
|
+
if(id_type=="id"){
|
|
36
|
+
parts.shift()
|
|
37
|
+
criteria["_id"] = parts.join("")
|
|
38
|
+
}else if(id_type=="link"){
|
|
39
|
+
parts.shift()
|
|
40
|
+
criteria["link"] = parts.join("")
|
|
41
|
+
}else if(id_type=="key"){
|
|
42
|
+
parts.shift()
|
|
43
|
+
let text= parts.join()
|
|
44
|
+
let p = text.split(",")
|
|
45
|
+
|
|
46
|
+
p.map(itm=>{
|
|
47
|
+
let p1 = itm.split("=")
|
|
48
|
+
if(p1[0]=="schema"){
|
|
49
|
+
criteria["schema"] = p1[1]
|
|
50
|
+
}else{
|
|
51
|
+
criteria["data"][p1[0]] = p1[1]
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if(!criteria["schema"]){
|
|
56
|
+
throw new Error("Key requires a schema")
|
|
57
|
+
}
|
|
58
|
+
}else{
|
|
59
|
+
throw new Error("Invalid unique key")
|
|
60
|
+
}
|
|
61
|
+
return criteria
|
|
62
|
+
},
|
|
63
|
+
run: async (instance,command) => {
|
|
64
|
+
try {
|
|
65
|
+
let data = await instance.read(command.criteria,true)
|
|
66
|
+
return data
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw error
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
help: `To open a record using it's unique id. Format : open/"id||link|key"/"value". In case of key field name must be provided as : field1=value1,fields2=value2...`
|
|
72
|
+
},
|
|
73
|
+
tool:{
|
|
74
|
+
parse : async (instance,parts)=>{
|
|
75
|
+
let criteria = {}
|
|
76
|
+
criteria.type = parts.length==0?"info":parts.join("")
|
|
77
|
+
return criteria
|
|
78
|
+
},
|
|
79
|
+
run : async (instance,command)=>{
|
|
80
|
+
let c_type = command.criteria.type
|
|
81
|
+
let data = {}
|
|
82
|
+
if (c_type=="info"){
|
|
83
|
+
// to get all basic info about the database
|
|
84
|
+
let data = {
|
|
85
|
+
meta: instance.metadata(),
|
|
86
|
+
schemas : {},
|
|
87
|
+
logs:[]
|
|
88
|
+
}
|
|
89
|
+
let schemas = await instance.get("schema_list")
|
|
90
|
+
data.schemas = schemas
|
|
91
|
+
|
|
92
|
+
let logs_doc = await instance.read({"schema":"system_settings","data":{name:"system_logs"}})
|
|
93
|
+
//console.log(logs_doc)
|
|
94
|
+
data =logs_doc.doc.data.value
|
|
95
|
+
return data
|
|
96
|
+
|
|
97
|
+
}else if(c_type=="plugins"){
|
|
98
|
+
// to show list of all plugins installed
|
|
99
|
+
// todo later not implemented yet
|
|
100
|
+
}else if(c_type=="settings"){
|
|
101
|
+
// to show the list of all setting docs available
|
|
102
|
+
let search = instance.search({"selector":{"schema":"system_settings"}})
|
|
103
|
+
return {docs:search.docs}
|
|
104
|
+
}
|
|
105
|
+
else if(c_type=="keys"){
|
|
106
|
+
// to show the list of all keys present in the db
|
|
107
|
+
let search = instance.search({"selector":{"schema":"system_keys"}})
|
|
108
|
+
return {docs:search.docs}
|
|
109
|
+
}
|
|
110
|
+
else{
|
|
111
|
+
throw new Error("Invalid tool command")
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const parse = async (instance, text) => {
|
|
118
|
+
let data = {
|
|
119
|
+
errors: [],
|
|
120
|
+
valid: false,
|
|
121
|
+
name: "",
|
|
122
|
+
criteria: {},
|
|
123
|
+
};
|
|
124
|
+
if (!text) {
|
|
125
|
+
data.errors.push(
|
|
126
|
+
"No text command provided. Format : command_name/parameter"
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
let parts = text.split("/");
|
|
130
|
+
if (parts.length == 0) {
|
|
131
|
+
data.errors.push("Invalid text command");
|
|
132
|
+
}
|
|
133
|
+
let command_name = parts[0];
|
|
134
|
+
if (!commands[command_name]) {
|
|
135
|
+
data.errors.push(
|
|
136
|
+
"Invalid command name. Valid : " + Object.keys(commands).join(",")
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
data.name = command_name;
|
|
140
|
+
try {
|
|
141
|
+
parts.shift();
|
|
142
|
+
let criteria = await commands[command_name].parse(instance,parts);
|
|
143
|
+
data.criteria = criteria;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
data.errors.push(error.message);
|
|
146
|
+
}
|
|
147
|
+
if (data.errors.length == 0) {
|
|
148
|
+
data.valid = true;
|
|
149
|
+
}
|
|
150
|
+
return data;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const run = async (instance, command) => {
|
|
154
|
+
let data = {
|
|
155
|
+
result:{},
|
|
156
|
+
errors:[],
|
|
157
|
+
valid:false
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
if (!command) {
|
|
161
|
+
data.errors.push("No command object provided ");
|
|
162
|
+
}
|
|
163
|
+
if (!command.valid){
|
|
164
|
+
data.errors["Command cannot be run"]
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
if(!commands[command.name]){
|
|
168
|
+
data.errors["Invalid command name"]
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
let data1 = await commands[command.name].run(instance,command)
|
|
173
|
+
//console.log(data)
|
|
174
|
+
data.result = data1
|
|
175
|
+
} catch (error) {
|
|
176
|
+
data.errors.push(error.message)
|
|
177
|
+
}
|
|
178
|
+
if(data.errors.length==0){
|
|
179
|
+
data.valid = true
|
|
180
|
+
}
|
|
181
|
+
return data
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const parse_and_run = async(instance, text) => {
|
|
185
|
+
let command = await parse(instance,text)
|
|
186
|
+
console.log(command)
|
|
187
|
+
let command_result = await run(instance,command)
|
|
188
|
+
return command_result
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export const text_command = {
|
|
192
|
+
parse,run,parse_and_run
|
|
193
|
+
};
|