123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*
- * EliasDB
- *
- * Copyright 2016 Matthias Ladkau. All rights reserved.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
- package v1
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "devt.de/krotik/common/lang/graphql/parser"
- "devt.de/krotik/common/stringutil"
- "devt.de/krotik/eliasdb/api"
- "devt.de/krotik/eliasdb/graphql"
- )
- /*
- EndpointGraphQL is the GraphQL endpoint URL (rooted). Handles everything under graphql/...
- */
- const EndpointGraphQL = api.APIRoot + APIv1 + "/graphql/"
- /*
- GraphQLEndpointInst creates a new endpoint handler.
- */
- func GraphQLEndpointInst() api.RestEndpointHandler {
- return &graphQLEndpoint{}
- }
- /*
- Handler object for GraphQL operations.
- */
- type graphQLEndpoint struct {
- *api.DefaultEndpointHandler
- }
- /*
- 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{})
- if err := dec.Decode(&data); err != nil {
- http.Error(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
- return
- }
- 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
- } 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
- }
- part := fmt.Sprint(partData)
- if _, ok := data["variables"]; !ok {
- data["variables"] = nil
- }
- 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)
- return
- }
- w.Header().Set("content-type", "application/json; charset=utf-8")
- json.NewEncoder(w).Encode(res)
- }
- /*
- SwaggerDefs is used to describe the endpoint in swagger.
- */
- func (e *graphQLEndpoint) SwaggerDefs(s map[string]interface{}) {
- graphqlRequestParam := map[string]interface{}{
- "name": "graphql_request",
- "in": "body",
- "description": "GraphQL request",
- "required": true,
- "schema": map[string]interface{}{
- "$ref": "#/definitions/GraphQLRequest",
- },
- }
- s["paths"].(map[string]interface{})["/v1/graphql/{partition}"] = map[string]interface{}{
- "post": map[string]interface{}{
- "summary": "GraphQL interface.",
- "description": "The GraphQL interface can be used to query and modify data.",
- "consumes": []string{
- "application/json",
- },
- "produces": []string{
- "text/plain",
- "application/json",
- },
- "parameters": []map[string]interface{}{
- {
- "name": "partition",
- "in": "path",
- "description": "Partition to query.",
- "required": true,
- "type": "string",
- },
- graphqlRequestParam,
- },
- "responses": map[string]interface{}{
- "200": map[string]interface{}{
- "description": "The operation was successful.",
- },
- "default": map[string]interface{}{
- "description": "Error response",
- "schema": map[string]interface{}{
- "$ref": "#/definitions/Error",
- },
- },
- },
- },
- }
- s["definitions"].(map[string]interface{})["GraphQLRequest"] = map[string]interface{}{
- "type": "object",
- "properties": map[string]interface{}{
- "operationName": map[string]interface{}{
- "description": "GraphQL query operation name.",
- "type": "string",
- },
- "query": map[string]interface{}{
- "description": "GraphQL query.",
- "type": "string",
- },
- "variables": map[string]interface{}{
- "description": "GraphQL query variable values.",
- "type": "object",
- },
- },
- }
- 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",
- },
- },
- },
- },
- }
- }
|