Browse Source

fix: Allow mass deleteion via GraphQL

Matthias Ladkau 3 years ago
parent
commit
1adf80fdcc

+ 23 - 1
graphql.md

@@ -96,7 +96,29 @@ mutation {
   }
 }
 ```
-Possible arguments are `storeNode, storeEdge, removeNode and removeEdge`. Removal of nodes and edges requires only the `key` and `kind` to be specified. The operation allows retrieval of nodes as well (i.e. the single operation will insert *AND* retrieve data).
+Possible arguments are `storeNode, storeEdge, removeNode and removeEdge`. The operation allows retrieval of nodes as well (i.e. the single operation will insert *AND* retrieve data). Removal of edges requires only the `key` and `kind` to be specified. Removal of nodes requires only the `kind` to be specified. Using `removeNodes` with a missing `key` will remove all nodes of the kind.
+
+
+Variables
+---------
+To avoid parsing issues and possible security risks it is advisable to always use variables to pass data to EliasDB especially if it is a user-provided value. EliasDB supports all default GraphQL default types: string, integer, float
+```
+mutation($name: string) {
+    Person(storeNode: {
+        key: "hans"
+        name: $name
+    }) {
+        key
+        name
+    }
+}
+```
+The type name (in the example `string`) is not evaluated in EliasDB's GraphQL interpreter. The values should be send in a separate variables datastructure:
+```
+{
+  name: "Hans"
+}
+```
 
 Subscription to updates
 -----------------------

+ 2 - 2
graphql/interpreter/introspection.go

@@ -204,7 +204,7 @@ func (rt *selectionSetRuntime) GetFieldTypesIntrospection(action string, lookupA
 			map[string]interface{}{
 				"name":         "removeNode",
 				"defaultValue": nil,
-				"description":  "Remove a node according to this template.",
+				"description":  "Remove a node according to this template (only kind is needed).",
 				"type": map[string]interface{}{
 					"kind":   "OBJECT",
 					"name":   "NodeTemplate",
@@ -214,7 +214,7 @@ func (rt *selectionSetRuntime) GetFieldTypesIntrospection(action string, lookupA
 			map[string]interface{}{
 				"name":         "storeEdge",
 				"defaultValue": nil,
-				"description":  "Store an edge according to this template (only key and kind are needed).",
+				"description":  "Store an edge according to this template.",
 				"type": map[string]interface{}{
 					"kind":   "OBJECT",
 					"name":   "NodeTemplate",

+ 24 - 1
graphql/interpreter/selectionset.go

@@ -389,7 +389,30 @@ func (rt *selectionSetRuntime) handleMutationArgs(path []string, args map[string
 			if node.Kind() == "" {
 				node.SetAttr("kind", kind)
 			}
-			_, err = rt.rtp.gm.RemoveNode(rt.rtp.part, node.Key(), node.Kind())
+
+			if node.Key() == "" {
+				var it *graph.NodeKeyIterator
+
+				if it, err = rt.rtp.gm.NodeKeyIterator(rt.rtp.part, node.Kind()); err == nil {
+					var keys []string
+
+					for it.HasNext() && err == nil {
+						keys = append(keys, it.Next())
+						err = it.Error()
+					}
+
+					if err == nil {
+						for _, key := range keys {
+							if err == nil {
+								_, err = rt.rtp.gm.RemoveNode(rt.rtp.part, key, node.Kind())
+							}
+						}
+					}
+				}
+
+			} else {
+				_, err = rt.rtp.gm.RemoveNode(rt.rtp.part, node.Key(), node.Kind())
+			}
 
 		} else {
 

+ 23 - 0
graphql/interpreter/selectionset_test.go

@@ -693,6 +693,29 @@ mutation {
 		"operationName": nil,
 		"query": `
 mutation {
+  Song(removeNode : {}) {
+    key
+    kind
+  }
+}
+`,
+		"variables": nil,
+	}
+
+	if rerr := checkResult(`
+{
+  "data": {
+    "Song": []
+  }
+}`[1:], query, gm); rerr != nil {
+		t.Error(rerr)
+		return
+	}
+
+	query = map[string]interface{}{
+		"operationName": nil,
+		"query": `
+mutation {
   Song(storeNode : "Hans", storeEdge : "Franz", key : "Honk") {
     key,
     name,