149 lines
3.3 KiB
Go
149 lines
3.3 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/cli/cli/v2/internal/ghinstance"
|
|
)
|
|
|
|
func httpRequest(client *http.Client, hostname string, method string, p string, params interface{}, headers []string) (*http.Response, error) {
|
|
isGraphQL := p == "graphql"
|
|
var requestURL string
|
|
if strings.Contains(p, "://") {
|
|
requestURL = p
|
|
} else if isGraphQL {
|
|
requestURL = ghinstance.GraphQLEndpoint(hostname)
|
|
} else {
|
|
requestURL = ghinstance.RESTPrefix(hostname) + strings.TrimPrefix(p, "/")
|
|
}
|
|
|
|
var body io.Reader
|
|
var bodyIsJSON bool
|
|
|
|
switch pp := params.(type) {
|
|
case map[string]interface{}:
|
|
if strings.EqualFold(method, "GET") {
|
|
requestURL = addQuery(requestURL, pp)
|
|
} else {
|
|
if isGraphQL {
|
|
pp = groupGraphQLVariables(pp)
|
|
}
|
|
b, err := json.Marshal(pp)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error serializing parameters: %w", err)
|
|
}
|
|
body = bytes.NewBuffer(b)
|
|
bodyIsJSON = true
|
|
}
|
|
case io.Reader:
|
|
body = pp
|
|
case nil:
|
|
body = nil
|
|
default:
|
|
return nil, fmt.Errorf("unrecognized parameters type: %v", params)
|
|
}
|
|
|
|
req, err := http.NewRequest(strings.ToUpper(method), requestURL, body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, h := range headers {
|
|
idx := strings.IndexRune(h, ':')
|
|
if idx == -1 {
|
|
return nil, fmt.Errorf("header %q requires a value separated by ':'", h)
|
|
}
|
|
name, value := h[0:idx], strings.TrimSpace(h[idx+1:])
|
|
if strings.EqualFold(name, "Content-Length") {
|
|
length, err := strconv.ParseInt(value, 10, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.ContentLength = length
|
|
} else {
|
|
req.Header.Add(name, value)
|
|
}
|
|
}
|
|
if bodyIsJSON && req.Header.Get("Content-Type") == "" {
|
|
req.Header.Set("Content-Type", "application/json; charset=utf-8")
|
|
}
|
|
if req.Header.Get("Accept") == "" {
|
|
req.Header.Set("Accept", "*/*")
|
|
}
|
|
|
|
return client.Do(req)
|
|
}
|
|
|
|
func groupGraphQLVariables(params map[string]interface{}) map[string]interface{} {
|
|
topLevel := make(map[string]interface{})
|
|
variables := make(map[string]interface{})
|
|
|
|
for key, val := range params {
|
|
switch key {
|
|
case "query", "operationName":
|
|
topLevel[key] = val
|
|
default:
|
|
variables[key] = val
|
|
}
|
|
}
|
|
|
|
if len(variables) > 0 {
|
|
topLevel["variables"] = variables
|
|
}
|
|
return topLevel
|
|
}
|
|
|
|
func addQuery(path string, params map[string]interface{}) string {
|
|
if len(params) == 0 {
|
|
return path
|
|
}
|
|
|
|
query := url.Values{}
|
|
if err := addQueryParam(query, "", params); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
sep := "?"
|
|
if strings.ContainsRune(path, '?') {
|
|
sep = "&"
|
|
}
|
|
return path + sep + query.Encode()
|
|
}
|
|
|
|
func addQueryParam(query url.Values, key string, value interface{}) error {
|
|
switch v := value.(type) {
|
|
case string:
|
|
query.Add(key, v)
|
|
case []byte:
|
|
query.Add(key, string(v))
|
|
case nil:
|
|
query.Add(key, "")
|
|
case int:
|
|
query.Add(key, fmt.Sprintf("%d", v))
|
|
case bool:
|
|
query.Add(key, fmt.Sprintf("%v", v))
|
|
case map[string]interface{}:
|
|
for subkey, value := range v {
|
|
// support for nested subkeys can be added here if that is ever necessary
|
|
if err := addQueryParam(query, subkey, value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
case []interface{}:
|
|
for _, entry := range v {
|
|
if err := addQueryParam(query, key+"[]", entry); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
default:
|
|
return fmt.Errorf("unknown type %v", v)
|
|
}
|
|
return nil
|
|
}
|