67 lines
1.7 KiB
Go
67 lines
1.7 KiB
Go
|
// 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
|
|||
|
}
|