diff options
author | Michael Muré <batolettre@gmail.com> | 2018-09-26 15:55:14 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-09-26 16:26:50 +0200 |
commit | f969370901f9f6d3c71c0391c74ac933d683e784 (patch) | |
tree | 9230635ac72092f753edc06312d9319c2759f88e /vendor/github.com/vektah/gqlgen/client | |
parent | 879e147e2be0b6216a00f8141e71647a0be5b566 (diff) | |
download | git-bug-f969370901f9f6d3c71c0391c74ac933d683e784.tar.gz |
graphql: add a general test for the handler/resolvers
Diffstat (limited to 'vendor/github.com/vektah/gqlgen/client')
-rw-r--r-- | vendor/github.com/vektah/gqlgen/client/client.go | 141 | ||||
-rw-r--r-- | vendor/github.com/vektah/gqlgen/client/readme.md | 5 | ||||
-rw-r--r-- | vendor/github.com/vektah/gqlgen/client/websocket.go | 103 |
3 files changed, 249 insertions, 0 deletions
diff --git a/vendor/github.com/vektah/gqlgen/client/client.go b/vendor/github.com/vektah/gqlgen/client/client.go new file mode 100644 index 00000000..1d482700 --- /dev/null +++ b/vendor/github.com/vektah/gqlgen/client/client.go @@ -0,0 +1,141 @@ +// client is used internally for testing. See readme for alternatives +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "github.com/mitchellh/mapstructure" +) + +// Client for graphql requests +type Client struct { + url string + client *http.Client +} + +// New creates a graphql client +func New(url string, client ...*http.Client) *Client { + p := &Client{ + url: url, + } + + if len(client) > 0 { + p.client = client[0] + } else { + p.client = http.DefaultClient + } + return p +} + +type Request struct { + Query string `json:"query"` + Variables map[string]interface{} `json:"variables,omitempty"` + OperationName string `json:"operationName,omitempty"` +} + +type Option func(r *Request) + +func Var(name string, value interface{}) Option { + return func(r *Request) { + if r.Variables == nil { + r.Variables = map[string]interface{}{} + } + + r.Variables[name] = value + } +} + +func Operation(name string) Option { + return func(r *Request) { + r.OperationName = name + } +} + +func (p *Client) MustPost(query string, response interface{}, options ...Option) { + if err := p.Post(query, response, options...); err != nil { + panic(err) + } +} + +func (p *Client) mkRequest(query string, options ...Option) Request { + r := Request{ + Query: query, + } + + for _, option := range options { + option(&r) + } + + return r +} + +func (p *Client) Post(query string, response interface{}, options ...Option) (resperr error) { + r := p.mkRequest(query, options...) + requestBody, err := json.Marshal(r) + if err != nil { + return fmt.Errorf("encode: %s", err.Error()) + } + + rawResponse, err := p.client.Post(p.url, "application/json", bytes.NewBuffer(requestBody)) + if err != nil { + return fmt.Errorf("post: %s", err.Error()) + } + defer func() { + _ = rawResponse.Body.Close() + }() + + if rawResponse.StatusCode >= http.StatusBadRequest { + responseBody, _ := ioutil.ReadAll(rawResponse.Body) + return fmt.Errorf("http %d: %s", rawResponse.StatusCode, responseBody) + } + + responseBody, err := ioutil.ReadAll(rawResponse.Body) + if err != nil { + return fmt.Errorf("read: %s", err.Error()) + } + + // decode it into map string first, let mapstructure do the final decode + // because it can be much stricter about unknown fields. + respDataRaw := struct { + Data interface{} + Errors json.RawMessage + }{} + err = json.Unmarshal(responseBody, &respDataRaw) + if err != nil { + return fmt.Errorf("decode: %s", err.Error()) + } + + // we want to unpack even if there is an error, so we can see partial responses + unpackErr := unpack(respDataRaw.Data, response) + + if respDataRaw.Errors != nil { + return RawJsonError{respDataRaw.Errors} + } + return unpackErr +} + +type RawJsonError struct { + json.RawMessage +} + +func (r RawJsonError) Error() string { + return string(r.RawMessage) +} + +func unpack(data interface{}, into interface{}) error { + d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + Result: into, + TagName: "json", + ErrorUnused: true, + ZeroFields: true, + }) + if err != nil { + return fmt.Errorf("mapstructure: %s", err.Error()) + } + + return d.Decode(data) +} diff --git a/vendor/github.com/vektah/gqlgen/client/readme.md b/vendor/github.com/vektah/gqlgen/client/readme.md new file mode 100644 index 00000000..755a1433 --- /dev/null +++ b/vendor/github.com/vektah/gqlgen/client/readme.md @@ -0,0 +1,5 @@ +This client is used internally for testing. I wanted a simple graphql client sent user specified queries. + +You might want to look at: + - https://github.com/shurcooL/graphql: Uses reflection to build queries from structs. + - https://github.com/machinebox/graphql: Probably would have been a perfect fit, but it uses form encoding instead of json... diff --git a/vendor/github.com/vektah/gqlgen/client/websocket.go b/vendor/github.com/vektah/gqlgen/client/websocket.go new file mode 100644 index 00000000..bd92e3c0 --- /dev/null +++ b/vendor/github.com/vektah/gqlgen/client/websocket.go @@ -0,0 +1,103 @@ +package client + +import ( + "encoding/json" + "fmt" + "strings" + + "github.com/gorilla/websocket" + "github.com/vektah/gqlparser/gqlerror" +) + +const ( + connectionInitMsg = "connection_init" // Client -> Server + startMsg = "start" // Client -> Server + connectionAckMsg = "connection_ack" // Server -> Client + dataMsg = "data" // Server -> Client + errorMsg = "error" // Server -> Client +) + +type operationMessage struct { + Payload json.RawMessage `json:"payload,omitempty"` + ID string `json:"id,omitempty"` + Type string `json:"type"` +} + +type Subscription struct { + Close func() error + Next func(response interface{}) error +} + +func errorSubscription(err error) *Subscription { + return &Subscription{ + Close: func() error { return nil }, + Next: func(response interface{}) error { + return err + }, + } +} + +func (p *Client) Websocket(query string, options ...Option) *Subscription { + r := p.mkRequest(query, options...) + requestBody, err := json.Marshal(r) + if err != nil { + return errorSubscription(fmt.Errorf("encode: %s", err.Error())) + } + + url := strings.Replace(p.url, "http://", "ws://", -1) + url = strings.Replace(url, "https://", "wss://", -1) + + c, _, err := websocket.DefaultDialer.Dial(url, nil) + if err != nil { + return errorSubscription(fmt.Errorf("dial: %s", err.Error())) + } + + if err = c.WriteJSON(operationMessage{Type: connectionInitMsg}); err != nil { + return errorSubscription(fmt.Errorf("init: %s", err.Error())) + } + + var ack operationMessage + if err = c.ReadJSON(&ack); err != nil { + return errorSubscription(fmt.Errorf("ack: %s", err.Error())) + } + if ack.Type != connectionAckMsg { + return errorSubscription(fmt.Errorf("expected ack message, got %#v", ack)) + } + + if err = c.WriteJSON(operationMessage{Type: startMsg, ID: "1", Payload: requestBody}); err != nil { + return errorSubscription(fmt.Errorf("start: %s", err.Error())) + } + + return &Subscription{ + Close: c.Close, + Next: func(response interface{}) error { + var op operationMessage + c.ReadJSON(&op) + if op.Type != dataMsg { + if op.Type == errorMsg { + return fmt.Errorf(string(op.Payload)) + } else { + return fmt.Errorf("expected data message, got %#v", op) + } + } + + respDataRaw := map[string]interface{}{} + err = json.Unmarshal(op.Payload, &respDataRaw) + if err != nil { + return fmt.Errorf("decode: %s", err.Error()) + } + + if respDataRaw["errors"] != nil { + var errs []*gqlerror.Error + if err = unpack(respDataRaw["errors"], &errs); err != nil { + return err + } + if len(errs) > 0 { + return fmt.Errorf("errors: %s", errs) + } + } + + return unpack(respDataRaw["data"], response) + }, + } +} |