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

[api] Implement GraphQL pagination

The GraphQL query has to accept the optional `endCursor` string variable
and output `pageInfo`:

    pageInfo {
      hasNextPage
      endCursor
    }
上级 424acfa8
package commands
import (
"bytes"
"fmt"
"io"
"io/ioutil"
......@@ -189,7 +190,8 @@ func apiCommand(cmd *Command, args *Args) {
host = defHost.Host
}
if path == "graphql" && params["query"] != nil {
isGraphQL := path == "graphql"
if isGraphQL && params["query"] != nil {
query := params["query"].(string)
query = strings.Replace(query, quote("{owner}"), quote(owner), 1)
query = strings.Replace(query, quote("{repo}"), quote(repo), 1)
......@@ -253,8 +255,15 @@ func apiCommand(cmd *Command, args *Args) {
fmt.Fprintf(out, "\r\n")
}
endCursor := ""
hasNextPage := false
if parseJSON && jsonType {
utils.JSONPath(out, response.Body, colorize)
hasNextPage, endCursor = utils.JSONPath(out, response.Body, colorize)
} else if paginate && isGraphQL {
bodyCopy := &bytes.Buffer{}
io.Copy(out, io.TeeReader(response.Body, bodyCopy))
hasNextPage, endCursor = utils.JSONPath(ioutil.Discard, bodyCopy, false)
} else {
io.Copy(out, response.Body)
}
......@@ -266,7 +275,16 @@ func apiCommand(cmd *Command, args *Args) {
requestLoop = false
if paginate {
if nextLink := response.Link("next"); nextLink != "" {
if isGraphQL && hasNextPage && endCursor != "" {
if v, ok := params["variables"]; ok {
variables := v.(map[string]interface{})
variables["endCursor"] = endCursor
} else {
variables := map[string]interface{}{"endCursor": endCursor}
params["variables"] = variables
}
requestLoop = true
} else if nextLink := response.Link("next"); nextLink != "" {
path = nextLink
requestLoop = true
}
......
......@@ -127,6 +127,28 @@ Feature: hub api
[{"page":3}]
"""
Scenario: Paginate GraphQL
Given the GitHub API server:
"""
post('/graphql') {
variables = params[:variables] || {}
page = (variables["endCursor"] || 1).to_i
json :data => {
:pageInfo => {
:hasNextPage => page < 3,
:endCursor => (page+1).to_s
}
}
}
"""
When I successfully run `hub api --paginate graphql -f query=QUERY`
Then the output should contain exactly:
"""
{"data":{"pageInfo":{"hasNextPage":true,"endCursor":"2"}}}
{"data":{"pageInfo":{"hasNextPage":true,"endCursor":"3"}}}
{"data":{"pageInfo":{"hasNextPage":false,"endCursor":"4"}}}
"""
Scenario: Avoid leaking token to a 3rd party
Given the GitHub API server:
"""
......
......@@ -29,10 +29,7 @@ func stateKey(s *state) string {
}
}
func printValue(token json.Token) {
}
func JSONPath(out io.Writer, src io.Reader, colorize bool) {
func JSONPath(out io.Writer, src io.Reader, colorize bool) (hasNextPage bool, endCursor string) {
dec := json.NewDecoder(src)
dec.UseNumber()
......@@ -84,12 +81,18 @@ func JSONPath(out io.Writer, src io.Reader, colorize bool) {
switch tt := token.(type) {
case string:
fmt.Fprintf(out, "%s\n", strings.Replace(tt, "\n", "\\n", -1))
if strings.HasSuffix(k, ".pageInfo.endCursor") {
endCursor = tt
}
case json.Number:
fmt.Fprintf(out, "%s\n", color("0;35", tt))
case nil:
fmt.Fprintf(out, "\n")
case bool:
fmt.Fprintf(out, "%s\n", color("1;33", fmt.Sprintf("%v", tt)))
if strings.HasSuffix(k, ".pageInfo.hasNextPage") {
hasNextPage = tt
}
default:
panic("unknown type")
}
......@@ -97,4 +100,5 @@ func JSONPath(out io.Writer, src io.Reader, colorize bool) {
}
}
}
return
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册