tests/thirdparty: package metadata (#223)

Fetches third-party package metadata from Github.
This commit is contained in:
Michael McLoughlin
2021-11-07 16:13:33 -08:00
committed by GitHub
parent afe2d539b8
commit f355d27b13
9 changed files with 544 additions and 24 deletions

107
internal/github/client.go Normal file
View File

@@ -0,0 +1,107 @@
// Package github provides a client for the Github REST API.
package github
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
)
// Client for the Github REST API.
type Client struct {
client *http.Client
base string
token string
}
// Option configures a Github client.
type Option func(*Client)
// WithHTTPClient configures the HTTP client that should be used for Github API
// requests.
func WithHTTPClient(h *http.Client) Option {
return func(c *Client) { c.client = h }
}
// WithToken configures a Client with an authentication token for Github API
// requests.
func WithToken(token string) Option {
return func(c *Client) { c.token = token }
}
// WithTokenFromEnvironment configures a Client using the GITHUB_TOKEN
// environment variable.
func WithTokenFromEnvironment() Option {
return WithToken(os.Getenv("GITHUB_TOKEN"))
}
// NewClient initializes a client using the given HTTP client.
func NewClient(opts ...Option) *Client {
c := &Client{
client: http.DefaultClient,
base: "https://api.github.com",
}
for _, opt := range opts {
opt(c)
}
return c
}
// Repository gets information about the given Github repository.
func (c *Client) Repository(ctx context.Context, owner, name string) (*Repository, error) {
// Build request.
u := c.base + "/repos/" + owner + "/" + name
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
if err != nil {
return nil, err
}
// Execute.
repo := &Repository{}
if err := c.request(req, repo); err != nil {
return nil, err
}
return repo, nil
}
func (c *Client) request(req *http.Request, payload interface{}) (err error) {
// Add common headers.
if c.token != "" {
req.Header.Set("Authorization", "Bearer "+c.token)
}
req.Header.Set("Accept", "application/vnd.github.v3+json")
// Execute the request.
res, err := c.client.Do(req)
if err != nil {
return err
}
defer func() {
if errc := res.Body.Close(); errc != nil && err == nil {
err = errc
}
}()
// Check status.
if res.StatusCode != http.StatusOK {
return fmt.Errorf("http status %d: %s", res.StatusCode, http.StatusText(res.StatusCode))
}
// Parse response body.
d := json.NewDecoder(res.Body)
if err := d.Decode(payload); err != nil {
return err
}
// Should not have trailing data.
if d.More() {
return errors.New("unexpected extra data after JSON")
}
return nil
}

View File

@@ -0,0 +1,27 @@
package github
import (
"context"
"encoding/json"
"net/http"
"testing"
"github.com/mmcloughlin/avo/internal/test"
)
func TestClientRepository(t *testing.T) {
test.RequiresNetwork(t)
ctx := context.Background()
g := NewClient(WithHTTPClient(http.DefaultClient), WithTokenFromEnvironment())
r, err := g.Repository(ctx, "golang", "go")
if err != nil {
t.Fatal(err)
}
j, err := json.MarshalIndent(r, "", "\t")
if err != nil {
t.Fatal(err)
}
t.Logf("repository = %s", j)
}

140
internal/github/models.go Normal file
View File

