提交 46165a37 编写于 作者: M Mislav Marohnić

[api] Allow passing in a raw request body via `--input <FILE>`

上级 f2b3fd88
......@@ -22,7 +22,8 @@ var cmdApi = &Command{
## Options:
-X, --method <METHOD>
The HTTP method to use for the request (default: "GET"). The method is
automatically set to "POST" if '--field' or '--raw-field' are used.
automatically set to "POST" if '--field', '--raw-field', or '--input' are
used.
Use '-XGET' to force serializing fields into the query string for the GET
request instead of JSON body of the POST request.
......@@ -47,6 +48,10 @@ var cmdApi = &Command{
strings "true", "false", and "null", as well as strings that look like
numbers.
--input <FILE>
The filename to read the raw request body from. Use "-" to read from standard
input. Use this when you want to manually construct the request payload.
-H, --header <KEY-VALUE>
An HTTP request header in 'KEY: VALUE' format.
......@@ -107,7 +112,7 @@ func apiCommand(cmd *Command, args *Args) {
method := "GET"
if args.Flag.HasReceived("--method") {
method = args.Flag.Value("--method")
} else if args.Flag.HasReceived("--field") || args.Flag.HasReceived("--raw-field") {
} else if args.Flag.HasReceived("--field") || args.Flag.HasReceived("--raw-field") || args.Flag.HasReceived("--input") {
method = "POST"
}
cacheTTL := args.Flag.Int("--cache")
......@@ -174,8 +179,23 @@ func apiCommand(cmd *Command, args *Args) {
path = strings.Replace(path, "{repo}", repo, 1)
}
var body interface{}
if args.Flag.HasReceived("--input") {
fn := args.Flag.Value("--input")
if fn == "-" {
body = os.Stdin
} else {
fi, err := os.Open(fn)
utils.Check(err)
body = fi
defer fi.Close()
}
} else {
body = params
}
gh := github.NewClient(host)
response, err := gh.GenericAPIRequest(method, path, params, headers, cacheTTL)
response, err := gh.GenericAPIRequest(method, path, body, headers, cacheTTL)
utils.Check(err)
defer response.Body.Close()
......
......@@ -193,6 +193,40 @@ Feature: hub api
.query query {\n repository\n}\n\n
"""
Scenario: POST body from file
Given the GitHub API server:
"""
post('/create') {
params[:obj].inspect
}
"""
Given a file named "payload.json" with:
"""
{"obj": ["one", 2, null]}
"""
When I successfully run `hub api create --input payload.json`
Then the output should contain exactly:
"""
["one", 2, nil]
"""
Scenario: POST body from stdin
Given the GitHub API server:
"""
post('/create') {
params[:obj].inspect
}
"""
When I run `hub api create --input -` interactively
And I pass in:
"""
{"obj": {"name": "Ein", "datadog": true}}
"""
Then the output should contain exactly:
"""
{"name"=>"Ein", "datadog"=>true}
"""
Scenario: Pass extra GraphQL variables
Given the GitHub API server:
"""
......
......@@ -812,7 +812,7 @@ func (client *Client) FetchMilestones(project *Project) (milestones []Milestone,
return
}
func (client *Client) GenericAPIRequest(method, path string, params map[string]interface{}, headers map[string]string, ttl int) (*simpleResponse, error) {
func (client *Client) GenericAPIRequest(method, path string, data interface{}, headers map[string]string, ttl int) (*simpleResponse, error) {
api, err := client.simpleApi()
if err != nil {
return nil, err
......@@ -820,33 +820,19 @@ func (client *Client) GenericAPIRequest(method, path string, params map[string]i
api.CacheTTL = ttl
var body io.Reader
if len(params) > 0 {
switch d := data.(type) {
case map[string]interface{}:
if method == "GET" {
query := url.Values{}
for key, value := range params {
switch v := value.(type) {
case string:
query.Add(key, v)
case nil:
query.Add(key, "")
case int:
query.Add(key, fmt.Sprintf("%d", v))
case bool:
query.Add(key, fmt.Sprintf("%v", v))
}
}
sep := "?"
if strings.Contains(path, sep) {
sep = "&"
}
path += sep + query.Encode()
} else {
json, err := json.Marshal(params)
path = addQuery(path, d)
} else if len(d) > 0 {
json, err := json.Marshal(d)
if err != nil {
return nil, err
}
body = bytes.NewBuffer(json)
}
case io.Reader:
body = d
}
return api.performRequest(method, path, body, func(req *http.Request) {
......@@ -1111,3 +1097,29 @@ func perPage(limit, max int) int {
}
return max
}
func addQuery(path string, params map[string]interface{}) string {
if len(params) == 0 {
return path
}
query := url.Values{}
for key, value := range params {
switch v := value.(type) {
case string:
query.Add(key, v)
case nil:
query.Add(key, "")
case int:
query.Add(key, fmt.Sprintf("%d", v))
case bool:
query.Add(key, fmt.Sprintf("%v", v))
}
}
sep := "?"
if strings.Contains(path, sep) {
sep = "&"
}
return path + sep + query.Encode()
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册