2019-03-24 14:57:29 +01:00
|
|
|
package serv
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
2019-04-01 14:55:46 +02:00
|
|
|
"time"
|
2019-03-24 14:57:29 +01:00
|
|
|
|
|
|
|
"github.com/gorilla/websocket"
|
2019-06-06 14:57:00 +02:00
|
|
|
"github.com/rs/zerolog/log"
|
2019-03-24 14:57:29 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2019-04-19 07:55:03 +02:00
|
|
|
maxReadBytes = 100000 // 100Kb
|
2019-03-24 14:57:29 +01:00
|
|
|
introspectionQuery = "IntrospectionQuery"
|
|
|
|
openVar = "{{"
|
|
|
|
closeVar = "}}"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2019-04-19 07:55:03 +02:00
|
|
|
upgrader = websocket.Upgrader{}
|
|
|
|
errNoUserID = errors.New("no user_id available")
|
|
|
|
errUnauthorized = errors.New("not authorized")
|
2019-03-24 14:57:29 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type gqlReq struct {
|
2019-04-19 07:55:03 +02:00
|
|
|
OpName string `json:"operationName"`
|
|
|
|
Query string `json:"query"`
|
|
|
|
Vars variables `json:"variables"`
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
2019-04-19 07:55:03 +02:00
|
|
|
type variables map[string]interface{}
|
|
|
|
|
2019-03-24 14:57:29 +01:00
|
|
|
type gqlResp struct {
|
2019-04-01 14:55:46 +02:00
|
|
|
Error string `json:"error,omitempty"`
|
2019-04-19 07:55:03 +02:00
|
|
|
Data json.RawMessage `json:"data"`
|
2019-04-04 06:53:24 +02:00
|
|
|
Extensions *extensions `json:"extensions,omitempty"`
|
2019-04-01 14:55:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type extensions struct {
|
2019-04-04 06:53:24 +02:00
|
|
|
Tracing *trace `json:"tracing,omitempty"`
|
2019-04-01 14:55:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type trace struct {
|
|
|
|
Version int `json:"version"`
|
|
|
|
StartTime time.Time `json:"startTime"`
|
|
|
|
EndTime time.Time `json:"endTime"`
|
|
|
|
Duration time.Duration `json:"duration"`
|
|
|
|
Execution execution `json:"execution"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type execution struct {
|
|
|
|
Resolvers []resolver `json:"resolvers"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type resolver struct {
|
|
|
|
Path []string `json:"path"`
|
|
|
|
ParentType string `json:"parentType"`
|
|
|
|
FieldName string `json:"fieldName"`
|
|
|
|
ReturnType string `json:"returnType"`
|
|
|
|
StartOffset int `json:"startOffset"`
|
|
|
|
Duration time.Duration `json:"duration"`
|
2019-03-24 14:57:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func apiv1Http(w http.ResponseWriter, r *http.Request) {
|
2019-05-13 01:27:26 +02:00
|
|
|
ctx := &coreContext{Context: r.Context()}
|
2019-03-24 14:57:29 +01:00
|
|
|
|
|
|
|
if authFailBlock == authFailBlockAlways && authCheck(ctx) == false {
|
|
|
|
http.Error(w, "Not authorized", 401)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-04-19 07:55:03 +02:00
|
|
|
b, err := ioutil.ReadAll(io.LimitReader(r.Body, maxReadBytes))
|
2019-03-24 14:57:29 +01:00
|
|
|
defer r.Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
errorResp(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
if err := json.Unmarshal(b, &ctx.req); err != nil {
|
2019-03-24 14:57:29 +01:00
|
|
|
errorResp(w, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
if strings.EqualFold(ctx.req.OpName, introspectionQuery) {
|
2019-06-05 02:00:24 +02:00
|
|
|
// dat, err := ioutil.ReadFile("test.schema")
|
|
|
|
// if err != nil {
|
|
|
|
// http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//w.Write(dat)
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
w.Write([]byte(`{
|
|
|
|
"data": {
|
|
|
|
"__schema": {
|
|
|
|
"queryType": {
|
|
|
|
"name": "Query"
|
|
|
|
},
|
|
|
|
"mutationType": null,
|
|
|
|
"subscriptionType": null
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"extensions":{
|
|
|
|
"tracing":{
|
|
|
|
"version":1,
|
|
|
|
"startTime":"2019-06-04T19:53:31.093Z",
|
|
|
|
"endTime":"2019-06-04T19:53:31.108Z",
|
|
|
|
"duration":15219720,
|
|
|
|
"execution": {
|
|
|
|
"resolvers": [{
|
|
|
|
"path": ["__schema"],
|
|
|
|
"parentType": "Query",
|
|
|
|
"fieldName": "__schema",
|
|
|
|
"returnType": "__Schema!",
|
|
|
|
"startOffset": 50950,
|
|
|
|
"duration": 17187
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`))
|
2019-03-24 14:57:29 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-05-13 01:27:26 +02:00
|
|
|
err = ctx.handleReq(w, r)
|
2019-03-24 14:57:29 +01:00
|
|
|
|
2019-04-19 07:55:03 +02:00
|
|
|
if err == errUnauthorized {
|
2019-03-24 14:57:29 +01:00
|
|
|
http.Error(w, "Not authorized", 401)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
errorResp(w, err)
|
|
|
|
}
|
2019-04-01 14:55:46 +02:00
|
|
|
}
|
2019-05-13 01:27:26 +02:00
|
|
|
|
|
|
|
func errorResp(w http.ResponseWriter, err error) {
|
|
|
|
if conf.DebugLevel > 0 {
|
2019-06-06 14:57:00 +02:00
|
|
|
log.Error().Err(err)
|
2019-05-13 01:27:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
|
|
json.NewEncoder(w).Encode(gqlResp{Error: err.Error()})
|
|
|
|
}
|