Skip to content

Commit

Permalink
feat: Merging in 1.4.0 changes
Browse files Browse the repository at this point in the history
  • Loading branch information
krotik committed Aug 14, 2022
1 parent cdf4d97 commit 88a1da6
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 20 deletions.
149 changes: 132 additions & 17 deletions api/v1/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"fmt"
"net/http"

"github.com/krotik/common/lang/graphql/parser"
"github.com/krotik/common/stringutil"
"github.com/krotik/eliasdb/api"
"github.com/krotik/eliasdb/graphql"
Expand Down Expand Up @@ -43,6 +44,8 @@ type graphQLEndpoint struct {
HandlePOST handles GraphQL queries.
*/
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)
data := make(map[string]interface{})
Expand All @@ -52,28 +55,81 @@ func (e *graphQLEndpoint) HandlePOST(w http.ResponseWriter, r *http.Request, res
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
}

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 {
http.Error(w, err.Error(), http.StatusBadRequest)
Expand Down Expand Up @@ -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",
},
},
},
},
}
}
140 changes: 140 additions & 0 deletions api/v1/graphql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,143 @@ func TestGraphQLErrors(t *testing.T) {
return
}
}

func TestGraphQLParsing(t *testing.T) {
queryURL := "http://localhost" + TESTPORT + EndpointGraphQL

q, err := json.Marshal(map[string]interface{}{
"query-to-ast": `{
Song
}`,
})
errorutil.AssertOk(err)
_, _, res := sendTestRequest(queryURL+"main", "POST", q)

if res != `
{
"result-ast": {
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"name": "Name",
"value": "Song"
}
],
"name": "Field"
}
],
"name": "SelectionSet"
}
],
"name": "OperationDefinition"
}
],
"name": "ExecutableDefinition"
}
],
"name": "Document"
}
}`[1:] {
t.Error("Unexpected response:", res)
return
}

_, _, res = sendTestRequest(queryURL+"main", "POST", []byte(`{"ast-to-query": {
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"children": [
{
"name": "Name",
"value": "Song"
}
],
"name": "Field"
}
],
"name": "SelectionSet"
}
],
"name": "OperationDefinition"
}
],
"name": "ExecutableDefinition"
}
],
"name": "Document"
}}`))

if res != `
{
"result-query": "{\n Song\n}"
}`[1:] {
t.Error("Unexpected response:", res)
return
}
}

func TestGraphQLParsingErrors(t *testing.T) {
queryURL := "http://localhost" + TESTPORT + EndpointGraphQL

q, err := json.Marshal(map[string]interface{}{
"query-to-ast": `{{
Song
}`,
})
errorutil.AssertOk(err)
_, _, res := sendTestRequest(queryURL+"main", "POST", q)

if res != "Parse error in request: Name expected ({) (Line:1 Pos:2)" {
t.Error("Unexpected response:", res)
return
}

q, err = json.Marshal(map[string]interface{}{
"ast-to-query": `aaa`,
})
errorutil.AssertOk(err)
_, _, res = sendTestRequest(queryURL+"main", "POST", q)

if res != "Plain AST object expected as 'ast-to-query' value" {
t.Error("Unexpected response:", res)
return
}

q, err = json.Marshal(map[string]interface{}{
"ast-to-query": map[string]interface{}{
"foo": `Document`,
},
})
errorutil.AssertOk(err)
_, _, res = sendTestRequest(queryURL+"main", "POST", q)

if res != "Found plain ast node without a name: map[foo:Document]" {
t.Error("Unexpected response:", res)
return
}

q, err = json.Marshal(map[string]interface{}{
"ast-to-query": map[string]interface{}{
"name": `foo`,
},
})
errorutil.AssertOk(err)
_, _, res = sendTestRequest(queryURL+"main", "POST", q)

if res != "Could not find template for foo (tempkey: foo)" {
t.Error("Unexpected response:", res)
return
}
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ go 1.12

require (
github.com/gorilla/websocket v1.4.1
github.com/krotik/common v1.4.6
github.com/krotik/common v1.5.1
github.com/krotik/ecal v1.6.3
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/krotik/common v1.4.4/go.mod h1:Ti5yTPm8lyOwgllpNNc0bFutiZ3nRu49QbSQCbjEaB0=
github.com/krotik/common v1.4.6 h1:0ZEofm4oS0370JeXCm+XteqTpS8BIFetUkYr1eTQDG8=
github.com/krotik/common v1.4.6/go.mod h1:Ti5yTPm8lyOwgllpNNc0bFutiZ3nRu49QbSQCbjEaB0=
github.com/krotik/common v1.5.1 h1:WkX2ewJjm4gma1wg34tDvaR4PM9FhIgfD1mvoHGoK4M=
github.com/krotik/common v1.5.1/go.mod h1:Ti5yTPm8lyOwgllpNNc0bFutiZ3nRu49QbSQCbjEaB0=
github.com/krotik/ecal v1.6.3 h1:HKJPB6Y3uCEcVNpSZsm2Jfo8RRgfBBS8HR69yMBZJ20=
github.com/krotik/ecal v1.6.3/go.mod h1:ULSgiGqiCxGtJKicRQKW8Unt6CeeCSYQ4i7fDdRFf3Q=

0 comments on commit 88a1da6

Please sign in to comment.