// Package github provides a Go API for the GitHub issue tracker. // See https://developer.github.com/v3/search/#search-issues. package github import ( "encoding/json" "fmt" "net/http" "net/url" "strings" "time" ) const IssuesURL = "https://api.github.com/search/issues" type IssuesSearchResult struct { TotalCount int `json:"total_count"` Items []*Issue } type Issue struct { Number int HTMLURL string `json:"html_url"` Title string State string User *User CreatedAt time.Time `json:"created_at"` Body string // in Markdown format } type User struct { Login string HTMLURL string `json:"html_url"` } // 和前面一样,即使对应的JSON对象名是小写字母, // 每个结构体的成员名也是声明为大写字母开头的。 // 因为有些JSON成员名字和Go结构体成员名字并不相同, // 因此需要Go语言结构体成员Tag来指定对应的JSON名字。 // 同样,在解码的时候也需要做同样的处理, // GitHub服务返回的信息比我们定义的要多很多。 // SearchIssues queries the GitHub issue tracker. func SearchIssues(terms []string) (*IssuesSearchResult, error) { q := url.QueryEscape(strings.Join(terms, " ")) resp, err := http.Get(IssuesURL + "?q=" + q) if err != nil { return nil, err } // We must close resp.Body on all execution paths. // (Chapter 5 presents 'defer', which makes this simpler.) if resp.StatusCode != http.StatusOK { resp.Body.Close() return nil, fmt.Errorf("search query failed: %s", resp.Status) } var result IssuesSearchResult if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { resp.Body.Close() return nil, err } resp.Body.Close() return &result, nil }