-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhttp_post.go
124 lines (99 loc) · 3.02 KB
/
http_post.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
package batching
import (
"fmt"
"io"
"net/http"
"strings"
"sync"
"github.com/99designs/gqlgen/graphql"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/vektah/gqlparser/v2/gqlerror"
)
const HeaderBatch = "X-Batch"
// POST implements the POST side of the default HTTP transport
// defined in https://github.com/APIs-guru/graphql-over-http#post
type POST struct {
transport.POST
// Map of all headers that are added to graphql response. If not
// set, only one header: Content-Type: application/json will be set.
ResponseHeaders map[string][]string
}
var _ graphql.Transport = POST{}
func getRequestBody(r *http.Request) (string, error) {
if r == nil || r.Body == nil {
return "", nil
}
body, err := io.ReadAll(r.Body)
if err != nil {
return "", fmt.Errorf("unable to get Request Body %w", err)
}
defer r.Body.Close()
return string(body), nil
}
func (h POST) Do(w http.ResponseWriter, r *http.Request, exec graphql.GraphExecutor) {
var paramsCollection GraphqlRawParamsCollection
start := graphql.Now()
ctx := r.Context()
writeHeaders(w, h.ResponseHeaders)
if batching := r.Header.Get(HeaderBatch); batching == "true" {
bodyString, err := getRequestBody(r)
if err != nil {
gqlErr := gqlerror.Errorf("could not get json request body: %+v", err)
resp := exec.DispatchError(graphql.WithOperationContext(ctx, &graphql.OperationContext{}), gqlerror.List{gqlErr})
writeJson(w, resp)
return
}
bodyReader := io.NopCloser(strings.NewReader(bodyString))
if err = jsonDecode(bodyReader, ¶msCollection); err != nil {
w.WriteHeader(http.StatusBadRequest)
gqlErr := gqlerror.Errorf(
"json request body could not be decoded: %+v body:%s",
err,
bodyString,
)
resp := exec.DispatchError(graphql.WithOperationContext(ctx, &graphql.OperationContext{}), gqlerror.List{gqlErr})
writeJson(w, resp)
return
}
// try to parse raw query (not json)
if len(paramsCollection) == 1 {
rootParam := paramsCollection[0]
q := rootParam.Query
if IsBatchingRawQuery(q) {
collection := SplitQuery(q)
for _, op := range collection {
op.Variables = rootParam.Variables
op.Headers = rootParam.Headers
op.Extensions = rootParam.Extensions
}
paramsCollection = collection
}
}
wg := sync.WaitGroup{}
responses := make([]*graphql.Response, len(paramsCollection))
for idx, op := range paramsCollection {
op.Headers = r.Header
op.ReadTime = graphql.TraceTiming{
Start: start,
End: graphql.Now(),
}
wg.Add(1)
go func(idx int, params *graphql.RawParams) {
defer wg.Done()
rc, OpErr := exec.CreateOperationContext(ctx, params)
if OpErr != nil {
w.WriteHeader(statusFor(OpErr))
responses[idx] = exec.DispatchError(graphql.WithOperationContext(ctx, rc), OpErr)
return
}
responseHandler, ctx := exec.DispatchOperation(ctx, rc)
responses[idx] = responseHandler(ctx)
}(idx, op)
}
wg.Wait()
writeJson(w, responses)
return
}
h.POST.ResponseHeaders = h.ResponseHeaders
h.POST.Do(w, r, exec)
}