* add migrations * fix package dependency * fix lints * implements migrations except pull requests * add releases * migrating releases * fix bug * fix lint * fix migrate releases * fix tests * add rollback * pull request migtations * fix import * fix go module vendor * add tests for upload to gitea * more migrate options * fix swagger-check * fix misspell * add options on migration UI * fix log error * improve UI options on migrating * add support for username password when migrating from github * fix tests * remove comments and fix migrate limitation * improve error handles * migrate API will also support migrate milestones/labels/issues/pulls/releases * fix tests and remove unused codes * add DownloaderFactory and docs about how to create a new Downloader * fix misspell * fix migration docs * Add hints about migrate options on migration page * fix testsfor-closed-social
@ -0,0 +1,72 @@ | |||
--- | |||
date: "2019-04-15T17:29:00+08:00" | |||
title: "Advanced: Migrations Interfaces" | |||
slug: "migrations-interfaces" | |||
weight: 30 | |||
toc: true | |||
draft: false | |||
menu: | |||
sidebar: | |||
parent: "advanced" | |||
name: "Migrations Interfaces" | |||
weight: 55 | |||
identifier: "migrations-interfaces" | |||
--- | |||
# Migration Features | |||
The new migration features were introduced in Gitea 1.9.0. It defines two interfaces to support migrating | |||
repositories data from other git host platforms to gitea or, in the future migrating gitea data to other | |||
git host platforms. Currently, only the migrations from github via APIv3 to Gitea is implemented. | |||
First of all, Gitea defines some standard objects in packages `modules/migrations/base`. They are | |||
`Repository`, `Milestone`, `Release`, `Label`, `Issue`, `Comment`, `PullRequest`. | |||
## Downloader Interfaces | |||
To migrate from a new git host platform, there are two steps to be updated. | |||
- You should implement a `Downloader` which will get all kinds of repository informations. | |||
- You should implement a `DownloaderFactory` which is used to detect if the URL matches and | |||
create a Downloader. | |||
- You'll need to register the `DownloaderFactory` via `RegisterDownloaderFactory` on init. | |||
```Go | |||
type Downloader interface { | |||
GetRepoInfo() (*Repository, error) | |||
GetMilestones() ([]*Milestone, error) | |||
GetReleases() ([]*Release, error) | |||
GetLabels() ([]*Label, error) | |||
GetIssues(start, limit int) ([]*Issue, error) | |||
GetComments(issueNumber int64) ([]*Comment, error) | |||
GetPullRequests(start, limit int) ([]*PullRequest, error) | |||
} | |||
``` | |||
```Go | |||
type DownloaderFactory interface { | |||
Match(opts MigrateOptions) (bool, error) | |||
New(opts MigrateOptions) (Downloader, error) | |||
} | |||
``` | |||
## Uploader Interface | |||
Currently, only a `GiteaLocalUploader` is implemented, so we only save downloaded | |||
data via this `Uploader` on the local Gitea instance. Other uploaders are not supported | |||
and will be implemented in future. | |||
```Go | |||
// Uploader uploads all the informations | |||
type Uploader interface { | |||
CreateRepo(repo *Repository, includeWiki bool) error | |||
CreateMilestone(milestone *Milestone) error | |||
CreateRelease(release *Release) error | |||
CreateLabel(label *Label) error | |||
CreateIssue(issue *Issue) error | |||
CreateComment(issueNumber int64, comment *Comment) error | |||
CreatePullRequest(pr *PullRequest) error | |||
Rollback() error | |||
} | |||
``` |
@ -0,0 +1,145 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import "github.com/go-xorm/xorm" | |||
// InsertIssue insert one issue to database | |||
func InsertIssue(issue *Issue, labelIDs []int64) error { | |||
sess := x.NewSession() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err := insertIssue(sess, issue, labelIDs); err != nil { | |||
return err | |||
} | |||
return sess.Commit() | |||
} | |||
func insertIssue(sess *xorm.Session, issue *Issue, labelIDs []int64) error { | |||
if issue.MilestoneID > 0 { | |||
sess.Incr("num_issues") | |||
if issue.IsClosed { | |||
sess.Incr("num_closed_issues") | |||
} | |||
if _, err := sess.ID(issue.MilestoneID).NoAutoTime().Update(new(Milestone)); err != nil { | |||
return err | |||
} | |||
} | |||
if _, err := sess.NoAutoTime().Insert(issue); err != nil { | |||
return err | |||
} | |||
var issueLabels = make([]IssueLabel, 0, len(labelIDs)) | |||
for _, labelID := range labelIDs { | |||
issueLabels = append(issueLabels, IssueLabel{ | |||
IssueID: issue.ID, | |||
LabelID: labelID, | |||
}) | |||
} | |||
if _, err := sess.Insert(issueLabels); err != nil { | |||
return err | |||
} | |||
if !issue.IsPull { | |||
sess.ID(issue.RepoID).Incr("num_issues") | |||
if issue.IsClosed { | |||
sess.Incr("num_closed_issues") | |||
} | |||
} else { | |||
sess.ID(issue.RepoID).Incr("num_pulls") | |||
if issue.IsClosed { | |||
sess.Incr("num_closed_pulls") | |||
} | |||
} | |||
if _, err := sess.NoAutoTime().Update(issue.Repo); err != nil { | |||
return err | |||
} | |||
sess.Incr("num_issues") | |||
if issue.IsClosed { | |||
sess.Incr("num_closed_issues") | |||
} | |||
if _, err := sess.In("id", labelIDs).Update(new(Label)); err != nil { | |||
return err | |||
} | |||
if issue.MilestoneID > 0 { | |||
if _, err := sess.ID(issue.MilestoneID).SetExpr("completeness", "num_closed_issues * 100 / num_issues").Update(new(Milestone)); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} | |||
// InsertComment inserted a comment | |||
func InsertComment(comment *Comment) error { | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
if _, err := sess.NoAutoTime().Insert(comment); err != nil { | |||
return err | |||
} | |||
if _, err := sess.ID(comment.IssueID).Incr("num_comments").Update(new(Issue)); err != nil { | |||
return err | |||
} | |||
return sess.Commit() | |||
} | |||
// InsertPullRequest inserted a pull request | |||
func InsertPullRequest(pr *PullRequest, labelIDs []int64) error { | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
if err := insertIssue(sess, pr.Issue, labelIDs); err != nil { | |||
return err | |||
} | |||
pr.IssueID = pr.Issue.ID | |||
if _, err := sess.NoAutoTime().Insert(pr); err != nil { | |||
return err | |||
} | |||
return sess.Commit() | |||
} | |||
// MigrateRelease migrates release | |||
func MigrateRelease(rel *Release) error { | |||
sess := x.NewSession() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
var oriRel = Release{ | |||
RepoID: rel.RepoID, | |||
TagName: rel.TagName, | |||
} | |||
exist, err := sess.Get(&oriRel) | |||
if err != nil { | |||
return err | |||
} | |||
if !exist { | |||
if _, err := sess.NoAutoTime().Insert(rel); err != nil { | |||
return err | |||
} | |||
} else { | |||
rel.ID = oriRel.ID | |||
if _, err := sess.ID(rel.ID).Cols("target, title, note, is_tag, num_commits").Update(rel); err != nil { | |||
return err | |||
} | |||
} | |||
for i := 0; i < len(rel.Attachments); i++ { | |||
rel.Attachments[i].ReleaseID = rel.ID | |||
} | |||
if _, err := sess.NoAutoTime().Insert(rel.Attachments); err != nil { | |||
return err | |||
} | |||
return sess.Commit() | |||
} |
@ -0,0 +1,17 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
import "time" | |||
// Comment is a standard comment information | |||
type Comment struct { | |||
PosterName string | |||
PosterEmail string | |||
Created time.Time | |||
Content string | |||
Reactions *Reactions | |||
} |
@ -0,0 +1,23 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// Downloader downloads the site repo informations | |||
type Downloader interface { | |||
GetRepoInfo() (*Repository, error) | |||
GetMilestones() ([]*Milestone, error) | |||
GetReleases() ([]*Release, error) | |||
GetLabels() ([]*Label, error) | |||
GetIssues(start, limit int) ([]*Issue, error) | |||
GetComments(issueNumber int64) ([]*Comment, error) | |||
GetPullRequests(start, limit int) ([]*PullRequest, error) | |||
} | |||
// DownloaderFactory defines an interface to match a downloader implementation and create a downloader | |||
type DownloaderFactory interface { | |||
Match(opts MigrateOptions) (bool, error) | |||
New(opts MigrateOptions) (Downloader, error) | |||
} |
@ -0,0 +1,24 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
import "time" | |||
// Issue is a standard issue information | |||
type Issue struct { | |||
Number int64 | |||
PosterName string | |||
PosterEmail string | |||
Title string | |||
Content string | |||
Milestone string | |||
State string // closed, open | |||
IsLocked bool | |||
Created time.Time | |||
Closed *time.Time | |||
Labels []*Label | |||
Reactions *Reactions | |||
} |
@ -0,0 +1,13 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// Label defines a standard label informations | |||
type Label struct { | |||
Name string | |||
Color string | |||
Description string | |||
} |
@ -0,0 +1,19 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
import "time" | |||
// Milestone defines a standard milestone | |||
type Milestone struct { | |||
Title string | |||
Description string | |||
Deadline *time.Time | |||
Created time.Time | |||
Updated *time.Time | |||
Closed *time.Time | |||
State string | |||
} |
@ -0,0 +1,26 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// MigrateOptions defines the way a repository gets migrated | |||
type MigrateOptions struct { | |||
RemoteURL string | |||
AuthUsername string | |||
AuthPassword string | |||
Name string | |||
Description string | |||
Wiki bool | |||
Issues bool | |||
Milestones bool | |||
Labels bool | |||
Releases bool | |||
Comments bool | |||
PullRequests bool | |||
Private bool | |||
Mirror bool | |||
IgnoreIssueAuthor bool // if true will not add original author information before issues or comments content. | |||
} |
@ -0,0 +1,53 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
import ( | |||
"fmt" | |||
"time" | |||
) | |||
// PullRequest defines a standard pull request information | |||
type PullRequest struct { | |||
Number int64 | |||
Title string | |||
PosterName string | |||
PosterEmail string | |||
Content string | |||
Milestone string | |||
State string | |||
Created time.Time | |||
Closed *time.Time | |||
Labels []*Label | |||
PatchURL string | |||
Merged bool | |||
MergedTime *time.Time | |||
MergeCommitSHA string | |||
Head PullRequestBranch | |||
Base PullRequestBranch | |||
Assignee string | |||
Assignees []string | |||
IsLocked bool | |||
} | |||
// IsForkPullRequest returns true if the pull request from a forked repository but not the same repository | |||
func (p *PullRequest) IsForkPullRequest() bool { | |||
return p.Head.RepoPath() != p.Base.RepoPath() | |||
} | |||
// PullRequestBranch represents a pull request branch | |||
type PullRequestBranch struct { | |||
CloneURL string | |||
Ref string | |||
SHA string | |||
RepoName string | |||
OwnerName string | |||
} | |||
// RepoPath returns pull request repo path | |||
func (p PullRequestBranch) RepoPath() string { | |||
return fmt.Sprintf("%s/%s", p.OwnerName, p.RepoName) | |||
} |
@ -0,0 +1,17 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// Reactions represents a summary of reactions. | |||
type Reactions struct { | |||
TotalCount int | |||
PlusOne int | |||
MinusOne int | |||
Laugh int | |||
Confused int | |||
Heart int | |||
Hooray int | |||
} |
@ -0,0 +1,31 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
import "time" | |||
// ReleaseAsset represents a release asset | |||
type ReleaseAsset struct { | |||
URL string | |||
Name string | |||
ContentType *string | |||
Size *int | |||
DownloadCount *int | |||
Created time.Time | |||
Updated time.Time | |||
} | |||
// Release represents a release | |||
type Release struct { | |||
TagName string | |||
TargetCommitish string | |||
Name string | |||
Body string | |||
Draft bool | |||
Prerelease bool | |||
Assets []ReleaseAsset | |||
Created time.Time | |||
Published time.Time | |||
} |
@ -0,0 +1,18 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// Repository defines a standard repository information | |||
type Repository struct { | |||
Name string | |||
Owner string | |||
IsPrivate bool | |||
IsMirror bool | |||
Description string | |||
AuthUsername string | |||
AuthPassword string | |||
CloneURL string | |||
} |
@ -0,0 +1,18 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package base | |||
// Uploader uploads all the informations | |||
type Uploader interface { | |||
CreateRepo(repo *Repository, includeWiki bool) error | |||
CreateMilestone(milestone *Milestone) error | |||
CreateRelease(release *Release) error | |||
CreateLabel(label *Label) error | |||
CreateIssue(issue *Issue) error | |||
CreateComment(issueNumber int64, comment *Comment) error | |||
CreatePullRequest(pr *PullRequest) error | |||
Rollback() error | |||
} |
@ -0,0 +1,29 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"errors" | |||
"github.com/google/go-github/v24/github" | |||
) | |||
var ( | |||
// ErrNotSupported returns the error not supported | |||
ErrNotSupported = errors.New("not supported") | |||
) | |||
// IsRateLimitError returns true if the err is github.RateLimitError | |||
func IsRateLimitError(err error) bool { | |||
_, ok := err.(*github.RateLimitError) | |||
return ok | |||
} | |||
// IsTwoFactorAuthError returns true if the err is github.TwoFactorAuthError | |||
func IsTwoFactorAuthError(err error) bool { | |||
_, ok := err.(*github.TwoFactorAuthError) | |||
return ok | |||
} |
@ -0,0 +1,69 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"code.gitea.io/gitea/modules/migrations/base" | |||
) | |||
var ( | |||
_ base.Downloader = &PlainGitDownloader{} | |||
) | |||
// PlainGitDownloader implements a Downloader interface to clone git from a http/https URL | |||
type PlainGitDownloader struct { | |||
ownerName string | |||
repoName string | |||
remoteURL string | |||
} | |||
// NewPlainGitDownloader creates a git Downloader | |||
func NewPlainGitDownloader(ownerName, repoName, remoteURL string) *PlainGitDownloader { | |||
return &PlainGitDownloader{ | |||
ownerName: ownerName, | |||
repoName: repoName, | |||
remoteURL: remoteURL, | |||
} | |||
} | |||
// GetRepoInfo returns a repository information | |||
func (g *PlainGitDownloader) GetRepoInfo() (*base.Repository, error) { | |||
// convert github repo to stand Repo | |||
return &base.Repository{ | |||
Owner: g.ownerName, | |||
Name: g.repoName, | |||
CloneURL: g.remoteURL, | |||
}, nil | |||
} | |||
// GetMilestones returns milestones | |||
func (g *PlainGitDownloader) GetMilestones() ([]*base.Milestone, error) { | |||
return nil, ErrNotSupported | |||
} | |||
// GetLabels returns labels | |||
func (g *PlainGitDownloader) GetLabels() ([]*base.Label, error) { | |||
return nil, ErrNotSupported | |||
} | |||
// GetReleases returns releases | |||
func (g *PlainGitDownloader) GetReleases() ([]*base.Release, error) { | |||
return nil, ErrNotSupported | |||
} | |||
// GetIssues returns issues according start and limit | |||
func (g *PlainGitDownloader) GetIssues(start, limit int) ([]*base.Issue, error) { | |||
return nil, ErrNotSupported | |||
} | |||
// GetComments returns comments according issueNumber | |||
func (g *PlainGitDownloader) GetComments(issueNumber int64) ([]*base.Comment, error) { | |||
return nil, ErrNotSupported | |||
} | |||
// GetPullRequests returns pull requests according start and limit | |||
func (g *PlainGitDownloader) GetPullRequests(start, limit int) ([]*base.PullRequest, error) { | |||
return nil, ErrNotSupported | |||
} |
@ -0,0 +1,403 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"fmt" | |||
"io" | |||
"net/http" | |||
"os" | |||
"path" | |||
"path/filepath" | |||
"strings" | |||
"sync" | |||
"time" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/migrations/base" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/util" | |||
gouuid "github.com/satori/go.uuid" | |||
) | |||
var ( | |||
_ base.Uploader = &GiteaLocalUploader{} | |||
) | |||
// GiteaLocalUploader implements an Uploader to gitea sites | |||
type GiteaLocalUploader struct { | |||
doer *models.User | |||
repoOwner string | |||
repoName string | |||
repo *models.Repository | |||
labels sync.Map | |||
milestones sync.Map | |||
issues sync.Map | |||
gitRepo *git.Repository | |||
prHeadCache map[string]struct{} | |||
} | |||
// NewGiteaLocalUploader creates an gitea Uploader via gitea API v1 | |||
func NewGiteaLocalUploader(doer *models.User, repoOwner, repoName string) *GiteaLocalUploader { | |||
return &GiteaLocalUploader{ | |||
doer: doer, | |||
repoOwner: repoOwner, | |||
repoName: repoName, | |||
prHeadCache: make(map[string]struct{}), | |||
} | |||
} | |||
// CreateRepo creates a repository | |||
func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, includeWiki bool) error { | |||
owner, err := models.GetUserByName(g.repoOwner) | |||
if err != nil { | |||
return err | |||
} | |||
r, err := models.MigrateRepository(g.doer, owner, models.MigrateRepoOptions{ | |||
Name: g.repoName, | |||
Description: repo.Description, | |||
IsMirror: repo.IsMirror, | |||
RemoteAddr: repo.CloneURL, | |||
IsPrivate: repo.IsPrivate, | |||
Wiki: includeWiki, | |||
}) | |||
if err != nil { | |||
return err | |||
} | |||
g.repo = r | |||
g.gitRepo, err = git.OpenRepository(r.RepoPath()) | |||
return err | |||
} | |||
// CreateMilestone creates milestone | |||
func (g *GiteaLocalUploader) CreateMilestone(milestone *base.Milestone) error { | |||
var deadline util.TimeStamp | |||
if milestone.Deadline != nil { | |||
deadline = util.TimeStamp(milestone.Deadline.Unix()) | |||
} | |||
if deadline == 0 { | |||
deadline = util.TimeStamp(time.Date(9999, 1, 1, 0, 0, 0, 0, setting.UILocation).Unix()) | |||
} | |||
var ms = models.Milestone{ | |||
RepoID: g.repo.ID, | |||
Name: milestone.Title, | |||
Content: milestone.Description, | |||
IsClosed: milestone.State == "close", | |||
DeadlineUnix: deadline, | |||
} | |||
if ms.IsClosed && milestone.Closed != nil { | |||
ms.ClosedDateUnix = util.TimeStamp(milestone.Closed.Unix()) | |||
} | |||
err := models.NewMilestone(&ms) | |||
if err != nil { | |||
return err | |||
} | |||
g.milestones.Store(ms.Name, ms.ID) | |||
return nil | |||
} | |||
// CreateLabel creates label | |||
func (g *GiteaLocalUploader) CreateLabel(label *base.Label) error { | |||
var lb = models.Label{ | |||
RepoID: g.repo.ID, | |||
Name: label.Name, | |||
Description: label.Description, | |||
Color: fmt.Sprintf("#%s", label.Color), | |||
} | |||
err := models.NewLabel(&lb) | |||
if err != nil { | |||
return err | |||
} | |||
g.labels.Store(lb.Name, lb.ID) | |||
return nil | |||
} | |||
// CreateRelease creates release | |||
func (g *GiteaLocalUploader) CreateRelease(release *base.Release) error { | |||
var rel = models.Release{ | |||
RepoID: g.repo.ID, | |||
PublisherID: g.doer.ID, | |||
TagName: release.TagName, | |||
LowerTagName: strings.ToLower(release.TagName), | |||
Target: release.TargetCommitish, | |||
Title: release.Name, | |||
Sha1: release.TargetCommitish, | |||
Note: release.Body, | |||
IsDraft: release.Draft, | |||
IsPrerelease: release.Prerelease, | |||
IsTag: false, | |||
CreatedUnix: util.TimeStamp(release.Created.Unix()), | |||
} | |||
// calc NumCommits | |||
commit, err := g.gitRepo.GetCommit(rel.TagName) | |||
if err != nil { | |||
return fmt.Errorf("GetCommit: %v", err) | |||
} | |||
rel.NumCommits, err = commit.CommitsCount() | |||
if err != nil { | |||
return fmt.Errorf("CommitsCount: %v", err) | |||
} | |||
for _, asset := range release.Assets { | |||
var attach = models.Attachment{ | |||
UUID: gouuid.NewV4().String(), | |||
Name: asset.Name, | |||
DownloadCount: int64(*asset.DownloadCount), | |||
Size: int64(*asset.Size), | |||
CreatedUnix: util.TimeStamp(asset.Created.Unix()), | |||
} | |||
// download attachment | |||
resp, err := http.Get(asset.URL) | |||
if err != nil { | |||
return err | |||
} | |||
defer resp.Body.Close() | |||
localPath := attach.LocalPath() | |||
if err = os.MkdirAll(path.Dir(localPath), os.ModePerm); err != nil { | |||
return fmt.Errorf("MkdirAll: %v", err) | |||
} | |||
fw, err := os.Create(localPath) | |||
if err != nil { | |||
return fmt.Errorf("Create: %v", err) | |||
} | |||
defer fw.Close() | |||
if _, err := io.Copy(fw, resp.Body); err != nil { | |||
return err | |||
} | |||
rel.Attachments = append(rel.Attachments, &attach) | |||
} | |||
return models.MigrateRelease(&rel) | |||
} | |||
// CreateIssue creates issue | |||
func (g *GiteaLocalUploader) CreateIssue(issue *base.Issue) error { | |||
var labelIDs []int64 | |||
for _, label := range issue.Labels { | |||
id, ok := g.labels.Load(label.Name) | |||
if !ok { | |||
return fmt.Errorf("Label %s missing when create issue", label.Name) | |||
} | |||
labelIDs = append(labelIDs, id.(int64)) | |||
} | |||
var milestoneID int64 | |||
if issue.Milestone != "" { | |||
milestone, ok := g.milestones.Load(issue.Milestone) | |||
if !ok { | |||
return fmt.Errorf("Milestone %s missing when create issue", issue.Milestone) | |||
} | |||
milestoneID = milestone.(int64) | |||
} | |||
var is = models.Issue{ | |||
RepoID: g.repo.ID, | |||
Repo: g.repo, | |||
Index: issue.Number, | |||
PosterID: g.doer.ID, | |||
Title: issue.Title, | |||
Content: issue.Content, | |||
IsClosed: issue.State == "closed", | |||
IsLocked: issue.IsLocked, | |||
MilestoneID: milestoneID, | |||
CreatedUnix: util.TimeStamp(issue.Created.Unix()), | |||
} | |||
if issue.Closed != nil { | |||
is.ClosedUnix = util.TimeStamp(issue.Closed.Unix()) | |||
} | |||
err := models.InsertIssue(&is, labelIDs) | |||
if err != nil { | |||
return err | |||
} | |||
g.issues.Store(issue.Number, is.ID) | |||
// TODO: add reactions | |||
return err | |||
} | |||
// CreateComment creates comment | |||
func (g *GiteaLocalUploader) CreateComment(issueNumber int64, comment *base.Comment) error { | |||
var issueID int64 | |||
if issueIDStr, ok := g.issues.Load(issueNumber); !ok { | |||
issue, err := models.GetIssueByIndex(g.repo.ID, issueNumber) | |||
if err != nil { | |||
return err | |||
} | |||
issueID = issue.ID | |||
g.issues.Store(issueNumber, issueID) | |||
} else { | |||
issueID = issueIDStr.(int64) | |||
} | |||
var cm = models.Comment{ | |||
IssueID: issueID, | |||
Type: models.CommentTypeComment, | |||
PosterID: g.doer.ID, | |||
Content: comment.Content, | |||
CreatedUnix: util.TimeStamp(comment.Created.Unix()), | |||
} | |||
err := models.InsertComment(&cm) | |||
// TODO: Reactions | |||
return err | |||
} | |||
// CreatePullRequest creates pull request | |||
func (g *GiteaLocalUploader) CreatePullRequest(pr *base.PullRequest) error { | |||
var labelIDs []int64 | |||
for _, label := range pr.Labels { | |||
id, ok := g.labels.Load(label.Name) | |||
if !ok { | |||
return fmt.Errorf("Label %s missing when create issue", label.Name) | |||
} | |||
labelIDs = append(labelIDs, id.(int64)) | |||
} | |||
var milestoneID int64 | |||
if pr.Milestone != "" { | |||
milestone, ok := g.milestones.Load(pr.Milestone) | |||
if !ok { | |||
return fmt.Errorf("Milestone %s missing when create issue", pr.Milestone) | |||
} | |||
milestoneID = milestone.(int64) | |||
} | |||
// download patch file | |||
resp, err := http.Get(pr.PatchURL) | |||
if err != nil { | |||
return err | |||
} | |||
defer resp.Body.Close() | |||
pullDir := filepath.Join(g.repo.RepoPath(), "pulls") | |||
if err = os.MkdirAll(pullDir, os.ModePerm); err != nil { | |||
return err | |||
} | |||
f, err := os.Create(filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number))) | |||
if err != nil { | |||
return err | |||
} | |||
defer f.Close() | |||
_, err = io.Copy(f, resp.Body) | |||
if err != nil { | |||
return err | |||
} | |||
// set head information | |||
pullHead := filepath.Join(g.repo.RepoPath(), "refs", "pull", fmt.Sprintf("%d", pr.Number)) | |||
if err := os.MkdirAll(pullHead, os.ModePerm); err != nil { | |||
return err | |||
} | |||
p, err := os.Create(filepath.Join(pullHead, "head")) | |||
if err != nil { | |||
return err | |||
} | |||
defer p.Close() | |||
_, err = p.WriteString(pr.Head.SHA) | |||
if err != nil { | |||
return err | |||
} | |||
var head = "unknown repository" | |||
if pr.IsForkPullRequest() { | |||
if pr.Head.OwnerName != "" { | |||
remote := pr.Head.OwnerName | |||
_, ok := g.prHeadCache[remote] | |||
if !ok { | |||
// git remote add | |||
err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true) | |||
if err != nil { | |||
log.Error("AddRemote failed: %s", err) | |||
} else { | |||
g.prHeadCache[remote] = struct{}{} | |||
ok = true | |||
} | |||
} | |||
if ok { | |||
_, err = git.NewCommand("fetch", remote, pr.Head.Ref).RunInDir(g.repo.RepoPath()) | |||
if err != nil { | |||
log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err) | |||
} else { | |||
headBranch := filepath.Join(g.repo.RepoPath(), "refs", "heads", pr.Head.OwnerName, pr.Head.Ref) | |||
if err := os.MkdirAll(filepath.Dir(headBranch), os.ModePerm); err != nil { | |||
return err | |||
} | |||
b, err := os.Create(headBranch) | |||
if err != nil { | |||
return err | |||
} | |||
defer b.Close() | |||
_, err = b.WriteString(pr.Head.SHA) | |||
if err != nil { | |||
return err | |||
} | |||
head = pr.Head.OwnerName + "/" + pr.Head.Ref | |||
} | |||
} | |||
} | |||
} else { | |||
head = pr.Head.Ref | |||
} | |||
var pullRequest = models.PullRequest{ | |||
HeadRepoID: g.repo.ID, | |||
HeadBranch: head, | |||
HeadUserName: g.repoOwner, | |||
BaseRepoID: g.repo.ID, | |||
BaseBranch: pr.Base.Ref, | |||
MergeBase: pr.Base.SHA, | |||
Index: pr.Number, | |||
HasMerged: pr.Merged, | |||
Issue: &models.Issue{ | |||
RepoID: g.repo.ID, | |||
Repo: g.repo, | |||
Title: pr.Title, | |||
Index: pr.Number, | |||
PosterID: g.doer.ID, | |||
Content: pr.Content, | |||
MilestoneID: milestoneID, | |||
IsPull: true, | |||
IsClosed: pr.State == "closed", | |||
IsLocked: pr.IsLocked, | |||
CreatedUnix: util.TimeStamp(pr.Created.Unix()), | |||
}, | |||
} | |||
if pullRequest.Issue.IsClosed && pr.Closed != nil { | |||
pullRequest.Issue.ClosedUnix = util.TimeStamp(pr.Closed.Unix()) | |||
} | |||
if pullRequest.HasMerged && pr.MergedTime != nil { | |||
pullRequest.MergedUnix = util.TimeStamp(pr.MergedTime.Unix()) | |||
pullRequest.MergedCommitID = pr.MergeCommitSHA | |||
pullRequest.MergerID = g.doer.ID | |||
} | |||
// TODO: reactions | |||
// TODO: assignees | |||
return models.InsertPullRequest(&pullRequest, labelIDs) | |||
} | |||
// Rollback when migrating failed, this will rollback all the changes. | |||
func (g *GiteaLocalUploader) Rollback() error { | |||
if g.repo != nil && g.repo.ID > 0 { | |||
if err := models.DeleteRepository(g.doer, g.repo.OwnerID, g.repo.ID); err != nil { | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@ -0,0 +1,95 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"testing" | |||
"time" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/util" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func TestGiteaUploadRepo(t *testing.T) { | |||
// FIXME: Since no accesskey or user/password will trigger rate limit of github, just skip | |||
t.Skip() | |||
models.PrepareTestEnv(t) | |||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 1}).(*models.User) | |||
var ( | |||
downloader = NewGithubDownloaderV3("", "", "go-xorm", "builder") | |||
repoName = "builder-" + time.Now().Format("2006-01-02-15-04-05") | |||
uploader = NewGiteaLocalUploader(user, user.Name, repoName) | |||
) | |||
err := migrateRepository(downloader, uploader, MigrateOptions{ | |||
RemoteURL: "https://github.com/go-xorm/builder", | |||
Name: repoName, | |||
AuthUsername: "", | |||
Wiki: true, | |||
Issues: true, | |||
Milestones: true, | |||
Labels: true, | |||
Releases: true, | |||
Comments: true, | |||
PullRequests: true, | |||
Private: true, | |||
Mirror: false, | |||
IgnoreIssueAuthor: false, | |||
}) | |||
assert.NoError(t, err) | |||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{OwnerID: user.ID, Name: repoName}).(*models.Repository) | |||
assert.True(t, repo.HasWiki()) | |||
milestones, err := models.GetMilestones(repo.ID, 0, false, "") | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 1, len(milestones)) | |||
milestones, err = models.GetMilestones(repo.ID, 0, true, "") | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 0, len(milestones)) | |||
labels, err := models.GetLabelsByRepoID(repo.ID, "") | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 11, len(labels)) | |||
releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ | |||
IncludeTags: true, | |||
}, 0, 10) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 8, len(releases)) | |||
releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ | |||
IncludeTags: false, | |||
}, 0, 10) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 1, len(releases)) | |||
issues, err := models.Issues(&models.IssuesOptions{ | |||
RepoIDs: []int64{repo.ID}, | |||
IsPull: util.OptionalBoolFalse, | |||
SortType: "oldest", | |||
}) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 14, len(issues)) | |||
assert.NoError(t, issues[0].LoadDiscussComments()) | |||
assert.EqualValues(t, 0, len(issues[0].Comments)) | |||
pulls, _, err := models.PullRequests(repo.ID, &models.PullRequestsOptions{ | |||
SortType: "oldest", | |||
}) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 34, len(pulls)) | |||
assert.NoError(t, pulls[0].LoadIssue()) | |||
assert.NoError(t, pulls[0].Issue.LoadDiscussComments()) | |||
assert.EqualValues(t, 2, len(pulls[0].Issue.Comments)) | |||
} |
@ -0,0 +1,475 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"context" | |||
"fmt" | |||
"net/http" | |||
"net/url" | |||
"strings" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/migrations/base" | |||
"github.com/google/go-github/v24/github" | |||
"golang.org/x/oauth2" | |||
) | |||
var ( | |||
_ base.Downloader = &GithubDownloaderV3{} | |||
_ base.DownloaderFactory = &GithubDownloaderV3Factory{} | |||
) | |||
func init() { | |||
RegisterDownloaderFactory(&GithubDownloaderV3Factory{}) | |||
} | |||
// GithubDownloaderV3Factory defines a github downloader v3 factory | |||
type GithubDownloaderV3Factory struct { | |||
} | |||
// Match returns ture if the migration remote URL matched this downloader factory | |||
func (f *GithubDownloaderV3Factory) Match(opts base.MigrateOptions) (bool, error) { | |||
u, err := url.Parse(opts.RemoteURL) | |||
if err != nil { | |||
return false, err | |||
} | |||
return u.Host == "github.com" && opts.AuthUsername != "", nil | |||
} | |||
// New returns a Downloader related to this factory according MigrateOptions | |||
func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Downloader, error) { | |||
u, err := url.Parse(opts.RemoteURL) | |||
if err != nil { | |||
return nil, err | |||
} | |||
fields := strings.Split(u.Path, "/") | |||
oldOwner := fields[1] | |||
oldName := strings.TrimSuffix(fields[2], ".git") | |||
log.Trace("Create github downloader: %s/%s", oldOwner, oldName) | |||
return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil | |||
} | |||
// GithubDownloaderV3 implements a Downloader interface to get repository informations | |||
// from github via APIv3 | |||
type GithubDownloaderV3 struct { | |||
ctx context.Context | |||
client *github.Client | |||
repoOwner string | |||
repoName string | |||
userName string | |||
password string | |||
} | |||
// NewGithubDownloaderV3 creates a github Downloader via github v3 API | |||
func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *GithubDownloaderV3 { | |||
var downloader = GithubDownloaderV3{ | |||
userName: userName, | |||
password: password, | |||
ctx: context.Background(), | |||
repoOwner: repoOwner, | |||
repoName: repoName, | |||
} | |||
var client *http.Client | |||
if userName != "" { | |||
if password == "" { | |||
ts := oauth2.StaticTokenSource( | |||
&oauth2.Token{AccessToken: userName}, | |||
) | |||
client = oauth2.NewClient(downloader.ctx, ts) | |||
} else { | |||
client = &http.Client{ | |||
Transport: &http.Transport{ | |||
Proxy: func(req *http.Request) (*url.URL, error) { | |||
req.SetBasicAuth(userName, password) | |||
return nil, nil | |||
}, | |||
}, | |||
} | |||
} | |||
} | |||
downloader.client = github.NewClient(client) | |||
return &downloader | |||
} | |||
// GetRepoInfo returns a repository information | |||
func (g *GithubDownloaderV3) GetRepoInfo() (*base.Repository, error) { | |||
gr, _, err := g.client.Repositories.Get(g.ctx, g.repoOwner, g.repoName) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// convert github repo to stand Repo | |||
return &base.Repository{ | |||
Owner: g.repoOwner, | |||
Name: gr.GetName(), | |||
IsPrivate: *gr.Private, | |||
Description: gr.GetDescription(), | |||
CloneURL: gr.GetCloneURL(), | |||
}, nil | |||
} | |||
// GetMilestones returns milestones | |||
func (g *GithubDownloaderV3) GetMilestones() ([]*base.Milestone, error) { | |||
var perPage = 100 | |||
var milestones = make([]*base.Milestone, 0, perPage) | |||
for i := 1; ; i++ { | |||
ms, _, err := g.client.Issues.ListMilestones(g.ctx, g.repoOwner, g.repoName, | |||
&github.MilestoneListOptions{ | |||
State: "all", | |||
ListOptions: github.ListOptions{ | |||
Page: i, | |||
PerPage: perPage, | |||
}}) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, m := range ms { | |||
var desc string | |||
if m.Description != nil { | |||
desc = *m.Description | |||
} | |||
var state = "open" | |||
if m.State != nil { | |||
state = *m.State | |||
} | |||
milestones = append(milestones, &base.Milestone{ | |||
Title: *m.Title, | |||
Description: desc, | |||
Deadline: m.DueOn, | |||
State: state, | |||
Created: *m.CreatedAt, | |||
Updated: m.UpdatedAt, | |||
Closed: m.ClosedAt, | |||
}) | |||
} | |||
if len(ms) < perPage { | |||
break | |||
} | |||
} | |||
return milestones, nil | |||
} | |||
func convertGithubLabel(label *github.Label) *base.Label { | |||
var desc string | |||
if label.Description != nil { | |||
desc = *label.Description | |||
} | |||
return &base.Label{ | |||
Name: *label.Name, | |||
Color: *label.Color, | |||
Description: desc, | |||
} | |||
} | |||
// GetLabels returns labels | |||
func (g *GithubDownloaderV3) GetLabels() ([]*base.Label, error) { | |||
var perPage = 100 | |||
var labels = make([]*base.Label, 0, perPage) | |||
for i := 1; ; i++ { | |||
ls, _, err := g.client.Issues.ListLabels(g.ctx, g.repoOwner, g.repoName, | |||
&github.ListOptions{ | |||
Page: i, | |||
PerPage: perPage, | |||
}) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, label := range ls { | |||
labels = append(labels, convertGithubLabel(label)) | |||
} | |||
if len(ls) < perPage { | |||
break | |||
} | |||
} | |||
return labels, nil | |||
} | |||
func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease) *base.Release { | |||
var ( | |||
name string | |||
desc string | |||
) | |||
if rel.Body != nil { | |||
desc = *rel.Body | |||
} | |||
if rel.Name != nil { | |||
name = *rel.Name | |||
} | |||
r := &base.Release{ | |||
TagName: *rel.TagName, | |||
TargetCommitish: *rel.TargetCommitish, | |||
Name: name, | |||
Body: desc, | |||
Draft: *rel.Draft, | |||
Prerelease: *rel.Prerelease, | |||
Created: rel.CreatedAt.Time, | |||
Published: rel.PublishedAt.Time, | |||
} | |||
for _, asset := range rel.Assets { | |||
u, _ := url.Parse(*asset.BrowserDownloadURL) | |||
u.User = url.UserPassword(g.userName, g.password) | |||
r.Assets = append(r.Assets, base.ReleaseAsset{ | |||
URL: u.String(), | |||
Name: *asset.Name, | |||
ContentType: asset.ContentType, | |||
Size: asset.Size, | |||
DownloadCount: asset.DownloadCount, | |||
Created: asset.CreatedAt.Time, | |||
Updated: asset.UpdatedAt.Time, | |||
}) | |||
} | |||
return r | |||
} | |||
// GetReleases returns releases | |||
func (g *GithubDownloaderV3) GetReleases() ([]*base.Release, error) { | |||
var perPage = 100 | |||
var releases = make([]*base.Release, 0, perPage) | |||
for i := 1; ; i++ { | |||
ls, _, err := g.client.Repositories.ListReleases(g.ctx, g.repoOwner, g.repoName, | |||
&github.ListOptions{ | |||
Page: i, | |||
PerPage: perPage, | |||
}) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, release := range ls { | |||
releases = append(releases, g.convertGithubRelease(release)) | |||
} | |||
if len(ls) < perPage { | |||
break | |||
} | |||
} | |||
return releases, nil | |||
} | |||
func convertGithubReactions(reactions *github.Reactions) *base.Reactions { | |||
return &base.Reactions{ | |||
TotalCount: *reactions.TotalCount, | |||
PlusOne: *reactions.PlusOne, | |||
MinusOne: *reactions.MinusOne, | |||
Laugh: *reactions.Laugh, | |||
Confused: *reactions.Confused, | |||
Heart: *reactions.Heart, | |||
Hooray: *reactions.Hooray, | |||
} | |||
} | |||
// GetIssues returns issues according start and limit | |||
func (g *GithubDownloaderV3) GetIssues(start, limit int) ([]*base.Issue, error) { | |||
var perPage = 100 | |||
opt := &github.IssueListByRepoOptions{ | |||
Sort: "created", | |||
Direction: "asc", | |||
State: "all", | |||
ListOptions: github.ListOptions{ | |||
PerPage: perPage, | |||
}, | |||
} | |||
var allIssues = make([]*base.Issue, 0, limit) | |||
for { | |||
issues, resp, err := g.client.Issues.ListByRepo(g.ctx, g.repoOwner, g.repoName, opt) | |||
if err != nil { | |||
return nil, fmt.Errorf("error while listing repos: %v", err) | |||
} | |||
for _, issue := range issues { | |||
if issue.IsPullRequest() { | |||
continue | |||
} | |||
var body string | |||
if issue.Body != nil { | |||
body = *issue.Body | |||
} | |||
var milestone string | |||
if issue.Milestone != nil { | |||
milestone = *issue.Milestone.Title | |||
} | |||
var labels = make([]*base.Label, 0, len(issue.Labels)) | |||
for _, l := range issue.Labels { | |||
labels = append(labels, convertGithubLabel(&l)) | |||
} | |||
var reactions *base.Reactions | |||
if issue.Reactions != nil { | |||
reactions = convertGithubReactions(issue.Reactions) | |||
} | |||
var email string | |||
if issue.User.Email != nil { | |||
email = *issue.User.Email | |||
} | |||
allIssues = append(allIssues, &base.Issue{ | |||
Title: *issue.Title, | |||
Number: int64(*issue.Number), | |||
PosterName: *issue.User.Login, | |||
PosterEmail: email, | |||
Content: body, | |||
Milestone: milestone, | |||
State: *issue.State, | |||
Created: *issue.CreatedAt, | |||
Labels: labels, | |||
Reactions: reactions, | |||
Closed: issue.ClosedAt, | |||
IsLocked: *issue.Locked, | |||
}) | |||
if len(allIssues) >= limit { | |||
return allIssues, nil | |||
} | |||
} | |||
if resp.NextPage == 0 { | |||
break | |||
} | |||
opt.Page = resp.NextPage | |||
} | |||
return allIssues, nil | |||
} | |||
// GetComments returns comments according issueNumber | |||
func (g *GithubDownloaderV3) GetComments(issueNumber int64) ([]*base.Comment, error) { | |||
var allComments = make([]*base.Comment, 0, 100) | |||
opt := &github.IssueListCommentsOptions{ | |||
Sort: "created", | |||
Direction: "asc", | |||
ListOptions: github.ListOptions{ | |||
PerPage: 100, | |||
}, | |||
} | |||
for { | |||
comments, resp, err := g.client.Issues.ListComments(g.ctx, g.repoOwner, g.repoName, int(issueNumber), opt) | |||
if err != nil { | |||
return nil, fmt.Errorf("error while listing repos: %v", err) | |||
} | |||
for _, comment := range comments { | |||
var email string | |||
if comment.User.Email != nil { | |||
email = *comment.User.Email | |||
} | |||
var reactions *base.Reactions | |||
if comment.Reactions != nil { | |||
reactions = convertGithubReactions(comment.Reactions) | |||
} | |||
allComments = append(allComments, &base.Comment{ | |||
PosterName: *comment.User.Login, | |||
PosterEmail: email, | |||
Content: *comment.Body, | |||
Created: *comment.CreatedAt, | |||
Reactions: reactions, | |||
}) | |||
} | |||
if resp.NextPage == 0 { | |||
break | |||
} | |||
opt.Page = resp.NextPage | |||
} | |||
return allComments, nil | |||
} | |||
// GetPullRequests returns pull requests according start and limit | |||
func (g *GithubDownloaderV3) GetPullRequests(start, limit int) ([]*base.PullRequest, error) { | |||
opt := &github.PullRequestListOptions{ | |||
Sort: "created", | |||
Direction: "asc", | |||
State: "all", | |||
ListOptions: github.ListOptions{ | |||
PerPage: 100, | |||
}, | |||
} | |||
var allPRs = make([]*base.PullRequest, 0, 100) | |||
for { | |||
prs, resp, err := g.client.PullRequests.List(g.ctx, g.repoOwner, g.repoName, opt) | |||
if err != nil { | |||
return nil, fmt.Errorf("error while listing repos: %v", err) | |||
} | |||
for _, pr := range prs { | |||
var body string | |||
if pr.Body != nil { | |||
body = *pr.Body | |||
} | |||
var milestone string | |||
if pr.Milestone != nil { | |||
milestone = *pr.Milestone.Title | |||
} | |||
var labels = make([]*base.Label, 0, len(pr.Labels)) | |||
for _, l := range pr.Labels { | |||
labels = append(labels, convertGithubLabel(l)) | |||
} | |||
// FIXME: This API missing reactions, we may need another extra request to get reactions | |||
var email string | |||
if pr.User.Email != nil { | |||
email = *pr.User.Email | |||
} | |||
var merged bool | |||
// pr.Merged is not valid, so use MergedAt to test if it's merged | |||
if pr.MergedAt != nil { | |||
merged = true | |||
} | |||
var headRepoName string | |||
var cloneURL string | |||
if pr.Head.Repo != nil { | |||
headRepoName = *pr.Head.Repo.Name | |||
cloneURL = *pr.Head.Repo.CloneURL | |||
} | |||
var mergeCommitSHA string | |||
if pr.MergeCommitSHA != nil { | |||
mergeCommitSHA = *pr.MergeCommitSHA | |||
} | |||
allPRs = append(allPRs, &base.PullRequest{ | |||
Title: *pr.Title, | |||
Number: int64(*pr.Number), | |||
PosterName: *pr.User.Login, | |||
PosterEmail: email, | |||
Content: body, | |||
Milestone: milestone, | |||
State: *pr.State, | |||
Created: *pr.CreatedAt, | |||
Closed: pr.ClosedAt, | |||
Labels: labels, | |||
Merged: merged, | |||
MergeCommitSHA: mergeCommitSHA, | |||
MergedTime: pr.MergedAt, | |||
IsLocked: pr.ActiveLockReason != nil, | |||
Head: base.PullRequestBranch{ | |||
Ref: *pr.Head.Ref, | |||
SHA: *pr.Head.SHA, | |||
RepoName: headRepoName, | |||
OwnerName: *pr.Head.User.Login, | |||
CloneURL: cloneURL, | |||
}, | |||
Base: base.PullRequestBranch{ | |||
Ref: *pr.Base.Ref, | |||
SHA: *pr.Base.SHA, | |||
RepoName: *pr.Base.Repo.Name, | |||
OwnerName: *pr.Base.User.Login, | |||
}, | |||
PatchURL: *pr.PatchURL, | |||
}) | |||
if len(allPRs) >= limit { | |||
return allPRs, nil | |||
} | |||
} | |||
if resp.NextPage == 0 { | |||
break | |||
} | |||
opt.Page = resp.NextPage | |||
} | |||
return allPRs, nil | |||
} |
@ -0,0 +1,448 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"testing" | |||
"time" | |||
"code.gitea.io/gitea/modules/migrations/base" | |||
"github.com/stretchr/testify/assert" | |||
) | |||
func assertMilestoneEqual(t *testing.T, title, dueOn, created, updated, closed, state string, ms *base.Milestone) { | |||
var tmPtr *time.Time | |||
if dueOn != "" { | |||
tm, err := time.Parse("2006-01-02 15:04:05 -0700 MST", dueOn) | |||
assert.NoError(t, err) | |||
tmPtr = &tm | |||
} | |||
var ( | |||
createdTM time.Time | |||
updatedTM *time.Time | |||
closedTM *time.Time | |||
) | |||
if created != "" { | |||
var err error | |||
createdTM, err = time.Parse("2006-01-02 15:04:05 -0700 MST", created) | |||
assert.NoError(t, err) | |||
} | |||
if updated != "" { | |||
updatedTemp, err := time.Parse("2006-01-02 15:04:05 -0700 MST", updated) | |||
assert.NoError(t, err) | |||
updatedTM = &updatedTemp | |||
} | |||
if closed != "" { | |||
closedTemp, err := time.Parse("2006-01-02 15:04:05 -0700 MST", closed) | |||
assert.NoError(t, err) | |||
closedTM = &closedTemp | |||
} | |||
assert.EqualValues(t, &base.Milestone{ | |||
Title: title, | |||
Deadline: tmPtr, | |||
State: state, | |||
Created: createdTM, | |||
Updated: updatedTM, | |||
Closed: closedTM, | |||
}, ms) | |||
} | |||
func assertLabelEqual(t *testing.T, name, color string, label *base.Label) { | |||
assert.EqualValues(t, &base.Label{ | |||
Name: name, | |||
Color: color, | |||
}, label) | |||
} | |||
func TestGitHubDownloadRepo(t *testing.T) { | |||
downloader := NewGithubDownloaderV3("", "", "go-gitea", "gitea") | |||
repo, err := downloader.GetRepoInfo() | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, &base.Repository{ | |||
Name: "gitea", | |||
Owner: "go-gitea", | |||
Description: "Git with a cup of tea, painless self-hosted git service", | |||
CloneURL: "https://github.com/go-gitea/gitea.git", | |||
}, repo) | |||
milestones, err := downloader.GetMilestones() | |||
assert.NoError(t, err) | |||
// before this tool release, we have 39 milestones on github.com/go-gitea/gitea | |||
assert.True(t, len(milestones) >= 39) | |||
for _, milestone := range milestones { | |||
switch milestone.Title { | |||
case "1.0.0": | |||
assertMilestoneEqual(t, "1.0.0", "2016-12-23 08:00:00 +0000 UTC", | |||
"2016-11-02 18:06:55 +0000 UTC", | |||
"2016-12-29 10:26:00 +0000 UTC", | |||
"2016-12-24 00:40:56 +0000 UTC", | |||
"closed", milestone) | |||
case "1.1.0": | |||
assertMilestoneEqual(t, "1.1.0", "2017-02-24 08:00:00 +0000 UTC", | |||
"2016-11-03 08:40:10 +0000 UTC", | |||
"2017-06-15 05:04:36 +0000 UTC", | |||
"2017-03-09 21:22:21 +0000 UTC", | |||
"closed", milestone) | |||
case "1.2.0": | |||
assertMilestoneEqual(t, "1.2.0", "2017-04-24 07:00:00 +0000 UTC", | |||
"2016-11-03 08:40:15 +0000 UTC", | |||
"2017-12-10 02:43:29 +0000 UTC", | |||
"2017-10-12 08:24:28 +0000 UTC", | |||
"closed", milestone) | |||
case "1.3.0": | |||
assertMilestoneEqual(t, "1.3.0", "2017-11-29 08:00:00 +0000 UTC", | |||
"2017-03-03 08:08:59 +0000 UTC", | |||
"2017-12-04 07:48:44 +0000 UTC", | |||
"2017-11-29 18:39:00 +0000 UTC", | |||
"closed", milestone) | |||
case "1.4.0": | |||
assertMilestoneEqual(t, "1.4.0", "2018-01-25 08:00:00 +0000 UTC", | |||
"2017-08-23 11:02:37 +0000 UTC", | |||
"2018-03-25 20:01:56 +0000 UTC", | |||
"2018-03-25 20:01:56 +0000 UTC", | |||
"closed", milestone) | |||
case "1.5.0": | |||
assertMilestoneEqual(t, "1.5.0", "2018-06-15 07:00:00 +0000 UTC", | |||
"2017-12-30 04:21:56 +0000 UTC", | |||
"2018-09-05 16:34:22 +0000 UTC", | |||
"2018-08-11 08:45:01 +0000 UTC", | |||
"closed", milestone) | |||
case "1.6.0": | |||
assertMilestoneEqual(t, "1.6.0", "2018-09-25 07:00:00 +0000 UTC", | |||
"2018-05-11 05:37:01 +0000 UTC", | |||
"2019-01-27 19:21:22 +0000 UTC", | |||
"2018-11-23 13:23:16 +0000 UTC", | |||
"closed", milestone) | |||
case "1.7.0": | |||
assertMilestoneEqual(t, "1.7.0", "2018-12-25 08:00:00 +0000 UTC", | |||
"2018-08-28 14:20:14 +0000 UTC", | |||
"2019-01-27 11:30:24 +0000 UTC", | |||
"2019-01-23 08:58:23 +0000 UTC", | |||
"closed", milestone) | |||
} | |||
} | |||
labels, err := downloader.GetLabels() | |||
assert.NoError(t, err) | |||
assert.True(t, len(labels) >= 48) | |||
for _, l := range labels { | |||
switch l.Name { | |||
case "backport/v1.7": | |||
assertLabelEqual(t, "backport/v1.7", "fbca04", l) | |||
case "backport/v1.8": | |||
assertLabelEqual(t, "backport/v1.8", "fbca04", l) | |||
case "kind/api": | |||
assertLabelEqual(t, "kind/api", "5319e7", l) | |||
case "kind/breaking": | |||
assertLabelEqual(t, "kind/breaking", "fbca04", l) | |||
case "kind/bug": | |||
assertLabelEqual(t, "kind/bug", "ee0701", l) | |||
case "kind/docs": | |||
assertLabelEqual(t, "kind/docs", "c2e0c6", l) | |||
case "kind/enhancement": | |||
assertLabelEqual(t, "kind/enhancement", "84b6eb", l) | |||
case "kind/feature": | |||
assertLabelEqual(t, "kind/feature", "006b75", l) | |||
} | |||
} | |||
releases, err := downloader.GetReleases() | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, []*base.Release{ | |||
{ | |||
TagName: "v0.9.99", | |||
TargetCommitish: "master", | |||
Name: "fork", | |||
Body: "Forked source from Gogs into Gitea\n", | |||
Created: time.Date(2016, 10, 17, 02, 17, 59, 0, time.UTC), | |||
Published: time.Date(2016, 11, 17, 15, 37, 0, 0, time.UTC), | |||
}, | |||
}, releases[len(releases)-1:]) | |||
// downloader.GetIssues() | |||
issues, err := downloader.GetIssues(0, 3) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 3, len(issues)) | |||
var ( | |||
closed1 = time.Date(2018, 10, 23, 02, 57, 43, 0, time.UTC) | |||
) | |||
assert.EqualValues(t, []*base.Issue{ | |||
{ | |||
Number: 6, | |||
Title: "Contribution system: History heatmap for user", | |||
Content: "Hi guys,\r\n\r\nI think that is a possible feature, a history heatmap similar to github or gitlab.\r\nActually exists a plugin called Calendar HeatMap. I used this on mine project to heat application log and worked fine here.\r\nThen, is only a idea, what you think? :)\r\n\r\nhttp://cal-heatmap.com/\r\nhttps://github.com/wa0x6e/cal-heatmap\r\n\r\nReference: https://github.com/gogits/gogs/issues/1640", | |||
Milestone: "1.7.0", | |||
PosterName: "joubertredrat", | |||
State: "closed", | |||
Created: time.Date(2016, 11, 02, 18, 51, 55, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/feature", | |||
Color: "006b75", | |||
}, | |||
{ | |||
Name: "kind/ui", | |||
Color: "fef2c0", | |||
}, | |||
}, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 0, | |||
PlusOne: 0, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 0, | |||
Heart: 0, | |||
Hooray: 0, | |||
}, | |||
Closed: &closed1, | |||
}, | |||
{ | |||
Number: 7, | |||
Title: "display page revisions on wiki", | |||
Content: "Hi guys,\r\n\r\nWiki on Gogs is very fine, I liked a lot, but I think that is good idea to be possible see other revisions from page as a page history.\r\n\r\nWhat you think?\r\n\r\nReference: https://github.com/gogits/gogs/issues/2991", | |||
Milestone: "1.x.x", | |||
PosterName: "joubertredrat", | |||
State: "open", | |||
Created: time.Date(2016, 11, 02, 18, 57, 32, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/feature", | |||
Color: "006b75", | |||
}, | |||
{ | |||
Name: "reviewed/confirmed", | |||
Color: "8d9b12", | |||
Description: "Issue has been reviewed and confirmed to be present or accepted to be implemented", | |||
}, | |||
}, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 6, | |||
PlusOne: 5, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 1, | |||
Heart: 0, | |||
Hooray: 0, | |||
}, | |||
}, | |||
{ | |||
Number: 8, | |||
Title: "audit logs", | |||
Content: "Hi,\r\n\r\nI think that is good idea to have user operation log to admin see what the user is doing at Gogs. Similar to example below\r\n\r\n| user | operation | information |\r\n| --- | --- | --- |\r\n| joubertredrat | repo.create | Create repo MyProjectData |\r\n| joubertredrat | user.settings | Edit settings |\r\n| tboerger | repo.fork | Create Fork from MyProjectData to ForkMyProjectData |\r\n| bkcsoft | repo.remove | Remove repo MySource |\r\n| tboerger | admin.auth | Edit auth LDAP org-connection |\r\n\r\nThis resource can be used on user page too, as user activity, set that log row is public (repo._) or private (user._, admin.*) and display only public activity.\r\n\r\nWhat you think?\r\n\r\n[Chat summary from March 14, 2017](https://github.com/go-gitea/gitea/issues/8#issuecomment-286463807)\r\n\r\nReferences:\r\nhttps://github.com/gogits/gogs/issues/3016", | |||
Milestone: "1.x.x", | |||
PosterName: "joubertredrat", | |||
State: "open", | |||
Created: time.Date(2016, 11, 02, 18, 59, 20, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/feature", | |||
Color: "006b75", | |||
}, | |||
{ | |||
Name: "kind/proposal", | |||
Color: "5319e7", | |||
}, | |||
}, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 9, | |||
PlusOne: 8, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 0, | |||
Heart: 1, | |||
Hooray: 0, | |||
}, | |||
}, | |||
}, issues) | |||
// downloader.GetComments() | |||
comments, err := downloader.GetComments(6) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 35, len(comments)) | |||
assert.EqualValues(t, []*base.Comment{ | |||
{ | |||
PosterName: "bkcsoft", | |||
Created: time.Date(2016, 11, 02, 18, 59, 48, 0, time.UTC), | |||
Content: `I would prefer a solution that is in the backend, unless it's required to have it update without reloading. Unfortunately I can't seem to find anything that does that :unamused: | |||
Also this would _require_ caching, since it will fetch huge amounts of data from disk... | |||
`, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 2, | |||
PlusOne: 2, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 0, | |||
Heart: 0, | |||
Hooray: 0, | |||
}, | |||
}, | |||
{ | |||
PosterName: "joubertredrat", | |||
Created: time.Date(2016, 11, 02, 19, 16, 56, 0, time.UTC), | |||
Content: `Yes, this plugin build on front-end, with backend I don't know too, but we can consider make component for this. | |||
In my case I use ajax to get data, but build on frontend anyway | |||
`, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 0, | |||
PlusOne: 0, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 0, | |||
Heart: 0, | |||
Hooray: 0, | |||
}, | |||
}, | |||
{ | |||
PosterName: "xinity", | |||
Created: time.Date(2016, 11, 03, 13, 04, 56, 0, time.UTC), | |||
Content: `following @bkcsoft retention strategy in cache is a must if we don't want gitea to waste ressources. | |||
something like in the latest 15days could be enough don't you think ? | |||
`, | |||
Reactions: &base.Reactions{ | |||
TotalCount: 2, | |||
PlusOne: 2, | |||
MinusOne: 0, | |||
Laugh: 0, | |||
Confused: 0, | |||
Heart: 0, | |||
Hooray: 0, | |||
}, | |||
}, | |||
}, comments[:3]) | |||
// downloader.GetPullRequests() | |||
prs, err := downloader.GetPullRequests(0, 3) | |||
assert.NoError(t, err) | |||
assert.EqualValues(t, 3, len(prs)) | |||
closed1 = time.Date(2016, 11, 02, 18, 22, 21, 0, time.UTC) | |||
var ( | |||
closed2 = time.Date(2016, 11, 03, 8, 06, 27, 0, time.UTC) | |||
closed3 = time.Date(2016, 11, 02, 18, 22, 31, 0, time.UTC) | |||
) | |||
var ( | |||
merged1 = time.Date(2016, 11, 02, 18, 22, 21, 0, time.UTC) | |||
merged2 = time.Date(2016, 11, 03, 8, 06, 27, 0, time.UTC) | |||
merged3 = time.Date(2016, 11, 02, 18, 22, 31, 0, time.UTC) | |||
) | |||
assert.EqualValues(t, []*base.PullRequest{ | |||
{ | |||
Number: 1, | |||
Title: "Rename import paths: \"github.com/gogits/gogs\" -> \"github.com/go-gitea/gitea\"", | |||
Content: "", | |||
Milestone: "1.0.0", | |||
PosterName: "andreynering", | |||
State: "closed", | |||
Created: time.Date(2016, 11, 02, 17, 01, 19, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/enhancement", | |||
Color: "84b6eb", | |||
}, | |||
{ | |||
Name: "lgtm/done", | |||
Color: "0e8a16", | |||
}, | |||
}, | |||
PatchURL: "https://github.com/go-gitea/gitea/pull/1.patch", | |||
Head: base.PullRequestBranch{ | |||
Ref: "import-paths", | |||
SHA: "1b0ec3208db8501acba44a137c009a5a126ebaa9", | |||
OwnerName: "andreynering", | |||
}, | |||
Base: base.PullRequestBranch{ | |||
Ref: "master", | |||
SHA: "6bcff7828f117af8d51285ce3acba01a7e40a867", | |||
OwnerName: "go-gitea", | |||
RepoName: "gitea", | |||
}, | |||
Closed: &closed1, | |||
Merged: true, | |||
MergedTime: &merged1, | |||
MergeCommitSHA: "142d35e8d2baec230ddb565d1265940d59141fab", | |||
}, | |||
{ | |||
Number: 2, | |||
Title: "Fix sender of issue notifications", | |||
Content: "It is the FROM field in mailer configuration that needs be used,\r\nnot the USER field, which is for authentication.\r\n\r\nMigrated from https://github.com/gogits/gogs/pull/3616\r\n", | |||
Milestone: "1.0.0", | |||
PosterName: "strk", | |||
State: "closed", | |||
Created: time.Date(2016, 11, 02, 17, 24, 19, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/bug", | |||
Color: "ee0701", | |||
}, | |||
{ | |||
Name: "lgtm/done", | |||
Color: "0e8a16", | |||
}, | |||
}, | |||
PatchURL: "https://github.com/go-gitea/gitea/pull/2.patch", | |||
Head: base.PullRequestBranch{ | |||
Ref: "proper-from-on-issue-mail", | |||
SHA: "af03d00780a6ee70c58e135c6679542cde4f8d50", | |||
RepoName: "gogs", | |||
OwnerName: "strk", | |||
CloneURL: "https://github.com/strk/gogs.git", | |||
}, | |||
Base: base.PullRequestBranch{ | |||
Ref: "develop", | |||
SHA: "5c5424301443ffa3659737d12de48ab1dfe39a00", | |||
OwnerName: "go-gitea", | |||
RepoName: "gitea", | |||
}, | |||
Closed: &closed2, | |||
Merged: true, | |||
MergedTime: &merged2, | |||
MergeCommitSHA: "d8de2beb5b92d02a0597ba7c7803839380666653", | |||
}, | |||
{ | |||
Number: 3, | |||
Title: "Use proper url for libravatar dep", | |||
Content: "Fetch go-libravatar from its official source, rather than from an unmaintained fork\r\n", | |||
Milestone: "1.0.0", | |||
PosterName: "strk", | |||
State: "closed", | |||
Created: time.Date(2016, 11, 02, 17, 34, 31, 0, time.UTC), | |||
Labels: []*base.Label{ | |||
{ | |||
Name: "kind/enhancement", | |||
Color: "84b6eb", | |||
}, | |||
{ | |||
Name: "lgtm/done", | |||
Color: "0e8a16", | |||
}, | |||
}, | |||
PatchURL: "https://github.com/go-gitea/gitea/pull/3.patch", | |||
Head: base.PullRequestBranch{ | |||
Ref: "libravatar-proper-url", | |||
SHA: "d59a48a2550abd4129b96d38473941b895a4859b", | |||
RepoName: "gogs", | |||
OwnerName: "strk", | |||
CloneURL: "https://github.com/strk/gogs.git", | |||
}, | |||
Base: base.PullRequestBranch{ | |||
Ref: "develop", | |||
SHA: "6bcff7828f117af8d51285ce3acba01a7e40a867", | |||
OwnerName: "go-gitea", | |||
RepoName: "gitea", | |||
}, | |||
Closed: &closed3, | |||
Merged: true, | |||
MergedTime: &merged3, | |||
MergeCommitSHA: "5c5424301443ffa3659737d12de48ab1dfe39a00", | |||
}, | |||
}, prs) | |||
} |
@ -0,0 +1,17 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"path/filepath" | |||
"testing" | |||
"code.gitea.io/gitea/models" | |||
) | |||
func TestMain(m *testing.M) { | |||
models.MainTest(m, filepath.Join("..", "..")) | |||
} |
@ -0,0 +1,205 @@ | |||
// Copyright 2019 The Gitea Authors. All rights reserved. | |||
// Copyright 2018 Jonas Franz. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package migrations | |||
import ( | |||
"fmt" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/migrations/base" | |||
) | |||
// MigrateOptions is equal to base.MigrateOptions | |||
type MigrateOptions = base.MigrateOptions | |||
var ( | |||
factories []base.DownloaderFactory | |||
) | |||
// RegisterDownloaderFactory registers a downloader factory | |||
func RegisterDownloaderFactory(factory base.DownloaderFactory) { | |||
factories = append(factories, factory) | |||
} | |||
// MigrateRepository migrate repository according MigrateOptions | |||
func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) { | |||
var ( | |||
downloader base.Downloader | |||
uploader = NewGiteaLocalUploader(doer, ownerName, opts.Name) | |||
) | |||
for _, factory := range factories { | |||
if match, err := factory.Match(opts); err != nil { | |||
return nil, err | |||
} else if match { | |||
downloader, err = factory.New(opts) | |||
if err != nil { | |||
return nil, err | |||
} | |||
break | |||
} | |||
} | |||
if downloader == nil { | |||
opts.Wiki = true | |||
opts.Milestones = false | |||
opts.Labels = false | |||
opts.Releases = false | |||
opts.Comments = false | |||
opts.Issues = false | |||
opts.PullRequests = false | |||
downloader = NewPlainGitDownloader(ownerName, opts.Name, opts.RemoteURL) | |||
log.Trace("Will migrate from git: %s", opts.RemoteURL) | |||
} | |||
if err := migrateRepository(downloader, uploader, opts); err != nil { | |||
if err1 := uploader.Rollback(); err1 != nil { | |||
log.Error("rollback failed: %v", err1) | |||
} | |||
return nil, err | |||
} | |||
return uploader.repo, nil | |||
} | |||
// migrateRepository will download informations and upload to Uploader, this is a simple | |||
// process for small repository. For a big repository, save all the data to disk | |||
// before upload is better | |||
func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions) error { | |||
repo, err := downloader.GetRepoInfo() | |||
if err != nil { | |||
return err | |||
} | |||
repo.IsPrivate = opts.Private | |||
repo.IsMirror = opts.Mirror | |||
log.Trace("migrating git data") | |||
if err := uploader.CreateRepo(repo, opts.Wiki); err != nil { | |||
return err | |||
} | |||
if opts.Milestones { | |||
log.Trace("migrating milestones") | |||
milestones, err := downloader.GetMilestones() | |||
if err != nil { | |||
return err | |||
} | |||
for _, milestone := range milestones { | |||
if err := uploader.CreateMilestone(milestone); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
if opts.Labels { | |||
log.Trace("migrating labels") | |||
labels, err := downloader.GetLabels() | |||
if err != nil { | |||
return err | |||
} | |||
for _, label := range labels { | |||
if err := uploader.CreateLabel(label); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
if opts.Releases { | |||
log.Trace("migrating releases") | |||
releases, err := downloader.GetReleases() | |||
if err != nil { | |||
return err | |||
} | |||
for _, release := range releases { | |||
if err := uploader.CreateRelease(release); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
if opts.Issues { | |||
log.Trace("migrating issues and comments") | |||
for { | |||
issues, err := downloader.GetIssues(0, 100) | |||
if err != nil { | |||
return err | |||
} | |||
for _, issue := range issues { | |||
if !opts.IgnoreIssueAuthor { | |||
issue.Content = fmt.Sprintf("Author: @%s \n\n%s", issue.PosterName, issue.Content) | |||
} | |||
if err := uploader.CreateIssue(issue); err != nil { | |||
return err | |||
} | |||
if !opts.Comments { | |||
continue | |||
} | |||
comments, err := downloader.GetComments(issue.Number) | |||
if err != nil { | |||
return err | |||
} | |||
for _, comment := range comments { | |||
if !opts.IgnoreIssueAuthor { | |||
comment.Content = fmt.Sprintf("Author: @%s \n\n%s", comment.PosterName, comment.Content) | |||
} | |||
if err := uploader.CreateComment(issue.Number, comment); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
if len(issues) < 100 { | |||
break | |||
} | |||
} | |||
} | |||
if opts.PullRequests { | |||
log.Trace("migrating pull requests and comments") | |||
for { | |||
prs, err := downloader.GetPullRequests(0, 100) | |||
if err != nil { | |||
return err | |||
} | |||
for _, pr := range prs { | |||
if !opts.IgnoreIssueAuthor { | |||
pr.Content = fmt.Sprintf("Author: @%s \n\n%s", pr.PosterName, pr.Content) | |||
} | |||
if err := uploader.CreatePullRequest(pr); err != nil { | |||
return err | |||
} | |||
if !opts.Comments { | |||
continue | |||
} | |||
comments, err := downloader.GetComments(pr.Number) | |||
if err != nil { | |||
return err | |||
} | |||
for _, comment := range comments { | |||
if !opts.IgnoreIssueAuthor { | |||
comment.Content = fmt.Sprintf("Author: @%s \n\n%s", comment.PosterName, comment.Content) | |||
} | |||
if err := uploader.CreateComment(pr.Number, comment); err != nil { | |||
return err | |||
} | |||
} | |||
} | |||
if len(prs) < 100 { | |||
break | |||
} | |||
} | |||
} | |||
return nil | |||
} |
@ -0,0 +1,229 @@ | |||
# This is the official list of go-github authors for copyright purposes. | |||
# | |||
# This does not necessarily list everyone who has contributed code, since in | |||
# some cases, their employer may be the copyright holder. To see the full list | |||
# of contributors, see the revision history in source control or | |||
# https://github.com/google/go-github/graphs/contributors. | |||
# | |||
# Authors who wish to be recognized in this file should add themselves (or | |||
# their employer, as appropriate). | |||
178inaba <masahiro.furudate@gmail.com> | |||
Abhinav Gupta <mail@abhinavg.net> | |||
Ahmed Hagy <a.akram93@gmail.com> | |||
Ainsley Chong <ainsley.chong@gmail.com> | |||
Akeda Bagus <akeda@x-team.com> | |||
Akhil Mohan <akhilerm@gmail.com> | |||
Alec Thomas <alec@swapoff.org> | |||
Aleks Clark <aleks.clark@gmail.com> | |||
Alex Bramley <a.bramley@gmail.com> | |||
Alexander Harkness <me@bearbin.net> | |||
Allen Sun <shlallen1990@gmail.com> | |||
Amey Sakhadeo <me@ameyms.com> | |||
Andreas Garnæs <https://github.com/andreas> | |||
Andrew Ryabchun <aryabchun@mail.ua> | |||
Andy Grunwald <andygrunwald@gmail.com> | |||
Andy Hume <andyhume@gmail.com> | |||
Andy Lindeman <andy@lindeman.io> | |||
Anshuman Bhartiya <anshuman.bhartiya@gmail.com> | |||
Antoine <antoine.tu@mail.mcgill.ca> | |||
Antoine Pelisse <apelisse@gmail.com> | |||
Anubha Kushwaha <anubha_bt2k14@dtu.ac.in> | |||
appilon <apilon@hashicorp.com> | |||
Aravind <aravindkp@outlook.in> | |||
Arda Kuyumcu <kuyumcuarda@gmail.com> | |||
Arıl Bozoluk <arilbozoluk@hotmail.com> | |||
Austin Dizzy <dizzy@wow.com> | |||
Ben Batha <bhbatha@gmail.com> | |||
Benjamen Keroack <benjamen@dollarshaveclub.com> | |||
Beshr Kayali <beshrkayali@gmail.com> | |||
Beyang Liu <beyang.liu@gmail.com> | |||
Billy Lynch <wlynch92@gmail.com> | |||
Björn Häuser <b.haeuser@rebuy.de> | |||
Brad Harris <bmharris@gmail.com> | |||
Brad Moylan <moylan.brad@gmail.com> | |||
Bradley Falzon <brad@teambrad.net> | |||
Brandon Cook <phylake@gmail.com> | |||
Brian Egizi <brian@mojotech.com> | |||
Bryan Boreham <bryan@weave.works> | |||
Cami Diez <diezcami@gmail.com> | |||
Carlos Alexandro Becker <caarlos0@gmail.com> | |||
chandresh-pancholi <chandreshpancholi007@gmail.com> | |||
Charles Fenwick Elliott <Charles@FenwickElliott.io> | |||
Charlie Yan <charlieyan08@gmail.com> | |||
Chris King <chriskingnet@gmail.com> | |||
Chris Roche <chris@vsco.co> | |||
Chris Schaefer <chris@dtzq.com> | |||
Christoph Sassenberg <defsprite@gmail.com> | |||
Colin Misare <github.com/cmisare> | |||
Craig Peterson <cpeterson@stackoverflow.com> | |||
Cristian Maglie <c.maglie@bug.st> | |||
Daehyeok Mun <daehyeok@gmail.com> | |||
Daniel Leavitt <daniel.leavitt@gmail.com> | |||
Daniel Nilsson <daniel.nilsson1989@gmail.com> | |||
Dave Du Cros <davidducros@gmail.com> | |||
Dave Henderson <dhenderson@gmail.com> | |||
David Deng <daviddengcn@gmail.com> | |||
David Jannotta <djannotta@gmail.com> | |||
Davide Zipeto <dawez1@gmail.com> | |||
Dennis Webb <dennis@bluesentryit.com> | |||
Dhi Aurrahman <diorahman@rockybars.com> | |||
Diego Lapiduz <diego.lapiduz@cfpb.gov> | |||
Dmitri Shuralyov <shurcooL@gmail.com> | |||
dmnlk <seikima2demon@gmail.com> | |||
Don Petersen <don@donpetersen.net> | |||
Doug Turner <doug.turner@gmail.com> | |||
Drew Fradette <drew.fradette@gmail.com> | |||
Eli Uriegas <seemethere101@gmail.com> | |||
Elliott Beach <elliott2.71828@gmail.com> | |||
Emerson Wood <emersonwood94@gmail.com> | |||
eperm <staffordworrell@gmail.com> | |||
Erick Fejta <erick@fejta.com> | |||
erwinvaneyk <erwinvaneyk@gmail.com> | |||
Fabrice <fabrice.vaillant@student.ecp.fr> | |||
Felix Geisendörfer <felix@debuggable.com> | |||
Filippo Valsorda <hi@filippo.io> | |||
Florian Forster <ff@octo.it> | |||
Francesc Gil <xescugil@gmail.com> | |||
Francis <hello@francismakes.com> | |||
Fredrik Jönsson <fredrik.jonsson@izettle.com> | |||
Garrett Squire <garrettsquire@gmail.com> | |||
George Kontridze <george.kontridze@gmail.com> | |||
Georgy Buranov <gburanov@gmail.com> | |||
Gnahz <p@oath.pl> | |||
Google Inc. | |||
Grachev Mikhail <work@mgrachev.com> | |||
griffin_stewie <panterathefamilyguy@gmail.com> | |||
Guillaume Jacquet <guillaume.jacquet@gmail.com> | |||
Guz Alexander <kalimatas@gmail.com> | |||
Guðmundur Bjarni Ólafsson <gudmundur@github.com> | |||
Hanno Hecker <hanno.hecker@zalando.de> | |||
Hari haran <hariharan.uno@gmail.com> | |||
haya14busa <hayabusa1419@gmail.com> | |||
Huy Tr <kingbazoka@gmail.com> | |||
huydx <doxuanhuy@gmail.com> | |||
i2bskn <i2bskn@gmail.com> | |||
Isao Jonas <isao.jonas@gmail.com> | |||
isqua <isqua@isqua.ru> | |||
Jameel Haffejee <RC1140@republiccommandos.co.za> | |||
Jan Kosecki <jan.kosecki91@gmail.com> | |||
Javier Campanini <jcampanini@palantir.com> | |||
Jens Rantil <jens.rantil@gmail.com> | |||
Jeremy Morris <jeremylevanmorris@gmail.com> | |||
Jesse Newland <jesse@jnewland.com> | |||
Jihoon Chung <j.c@navercorp.com> | |||
Jimmi Dyson <jimmidyson@gmail.com> | |||
Joan Saum <joan.saum@epitech.eu> | |||
Joe Tsai <joetsai@digital-static.net> | |||
John Barton <jrbarton@gmail.com> | |||
John Engelman <john.r.engelman@gmail.com> | |||
Jordan Sussman <jordansail22@gmail.com> | |||
Joshua Bezaleel Abednego <joshua.bezaleel@gmail.com> | |||
JP Phillips <jonphill9@gmail.com> | |||
jpbelanger-mtl <jp.belanger@gmail.com> | |||
Juan Basso <jrbasso@gmail.com> | |||
Julien Garcia Gonzalez <garciagonzalez.julien@gmail.com> | |||
Julien Rostand <jrostand@users.noreply.github.com> | |||
Justin Abrahms <justin@abrah.ms> | |||
Jusung Lee <e.jusunglee@gmail.com> | |||
jzhoucliqr <jzhou@cliqr.com> | |||
Katrina Owen <kytrinyx@github.com> | |||
Kautilya Tripathi < tripathi.kautilya@gmail.com> | |||
Kautilya Tripathi <tripathi.kautilya@gmail.com> | |||
Keita Urashima <ursm@ursm.jp> | |||
Kevin Burke <kev@inburke.com> | |||
Konrad Malawski <konrad.malawski@project13.pl> | |||
Kookheon Kwon <kucuny@gmail.com> | |||
Krzysztof Kowalczyk <kkowalczyk@gmail.com> | |||
Kshitij Saraogi <KshitijSaraogi@gmail.com> | |||
kyokomi <kyoko1220adword@gmail.com> | |||
Lovro Mažgon <lovro.mazgon@gmail.com> | |||
Lucas Alcantara <lucasalcantaraf@gmail.com> | |||
Luke Evers <me@lukevers.com> | |||
Luke Kysow <lkysow@gmail.com> | |||
Luke Roberts <email@luke-roberts.co.uk> | |||
Luke Young <luke@hydrantlabs.org> | |||
Maksim Zhylinski <uzzable@gmail.com> | |||
Martin-Louis Bright <mlbright@gmail.com> | |||
Marwan Sulaiman <marwan.sameer@gmail.com> | |||
Mat Geist <matgeist@gmail.com> | |||
Matt <alpmatthew@gmail.com> | |||
Matt Brender <mjbrender@gmail.com> | |||
Matt Gaunt <matt@gauntface.co.uk> | |||
Matt Landis <landis.matt@gmail.com> | |||
Maxime Bury <maxime.bury@gmail.com> | |||
Michael Spiegel <michael.m.spiegel@gmail.com> | |||
Michael Tiller <michael.tiller@gmail.com> | |||
Michał Glapa <michal.glapa@gmail.com> | |||
Nathan VanBenschoten <nvanbenschoten@gmail.com> | |||
Navaneeth Suresh <navaneeths1998@gmail.com> | |||
Neil O'Toole <neilotoole@apache.org> | |||
Nick Miyake <nmiyake@palantir.com> | |||
Nick Spragg <nick.spragg@bbc.co.uk> | |||
Nikhita Raghunath <nikitaraghunath@gmail.com> | |||
Noah Zoschke <noah+sso2@convox.com> | |||
ns-cweber <cweber@narrativescience.com> | |||
Oleg Kovalov <iamolegkovalov@gmail.com> | |||
Ondřej Kupka <ondra.cap@gmail.com> | |||
Palash Nigam <npalash25@gmail.com> | |||
Panagiotis Moustafellos <pmoust@gmail.com> | |||
Parham Alvani <parham.alvani@gmail.com> | |||
Parker Moore <parkrmoore@gmail.com> | |||
parkhyukjun89 <park.hyukjun89@gmail.com> | |||
Pavel Shtanko <pavel.shtanko@gmail.com> | |||
Pete Wagner <thepwagner@github.com> | |||
Petr Shevtsov <petr.shevtsov@gmail.com> | |||
Pierre Carrier <pierre@meteor.com> | |||
Piotr Zurek <p.zurek@gmail.com> | |||
Quinn Slack <qslack@qslack.com> | |||
Rackspace US, Inc. | |||
Radek Simko <radek.simko@gmail.com> | |||
Radliński Ignacy <radlinsk@student.agh.edu.pl> | |||
Rajendra arora <rajendraarora16@yahoo.com> | |||
RaviTeja Pothana <ravi-teja@live.com> | |||
rc1140 <jameel@republiccommandos.co.za> | |||
Red Hat, Inc. | |||
Rob Figueiredo <robfig@yext.com> | |||
Rohit Upadhyay <urohit011@gmail.com> | |||
Ronak Jain <ronakjain@outlook.in> | |||
Ruben Vereecken <rubenvereecken@gmail.com> | |||
Ryan Leung <rleungx@gmail.com> | |||
Ryan Lower <rpjlower@gmail.com> | |||
Sahil Dua <sahildua2305@gmail.com> | |||
saisi <saisi@users.noreply.github.com> | |||
Sam Minnée <sam@silverstripe.com> | |||
Sandeep Sukhani <sandeep.d.sukhani@gmail.com> | |||
Sander van Harmelen <svanharmelen@schubergphilis.com> | |||
Sanket Payghan <sanket.payghan8@gmail.com> | |||
Sarasa Kisaragi <lingsamuelgrace@gmail.com> | |||
Sean Wang <sean@decrypted.org> | |||
Sebastian Mandrean <sebastian.mandrean@gmail.com> | |||
Sebastian Mæland Pedersen <sem.pedersen@stud.uis.no> | |||
Sergey Romanov <xxsmotur@gmail.com> | |||
Sevki <s@sevki.org> | |||
Shagun Khemka <shagun.khemka60@gmail.com> | |||
shakeelrao <shakeelrao79@gmail.com> | |||
Shawn Catanzarite <me@shawncatz.com> | |||
Shawn Smith <shawnpsmith@gmail.com> | |||
sona-tar <sona.zip@gmail.com> | |||
SoundCloud, Ltd. | |||
Sridhar Mocherla <srmocher@microsoft.com> | |||
Stian Eikeland <stian@eikeland.se> | |||
Tasya Aditya Rukmana <tadityar@gmail.com> | |||
Thomas Bruyelle <thomas.bruyelle@gmail.com> | |||
Timothée Peignier <timothee.peignier@tryphon.org> | |||
Trey Tacon <ttacon@gmail.com> | |||
ttacon <ttacon@gmail.com> | |||
Varadarajan Aravamudhan <varadaraajan@gmail.com> | |||
Victor Castell <victor@victorcastell.com> | |||
Victor Vrantchan <vrancean+github@gmail.com> | |||
Vlad Ungureanu <vladu@palantir.com> | |||
Wasim Thabraze <wasim@thabraze.me> | |||
Will Maier <wcmaier@gmail.com> | |||
William Bailey <mail@williambailey.org.uk> | |||
xibz <impactbchang@gmail.com> | |||
Yann Malet <yann.malet@gmail.com> | |||
Yannick Utard <yannickutard@gmail.com> | |||
Yicheng Qin <qycqycqycqycqyc@gmail.com> | |||
Yumikiyo Osanai <yumios.art@gmail.com> | |||
Zach Latta <zach@zachlatta.com> |
@ -0,0 +1,27 @@ | |||
Copyright (c) 2013 The go-github AUTHORS. All rights reserved. | |||
Redistribution and use in source and binary forms, with or without | |||
modification, are permitted provided that the following conditions are | |||
met: | |||
* Redistributions of source code must retain the above copyright | |||
notice, this list of conditions and the following disclaimer. | |||
* Redistributions in binary form must reproduce the above | |||
copyright notice, this list of conditions and the following disclaimer | |||
in the documentation and/or other materials provided with the | |||
distribution. | |||
* Neither the name of Google Inc. nor the names of its | |||
contributors may be used to endorse or promote products derived from | |||
this software without specific prior written permission. | |||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,69 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import "context" | |||
// ActivityService handles communication with the activity related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/ | |||
type ActivityService service | |||
// FeedLink represents a link to a related resource. | |||
type FeedLink struct { | |||
HRef *string `json:"href,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
} | |||
// Feeds represents timeline resources in Atom format. | |||
type Feeds struct { | |||
TimelineURL *string `json:"timeline_url,omitempty"` | |||
UserURL *string `json:"user_url,omitempty"` | |||
CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` | |||
CurrentUserURL *string `json:"current_user_url,omitempty"` | |||
CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` | |||
CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` | |||
CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` | |||
Links *struct { | |||
Timeline *FeedLink `json:"timeline,omitempty"` | |||
User *FeedLink `json:"user,omitempty"` | |||
CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` | |||
CurrentUser *FeedLink `json:"current_user,omitempty"` | |||
CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` | |||
CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` | |||
CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` | |||
} `json:"_links,omitempty"` | |||
} | |||
// ListFeeds lists all the feeds available to the authenticated user. | |||
// | |||
// GitHub provides several timeline resources in Atom format: | |||
// Timeline: The GitHub global public timeline | |||
// User: The public timeline for any user, using URI template | |||
// Current user public: The public timeline for the authenticated user | |||
// Current user: The private timeline for the authenticated user | |||
// Current user actor: The private timeline for activity created by the | |||
// authenticated user | |||
// Current user organizations: The private timeline for the organizations | |||
// the authenticated user is a member of. | |||
// | |||
// Note: Private feeds are only returned when authenticating via Basic Auth | |||
// since current feed URIs use the older, non revocable auth tokens. | |||
func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "feeds", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
f := &Feeds{} | |||
resp, err := s.client.Do(ctx, req, f) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return f, resp, nil | |||
} |
@ -0,0 +1,215 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListEvents drinks from the firehose of all public events across GitHub. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events | |||
func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) { | |||
u, err := addOptions("events", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListRepositoryEvents lists events for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events | |||
func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/events", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListIssueEventsForRepository lists issue events for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository | |||
func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*IssueEvent | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListEventsForRepoNetwork lists public events for a network of repositories. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories | |||
func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { | |||
u := fmt.Sprintf("networks/%v/%v/events", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListEventsForOrganization lists public events for an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization | |||
func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/events", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is | |||
// true, only public events will be returned. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user | |||
func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { | |||
var u string | |||
if publicOnly { | |||
u = fmt.Sprintf("users/%v/events/public", user) | |||
} else { | |||
u = fmt.Sprintf("users/%v/events", user) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListEventsReceivedByUser lists the events received by a user. If publicOnly is | |||
// true, only public events will be returned. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received | |||
func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { | |||
var u string | |||
if publicOnly { | |||
u = fmt.Sprintf("users/%v/received_events/public", user) | |||
} else { | |||
u = fmt.Sprintf("users/%v/received_events", user) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListUserEventsForOrganization provides the user’s organization dashboard. You | |||
// must be authenticated as the user to view this. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization | |||
func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) { | |||
u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*Event | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} |
@ -0,0 +1,223 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// Notification identifies a GitHub notification for a user. | |||
type Notification struct { | |||
ID *string `json:"id,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
Subject *NotificationSubject `json:"subject,omitempty"` | |||
// Reason identifies the event that triggered the notification. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons | |||
Reason *string `json:"reason,omitempty"` | |||
Unread *bool `json:"unread,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
LastReadAt *time.Time `json:"last_read_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
} | |||
// NotificationSubject identifies the subject of a notification. | |||
type NotificationSubject struct { | |||
Title *string `json:"title,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
LatestCommentURL *string `json:"latest_comment_url,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
} | |||
// NotificationListOptions specifies the optional parameters to the | |||
// ActivityService.ListNotifications method. | |||
type NotificationListOptions struct { | |||
All bool `url:"all,omitempty"` | |||
Participating bool `url:"participating,omitempty"` | |||
Since time.Time `url:"since,omitempty"` | |||
Before time.Time `url:"before,omitempty"` | |||
ListOptions | |||
} | |||
// ListNotifications lists all notifications for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications | |||
func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) { | |||
u := fmt.Sprintf("notifications") | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var notifications []*Notification | |||
resp, err := s.client.Do(ctx, req, ¬ifications) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return notifications, resp, nil | |||
} | |||
// ListRepositoryNotifications lists all notifications in a given repository | |||
// for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository | |||
func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var notifications []*Notification | |||
resp, err := s.client.Do(ctx, req, ¬ifications) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return notifications, resp, nil | |||
} | |||
type markReadOptions struct { | |||
LastReadAt time.Time `json:"last_read_at,omitempty"` | |||
} | |||
// MarkNotificationsRead marks all notifications up to lastRead as read. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read | |||
func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) { | |||
opts := &markReadOptions{ | |||
LastReadAt: lastRead, | |||
} | |||
req, err := s.client.NewRequest("PUT", "notifications", opts) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// MarkRepositoryNotificationsRead marks all notifications up to lastRead in | |||
// the specified repository as read. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository | |||
func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) { | |||
opts := &markReadOptions{ | |||
LastReadAt: lastRead, | |||
} | |||
u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) | |||
req, err := s.client.NewRequest("PUT", u, opts) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// GetThread gets the specified notification thread. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread | |||
func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) { | |||
u := fmt.Sprintf("notifications/threads/%v", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
notification := new(Notification) | |||
resp, err := s.client.Do(ctx, req, notification) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return notification, resp, nil | |||
} | |||
// MarkThreadRead marks the specified thread as read. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read | |||
func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) { | |||
u := fmt.Sprintf("notifications/threads/%v", id) | |||
req, err := s.client.NewRequest("PATCH", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// GetThreadSubscription checks to see if the authenticated user is subscribed | |||
// to a thread. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription | |||
func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) { | |||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
sub := new(Subscription) | |||
resp, err := s.client.Do(ctx, req, sub) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return sub, resp, nil | |||
} | |||
// SetThreadSubscription sets the subscription for the specified thread for the | |||
// authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription | |||
func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) { | |||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) | |||
req, err := s.client.NewRequest("PUT", u, subscription) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
sub := new(Subscription) | |||
resp, err := s.client.Do(ctx, req, sub) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return sub, resp, nil | |||
} | |||
// DeleteThreadSubscription deletes the subscription for the specified thread | |||
// for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription | |||
func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) { | |||
u := fmt.Sprintf("notifications/threads/%v/subscription", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,137 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"strings" | |||
) | |||
// StarredRepository is returned by ListStarred. | |||
type StarredRepository struct { | |||
StarredAt *Timestamp `json:"starred_at,omitempty"` | |||
Repository *Repository `json:"repo,omitempty"` | |||
} | |||
// Stargazer represents a user that has starred a repository. | |||
type Stargazer struct { | |||
StarredAt *Timestamp `json:"starred_at,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
} | |||
// ListStargazers lists people who have starred the specified repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers | |||
func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeStarringPreview) | |||
var stargazers []*Stargazer | |||
resp, err := s.client.Do(ctx, req, &stargazers) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return stargazers, resp, nil | |||
} | |||
// ActivityListStarredOptions specifies the optional parameters to the | |||
// ActivityService.ListStarred method. | |||
type ActivityListStarredOptions struct { | |||
// How to sort the repository list. Possible values are: created, updated, | |||
// pushed, full_name. Default is "full_name". | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort repositories. Possible values are: asc, desc. | |||
// Default is "asc" when sort is "full_name", otherwise default is "desc". | |||
Direction string `url:"direction,omitempty"` | |||
ListOptions | |||
} | |||
// ListStarred lists all the repos starred by a user. Passing the empty string | |||
// will list the starred repositories for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred | |||
func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { | |||
var u string | |||
if user != "" { | |||
u = fmt.Sprintf("users/%v/starred", user) | |||
} else { | |||
u = "user/starred" | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when APIs fully launch | |||
acceptHeaders := []string{mediaTypeStarringPreview, mediaTypeTopicsPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var repos []*StarredRepository | |||
resp, err := s.client.Do(ctx, req, &repos) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return repos, resp, nil | |||
} | |||
// IsStarred checks if a repository is starred by authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository | |||
func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) { | |||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
starred, err := parseBoolResponse(err) | |||
return starred, resp, err | |||
} | |||
// Star a repository as the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository | |||
func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) { | |||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Unstar a repository as the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository | |||
func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) { | |||
u := fmt.Sprintf("user/starred/%v/%v", owner, repo) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,146 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Subscription identifies a repository or thread subscription. | |||
type Subscription struct { | |||
Subscribed *bool `json:"subscribed,omitempty"` | |||
Ignored *bool `json:"ignored,omitempty"` | |||
Reason *string `json:"reason,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
// only populated for repository subscriptions | |||
RepositoryURL *string `json:"repository_url,omitempty"` | |||
// only populated for thread subscriptions | |||
ThreadURL *string `json:"thread_url,omitempty"` | |||
} | |||
// ListWatchers lists watchers of a particular repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers | |||
func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var watchers []*User | |||
resp, err := s.client.Do(ctx, req, &watchers) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return watchers, resp, nil | |||
} | |||
// ListWatched lists the repositories the specified user is watching. Passing | |||
// the empty string will fetch watched repos for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched | |||
func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) { | |||
var u string | |||
if user != "" { | |||
u = fmt.Sprintf("users/%v/subscriptions", user) | |||
} else { | |||
u = "user/subscriptions" | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var watched []*Repository | |||
resp, err := s.client.Do(ctx, req, &watched) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return watched, resp, nil | |||
} | |||
// GetRepositorySubscription returns the subscription for the specified | |||
// repository for the authenticated user. If the authenticated user is not | |||
// watching the repository, a nil Subscription is returned. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription | |||
func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
sub := new(Subscription) | |||
resp, err := s.client.Do(ctx, req, sub) | |||
if err != nil { | |||
// if it's just a 404, don't return that as an error | |||
_, err = parseBoolResponse(err) | |||
return nil, resp, err | |||
} | |||
return sub, resp, nil | |||
} | |||
// SetRepositorySubscription sets the subscription for the specified repository | |||
// for the authenticated user. | |||
// | |||
// To watch a repository, set subscription.Subscribed to true. | |||
// To ignore notifications made within a repository, set subscription.Ignored to true. | |||
// To stop watching a repository, use DeleteRepositorySubscription. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription | |||
func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | |||
req, err := s.client.NewRequest("PUT", u, subscription) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
sub := new(Subscription) | |||
resp, err := s.client.Do(ctx, req, sub) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return sub, resp, nil | |||
} | |||
// DeleteRepositorySubscription deletes the subscription for the specified | |||
// repository for the authenticated user. | |||
// | |||
// This is used to stop watching a repository. To control whether or not to | |||
// receive notifications from a repository, use SetRepositorySubscription. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription | |||
func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,101 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// AdminService handles communication with the admin related methods of the | |||
// GitHub API. These API routes are normally only accessible for GitHub | |||
// Enterprise installations. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/enterprise/ | |||
type AdminService service | |||
// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group. | |||
type TeamLDAPMapping struct { | |||
ID *int64 `json:"id,omitempty"` | |||
LDAPDN *string `json:"ldap_dn,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Slug *string `json:"slug,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Privacy *string `json:"privacy,omitempty"` | |||
Permission *string `json:"permission,omitempty"` | |||
MembersURL *string `json:"members_url,omitempty"` | |||
RepositoriesURL *string `json:"repositories_url,omitempty"` | |||
} | |||
func (m TeamLDAPMapping) String() string { | |||
return Stringify(m) | |||
} | |||
// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user. | |||
type UserLDAPMapping struct { | |||
ID *int64 `json:"id,omitempty"` | |||
LDAPDN *string `json:"ldap_dn,omitempty"` | |||
Login *string `json:"login,omitempty"` | |||
AvatarURL *string `json:"avatar_url,omitempty"` | |||
GravatarID *string `json:"gravatar_id,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
SiteAdmin *bool `json:"site_admin,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
EventsURL *string `json:"events_url,omitempty"` | |||
FollowingURL *string `json:"following_url,omitempty"` | |||
FollowersURL *string `json:"followers_url,omitempty"` | |||
GistsURL *string `json:"gists_url,omitempty"` | |||
OrganizationsURL *string `json:"organizations_url,omitempty"` | |||
ReceivedEventsURL *string `json:"received_events_url,omitempty"` | |||
ReposURL *string `json:"repos_url,omitempty"` | |||
StarredURL *string `json:"starred_url,omitempty"` | |||
SubscriptionsURL *string `json:"subscriptions_url,omitempty"` | |||
} | |||
func (m UserLDAPMapping) String() string { | |||
return Stringify(m) | |||
} | |||
// UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user | |||
func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) { | |||
u := fmt.Sprintf("admin/ldap/users/%v/mapping", user) | |||
req, err := s.client.NewRequest("PATCH", u, mapping) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(UserLDAPMapping) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team | |||
func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int64, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) { | |||
u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team) | |||
req, err := s.client.NewRequest("PATCH", u, mapping) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(TeamLDAPMapping) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} |
@ -0,0 +1,171 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// AdminStats represents a variety of stats of a Github Enterprise | |||
// installation. | |||
type AdminStats struct { | |||
Issues *IssueStats `json:"issues,omitempty"` | |||
Hooks *HookStats `json:"hooks,omitempty"` | |||
Milestones *MilestoneStats `json:"milestones,omitempty"` | |||
Orgs *OrgStats `json:"orgs,omitempty"` | |||
Comments *CommentStats `json:"comments,omitempty"` | |||
Pages *PageStats `json:"pages,omitempty"` | |||
Users *UserStats `json:"users,omitempty"` | |||
Gists *GistStats `json:"gists,omitempty"` | |||
Pulls *PullStats `json:"pulls,omitempty"` | |||
Repos *RepoStats `json:"repos,omitempty"` | |||
} | |||
func (s AdminStats) String() string { | |||
return Stringify(s) | |||
} | |||
// IssueStats represents the number of total, open and closed issues. | |||
type IssueStats struct { | |||
TotalIssues *int `json:"total_issues,omitempty"` | |||
OpenIssues *int `json:"open_issues,omitempty"` | |||
ClosedIssues *int `json:"closed_issues,omitempty"` | |||
} | |||
func (s IssueStats) String() string { | |||
return Stringify(s) | |||
} | |||
// HookStats represents the number of total, active and inactive hooks. | |||
type HookStats struct { | |||
TotalHooks *int `json:"total_hooks,omitempty"` | |||
ActiveHooks *int `json:"active_hooks,omitempty"` | |||
InactiveHooks *int `json:"inactive_hooks,omitempty"` | |||
} | |||
func (s HookStats) String() string { | |||
return Stringify(s) | |||
} | |||
// MilestoneStats represents the number of total, open and close milestones. | |||
type MilestoneStats struct { | |||
TotalMilestones *int `json:"total_milestones,omitempty"` | |||
OpenMilestones *int `json:"open_milestones,omitempty"` | |||
ClosedMilestones *int `json:"closed_milestones,omitempty"` | |||
} | |||
func (s MilestoneStats) String() string { | |||
return Stringify(s) | |||
} | |||
// OrgStats represents the number of total, disabled organizations and the team | |||
// and team member count. | |||
type OrgStats struct { | |||
TotalOrgs *int `json:"total_orgs,omitempty"` | |||
DisabledOrgs *int `json:"disabled_orgs,omitempty"` | |||
TotalTeams *int `json:"total_teams,omitempty"` | |||
TotalTeamMembers *int `json:"total_team_members,omitempty"` | |||
} | |||
func (s OrgStats) String() string { | |||
return Stringify(s) | |||
} | |||
// CommentStats represents the number of total comments on commits, gists, issues | |||
// and pull requests. | |||
type CommentStats struct { | |||
TotalCommitComments *int `json:"total_commit_comments,omitempty"` | |||
TotalGistComments *int `json:"total_gist_comments,omitempty"` | |||
TotalIssueComments *int `json:"total_issue_comments,omitempty"` | |||
TotalPullRequestComments *int `json:"total_pull_request_comments,omitempty"` | |||
} | |||
func (s CommentStats) String() string { | |||
return Stringify(s) | |||
} | |||
// PageStats represents the total number of github pages. | |||
type PageStats struct { | |||
TotalPages *int `json:"total_pages,omitempty"` | |||
} | |||
func (s PageStats) String() string { | |||
return Stringify(s) | |||
} | |||
// UserStats represents the number of total, admin and suspended users. | |||
type UserStats struct { | |||
TotalUsers *int `json:"total_users,omitempty"` | |||
AdminUsers *int `json:"admin_users,omitempty"` | |||
SuspendedUsers *int `json:"suspended_users,omitempty"` | |||
} | |||
func (s UserStats) String() string { | |||
return Stringify(s) | |||
} | |||
// GistStats represents the number of total, private and public gists. | |||
type GistStats struct { | |||
TotalGists *int `json:"total_gists,omitempty"` | |||
PrivateGists *int `json:"private_gists,omitempty"` | |||
PublicGists *int `json:"public_gists,omitempty"` | |||
} | |||
func (s GistStats) String() string { | |||
return Stringify(s) | |||
} | |||
// PullStats represents the number of total, merged, mergable and unmergeable | |||
// pull-requests. | |||
type PullStats struct { | |||
TotalPulls *int `json:"total_pulls,omitempty"` | |||
MergedPulls *int `json:"merged_pulls,omitempty"` | |||
MergablePulls *int `json:"mergeable_pulls,omitempty"` | |||
UnmergablePulls *int `json:"unmergeable_pulls,omitempty"` | |||
} | |||
func (s PullStats) String() string { | |||
return Stringify(s) | |||
} | |||
// RepoStats represents the number of total, root, fork, organization repositories | |||
// together with the total number of pushes and wikis. | |||
type RepoStats struct { | |||
TotalRepos *int `json:"total_repos,omitempty"` | |||
RootRepos *int `json:"root_repos,omitempty"` | |||
ForkRepos *int `json:"fork_repos,omitempty"` | |||
OrgRepos *int `json:"org_repos,omitempty"` | |||
TotalPushes *int `json:"total_pushes,omitempty"` | |||
TotalWikis *int `json:"total_wikis,omitempty"` | |||
} | |||
func (s RepoStats) String() string { | |||
return Stringify(s) | |||
} | |||
// GetAdminStats returns a variety of metrics about a Github Enterprise | |||
// installation. | |||
// | |||
// Please note that this is only available to site administrators, | |||
// otherwise it will error with a 404 not found (instead of 401 or 403). | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/enterprise-admin/admin_stats/ | |||
func (s *AdminService) GetAdminStats(ctx context.Context) (*AdminStats, *Response, error) { | |||
u := fmt.Sprintf("enterprise/stats/all") | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(AdminStats) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} |
@ -0,0 +1,230 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// AppsService provides access to the installation related functions | |||
// in the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/ | |||
type AppsService service | |||
// App represents a GitHub App. | |||
type App struct { | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
Owner *User `json:"owner,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
ExternalURL *string `json:"external_url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
} | |||
// InstallationToken represents an installation token. | |||
type InstallationToken struct { | |||
Token *string `json:"token,omitempty"` | |||
ExpiresAt *time.Time `json:"expires_at,omitempty"` | |||
} | |||
// InstallationPermissions lists the permissions for metadata, contents, issues and single file for an installation. | |||
type InstallationPermissions struct { | |||
Metadata *string `json:"metadata,omitempty"` | |||
Contents *string `json:"contents,omitempty"` | |||
Issues *string `json:"issues,omitempty"` | |||
SingleFile *string `json:"single_file,omitempty"` | |||
} | |||
// Installation represents a GitHub Apps installation. | |||
type Installation struct { | |||
ID *int64 `json:"id,omitempty"` | |||
AppID *int64 `json:"app_id,omitempty"` | |||
TargetID *int64 `json:"target_id,omitempty"` | |||
Account *User `json:"account,omitempty"` | |||
AccessTokensURL *string `json:"access_tokens_url,omitempty"` | |||
RepositoriesURL *string `json:"repositories_url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
TargetType *string `json:"target_type,omitempty"` | |||
SingleFileName *string `json:"single_file_name,omitempty"` | |||
RepositorySelection *string `json:"repository_selection,omitempty"` | |||
Events []string `json:"events,omitempty"` | |||
Permissions *InstallationPermissions `json:"permissions,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
} | |||
func (i Installation) String() string { | |||
return Stringify(i) | |||
} | |||
// Get a single GitHub App. Passing the empty string will get | |||
// the authenticated GitHub App. | |||
// | |||
// Note: appSlug is just the URL-friendly name of your GitHub App. | |||
// You can find this on the settings page for your GitHub App | |||
// (e.g., https://github.com/settings/apps/:app_slug). | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app | |||
func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) { | |||
var u string | |||
if appSlug != "" { | |||
u = fmt.Sprintf("apps/%v", appSlug) | |||
} else { | |||
u = "app" | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
app := new(App) | |||
resp, err := s.client.Do(ctx, req, app) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return app, resp, nil | |||
} | |||
// ListInstallations lists the installations that the current GitHub App has. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#find-installations | |||
func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { | |||
u, err := addOptions("app/installations", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
var i []*Installation | |||
resp, err := s.client.Do(ctx, req, &i) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return i, resp, nil | |||
} | |||
// GetInstallation returns the specified installation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation | |||
func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) { | |||
return s.getInstallation(ctx, fmt.Sprintf("app/installations/%v", id)) | |||
} | |||
// ListUserInstallations lists installations that are accessible to the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user | |||
func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { | |||
u, err := addOptions("user/installations", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
var i struct { | |||
Installations []*Installation `json:"installations"` | |||
} | |||
resp, err := s.client.Do(ctx, req, &i) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return i.Installations, resp, nil | |||
} | |||
// CreateInstallationToken creates a new installation token. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token | |||
func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) { | |||
u := fmt.Sprintf("app/installations/%v/access_tokens", id) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
t := new(InstallationToken) | |||
resp, err := s.client.Do(ctx, req, t) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return t, resp, nil | |||
} | |||
// FindOrganizationInstallation finds the organization's installation information. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#find-organization-installation | |||
func (s *AppsService) FindOrganizationInstallation(ctx context.Context, org string) (*Installation, *Response, error) { | |||
return s.getInstallation(ctx, fmt.Sprintf("orgs/%v/installation", org)) | |||
} | |||
// FindRepositoryInstallation finds the repository's installation information. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation | |||
func (s *AppsService) FindRepositoryInstallation(ctx context.Context, owner, repo string) (*Installation, *Response, error) { | |||
return s.getInstallation(ctx, fmt.Sprintf("repos/%v/%v/installation", owner, repo)) | |||
} | |||
// FindRepositoryInstallationByID finds the repository's installation information. | |||
// | |||
// Note: FindRepositoryInstallationByID uses the undocumented GitHub API endpoint /repositories/:id/installation. | |||
func (s *AppsService) FindRepositoryInstallationByID(ctx context.Context, id int64) (*Installation, *Response, error) { | |||
return s.getInstallation(ctx, fmt.Sprintf("repositories/%d/installation", id)) | |||
} | |||
// FindUserInstallation finds the user's installation information. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/#find-repository-installation | |||
func (s *AppsService) FindUserInstallation(ctx context.Context, user string) (*Installation, *Response, error) { | |||
return s.getInstallation(ctx, fmt.Sprintf("users/%v/installation", user)) | |||
} | |||
func (s *AppsService) getInstallation(ctx context.Context, url string) (*Installation, *Response, error) { | |||
req, err := s.client.NewRequest("GET", url, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
i := new(Installation) | |||
resp, err := s.client.Do(ctx, req, i) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return i, resp, nil | |||
} |
@ -0,0 +1,103 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListRepos lists the repositories that are accessible to the authenticated installation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories | |||
func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) { | |||
u, err := addOptions("installation/repositories", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
var r struct { | |||
Repositories []*Repository `json:"repositories"` | |||
} | |||
resp, err := s.client.Do(ctx, req, &r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r.Repositories, resp, nil | |||
} | |||
// ListUserRepos lists repositories that are accessible | |||
// to the authenticated user for an installation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation | |||
func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) { | |||
u := fmt.Sprintf("user/installations/%v/repositories", id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
var r struct { | |||
Repositories []*Repository `json:"repositories"` | |||
} | |||
resp, err := s.client.Do(ctx, req, &r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r.Repositories, resp, nil | |||
} | |||
// AddRepository adds a single repository to an installation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation | |||
func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) { | |||
u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
r := new(Repository) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// RemoveRepository removes a single repository from an installation. | |||
// | |||
// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation | |||
func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) { | |||
u := fmt.Sprintf("user/installations/%v/repositories/%v", instID, repoID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeIntegrationPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,183 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// MarketplaceService handles communication with the marketplace related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/ | |||
type MarketplaceService struct { | |||
client *Client | |||
// Stubbed controls whether endpoints that return stubbed data are used | |||
// instead of production endpoints. Stubbed data is fake data that's useful | |||
// for testing your GitHub Apps. Stubbed data is hard-coded and will not | |||
// change based on actual subscriptions. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/ | |||
Stubbed bool | |||
} | |||
// MarketplacePlan represents a GitHub Apps Marketplace Listing Plan. | |||
type MarketplacePlan struct { | |||
URL *string `json:"url,omitempty"` | |||
AccountsURL *string `json:"accounts_url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"` | |||
YearlyPriceInCents *int `json:"yearly_price_in_cents,omitempty"` | |||
// The pricing model for this listing. Can be one of "flat-rate", "per-unit", or "free". | |||
PriceModel *string `json:"price_model,omitempty"` | |||
UnitName *string `json:"unit_name,omitempty"` | |||
Bullets *[]string `json:"bullets,omitempty"` | |||
// State can be one of the values "draft" or "published". | |||
State *string `json:"state,omitempty"` | |||
HasFreeTrial *bool `json:"has_free_trial,omitempty"` | |||
} | |||
// MarketplacePurchase represents a GitHub Apps Marketplace Purchase. | |||
type MarketplacePurchase struct { | |||
// BillingCycle can be one of the values "yearly", "monthly" or nil. | |||
BillingCycle *string `json:"billing_cycle,omitempty"` | |||
NextBillingDate *Timestamp `json:"next_billing_date,omitempty"` | |||
UnitCount *int `json:"unit_count,omitempty"` | |||
Plan *MarketplacePlan `json:"plan,omitempty"` | |||
Account *MarketplacePlanAccount `json:"account,omitempty"` | |||
OnFreeTrial *bool `json:"on_free_trial,omitempty"` | |||
FreeTrialEndsOn *Timestamp `json:"free_trial_ends_on,omitempty"` | |||
} | |||
// MarketplacePendingChange represents a pending change to a GitHub Apps Marketplace Plan. | |||
type MarketplacePendingChange struct { | |||
EffectiveDate *Timestamp `json:"effective_date,omitempty"` | |||
UnitCount *int `json:"unit_count,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Plan *MarketplacePlan `json:"plan,omitempty"` | |||
} | |||
// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan. | |||
type MarketplacePlanAccount struct { | |||
URL *string `json:"url,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Login *string `json:"login,omitempty"` | |||
Email *string `json:"email,omitempty"` | |||
OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` | |||
MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` | |||
MarketplacePendingChange *MarketplacePendingChange `json:"marketplace_pending_change,omitempty"` | |||
} | |||
// ListPlans lists all plans for your Marketplace listing. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing | |||
func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) { | |||
uri := s.marketplaceURI("plans") | |||
u, err := addOptions(uri, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var plans []*MarketplacePlan | |||
resp, err := s.client.Do(ctx, req, &plans) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return plans, resp, nil | |||
} | |||
// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan | |||
func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { | |||
uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID)) | |||
u, err := addOptions(uri, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var accounts []*MarketplacePlanAccount | |||
resp, err := s.client.Do(ctx, req, &accounts) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return accounts, resp, nil | |||
} | |||
// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing | |||
func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { | |||
uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID)) | |||
u, err := addOptions(uri, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var accounts []*MarketplacePlanAccount | |||
resp, err := s.client.Do(ctx, req, &accounts) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return accounts, resp, nil | |||
} | |||
// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases | |||
func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) { | |||
uri := "user/marketplace_purchases" | |||
if s.Stubbed { | |||
uri = "user/marketplace_purchases/stubbed" | |||
} | |||
u, err := addOptions(uri, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var purchases []*MarketplacePurchase | |||
resp, err := s.client.Do(ctx, req, &purchases) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return purchases, resp, nil | |||
} | |||
func (s *MarketplaceService) marketplaceURI(endpoint string) string { | |||
url := "marketplace_listing" | |||
if s.Stubbed { | |||
url = "marketplace_listing/stubbed" | |||
} | |||
return url + "/" + endpoint | |||
} |
@ -0,0 +1,435 @@ | |||
// Copyright 2015 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Scope models a GitHub authorization scope. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth/#scopes | |||
type Scope string | |||
// This is the set of scopes for GitHub API V3 | |||
const ( | |||
ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact? | |||
ScopeUser Scope = "user" | |||
ScopeUserEmail Scope = "user:email" | |||
ScopeUserFollow Scope = "user:follow" | |||
ScopePublicRepo Scope = "public_repo" | |||
ScopeRepo Scope = "repo" | |||
ScopeRepoDeployment Scope = "repo_deployment" | |||
ScopeRepoStatus Scope = "repo:status" | |||
ScopeDeleteRepo Scope = "delete_repo" | |||
ScopeNotifications Scope = "notifications" | |||
ScopeGist Scope = "gist" | |||
ScopeReadRepoHook Scope = "read:repo_hook" | |||
ScopeWriteRepoHook Scope = "write:repo_hook" | |||
ScopeAdminRepoHook Scope = "admin:repo_hook" | |||
ScopeAdminOrgHook Scope = "admin:org_hook" | |||
ScopeReadOrg Scope = "read:org" | |||
ScopeWriteOrg Scope = "write:org" | |||
ScopeAdminOrg Scope = "admin:org" | |||
ScopeReadPublicKey Scope = "read:public_key" | |||
ScopeWritePublicKey Scope = "write:public_key" | |||
ScopeAdminPublicKey Scope = "admin:public_key" | |||
ScopeReadGPGKey Scope = "read:gpg_key" | |||
ScopeWriteGPGKey Scope = "write:gpg_key" | |||
ScopeAdminGPGKey Scope = "admin:gpg_key" | |||
) | |||
// AuthorizationsService handles communication with the authorization related | |||
// methods of the GitHub API. | |||
// | |||
// This service requires HTTP Basic Authentication; it cannot be accessed using | |||
// an OAuth token. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/ | |||
type AuthorizationsService service | |||
// Authorization represents an individual GitHub authorization. | |||
type Authorization struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Scopes []Scope `json:"scopes,omitempty"` | |||
Token *string `json:"token,omitempty"` | |||
TokenLastEight *string `json:"token_last_eight,omitempty"` | |||
HashedToken *string `json:"hashed_token,omitempty"` | |||
App *AuthorizationApp `json:"app,omitempty"` | |||
Note *string `json:"note,omitempty"` | |||
NoteURL *string `json:"note_url,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
Fingerprint *string `json:"fingerprint,omitempty"` | |||
// User is only populated by the Check and Reset methods. | |||
User *User `json:"user,omitempty"` | |||
} | |||
func (a Authorization) String() string { | |||
return Stringify(a) | |||
} | |||
// AuthorizationApp represents an individual GitHub app (in the context of authorization). | |||
type AuthorizationApp struct { | |||
URL *string `json:"url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
ClientID *string `json:"client_id,omitempty"` | |||
} | |||
func (a AuthorizationApp) String() string { | |||
return Stringify(a) | |||
} | |||
// Grant represents an OAuth application that has been granted access to an account. | |||
type Grant struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
App *AuthorizationApp `json:"app,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
Scopes []string `json:"scopes,omitempty"` | |||
} | |||
func (g Grant) String() string { | |||
return Stringify(g) | |||
} | |||
// AuthorizationRequest represents a request to create an authorization. | |||
type AuthorizationRequest struct { | |||
Scopes []Scope `json:"scopes,omitempty"` | |||
Note *string `json:"note,omitempty"` | |||
NoteURL *string `json:"note_url,omitempty"` | |||
ClientID *string `json:"client_id,omitempty"` | |||
ClientSecret *string `json:"client_secret,omitempty"` | |||
Fingerprint *string `json:"fingerprint,omitempty"` | |||
} | |||
func (a AuthorizationRequest) String() string { | |||
return Stringify(a) | |||
} | |||
// AuthorizationUpdateRequest represents a request to update an authorization. | |||
// | |||
// Note that for any one update, you must only provide one of the "scopes" | |||
// fields. That is, you may provide only one of "Scopes", or "AddScopes", or | |||
// "RemoveScopes". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization | |||
type AuthorizationUpdateRequest struct { | |||
Scopes []string `json:"scopes,omitempty"` | |||
AddScopes []string `json:"add_scopes,omitempty"` | |||
RemoveScopes []string `json:"remove_scopes,omitempty"` | |||
Note *string `json:"note,omitempty"` | |||
NoteURL *string `json:"note_url,omitempty"` | |||
Fingerprint *string `json:"fingerprint,omitempty"` | |||
} | |||
func (a AuthorizationUpdateRequest) String() string { | |||
return Stringify(a) | |||
} | |||
// List the authorizations for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations | |||
func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) { | |||
u := "authorizations" | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var auths []*Authorization | |||
resp, err := s.client.Do(ctx, req, &auths) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return auths, resp, nil | |||
} | |||
// Get a single authorization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization | |||
func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) { | |||
u := fmt.Sprintf("authorizations/%d", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// Create a new authorization for the specified OAuth application. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization | |||
func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) { | |||
u := "authorizations" | |||
req, err := s.client.NewRequest("POST", u, auth) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// GetOrCreateForApp creates a new authorization for the specified OAuth | |||
// application, only if an authorization for that application doesn’t already | |||
// exist for the user. | |||
// | |||
// If a new token is created, the HTTP status code will be "201 Created", and | |||
// the returned Authorization.Token field will be populated. If an existing | |||
// token is returned, the status code will be "200 OK" and the | |||
// Authorization.Token field will be empty. | |||
// | |||
// clientID is the OAuth Client ID with which to create the token. | |||
// | |||
// GitHub API docs: | |||
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app | |||
// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint | |||
func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { | |||
var u string | |||
if auth.Fingerprint == nil || *auth.Fingerprint == "" { | |||
u = fmt.Sprintf("authorizations/clients/%v", clientID) | |||
} else { | |||
u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) | |||
} | |||
req, err := s.client.NewRequest("PUT", u, auth) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// Edit a single authorization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization | |||
func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { | |||
u := fmt.Sprintf("authorizations/%d", id) | |||
req, err := s.client.NewRequest("PATCH", u, auth) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// Delete a single authorization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization | |||
func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) { | |||
u := fmt.Sprintf("authorizations/%d", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Check if an OAuth token is valid for a specific app. | |||
// | |||
// Note that this operation requires the use of BasicAuth, but where the | |||
// username is the OAuth application clientID, and the password is its | |||
// clientSecret. Invalid tokens will return a 404 Not Found. | |||
// | |||
// The returned Authorization.User field will be populated. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization | |||
func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { | |||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// Reset is used to reset a valid OAuth token without end user involvement. | |||
// Applications must save the "token" property in the response, because changes | |||
// take effect immediately. | |||
// | |||
// Note that this operation requires the use of BasicAuth, but where the | |||
// username is the OAuth application clientID, and the password is its | |||
// clientSecret. Invalid tokens will return a 404 Not Found. | |||
// | |||
// The returned Authorization.User field will be populated. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization | |||
func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { | |||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// Revoke an authorization for an application. | |||
// | |||
// Note that this operation requires the use of BasicAuth, but where the | |||
// username is the OAuth application clientID, and the password is its | |||
// clientSecret. Invalid tokens will return a 404 Not Found. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application | |||
func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) { | |||
u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListGrants lists the set of OAuth applications that have been granted | |||
// access to a user's account. This will return one entry for each application | |||
// that has been granted access to the account, regardless of the number of | |||
// tokens an application has generated for the user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants | |||
func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions) ([]*Grant, *Response, error) { | |||
u, err := addOptions("applications/grants", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
grants := []*Grant{} | |||
resp, err := s.client.Do(ctx, req, &grants) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return grants, resp, nil | |||
} | |||
// GetGrant gets a single OAuth application grant. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant | |||
func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) { | |||
u := fmt.Sprintf("applications/grants/%d", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
grant := new(Grant) | |||
resp, err := s.client.Do(ctx, req, grant) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return grant, resp, nil | |||
} | |||
// DeleteGrant deletes an OAuth application grant. Deleting an application's | |||
// grant will also delete all OAuth tokens associated with the application for | |||
// the user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant | |||
func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) { | |||
u := fmt.Sprintf("applications/grants/%d", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// CreateImpersonation creates an impersonation OAuth token. | |||
// | |||
// This requires admin permissions. With the returned Authorization.Token | |||
// you can e.g. create or delete a user's public SSH key. NOTE: creating a | |||
// new token automatically revokes an existing one. | |||
// | |||
// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#create-an-impersonation-oauth-token | |||
func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) { | |||
u := fmt.Sprintf("admin/users/%v/authorizations", username) | |||
req, err := s.client.NewRequest("POST", u, authReq) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
a := new(Authorization) | |||
resp, err := s.client.Do(ctx, req, a) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return a, resp, nil | |||
} | |||
// DeleteImpersonation deletes an impersonation OAuth token. | |||
// | |||
// NOTE: there can be only one at a time. | |||
// | |||
// GitHub API docs: https://developer.github.com/enterprise/v3/enterprise-admin/users/#delete-an-impersonation-oauth-token | |||
func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) { | |||
u := fmt.Sprintf("admin/users/%v/authorizations", username) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,432 @@ | |||
// Copyright 2018 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"net/url" | |||
) | |||
// ChecksService provides access to the Checks API in the | |||
// GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/ | |||
type ChecksService service | |||
// CheckRun represents a GitHub check run on a repository associated with a GitHub app. | |||
type CheckRun struct { | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
HeadSHA *string `json:"head_sha,omitempty"` | |||
ExternalID *string `json:"external_id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
DetailsURL *string `json:"details_url,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Conclusion *string `json:"conclusion,omitempty"` | |||
StartedAt *Timestamp `json:"started_at,omitempty"` | |||
CompletedAt *Timestamp `json:"completed_at,omitempty"` | |||
Output *CheckRunOutput `json:"output,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
CheckSuite *CheckSuite `json:"check_suite,omitempty"` | |||
App *App `json:"app,omitempty"` | |||
PullRequests []*PullRequest `json:"pull_requests,omitempty"` | |||
} | |||
// CheckRunOutput represents the output of a CheckRun. | |||
type CheckRunOutput struct { | |||
Title *string `json:"title,omitempty"` | |||
Summary *string `json:"summary,omitempty"` | |||
Text *string `json:"text,omitempty"` | |||
AnnotationsCount *int `json:"annotations_count,omitempty"` | |||
AnnotationsURL *string `json:"annotations_url,omitempty"` | |||
Annotations []*CheckRunAnnotation `json:"annotations,omitempty"` | |||
Images []*CheckRunImage `json:"images,omitempty"` | |||
} | |||
// CheckRunAnnotation represents an annotation object for a CheckRun output. | |||
type CheckRunAnnotation struct { | |||
Path *string `json:"path,omitempty"` | |||
BlobHRef *string `json:"blob_href,omitempty"` | |||
StartLine *int `json:"start_line,omitempty"` | |||
EndLine *int `json:"end_line,omitempty"` | |||
AnnotationLevel *string `json:"annotation_level,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Title *string `json:"title,omitempty"` | |||
RawDetails *string `json:"raw_details,omitempty"` | |||
} | |||
// CheckRunImage represents an image object for a CheckRun output. | |||
type CheckRunImage struct { | |||
Alt *string `json:"alt,omitempty"` | |||
ImageURL *string `json:"image_url,omitempty"` | |||
Caption *string `json:"caption,omitempty"` | |||
} | |||
// CheckSuite represents a suite of check runs. | |||
type CheckSuite struct { | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
HeadBranch *string `json:"head_branch,omitempty"` | |||
HeadSHA *string `json:"head_sha,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
BeforeSHA *string `json:"before,omitempty"` | |||
AfterSHA *string `json:"after,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Conclusion *string `json:"conclusion,omitempty"` | |||
App *App `json:"app,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
PullRequests []*PullRequest `json:"pull_requests,omitempty"` | |||
} | |||
func (c CheckRun) String() string { | |||
return Stringify(c) | |||
} | |||
func (c CheckSuite) String() string { | |||
return Stringify(c) | |||
} | |||
// GetCheckRun gets a check-run for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#get-a-single-check-run | |||
func (s *ChecksService) GetCheckRun(ctx context.Context, owner, repo string, checkRunID int64) (*CheckRun, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
checkRun := new(CheckRun) | |||
resp, err := s.client.Do(ctx, req, checkRun) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRun, resp, nil | |||
} | |||
// GetCheckSuite gets a single check suite. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#get-a-single-check-suite | |||
func (s *ChecksService) GetCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*CheckSuite, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-suites/%v", owner, repo, checkSuiteID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
checkSuite := new(CheckSuite) | |||
resp, err := s.client.Do(ctx, req, checkSuite) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkSuite, resp, nil | |||
} | |||
// CreateCheckRunOptions sets up parameters needed to create a CheckRun. | |||
type CreateCheckRunOptions struct { | |||
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.) | |||
HeadBranch string `json:"head_branch"` // The name of the branch to perform a check against. (Required.) | |||
HeadSHA string `json:"head_sha"` // The SHA of the commit. (Required.) | |||
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.) | |||
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.) | |||
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.) | |||
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) | |||
StartedAt *Timestamp `json:"started_at,omitempty"` // The time that the check run began. (Optional.) | |||
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.) | |||
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional) | |||
Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.) | |||
} | |||
// CheckRunAction exposes further actions the integrator can perform, which a user may trigger. | |||
type CheckRunAction struct { | |||
Label string `json:"label"` // The text to be displayed on a button in the web UI. The maximum size is 20 characters. (Required.) | |||
Description string `json:"description"` // A short explanation of what this action would do. The maximum size is 40 characters. (Required.) | |||
Identifier string `json:"identifier"` // A reference for the action on the integrator's system. The maximum size is 20 characters. (Required.) | |||
} | |||
// CreateCheckRun creates a check run for repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#create-a-check-run | |||
func (s *ChecksService) CreateCheckRun(ctx context.Context, owner, repo string, opt CreateCheckRunOptions) (*CheckRun, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-runs", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
checkRun := new(CheckRun) | |||
resp, err := s.client.Do(ctx, req, checkRun) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRun, resp, nil | |||
} | |||
// UpdateCheckRunOptions sets up parameters needed to update a CheckRun. | |||
type UpdateCheckRunOptions struct { | |||
Name string `json:"name"` // The name of the check (e.g., "code-coverage"). (Required.) | |||
HeadBranch *string `json:"head_branch,omitempty"` // The name of the branch to perform a check against. (Optional.) | |||
HeadSHA *string `json:"head_sha,omitempty"` // The SHA of the commit. (Optional.) | |||
DetailsURL *string `json:"details_url,omitempty"` // The URL of the integrator's site that has the full details of the check. (Optional.) | |||
ExternalID *string `json:"external_id,omitempty"` // A reference for the run on the integrator's system. (Optional.) | |||
Status *string `json:"status,omitempty"` // The current status. Can be one of "queued", "in_progress", or "completed". Default: "queued". (Optional.) | |||
Conclusion *string `json:"conclusion,omitempty"` // Can be one of "success", "failure", "neutral", "cancelled", "timed_out", or "action_required". (Optional. Required if you provide a status of "completed".) | |||
CompletedAt *Timestamp `json:"completed_at,omitempty"` // The time the check completed. (Optional. Required if you provide conclusion.) | |||
Output *CheckRunOutput `json:"output,omitempty"` // Provide descriptive details about the run. (Optional) | |||
Actions []*CheckRunAction `json:"actions,omitempty"` // Possible further actions the integrator can perform, which a user may trigger. (Optional.) | |||
} | |||
// UpdateCheckRun updates a check run for a specific commit in a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#update-a-check-run | |||
func (s *ChecksService) UpdateCheckRun(ctx context.Context, owner, repo string, checkRunID int64, opt UpdateCheckRunOptions) (*CheckRun, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-runs/%v", owner, repo, checkRunID) | |||
req, err := s.client.NewRequest("PATCH", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
checkRun := new(CheckRun) | |||
resp, err := s.client.Do(ctx, req, checkRun) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRun, resp, nil | |||
} | |||
// ListCheckRunAnnotations lists the annotations for a check run. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-annotations-for-a-check-run | |||
func (s *ChecksService) ListCheckRunAnnotations(ctx context.Context, owner, repo string, checkRunID int64, opt *ListOptions) ([]*CheckRunAnnotation, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-runs/%v/annotations", owner, repo, checkRunID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
var checkRunAnnotations []*CheckRunAnnotation | |||
resp, err := s.client.Do(ctx, req, &checkRunAnnotations) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRunAnnotations, resp, nil | |||
} | |||
// ListCheckRunsOptions represents parameters to list check runs. | |||
type ListCheckRunsOptions struct { | |||
CheckName *string `url:"check_name,omitempty"` // Returns check runs with the specified name. | |||
Status *string `url:"status,omitempty"` // Returns check runs with the specified status. Can be one of "queued", "in_progress", or "completed". | |||
Filter *string `url:"filter,omitempty"` // Filters check runs by their completed_at timestamp. Can be one of "latest" (returning the most recent check runs) or "all". Default: "latest" | |||
ListOptions | |||
} | |||
// ListCheckRunsResults represents the result of a check run list. | |||
type ListCheckRunsResults struct { | |||
Total *int `json:"total_count,omitempty"` | |||
CheckRuns []*CheckRun `json:"check_runs,omitempty"` | |||
} | |||
// ListCheckRunsForRef lists check runs for a specific ref. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-for-a-specific-ref | |||
func (s *ChecksService) ListCheckRunsForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-runs", owner, repo, url.QueryEscape(ref)) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
var checkRunResults *ListCheckRunsResults | |||
resp, err := s.client.Do(ctx, req, &checkRunResults) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRunResults, resp, nil | |||
} | |||
// ListCheckRunsCheckSuite lists check runs for a check suite. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/runs/#list-check-runs-in-a-check-suite | |||
func (s *ChecksService) ListCheckRunsCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64, opt *ListCheckRunsOptions) (*ListCheckRunsResults, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/check-runs", owner, repo, checkSuiteID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
var checkRunResults *ListCheckRunsResults | |||
resp, err := s.client.Do(ctx, req, &checkRunResults) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkRunResults, resp, nil | |||
} | |||
// ListCheckSuiteOptions represents parameters to list check suites. | |||
type ListCheckSuiteOptions struct { | |||
CheckName *string `url:"check_name,omitempty"` // Filters checks suites by the name of the check run. | |||
AppID *int `url:"app_id,omitempty"` // Filters check suites by GitHub App id. | |||
ListOptions | |||
} | |||
// ListCheckSuiteResults represents the result of a check run list. | |||
type ListCheckSuiteResults struct { | |||
Total *int `json:"total_count,omitempty"` | |||
CheckSuites []*CheckSuite `json:"check_suites,omitempty"` | |||
} | |||
// ListCheckSuitesForRef lists check suite for a specific ref. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#list-check-suites-for-a-specific-ref | |||
func (s *ChecksService) ListCheckSuitesForRef(ctx context.Context, owner, repo, ref string, opt *ListCheckSuiteOptions) (*ListCheckSuiteResults, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v/check-suites", owner, repo, url.QueryEscape(ref)) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
var checkSuiteResults *ListCheckSuiteResults | |||
resp, err := s.client.Do(ctx, req, &checkSuiteResults) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkSuiteResults, resp, nil | |||
} | |||
// AutoTriggerCheck enables or disables automatic creation of CheckSuite events upon pushes to the repository. | |||
type AutoTriggerCheck struct { | |||
AppID *int64 `json:"app_id,omitempty"` // The id of the GitHub App. (Required.) | |||
Setting *bool `json:"setting,omitempty"` // Set to "true" to enable automatic creation of CheckSuite events upon pushes to the repository, or "false" to disable them. Default: "true" (Required.) | |||
} | |||
// CheckSuitePreferenceOptions set options for check suite preferences for a repository. | |||
type CheckSuitePreferenceOptions struct { | |||
PreferenceList *PreferenceList `json:"auto_trigger_checks,omitempty"` // A list of auto trigger checks that can be set for a check suite in a repository. | |||
} | |||
// CheckSuitePreferenceResults represents the results of the preference set operation. | |||
type CheckSuitePreferenceResults struct { | |||
Preferences *PreferenceList `json:"preferences,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
} | |||
// PreferenceList represents a list of auto trigger checks for repository | |||
type PreferenceList struct { | |||
AutoTriggerChecks []*AutoTriggerCheck `json:"auto_trigger_checks,omitempty"` // A slice of auto trigger checks that can be set for a check suite in a repository. | |||
} | |||
// SetCheckSuitePreferences changes the default automatic flow when creating check suites. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#set-preferences-for-check-suites-on-a-repository | |||
func (s *ChecksService) SetCheckSuitePreferences(ctx context.Context, owner, repo string, opt CheckSuitePreferenceOptions) (*CheckSuitePreferenceResults, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-suites/preferences", owner, repo) | |||
req, err := s.client.NewRequest("PATCH", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
var checkSuitePrefResults *CheckSuitePreferenceResults | |||
resp, err := s.client.Do(ctx, req, &checkSuitePrefResults) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkSuitePrefResults, resp, nil | |||
} | |||
// CreateCheckSuiteOptions sets up parameters to manually create a check suites | |||
type CreateCheckSuiteOptions struct { | |||
HeadSHA string `json:"head_sha"` // The sha of the head commit. (Required.) | |||
HeadBranch *string `json:"head_branch,omitempty"` // The name of the head branch where the code changes are implemented. | |||
} | |||
// CreateCheckSuite manually creates a check suite for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#create-a-check-suite | |||
func (s *ChecksService) CreateCheckSuite(ctx context.Context, owner, repo string, opt CreateCheckSuiteOptions) (*CheckSuite, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-suites", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
checkSuite := new(CheckSuite) | |||
resp, err := s.client.Do(ctx, req, checkSuite) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return checkSuite, resp, nil | |||
} | |||
// ReRequestCheckSuite triggers GitHub to rerequest an existing check suite, without pushing new code to a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/checks/suites/#rerequest-check-suite | |||
func (s *ChecksService) ReRequestCheckSuite(ctx context.Context, owner, repo string, checkSuiteID int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/check-suites/%v/rerequest", owner, repo, checkSuiteID) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeCheckRunsPreview) | |||
resp, err := s.client.Do(ctx, req, nil) | |||
return resp, err | |||
} |
@ -0,0 +1,188 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
/* | |||
Package github provides a client for using the GitHub API. | |||
Usage: | |||
import "github.com/google/go-github/v24/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) | |||
import "github.com/google/go-github/github" // with go modules disabled | |||
Construct a new GitHub client, then use the various services on the client to | |||
access different parts of the GitHub API. For example: | |||
client := github.NewClient(nil) | |||
// list all organizations for user "willnorris" | |||
orgs, _, err := client.Organizations.List(ctx, "willnorris", nil) | |||
Some API methods have optional parameters that can be passed. For example: | |||
client := github.NewClient(nil) | |||
// list public repositories for org "github" | |||
opt := &github.RepositoryListByOrgOptions{Type: "public"} | |||
repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt) | |||
The services of a client divide the API into logical chunks and correspond to | |||
the structure of the GitHub API documentation at | |||
https://developer.github.com/v3/. | |||
NOTE: Using the https://godoc.org/context package, one can easily | |||
pass cancelation signals and deadlines to various services of the client for | |||
handling a request. In case there is no context available, then context.Background() | |||
can be used as a starting point. | |||
For more sample code snippets, head over to the https://github.com/google/go-github/tree/master/example directory. | |||
Authentication | |||
The go-github library does not directly handle authentication. Instead, when | |||
creating a new client, pass an http.Client that can handle authentication for | |||
you. The easiest and recommended way to do this is using the golang.org/x/oauth2 | |||
library, but you can always use any other library that provides an http.Client. | |||
If you have an OAuth2 access token (for example, a personal API token), you can | |||
use it with the oauth2 library using: | |||
import "golang.org/x/oauth2" | |||
func main() { | |||
ctx := context.Background() | |||
ts := oauth2.StaticTokenSource( | |||
&oauth2.Token{AccessToken: "... your access token ..."}, | |||
) | |||
tc := oauth2.NewClient(ctx, ts) | |||
client := github.NewClient(tc) | |||
// list all repositories for the authenticated user | |||
repos, _, err := client.Repositories.List(ctx, "", nil) | |||
} | |||
Note that when using an authenticated Client, all calls made by the client will | |||
include the specified OAuth token. Therefore, authenticated clients should | |||
almost never be shared between different users. | |||
See the oauth2 docs for complete instructions on using that library. | |||
For API methods that require HTTP Basic Authentication, use the | |||
BasicAuthTransport. | |||
GitHub Apps authentication can be provided by the | |||
https://github.com/bradleyfalzon/ghinstallation package. | |||
import "github.com/bradleyfalzon/ghinstallation" | |||
func main() { | |||
// Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99. | |||
itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem") | |||
if err != nil { | |||
// Handle error. | |||
} | |||
// Use installation transport with client | |||
client := github.NewClient(&http.Client{Transport: itr}) | |||
// Use client... | |||
} | |||
Rate Limiting | |||
GitHub imposes a rate limit on all API clients. Unauthenticated clients are | |||
limited to 60 requests per hour, while authenticated clients can make up to | |||
5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated | |||
clients are limited to 10 requests per minute, while authenticated clients | |||
can make up to 30 requests per minute. To receive the higher rate limit when | |||
making calls that are not issued on behalf of a user, | |||
use UnauthenticatedRateLimitedTransport. | |||
The returned Response.Rate value contains the rate limit information | |||
from the most recent API call. If a recent enough response isn't | |||
available, you can use RateLimits to fetch the most up-to-date rate | |||
limit data for the client. | |||
To detect an API rate limit error, you can check if its type is *github.RateLimitError: | |||
repos, _, err := client.Repositories.List(ctx, "", nil) | |||
if _, ok := err.(*github.RateLimitError); ok { | |||
log.Println("hit rate limit") | |||
} | |||
Learn more about GitHub rate limiting at | |||
https://developer.github.com/v3/#rate-limiting. | |||
Accepted Status | |||
Some endpoints may return a 202 Accepted status code, meaning that the | |||
information required is not yet ready and was scheduled to be gathered on | |||
the GitHub side. Methods known to behave like this are documented specifying | |||
this behavior. | |||
To detect this condition of error, you can check if its type is | |||
*github.AcceptedError: | |||
stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) | |||
if _, ok := err.(*github.AcceptedError); ok { | |||
log.Println("scheduled on GitHub side") | |||
} | |||
Conditional Requests | |||
The GitHub API has good support for conditional requests which will help | |||
prevent you from burning through your rate limit, as well as help speed up your | |||
application. go-github does not handle conditional requests directly, but is | |||
instead designed to work with a caching http.Transport. We recommend using | |||
https://github.com/gregjones/httpcache for that. | |||
Learn more about GitHub conditional requests at | |||
https://developer.github.com/v3/#conditional-requests. | |||
Creating and Updating Resources | |||
All structs for GitHub resources use pointer values for all non-repeated fields. | |||
This allows distinguishing between unset fields and those set to a zero-value. | |||
Helper functions have been provided to easily create these pointers for string, | |||
bool, and int values. For example: | |||
// create a new private repository named "foo" | |||
repo := &github.Repository{ | |||
Name: github.String("foo"), | |||
Private: github.Bool(true), | |||
} | |||
client.Repositories.Create(ctx, "", repo) | |||
Users who have worked with protocol buffers should find this pattern familiar. | |||
Pagination | |||
All requests for resource collections (repos, pull requests, issues, etc.) | |||
support pagination. Pagination options are described in the | |||
github.ListOptions struct and passed to the list methods directly or as an | |||
embedded type of a more specific list options struct (for example | |||
github.PullRequestListOptions). Pages information is available via the | |||
github.Response struct. | |||
client := github.NewClient(nil) | |||
opt := &github.RepositoryListByOrgOptions{ | |||
ListOptions: github.ListOptions{PerPage: 10}, | |||
} | |||
// get all pages of results | |||
var allRepos []*github.Repository | |||
for { | |||
repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt) | |||
if err != nil { | |||
return err | |||
} | |||
allRepos = append(allRepos, repos...) | |||
if resp.NextPage == 0 { | |||
break | |||
} | |||
opt.Page = resp.NextPage | |||
} | |||
*/ | |||
package github |
@ -0,0 +1,126 @@ | |||
// Copyright 2018 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"encoding/json" | |||
"time" | |||
) | |||
// Event represents a GitHub event. | |||
type Event struct { | |||
Type *string `json:"type,omitempty"` | |||
Public *bool `json:"public,omitempty"` | |||
RawPayload *json.RawMessage `json:"payload,omitempty"` | |||
Repo *Repository `json:"repo,omitempty"` | |||
Actor *User `json:"actor,omitempty"` | |||
Org *Organization `json:"org,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
ID *string `json:"id,omitempty"` | |||
} | |||
func (e Event) String() string { | |||
return Stringify(e) | |||
} | |||
// ParsePayload parses the event payload. For recognized event types, | |||
// a value of the corresponding struct type will be returned. | |||
func (e *Event) ParsePayload() (payload interface{}, err error) { | |||
switch *e.Type { | |||
case "CheckRunEvent": | |||
payload = &CheckRunEvent{} | |||
case "CheckSuiteEvent": | |||
payload = &CheckSuiteEvent{} | |||
case "CommitCommentEvent": | |||
payload = &CommitCommentEvent{} | |||
case "CreateEvent": | |||
payload = &CreateEvent{} | |||
case "DeleteEvent": | |||
payload = &DeleteEvent{} | |||
case "DeploymentEvent": | |||
payload = &DeploymentEvent{} | |||
case "DeploymentStatusEvent": | |||
payload = &DeploymentStatusEvent{} | |||
case "ForkEvent": | |||
payload = &ForkEvent{} | |||
case "GitHubAppAuthorizationEvent": | |||
payload = &GitHubAppAuthorizationEvent{} | |||
case "GollumEvent": | |||
payload = &GollumEvent{} | |||
case "InstallationEvent": | |||
payload = &InstallationEvent{} | |||
case "InstallationRepositoriesEvent": | |||
payload = &InstallationRepositoriesEvent{} | |||
case "IssueCommentEvent": | |||
payload = &IssueCommentEvent{} | |||
case "IssuesEvent": | |||
payload = &IssuesEvent{} | |||
case "LabelEvent": | |||
payload = &LabelEvent{} | |||
case "MarketplacePurchaseEvent": | |||
payload = &MarketplacePurchaseEvent{} | |||
case "MemberEvent": | |||
payload = &MemberEvent{} | |||
case "MembershipEvent": | |||
payload = &MembershipEvent{} | |||
case "MilestoneEvent": | |||
payload = &MilestoneEvent{} | |||
case "OrganizationEvent": | |||
payload = &OrganizationEvent{} | |||
case "OrgBlockEvent": | |||
payload = &OrgBlockEvent{} | |||
case "PageBuildEvent": | |||
payload = &PageBuildEvent{} | |||
case "PingEvent": | |||
payload = &PingEvent{} | |||
case "ProjectEvent": | |||
payload = &ProjectEvent{} | |||
case "ProjectCardEvent": | |||
payload = &ProjectCardEvent{} | |||
case "ProjectColumnEvent": | |||
payload = &ProjectColumnEvent{} | |||
case "PublicEvent": | |||
payload = &PublicEvent{} | |||
case "PullRequestEvent": | |||
payload = &PullRequestEvent{} | |||
case "PullRequestReviewEvent": | |||
payload = &PullRequestReviewEvent{} | |||
case "PullRequestReviewCommentEvent": | |||
payload = &PullRequestReviewCommentEvent{} | |||
case "PushEvent": | |||
payload = &PushEvent{} | |||
case "ReleaseEvent": | |||
payload = &ReleaseEvent{} | |||
case "RepositoryEvent": | |||
payload = &RepositoryEvent{} | |||
case "RepositoryVulnerabilityAlertEvent": | |||
payload = &RepositoryVulnerabilityAlertEvent{} | |||
case "StatusEvent": | |||
payload = &StatusEvent{} | |||
case "TeamEvent": | |||
payload = &TeamEvent{} | |||
case "TeamAddEvent": | |||
payload = &TeamAddEvent{} | |||
case "WatchEvent": | |||
payload = &WatchEvent{} | |||
} | |||
err = json.Unmarshal(*e.RawPayload, &payload) | |||
return payload, err | |||
} | |||
// Payload returns the parsed event payload. For recognized event types, | |||
// a value of the corresponding struct type will be returned. | |||
// | |||
// Deprecated: Use ParsePayload instead, which returns an error | |||
// rather than panics if JSON unmarshaling raw payload fails. | |||
func (e *Event) Payload() (payload interface{}) { | |||
var err error | |||
payload, err = e.ParsePayload() | |||
if err != nil { | |||
panic(err) | |||
} | |||
return payload | |||
} |
@ -0,0 +1,833 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// These event types are shared between the Events API and used as Webhook payloads. | |||
package github | |||
// RequestedAction is included in a CheckRunEvent when a user has invoked an action, | |||
// i.e. when the CheckRunEvent's Action field is "requested_action". | |||
type RequestedAction struct { | |||
Identifier string `json:"identifier"` // The integrator reference of the action requested by the user. | |||
} | |||
// CheckRunEvent is triggered when a check run is "created", "updated", or "re-requested". | |||
// The Webhook event name is "check_run". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checkrunevent | |||
type CheckRunEvent struct { | |||
CheckRun *CheckRun `json:"check_run,omitempty"` | |||
// The action performed. Can be "created", "updated", "rerequested" or "requested_action". | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
// The action requested by the user. Populated when the Action is "requested_action". | |||
RequestedAction *RequestedAction `json:"requested_action,omitempty"` // | |||
} | |||
// CheckSuiteEvent is triggered when a check suite is "completed", "requested", or "re-requested". | |||
// The Webhook event name is "check_suite". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#checksuiteevent | |||
type CheckSuiteEvent struct { | |||
CheckSuite *CheckSuite `json:"check_suite,omitempty"` | |||
// The action performed. Can be "completed", "requested" or "re-requested". | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// CommitCommentEvent is triggered when a commit comment is created. | |||
// The Webhook event name is "commit_comment". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent | |||
type CommitCommentEvent struct { | |||
Comment *RepositoryComment `json:"comment,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Action *string `json:"action,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// CreateEvent represents a created repository, branch, or tag. | |||
// The Webhook event name is "create". | |||
// | |||
// Note: webhooks will not receive this event for created repositories. | |||
// Additionally, webhooks will not receive this event for tags if more | |||
// than three tags are pushed at once. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent | |||
type CreateEvent struct { | |||
Ref *string `json:"ref,omitempty"` | |||
// RefType is the object that was created. Possible values are: "repository", "branch", "tag". | |||
RefType *string `json:"ref_type,omitempty"` | |||
MasterBranch *string `json:"master_branch,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
PusherType *string `json:"pusher_type,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// DeleteEvent represents a deleted branch or tag. | |||
// The Webhook event name is "delete". | |||
// | |||
// Note: webhooks will not receive this event for tags if more than three tags | |||
// are deleted at once. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent | |||
type DeleteEvent struct { | |||
Ref *string `json:"ref,omitempty"` | |||
// RefType is the object that was deleted. Possible values are: "branch", "tag". | |||
RefType *string `json:"ref_type,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
PusherType *string `json:"pusher_type,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// DeploymentEvent represents a deployment. | |||
// The Webhook event name is "deployment". | |||
// | |||
// Events of this type are not visible in timelines, they are only used to trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent | |||
type DeploymentEvent struct { | |||
Deployment *Deployment `json:"deployment,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// DeploymentStatusEvent represents a deployment status. | |||
// The Webhook event name is "deployment_status". | |||
// | |||
// Events of this type are not visible in timelines, they are only used to trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent | |||
type DeploymentStatusEvent struct { | |||
Deployment *Deployment `json:"deployment,omitempty"` | |||
DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// ForkEvent is triggered when a user forks a repository. | |||
// The Webhook event name is "fork". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent | |||
type ForkEvent struct { | |||
// Forkee is the created repository. | |||
Forkee *Repository `json:"forkee,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// GitHubAppAuthorizationEvent is triggered when a user's authorization for a | |||
// GitHub Application is revoked. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#githubappauthorizationevent | |||
type GitHubAppAuthorizationEvent struct { | |||
// The action performed. Can be "revoked". | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Sender *User `json:"sender,omitempty"` | |||
} | |||
// Page represents a single Wiki page. | |||
type Page struct { | |||
PageName *string `json:"page_name,omitempty"` | |||
Title *string `json:"title,omitempty"` | |||
Summary *string `json:"summary,omitempty"` | |||
Action *string `json:"action,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
} | |||
// GollumEvent is triggered when a Wiki page is created or updated. | |||
// The Webhook event name is "gollum". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent | |||
type GollumEvent struct { | |||
Pages []*Page `json:"pages,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// EditChange represents the changes when an issue, pull request, or comment has | |||
// been edited. | |||
type EditChange struct { | |||
Title *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"title,omitempty"` | |||
Body *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"body,omitempty"` | |||
} | |||
// ProjectChange represents the changes when a project has been edited. | |||
type ProjectChange struct { | |||
Name *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"name,omitempty"` | |||
Body *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"body,omitempty"` | |||
} | |||
// ProjectCardChange represents the changes when a project card has been edited. | |||
type ProjectCardChange struct { | |||
Note *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"note,omitempty"` | |||
} | |||
// ProjectColumnChange represents the changes when a project column has been edited. | |||
type ProjectColumnChange struct { | |||
Name *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"name,omitempty"` | |||
} | |||
// TeamChange represents the changes when a team has been edited. | |||
type TeamChange struct { | |||
Description *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"description,omitempty"` | |||
Name *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"name,omitempty"` | |||
Privacy *struct { | |||
From *string `json:"from,omitempty"` | |||
} `json:"privacy,omitempty"` | |||
Repository *struct { | |||
Permissions *struct { | |||
From *struct { | |||
Admin *bool `json:"admin,omitempty"` | |||
Pull *bool `json:"pull,omitempty"` | |||
Push *bool `json:"push,omitempty"` | |||
} `json:"from,omitempty"` | |||
} `json:"permissions,omitempty"` | |||
} `json:"repository,omitempty"` | |||
} | |||
// InstallationEvent is triggered when a GitHub App has been installed or uninstalled. | |||
// The Webhook event name is "installation". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationevent | |||
type InstallationEvent struct { | |||
// The action that was performed. Can be either "created" or "deleted". | |||
Action *string `json:"action,omitempty"` | |||
Repositories []*Repository `json:"repositories,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// InstallationRepositoriesEvent is triggered when a repository is added or | |||
// removed from an installation. The Webhook event name is "installation_repositories". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationrepositoriesevent | |||
type InstallationRepositoriesEvent struct { | |||
// The action that was performed. Can be either "added" or "removed". | |||
Action *string `json:"action,omitempty"` | |||
RepositoriesAdded []*Repository `json:"repositories_added,omitempty"` | |||
RepositoriesRemoved []*Repository `json:"repositories_removed,omitempty"` | |||
RepositorySelection *string `json:"repository_selection,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// IssueCommentEvent is triggered when an issue comment is created on an issue | |||
// or pull request. | |||
// The Webhook event name is "issue_comment". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent | |||
type IssueCommentEvent struct { | |||
// Action is the action that was performed on the comment. | |||
// Possible values are: "created", "edited", "deleted". | |||
Action *string `json:"action,omitempty"` | |||
Issue *Issue `json:"issue,omitempty"` | |||
Comment *IssueComment `json:"comment,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// IssuesEvent is triggered when an issue is assigned, unassigned, labeled, | |||
// unlabeled, opened, closed, or reopened. | |||
// The Webhook event name is "issues". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent | |||
type IssuesEvent struct { | |||
// Action is the action that was performed. Possible values are: "assigned", | |||
// "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited". | |||
Action *string `json:"action,omitempty"` | |||
Issue *Issue `json:"issue,omitempty"` | |||
Assignee *User `json:"assignee,omitempty"` | |||
Label *Label `json:"label,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// LabelEvent is triggered when a repository's label is created, edited, or deleted. | |||
// The Webhook event name is "label" | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent | |||
type LabelEvent struct { | |||
// Action is the action that was performed. Possible values are: | |||
// "created", "edited", "deleted" | |||
Action *string `json:"action,omitempty"` | |||
Label *Label `json:"label,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// MarketplacePurchaseEvent is triggered when a user purchases, cancels, or changes | |||
// their GitHub Marketplace plan. | |||
// Webhook event name "marketplace_purchase". | |||
// | |||
// Github API docs: https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent | |||
type MarketplacePurchaseEvent struct { | |||
// Action is the action that was performed. Possible values are: | |||
// "purchased", "cancelled", "pending_change", "pending_change_cancelled", "changed". | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
EffectiveDate *Timestamp `json:"effective_date,omitempty"` | |||
MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` | |||
PreviousMarketplacePurchase *MarketplacePurchase `json:"previous_marketplace_purchase,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// MemberEvent is triggered when a user is added as a collaborator to a repository. | |||
// The Webhook event name is "member". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent | |||
type MemberEvent struct { | |||
// Action is the action that was performed. Possible value is: "added". | |||
Action *string `json:"action,omitempty"` | |||
Member *User `json:"member,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// MembershipEvent is triggered when a user is added or removed from a team. | |||
// The Webhook event name is "membership". | |||
// | |||
// Events of this type are not visible in timelines, they are only used to | |||
// trigger organization webhooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent | |||
type MembershipEvent struct { | |||
// Action is the action that was performed. Possible values are: "added", "removed". | |||
Action *string `json:"action,omitempty"` | |||
// Scope is the scope of the membership. Possible value is: "team". | |||
Scope *string `json:"scope,omitempty"` | |||
Member *User `json:"member,omitempty"` | |||
Team *Team `json:"team,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted. | |||
// The Webhook event name is "milestone". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent | |||
type MilestoneEvent struct { | |||
// Action is the action that was performed. Possible values are: | |||
// "created", "closed", "opened", "edited", "deleted" | |||
Action *string `json:"action,omitempty"` | |||
Milestone *Milestone `json:"milestone,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// OrganizationEvent is triggered when a user is added, removed, or invited to an organization. | |||
// Events of this type are not visible in timelines. These events are only used to trigger organization hooks. | |||
// Webhook event name is "organization". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent | |||
type OrganizationEvent struct { | |||
// Action is the action that was performed. | |||
// Can be one of "member_added", "member_removed", or "member_invited". | |||
Action *string `json:"action,omitempty"` | |||
// Invitaion is the invitation for the user or email if the action is "member_invited". | |||
Invitation *Invitation `json:"invitation,omitempty"` | |||
// Membership is the membership between the user and the organization. | |||
// Not present when the action is "member_invited". | |||
Membership *Membership `json:"membership,omitempty"` | |||
Organization *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// OrgBlockEvent is triggered when an organization blocks or unblocks a user. | |||
// The Webhook event name is "org_block". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#orgblockevent | |||
type OrgBlockEvent struct { | |||
// Action is the action that was performed. | |||
// Can be "blocked" or "unblocked". | |||
Action *string `json:"action,omitempty"` | |||
BlockedUser *User `json:"blocked_user,omitempty"` | |||
Organization *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PageBuildEvent represents an attempted build of a GitHub Pages site, whether | |||
// successful or not. | |||
// The Webhook event name is "page_build". | |||
// | |||
// This event is triggered on push to a GitHub Pages enabled branch (gh-pages | |||
// for project pages, master for user and organization pages). | |||
// | |||
// Events of this type are not visible in timelines, they are only used to trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent | |||
type PageBuildEvent struct { | |||
Build *PagesBuild `json:"build,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
ID *int64 `json:"id,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PingEvent is triggered when a Webhook is added to GitHub. | |||
// | |||
// GitHub API docs: https://developer.github.com/webhooks/#ping-event | |||
type PingEvent struct { | |||
// Random string of GitHub zen. | |||
Zen *string `json:"zen,omitempty"` | |||
// The ID of the webhook that triggered the ping. | |||
HookID *int64 `json:"hook_id,omitempty"` | |||
// The webhook configuration. | |||
Hook *Hook `json:"hook,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// ProjectEvent is triggered when project is created, modified or deleted. | |||
// The webhook event name is "project". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent | |||
type ProjectEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Changes *ProjectChange `json:"changes,omitempty"` | |||
Project *Project `json:"project,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted. | |||
// The webhook event name is "project_card". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent | |||
type ProjectCardEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Changes *ProjectCardChange `json:"changes,omitempty"` | |||
AfterID *int64 `json:"after_id,omitempty"` | |||
ProjectCard *ProjectCard `json:"project_card,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted. | |||
// The webhook event name is "project_column". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent | |||
type ProjectColumnEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Changes *ProjectColumnChange `json:"changes,omitempty"` | |||
AfterID *int64 `json:"after_id,omitempty"` | |||
ProjectColumn *ProjectColumn `json:"project_column,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PublicEvent is triggered when a private repository is open sourced. | |||
// According to GitHub: "Without a doubt: the best GitHub event." | |||
// The Webhook event name is "public". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent | |||
type PublicEvent struct { | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PullRequestEvent is triggered when a pull request is assigned, unassigned, | |||
// labeled, unlabeled, opened, closed, reopened, or synchronized. | |||
// The Webhook event name is "pull_request". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent | |||
type PullRequestEvent struct { | |||
// Action is the action that was performed. Possible values are: | |||
// "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled", | |||
// "opened", "closed", "reopened", "synchronize", "edited". | |||
// If the action is "closed" and the merged key is false, | |||
// the pull request was closed with unmerged commits. If the action is "closed" | |||
// and the merged key is true, the pull request was merged. | |||
Action *string `json:"action,omitempty"` | |||
Assignee *User `json:"assignee,omitempty"` | |||
Number *int `json:"number,omitempty"` | |||
PullRequest *PullRequest `json:"pull_request,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
// RequestedReviewer is populated in "review_requested", "review_request_removed" event deliveries. | |||
// A request affecting multiple reviewers at once is split into multiple | |||
// such event deliveries, each with a single, different RequestedReviewer. | |||
RequestedReviewer *User `json:"requested_reviewer,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
Label *Label `json:"label,omitempty"` // Populated in "labeled" event deliveries. | |||
// The following field is only present when the webhook is triggered on | |||
// a repository belonging to an organization. | |||
Organization *Organization `json:"organization,omitempty"` | |||
} | |||
// PullRequestReviewEvent is triggered when a review is submitted on a pull | |||
// request. | |||
// The Webhook event name is "pull_request_review". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent | |||
type PullRequestReviewEvent struct { | |||
// Action is always "submitted". | |||
Action *string `json:"action,omitempty"` | |||
Review *PullRequestReview `json:"review,omitempty"` | |||
PullRequest *PullRequest `json:"pull_request,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
// The following field is only present when the webhook is triggered on | |||
// a repository belonging to an organization. | |||
Organization *Organization `json:"organization,omitempty"` | |||
} | |||
// PullRequestReviewCommentEvent is triggered when a comment is created on a | |||
// portion of the unified diff of a pull request. | |||
// The Webhook event name is "pull_request_review_comment". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent | |||
type PullRequestReviewCommentEvent struct { | |||
// Action is the action that was performed on the comment. | |||
// Possible values are: "created", "edited", "deleted". | |||
Action *string `json:"action,omitempty"` | |||
PullRequest *PullRequest `json:"pull_request,omitempty"` | |||
Comment *PullRequestComment `json:"comment,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Changes *EditChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// PushEvent represents a git push to a GitHub repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent | |||
type PushEvent struct { | |||
PushID *int64 `json:"push_id,omitempty"` | |||
Head *string `json:"head,omitempty"` | |||
Ref *string `json:"ref,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
Commits []PushEventCommit `json:"commits,omitempty"` | |||
Before *string `json:"before,omitempty"` | |||
DistinctSize *int `json:"distinct_size,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
After *string `json:"after,omitempty"` | |||
Created *bool `json:"created,omitempty"` | |||
Deleted *bool `json:"deleted,omitempty"` | |||
Forced *bool `json:"forced,omitempty"` | |||
BaseRef *string `json:"base_ref,omitempty"` | |||
Compare *string `json:"compare,omitempty"` | |||
Repo *PushEventRepository `json:"repository,omitempty"` | |||
HeadCommit *PushEventCommit `json:"head_commit,omitempty"` | |||
Pusher *User `json:"pusher,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
func (p PushEvent) String() string { | |||
return Stringify(p) | |||
} | |||
// PushEventCommit represents a git commit in a GitHub PushEvent. | |||
type PushEventCommit struct { | |||
Message *string `json:"message,omitempty"` | |||
Author *CommitAuthor `json:"author,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Distinct *bool `json:"distinct,omitempty"` | |||
// The following fields are only populated by Events API. | |||
SHA *string `json:"sha,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
ID *string `json:"id,omitempty"` | |||
TreeID *string `json:"tree_id,omitempty"` | |||
Timestamp *Timestamp `json:"timestamp,omitempty"` | |||
Committer *CommitAuthor `json:"committer,omitempty"` | |||
Added []string `json:"added,omitempty"` | |||
Removed []string `json:"removed,omitempty"` | |||
Modified []string `json:"modified,omitempty"` | |||
} | |||
func (p PushEventCommit) String() string { | |||
return Stringify(p) | |||
} | |||
// PushEventRepository represents the repo object in a PushEvent payload. | |||
type PushEventRepository struct { | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
FullName *string `json:"full_name,omitempty"` | |||
Owner *User `json:"owner,omitempty"` | |||
Private *bool `json:"private,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Fork *bool `json:"fork,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
PushedAt *Timestamp `json:"pushed_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
Homepage *string `json:"homepage,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
StargazersCount *int `json:"stargazers_count,omitempty"` | |||
WatchersCount *int `json:"watchers_count,omitempty"` | |||
Language *string `json:"language,omitempty"` | |||
HasIssues *bool `json:"has_issues,omitempty"` | |||
HasDownloads *bool `json:"has_downloads,omitempty"` | |||
HasWiki *bool `json:"has_wiki,omitempty"` | |||
HasPages *bool `json:"has_pages,omitempty"` | |||
ForksCount *int `json:"forks_count,omitempty"` | |||
OpenIssuesCount *int `json:"open_issues_count,omitempty"` | |||
DefaultBranch *string `json:"default_branch,omitempty"` | |||
MasterBranch *string `json:"master_branch,omitempty"` | |||
Organization *string `json:"organization,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
ArchiveURL *string `json:"archive_url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
StatusesURL *string `json:"statuses_url,omitempty"` | |||
GitURL *string `json:"git_url,omitempty"` | |||
SSHURL *string `json:"ssh_url,omitempty"` | |||
CloneURL *string `json:"clone_url,omitempty"` | |||
SVNURL *string `json:"svn_url,omitempty"` | |||
} | |||
// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload. | |||
type PushEventRepoOwner struct { | |||
Name *string `json:"name,omitempty"` | |||
Email *string `json:"email,omitempty"` | |||
} | |||
// ReleaseEvent is triggered when a release is published. | |||
// The Webhook event name is "release". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent | |||
type ReleaseEvent struct { | |||
// Action is the action that was performed. Possible value is: "published". | |||
Action *string `json:"action,omitempty"` | |||
Release *RepositoryRelease `json:"release,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// RepositoryEvent is triggered when a repository is created. | |||
// The Webhook event name is "repository". | |||
// | |||
// Events of this type are not visible in timelines, they are only used to | |||
// trigger organization webhooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent | |||
type RepositoryEvent struct { | |||
// Action is the action that was performed. Possible values are: "created", "deleted", | |||
// "publicized", "privatized". | |||
Action *string `json:"action,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryvulnerabilityalertevent | |||
type RepositoryVulnerabilityAlertEvent struct { | |||
// Action is the action that was performed. This can be: "create", "dismiss", "resolve". | |||
Action *string `json:"action,omitempty"` | |||
//The security alert of the vulnerable dependency. | |||
Alert *struct { | |||
ID *int64 `json:"id,omitempty"` | |||
AffectedRange *string `json:"affected_range,omitempty"` | |||
AffectedPackageName *string `json:"affected_package_name,omitempty"` | |||
ExternalReference *string `json:"external_reference,omitempty"` | |||
ExternalIdentifier *string `json:"external_identifier,omitempty"` | |||
FixedIn *string `json:"fixed_in,omitempty"` | |||
Dismisser *User `json:"dismisser,omitempty"` | |||
DismissReason *string `json:"dismiss_reason,omitempty"` | |||
DismissedAt *Timestamp `json:"dismissed_at,omitempty"` | |||
} `json:"alert,omitempty"` | |||
} | |||
// StatusEvent is triggered when the status of a Git commit changes. | |||
// The Webhook event name is "status". | |||
// | |||
// Events of this type are not visible in timelines, they are only used to | |||
// trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent | |||
type StatusEvent struct { | |||
SHA *string `json:"sha,omitempty"` | |||
// State is the new state. Possible values are: "pending", "success", "failure", "error". | |||
State *string `json:"state,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
TargetURL *string `json:"target_url,omitempty"` | |||
Branches []*Branch `json:"branches,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
ID *int64 `json:"id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Context *string `json:"context,omitempty"` | |||
Commit *RepositoryCommit `json:"commit,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// TeamEvent is triggered when an organization's team is created, modified or deleted. | |||
// The Webhook event name is "team". | |||
// | |||
// Events of this type are not visible in timelines. These events are only used | |||
// to trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamevent | |||
type TeamEvent struct { | |||
Action *string `json:"action,omitempty"` | |||
Team *Team `json:"team,omitempty"` | |||
Changes *TeamChange `json:"changes,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// TeamAddEvent is triggered when a repository is added to a team. | |||
// The Webhook event name is "team_add". | |||
// | |||
// Events of this type are not visible in timelines. These events are only used | |||
// to trigger hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent | |||
type TeamAddEvent struct { | |||
Team *Team `json:"team,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Org *Organization `json:"organization,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} | |||
// WatchEvent is related to starring a repository, not watching. See this API | |||
// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/ | |||
// | |||
// The event’s actor is the user who starred a repository, and the event’s | |||
// repository is the repository that was starred. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent | |||
type WatchEvent struct { | |||
// Action is the action that was performed. Possible value is: "started". | |||
Action *string `json:"action,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
Installation *Installation `json:"installation,omitempty"` | |||
} |
@ -0,0 +1,332 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// +build ignore | |||
// gen-accessors generates accessor methods for structs with pointer fields. | |||
// | |||
// It is meant to be used by the go-github authors in conjunction with the | |||
// go generate tool before sending a commit to GitHub. | |||
package main | |||
import ( | |||
"bytes" | |||
"flag" | |||
"fmt" | |||
"go/ast" | |||
"go/format" | |||
"go/parser" | |||
"go/token" | |||
"io/ioutil" | |||
"log" | |||
"os" | |||
"sort" | |||
"strings" | |||
"text/template" | |||
) | |||
const ( | |||
fileSuffix = "-accessors.go" | |||
) | |||
var ( | |||
verbose = flag.Bool("v", false, "Print verbose log messages") | |||
sourceTmpl = template.Must(template.New("source").Parse(source)) | |||
// blacklistStructMethod lists "struct.method" combos to skip. | |||
blacklistStructMethod = map[string]bool{ | |||
"RepositoryContent.GetContent": true, | |||
"Client.GetBaseURL": true, | |||
"Client.GetUploadURL": true, | |||
"ErrorResponse.GetResponse": true, | |||
"RateLimitError.GetResponse": true, | |||
"AbuseRateLimitError.GetResponse": true, | |||
} | |||
// blacklistStruct lists structs to skip. | |||
blacklistStruct = map[string]bool{ | |||
"Client": true, | |||
} | |||
) | |||
func logf(fmt string, args ...interface{}) { | |||
if *verbose { | |||
log.Printf(fmt, args...) | |||
} | |||
} | |||
func main() { | |||
flag.Parse() | |||
fset := token.NewFileSet() | |||
pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0) | |||
if err != nil { | |||
log.Fatal(err) | |||
return | |||
} | |||
for pkgName, pkg := range pkgs { | |||
t := &templateData{ | |||
filename: pkgName + fileSuffix, | |||
Year: 2017, | |||
Package: pkgName, | |||
Imports: map[string]string{}, | |||
} | |||
for filename, f := range pkg.Files { | |||
logf("Processing %v...", filename) | |||
if err := t.processAST(f); err != nil { | |||
log.Fatal(err) | |||
} | |||
} | |||
if err := t.dump(); err != nil { | |||
log.Fatal(err) | |||
} | |||
} | |||
logf("Done.") | |||
} | |||
func (t *templateData) processAST(f *ast.File) error { | |||
for _, decl := range f.Decls { | |||
gd, ok := decl.(*ast.GenDecl) | |||
if !ok { | |||
continue | |||
} | |||
for _, spec := range gd.Specs { | |||
ts, ok := spec.(*ast.TypeSpec) | |||
if !ok { | |||
continue | |||
} | |||
// Skip unexported identifiers. | |||
if !ts.Name.IsExported() { | |||
logf("Struct %v is unexported; skipping.", ts.Name) | |||
continue | |||
} | |||
// Check if the struct is blacklisted. | |||
if blacklistStruct[ts.Name.Name] { | |||
logf("Struct %v is blacklisted; skipping.", ts.Name) | |||
continue | |||
} | |||
st, ok := ts.Type.(*ast.StructType) | |||
if !ok { | |||
continue | |||
} | |||
for _, field := range st.Fields.List { | |||
se, ok := field.Type.(*ast.StarExpr) | |||
if len(field.Names) == 0 || !ok { | |||
continue | |||
} | |||
fieldName := field.Names[0] | |||
// Skip unexported identifiers. | |||
if !fieldName.IsExported() { | |||
logf("Field %v is unexported; skipping.", fieldName) | |||
continue | |||
} | |||
// Check if "struct.method" is blacklisted. | |||
if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] { | |||
logf("Method %v is blacklisted; skipping.", key) | |||
continue | |||
} | |||
switch x := se.X.(type) { | |||
case *ast.ArrayType: | |||
t.addArrayType(x, ts.Name.String(), fieldName.String()) | |||
case *ast.Ident: | |||
t.addIdent(x, ts.Name.String(), fieldName.String()) | |||
case *ast.MapType: | |||
t.addMapType(x, ts.Name.String(), fieldName.String()) | |||
case *ast.SelectorExpr: | |||
t.addSelectorExpr(x, ts.Name.String(), fieldName.String()) | |||
default: | |||
logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x) | |||
} | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func sourceFilter(fi os.FileInfo) bool { | |||
return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix) | |||
} | |||
func (t *templateData) dump() error { | |||
if len(t.Getters) == 0 { | |||
logf("No getters for %v; skipping.", t.filename) | |||
return nil | |||
} | |||
// Sort getters by ReceiverType.FieldName. | |||
sort.Sort(byName(t.Getters)) | |||
var buf bytes.Buffer | |||
if err := sourceTmpl.Execute(&buf, t); err != nil { | |||
return err | |||
} | |||
clean, err := format.Source(buf.Bytes()) | |||
if err != nil { | |||
return err | |||
} | |||
logf("Writing %v...", t.filename) | |||
return ioutil.WriteFile(t.filename, clean, 0644) | |||
} | |||
func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter { | |||
return &getter{ | |||
sortVal: strings.ToLower(receiverType) + "." + strings.ToLower(fieldName), | |||
ReceiverVar: strings.ToLower(receiverType[:1]), | |||
ReceiverType: receiverType, | |||
FieldName: fieldName, | |||
FieldType: fieldType, | |||
ZeroValue: zeroValue, | |||
NamedStruct: namedStruct, | |||
} | |||
} | |||
func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) { | |||
var eltType string | |||
switch elt := x.Elt.(type) { | |||
case *ast.Ident: | |||
eltType = elt.String() | |||
default: | |||
logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt) | |||
return | |||
} | |||
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false)) | |||
} | |||
func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { | |||
var zeroValue string | |||
var namedStruct = false | |||
switch x.String() { | |||
case "int", "int64": | |||
zeroValue = "0" | |||
case "string": | |||
zeroValue = `""` | |||
case "bool": | |||
zeroValue = "false" | |||
case "Timestamp": | |||
zeroValue = "Timestamp{}" | |||
default: | |||
zeroValue = "nil" | |||
namedStruct = true | |||
} | |||
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct)) | |||
} | |||
func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) { | |||
var keyType string | |||
switch key := x.Key.(type) { | |||
case *ast.Ident: | |||
keyType = key.String() | |||
default: | |||
logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key) | |||
return | |||
} | |||
var valueType string | |||
switch value := x.Value.(type) { | |||
case *ast.Ident: | |||
valueType = value.String() | |||
default: | |||
logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value) | |||
return | |||
} | |||
fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType) | |||
zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType) | |||
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) | |||
} | |||
func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) { | |||
if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field. | |||
return | |||
} | |||
var xX string | |||
if xx, ok := x.X.(*ast.Ident); ok { | |||
xX = xx.String() | |||
} | |||
switch xX { | |||
case "time", "json": | |||
if xX == "json" { | |||
t.Imports["encoding/json"] = "encoding/json" | |||
} else { | |||
t.Imports[xX] = xX | |||
} | |||
fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name) | |||
zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name) | |||
if xX == "time" && x.Sel.Name == "Duration" { | |||
zeroValue = "0" | |||
} | |||
t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) | |||
default: | |||
logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x) | |||
} | |||
} | |||
type templateData struct { | |||
filename string | |||
Year int | |||
Package string | |||
Imports map[string]string | |||
Getters []*getter | |||
} | |||
type getter struct { | |||
sortVal string // Lower-case version of "ReceiverType.FieldName". | |||
ReceiverVar string // The one-letter variable name to match the ReceiverType. | |||
ReceiverType string | |||
FieldName string | |||
FieldType string | |||
ZeroValue string | |||
NamedStruct bool // Getter for named struct. | |||
} | |||
type byName []*getter | |||
func (b byName) Len() int { return len(b) } | |||
func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal } | |||
func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | |||
const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Code generated by gen-accessors; DO NOT EDIT. | |||
package {{.Package}} | |||
{{with .Imports}} | |||
import ( | |||
{{- range . -}} | |||
"{{.}}" | |||
{{end -}} | |||
) | |||
{{end}} | |||
{{range .Getters}} | |||
{{if .NamedStruct}} | |||
// Get{{.FieldName}} returns the {{.FieldName}} field. | |||
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} { | |||
if {{.ReceiverVar}} == nil { | |||
return {{.ZeroValue}} | |||
} | |||
return {{.ReceiverVar}}.{{.FieldName}} | |||
} | |||
{{else}} | |||
// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise. | |||
func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { | |||
if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil { | |||
return {{.ZeroValue}} | |||
} | |||
return *{{.ReceiverVar}}.{{.FieldName}} | |||
} | |||
{{end}} | |||
{{end}} | |||
` |
@ -0,0 +1,358 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// GistsService handles communication with the Gist related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/ | |||
type GistsService service | |||
// Gist represents a GitHub's gist. | |||
type Gist struct { | |||
ID *string `json:"id,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Public *bool `json:"public,omitempty"` | |||
Owner *User `json:"owner,omitempty"` | |||
Files map[GistFilename]GistFile `json:"files,omitempty"` | |||
Comments *int `json:"comments,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
GitPullURL *string `json:"git_pull_url,omitempty"` | |||
GitPushURL *string `json:"git_push_url,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (g Gist) String() string { | |||
return Stringify(g) | |||
} | |||
// GistFilename represents filename on a gist. | |||
type GistFilename string | |||
// GistFile represents a file on a gist. | |||
type GistFile struct { | |||
Size *int `json:"size,omitempty"` | |||
Filename *string `json:"filename,omitempty"` | |||
Language *string `json:"language,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
RawURL *string `json:"raw_url,omitempty"` | |||
Content *string `json:"content,omitempty"` | |||
} | |||
func (g GistFile) String() string { | |||
return Stringify(g) | |||
} | |||
// GistCommit represents a commit on a gist. | |||
type GistCommit struct { | |||
URL *string `json:"url,omitempty"` | |||
Version *string `json:"version,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
ChangeStatus *CommitStats `json:"change_status,omitempty"` | |||
CommittedAt *Timestamp `json:"committed_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (gc GistCommit) String() string { | |||
return Stringify(gc) | |||
} | |||
// GistFork represents a fork of a gist. | |||
type GistFork struct { | |||
URL *string `json:"url,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
ID *string `json:"id,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (gf GistFork) String() string { | |||
return Stringify(gf) | |||
} | |||
// GistListOptions specifies the optional parameters to the | |||
// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods. | |||
type GistListOptions struct { | |||
// Since filters Gists by time. | |||
Since time.Time `url:"since,omitempty"` | |||
ListOptions | |||
} | |||
// List gists for a user. Passing the empty string will list | |||
// all public gists if called anonymously. However, if the call | |||
// is authenticated, it will returns all gists for the authenticated | |||
// user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists | |||
func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptions) ([]*Gist, *Response, error) { | |||
var u string | |||
if user != "" { | |||
u = fmt.Sprintf("users/%v/gists", user) | |||
} else { | |||
u = "gists" | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gists []*Gist | |||
resp, err := s.client.Do(ctx, req, &gists) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gists, resp, nil | |||
} | |||
// ListAll lists all public gists. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists | |||
func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { | |||
u, err := addOptions("gists/public", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gists []*Gist | |||
resp, err := s.client.Do(ctx, req, &gists) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gists, resp, nil | |||
} | |||
// ListStarred lists starred gists of authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#list-gists | |||
func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { | |||
u, err := addOptions("gists/starred", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gists []*Gist | |||
resp, err := s.client.Do(ctx, req, &gists) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gists, resp, nil | |||
} | |||
// Get a single gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-gist | |||
func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, error) { | |||
u := fmt.Sprintf("gists/%v", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gist := new(Gist) | |||
resp, err := s.client.Do(ctx, req, gist) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gist, resp, nil | |||
} | |||
// GetRevision gets a specific revision of a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist | |||
func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/%v", id, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gist := new(Gist) | |||
resp, err := s.client.Do(ctx, req, gist) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gist, resp, nil | |||
} | |||
// Create a gist for authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#create-a-gist | |||
func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response, error) { | |||
u := "gists" | |||
req, err := s.client.NewRequest("POST", u, gist) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
g := new(Gist) | |||
resp, err := s.client.Do(ctx, req, g) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return g, resp, nil | |||
} | |||
// Edit a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#edit-a-gist | |||
func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, *Response, error) { | |||
u := fmt.Sprintf("gists/%v", id) | |||
req, err := s.client.NewRequest("PATCH", u, gist) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
g := new(Gist) | |||
resp, err := s.client.Do(ctx, req, g) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return g, resp, nil | |||
} | |||
// ListCommits lists commits of a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-commits | |||
func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOptions) ([]*GistCommit, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/commits", id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gistCommits []*GistCommit | |||
resp, err := s.client.Do(ctx, req, &gistCommits) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gistCommits, resp, nil | |||
} | |||
// Delete a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#delete-a-gist | |||
func (s *GistsService) Delete(ctx context.Context, id string) (*Response, error) { | |||
u := fmt.Sprintf("gists/%v", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Star a gist on behalf of authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#star-a-gist | |||
func (s *GistsService) Star(ctx context.Context, id string) (*Response, error) { | |||
u := fmt.Sprintf("gists/%v/star", id) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Unstar a gist on a behalf of authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#unstar-a-gist | |||
func (s *GistsService) Unstar(ctx context.Context, id string) (*Response, error) { | |||
u := fmt.Sprintf("gists/%v/star", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// IsStarred checks if a gist is starred by authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#check-if-a-gist-is-starred | |||
func (s *GistsService) IsStarred(ctx context.Context, id string) (bool, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/star", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
starred, err := parseBoolResponse(err) | |||
return starred, resp, err | |||
} | |||
// Fork a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#fork-a-gist | |||
func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/forks", id) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
g := new(Gist) | |||
resp, err := s.client.Do(ctx, req, g) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return g, resp, nil | |||
} | |||
// ListForks lists forks of a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-forks | |||
func (s *GistsService) ListForks(ctx context.Context, id string) ([]*GistFork, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/forks", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var gistForks []*GistFork | |||
resp, err := s.client.Do(ctx, req, &gistForks) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gistForks, resp, nil | |||
} |
@ -0,0 +1,119 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// GistComment represents a Gist comment. | |||
type GistComment struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
} | |||
func (g GistComment) String() string { | |||
return Stringify(g) | |||
} | |||
// ListComments lists all comments for a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist | |||
func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *ListOptions) ([]*GistComment, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/comments", gistID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var comments []*GistComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// GetComment retrieves a single comment from a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment | |||
func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(GistComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// CreateComment creates a comment for a gist. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-comment | |||
func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment *GistComment) (*GistComment, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/comments", gistID) | |||
req, err := s.client.NewRequest("POST", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(GistComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// EditComment edits an existing gist comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment | |||
func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) { | |||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | |||
req, err := s.client.NewRequest("PATCH", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(GistComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// DeleteComment deletes a gist comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment | |||
func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) { | |||
u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,12 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
// GitService handles communication with the git data related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/ | |||
type GitService service |
@ -0,0 +1,69 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"bytes" | |||
"context" | |||
"fmt" | |||
) | |||
// Blob represents a blob object. | |||
type Blob struct { | |||
Content *string `json:"content,omitempty"` | |||
Encoding *string `json:"encoding,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
// GetBlob fetches a blob from a repo given a SHA. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob | |||
func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
blob := new(Blob) | |||
resp, err := s.client.Do(ctx, req, blob) | |||
return blob, resp, err | |||
} | |||
// GetBlobRaw fetches a blob's contents from a repo. | |||
// Unlike GetBlob, it returns the raw bytes rather than the base64-encoded data. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob | |||
func (s *GitService) GetBlobRaw(ctx context.Context, owner, repo, sha string) ([]byte, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", "application/vnd.github.v3.raw") | |||
var buf bytes.Buffer | |||
resp, err := s.client.Do(ctx, req, &buf) | |||
return buf.Bytes(), resp, err | |||
} | |||
// CreateBlob creates a blob object. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob | |||
func (s *GitService) CreateBlob(ctx context.Context, owner string, repo string, blob *Blob) (*Blob, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, blob) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(Blob) | |||
resp, err := s.client.Do(ctx, req, t) | |||
return t, resp, err | |||
} |
@ -0,0 +1,135 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// SignatureVerification represents GPG signature verification. | |||
type SignatureVerification struct { | |||
Verified *bool `json:"verified,omitempty"` | |||
Reason *string `json:"reason,omitempty"` | |||
Signature *string `json:"signature,omitempty"` | |||
Payload *string `json:"payload,omitempty"` | |||
} | |||
// Commit represents a GitHub commit. | |||
type Commit struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Author *CommitAuthor `json:"author,omitempty"` | |||
Committer *CommitAuthor `json:"committer,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Tree *Tree `json:"tree,omitempty"` | |||
Parents []Commit `json:"parents,omitempty"` | |||
Stats *CommitStats `json:"stats,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Verification *SignatureVerification `json:"verification,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
// CommentCount is the number of GitHub comments on the commit. This | |||
// is only populated for requests that fetch GitHub data like | |||
// Pulls.ListCommits, Repositories.ListCommits, etc. | |||
CommentCount *int `json:"comment_count,omitempty"` | |||
} | |||
func (c Commit) String() string { | |||
return Stringify(c) | |||
} | |||
// CommitAuthor represents the author or committer of a commit. The commit | |||
// author may not correspond to a GitHub User. | |||
type CommitAuthor struct { | |||
Date *time.Time `json:"date,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Email *string `json:"email,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
Login *string `json:"username,omitempty"` // Renamed for go-github consistency. | |||
} | |||
func (c CommitAuthor) String() string { | |||
return Stringify(c) | |||
} | |||
// GetCommit fetches the Commit object for a given SHA. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit | |||
func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(Commit) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// createCommit represents the body of a CreateCommit request. | |||
type createCommit struct { | |||
Author *CommitAuthor `json:"author,omitempty"` | |||
Committer *CommitAuthor `json:"committer,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Tree *string `json:"tree,omitempty"` | |||
Parents []string `json:"parents,omitempty"` | |||
Signature *string `json:"signature,omitempty"` | |||
} | |||
// CreateCommit creates a new commit in a repository. | |||
// commit must not be nil. | |||
// | |||
// The commit.Committer is optional and will be filled with the commit.Author | |||
// data if omitted. If the commit.Author is omitted, it will be filled in with | |||
// the authenticated user’s information and the current date. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/commits/#create-a-commit | |||
func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) { | |||
if commit == nil { | |||
return nil, nil, fmt.Errorf("commit must be provided") | |||
} | |||
u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) | |||
parents := make([]string, len(commit.Parents)) | |||
for i, parent := range commit.Parents { | |||
parents[i] = *parent.SHA | |||
} | |||
body := &createCommit{ | |||
Author: commit.Author, | |||
Committer: commit.Committer, | |||
Message: commit.Message, | |||
Parents: parents, | |||
} | |||
if commit.Tree != nil { | |||
body.Tree = commit.Tree.SHA | |||
} | |||
if commit.Verification != nil { | |||
body.Signature = commit.Verification.Signature | |||
} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(Commit) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} |
@ -0,0 +1,219 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"net/url" | |||
"strings" | |||
) | |||
// Reference represents a GitHub reference. | |||
type Reference struct { | |||
Ref *string `json:"ref"` | |||
URL *string `json:"url"` | |||
Object *GitObject `json:"object"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (r Reference) String() string { | |||
return Stringify(r) | |||
} | |||
// GitObject represents a Git object. | |||
type GitObject struct { | |||
Type *string `json:"type"` | |||
SHA *string `json:"sha"` | |||
URL *string `json:"url"` | |||
} | |||
func (o GitObject) String() string { | |||
return Stringify(o) | |||
} | |||
// createRefRequest represents the payload for creating a reference. | |||
type createRefRequest struct { | |||
Ref *string `json:"ref"` | |||
SHA *string `json:"sha"` | |||
} | |||
// updateRefRequest represents the payload for updating a reference. | |||
type updateRefRequest struct { | |||
SHA *string `json:"sha"` | |||
Force *bool `json:"force"` | |||
} | |||
// GetRef fetches a single Reference object for a given Git ref. | |||
// If there is no exact match, GetRef will return an error. | |||
// | |||
// Note: The GitHub API can return multiple matches. | |||
// If you wish to use this functionality please use the GetRefs() method. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference | |||
func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) { | |||
ref = strings.TrimPrefix(ref, "refs/") | |||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(Reference) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if _, ok := err.(*json.UnmarshalTypeError); ok { | |||
// Multiple refs, means there wasn't an exact match. | |||
return nil, resp, errors.New("no exact match found for this ref") | |||
} else if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// GetRefs fetches a slice of Reference objects for a given Git ref. | |||
// If there is an exact match, only that ref is returned. | |||
// If there is no exact match, GitHub returns all refs that start with ref. | |||
// If returned error is nil, there will be at least 1 ref returned. | |||
// For example: | |||
// | |||
// "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned. | |||
// "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref. | |||
// "heads/notexist" -> [] // Returns an error. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference | |||
func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) { | |||
ref = strings.TrimPrefix(ref, "refs/") | |||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var rawJSON json.RawMessage | |||
resp, err := s.client.Do(ctx, req, &rawJSON) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
// Prioritize the most common case: a single returned ref. | |||
r := new(Reference) | |||
singleUnmarshalError := json.Unmarshal(rawJSON, r) | |||
if singleUnmarshalError == nil { | |||
return []*Reference{r}, resp, nil | |||
} | |||
// Attempt to unmarshal multiple refs. | |||
var rs []*Reference | |||
multipleUnmarshalError := json.Unmarshal(rawJSON, &rs) | |||
if multipleUnmarshalError == nil { | |||
if len(rs) == 0 { | |||
return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0") | |||
} | |||
return rs, resp, nil | |||
} | |||
return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError) | |||
} | |||
// ReferenceListOptions specifies optional parameters to the | |||
// GitService.ListRefs method. | |||
type ReferenceListOptions struct { | |||
Type string `url:"-"` | |||
ListOptions | |||
} | |||
// ListRefs lists all refs in a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references | |||
func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) { | |||
var u string | |||
if opt != nil && opt.Type != "" { | |||
u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) | |||
} else { | |||
u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var rs []*Reference | |||
resp, err := s.client.Do(ctx, req, &rs) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return rs, resp, nil | |||
} | |||
// CreateRef creates a new ref in a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference | |||
func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, &createRefRequest{ | |||
// back-compat with previous behavior that didn't require 'refs/' prefix | |||
Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), | |||
SHA: ref.Object.SHA, | |||
}) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(Reference) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// UpdateRef updates an existing ref in a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference | |||
func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { | |||
refPath := strings.TrimPrefix(*ref.Ref, "refs/") | |||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) | |||
req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ | |||
SHA: ref.Object.SHA, | |||
Force: &force, | |||
}) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(Reference) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// DeleteRef deletes a ref from a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference | |||
func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) { | |||
ref = strings.TrimPrefix(ref, "refs/") | |||
u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, url.QueryEscape(ref)) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,76 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Tag represents a tag object. | |||
type Tag struct { | |||
Tag *string `json:"tag,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Tagger *CommitAuthor `json:"tagger,omitempty"` | |||
Object *GitObject `json:"object,omitempty"` | |||
Verification *SignatureVerification `json:"verification,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
// createTagRequest represents the body of a CreateTag request. This is mostly | |||
// identical to Tag with the exception that the object SHA and Type are | |||
// top-level fields, rather than being nested inside a JSON object. | |||
type createTagRequest struct { | |||
Tag *string `json:"tag,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Object *string `json:"object,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
Tagger *CommitAuthor `json:"tagger,omitempty"` | |||
} | |||
// GetTag fetches a tag from a repo given a SHA. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag | |||
func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
tag := new(Tag) | |||
resp, err := s.client.Do(ctx, req, tag) | |||
return tag, resp, err | |||
} | |||
// CreateTag creates a tag object. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object | |||
func (s *GitService) CreateTag(ctx context.Context, owner string, repo string, tag *Tag) (*Tag, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) | |||
// convert Tag into a createTagRequest | |||
tagRequest := &createTagRequest{ | |||
Tag: tag.Tag, | |||
Message: tag.Message, | |||
Tagger: tag.Tagger, | |||
} | |||
if tag.Object != nil { | |||
tagRequest.Object = tag.Object.SHA | |||
tagRequest.Type = tag.Object.Type | |||
} | |||
req, err := s.client.NewRequest("POST", u, tagRequest) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(Tag) | |||
resp, err := s.client.Do(ctx, req, t) | |||
return t, resp, err | |||
} |
@ -0,0 +1,99 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Tree represents a GitHub tree. | |||
type Tree struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Entries []TreeEntry `json:"tree,omitempty"` | |||
// Truncated is true if the number of items in the tree | |||
// exceeded GitHub's maximum limit and the Entries were truncated | |||
// in the response. Only populated for requests that fetch | |||
// trees like Git.GetTree. | |||
Truncated *bool `json:"truncated,omitempty"` | |||
} | |||
func (t Tree) String() string { | |||
return Stringify(t) | |||
} | |||
// TreeEntry represents the contents of a tree structure. TreeEntry can | |||
// represent either a blob, a commit (in the case of a submodule), or another | |||
// tree. | |||
type TreeEntry struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Path *string `json:"path,omitempty"` | |||
Mode *string `json:"mode,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
Content *string `json:"content,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
} | |||
func (t TreeEntry) String() string { | |||
return Stringify(t) | |||
} | |||
// GetTree fetches the Tree object for a given sha hash from a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree | |||
func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) | |||
if recursive { | |||
u += "?recursive=1" | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(Tree) | |||
resp, err := s.client.Do(ctx, req, t) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return t, resp, nil | |||
} | |||
// createTree represents the body of a CreateTree request. | |||
type createTree struct { | |||
BaseTree string `json:"base_tree,omitempty"` | |||
Entries []TreeEntry `json:"tree"` | |||
} | |||
// CreateTree creates a new tree in a repository. If both a tree and a nested | |||
// path modifying that tree are specified, it will overwrite the contents of | |||
// that tree with the new path contents and write a new tree out. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/git/trees/#create-a-tree | |||
func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) | |||
body := &createTree{ | |||
BaseTree: baseTree, | |||
Entries: entries, | |||
} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
t := new(Tree) | |||
resp, err := s.client.Do(ctx, req, t) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return t, resp, nil | |||
} |
@ -0,0 +1,64 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// GitignoresService provides access to the gitignore related functions in the | |||
// GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gitignore/ | |||
type GitignoresService service | |||
// Gitignore represents a .gitignore file as returned by the GitHub API. | |||
type Gitignore struct { | |||
Name *string `json:"name,omitempty"` | |||
Source *string `json:"source,omitempty"` | |||
} | |||
func (g Gitignore) String() string { | |||
return Stringify(g) | |||
} | |||
// List all available Gitignore templates. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gitignore/#listing-available-templates | |||
func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "gitignore/templates", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var availableTemplates []string | |||
resp, err := s.client.Do(ctx, req, &availableTemplates) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return availableTemplates, resp, nil | |||
} | |||
// Get a Gitignore by name. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-single-template | |||
func (s GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) { | |||
u := fmt.Sprintf("gitignore/templates/%v", name) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
gitignore := new(Gitignore) | |||
resp, err := s.client.Do(ctx, req, gitignore) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return gitignore, resp, nil | |||
} |
@ -0,0 +1,28 @@ | |||
// Copyright 2018 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
// InteractionsService handles communication with the repository and organization related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/ | |||
type InteractionsService service | |||
// InteractionRestriction represents the interaction restrictions for repository and organization. | |||
type InteractionRestriction struct { | |||
// Specifies the group of GitHub users who can | |||
// comment, open issues, or create pull requests for the given repository. | |||
// Possible values are: "existing_users", "contributors_only" and "collaborators_only". | |||
Limit *string `json:"limit,omitempty"` | |||
// Origin specifies the type of the resource to interact with. | |||
// Possible values are: "repository" and "organization". | |||
Origin *string `json:"origin,omitempty"` | |||
// ExpiresAt specifies the time after which the interaction restrictions expire. | |||
// The default expiry time is 24 hours from the time restriction is created. | |||
ExpiresAt *Timestamp `json:"expires_at,omitempty"` | |||
} |
@ -0,0 +1,80 @@ | |||
// Copyright 2019 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// GetRestrictionsForOrg fetches the interaction restrictions for an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#get-interaction-restrictions-for-an-organization | |||
func (s *InteractionsService) GetRestrictionsForOrg(ctx context.Context, organization string) (*InteractionRestriction, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/interaction-limits", organization) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
organizationInteractions := new(InteractionRestriction) | |||
resp, err := s.client.Do(ctx, req, organizationInteractions) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return organizationInteractions, resp, nil | |||
} | |||
// UpdateRestrictionsForOrg adds or updates the interaction restrictions for an organization. | |||
// | |||
// limit specifies the group of GitHub users who can comment, open issues, or create pull requests | |||
// in public repositories for the given organization. | |||
// Possible values are: "existing_users", "contributors_only", "collaborators_only". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#add-or-update-interaction-restrictions-for-an-organization | |||
func (s *InteractionsService) UpdateRestrictionsForOrg(ctx context.Context, organization, limit string) (*InteractionRestriction, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/interaction-limits", organization) | |||
interaction := &InteractionRestriction{Limit: String(limit)} | |||
req, err := s.client.NewRequest("PUT", u, interaction) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
organizationInteractions := new(InteractionRestriction) | |||
resp, err := s.client.Do(ctx, req, organizationInteractions) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return organizationInteractions, resp, nil | |||
} | |||
// RemoveRestrictionsFromOrg removes the interaction restrictions for an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/orgs/#remove-interaction-restrictions-for-an-organization | |||
func (s *InteractionsService) RemoveRestrictionsFromOrg(ctx context.Context, organization string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/interaction-limits", organization) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,80 @@ | |||
// Copyright 2018 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// GetRestrictionsForRepo fetches the interaction restrictions for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/repos/#get-interaction-restrictions-for-a-repository | |||
func (s *InteractionsService) GetRestrictionsForRepo(ctx context.Context, owner, repo string) (*InteractionRestriction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
repositoryInteractions := new(InteractionRestriction) | |||
resp, err := s.client.Do(ctx, req, repositoryInteractions) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return repositoryInteractions, resp, nil | |||
} | |||
// UpdateRestrictionsForRepo adds or updates the interaction restrictions for a repository. | |||
// | |||
// limit specifies the group of GitHub users who can comment, open issues, or create pull requests | |||
// for the given repository. | |||
// Possible values are: "existing_users", "contributors_only", "collaborators_only". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/repos/#add-or-update-interaction-restrictions-for-a-repository | |||
func (s *InteractionsService) UpdateRestrictionsForRepo(ctx context.Context, owner, repo, limit string) (*InteractionRestriction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) | |||
interaction := &InteractionRestriction{Limit: String(limit)} | |||
req, err := s.client.NewRequest("PUT", u, interaction) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
repositoryInteractions := new(InteractionRestriction) | |||
resp, err := s.client.Do(ctx, req, repositoryInteractions) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return repositoryInteractions, resp, nil | |||
} | |||
// RemoveRestrictionsFromRepo removes the interaction restrictions for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/interactions/repos/#remove-interaction-restrictions-for-a-repository | |||
func (s *InteractionsService) RemoveRestrictionsFromRepo(ctx context.Context, owner, repo string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/interaction-limits", owner, repo) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeInteractionRestrictionsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,347 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"strings" | |||
"time" | |||
) | |||
// IssuesService handles communication with the issue related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/ | |||
type IssuesService service | |||
// Issue represents a GitHub issue on a repository. | |||
// | |||
// Note: As far as the GitHub API is concerned, every pull request is an issue, | |||
// but not every issue is a pull request. Some endpoints, events, and webhooks | |||
// may also return pull requests via this struct. If PullRequestLinks is nil, | |||
// this is an issue, and if PullRequestLinks is not nil, this is a pull request. | |||
// The IsPullRequest helper method can be used to check that. | |||
type Issue struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Number *int `json:"number,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
Locked *bool `json:"locked,omitempty"` | |||
Title *string `json:"title,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Labels []Label `json:"labels,omitempty"` | |||
Assignee *User `json:"assignee,omitempty"` | |||
Comments *int `json:"comments,omitempty"` | |||
ClosedAt *time.Time `json:"closed_at,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
ClosedBy *User `json:"closed_by,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
CommentsURL *string `json:"comments_url,omitempty"` | |||
EventsURL *string `json:"events_url,omitempty"` | |||
LabelsURL *string `json:"labels_url,omitempty"` | |||
RepositoryURL *string `json:"repository_url,omitempty"` | |||
Milestone *Milestone `json:"milestone,omitempty"` | |||
PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` | |||
Repository *Repository `json:"repository,omitempty"` | |||
Reactions *Reactions `json:"reactions,omitempty"` | |||
Assignees []*User `json:"assignees,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
// TextMatches is only populated from search results that request text matches | |||
// See: search.go and https://developer.github.com/v3/search/#text-match-metadata | |||
TextMatches []TextMatch `json:"text_matches,omitempty"` | |||
// ActiveLockReason is populated only when LockReason is provided while locking the issue. | |||
// Possible values are: "off-topic", "too heated", "resolved", and "spam". | |||
ActiveLockReason *string `json:"active_lock_reason,omitempty"` | |||
} | |||
func (i Issue) String() string { | |||
return Stringify(i) | |||
} | |||
// IsPullRequest reports whether the issue is also a pull request. It uses the | |||
// method recommended by GitHub's API documentation, which is to check whether | |||
// PullRequestLinks is non-nil. | |||
func (i Issue) IsPullRequest() bool { | |||
return i.PullRequestLinks != nil | |||
} | |||
// IssueRequest represents a request to create/edit an issue. | |||
// It is separate from Issue above because otherwise Labels | |||
// and Assignee fail to serialize to the correct JSON. | |||
type IssueRequest struct { | |||
Title *string `json:"title,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
Labels *[]string `json:"labels,omitempty"` | |||
Assignee *string `json:"assignee,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
Milestone *int `json:"milestone,omitempty"` | |||
Assignees *[]string `json:"assignees,omitempty"` | |||
} | |||
// IssueListOptions specifies the optional parameters to the IssuesService.List | |||
// and IssuesService.ListByOrg methods. | |||
type IssueListOptions struct { | |||
// Filter specifies which issues to list. Possible values are: assigned, | |||
// created, mentioned, subscribed, all. Default is "assigned". | |||
Filter string `url:"filter,omitempty"` | |||
// State filters issues based on their state. Possible values are: open, | |||
// closed, all. Default is "open". | |||
State string `url:"state,omitempty"` | |||
// Labels filters issues based on their label. | |||
Labels []string `url:"labels,comma,omitempty"` | |||
// Sort specifies how to sort issues. Possible values are: created, updated, | |||
// and comments. Default value is "created". | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort issues. Possible values are: asc, desc. | |||
// Default is "desc". | |||
Direction string `url:"direction,omitempty"` | |||
// Since filters issues by time. | |||
Since time.Time `url:"since,omitempty"` | |||
ListOptions | |||
} | |||
// PullRequestLinks object is added to the Issue object when it's an issue included | |||
// in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR. | |||
type PullRequestLinks struct { | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
DiffURL *string `json:"diff_url,omitempty"` | |||
PatchURL *string `json:"patch_url,omitempty"` | |||
} | |||
// List the issues for the authenticated user. If all is true, list issues | |||
// across all the user's visible repositories including owned, member, and | |||
// organization repositories; if false, list only owned and member | |||
// repositories. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues | |||
func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) { | |||
var u string | |||
if all { | |||
u = "issues" | |||
} else { | |||
u = "user/issues" | |||
} | |||
return s.listIssues(ctx, u, opt) | |||
} | |||
// ListByOrg fetches the issues in the specified organization for the | |||
// authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues | |||
func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/issues", org) | |||
return s.listIssues(ctx, u, opt) | |||
} | |||
func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) { | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var issues []*Issue | |||
resp, err := s.client.Do(ctx, req, &issues) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return issues, resp, nil | |||
} | |||
// IssueListByRepoOptions specifies the optional parameters to the | |||
// IssuesService.ListByRepo method. | |||
type IssueListByRepoOptions struct { | |||
// Milestone limits issues for the specified milestone. Possible values are | |||
// a milestone number, "none" for issues with no milestone, "*" for issues | |||
// with any milestone. | |||
Milestone string `url:"milestone,omitempty"` | |||
// State filters issues based on their state. Possible values are: open, | |||
// closed, all. Default is "open". | |||
State string `url:"state,omitempty"` | |||
// Assignee filters issues based on their assignee. Possible values are a | |||
// user name, "none" for issues that are not assigned, "*" for issues with | |||
// any assigned user. | |||
Assignee string `url:"assignee,omitempty"` | |||
// Creator filters issues based on their creator. | |||
Creator string `url:"creator,omitempty"` | |||
// Mentioned filters issues to those mentioned a specific user. | |||
Mentioned string `url:"mentioned,omitempty"` | |||
// Labels filters issues based on their label. | |||
Labels []string `url:"labels,omitempty,comma"` | |||
// Sort specifies how to sort issues. Possible values are: created, updated, | |||
// and comments. Default value is "created". | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort issues. Possible values are: asc, desc. | |||
// Default is "desc". | |||
Direction string `url:"direction,omitempty"` | |||
// Since filters issues by time. | |||
Since time.Time `url:"since,omitempty"` | |||
ListOptions | |||
} | |||
// ListByRepo lists the issues for the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository | |||
func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeIntegrationPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var issues []*Issue | |||
resp, err := s.client.Do(ctx, req, &issues) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return issues, resp, nil | |||
} | |||
// Get a single issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue | |||
func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
issue := new(Issue) | |||
resp, err := s.client.Do(ctx, req, issue) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return issue, resp, nil | |||
} | |||
// Create a new issue on the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue | |||
func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, issue) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
i := new(Issue) | |||
resp, err := s.client.Do(ctx, req, i) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return i, resp, nil | |||
} | |||
// Edit an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue | |||
func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("PATCH", u, issue) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
i := new(Issue) | |||
resp, err := s.client.Do(ctx, req, i) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return i, resp, nil | |||
} | |||
// LockIssueOptions specifies the optional parameters to the | |||
// IssuesService.Lock method. | |||
type LockIssueOptions struct { | |||
// LockReason specifies the reason to lock this issue. | |||
// Providing a lock reason can help make it clearer to contributors why an issue | |||
// was locked. Possible values are: "off-topic", "too heated", "resolved", and "spam". | |||
LockReason string `json:"lock_reason,omitempty"` | |||
} | |||
// Lock an issue's conversation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue | |||
func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int, opt *LockIssueOptions) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) | |||
req, err := s.client.NewRequest("PUT", u, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
if opt != nil { | |||
req.Header.Set("Accept", mediaTypeLockReasonPreview) | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// Unlock an issue's conversation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue | |||
func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,85 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListAssignees fetches all available assignees (owners and collaborators) to | |||
// which issues may be assigned. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#list-assignees | |||
func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var assignees []*User | |||
resp, err := s.client.Do(ctx, req, &assignees) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return assignees, resp, nil | |||
} | |||
// IsAssignee checks if a user is an assignee for the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee | |||
func (s *IssuesService) IsAssignee(ctx context.Context, owner, repo, user string) (bool, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
assignee, err := parseBoolResponse(err) | |||
return assignee, resp, err | |||
} | |||
// AddAssignees adds the provided GitHub users as assignees to the issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue | |||
func (s *IssuesService) AddAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { | |||
users := &struct { | |||
Assignees []string `json:"assignees,omitempty"` | |||
}{Assignees: assignees} | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, users) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
issue := &Issue{} | |||
resp, err := s.client.Do(ctx, req, issue) | |||
return issue, resp, err | |||
} | |||
// RemoveAssignees removes the provided GitHub users as assignees from the issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue | |||
func (s *IssuesService) RemoveAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { | |||
users := &struct { | |||
Assignees []string `json:"assignees,omitempty"` | |||
}{Assignees: assignees} | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) | |||
req, err := s.client.NewRequest("DELETE", u, users) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
issue := &Issue{} | |||
resp, err := s.client.Do(ctx, req, issue) | |||
return issue, resp, err | |||
} |
@ -0,0 +1,153 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// IssueComment represents a comment left on an issue. | |||
type IssueComment struct { | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Reactions *Reactions `json:"reactions,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
// AuthorAssociation is the comment author's relationship to the issue's repository. | |||
// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". | |||
AuthorAssociation *string `json:"author_association,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
IssueURL *string `json:"issue_url,omitempty"` | |||
} | |||
func (i IssueComment) String() string { | |||
return Stringify(i) | |||
} | |||
// IssueListCommentsOptions specifies the optional parameters to the | |||
// IssuesService.ListComments method. | |||
type IssueListCommentsOptions struct { | |||
// Sort specifies how to sort comments. Possible values are: created, updated. | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort comments. Possible values are: asc, desc. | |||
Direction string `url:"direction,omitempty"` | |||
// Since filters comments by time. | |||
Since time.Time `url:"since,omitempty"` | |||
ListOptions | |||
} | |||
// ListComments lists all comments on the specified issue. Specifying an issue | |||
// number of 0 will return all comments on all issues for the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue | |||
func (s *IssuesService) ListComments(ctx context.Context, owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { | |||
var u string | |||
if number == 0 { | |||
u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) | |||
} else { | |||
u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var comments []*IssueComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// GetComment fetches the specified issue comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment | |||
func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*IssueComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
comment := new(IssueComment) | |||
resp, err := s.client.Do(ctx, req, comment) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comment, resp, nil | |||
} | |||
// CreateComment creates a new comment on the specified issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/comments/#create-a-comment | |||
func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(IssueComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// EditComment updates an issue comment. | |||
// A non-nil comment.Body must be provided. Other comment fields should be left nil. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment | |||
func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *IssueComment) (*IssueComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("PATCH", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(IssueComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// DeleteComment deletes an issue comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment | |||
func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,161 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"strings" | |||
"time" | |||
) | |||
// IssueEvent represents an event that occurred around an Issue or Pull Request. | |||
type IssueEvent struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
// The User that generated this event. | |||
Actor *User `json:"actor,omitempty"` | |||
// Event identifies the actual type of Event that occurred. Possible | |||
// values are: | |||
// | |||
// closed | |||
// The Actor closed the issue. | |||
// If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit. | |||
// | |||
// merged | |||
// The Actor merged into master a branch containing a commit mentioning the issue. | |||
// CommitID holds the SHA1 of the merge commit. | |||
// | |||
// referenced | |||
// The Actor committed to master a commit mentioning the issue in its commit message. | |||
// CommitID holds the SHA1 of the commit. | |||
// | |||
// reopened, unlocked | |||
// The Actor did that to the issue. | |||
// | |||
// locked | |||
// The Actor locked the issue. | |||
// LockReason holds the reason of locking the issue (if provided while locking). | |||
// | |||
// renamed | |||
// The Actor changed the issue title from Rename.From to Rename.To. | |||
// | |||
// mentioned | |||
// Someone unspecified @mentioned the Actor [sic] in an issue comment body. | |||
// | |||
// assigned, unassigned | |||
// The Assigner assigned the issue to or removed the assignment from the Assignee. | |||
// | |||
// labeled, unlabeled | |||
// The Actor added or removed the Label from the issue. | |||
// | |||
// milestoned, demilestoned | |||
// The Actor added or removed the issue from the Milestone. | |||
// | |||
// subscribed, unsubscribed | |||
// The Actor subscribed to or unsubscribed from notifications for an issue. | |||
// | |||
// head_ref_deleted, head_ref_restored | |||
// The pull request’s branch was deleted or restored. | |||
// | |||
Event *string `json:"event,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
Issue *Issue `json:"issue,omitempty"` | |||
// Only present on certain events; see above. | |||
Assignee *User `json:"assignee,omitempty"` | |||
Assigner *User `json:"assigner,omitempty"` | |||
CommitID *string `json:"commit_id,omitempty"` | |||
Milestone *Milestone `json:"milestone,omitempty"` | |||
Label *Label `json:"label,omitempty"` | |||
Rename *Rename `json:"rename,omitempty"` | |||
LockReason *string `json:"lock_reason,omitempty"` | |||
ProjectCard *ProjectCard `json:"project_card,omitempty"` | |||
} | |||
// ListIssueEvents lists events for the specified issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue | |||
func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
acceptHeaders := []string{mediaTypeLockReasonPreview, mediaTypeProjectCardDetailsPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var events []*IssueEvent | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// ListRepositoryEvents lists events for the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository | |||
func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var events []*IssueEvent | |||
resp, err := s.client.Do(ctx, req, &events) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return events, resp, nil | |||
} | |||
// GetEvent returns the specified issue event. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event | |||
func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int64) (*IssueEvent, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
event := new(IssueEvent) | |||
resp, err := s.client.Do(ctx, req, event) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return event, resp, nil | |||
} | |||
// Rename contains details for 'renamed' events. | |||
type Rename struct { | |||
From *string `json:"from,omitempty"` | |||
To *string `json:"to,omitempty"` | |||
} | |||
func (r Rename) String() string { | |||
return Stringify(r) | |||
} |
@ -0,0 +1,261 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Label represents a GitHub label on an Issue | |||
type Label struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Color *string `json:"color,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Default *bool `json:"default,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (l Label) String() string { | |||
return Stringify(l) | |||
} | |||
// ListLabels lists all labels for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository | |||
func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
var labels []*Label | |||
resp, err := s.client.Do(ctx, req, &labels) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return labels, resp, nil | |||
} | |||
// GetLabel gets a single label. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label | |||
func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, name string) (*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
label := new(Label) | |||
resp, err := s.client.Do(ctx, req, label) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return label, resp, nil | |||
} | |||
// CreateLabel creates a new label on the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#create-a-label | |||
func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo string, label *Label) (*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, label) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
l := new(Label) | |||
resp, err := s.client.Do(ctx, req, l) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return l, resp, nil | |||
} | |||
// EditLabel edits a label. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#update-a-label | |||
func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string, name string, label *Label) (*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | |||
req, err := s.client.NewRequest("PATCH", u, label) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
l := new(Label) | |||
resp, err := s.client.Do(ctx, req, l) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return l, resp, nil | |||
} | |||
// DeleteLabel deletes a label. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#delete-a-label | |||
func (s *IssuesService) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListLabelsByIssue lists all labels for an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue | |||
func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
var labels []*Label | |||
resp, err := s.client.Do(ctx, req, &labels) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return labels, resp, nil | |||
} | |||
// AddLabelsToIssue adds labels to an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue | |||
func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, labels) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
var l []*Label | |||
resp, err := s.client.Do(ctx, req, &l) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return l, resp, nil | |||
} | |||
// RemoveLabelForIssue removes a label for an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue | |||
func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, repo string, number int, label string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ReplaceLabelsForIssue replaces all labels for an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue | |||
func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | |||
req, err := s.client.NewRequest("PUT", u, labels) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
var l []*Label | |||
resp, err := s.client.Do(ctx, req, &l) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return l, resp, nil | |||
} | |||
// RemoveLabelsForIssue removes all labels for an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue | |||
func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string, repo string, number int) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListLabelsForMilestone lists labels for every issue in a milestone. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone | |||
func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
var labels []*Label | |||
resp, err := s.client.Do(ctx, req, &labels) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return labels, resp, nil | |||
} |
@ -0,0 +1,148 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// Milestone represents a GitHub repository milestone. | |||
type Milestone struct { | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
LabelsURL *string `json:"labels_url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Number *int `json:"number,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
Title *string `json:"title,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Creator *User `json:"creator,omitempty"` | |||
OpenIssues *int `json:"open_issues,omitempty"` | |||
ClosedIssues *int `json:"closed_issues,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
ClosedAt *time.Time `json:"closed_at,omitempty"` | |||
DueOn *time.Time `json:"due_on,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
func (m Milestone) String() string { | |||
return Stringify(m) | |||
} | |||
// MilestoneListOptions specifies the optional parameters to the | |||
// IssuesService.ListMilestones method. | |||
type MilestoneListOptions struct { | |||
// State filters milestones based on their state. Possible values are: | |||
// open, closed, all. Default is "open". | |||
State string `url:"state,omitempty"` | |||
// Sort specifies how to sort milestones. Possible values are: due_on, completeness. | |||
// Default value is "due_on". | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort milestones. Possible values are: asc, desc. | |||
// Default is "asc". | |||
Direction string `url:"direction,omitempty"` | |||
ListOptions | |||
} | |||
// ListMilestones lists all milestones for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository | |||
func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var milestones []*Milestone | |||
resp, err := s.client.Do(ctx, req, &milestones) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return milestones, resp, nil | |||
} | |||
// GetMilestone gets a single milestone. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone | |||
func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo string, number int) (*Milestone, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
milestone := new(Milestone) | |||
resp, err := s.client.Do(ctx, req, milestone) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return milestone, resp, nil | |||
} | |||
// CreateMilestone creates a new milestone on the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone | |||
func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, milestone) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(Milestone) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// EditMilestone edits a milestone. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone | |||
func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("PATCH", u, milestone) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(Milestone) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// DeleteMilestone deletes a milestone. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone | |||
func (s *IssuesService) DeleteMilestone(ctx context.Context, owner string, repo string, number int) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,154 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"strings" | |||
"time" | |||
) | |||
// Timeline represents an event that occurred around an Issue or Pull Request. | |||
// | |||
// It is similar to an IssueEvent but may contain more information. | |||
// GitHub API docs: https://developer.github.com/v3/issues/timeline/ | |||
type Timeline struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
CommitURL *string `json:"commit_url,omitempty"` | |||
// The User object that generated the event. | |||
Actor *User `json:"actor,omitempty"` | |||
// Event identifies the actual type of Event that occurred. Possible values | |||
// are: | |||
// | |||
// assigned | |||
// The issue was assigned to the assignee. | |||
// | |||
// closed | |||
// The issue was closed by the actor. When the commit_id is present, it | |||
// identifies the commit that closed the issue using "closes / fixes #NN" | |||
// syntax. | |||
// | |||
// commented | |||
// A comment was added to the issue. | |||
// | |||
// committed | |||
// A commit was added to the pull request's 'HEAD' branch. Only provided | |||
// for pull requests. | |||
// | |||
// cross-referenced | |||
// The issue was referenced from another issue. The 'source' attribute | |||
// contains the 'id', 'actor', and 'url' of the reference's source. | |||
// | |||
// demilestoned | |||
// The issue was removed from a milestone. | |||
// | |||
// head_ref_deleted | |||
// The pull request's branch was deleted. | |||
// | |||
// head_ref_restored | |||
// The pull request's branch was restored. | |||
// | |||
// labeled | |||
// A label was added to the issue. | |||
// | |||
// locked | |||
// The issue was locked by the actor. | |||
// | |||
// mentioned | |||
// The actor was @mentioned in an issue body. | |||
// | |||
// merged | |||
// The issue was merged by the actor. The 'commit_id' attribute is the | |||
// SHA1 of the HEAD commit that was merged. | |||
// | |||
// milestoned | |||
// The issue was added to a milestone. | |||
// | |||
// referenced | |||
// The issue was referenced from a commit message. The 'commit_id' | |||
// attribute is the commit SHA1 of where that happened. | |||
// | |||
// renamed | |||
// The issue title was changed. | |||
// | |||
// reopened | |||
// The issue was reopened by the actor. | |||
// | |||
// subscribed | |||
// The actor subscribed to receive notifications for an issue. | |||
// | |||
// unassigned | |||
// The assignee was unassigned from the issue. | |||
// | |||
// unlabeled | |||
// A label was removed from the issue. | |||
// | |||
// unlocked | |||
// The issue was unlocked by the actor. | |||
// | |||
// unsubscribed | |||
// The actor unsubscribed to stop receiving notifications for an issue. | |||
// | |||
Event *string `json:"event,omitempty"` | |||
// The string SHA of a commit that referenced this Issue or Pull Request. | |||
CommitID *string `json:"commit_id,omitempty"` | |||
// The timestamp indicating when the event occurred. | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
// The Label object including `name` and `color` attributes. Only provided for | |||
// 'labeled' and 'unlabeled' events. | |||
Label *Label `json:"label,omitempty"` | |||
// The User object which was assigned to (or unassigned from) this Issue or | |||
// Pull Request. Only provided for 'assigned' and 'unassigned' events. | |||
Assignee *User `json:"assignee,omitempty"` | |||
// The Milestone object including a 'title' attribute. | |||
// Only provided for 'milestoned' and 'demilestoned' events. | |||
Milestone *Milestone `json:"milestone,omitempty"` | |||
// The 'id', 'actor', and 'url' for the source of a reference from another issue. | |||
// Only provided for 'cross-referenced' events. | |||
Source *Source `json:"source,omitempty"` | |||
// An object containing rename details including 'from' and 'to' attributes. | |||
// Only provided for 'renamed' events. | |||
Rename *Rename `json:"rename,omitempty"` | |||
ProjectCard *ProjectCard `json:"project_card,omitempty"` | |||
} | |||
// Source represents a reference's source. | |||
type Source struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Actor *User `json:"actor,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
Issue *Issue `json:"issue,omitempty"` | |||
} | |||
// ListIssueTimeline lists events for the specified issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue | |||
func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
acceptHeaders := []string{mediaTypeTimelinePreview, mediaTypeProjectCardDetailsPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var events []*Timeline | |||
resp, err := s.client.Do(ctx, req, &events) | |||
return events, resp, err | |||
} |
@ -0,0 +1,97 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// LicensesService handles communication with the license related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/licenses/ | |||
type LicensesService service | |||
// RepositoryLicense represents the license for a repository. | |||
type RepositoryLicense struct { | |||
Name *string `json:"name,omitempty"` | |||
Path *string `json:"path,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
GitURL *string `json:"git_url,omitempty"` | |||
DownloadURL *string `json:"download_url,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
Content *string `json:"content,omitempty"` | |||
Encoding *string `json:"encoding,omitempty"` | |||
License *License `json:"license,omitempty"` | |||
} | |||
func (l RepositoryLicense) String() string { | |||
return Stringify(l) | |||
} | |||
// License represents an open source license. | |||
type License struct { | |||
Key *string `json:"key,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
SPDXID *string `json:"spdx_id,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
Featured *bool `json:"featured,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Implementation *string `json:"implementation,omitempty"` | |||
Permissions *[]string `json:"permissions,omitempty"` | |||
Conditions *[]string `json:"conditions,omitempty"` | |||
Limitations *[]string `json:"limitations,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
} | |||
func (l License) String() string { | |||
return Stringify(l) | |||
} | |||
// List popular open source licenses. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses | |||
func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) { | |||
req, err := s.client.NewRequest("GET", "licenses", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var licenses []*License | |||
resp, err := s.client.Do(ctx, req, &licenses) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return licenses, resp, nil | |||
} | |||
// Get extended metadata for one license. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license | |||
func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License, *Response, error) { | |||
u := fmt.Sprintf("licenses/%s", licenseName) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
license := new(License) | |||
resp, err := s.client.Do(ctx, req, license) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return license, resp, nil | |||
} |
@ -0,0 +1,248 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// This file provides functions for validating payloads from GitHub Webhooks. | |||
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github | |||
package github | |||
import ( | |||
"crypto/hmac" | |||
"crypto/sha1" | |||
"crypto/sha256" | |||
"crypto/sha512" | |||
"encoding/hex" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"hash" | |||
"io/ioutil" | |||
"net/http" | |||
"net/url" | |||
"strings" | |||
) | |||
const ( | |||
// sha1Prefix is the prefix used by GitHub before the HMAC hexdigest. | |||
sha1Prefix = "sha1" | |||
// sha256Prefix and sha512Prefix are provided for future compatibility. | |||
sha256Prefix = "sha256" | |||
sha512Prefix = "sha512" | |||
// signatureHeader is the GitHub header key used to pass the HMAC hexdigest. | |||
signatureHeader = "X-Hub-Signature" | |||
// eventTypeHeader is the GitHub header key used to pass the event type. | |||
eventTypeHeader = "X-Github-Event" | |||
// deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. | |||
deliveryIDHeader = "X-Github-Delivery" | |||
) | |||
var ( | |||
// eventTypeMapping maps webhooks types to their corresponding go-github struct types. | |||
eventTypeMapping = map[string]string{ | |||
"check_run": "CheckRunEvent", | |||
"check_suite": "CheckSuiteEvent", | |||
"commit_comment": "CommitCommentEvent", | |||
"create": "CreateEvent", | |||
"delete": "DeleteEvent", | |||
"deployment": "DeploymentEvent", | |||
"deployment_status": "DeploymentStatusEvent", | |||
"fork": "ForkEvent", | |||
"gollum": "GollumEvent", | |||
"installation": "InstallationEvent", | |||
"installation_repositories": "InstallationRepositoriesEvent", | |||
"issue_comment": "IssueCommentEvent", | |||
"issues": "IssuesEvent", | |||
"label": "LabelEvent", | |||
"marketplace_purchase": "MarketplacePurchaseEvent", | |||
"member": "MemberEvent", | |||
"membership": "MembershipEvent", | |||
"milestone": "MilestoneEvent", | |||
"organization": "OrganizationEvent", | |||
"org_block": "OrgBlockEvent", | |||
"page_build": "PageBuildEvent", | |||
"ping": "PingEvent", | |||
"project": "ProjectEvent", | |||
"project_card": "ProjectCardEvent", | |||
"project_column": "ProjectColumnEvent", | |||
"public": "PublicEvent", | |||
"pull_request_review": "PullRequestReviewEvent", | |||
"pull_request_review_comment": "PullRequestReviewCommentEvent", | |||
"pull_request": "PullRequestEvent", | |||
"push": "PushEvent", | |||
"repository": "RepositoryEvent", | |||
"repository_vulnerability_alert": "RepositoryVulnerabilityAlertEvent", | |||
"release": "ReleaseEvent", | |||
"status": "StatusEvent", | |||
"team": "TeamEvent", | |||
"team_add": "TeamAddEvent", | |||
"watch": "WatchEvent", | |||
} | |||
) | |||
// genMAC generates the HMAC signature for a message provided the secret key | |||
// and hashFunc. | |||
func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { | |||
mac := hmac.New(hashFunc, key) | |||
mac.Write(message) | |||
return mac.Sum(nil) | |||
} | |||
// checkMAC reports whether messageMAC is a valid HMAC tag for message. | |||
func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { | |||
expectedMAC := genMAC(message, key, hashFunc) | |||
return hmac.Equal(messageMAC, expectedMAC) | |||
} | |||
// messageMAC returns the hex-decoded HMAC tag from the signature and its | |||
// corresponding hash function. | |||
func messageMAC(signature string) ([]byte, func() hash.Hash, error) { | |||
if signature == "" { | |||
return nil, nil, errors.New("missing signature") | |||
} | |||
sigParts := strings.SplitN(signature, "=", 2) | |||
if len(sigParts) != 2 { | |||
return nil, nil, fmt.Errorf("error parsing signature %q", signature) | |||
} | |||
var hashFunc func() hash.Hash | |||
switch sigParts[0] { | |||
case sha1Prefix: | |||
hashFunc = sha1.New | |||
case sha256Prefix: | |||
hashFunc = sha256.New | |||
case sha512Prefix: | |||
hashFunc = sha512.New | |||
default: | |||
return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0]) | |||
} | |||
buf, err := hex.DecodeString(sigParts[1]) | |||
if err != nil { | |||
return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err) | |||
} | |||
return buf, hashFunc, nil | |||
} | |||
// ValidatePayload validates an incoming GitHub Webhook event request | |||
// and returns the (JSON) payload. | |||
// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". | |||
// If the Content-Type is neither then an error is returned. | |||
// secretKey is the GitHub Webhook secret message. | |||
// | |||
// Example usage: | |||
// | |||
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
// payload, err := github.ValidatePayload(r, s.webhookSecretKey) | |||
// if err != nil { ... } | |||
// // Process payload... | |||
// } | |||
// | |||
func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) { | |||
var body []byte // Raw body that GitHub uses to calculate the signature. | |||
switch ct := r.Header.Get("Content-Type"); ct { | |||
case "application/json": | |||
var err error | |||
if body, err = ioutil.ReadAll(r.Body); err != nil { | |||
return nil, err | |||
} | |||
// If the content type is application/json, | |||
// the JSON payload is just the original body. | |||
payload = body | |||
case "application/x-www-form-urlencoded": | |||
// payloadFormParam is the name of the form parameter that the JSON payload | |||
// will be in if a webhook has its content type set to application/x-www-form-urlencoded. | |||
const payloadFormParam = "payload" | |||
var err error | |||
if body, err = ioutil.ReadAll(r.Body); err != nil { | |||
return nil, err | |||
} | |||
// If the content type is application/x-www-form-urlencoded, | |||
// the JSON payload will be under the "payload" form param. | |||
form, err := url.ParseQuery(string(body)) | |||
if err != nil { | |||
return nil, err | |||
} | |||
payload = []byte(form.Get(payloadFormParam)) | |||
default: | |||
return nil, fmt.Errorf("Webhook request has unsupported Content-Type %q", ct) | |||
} | |||
sig := r.Header.Get(signatureHeader) | |||
if err := ValidateSignature(sig, body, secretKey); err != nil { | |||
return nil, err | |||
} | |||
return payload, nil | |||
} | |||
// ValidateSignature validates the signature for the given payload. | |||
// signature is the GitHub hash signature delivered in the X-Hub-Signature header. | |||
// payload is the JSON payload sent by GitHub Webhooks. | |||
// secretKey is the GitHub Webhook secret message. | |||
// | |||
// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github | |||
func ValidateSignature(signature string, payload, secretKey []byte) error { | |||
messageMAC, hashFunc, err := messageMAC(signature) | |||
if err != nil { | |||
return err | |||
} | |||
if !checkMAC(payload, messageMAC, secretKey, hashFunc) { | |||
return errors.New("payload signature check failed") | |||
} | |||
return nil | |||
} | |||
// WebHookType returns the event type of webhook request r. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers | |||
func WebHookType(r *http.Request) string { | |||
return r.Header.Get(eventTypeHeader) | |||
} | |||
// DeliveryID returns the unique delivery ID of webhook request r. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers | |||
func DeliveryID(r *http.Request) string { | |||
return r.Header.Get(deliveryIDHeader) | |||
} | |||
// ParseWebHook parses the event payload. For recognized event types, a | |||
// value of the corresponding struct type will be returned (as returned | |||
// by Event.ParsePayload()). An error will be returned for unrecognized event | |||
// types. | |||
// | |||
// Example usage: | |||
// | |||
// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |||
// payload, err := github.ValidatePayload(r, s.webhookSecretKey) | |||
// if err != nil { ... } | |||
// event, err := github.ParseWebHook(github.WebHookType(r), payload) | |||
// if err != nil { ... } | |||
// switch event := event.(type) { | |||
// case *github.CommitCommentEvent: | |||
// processCommitCommentEvent(event) | |||
// case *github.CreateEvent: | |||
// processCreateEvent(event) | |||
// ... | |||
// } | |||
// } | |||
// | |||
func ParseWebHook(messageType string, payload []byte) (interface{}, error) { | |||
eventType, ok := eventTypeMapping[messageType] | |||
if !ok { | |||
return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType) | |||
} | |||
event := Event{ | |||
Type: &eventType, | |||
RawPayload: (*json.RawMessage)(&payload), | |||
} | |||
return event.ParsePayload() | |||
} |
@ -0,0 +1,224 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"errors" | |||
"fmt" | |||
"net/http" | |||
"strings" | |||
) | |||
// MigrationService provides access to the migration related functions | |||
// in the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/ | |||
type MigrationService service | |||
// Migration represents a GitHub migration (archival). | |||
type Migration struct { | |||
ID *int64 `json:"id,omitempty"` | |||
GUID *string `json:"guid,omitempty"` | |||
// State is the current state of a migration. | |||
// Possible values are: | |||
// "pending" which means the migration hasn't started yet, | |||
// "exporting" which means the migration is in progress, | |||
// "exported" which means the migration finished successfully, or | |||
// "failed" which means the migration failed. | |||
State *string `json:"state,omitempty"` | |||
// LockRepositories indicates whether repositories are locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories *bool `json:"lock_repositories,omitempty"` | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
CreatedAt *string `json:"created_at,omitempty"` | |||
UpdatedAt *string `json:"updated_at,omitempty"` | |||
Repositories []*Repository `json:"repositories,omitempty"` | |||
} | |||
func (m Migration) String() string { | |||
return Stringify(m) | |||
} | |||
// MigrationOptions specifies the optional parameters to Migration methods. | |||
type MigrationOptions struct { | |||
// LockRepositories indicates whether repositories should be locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories bool | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments bool | |||
} | |||
// startMigration represents the body of a StartMigration request. | |||
type startMigration struct { | |||
// Repositories is a slice of repository names to migrate. | |||
Repositories []string `json:"repositories,omitempty"` | |||
// LockRepositories indicates whether repositories should be locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories *bool `json:"lock_repositories,omitempty"` | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` | |||
} | |||
// StartMigration starts the generation of a migration archive. | |||
// repos is a slice of repository names to migrate. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration | |||
func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/migrations", org) | |||
body := &startMigration{Repositories: repos} | |||
if opt != nil { | |||
body.LockRepositories = Bool(opt.LockRepositories) | |||
body.ExcludeAttachments = Bool(opt.ExcludeAttachments) | |||
} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
m := &Migration{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListMigrations lists the most recent migrations. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations | |||
func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*Migration, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/migrations", org) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
var m []*Migration | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// MigrationStatus gets the status of a specific migration archive. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration | |||
func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int64) (*Migration, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/migrations/%v", org, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
m := &Migration{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// MigrationArchiveURL fetches a migration archive URL. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive | |||
func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int64) (url string, err error) { | |||
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return "", err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
s.client.clientMu.Lock() | |||
defer s.client.clientMu.Unlock() | |||
// Disable the redirect mechanism because AWS fails if the GitHub auth token is provided. | |||
var loc string | |||
saveRedirect := s.client.client.CheckRedirect | |||
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { | |||
loc = req.URL.String() | |||
return errors.New("disable redirect") | |||
} | |||
defer func() { s.client.client.CheckRedirect = saveRedirect }() | |||
_, err = s.client.Do(ctx, req, nil) // expect error from disable redirect | |||
if err == nil { | |||
return "", errors.New("expected redirect, none provided") | |||
} | |||
if !strings.Contains(err.Error(), "disable redirect") { | |||
return "", err | |||
} | |||
return loc, nil | |||
} | |||
// DeleteMigration deletes a previous migration archive. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive | |||
func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// UnlockRepo unlocks a repository that was locked for migration. | |||
// id is the migration ID. | |||
// You should unlock each migrated repository and delete them when the migration | |||
// is complete and you no longer need the source data. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository | |||
func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int64, repo string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,329 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Import represents a repository import request. | |||
type Import struct { | |||
// The URL of the originating repository. | |||
VCSURL *string `json:"vcs_url,omitempty"` | |||
// The originating VCS type. Can be one of 'subversion', 'git', | |||
// 'mercurial', or 'tfvc'. Without this parameter, the import job will | |||
// take additional time to detect the VCS type before beginning the | |||
// import. This detection step will be reflected in the response. | |||
VCS *string `json:"vcs,omitempty"` | |||
// VCSUsername and VCSPassword are only used for StartImport calls that | |||
// are importing a password-protected repository. | |||
VCSUsername *string `json:"vcs_username,omitempty"` | |||
VCSPassword *string `json:"vcs_password,omitempty"` | |||
// For a tfvc import, the name of the project that is being imported. | |||
TFVCProject *string `json:"tfvc_project,omitempty"` | |||
// LFS related fields that may be preset in the Import Progress response | |||
// Describes whether the import has been opted in or out of using Git | |||
// LFS. The value can be 'opt_in', 'opt_out', or 'undecided' if no | |||
// action has been taken. | |||
UseLFS *string `json:"use_lfs,omitempty"` | |||
// Describes whether files larger than 100MB were found during the | |||
// importing step. | |||
HasLargeFiles *bool `json:"has_large_files,omitempty"` | |||
// The total size in gigabytes of files larger than 100MB found in the | |||
// originating repository. | |||
LargeFilesSize *int `json:"large_files_size,omitempty"` | |||
// The total number of files larger than 100MB found in the originating | |||
// repository. To see a list of these files, call LargeFiles. | |||
LargeFilesCount *int `json:"large_files_count,omitempty"` | |||
// Identifies the current status of an import. An import that does not | |||
// have errors will progress through these steps: | |||
// | |||
// detecting - the "detection" step of the import is in progress | |||
// because the request did not include a VCS parameter. The | |||
// import is identifying the type of source control present at | |||
// the URL. | |||
// importing - the "raw" step of the import is in progress. This is | |||
// where commit data is fetched from the original repository. | |||
// The import progress response will include CommitCount (the | |||
// total number of raw commits that will be imported) and | |||
// Percent (0 - 100, the current progress through the import). | |||
// mapping - the "rewrite" step of the import is in progress. This | |||
// is where SVN branches are converted to Git branches, and | |||
// where author updates are applied. The import progress | |||
// response does not include progress information. | |||
// pushing - the "push" step of the import is in progress. This is | |||
// where the importer updates the repository on GitHub. The | |||
// import progress response will include PushPercent, which is | |||
// the percent value reported by git push when it is "Writing | |||
// objects". | |||
// complete - the import is complete, and the repository is ready | |||
// on GitHub. | |||
// | |||
// If there are problems, you will see one of these in the status field: | |||
// | |||
// auth_failed - the import requires authentication in order to | |||
// connect to the original repository. Make an UpdateImport | |||
// request, and include VCSUsername and VCSPassword. | |||
// error - the import encountered an error. The import progress | |||
// response will include the FailedStep and an error message. | |||
// Contact GitHub support for more information. | |||
// detection_needs_auth - the importer requires authentication for | |||
// the originating repository to continue detection. Make an | |||
// UpdatImport request, and include VCSUsername and | |||
// VCSPassword. | |||
// detection_found_nothing - the importer didn't recognize any | |||
// source control at the URL. | |||
// detection_found_multiple - the importer found several projects | |||
// or repositories at the provided URL. When this is the case, | |||
// the Import Progress response will also include a | |||
// ProjectChoices field with the possible project choices as | |||
// values. Make an UpdateImport request, and include VCS and | |||
// (if applicable) TFVCProject. | |||
Status *string `json:"status,omitempty"` | |||
CommitCount *int `json:"commit_count,omitempty"` | |||
StatusText *string `json:"status_text,omitempty"` | |||
AuthorsCount *int `json:"authors_count,omitempty"` | |||
Percent *int `json:"percent,omitempty"` | |||
PushPercent *int `json:"push_percent,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
AuthorsURL *string `json:"authors_url,omitempty"` | |||
RepositoryURL *string `json:"repository_url,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
FailedStep *string `json:"failed_step,omitempty"` | |||
// Human readable display name, provided when the Import appears as | |||
// part of ProjectChoices. | |||
HumanName *string `json:"human_name,omitempty"` | |||
// When the importer finds several projects or repositories at the | |||
// provided URLs, this will identify the available choices. Call | |||
// UpdateImport with the selected Import value. | |||
ProjectChoices []Import `json:"project_choices,omitempty"` | |||
} | |||
func (i Import) String() string { | |||
return Stringify(i) | |||
} | |||
// SourceImportAuthor identifies an author imported from a source repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors | |||
type SourceImportAuthor struct { | |||
ID *int64 `json:"id,omitempty"` | |||
RemoteID *string `json:"remote_id,omitempty"` | |||
RemoteName *string `json:"remote_name,omitempty"` | |||
Email *string `json:"email,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
ImportURL *string `json:"import_url,omitempty"` | |||
} | |||
func (a SourceImportAuthor) String() string { | |||
return Stringify(a) | |||
} | |||
// LargeFile identifies a file larger than 100MB found during a repository import. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files | |||
type LargeFile struct { | |||
RefName *string `json:"ref_name,omitempty"` | |||
Path *string `json:"path,omitempty"` | |||
OID *string `json:"oid,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
} | |||
func (f LargeFile) String() string { | |||
return Stringify(f) | |||
} | |||
// StartImport initiates a repository import. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import | |||
func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) | |||
req, err := s.client.NewRequest("PUT", u, in) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
out := new(Import) | |||
resp, err := s.client.Do(ctx, req, out) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return out, resp, nil | |||
} | |||
// ImportProgress queries for the status and progress of an ongoing repository import. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress | |||
func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo string) (*Import, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
out := new(Import) | |||
resp, err := s.client.Do(ctx, req, out) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return out, resp, nil | |||
} | |||
// UpdateImport initiates a repository import. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import | |||
func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) | |||
req, err := s.client.NewRequest("PATCH", u, in) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
out := new(Import) | |||
resp, err := s.client.Do(ctx, req, out) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return out, resp, nil | |||
} | |||
// CommitAuthors gets the authors mapped from the original repository. | |||
// | |||
// Each type of source control system represents authors in a different way. | |||
// For example, a Git commit author has a display name and an email address, | |||
// but a Subversion commit author just has a username. The GitHub Importer will | |||
// make the author information valid, but the author might not be correct. For | |||
// example, it will change the bare Subversion username "hubot" into something | |||
// like "hubot <hubot@12341234-abab-fefe-8787-fedcba987654>". | |||
// | |||
// This method and MapCommitAuthor allow you to provide correct Git author | |||
// information. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors | |||
func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string) ([]*SourceImportAuthor, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
var authors []*SourceImportAuthor | |||
resp, err := s.client.Do(ctx, req, &authors) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return authors, resp, nil | |||
} | |||
// MapCommitAuthor updates an author's identity for the import. Your | |||
// application can continue updating authors any time before you push new | |||
// commits to the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author | |||
func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("PATCH", u, author) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
out := new(SourceImportAuthor) | |||
resp, err := s.client.Do(ctx, req, out) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return out, resp, nil | |||
} | |||
// SetLFSPreference sets whether imported repositories should use Git LFS for | |||
// files larger than 100MB. Only the UseLFS field on the provided Import is | |||
// used. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference | |||
func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo) | |||
req, err := s.client.NewRequest("PATCH", u, in) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
out := new(Import) | |||
resp, err := s.client.Do(ctx, req, out) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return out, resp, nil | |||
} | |||
// LargeFiles lists files larger than 100MB found during the import. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files | |||
func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ([]*LargeFile, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
var files []*LargeFile | |||
resp, err := s.client.Do(ctx, req, &files) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return files, resp, nil | |||
} | |||
// CancelImport stops an import for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import | |||
func (s *MigrationService) CancelImport(ctx context.Context, owner, repo string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/import", owner, repo) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches | |||
req.Header.Set("Accept", mediaTypeImportPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,214 @@ | |||
// Copyright 2018 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"errors" | |||
"fmt" | |||
"net/http" | |||
) | |||
// UserMigration represents a GitHub migration (archival). | |||
type UserMigration struct { | |||
ID *int64 `json:"id,omitempty"` | |||
GUID *string `json:"guid,omitempty"` | |||
// State is the current state of a migration. | |||
// Possible values are: | |||
// "pending" which means the migration hasn't started yet, | |||
// "exporting" which means the migration is in progress, | |||
// "exported" which means the migration finished successfully, or | |||
// "failed" which means the migration failed. | |||
State *string `json:"state,omitempty"` | |||
// LockRepositories indicates whether repositories are locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories *bool `json:"lock_repositories,omitempty"` | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
CreatedAt *string `json:"created_at,omitempty"` | |||
UpdatedAt *string `json:"updated_at,omitempty"` | |||
Repositories []*Repository `json:"repositories,omitempty"` | |||
} | |||
func (m UserMigration) String() string { | |||
return Stringify(m) | |||
} | |||
// UserMigrationOptions specifies the optional parameters to Migration methods. | |||
type UserMigrationOptions struct { | |||
// LockRepositories indicates whether repositories should be locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories bool | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments bool | |||
} | |||
// startUserMigration represents the body of a StartMigration request. | |||
type startUserMigration struct { | |||
// Repositories is a slice of repository names to migrate. | |||
Repositories []string `json:"repositories,omitempty"` | |||
// LockRepositories indicates whether repositories should be locked (to prevent | |||
// manipulation) while migrating data. | |||
LockRepositories *bool `json:"lock_repositories,omitempty"` | |||
// ExcludeAttachments indicates whether attachments should be excluded from | |||
// the migration (to reduce migration archive file size). | |||
ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` | |||
} | |||
// StartUserMigration starts the generation of a migration archive. | |||
// repos is a slice of repository names to migrate. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#start-a-user-migration | |||
func (s *MigrationService) StartUserMigration(ctx context.Context, repos []string, opt *UserMigrationOptions) (*UserMigration, *Response, error) { | |||
u := "user/migrations" | |||
body := &startUserMigration{Repositories: repos} | |||
if opt != nil { | |||
body.LockRepositories = Bool(opt.LockRepositories) | |||
body.ExcludeAttachments = Bool(opt.ExcludeAttachments) | |||
} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
m := &UserMigration{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListUserMigrations lists the most recent migrations. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-a-list-of-user-migrations | |||
func (s *MigrationService) ListUserMigrations(ctx context.Context) ([]*UserMigration, *Response, error) { | |||
u := "user/migrations" | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
var m []*UserMigration | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// UserMigrationStatus gets the status of a specific migration archive. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#get-the-status-of-a-user-migration | |||
func (s *MigrationService) UserMigrationStatus(ctx context.Context, id int64) (*UserMigration, *Response, error) { | |||
u := fmt.Sprintf("user/migrations/%v", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
m := &UserMigration{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// UserMigrationArchiveURL gets the URL for a specific migration archive. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#download-a-user-migration-archive | |||
func (s *MigrationService) UserMigrationArchiveURL(ctx context.Context, id int64) (string, error) { | |||
url := fmt.Sprintf("user/migrations/%v/archive", id) | |||
req, err := s.client.NewRequest("GET", url, nil) | |||
if err != nil { | |||
return "", err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
m := &UserMigration{} | |||
var loc string | |||
originalRedirect := s.client.client.CheckRedirect | |||
s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { | |||
loc = req.URL.String() | |||
return http.ErrUseLastResponse | |||
} | |||
defer func() { | |||
s.client.client.CheckRedirect = originalRedirect | |||
}() | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err == nil { | |||
return "", errors.New("expected redirect, none provided") | |||
} | |||
loc = resp.Header.Get("Location") | |||
return loc, nil | |||
} | |||
// DeleteUserMigration will delete a previous migration archive. | |||
// id is the migration ID. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#delete-a-user-migration-archive | |||
func (s *MigrationService) DeleteUserMigration(ctx context.Context, id int64) (*Response, error) { | |||
url := fmt.Sprintf("user/migrations/%v/archive", id) | |||
req, err := s.client.NewRequest("DELETE", url, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// UnlockUserRepo will unlock a repo that was locked for migration. | |||
// id is migration ID. | |||
// You should unlock each migrated repository and delete them when the migration | |||
// is complete and you no longer need the source data. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/migrations/users/#unlock-a-user-repository | |||
func (s *MigrationService) UnlockUserRepo(ctx context.Context, id int64, repo string) (*Response, error) { | |||
url := fmt.Sprintf("user/migrations/%v/repos/%v/lock", id, repo) | |||
req, err := s.client.NewRequest("DELETE", url, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeMigrationsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,257 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"bytes" | |||
"context" | |||
"fmt" | |||
"net/url" | |||
) | |||
// MarkdownOptions specifies optional parameters to the Markdown method. | |||
type MarkdownOptions struct { | |||
// Mode identifies the rendering mode. Possible values are: | |||
// markdown - render a document as plain Markdown, just like | |||
// README files are rendered. | |||
// | |||
// gfm - to render a document as user-content, e.g. like user | |||
// comments or issues are rendered. In GFM mode, hard line breaks are | |||
// always taken into account, and issue and user mentions are linked | |||
// accordingly. | |||
// | |||
// Default is "markdown". | |||
Mode string | |||
// Context identifies the repository context. Only taken into account | |||
// when rendering as "gfm". | |||
Context string | |||
} | |||
type markdownRequest struct { | |||
Text *string `json:"text,omitempty"` | |||
Mode *string `json:"mode,omitempty"` | |||
Context *string `json:"context,omitempty"` | |||
} | |||
// Markdown renders an arbitrary Markdown document. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/markdown/ | |||
func (c *Client) Markdown(ctx context.Context, text string, opt *MarkdownOptions) (string, *Response, error) { | |||
request := &markdownRequest{Text: String(text)} | |||
if opt != nil { | |||
if opt.Mode != "" { | |||
request.Mode = String(opt.Mode) | |||
} | |||
if opt.Context != "" { | |||
request.Context = String(opt.Context) | |||
} | |||
} | |||
req, err := c.NewRequest("POST", "markdown", request) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
buf := new(bytes.Buffer) | |||
resp, err := c.Do(ctx, req, buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// ListEmojis returns the emojis available to use on GitHub. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/emojis/ | |||
func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { | |||
req, err := c.NewRequest("GET", "emojis", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var emoji map[string]string | |||
resp, err := c.Do(ctx, req, &emoji) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return emoji, resp, nil | |||
} | |||
// CodeOfConduct represents a code of conduct. | |||
type CodeOfConduct struct { | |||
Name *string `json:"name,omitempty"` | |||
Key *string `json:"key,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
} | |||
func (c *CodeOfConduct) String() string { | |||
return Stringify(c) | |||
} | |||
// ListCodesOfConduct returns all codes of conduct. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#list-all-codes-of-conduct | |||
func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { | |||
req, err := c.NewRequest("GET", "codes_of_conduct", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeCodesOfConductPreview) | |||
var cs []*CodeOfConduct | |||
resp, err := c.Do(ctx, req, &cs) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return cs, resp, nil | |||
} | |||
// GetCodeOfConduct returns an individual code of conduct. | |||
// | |||
// https://developer.github.com/v3/codes_of_conduct/#get-an-individual-code-of-conduct | |||
func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { | |||
u := fmt.Sprintf("codes_of_conduct/%s", key) | |||
req, err := c.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeCodesOfConductPreview) | |||
coc := new(CodeOfConduct) | |||
resp, err := c.Do(ctx, req, coc) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return coc, resp, nil | |||
} | |||
// APIMeta represents metadata about the GitHub API. | |||
type APIMeta struct { | |||
// An Array of IP addresses in CIDR format specifying the addresses | |||
// that incoming service hooks will originate from on GitHub.com. | |||
Hooks []string `json:"hooks,omitempty"` | |||
// An Array of IP addresses in CIDR format specifying the Git servers | |||
// for GitHub.com. | |||
Git []string `json:"git,omitempty"` | |||
// Whether authentication with username and password is supported. | |||
// (GitHub Enterprise instances using CAS or OAuth for authentication | |||
// will return false. Features like Basic Authentication with a | |||
// username and password, sudo mode, and two-factor authentication are | |||
// not supported on these servers.) | |||
VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` | |||
// An array of IP addresses in CIDR format specifying the addresses | |||
// which serve GitHub Pages websites. | |||
Pages []string `json:"pages,omitempty"` | |||
// An Array of IP addresses specifying the addresses that source imports | |||
// will originate from on GitHub.com. | |||
Importer []string `json:"importer,omitempty"` | |||
} | |||
// APIMeta returns information about GitHub.com, the service. Or, if you access | |||
// this endpoint on your organization’s GitHub Enterprise installation, this | |||
// endpoint provides information about that installation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/meta/ | |||
func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { | |||
req, err := c.NewRequest("GET", "meta", nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
meta := new(APIMeta) | |||
resp, err := c.Do(ctx, req, meta) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return meta, resp, nil | |||
} | |||
// Octocat returns an ASCII art octocat with the specified message in a speech | |||
// bubble. If message is empty, a random zen phrase is used. | |||
func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { | |||
u := "octocat" | |||
if message != "" { | |||
u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) | |||
} | |||
req, err := c.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
buf := new(bytes.Buffer) | |||
resp, err := c.Do(ctx, req, buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// Zen returns a random line from The Zen of GitHub. | |||
// | |||
// see also: http://warpspire.com/posts/taste/ | |||
func (c *Client) Zen(ctx context.Context) (string, *Response, error) { | |||
req, err := c.NewRequest("GET", "zen", nil) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
buf := new(bytes.Buffer) | |||
resp, err := c.Do(ctx, req, buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// ServiceHook represents a hook that has configuration settings, a list of | |||
// available events, and default events. | |||
type ServiceHook struct { | |||
Name *string `json:"name,omitempty"` | |||
Events []string `json:"events,omitempty"` | |||
SupportedEvents []string `json:"supported_events,omitempty"` | |||
Schema [][]string `json:"schema,omitempty"` | |||
} | |||
func (s *ServiceHook) String() string { | |||
return Stringify(s) | |||
} | |||
// ListServiceHooks lists all of the available service hooks. | |||
// | |||
// GitHub API docs: https://developer.github.com/webhooks/#services | |||
func (c *Client) ListServiceHooks(ctx context.Context) ([]*ServiceHook, *Response, error) { | |||
u := "hooks" | |||
req, err := c.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var hooks []*ServiceHook | |||
resp, err := c.Do(ctx, req, &hooks) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return hooks, resp, nil | |||
} |
@ -0,0 +1,208 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// OrganizationsService provides access to the organization related functions | |||
// in the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/ | |||
type OrganizationsService service | |||
// Organization represents a GitHub organization account. | |||
type Organization struct { | |||
Login *string `json:"login,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
AvatarURL *string `json:"avatar_url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Company *string `json:"company,omitempty"` | |||
Blog *string `json:"blog,omitempty"` | |||
Location *string `json:"location,omitempty"` | |||
Email *string `json:"email,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
PublicRepos *int `json:"public_repos,omitempty"` | |||
PublicGists *int `json:"public_gists,omitempty"` | |||
Followers *int `json:"followers,omitempty"` | |||
Following *int `json:"following,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
TotalPrivateRepos *int `json:"total_private_repos,omitempty"` | |||
OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` | |||
PrivateGists *int `json:"private_gists,omitempty"` | |||
DiskUsage *int `json:"disk_usage,omitempty"` | |||
Collaborators *int `json:"collaborators,omitempty"` | |||
BillingEmail *string `json:"billing_email,omitempty"` | |||
Type *string `json:"type,omitempty"` | |||
Plan *Plan `json:"plan,omitempty"` | |||
TwoFactorRequirementEnabled *bool `json:"two_factor_requirement_enabled,omitempty"` | |||
// DefaultRepoPermission can be one of: "read", "write", "admin", or "none". (Default: "read"). | |||
// It is only used in OrganizationsService.Edit. | |||
DefaultRepoPermission *string `json:"default_repository_permission,omitempty"` | |||
// DefaultRepoSettings can be one of: "read", "write", "admin", or "none". (Default: "read"). | |||
// It is only used in OrganizationsService.Get. | |||
DefaultRepoSettings *string `json:"default_repository_settings,omitempty"` | |||
// MembersCanCreateRepos default value is true and is only used in Organizations.Edit. | |||
MembersCanCreateRepos *bool `json:"members_can_create_repositories,omitempty"` | |||
// API URLs | |||
URL *string `json:"url,omitempty"` | |||
EventsURL *string `json:"events_url,omitempty"` | |||
HooksURL *string `json:"hooks_url,omitempty"` | |||
IssuesURL *string `json:"issues_url,omitempty"` | |||
MembersURL *string `json:"members_url,omitempty"` | |||
PublicMembersURL *string `json:"public_members_url,omitempty"` | |||
ReposURL *string `json:"repos_url,omitempty"` | |||
} | |||
func (o Organization) String() string { | |||
return Stringify(o) | |||
} | |||
// Plan represents the payment plan for an account. See plans at https://github.com/plans. | |||
type Plan struct { | |||
Name *string `json:"name,omitempty"` | |||
Space *int `json:"space,omitempty"` | |||
Collaborators *int `json:"collaborators,omitempty"` | |||
PrivateRepos *int `json:"private_repos,omitempty"` | |||
} | |||
func (p Plan) String() string { | |||
return Stringify(p) | |||
} | |||
// OrganizationsListOptions specifies the optional parameters to the | |||
// OrganizationsService.ListAll method. | |||
type OrganizationsListOptions struct { | |||
// Since filters Organizations by ID. | |||
Since int64 `url:"since,omitempty"` | |||
// Note: Pagination is powered exclusively by the Since parameter, | |||
// ListOptions.Page has no effect. | |||
// ListOptions.PerPage controls an undocumented GitHub API parameter. | |||
ListOptions | |||
} | |||
// ListAll lists all organizations, in the order that they were created on GitHub. | |||
// | |||
// Note: Pagination is powered exclusively by the since parameter. To continue | |||
// listing the next set of organizations, use the ID of the last-returned organization | |||
// as the opts.Since parameter for the next call. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations | |||
func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsListOptions) ([]*Organization, *Response, error) { | |||
u, err := addOptions("organizations", opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
orgs := []*Organization{} | |||
resp, err := s.client.Do(ctx, req, &orgs) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return orgs, resp, nil | |||
} | |||
// List the organizations for a user. Passing the empty string will list | |||
// organizations for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/#list-user-organizations | |||
func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListOptions) ([]*Organization, *Response, error) { | |||
var u string | |||
if user != "" { | |||
u = fmt.Sprintf("users/%v/orgs", user) | |||
} else { | |||
u = "user/orgs" | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var orgs []*Organization | |||
resp, err := s.client.Do(ctx, req, &orgs) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return orgs, resp, nil | |||
} | |||
// Get fetches an organization by name. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/#get-an-organization | |||
func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organization, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v", org) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
organization := new(Organization) | |||
resp, err := s.client.Do(ctx, req, organization) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return organization, resp, nil | |||
} | |||
// GetByID fetches an organization. | |||
// | |||
// Note: GetByID uses the undocumented GitHub API endpoint /organizations/:id. | |||
func (s *OrganizationsService) GetByID(ctx context.Context, id int64) (*Organization, *Response, error) { | |||
u := fmt.Sprintf("organizations/%d", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
organization := new(Organization) | |||
resp, err := s.client.Do(ctx, req, organization) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return organization, resp, nil | |||
} | |||
// Edit an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/#edit-an-organization | |||
func (s *OrganizationsService) Edit(ctx context.Context, name string, org *Organization) (*Organization, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v", name) | |||
req, err := s.client.NewRequest("PATCH", u, org) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
o := new(Organization) | |||
resp, err := s.client.Do(ctx, req, o) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return o, resp, nil | |||
} |
@ -0,0 +1,117 @@ | |||
// Copyright 2015 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListHooks lists all Hooks for the specified organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks | |||
func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *ListOptions) ([]*Hook, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var hooks []*Hook | |||
resp, err := s.client.Do(ctx, req, &hooks) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return hooks, resp, nil | |||
} | |||
// GetHook returns a single specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook | |||
func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int64) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
hook := new(Hook) | |||
resp, err := s.client.Do(ctx, req, hook) | |||
return hook, resp, err | |||
} | |||
// CreateHook creates a Hook for the specified org. | |||
// Config is a required field. | |||
// | |||
// Note that only a subset of the hook fields are used and hook must | |||
// not be nil. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook | |||
func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks", org) | |||
hookReq := &createHookRequest{ | |||
Events: hook.Events, | |||
Active: hook.Active, | |||
Config: hook.Config, | |||
} | |||
req, err := s.client.NewRequest("POST", u, hookReq) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(Hook) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// EditHook updates a specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook | |||
func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int64, hook *Hook) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) | |||
req, err := s.client.NewRequest("PATCH", u, hook) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(Hook) | |||
resp, err := s.client.Do(ctx, req, h) | |||
return h, resp, err | |||
} | |||
// PingHook triggers a 'ping' event to be sent to the Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook | |||
func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// DeleteHook deletes a specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook | |||
func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,370 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// Membership represents the status of a user's membership in an organization or team. | |||
type Membership struct { | |||
URL *string `json:"url,omitempty"` | |||
// State is the user's status within the organization or team. | |||
// Possible values are: "active", "pending" | |||
State *string `json:"state,omitempty"` | |||
// Role identifies the user's role within the organization or team. | |||
// Possible values for organization membership: | |||
// member - non-owner organization member | |||
// admin - organization owner | |||
// | |||
// Possible values for team membership are: | |||
// member - a normal member of the team | |||
// maintainer - a team maintainer. Able to add/remove other team | |||
// members, promote other team members to team | |||
// maintainer, and edit the team’s name and description | |||
Role *string `json:"role,omitempty"` | |||
// For organization membership, the API URL of the organization. | |||
OrganizationURL *string `json:"organization_url,omitempty"` | |||
// For organization membership, the organization the membership is for. | |||
Organization *Organization `json:"organization,omitempty"` | |||
// For organization membership, the user the membership is for. | |||
User *User `json:"user,omitempty"` | |||
} | |||
func (m Membership) String() string { | |||
return Stringify(m) | |||
} | |||
// ListMembersOptions specifies optional parameters to the | |||
// OrganizationsService.ListMembers method. | |||
type ListMembersOptions struct { | |||
// If true (or if the authenticated user is not an owner of the | |||
// organization), list only publicly visible members. | |||
PublicOnly bool `url:"-"` | |||
// Filter members returned in the list. Possible values are: | |||
// 2fa_disabled, all. Default is "all". | |||
Filter string `url:"filter,omitempty"` | |||
// Role filters members returned by their role in the organization. | |||
// Possible values are: | |||
// all - all members of the organization, regardless of role | |||
// admin - organization owners | |||
// member - non-owner organization members | |||
// | |||
// Default is "all". | |||
Role string `url:"role,omitempty"` | |||
ListOptions | |||
} | |||
// ListMembers lists the members for an organization. If the authenticated | |||
// user is an owner of the organization, this will return both concealed and | |||
// public members, otherwise it will only return public members. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#members-list | |||
func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt *ListMembersOptions) ([]*User, *Response, error) { | |||
var u string | |||
if opt != nil && opt.PublicOnly { | |||
u = fmt.Sprintf("orgs/%v/public_members", org) | |||
} else { | |||
u = fmt.Sprintf("orgs/%v/members", org) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var members []*User | |||
resp, err := s.client.Do(ctx, req, &members) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return members, resp, nil | |||
} | |||
// IsMember checks if a user is a member of an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-membership | |||
func (s *OrganizationsService) IsMember(ctx context.Context, org, user string) (bool, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/members/%v", org, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
member, err := parseBoolResponse(err) | |||
return member, resp, err | |||
} | |||
// IsPublicMember checks if a user is a public member of an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-public-membership | |||
func (s *OrganizationsService) IsPublicMember(ctx context.Context, org, user string) (bool, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
member, err := parseBoolResponse(err) | |||
return member, resp, err | |||
} | |||
// RemoveMember removes a user from all teams of an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-a-member | |||
func (s *OrganizationsService) RemoveMember(ctx context.Context, org, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/members/%v", org, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// PublicizeMembership publicizes a user's membership in an organization. (A | |||
// user cannot publicize the membership for another user.) | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#publicize-a-users-membership | |||
func (s *OrganizationsService) PublicizeMembership(ctx context.Context, org, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ConcealMembership conceals a user's membership in an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#conceal-a-users-membership | |||
func (s *OrganizationsService) ConcealMembership(ctx context.Context, org, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListOrgMembershipsOptions specifies optional parameters to the | |||
// OrganizationsService.ListOrgMemberships method. | |||
type ListOrgMembershipsOptions struct { | |||
// Filter memberships to include only those with the specified state. | |||
// Possible values are: "active", "pending". | |||
State string `url:"state,omitempty"` | |||
ListOptions | |||
} | |||
// ListOrgMemberships lists the organization memberships for the authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships | |||
func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { | |||
u := "user/memberships/orgs" | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var memberships []*Membership | |||
resp, err := s.client.Do(ctx, req, &memberships) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return memberships, resp, nil | |||
} | |||
// GetOrgMembership gets the membership for a user in a specified organization. | |||
// Passing an empty string for user will get the membership for the | |||
// authenticated user. | |||
// | |||
// GitHub API docs: | |||
// https://developer.github.com/v3/orgs/members/#get-organization-membership | |||
// https://developer.github.com/v3/orgs/members/#get-your-organization-membership | |||
func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org string) (*Membership, *Response, error) { | |||
var u string | |||
if user != "" { | |||
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) | |||
} else { | |||
u = fmt.Sprintf("user/memberships/orgs/%v", org) | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
membership := new(Membership) | |||
resp, err := s.client.Do(ctx, req, membership) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return membership, resp, nil | |||
} | |||
// EditOrgMembership edits the membership for user in specified organization. | |||
// Passing an empty string for user will edit the membership for the | |||
// authenticated user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership | |||
func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org string, membership *Membership) (*Membership, *Response, error) { | |||
var u, method string | |||
if user != "" { | |||
u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) | |||
method = "PUT" | |||
} else { | |||
u = fmt.Sprintf("user/memberships/orgs/%v", org) | |||
method = "PATCH" | |||
} | |||
req, err := s.client.NewRequest(method, u, membership) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
m := new(Membership) | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// RemoveOrgMembership removes user from the specified organization. If the | |||
// user has been invited to the organization, this will cancel their invitation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership | |||
func (s *OrganizationsService) RemoveOrgMembership(ctx context.Context, user, org string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/memberships/%v", org, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListPendingOrgInvitations returns a list of pending invitations. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations | |||
func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org string, opt *ListOptions) ([]*Invitation, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/invitations", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var pendingInvitations []*Invitation | |||
resp, err := s.client.Do(ctx, req, &pendingInvitations) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return pendingInvitations, resp, nil | |||
} | |||
// CreateOrgInvitationOptions specifies the parameters to the OrganizationService.Invite | |||
// method. | |||
type CreateOrgInvitationOptions struct { | |||
// GitHub user ID for the person you are inviting. Not required if you provide Email. | |||
InviteeID *int64 `json:"invitee_id,omitempty"` | |||
// Email address of the person you are inviting, which can be an existing GitHub user. | |||
// Not required if you provide InviteeID | |||
Email *string `json:"email,omitempty"` | |||
// Specify role for new member. Can be one of: | |||
// * admin - Organization owners with full administrative rights to the | |||
// organization and complete access to all repositories and teams. | |||
// * direct_member - Non-owner organization members with ability to see | |||
// other members and join teams by invitation. | |||
// * billing_manager - Non-owner organization members with ability to | |||
// manage the billing settings of your organization. | |||
// Default is "direct_member". | |||
Role *string `json:"role"` | |||
TeamID []int64 `json:"team_ids"` | |||
} | |||
// CreateOrgInvitation invites people to an organization by using their GitHub user ID or their email address. | |||
// In order to create invitations in an organization, | |||
// the authenticated user must be an organization owner. | |||
// | |||
// https://developer.github.com/v3/orgs/members/#create-organization-invitation | |||
func (s *OrganizationsService) CreateOrgInvitation(ctx context.Context, org string, opt *CreateOrgInvitationOptions) (*Invitation, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/invitations", org) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview) | |||
var invitation *Invitation | |||
resp, err := s.client.Do(ctx, req, &invitation) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return invitation, resp, nil | |||
} | |||
// ListOrgInvitationTeams lists all teams associated with an invitation. In order to see invitations in an organization, | |||
// the authenticated user must be an organization owner. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-organization-invitation-teams | |||
func (s *OrganizationsService) ListOrgInvitationTeams(ctx context.Context, org, invitationID string, opt *ListOptions) ([]*Team, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/invitations/%v/teams", org, invitationID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeOrganizationInvitationPreview) | |||
var orgInvitationTeams []*Team | |||
resp, err := s.client.Do(ctx, req, &orgInvitationTeams) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return orgInvitationTeams, resp, nil | |||
} |
@ -0,0 +1,81 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListOutsideCollaboratorsOptions specifies optional parameters to the | |||
// OrganizationsService.ListOutsideCollaborators method. | |||
type ListOutsideCollaboratorsOptions struct { | |||
// Filter outside collaborators returned in the list. Possible values are: | |||
// 2fa_disabled, all. Default is "all". | |||
Filter string `url:"filter,omitempty"` | |||
ListOptions | |||
} | |||
// ListOutsideCollaborators lists outside collaborators of organization's repositories. | |||
// This will only work if the authenticated | |||
// user is an owner of the organization. | |||
// | |||
// Warning: The API may change without advance notice during the preview period. | |||
// Preview features are not supported for production use. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators | |||
func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opt *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/outside_collaborators", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var members []*User | |||
resp, err := s.client.Do(ctx, req, &members) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return members, resp, nil | |||
} | |||
// RemoveOutsideCollaborator removes a user from the list of outside collaborators; | |||
// consequently, removing them from all the organization's repositories. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator | |||
func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ConvertMemberToOutsideCollaborator reduces the permission level of a member of the | |||
// organization to that of an outside collaborator. Therefore, they will only | |||
// have access to the repositories that their current team membership allows. | |||
// Responses for converting a non-member or the last owner to an outside collaborator | |||
// are listed in GitHub API docs. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#convert-member-to-outside-collaborator | |||
func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,60 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListProjects lists the projects for an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/#list-organization-projects | |||
func (s *OrganizationsService) ListProjects(ctx context.Context, org string, opt *ProjectListOptions) ([]*Project, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/projects", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
var projects []*Project | |||
resp, err := s.client.Do(ctx, req, &projects) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return projects, resp, nil | |||
} | |||
// CreateProject creates a GitHub Project for the specified organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/#create-an-organization-project | |||
func (s *OrganizationsService) CreateProject(ctx context.Context, org string, opt *ProjectOptions) (*Project, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/projects", org) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
project := &Project{} | |||
resp, err := s.client.Do(ctx, req, project) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return project, resp, nil | |||
} |
@ -0,0 +1,91 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListBlockedUsers lists all the users blocked by an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#list-blocked-users | |||
func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opt *ListOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/blocks", org) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeBlockUsersPreview) | |||
var blockedUsers []*User | |||
resp, err := s.client.Do(ctx, req, &blockedUsers) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return blockedUsers, resp, nil | |||
} | |||
// IsBlocked reports whether specified user is blocked from an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#check-whether-a-user-is-blocked-from-an-organization | |||
func (s *OrganizationsService) IsBlocked(ctx context.Context, org string, user string) (bool, *Response, error) { | |||
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeBlockUsersPreview) | |||
resp, err := s.client.Do(ctx, req, nil) | |||
isBlocked, err := parseBoolResponse(err) | |||
return isBlocked, resp, err | |||
} | |||
// BlockUser blocks specified user from an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#block-a-user | |||
func (s *OrganizationsService) BlockUser(ctx context.Context, org string, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) | |||
req, err := s.client.NewRequest("PUT", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeBlockUsersPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// UnblockUser unblocks specified user from an organization. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#unblock-a-user | |||
func (s *OrganizationsService) UnblockUser(ctx context.Context, org string, user string) (*Response, error) { | |||
u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeBlockUsersPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,594 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ProjectsService provides access to the projects functions in the | |||
// GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/ | |||
type ProjectsService service | |||
// Project represents a GitHub Project. | |||
type Project struct { | |||
ID *int64 `json:"id,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
ColumnsURL *string `json:"columns_url,omitempty"` | |||
OwnerURL *string `json:"owner_url,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
Number *int `json:"number,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
// The User object that generated the project. | |||
Creator *User `json:"creator,omitempty"` | |||
} | |||
func (p Project) String() string { | |||
return Stringify(p) | |||
} | |||
// GetProject gets a GitHub Project for a repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project | |||
func (s *ProjectsService) GetProject(ctx context.Context, id int64) (*Project, *Response, error) { | |||
u := fmt.Sprintf("projects/%v", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
project := &Project{} | |||
resp, err := s.client.Do(ctx, req, project) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return project, resp, nil | |||
} | |||
// ProjectOptions specifies the parameters to the | |||
// RepositoriesService.CreateProject and | |||
// ProjectsService.UpdateProject methods. | |||
type ProjectOptions struct { | |||
// The name of the project. (Required for creation; optional for update.) | |||
Name *string `json:"name,omitempty"` | |||
// The body of the project. (Optional.) | |||
Body *string `json:"body,omitempty"` | |||
// The following field(s) are only applicable for update. | |||
// They should be left with zero values for creation. | |||
// State of the project. Either "open" or "closed". (Optional.) | |||
State *string `json:"state,omitempty"` | |||
// The permission level that all members of the project's organization | |||
// will have on this project. | |||
// Setting the organization permission is only available | |||
// for organization projects. (Optional.) | |||
OrganizationPermission *string `json:"organization_permission,omitempty"` | |||
// Sets visibility of the project within the organization. | |||
// Setting visibility is only available | |||
// for organization projects.(Optional.) | |||
Public *bool `json:"public,omitempty"` | |||
} | |||
// UpdateProject updates a repository project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project | |||
func (s *ProjectsService) UpdateProject(ctx context.Context, id int64, opt *ProjectOptions) (*Project, *Response, error) { | |||
u := fmt.Sprintf("projects/%v", id) | |||
req, err := s.client.NewRequest("PATCH", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
project := &Project{} | |||
resp, err := s.client.Do(ctx, req, project) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return project, resp, nil | |||
} | |||
// DeleteProject deletes a GitHub Project from a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project | |||
func (s *ProjectsService) DeleteProject(ctx context.Context, id int64) (*Response, error) { | |||
u := fmt.Sprintf("projects/%v", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ProjectColumn represents a column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/projects/ | |||
type ProjectColumn struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
ProjectURL *string `json:"project_url,omitempty"` | |||
CardsURL *string `json:"cards_url,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
// ListProjectColumns lists the columns of a GitHub Project for a repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns | |||
func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int64, opt *ListOptions) ([]*ProjectColumn, *Response, error) { | |||
u := fmt.Sprintf("projects/%v/columns", projectID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
columns := []*ProjectColumn{} | |||
resp, err := s.client.Do(ctx, req, &columns) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return columns, resp, nil | |||
} | |||
// GetProjectColumn gets a column of a GitHub Project for a repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column | |||
func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int64) (*ProjectColumn, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v", id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
column := &ProjectColumn{} | |||
resp, err := s.client.Do(ctx, req, column) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return column, resp, nil | |||
} | |||
// ProjectColumnOptions specifies the parameters to the | |||
// ProjectsService.CreateProjectColumn and | |||
// ProjectsService.UpdateProjectColumn methods. | |||
type ProjectColumnOptions struct { | |||
// The name of the project column. (Required for creation and update.) | |||
Name string `json:"name"` | |||
} | |||
// CreateProjectColumn creates a column for the specified (by number) project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column | |||
func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { | |||
u := fmt.Sprintf("projects/%v/columns", projectID) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
column := &ProjectColumn{} | |||
resp, err := s.client.Do(ctx, req, column) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return column, resp, nil | |||
} | |||
// UpdateProjectColumn updates a column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column | |||
func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v", columnID) | |||
req, err := s.client.NewRequest("PATCH", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
column := &ProjectColumn{} | |||
resp, err := s.client.Do(ctx, req, column) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return column, resp, nil | |||
} | |||
// DeleteProjectColumn deletes a column from a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column | |||
func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int64) (*Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v", columnID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ProjectColumnMoveOptions specifies the parameters to the | |||
// ProjectsService.MoveProjectColumn method. | |||
type ProjectColumnMoveOptions struct { | |||
// Position can be one of "first", "last", or "after:<column-id>", where | |||
// <column-id> is the ID of a column in the same project. (Required.) | |||
Position string `json:"position"` | |||
} | |||
// MoveProjectColumn moves a column within a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column | |||
func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnMoveOptions) (*Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v/moves", columnID) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ProjectCard represents a card in a column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card | |||
type ProjectCard struct { | |||
URL *string `json:"url,omitempty"` | |||
ColumnURL *string `json:"column_url,omitempty"` | |||
ContentURL *string `json:"content_url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
Note *string `json:"note,omitempty"` | |||
Creator *User `json:"creator,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
Archived *bool `json:"archived,omitempty"` | |||
// The following fields are only populated by Webhook events. | |||
ColumnID *int64 `json:"column_id,omitempty"` | |||
// The following fields are only populated by Events API. | |||
ProjectID *int64 `json:"project_id,omitempty"` | |||
ProjectURL *string `json:"project_url,omitempty"` | |||
ColumnName *string `json:"column_name,omitempty"` | |||
PreviousColumnName *string `json:"previous_column_name,omitempty"` // Populated in "moved_columns_in_project" event deliveries. | |||
} | |||
// ProjectCardListOptions specifies the optional parameters to the | |||
// ProjectsService.ListProjectCards method. | |||
type ProjectCardListOptions struct { | |||
// ArchivedState is used to list all, archived, or not_archived project cards. | |||
// Defaults to not_archived when you omit this parameter. | |||
ArchivedState *string `url:"archived_state,omitempty"` | |||
ListOptions | |||
} | |||
// ListProjectCards lists the cards in a column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards | |||
func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int64, opt *ProjectCardListOptions) ([]*ProjectCard, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v/cards", columnID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
cards := []*ProjectCard{} | |||
resp, err := s.client.Do(ctx, req, &cards) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return cards, resp, nil | |||
} | |||
// GetProjectCard gets a card in a column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card | |||
func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int64) (*ProjectCard, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/cards/%v", columnID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
card := &ProjectCard{} | |||
resp, err := s.client.Do(ctx, req, card) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return card, resp, nil | |||
} | |||
// ProjectCardOptions specifies the parameters to the | |||
// ProjectsService.CreateProjectCard and | |||
// ProjectsService.UpdateProjectCard methods. | |||
type ProjectCardOptions struct { | |||
// The note of the card. Note and ContentID are mutually exclusive. | |||
Note string `json:"note,omitempty"` | |||
// The ID (not Number) of the Issue to associate with this card. | |||
// Note and ContentID are mutually exclusive. | |||
ContentID int64 `json:"content_id,omitempty"` | |||
// The type of content to associate with this card. Possible values are: "Issue" and "PullRequest". | |||
ContentType string `json:"content_type,omitempty"` | |||
// Use true to archive a project card. | |||
// Specify false if you need to restore a previously archived project card. | |||
Archived *bool `json:"archived,omitempty"` | |||
} | |||
// CreateProjectCard creates a card in the specified column of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card | |||
func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/%v/cards", columnID) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
card := &ProjectCard{} | |||
resp, err := s.client.Do(ctx, req, card) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return card, resp, nil | |||
} | |||
// UpdateProjectCard updates a card of a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card | |||
func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { | |||
u := fmt.Sprintf("projects/columns/cards/%v", cardID) | |||
req, err := s.client.NewRequest("PATCH", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
card := &ProjectCard{} | |||
resp, err := s.client.Do(ctx, req, card) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return card, resp, nil | |||
} | |||
// DeleteProjectCard deletes a card from a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card | |||
func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int64) (*Response, error) { | |||
u := fmt.Sprintf("projects/columns/cards/%v", cardID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ProjectCardMoveOptions specifies the parameters to the | |||
// ProjectsService.MoveProjectCard method. | |||
type ProjectCardMoveOptions struct { | |||
// Position can be one of "top", "bottom", or "after:<card-id>", where | |||
// <card-id> is the ID of a card in the same project. | |||
Position string `json:"position"` | |||
// ColumnID is the ID of a column in the same project. Note that ColumnID | |||
// is required when using Position "after:<card-id>" when that card is in | |||
// another column; otherwise it is optional. | |||
ColumnID int64 `json:"column_id,omitempty"` | |||
} | |||
// MoveProjectCard moves a card within a GitHub Project. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card | |||
func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int64, opt *ProjectCardMoveOptions) (*Response, error) { | |||
u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID) | |||
req, err := s.client.NewRequest("POST", u, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ProjectCollaboratorOptions specifies the optional parameters to the | |||
// ProjectsService.AddProjectCollaborator method. | |||
type ProjectCollaboratorOptions struct { | |||
// Permission specifies the permission to grant to the collaborator. | |||
// Possible values are: | |||
// "read" - can read, but not write to or administer this project. | |||
// "write" - can read and write, but not administer this project. | |||
// "admin" - can read, write and administer this project. | |||
// | |||
// Default value is "write" | |||
Permission *string `json:"permission,omitempty"` | |||
} | |||
// AddProjectCollaborator adds a collaborator to an organization project and sets | |||
// their permission level. You must be an organization owner or a project admin to add a collaborator. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#add-user-as-a-collaborator | |||
func (s *ProjectsService) AddProjectCollaborator(ctx context.Context, id int64, username string, opt *ProjectCollaboratorOptions) (*Response, error) { | |||
u := fmt.Sprintf("projects/%v/collaborators/%v", id, username) | |||
req, err := s.client.NewRequest("PUT", u, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// RemoveProjectCollaborator removes a collaborator from an organization project. | |||
// You must be an organization owner or a project admin to remove a collaborator. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#remove-user-as-a-collaborator | |||
func (s *ProjectsService) RemoveProjectCollaborator(ctx context.Context, id int64, username string) (*Response, error) { | |||
u := fmt.Sprintf("projects/%v/collaborators/%v", id, username) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// ListCollaboratorOptions specifies the optional parameters to the | |||
// ProjectsService.ListProjectCollaborators method. | |||
type ListCollaboratorOptions struct { | |||
// Affiliation specifies how collaborators should be filtered by their affiliation. | |||
// Possible values are: | |||
// "outside" - All outside collaborators of an organization-owned repository | |||
// "direct" - All collaborators with permissions to an organization-owned repository, | |||
// regardless of organization membership status | |||
// "all" - All collaborators the authenticated user can see | |||
// | |||
// Default value is "all". | |||
Affiliation *string `url:"affiliation,omitempty"` | |||
ListOptions | |||
} | |||
// ListProjectCollaborators lists the collaborators for an organization project. For a project, | |||
// the list of collaborators includes outside collaborators, organization members that are direct | |||
// collaborators, organization members with access through team memberships, organization members | |||
// with access through default organization permissions, and organization owners. You must be an | |||
// organization owner or a project admin to list collaborators. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#list-collaborators | |||
func (s *ProjectsService) ListProjectCollaborators(ctx context.Context, id int64, opt *ListCollaboratorOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("projects/%v/collaborators", id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
var users []*User | |||
resp, err := s.client.Do(ctx, req, &users) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return users, resp, nil | |||
} | |||
// ProjectPermissionLevel represents the permission level an organization | |||
// member has for a given project. | |||
type ProjectPermissionLevel struct { | |||
// Possible values: "admin", "write", "read", "none" | |||
Permission *string `json:"permission,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
} | |||
// ReviewProjectCollaboratorPermission returns the collaborator's permission level for an organization | |||
// project. Possible values for the permission key: "admin", "write", "read", "none". | |||
// You must be an organization owner or a project admin to review a user's permission level. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/projects/collaborators/#review-a-users-permission-level | |||
func (s *ProjectsService) ReviewProjectCollaboratorPermission(ctx context.Context, id int64, username string) (*ProjectPermissionLevel, *Response, error) { | |||
u := fmt.Sprintf("projects/%v/collaborators/%v/permission", id, username) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeProjectsPreview) | |||
ppl := new(ProjectPermissionLevel) | |||
resp, err := s.client.Do(ctx, req, ppl) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return ppl, resp, nil | |||
} |
@ -0,0 +1,404 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"bytes" | |||
"context" | |||
"fmt" | |||
"strings" | |||
"time" | |||
) | |||
// PullRequestsService handles communication with the pull request related | |||
// methods of the GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/ | |||
type PullRequestsService service | |||
// PullRequest represents a GitHub pull request on a repository. | |||
type PullRequest struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Number *int `json:"number,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
Title *string `json:"title,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
ClosedAt *time.Time `json:"closed_at,omitempty"` | |||
MergedAt *time.Time `json:"merged_at,omitempty"` | |||
Labels []*Label `json:"labels,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Draft *bool `json:"draft,omitempty"` | |||
Merged *bool `json:"merged,omitempty"` | |||
Mergeable *bool `json:"mergeable,omitempty"` | |||
MergeableState *string `json:"mergeable_state,omitempty"` | |||
MergedBy *User `json:"merged_by,omitempty"` | |||
MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` | |||
Comments *int `json:"comments,omitempty"` | |||
Commits *int `json:"commits,omitempty"` | |||
Additions *int `json:"additions,omitempty"` | |||
Deletions *int `json:"deletions,omitempty"` | |||
ChangedFiles *int `json:"changed_files,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
IssueURL *string `json:"issue_url,omitempty"` | |||
StatusesURL *string `json:"statuses_url,omitempty"` | |||
DiffURL *string `json:"diff_url,omitempty"` | |||
PatchURL *string `json:"patch_url,omitempty"` | |||
CommitsURL *string `json:"commits_url,omitempty"` | |||
CommentsURL *string `json:"comments_url,omitempty"` | |||
ReviewCommentsURL *string `json:"review_comments_url,omitempty"` | |||
ReviewCommentURL *string `json:"review_comment_url,omitempty"` | |||
ReviewComments *int `json:"review_comments,omitempty"` | |||
Assignee *User `json:"assignee,omitempty"` | |||
Assignees []*User `json:"assignees,omitempty"` | |||
Milestone *Milestone `json:"milestone,omitempty"` | |||
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` | |||
AuthorAssociation *string `json:"author_association,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
RequestedReviewers []*User `json:"requested_reviewers,omitempty"` | |||
// RequestedTeams is populated as part of the PullRequestEvent. | |||
// See, https://developer.github.com/v3/activity/events/types/#pullrequestevent for an example. | |||
RequestedTeams []*Team `json:"requested_teams,omitempty"` | |||
Links *PRLinks `json:"_links,omitempty"` | |||
Head *PullRequestBranch `json:"head,omitempty"` | |||
Base *PullRequestBranch `json:"base,omitempty"` | |||
// ActiveLockReason is populated only when LockReason is provided while locking the pull request. | |||
// Possible values are: "off-topic", "too heated", "resolved", and "spam". | |||
ActiveLockReason *string `json:"active_lock_reason,omitempty"` | |||
} | |||
func (p PullRequest) String() string { | |||
return Stringify(p) | |||
} | |||
// PRLink represents a single link object from Github pull request _links. | |||
type PRLink struct { | |||
HRef *string `json:"href,omitempty"` | |||
} | |||
// PRLinks represents the "_links" object in a Github pull request. | |||
type PRLinks struct { | |||
Self *PRLink `json:"self,omitempty"` | |||
HTML *PRLink `json:"html,omitempty"` | |||
Issue *PRLink `json:"issue,omitempty"` | |||
Comments *PRLink `json:"comments,omitempty"` | |||
ReviewComments *PRLink `json:"review_comments,omitempty"` | |||
ReviewComment *PRLink `json:"review_comment,omitempty"` | |||
Commits *PRLink `json:"commits,omitempty"` | |||
Statuses *PRLink `json:"statuses,omitempty"` | |||
} | |||
// PullRequestBranch represents a base or head branch in a GitHub pull request. | |||
type PullRequestBranch struct { | |||
Label *string `json:"label,omitempty"` | |||
Ref *string `json:"ref,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
Repo *Repository `json:"repo,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
} | |||
// PullRequestListOptions specifies the optional parameters to the | |||
// PullRequestsService.List method. | |||
type PullRequestListOptions struct { | |||
// State filters pull requests based on their state. Possible values are: | |||
// open, closed, all. Default is "open". | |||
State string `url:"state,omitempty"` | |||
// Head filters pull requests by head user and branch name in the format of: | |||
// "user:ref-name". | |||
Head string `url:"head,omitempty"` | |||
// Base filters pull requests by base branch name. | |||
Base string `url:"base,omitempty"` | |||
// Sort specifies how to sort pull requests. Possible values are: created, | |||
// updated, popularity, long-running. Default is "created". | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort pull requests. Possible values are: asc, desc. | |||
// If Sort is "created" or not specified, Default is "desc", otherwise Default | |||
// is "asc" | |||
Direction string `url:"direction,omitempty"` | |||
ListOptions | |||
} | |||
// List the pull requests for the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests | |||
func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
var pulls []*PullRequest | |||
resp, err := s.client.Do(ctx, req, &pulls) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return pulls, resp, nil | |||
} | |||
// Get a single pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request | |||
func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview, mediaTypeDraftPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
pull := new(PullRequest) | |||
resp, err := s.client.Do(ctx, req, pull) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return pull, resp, nil | |||
} | |||
// GetRaw gets a single pull request in raw (diff or patch) format. | |||
func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
switch opt.Type { | |||
case Diff: | |||
req.Header.Set("Accept", mediaTypeV3Diff) | |||
case Patch: | |||
req.Header.Set("Accept", mediaTypeV3Patch) | |||
default: | |||
return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) | |||
} | |||
var buf bytes.Buffer | |||
resp, err := s.client.Do(ctx, req, &buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// NewPullRequest represents a new pull request to be created. | |||
type NewPullRequest struct { | |||
Title *string `json:"title,omitempty"` | |||
Head *string `json:"head,omitempty"` | |||
Base *string `json:"base,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
Issue *int `json:"issue,omitempty"` | |||
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` | |||
} | |||
// Create a new pull request on the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request | |||
func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, pull) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeLabelDescriptionSearchPreview) | |||
p := new(PullRequest) | |||
resp, err := s.client.Do(ctx, req, p) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return p, resp, nil | |||
} | |||
type pullRequestUpdate struct { | |||
Title *string `json:"title,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
Base *string `json:"base,omitempty"` | |||
MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` | |||
} | |||
// Edit a pull request. | |||
// pull must not be nil. | |||
// | |||
// The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify. | |||
// Base.Ref updates the base branch of the pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request | |||
func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) { | |||
if pull == nil { | |||
return nil, nil, fmt.Errorf("pull must be provided") | |||
} | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) | |||
update := &pullRequestUpdate{ | |||
Title: pull.Title, | |||
Body: pull.Body, | |||
State: pull.State, | |||
MaintainerCanModify: pull.MaintainerCanModify, | |||
} | |||
if pull.Base != nil { | |||
update.Base = pull.Base.Ref | |||
} | |||
req, err := s.client.NewRequest("PATCH", u, update) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
acceptHeaders := []string{mediaTypeLabelDescriptionSearchPreview, mediaTypeLockReasonPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
p := new(PullRequest) | |||
resp, err := s.client.Do(ctx, req, p) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return p, resp, nil | |||
} | |||
// ListCommits lists the commits in a pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request | |||
func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var commits []*RepositoryCommit | |||
resp, err := s.client.Do(ctx, req, &commits) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return commits, resp, nil | |||
} | |||
// ListFiles lists the files in a pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files | |||
func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var commitFiles []*CommitFile | |||
resp, err := s.client.Do(ctx, req, &commitFiles) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return commitFiles, resp, nil | |||
} | |||
// IsMerged checks if a pull request has been merged. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged | |||
func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
merged, err := parseBoolResponse(err) | |||
return merged, resp, err | |||
} | |||
// PullRequestMergeResult represents the result of merging a pull request. | |||
type PullRequestMergeResult struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Merged *bool `json:"merged,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
} | |||
// PullRequestOptions lets you define how a pull request will be merged. | |||
type PullRequestOptions struct { | |||
CommitTitle string // Extra detail to append to automatic commit message. (Optional.) | |||
SHA string // SHA that pull request head must match to allow merge. (Optional.) | |||
// The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.) | |||
MergeMethod string | |||
} | |||
type pullRequestMergeRequest struct { | |||
CommitMessage string `json:"commit_message"` | |||
CommitTitle string `json:"commit_title,omitempty"` | |||
MergeMethod string `json:"merge_method,omitempty"` | |||
SHA string `json:"sha,omitempty"` | |||
} | |||
// Merge a pull request (Merge Button™). | |||
// commitMessage is the title for the automatic commit message. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade | |||
func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) | |||
pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage} | |||
if options != nil { | |||
pullRequestBody.CommitTitle = options.CommitTitle | |||
pullRequestBody.MergeMethod = options.MergeMethod | |||
pullRequestBody.SHA = options.SHA | |||
} | |||
req, err := s.client.NewRequest("PUT", u, pullRequestBody) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
mergeResult := new(PullRequestMergeResult) | |||
resp, err := s.client.Do(ctx, req, mergeResult) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return mergeResult, resp, nil | |||
} |
@ -0,0 +1,188 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// PullRequestComment represents a comment left on a pull request. | |||
type PullRequestComment struct { | |||
ID *int64 `json:"id,omitempty"` | |||
InReplyTo *int64 `json:"in_reply_to_id,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
Path *string `json:"path,omitempty"` | |||
DiffHunk *string `json:"diff_hunk,omitempty"` | |||
PullRequestReviewID *int64 `json:"pull_request_review_id,omitempty"` | |||
Position *int `json:"position,omitempty"` | |||
OriginalPosition *int `json:"original_position,omitempty"` | |||
CommitID *string `json:"commit_id,omitempty"` | |||
OriginalCommitID *string `json:"original_commit_id,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Reactions *Reactions `json:"reactions,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
// AuthorAssociation is the comment author's relationship to the pull request's repository. | |||
// Possible values are "COLLABORATOR", "CONTRIBUTOR", "FIRST_TIMER", "FIRST_TIME_CONTRIBUTOR", "MEMBER", "OWNER", or "NONE". | |||
AuthorAssociation *string `json:"author_association,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
PullRequestURL *string `json:"pull_request_url,omitempty"` | |||
} | |||
func (p PullRequestComment) String() string { | |||
return Stringify(p) | |||
} | |||
// PullRequestListCommentsOptions specifies the optional parameters to the | |||
// PullRequestsService.ListComments method. | |||
type PullRequestListCommentsOptions struct { | |||
// Sort specifies how to sort comments. Possible values are: created, updated. | |||
Sort string `url:"sort,omitempty"` | |||
// Direction in which to sort comments. Possible values are: asc, desc. | |||
Direction string `url:"direction,omitempty"` | |||
// Since filters comments by time. | |||
Since time.Time `url:"since,omitempty"` | |||
ListOptions | |||
} | |||
// ListComments lists all comments on the specified pull request. Specifying a | |||
// pull request number of 0 will return all comments on all pull requests for | |||
// the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request | |||
func (s *PullRequestsService) ListComments(ctx context.Context, owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) { | |||
var u string | |||
if number == 0 { | |||
u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo) | |||
} else { | |||
u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) | |||
} | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var comments []*PullRequestComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// GetComment fetches the specified pull request comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment | |||
func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, commentID int64) (*PullRequestComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
comment := new(PullRequestComment) | |||
resp, err := s.client.Do(ctx, req, comment) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comment, resp, nil | |||
} | |||
// CreateComment creates a new comment on the specified pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment | |||
func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(PullRequestComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// CreateCommentInReplyTo creates a new comment as a reply to an existing pull request comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#alternative-input | |||
func (s *PullRequestsService) CreateCommentInReplyTo(ctx context.Context, owner string, repo string, number int, body string, commentID int64) (*PullRequestComment, *Response, error) { | |||
comment := &struct { | |||
Body string `json:"body,omitempty"` | |||
InReplyTo int64 `json:"in_reply_to,omitempty"` | |||
}{ | |||
Body: body, | |||
InReplyTo: commentID, | |||
} | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(PullRequestComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// EditComment updates a pull request comment. | |||
// A non-nil comment.Body must be provided. Other comment fields should be left nil. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment | |||
func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, commentID int64, comment *PullRequestComment) (*PullRequestComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("PATCH", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(PullRequestComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// DeleteComment deletes a pull request comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment | |||
func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, commentID int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, commentID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,79 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ReviewersRequest specifies users and teams for a pull request review request. | |||
type ReviewersRequest struct { | |||
Reviewers []string `json:"reviewers,omitempty"` | |||
TeamReviewers []string `json:"team_reviewers,omitempty"` | |||
} | |||
// Reviewers represents reviewers of a pull request. | |||
type Reviewers struct { | |||
Users []*User `json:"users,omitempty"` | |||
Teams []*Team `json:"teams,omitempty"` | |||
} | |||
// RequestReviewers creates a review request for the provided reviewers for the specified pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request | |||
func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*PullRequest, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, &reviewers) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(PullRequest) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// ListReviewers lists reviewers whose reviews have been requested on the specified pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#list-review-requests | |||
func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int, opt *ListOptions) (*Reviewers, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/requested_reviewers", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
reviewers := new(Reviewers) | |||
resp, err := s.client.Do(ctx, req, reviewers) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return reviewers, resp, nil | |||
} | |||
// RemoveReviewers removes the review request for the provided reviewers for the specified pull request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request | |||
func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) | |||
req, err := s.client.NewRequest("DELETE", u, &reviewers) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,236 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// PullRequestReview represents a review of a pull request. | |||
type PullRequestReview struct { | |||
ID *int64 `json:"id,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
SubmittedAt *time.Time `json:"submitted_at,omitempty"` | |||
CommitID *string `json:"commit_id,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
PullRequestURL *string `json:"pull_request_url,omitempty"` | |||
State *string `json:"state,omitempty"` | |||
} | |||
func (p PullRequestReview) String() string { | |||
return Stringify(p) | |||
} | |||
// DraftReviewComment represents a comment part of the review. | |||
type DraftReviewComment struct { | |||
Path *string `json:"path,omitempty"` | |||
Position *int `json:"position,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
} | |||
func (c DraftReviewComment) String() string { | |||
return Stringify(c) | |||
} | |||
// PullRequestReviewRequest represents a request to create a review. | |||
type PullRequestReviewRequest struct { | |||
CommitID *string `json:"commit_id,omitempty"` | |||
Body *string `json:"body,omitempty"` | |||
Event *string `json:"event,omitempty"` | |||
Comments []*DraftReviewComment `json:"comments,omitempty"` | |||
} | |||
func (r PullRequestReviewRequest) String() string { | |||
return Stringify(r) | |||
} | |||
// PullRequestReviewDismissalRequest represents a request to dismiss a review. | |||
type PullRequestReviewDismissalRequest struct { | |||
Message *string `json:"message,omitempty"` | |||
} | |||
func (r PullRequestReviewDismissalRequest) String() string { | |||
return Stringify(r) | |||
} | |||
// ListReviews lists all reviews on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request | |||
func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var reviews []*PullRequestReview | |||
resp, err := s.client.Do(ctx, req, &reviews) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return reviews, resp, nil | |||
} | |||
// GetReview fetches the specified pull request review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-a-single-review | |||
func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
review := new(PullRequestReview) | |||
resp, err := s.client.Do(ctx, req, review) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return review, resp, nil | |||
} | |||
// DeletePendingReview deletes the specified pull request pending review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review | |||
func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number int, reviewID int64) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
review := new(PullRequestReview) | |||
resp, err := s.client.Do(ctx, req, review) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return review, resp, nil | |||
} | |||
// ListReviewComments lists all the comments for the specified review. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review | |||
func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number int, reviewID int64, opt *ListOptions) ([]*PullRequestComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var comments []*PullRequestComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// CreateReview creates a new review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review | |||
func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) | |||
req, err := s.client.NewRequest("POST", u, review) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(PullRequestReview) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// SubmitReview submits a specified review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review | |||
func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) | |||
req, err := s.client.NewRequest("POST", u, review) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(PullRequestReview) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} | |||
// DismissReview dismisses a specified review on the specified pull request. | |||
// | |||
// TODO: Follow up with GitHub support about an issue with this method's | |||
// returned error format and remove this comment once it's fixed. | |||
// Read more about it here - https://github.com/google/go-github/issues/540 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review | |||
func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number int, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) | |||
req, err := s.client.NewRequest("PUT", u, review) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
r := new(PullRequestReview) | |||
resp, err := s.client.Do(ctx, req, r) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return r, resp, nil | |||
} |
@ -0,0 +1,377 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ReactionsService provides access to the reactions-related functions in the | |||
// GitHub API. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/ | |||
type ReactionsService service | |||
// Reaction represents a GitHub reaction. | |||
type Reaction struct { | |||
// ID is the Reaction ID. | |||
ID *int64 `json:"id,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
// Content is the type of reaction. | |||
// Possible values are: | |||
// "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
Content *string `json:"content,omitempty"` | |||
} | |||
// Reactions represents a summary of GitHub reactions. | |||
type Reactions struct { | |||
TotalCount *int `json:"total_count,omitempty"` | |||
PlusOne *int `json:"+1,omitempty"` | |||
MinusOne *int `json:"-1,omitempty"` | |||
Laugh *int `json:"laugh,omitempty"` | |||
Confused *int `json:"confused,omitempty"` | |||
Heart *int `json:"heart,omitempty"` | |||
Hooray *int `json:"hooray,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
} | |||
func (r Reaction) String() string { | |||
return Stringify(r) | |||
} | |||
// ListCommentReactions lists the reactions for a commit comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment | |||
func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreateCommentReaction creates a reaction for a commit comment. | |||
// Note that if you have already created a reaction of type content, the | |||
// previously created reaction will be returned with Status: 200 OK. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment | |||
func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListIssueReactions lists the reactions for an issue. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue | |||
func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreateIssueReaction creates a reaction for an issue. | |||
// Note that if you have already created a reaction of type content, the | |||
// previously created reaction will be returned with Status: 200 OK. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue | |||
func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListIssueCommentReactions lists the reactions for an issue comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment | |||
func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreateIssueCommentReaction creates a reaction for an issue comment. | |||
// Note that if you have already created a reaction of type content, the | |||
// previously created reaction will be returned with Status: 200 OK. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment | |||
func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListPullRequestCommentReactions lists the reactions for a pull request review comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment | |||
func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreatePullRequestCommentReaction creates a reaction for a pull request review comment. | |||
// Note that if you have already created a reaction of type content, the | |||
// previously created reaction will be returned with Status: 200 OK. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment | |||
func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListTeamDiscussionReactions lists the reactions for a team discussion. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion | |||
func (s *ReactionsService) ListTeamDiscussionReactions(ctx context.Context, teamID int64, discussionNumber int, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreateTeamDiscussionReaction creates a reaction for a team discussion. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion | |||
func (s *ReactionsService) CreateTeamDiscussionReaction(ctx context.Context, teamID int64, discussionNumber int, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("teams/%v/discussions/%v/reactions", teamID, discussionNumber) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// ListTeamDiscussionCommentReactions lists the reactions for a team discussion comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-team-discussion-comment | |||
func (s *ReactionsService) ListTeamDiscussionCommentReactions(ctx context.Context, teamID int64, discussionNumber, commentNumber int, opt *ListOptions) ([]*Reaction, *Response, error) { | |||
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var m []*Reaction | |||
resp, err := s.client.Do(ctx, req, &m) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
return m, resp, nil | |||
} | |||
// CreateTeamDiscussionCommentReaction creates a reaction for a team discussion comment. | |||
// The content should have one of the following values: "+1", "-1", "laugh", "confused", "heart", "hooray". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-team-discussion-comment | |||
func (s *ReactionsService) CreateTeamDiscussionCommentReaction(ctx context.Context, teamID int64, discussionNumber, commentNumber int, content string) (*Reaction, *Response, error) { | |||
u := fmt.Sprintf("teams/%v/discussions/%v/comments/%v/reactions", teamID, discussionNumber, commentNumber) | |||
body := &Reaction{Content: String(content)} | |||
req, err := s.client.NewRequest("POST", u, body) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
m := &Reaction{} | |||
resp, err := s.client.Do(ctx, req, m) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return m, resp, nil | |||
} | |||
// DeleteReaction deletes a reaction. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive | |||
func (s *ReactionsService) DeleteReaction(ctx context.Context, id int64) (*Response, error) { | |||
u := fmt.Sprintf("reactions/%v", id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,137 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// ListCollaboratorsOptions specifies the optional parameters to the | |||
// RepositoriesService.ListCollaborators method. | |||
type ListCollaboratorsOptions struct { | |||
// Affiliation specifies how collaborators should be filtered by their affiliation. | |||
// Possible values are: | |||
// outside - All outside collaborators of an organization-owned repository | |||
// direct - All collaborators with permissions to an organization-owned repository, | |||
// regardless of organization membership status | |||
// all - All collaborators the authenticated user can see | |||
// | |||
// Default value is "all". | |||
Affiliation string `url:"affiliation,omitempty"` | |||
ListOptions | |||
} | |||
// ListCollaborators lists the GitHub users that have access to the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list-collaborators | |||
func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opt *ListCollaboratorsOptions) ([]*User, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req.Header.Set("Accept", mediaTypeNestedTeamsPreview) | |||
var users []*User | |||
resp, err := s.client.Do(ctx, req, &users) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return users, resp, nil | |||
} | |||
// IsCollaborator checks whether the specified GitHub user has collaborator | |||
// access to the given repo. | |||
// Note: This will return false if the user is not a collaborator OR the user | |||
// is not a GitHub user. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#get | |||
func (s *RepositoriesService) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return false, nil, err | |||
} | |||
resp, err := s.client.Do(ctx, req, nil) | |||
isCollab, err := parseBoolResponse(err) | |||
return isCollab, resp, err | |||
} | |||
// RepositoryPermissionLevel represents the permission level an organization | |||
// member has for a given repository. | |||
type RepositoryPermissionLevel struct { | |||
// Possible values: "admin", "write", "read", "none" | |||
Permission *string `json:"permission,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
} | |||
// GetPermissionLevel retrieves the specific permission level a collaborator has for a given repository. | |||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level | |||
func (s *RepositoriesService) GetPermissionLevel(ctx context.Context, owner, repo, user string) (*RepositoryPermissionLevel, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v/permission", owner, repo, user) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
rpl := new(RepositoryPermissionLevel) | |||
resp, err := s.client.Do(ctx, req, rpl) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return rpl, resp, nil | |||
} | |||
// RepositoryAddCollaboratorOptions specifies the optional parameters to the | |||
// RepositoriesService.AddCollaborator method. | |||
type RepositoryAddCollaboratorOptions struct { | |||
// Permission specifies the permission to grant the user on this repository. | |||
// Possible values are: | |||
// pull - team members can pull, but not push to or administer this repository | |||
// push - team members can pull and push, but not administer this repository | |||
// admin - team members can pull, push and administer this repository | |||
// | |||
// Default value is "push". This option is only valid for organization-owned repositories. | |||
Permission string `json:"permission,omitempty"` | |||
} | |||
// AddCollaborator sends an invitation to the specified GitHub user | |||
// to become a collaborator to the given repo. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator | |||
func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) | |||
req, err := s.client.NewRequest("PUT", u, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// RemoveCollaborator removes the specified GitHub user as collaborator from the given repo. | |||
// Note: Does not return error if a valid user that is not a collaborator is removed. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#remove-collaborator | |||
func (s *RepositoriesService) RemoveCollaborator(ctx context.Context, owner, repo, user string) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,161 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// RepositoryComment represents a comment for a commit, file, or line in a repository. | |||
type RepositoryComment struct { | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
CommitID *string `json:"commit_id,omitempty"` | |||
User *User `json:"user,omitempty"` | |||
Reactions *Reactions `json:"reactions,omitempty"` | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
// User-mutable fields | |||
Body *string `json:"body"` | |||
// User-initialized fields | |||
Path *string `json:"path,omitempty"` | |||
Position *int `json:"position,omitempty"` | |||
} | |||
func (r RepositoryComment) String() string { | |||
return Stringify(r) | |||
} | |||
// ListComments lists all the comments for the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository | |||
func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var comments []*RepositoryComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// ListCommitComments lists all the comments for a given commit SHA. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit | |||
func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
var comments []*RepositoryComment | |||
resp, err := s.client.Do(ctx, req, &comments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comments, resp, nil | |||
} | |||
// CreateComment creates a comment for the given commit. | |||
// Note: GitHub allows for comments to be created for non-existing files and positions. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#create-a-commit-comment | |||
func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) | |||
req, err := s.client.NewRequest("POST", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(RepositoryComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// GetComment gets a single comment from a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment | |||
func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string, id int64) (*RepositoryComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeReactionsPreview) | |||
c := new(RepositoryComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// UpdateComment updates the body of a single comment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#update-a-commit-comment | |||
func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int64, comment *RepositoryComment) (*RepositoryComment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("PATCH", u, comment) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
c := new(RepositoryComment) | |||
resp, err := s.client.Do(ctx, req, c) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return c, resp, nil | |||
} | |||
// DeleteComment deletes a single comment from a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/comments/#delete-a-commit-comment | |||
func (s *RepositoriesService) DeleteComment(ctx context.Context, owner, repo string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,233 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"bytes" | |||
"context" | |||
"fmt" | |||
"net/url" | |||
"time" | |||
) | |||
// RepositoryCommit represents a commit in a repo. | |||
// Note that it's wrapping a Commit, so author/committer information is in two places, | |||
// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". | |||
type RepositoryCommit struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Commit *Commit `json:"commit,omitempty"` | |||
Author *User `json:"author,omitempty"` | |||
Committer *User `json:"committer,omitempty"` | |||
Parents []Commit `json:"parents,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
CommentsURL *string `json:"comments_url,omitempty"` | |||
// Details about how many changes were made in this commit. Only filled in during GetCommit! | |||
Stats *CommitStats `json:"stats,omitempty"` | |||
// Details about which files, and how this commit touched. Only filled in during GetCommit! | |||
Files []CommitFile `json:"files,omitempty"` | |||
} | |||
func (r RepositoryCommit) String() string { | |||
return Stringify(r) | |||
} | |||
// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit. | |||
type CommitStats struct { | |||
Additions *int `json:"additions,omitempty"` | |||
Deletions *int `json:"deletions,omitempty"` | |||
Total *int `json:"total,omitempty"` | |||
} | |||
func (c CommitStats) String() string { | |||
return Stringify(c) | |||
} | |||
// CommitFile represents a file modified in a commit. | |||
type CommitFile struct { | |||
SHA *string `json:"sha,omitempty"` | |||
Filename *string `json:"filename,omitempty"` | |||
Additions *int `json:"additions,omitempty"` | |||
Deletions *int `json:"deletions,omitempty"` | |||
Changes *int `json:"changes,omitempty"` | |||
Status *string `json:"status,omitempty"` | |||
Patch *string `json:"patch,omitempty"` | |||
BlobURL *string `json:"blob_url,omitempty"` | |||
RawURL *string `json:"raw_url,omitempty"` | |||
ContentsURL *string `json:"contents_url,omitempty"` | |||
PreviousFilename *string `json:"previous_filename,omitempty"` | |||
} | |||
func (c CommitFile) String() string { | |||
return Stringify(c) | |||
} | |||
// CommitsComparison is the result of comparing two commits. | |||
// See CompareCommits() for details. | |||
type CommitsComparison struct { | |||
BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` | |||
MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"` | |||
// Head can be 'behind' or 'ahead' | |||
Status *string `json:"status,omitempty"` | |||
AheadBy *int `json:"ahead_by,omitempty"` | |||
BehindBy *int `json:"behind_by,omitempty"` | |||
TotalCommits *int `json:"total_commits,omitempty"` | |||
Commits []RepositoryCommit `json:"commits,omitempty"` | |||
Files []CommitFile `json:"files,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
PermalinkURL *string `json:"permalink_url,omitempty"` | |||
DiffURL *string `json:"diff_url,omitempty"` | |||
PatchURL *string `json:"patch_url,omitempty"` | |||
URL *string `json:"url,omitempty"` // API URL. | |||
} | |||
func (c CommitsComparison) String() string { | |||
return Stringify(c) | |||
} | |||
// CommitsListOptions specifies the optional parameters to the | |||
// RepositoriesService.ListCommits method. | |||
type CommitsListOptions struct { | |||
// SHA or branch to start listing Commits from. | |||
SHA string `url:"sha,omitempty"` | |||
// Path that should be touched by the returned Commits. | |||
Path string `url:"path,omitempty"` | |||
// Author of by which to filter Commits. | |||
Author string `url:"author,omitempty"` | |||
// Since when should Commits be included in the response. | |||
Since time.Time `url:"since,omitempty"` | |||
// Until when should Commits be included in the response. | |||
Until time.Time `url:"until,omitempty"` | |||
ListOptions | |||
} | |||
// ListCommits lists the commits of a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/commits/#list | |||
func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var commits []*RepositoryCommit | |||
resp, err := s.client.Do(ctx, req, &commits) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return commits, resp, nil | |||
} | |||
// GetCommit fetches the specified commit, including all details about it. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit | |||
// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality | |||
func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
commit := new(RepositoryCommit) | |||
resp, err := s.client.Do(ctx, req, commit) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return commit, resp, nil | |||
} | |||
// GetCommitRaw fetches the specified commit in raw (diff or patch) format. | |||
func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
switch opt.Type { | |||
case Diff: | |||
req.Header.Set("Accept", mediaTypeV3Diff) | |||
case Patch: | |||
req.Header.Set("Accept", mediaTypeV3Patch) | |||
default: | |||
return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) | |||
} | |||
var buf bytes.Buffer | |||
resp, err := s.client.Do(ctx, req, &buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is | |||
// supplied and no new commits have occurred, a 304 Unmodified response is returned. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference | |||
func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, url.QueryEscape(ref)) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return "", nil, err | |||
} | |||
if lastSHA != "" { | |||
req.Header.Set("If-None-Match", `"`+lastSHA+`"`) | |||
} | |||
req.Header.Set("Accept", mediaTypeV3SHA) | |||
var buf bytes.Buffer | |||
resp, err := s.client.Do(ctx, req, &buf) | |||
if err != nil { | |||
return "", resp, err | |||
} | |||
return buf.String(), resp, nil | |||
} | |||
// CompareCommits compares a range of commits with each other. | |||
// todo: support media formats - https://github.com/google/go-github/issues/6 | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/commits/#compare-two-commits | |||
func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
comp := new(CommitsComparison) | |||
resp, err := s.client.Do(ctx, req, comp) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return comp, resp, nil | |||
} |
@ -0,0 +1,59 @@ | |||
// Copyright 2017 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// Metric represents the different fields for one file in community health files. | |||
type Metric struct { | |||
Name *string `json:"name"` | |||
Key *string `json:"key"` | |||
URL *string `json:"url"` | |||
HTMLURL *string `json:"html_url"` | |||
} | |||
// CommunityHealthFiles represents the different files in the community health metrics response. | |||
type CommunityHealthFiles struct { | |||
CodeOfConduct *Metric `json:"code_of_conduct"` | |||
Contributing *Metric `json:"contributing"` | |||
IssueTemplate *Metric `json:"issue_template"` | |||
PullRequestTemplate *Metric `json:"pull_request_template"` | |||
License *Metric `json:"license"` | |||
Readme *Metric `json:"readme"` | |||
} | |||
// CommunityHealthMetrics represents a response containing the community metrics of a repository. | |||
type CommunityHealthMetrics struct { | |||
HealthPercentage *int `json:"health_percentage"` | |||
Files *CommunityHealthFiles `json:"files"` | |||
UpdatedAt *time.Time `json:"updated_at"` | |||
} | |||
// GetCommunityHealthMetrics retrieves all the community health metrics for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/community/#retrieve-community-health-metrics | |||
func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when this API fully launches. | |||
req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) | |||
metrics := &CommunityHealthMetrics{} | |||
resp, err := s.client.Do(ctx, req, metrics) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return metrics, resp, nil | |||
} |
@ -0,0 +1,269 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
// Repository contents API methods. | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/ | |||
package github | |||
import ( | |||
"context" | |||
"encoding/base64" | |||
"encoding/json" | |||
"fmt" | |||
"io" | |||
"net/http" | |||
"net/url" | |||
"path" | |||
) | |||
// RepositoryContent represents a file or directory in a github repository. | |||
type RepositoryContent struct { | |||
Type *string `json:"type,omitempty"` | |||
// Target is only set if the type is "symlink" and the target is not a normal file. | |||
// If Target is set, Path will be the symlink path. | |||
Target *string `json:"target,omitempty"` | |||
Encoding *string `json:"encoding,omitempty"` | |||
Size *int `json:"size,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Path *string `json:"path,omitempty"` | |||
// Content contains the actual file content, which may be encoded. | |||
// Callers should call GetContent which will decode the content if | |||
// necessary. | |||
Content *string `json:"content,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
GitURL *string `json:"git_url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
DownloadURL *string `json:"download_url,omitempty"` | |||
} | |||
// RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile. | |||
type RepositoryContentResponse struct { | |||
Content *RepositoryContent `json:"content,omitempty"` | |||
Commit `json:"commit,omitempty"` | |||
} | |||
// RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile. | |||
type RepositoryContentFileOptions struct { | |||
Message *string `json:"message,omitempty"` | |||
Content []byte `json:"content,omitempty"` // unencoded | |||
SHA *string `json:"sha,omitempty"` | |||
Branch *string `json:"branch,omitempty"` | |||
Author *CommitAuthor `json:"author,omitempty"` | |||
Committer *CommitAuthor `json:"committer,omitempty"` | |||
} | |||
// RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA, | |||
// branch, or tag | |||
type RepositoryContentGetOptions struct { | |||
Ref string `url:"ref,omitempty"` | |||
} | |||
// String converts RepositoryContent to a string. It's primarily for testing. | |||
func (r RepositoryContent) String() string { | |||
return Stringify(r) | |||
} | |||
// GetContent returns the content of r, decoding it if necessary. | |||
func (r *RepositoryContent) GetContent() (string, error) { | |||
var encoding string | |||
if r.Encoding != nil { | |||
encoding = *r.Encoding | |||
} | |||
switch encoding { | |||
case "base64": | |||
c, err := base64.StdEncoding.DecodeString(*r.Content) | |||
return string(c), err | |||
case "": | |||
if r.Content == nil { | |||
return "", nil | |||
} | |||
return *r.Content, nil | |||
default: | |||
return "", fmt.Errorf("unsupported content encoding: %v", encoding) | |||
} | |||
} | |||
// GetReadme gets the Readme file for the repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-the-readme | |||
func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/readme", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
readme := new(RepositoryContent) | |||
resp, err := s.client.Do(ctx, req, readme) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return readme, resp, nil | |||
} | |||
// DownloadContents returns an io.ReadCloser that reads the contents of the | |||
// specified file. This function will work with files of any size, as opposed | |||
// to GetContents which is limited to 1 Mb files. It is the caller's | |||
// responsibility to close the ReadCloser. | |||
func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) { | |||
dir := path.Dir(filepath) | |||
filename := path.Base(filepath) | |||
_, dirContents, _, err := s.GetContents(ctx, owner, repo, dir, opt) | |||
if err != nil { | |||
return nil, err | |||
} | |||
for _, contents := range dirContents { | |||
if *contents.Name == filename { | |||
if contents.DownloadURL == nil || *contents.DownloadURL == "" { | |||
return nil, fmt.Errorf("No download link found for %s", filepath) | |||
} | |||
resp, err := s.client.client.Get(*contents.DownloadURL) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return resp.Body, nil | |||
} | |||
} | |||
return nil, fmt.Errorf("No file named %s found in %s", filename, dir) | |||
} | |||
// GetContents can return either the metadata and content of a single file | |||
// (when path references a file) or the metadata of all the files and/or | |||
// subdirectories of a directory (when path references a directory). To make it | |||
// easy to distinguish between both result types and to mimic the API as much | |||
// as possible, both result types will be returned but only one will contain a | |||
// value and the other will be nil. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-contents | |||
func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { | |||
escapedPath := (&url.URL{Path: path}).String() | |||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath) | |||
u, err = addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, nil, err | |||
} | |||
var rawJSON json.RawMessage | |||
resp, err = s.client.Do(ctx, req, &rawJSON) | |||
if err != nil { | |||
return nil, nil, resp, err | |||
} | |||
fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent) | |||
if fileUnmarshalError == nil { | |||
return fileContent, nil, resp, nil | |||
} | |||
directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent) | |||
if directoryUnmarshalError == nil { | |||
return nil, directoryContent, resp, nil | |||
} | |||
return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) | |||
} | |||
// CreateFile creates a new file in a repository at the given path and returns | |||
// the commit and file metadata. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#create-a-file | |||
func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) | |||
req, err := s.client.NewRequest("PUT", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
createResponse := new(RepositoryContentResponse) | |||
resp, err := s.client.Do(ctx, req, createResponse) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return createResponse, resp, nil | |||
} | |||
// UpdateFile updates a file in a repository at the given path and returns the | |||
// commit and file metadata. Requires the blob SHA of the file being updated. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#update-a-file | |||
func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) | |||
req, err := s.client.NewRequest("PUT", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
updateResponse := new(RepositoryContentResponse) | |||
resp, err := s.client.Do(ctx, req, updateResponse) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return updateResponse, resp, nil | |||
} | |||
// DeleteFile deletes a file from a repository and returns the commit. | |||
// Requires the blob SHA of the file to be deleted. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#delete-a-file | |||
func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) | |||
req, err := s.client.NewRequest("DELETE", u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
deleteResponse := new(RepositoryContentResponse) | |||
resp, err := s.client.Do(ctx, req, deleteResponse) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return deleteResponse, resp, nil | |||
} | |||
// archiveFormat is used to define the archive type when calling GetArchiveLink. | |||
type archiveFormat string | |||
const ( | |||
// Tarball specifies an archive in gzipped tar format. | |||
Tarball archiveFormat = "tarball" | |||
// Zipball specifies an archive in zip format. | |||
Zipball archiveFormat = "zipball" | |||
) | |||
// GetArchiveLink returns an URL to download a tarball or zipball archive for a | |||
// repository. The archiveFormat can be specified by either the github.Tarball | |||
// or github.Zipball constant. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link | |||
func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) { | |||
u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat) | |||
if opt != nil && opt.Ref != "" { | |||
u += fmt.Sprintf("/%s", opt.Ref) | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var resp *http.Response | |||
// Use http.DefaultTransport if no custom Transport is configured | |||
req = withContext(ctx, req) | |||
if s.client.client.Transport == nil { | |||
resp, err = http.DefaultTransport.RoundTrip(req) | |||
} else { | |||
resp, err = s.client.client.Transport.RoundTrip(req) | |||
} | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
resp.Body.Close() | |||
if resp.StatusCode != http.StatusFound { | |||
return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) | |||
} | |||
parsedURL, err := url.Parse(resp.Header.Get("Location")) | |||
return parsedURL, newResponse(resp), err | |||
} |
@ -0,0 +1,229 @@ | |||
// Copyright 2014 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"encoding/json" | |||
"fmt" | |||
"strings" | |||
) | |||
// Deployment represents a deployment in a repo | |||
type Deployment struct { | |||
URL *string `json:"url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
SHA *string `json:"sha,omitempty"` | |||
Ref *string `json:"ref,omitempty"` | |||
Task *string `json:"task,omitempty"` | |||
Payload json.RawMessage `json:"payload,omitempty"` | |||
Environment *string `json:"environment,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Creator *User `json:"creator,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
StatusesURL *string `json:"statuses_url,omitempty"` | |||
RepositoryURL *string `json:"repository_url,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
// DeploymentRequest represents a deployment request | |||
type DeploymentRequest struct { | |||
Ref *string `json:"ref,omitempty"` | |||
Task *string `json:"task,omitempty"` | |||
AutoMerge *bool `json:"auto_merge,omitempty"` | |||
RequiredContexts *[]string `json:"required_contexts,omitempty"` | |||
Payload *string `json:"payload,omitempty"` | |||
Environment *string `json:"environment,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
TransientEnvironment *bool `json:"transient_environment,omitempty"` | |||
ProductionEnvironment *bool `json:"production_environment,omitempty"` | |||
} | |||
// DeploymentsListOptions specifies the optional parameters to the | |||
// RepositoriesService.ListDeployments method. | |||
type DeploymentsListOptions struct { | |||
// SHA of the Deployment. | |||
SHA string `url:"sha,omitempty"` | |||
// List deployments for a given ref. | |||
Ref string `url:"ref,omitempty"` | |||
// List deployments for a given task. | |||
Task string `url:"task,omitempty"` | |||
// List deployments for a given environment. | |||
Environment string `url:"environment,omitempty"` | |||
ListOptions | |||
} | |||
// ListDeployments lists the deployments of a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments | |||
func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var deployments []*Deployment | |||
resp, err := s.client.Do(ctx, req, &deployments) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return deployments, resp, nil | |||
} | |||
// GetDeployment returns a single deployment of a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment | |||
func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Deployment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
deployment := new(Deployment) | |||
resp, err := s.client.Do(ctx, req, deployment) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return deployment, resp, nil | |||
} | |||
// CreateDeployment creates a new deployment for a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment | |||
func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) | |||
req, err := s.client.NewRequest("POST", u, request) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
d := new(Deployment) | |||
resp, err := s.client.Do(ctx, req, d) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return d, resp, nil | |||
} | |||
// DeploymentStatus represents the status of a | |||
// particular deployment. | |||
type DeploymentStatus struct { | |||
ID *int64 `json:"id,omitempty"` | |||
// State is the deployment state. | |||
// Possible values are: "pending", "success", "failure", "error", "inactive". | |||
State *string `json:"state,omitempty"` | |||
Creator *User `json:"creator,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
TargetURL *string `json:"target_url,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
UpdatedAt *Timestamp `json:"updated_at,omitempty"` | |||
DeploymentURL *string `json:"deployment_url,omitempty"` | |||
RepositoryURL *string `json:"repository_url,omitempty"` | |||
NodeID *string `json:"node_id,omitempty"` | |||
} | |||
// DeploymentStatusRequest represents a deployment request | |||
type DeploymentStatusRequest struct { | |||
State *string `json:"state,omitempty"` | |||
LogURL *string `json:"log_url,omitempty"` | |||
Description *string `json:"description,omitempty"` | |||
Environment *string `json:"environment,omitempty"` | |||
EnvironmentURL *string `json:"environment_url,omitempty"` | |||
AutoInactive *bool `json:"auto_inactive,omitempty"` | |||
} | |||
// ListDeploymentStatuses lists the statuses of a given deployment of a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses | |||
func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opt *ListOptions) ([]*DeploymentStatus, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var statuses []*DeploymentStatus | |||
resp, err := s.client.Do(ctx, req, &statuses) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return statuses, resp, nil | |||
} | |||
// GetDeploymentStatus returns a single deployment status of a repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment-status | |||
func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, repo string, deploymentID, deploymentStatusID int64) (*DeploymentStatus, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses/%v", owner, repo, deploymentID, deploymentStatusID) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
d := new(DeploymentStatus) | |||
resp, err := s.client.Do(ctx, req, d) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return d, resp, nil | |||
} | |||
// CreateDeploymentStatus creates a new status for a deployment. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status | |||
func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deployment int64, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) | |||
req, err := s.client.NewRequest("POST", u, request) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept headers when APIs fully launch. | |||
acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeExpandDeploymentStatusPreview} | |||
req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) | |||
d := new(DeploymentStatus) | |||
resp, err := s.client.Do(ctx, req, d) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return d, resp, nil | |||
} |
@ -0,0 +1,96 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"encoding/json" | |||
) | |||
// RepositoryListForksOptions specifies the optional parameters to the | |||
// RepositoriesService.ListForks method. | |||
type RepositoryListForksOptions struct { | |||
// How to sort the forks list. Possible values are: newest, oldest, | |||
// watchers. Default is "newest". | |||
Sort string `url:"sort,omitempty"` | |||
ListOptions | |||
} | |||
// ListForks lists the forks of the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/forks/#list-forks | |||
func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, opt *RepositoryListForksOptions) ([]*Repository, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
// TODO: remove custom Accept header when topics API fully launches. | |||
req.Header.Set("Accept", mediaTypeTopicsPreview) | |||
var repos []*Repository | |||
resp, err := s.client.Do(ctx, req, &repos) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return repos, resp, nil | |||
} | |||
// RepositoryCreateForkOptions specifies the optional parameters to the | |||
// RepositoriesService.CreateFork method. | |||
type RepositoryCreateForkOptions struct { | |||
// The organization to fork the repository into. | |||
Organization string `url:"organization,omitempty"` | |||
} | |||
// CreateFork creates a fork of the specified repository. | |||
// | |||
// This method might return an *AcceptedError and a status code of | |||
// 202. This is because this is the status that GitHub returns to signify that | |||
// it is now computing creating the fork in a background task. In this event, | |||
// the Repository value will be returned, which includes the details about the pending fork. | |||
// A follow up request, after a delay of a second or so, should result | |||
// in a successful request. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/forks/#create-a-fork | |||
func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opt *RepositoryCreateForkOptions) (*Repository, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
fork := new(Repository) | |||
resp, err := s.client.Do(ctx, req, fork) | |||
if err != nil { | |||
// Persist AcceptedError's metadata to the Repository object. | |||
if aerr, ok := err.(*AcceptedError); ok { | |||
if err := json.Unmarshal(aerr.Raw, fork); err != nil { | |||
return fork, resp, err | |||
} | |||
return fork, resp, err | |||
} | |||
return nil, resp, err | |||
} | |||
return fork, resp, nil | |||
} |
@ -0,0 +1,226 @@ | |||
// Copyright 2013 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
"time" | |||
) | |||
// WebHookPayload represents the data that is received from GitHub when a push | |||
// event hook is triggered. The format of these payloads pre-date most of the | |||
// GitHub v3 API, so there are lots of minor incompatibilities with the types | |||
// defined in the rest of the API. Therefore, several types are duplicated | |||
// here to account for these differences. | |||
// | |||
// GitHub API docs: https://help.github.com/articles/post-receive-hooks | |||
type WebHookPayload struct { | |||
After *string `json:"after,omitempty"` | |||
Before *string `json:"before,omitempty"` | |||
Commits []WebHookCommit `json:"commits,omitempty"` | |||
Compare *string `json:"compare,omitempty"` | |||
Created *bool `json:"created,omitempty"` | |||
Deleted *bool `json:"deleted,omitempty"` | |||
Forced *bool `json:"forced,omitempty"` | |||
HeadCommit *WebHookCommit `json:"head_commit,omitempty"` | |||
Pusher *User `json:"pusher,omitempty"` | |||
Ref *string `json:"ref,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Sender *User `json:"sender,omitempty"` | |||
} | |||
func (w WebHookPayload) String() string { | |||
return Stringify(w) | |||
} | |||
// WebHookCommit represents the commit variant we receive from GitHub in a | |||
// WebHookPayload. | |||
type WebHookCommit struct { | |||
Added []string `json:"added,omitempty"` | |||
Author *WebHookAuthor `json:"author,omitempty"` | |||
Committer *WebHookAuthor `json:"committer,omitempty"` | |||
Distinct *bool `json:"distinct,omitempty"` | |||
ID *string `json:"id,omitempty"` | |||
Message *string `json:"message,omitempty"` | |||
Modified []string `json:"modified,omitempty"` | |||
Removed []string `json:"removed,omitempty"` | |||
Timestamp *time.Time `json:"timestamp,omitempty"` | |||
} | |||
func (w WebHookCommit) String() string { | |||
return Stringify(w) | |||
} | |||
// WebHookAuthor represents the author or committer of a commit, as specified | |||
// in a WebHookCommit. The commit author may not correspond to a GitHub User. | |||
type WebHookAuthor struct { | |||
Email *string `json:"email,omitempty"` | |||
Name *string `json:"name,omitempty"` | |||
Username *string `json:"username,omitempty"` | |||
} | |||
func (w WebHookAuthor) String() string { | |||
return Stringify(w) | |||
} | |||
// Hook represents a GitHub (web and service) hook for a repository. | |||
type Hook struct { | |||
CreatedAt *time.Time `json:"created_at,omitempty"` | |||
UpdatedAt *time.Time `json:"updated_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
ID *int64 `json:"id,omitempty"` | |||
// Only the following fields are used when creating a hook. | |||
// Config is required. | |||
Config map[string]interface{} `json:"config,omitempty"` | |||
Events []string `json:"events,omitempty"` | |||
Active *bool `json:"active,omitempty"` | |||
} | |||
func (h Hook) String() string { | |||
return Stringify(h) | |||
} | |||
// createHookRequest is a subset of Hook and is used internally | |||
// by CreateHook to pass only the known fields for the endpoint. | |||
// | |||
// See https://github.com/google/go-github/issues/1015 for more | |||
// information. | |||
type createHookRequest struct { | |||
// Config is required. | |||
Name string `json:"name"` | |||
Config map[string]interface{} `json:"config,omitempty"` | |||
Events []string `json:"events,omitempty"` | |||
Active *bool `json:"active,omitempty"` | |||
} | |||
// CreateHook creates a Hook for the specified repository. | |||
// Config is a required field. | |||
// | |||
// Note that only a subset of the hook fields are used and hook must | |||
// not be nil. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#create-a-hook | |||
func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string, hook *Hook) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) | |||
hookReq := &createHookRequest{ | |||
Name: "web", | |||
Events: hook.Events, | |||
Active: hook.Active, | |||
Config: hook.Config, | |||
} | |||
req, err := s.client.NewRequest("POST", u, hookReq) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(Hook) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// ListHooks lists all Hooks for the specified repository. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#list | |||
func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Hook, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
var hooks []*Hook | |||
resp, err := s.client.Do(ctx, req, &hooks) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return hooks, resp, nil | |||
} | |||
// GetHook returns a single specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#get-single-hook | |||
func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, id int64) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(Hook) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// EditHook updates a specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#edit-a-hook | |||
func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, hook *Hook) (*Hook, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) | |||
req, err := s.client.NewRequest("PATCH", u, hook) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
h := new(Hook) | |||
resp, err := s.client.Do(ctx, req, h) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return h, resp, nil | |||
} | |||
// DeleteHook deletes a specified Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#delete-a-hook | |||
func (s *RepositoriesService) DeleteHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// PingHook triggers a 'ping' event to be sent to the Hook. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook | |||
func (s *RepositoriesService) PingHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// TestHook triggers a test Hook by github. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/hooks/#test-a-push-hook | |||
func (s *RepositoriesService) TestHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id) | |||
req, err := s.client.NewRequest("POST", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} |
@ -0,0 +1,89 @@ | |||
// Copyright 2016 The go-github AUTHORS. All rights reserved. | |||
// | |||
// Use of this source code is governed by a BSD-style | |||
// license that can be found in the LICENSE file. | |||
package github | |||
import ( | |||
"context" | |||
"fmt" | |||
) | |||
// RepositoryInvitation represents an invitation to collaborate on a repo. | |||
type RepositoryInvitation struct { | |||
ID *int64 `json:"id,omitempty"` | |||
Repo *Repository `json:"repository,omitempty"` | |||
Invitee *User `json:"invitee,omitempty"` | |||
Inviter *User `json:"inviter,omitempty"` | |||
// Permissions represents the permissions that the associated user will have | |||
// on the repository. Possible values are: "read", "write", "admin". | |||
Permissions *string `json:"permissions,omitempty"` | |||
CreatedAt *Timestamp `json:"created_at,omitempty"` | |||
URL *string `json:"url,omitempty"` | |||
HTMLURL *string `json:"html_url,omitempty"` | |||
} | |||
// ListInvitations lists all currently-open repository invitations. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository | |||
func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/invitations", owner, repo) | |||
u, err := addOptions(u, opt) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
req, err := s.client.NewRequest("GET", u, nil) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
invites := []*RepositoryInvitation{} | |||
resp, err := s.client.Do(ctx, req, &invites) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return invites, resp, nil | |||
} | |||
// DeleteInvitation deletes a repository invitation. | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation | |||
func (s *RepositoriesService) DeleteInvitation(ctx context.Context, owner, repo string, invitationID int64) (*Response, error) { | |||
u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) | |||
req, err := s.client.NewRequest("DELETE", u, nil) | |||
if err != nil { | |||
return nil, err | |||
} | |||
return s.client.Do(ctx, req, nil) | |||
} | |||
// UpdateInvitation updates the permissions associated with a repository | |||
// invitation. | |||
// | |||
// permissions represents the permissions that the associated user will have | |||
// on the repository. Possible values are: "read", "write", "admin". | |||
// | |||
// GitHub API docs: https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation | |||
func (s *RepositoriesService) UpdateInvitation(ctx context.Context, owner, repo string, invitationID int64, permissions string) (*RepositoryInvitation, *Response, error) { | |||
opts := &struct { | |||
Permissions string `json:"permissions"` | |||
}{Permissions: permissions} | |||
u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) | |||
req, err := s.client.NewRequest("PATCH", u, opts) | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
invite := &RepositoryInvitation{} | |||
resp, err := s.client.Do(ctx, req, invite) | |||
if err != nil { | |||
return nil, resp, err | |||
} | |||
return invite, resp, nil | |||
} |