-
Notifications
You must be signed in to change notification settings - Fork 0
/
graphql.go
132 lines (113 loc) · 3.25 KB
/
graphql.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package graphql
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/go-logr/logr"
"golang.org/x/net/context/ctxhttp"
)
// Client is a client for interacting with a GraphQL API.
type Client struct {
url string
httpClient *http.Client
logger *logr.Logger
}
// NewClient makes a new Client capable of making GraphQL requests.
func NewClient(url string, httpClient *http.Client, logger *logr.Logger) *Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
return &Client{
url: url,
httpClient: httpClient,
logger: logger,
}
}
func (c *Client) RawRequest(ctx context.Context, query string, variables map[string]interface{}) (*GraphQLResponse, error) {
requestData := struct {
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
}{
Query: query,
Variables: variables,
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
err := encoder.Encode(requestData)
if err != nil {
return nil, err
}
logger := c.logger
logger.Info("[POST] graphql request", "url", c.url)
logger.V(1).Info("data sent in request", "data", fmt.Sprintf("%q", buf.Bytes()))
responseContent, err := ctxhttp.Post(ctx, c.httpClient, c.url, "application/json", &buf)
var errBody []byte
if err != nil {
if responseContent != nil {
errBody, _ = ioutil.ReadAll(responseContent.Body)
logger.Error(err, "error returned from graphql request", "status", responseContent.Status)
logger.V(1).Info("response data received", "body", string(errBody))
return &GraphQLResponse{
StatusCode: responseContent.StatusCode,
}, err
} else {
logger.Error(err, "Error in graphql request")
logger.V(1).Info("Request", "url", c.url, "data", buf.String())
return nil, err
}
}
defer responseContent.Body.Close()
var body []byte
if errBody != nil {
body = errBody
} else {
body, _ = ioutil.ReadAll(responseContent.Body)
}
if responseContent.StatusCode != http.StatusOK {
err := fmt.Errorf("%v", responseContent.Status)
logger.Error(err, "graphql returned a non-200 response", "status code", responseContent.StatusCode)
logger.V(1).Info("response data received", "body", string(body))
return &GraphQLResponse{
StatusCode: responseContent.StatusCode,
ResponseContent: body,
}, err
}
result, err := decodeGraphQLResponse(body)
if err != nil {
logger.Error(err, "error while decoding graphql response")
logger.V(1).Info("json data", "data", string(body))
return nil, err
}
result.StatusCode = responseContent.StatusCode
result.ResponseContent = body
logger.Info("success", "status code", responseContent.StatusCode)
logger.V(1).Info("response content", "content", string(body))
return result, nil
}
func decodeGraphQLResponse(data []byte) (*GraphQLResponse, error) {
reader := bytes.NewReader(data)
decoder := json.NewDecoder(reader)
resp := &GraphQLResponse{}
err := decoder.Decode(resp)
if err != nil {
return nil, err
}
return resp, nil
}
type GraphQLResponse struct {
StatusCode int
ResponseContent []byte
Data *json.RawMessage
Errors GraphQLError
Extensions *json.RawMessage
}
type GraphQLError []struct {
Message string
Locations []struct {
Line int
Column int
}
}