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

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

上级 f2b3fd88
...@@ -22,7 +22,8 @@ var cmdApi = &Command{ ...@@ -22,7 +22,8 @@ var cmdApi = &Command{
## Options: ## Options:
-X, --method <METHOD> -X, --method <METHOD>
The HTTP method to use for the request (default: "GET"). The method is 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 Use '-XGET' to force serializing fields into the query string for the GET
request instead of JSON body of the POST request. request instead of JSON body of the POST request.
...@@ -47,6 +48,10 @@ var cmdApi = &Command{ ...@@ -47,6 +48,10 @@ var cmdApi = &Command{
strings "true", "false", and "null", as well as strings that look like strings "true", "false", and "null", as well as strings that look like
numbers. 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> -H, --header <KEY-VALUE>
An HTTP request header in 'KEY: VALUE' format. An HTTP request header in 'KEY: VALUE' format.
...@@ -107,7 +112,7 @@ func apiCommand(cmd *Command, args *Args) { ...@@ -107,7 +112,7 @@ func apiCommand(cmd *Command, args *Args) {
method := "GET" method := "GET"
if args.Flag.HasReceived("--method") { if args.Flag.HasReceived("--method") {
method = args.Flag.Value("--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" method = "POST"
} }
cacheTTL := args.Flag.Int("--cache") cacheTTL := args.Flag.Int("--cache")
...@@ -174,8 +179,23 @@ func apiCommand(cmd *Command, args *Args) { ...@@ -174,8 +179,23 @@ func apiCommand(cmd *Command, args *Args) {
path = strings.Replace(path, "{repo}", repo, 1) 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) 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) utils.Check(err)
defer response.Body.Close() defer response.Body.Close()
......
...@@ -193,6 +193,40 @@ Feature: hub api ...@@ -193,6 +193,40 @@ Feature: hub api
.query query {\n repository\n}\n\n .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 Scenario: Pass extra GraphQL variables
Given the GitHub API server: Given the GitHub API server:
""" """
......
...@@ -812,7 +812,7 @@ func (client *Client) FetchMilestones(project *Project) (milestones []Milestone, ...@@ -812,7 +812,7 @@ func (client *Client) FetchMilestones(project *Project) (milestones []Milestone,
return 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() api, err := client.simpleApi()
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -820,33 +820,19 @@ func (client *Client) GenericAPIRequest(method, path string, params map[string]i ...@@ -820,33 +820,19 @@ func (client *Client) GenericAPIRequest(method, path string, params map[string]i
api.CacheTTL = ttl api.CacheTTL = ttl
var body io.Reader var body io.Reader
if len(params) > 0 { switch d := data.(type) {
case map[string]interface{}:
if method == "GET" { if method == "GET" {
query := url.Values{} path = addQuery(path, d)
for key, value := range params { } else if len(d) > 0 {
switch v := value.(type) { json, err := json.Marshal(d)
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)
if err != nil { if err != nil {
return nil, err return nil, err
} }
body = bytes.NewBuffer(json) body = bytes.NewBuffer(json)
} }
case io.Reader:
body = d
} }
return api.performRequest(method, path, body, func(req *http.Request) { return api.performRequest(method, path, body, func(req *http.Request) {
...@@ -1111,3 +1097,29 @@ func perPage(limit, max int) int { ...@@ -1111,3 +1097,29 @@ func perPage(limit, max int) int {
} }
return max 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.
先完成此消息的编辑!
想要评论请 注册