@@ -0,0 +1,140 @@
package github
import "time"
// Repository is a github repository.
type Repository struct {
ID int `json:"id"`
NodeID string `json:"node_id"`
Name string `json:"name"`
FullName string `json:"full_name"`
Private bool `json:"private"`
Owner *User `json:"owner"`
HTMLURL string `json:"html_url"`
Description string `json:"description"`
Fork bool `json:"fork"`
URL string `json:"url"`
ForksURL string `json:"forks_url"`
KeysURL string `json:"keys_url"`
CollaboratorsURL string `json:"collaborators_url"`
TeamsURL string `json:"teams_url"`
HooksURL string `json:"hooks_url"`
IssueEventsURL string `json:"issue_events_url"`
EventsURL string `json:"events_url"`
AssigneesURL string `json:"assignees_url"`
BranchesURL string `json:"branches_url"`
TagsURL string `json:"tags_url"`
BlobsURL string `json:"blobs_url"`
GitTagsURL string `json:"git_tags_url"`
GitRefsURL string `json:"git_refs_url"`
TreesURL string `json:"trees_url"`
StatusesURL string `json:"statuses_url"`
LanguagesURL string `json:"languages_url"`
StargazersURL string `json:"stargazers_url"`
ContributorsURL string `json:"contributors_url"`
SubscribersURL string `json:"subscribers_url"`
SubscriptionURL string `json:"subscription_url"`
CommitsURL string `json:"commits_url"`
GitCommitsURL string `json:"git_commits_url"`
CommentsURL string `json:"comments_url"`
IssueCommentURL string `json:"issue_comment_url"`
ContentsURL string `json:"contents_url"`
CompareURL string `json:"compare_url"`
MergesURL string `json:"merges_url"`
ArchiveURL string `json:"archive_url"`
DownloadsURL string `json:"downloads_url"`
IssuesURL string `json:"issues_url"`
PullsURL string `json:"pulls_url"`
MilestonesURL string `json:"milestones_url"`
NotificationsURL string `json:"notifications_url"`
LabelsURL string `json:"labels_url"`
ReleasesURL string `json:"releases_url"`
DeploymentsURL string `json:"deployments_url"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
PushedAt time.Time `json:"pushed_at"`
GitURL string `json:"git_url"`
SSHURL string `json:"ssh_url"`
CloneURL string `json:"clone_url"`
SvnURL string `json:"svn_url"`
Homepage string `json:"homepage"`
Size int `json:"size"`
StargazersCount int `json:"stargazers_count"`
WatchersCount int `json:"watchers_count"`
Language string `json:"language"`
HasIssues bool `json:"has_issues"`
HasProjects bool `json:"has_projects"`
HasDownloads bool `json:"has_downloads"`
HasWiki bool `json:"has_wiki"`
HasPages bool `json:"has_pages"`
ForksCount int `json:"forks_count"`
MirrorURL string `json:"mirror_url"`
Archived bool `json:"archived"`
Disabled bool `json:"disabled"`
OpenIssuesCount int `json:"open_issues_count"`
License *License `json:"license"`
AllowForking bool `json:"allow_forking"`
IsTemplate bool `json:"is_template"`
Topics []string `json:"topics"`
Visibility string `json:"visibility"`
Forks int `json:"forks"`
OpenIssues int `json:"open_issues"`
Watchers int `json:"watchers"`
DefaultBranch string `json:"default_branch"`
Organization *Organization `json:"organization"`
NetworkCount int `json:"network_count"`
SubscribersCount int `json:"subscribers_count"`
}
// User is a Github user.
type User struct {
Login string `json:"login"`
ID int `json:"id"`
NodeID string `json:"node_id"`
AvatarURL string `json:"avatar_url"`
GravatarID string `json:"gravatar_id"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
FollowersURL string `json:"followers_url"`
FollowingURL string `json:"following_url"`
GistsURL string `json:"gists_url"`
StarredURL string `json:"starred_url"`
SubscriptionsURL string `json:"subscriptions_url"`
OrganizationsURL string `json:"organizations_url"`
ReposURL string `json:"repos_url"`
EventsURL string `json:"events_url"`
ReceivedEventsURL string `json:"received_events_url"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
// Organization is a Github organization.
type Organization struct {
Login string `json:"login"`
ID int `json:"id"`
NodeID string `json:"node_id"`
AvatarURL string `json:"avatar_url"`
GravatarID string `json:"gravatar_id"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
FollowersURL string `json:"followers_url"`
FollowingURL string `json:"following_url"`
GistsURL string `json:"gists_url"`
StarredURL string `json:"starred_url"`
SubscriptionsURL string `json:"subscriptions_url"`
OrganizationsURL string `json:"organizations_url"`
ReposURL string `json:"repos_url"`
EventsURL string `json:"events_url"`
ReceivedEventsURL string `json:"received_events_url"`
Type string `json:"type"`
SiteAdmin bool `json:"site_admin"`
}
// License is an open source license.
type License struct {
Key string `json:"key"`
Name string `json:"name"`
SPDXID string `json:"spdx_id"`
URL string `json:"url"`
NodeID string `json:"node_id"`
}