|
@@ -15,6 +15,7 @@ import (
|
|
"fmt"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http"
|
|
|
|
|
|
|
|
+ "devt.de/krotik/common/lang/graphql/parser"
|
|
"devt.de/krotik/common/stringutil"
|
|
"devt.de/krotik/common/stringutil"
|
|
"devt.de/krotik/eliasdb/api"
|
|
"devt.de/krotik/eliasdb/api"
|
|
"devt.de/krotik/eliasdb/graphql"
|
|
"devt.de/krotik/eliasdb/graphql"
|
|
@@ -43,6 +44,8 @@ type graphQLEndpoint struct {
|
|
HandlePOST handles GraphQL queries.
|
|
HandlePOST handles GraphQL queries.
|
|
*/
|
|
*/
|
|
func (e *graphQLEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, resources []string) {
|
|
func (e *graphQLEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, resources []string) {
|
|
|
|
+ var err error
|
|
|
|
+ var res map[string]interface{}
|
|
|
|
|
|
dec := json.NewDecoder(r.Body)
|
|
dec := json.NewDecoder(r.Body)
|
|
data := make(map[string]interface{})
|
|
data := make(map[string]interface{})
|
|
@@ -52,28 +55,81 @@ func (e *graphQLEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, res
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- partData, ok := data["partition"]
|
|
|
|
- if !ok && len(resources) > 0 {
|
|
|
|
- partData = resources[0]
|
|
|
|
- ok = true
|
|
|
|
- }
|
|
|
|
- if !ok || partData == "" {
|
|
|
|
- http.Error(w, "Need a partition", http.StatusBadRequest)
|
|
|
|
|
|
+ toAST, ok1 := data["query-to-ast"]
|
|
|
|
+ toQuery, ok2 := data["ast-to-query"]
|
|
|
|
+ if ok1 || ok2 {
|
|
|
|
+
|
|
|
|
+ res := make(map[string]interface{})
|
|
|
|
+
|
|
|
|
+ if ok1 {
|
|
|
|
+ resast, err := parser.Parse("request", fmt.Sprint(toAST))
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res["result-ast"] = resast.Plain()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ok2 {
|
|
|
|
+ astmap, ok := toQuery.(map[string]interface{})
|
|
|
|
+
|
|
|
|
+ if !ok {
|
|
|
|
+ http.Error(w, "Plain AST object expected as 'ast-to-query' value", http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Try to create a proper AST from plain AST
|
|
|
|
+
|
|
|
|
+ astnode, err := parser.ASTFromPlain(astmap)
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Now pretty print the AST
|
|
|
|
+
|
|
|
|
+ ppres, err := parser.PrettyPrint(astnode)
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ http.Error(w, err.Error(), http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res["result-query"] = ppres
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ w.Header().Set("content-type", "application/json; charset=utf-8")
|
|
|
|
+ json.NewEncoder(w).Encode(res)
|
|
|
|
+
|
|
return
|
|
return
|
|
- }
|
|
|
|
|
|
|
|
- part := fmt.Sprint(partData)
|
|
|
|
|
|
+ } else {
|
|
|
|
+ partData, ok := data["partition"]
|
|
|
|
+ if !ok && len(resources) > 0 {
|
|
|
|
+ partData = resources[0]
|
|
|
|
+ ok = true
|
|
|
|
+ }
|
|
|
|
+ if !ok || partData == "" {
|
|
|
|
+ http.Error(w, "Need a partition", http.StatusBadRequest)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
|
|
- if _, ok := data["variables"]; !ok {
|
|
|
|
- data["variables"] = nil
|
|
|
|
- }
|
|
|
|
|
|
+ part := fmt.Sprint(partData)
|
|
|
|
|
|
- if _, ok := data["operationName"]; !ok {
|
|
|
|
- data["operationName"] = nil
|
|
|
|
- }
|
|
|
|
|
|
+ if _, ok := data["variables"]; !ok {
|
|
|
|
+ data["variables"] = nil
|
|
|
|
+ }
|
|
|
|
|
|
- res, err := graphql.RunQuery(stringutil.CreateDisplayString(part)+" query",
|
|
|
|
- part, data, api.GM, nil, false)
|
|
|
|
|
|
+ if _, ok := data["operationName"]; !ok {
|
|
|
|
+ data["operationName"] = nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res, err = graphql.RunQuery(stringutil.CreateDisplayString(part)+" query",
|
|
|
|
+ part, data, api.GM, nil, false)
|
|
|
|
+ }
|
|
|
|
|
|
if err != nil {
|
|
if err != nil {
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
http.Error(w, err.Error(), http.StatusBadRequest)
|
|
@@ -151,4 +207,63 @@ func (e *graphQLEndpoint) SwaggerDefs(s map[string]interface{}) {
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ s["paths"].(map[string]interface{})["/v1/graphql"] = map[string]interface{}{
|
|
|
|
+ "post": map[string]interface{}{
|
|
|
|
+ "summary": "GraphQL parser and pretty printer endpoint.",
|
|
|
|
+ "description": "The GraphQL endpoint without specifying a partition should be used to parse a given GraphQL query into an Abstract Syntax Tree or pretty print a given Abstract Syntax Tree into a GraphQL query.",
|
|
|
|
+ "consumes": []string{
|
|
|
|
+ "application/json",
|
|
|
|
+ },
|
|
|
|
+ "produces": []string{
|
|
|
|
+ "text/plain",
|
|
|
|
+ "application/json",
|
|
|
|
+ },
|
|
|
|
+ "parameters": []map[string]interface{}{
|
|
|
|
+ {
|
|
|
|
+ "name": "data",
|
|
|
|
+ "in": "body",
|
|
|
|
+ "description": "Query or AST which should be converted.",
|
|
|
|
+ "required": true,
|
|
|
|
+ "schema": map[string]interface{}{
|
|
|
|
+ "type": "object",
|
|
|
|
+ "properties": map[string]interface{}{
|
|
|
|
+ "query-to-ast": map[string]interface{}{
|
|
|
|
+ "description": "Query which should be parsed.",
|
|
|
|
+ "type": "string",
|
|
|
|
+ },
|
|
|
|
+ "ast-to-query": map[string]interface{}{
|
|
|
|
+ "description": "AST which should be pretty printed.",
|
|
|
|
+ "type": "object",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ "responses": map[string]interface{}{
|
|
|
|
+ "200": map[string]interface{}{
|
|
|
|
+ "description": "The operation was successful.",
|
|
|
|
+ "schema": map[string]interface{}{
|
|
|
|
+ "type": "object",
|
|
|
|
+ "properties": map[string]interface{}{
|
|
|
|
+ "result-ast": map[string]interface{}{
|
|
|
|
+ "description": "The resulting AST if a query was parsed.",
|
|
|
|
+ "type": "object",
|
|
|
|
+ },
|
|
|
|
+ "result-query": map[string]interface{}{
|
|
|
|
+ "description": "The pretty printed query if an AST was given.",
|
|
|
|
+ "type": "string",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ "default": map[string]interface{}{
|
|
|
|
+ "description": "Error response",
|
|
|
|
+ "schema": map[string]interface{}{
|
|
|
|
+ "$ref": "#/definitions/Error",
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ }
|
|
}
|
|
}
|