Compare commits

...

17 Commits

Author SHA1 Message Date
zeripath
e99534cfd2 Fix Migration 176 yet again (#15132)
Backport #15131

Whilst creating a test for v176 in the migrations_test PR
it has become clear that this was still wrong.

This is now fixed. Genuinely.

Also fix repo transfer

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-03-23 23:18:05 +00:00
6543
27acf6165e update changelog for rc2 release (#15130) 2021-03-23 15:52:43 -04:00
zeripath
f286a28568 Fix consistency check (#15120) (#15128)
In my last fix I missed adding the label_ prefix to the
consistency check count.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-03-23 20:20:34 +01:00
6543
b5c4cb1bde Fix bug on avatar middleware (#15124) (#15126)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
2021-03-23 18:44:37 +00:00
6543
26b98417ad [Vendor] update gitea-sdk v0.14.0 (#15103) (#15107)
* upgraded code.gitea.io/sdk/gitea v0.13.2 => v0.14.0

* rm workaround
2021-03-23 10:10:32 +00:00
zeripath
8b0cf88c0c Changelog for v1.14-rc2 (#15115)
Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-03-22 22:00:51 +01:00
zeripath
23db3375df Fix another clusterfuzz identified issue (#15096) (#15113)
Backport #15096

Signed-off-by: Andrew Thornton <art27@cantab.net>

Co-authored-by: 6543 <6543@obermui.de>
2021-03-22 15:16:08 -04:00
zeripath
14011d77c9 Fix the v176 migration (#15110) (#15111)
Backport #15110

There is a serious issue with the v176 migration where there is a mistaken missing
label_id selection.

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-03-22 14:47:58 -04:00
silverwind
5519e26c2f Fix lock modal content rendering outside modal (#15095) (#15100)
* Fix lock modal content rendering outside modal

The .content was not a child to .modal so was rendering outside. This is
a recent regression but I'm not certain when it was introduced.

* remove extraneous closing div

Co-authored-by: zeripath <art27@cantab.net>
2021-03-22 02:00:42 +01:00
zeripath
6feb435867 Place wrapper around comment as diff to catch panics (#15085) (#15094)
Backport #15085

There are a few recurrent issues with comment as diff reporting panics that are resistant to fixing due to the fact that the panic occurs in the template render and is swallowed by the template renderer.

This PR just adds some logging to force the panic to properly logged and re-propagates back up to the template renderer so we can actually detect what the issue is.

Signed-off-by: Andrew Thornton art27@cantab.net
2021-03-21 23:41:40 +01:00
silverwind
61444ed8ca Fix markdown rendering in milestone content (#15056) (#15091)
- Add missing markdown class for rendered markdown.
- Increase font size of milestone name in list.

Fixes: https://github.com/go-gitea/gitea/issues/15046
2021-03-21 18:57:06 +01:00
6543
d770cc9886 Remove possible resource leak (#15067) (#15082)
* move "copy uploaded lfs files 2 repo" to own function for "defer file.Close()"

* rm type overload

Co-authored-by: zeripath <art27@cantab.net>
2021-03-21 17:07:37 +01:00
a1012112796
fbaa01998a fix double 'push tag' action feed (#15078) (#15083)
Signed-off-by: a1012112796 <1012112796@qq.com>
2021-03-21 14:51:31 +00:00
Lauris BH
ac2ae66ae7 Handle unauthorized user events gracefully (#15071) (#15074) 2021-03-21 10:21:28 +00:00
Lauris BH
ed60fe0986 Update release date (#15065)
* Update release date

* Remove unneeded entry
2021-03-20 21:29:01 +08:00
zeripath
29e0d62790 Update to goldmark 1.3.3 (#15059) (#15060)
Backport #15059

Signed-off-by: Andrew Thornton <art27@cantab.net>
2021-03-20 12:24:09 +01:00
6543
7b464fa67b Fix bug when upload on web (#15042) (#15054)
* Fix bug when upload on web

* move into own function

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: zeripath <art27@cantab.net>

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
2021-03-20 09:37:57 +08:00
71 changed files with 1549 additions and 509 deletions

View File

@@ -4,13 +4,24 @@ This changelog goes through all the changes that have been made in each release
without substantial changes to our git log; to see the highlights of what has without substantial changes to our git log; to see the highlights of what has
been added to each release, please refer to the [blog](https://blog.gitea.io). been added to each release, please refer to the [blog](https://blog.gitea.io).
## [1.14.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.14.0) - 2021-03-19 ## [1.14.0-RC2](https://github.com/go-gitea/gitea/releases/tag/v1.14.0-rc2) - 2021-03-22
* SECURITY
* Fix bug on avatar middleware (#15124) (#15125)
* Fix another clusterfuzz identified issue (#15096) (#15113)
* Update to goldmark 1.3.3 (#15059) (#15060)
* BUGFIXES
* Fix the v176 migration (#15110) (#15111)
* Fix double 'push tag' action feed (#15078) (#15083)
* Remove possible resource leak (#15067) (#15082)
* Handle unauthorized user events gracefully (#15071) (#15074)
## [1.14.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.14.0-rc1) - 2021-03-20
* SECURITY * SECURITY
* Respect approved email domain list for externally validated user registration (#15014) * Respect approved email domain list for externally validated user registration (#15014)
* Add reverse proxy configuration support for remote IP address detection (#14959) * Add reverse proxy configuration support for remote IP address detection (#14959)
* Ensure validation occurs on clone addresses too (#14994) * Ensure validation occurs on clone addresses too (#14994)
* Fix several render issues highlighted during fuzzing (#14986)
* BREAKING * BREAKING
* Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475) * Restore Access.log following migration to Chi framework (Stops access logging of /api/internal routes) (#14475)
* Migrate from Macaron to Chi framework (#14293) * Migrate from Macaron to Chi framework (#14293)

4
go.mod
View File

@@ -5,7 +5,7 @@ go 1.14
require ( require (
cloud.google.com/go v0.78.0 // indirect cloud.google.com/go v0.78.0 // indirect
code.gitea.io/gitea-vet v0.2.1 code.gitea.io/gitea-vet v0.2.1
code.gitea.io/sdk/gitea v0.13.2 code.gitea.io/sdk/gitea v0.14.0
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
@@ -128,7 +128,7 @@ require (
github.com/xanzy/go-gitlab v0.44.0 github.com/xanzy/go-gitlab v0.44.0
github.com/xanzy/ssh-agent v0.3.0 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect
github.com/yohcop/openid-go v1.0.0 github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.3.2 github.com/yuin/goldmark v1.3.3
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
github.com/yuin/goldmark-meta v1.0.0 github.com/yuin/goldmark-meta v1.0.0
go.jolheiser.com/hcaptcha v0.0.4 go.jolheiser.com/hcaptcha v0.0.4

8
go.sum
View File

@@ -38,8 +38,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s= code.gitea.io/gitea-vet v0.2.1 h1:b30by7+3SkmiftK0RjuXqFvZg2q4p68uoPGuxhzBN0s=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.13.2 h1:wAnT/J7Z62q3fJXbgnecoaOBh8CM1Qq0/DakWxiv4yA= code.gitea.io/sdk/gitea v0.14.0 h1:m4J352I3p9+bmJUfS+g0odeQzBY/5OXP91Gv6D4fnJ0=
code.gitea.io/sdk/gitea v0.13.2/go.mod h1:lee2y8LeV3kQb2iK+hHlMqoadL4bp27QOkOV/hawLKg= code.gitea.io/sdk/gitea v0.14.0/go.mod h1:89WiyOX1KEcvjP66sRHdu0RafojGo60bT9UqW17VbWs=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 h1:xCVJPY823C8RWpgMabTw2kOglDrg0iS3GcQU6wdwHkU= gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 h1:xCVJPY823C8RWpgMabTw2kOglDrg0iS3GcQU6wdwHkU=
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7/go.mod h1:AyfTrwtfYN54R/HmVvMYPnSTenH5bVoyh8x6tBluxEA= gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7/go.mod h1:AyfTrwtfYN54R/HmVvMYPnSTenH5bVoyh8x6tBluxEA=
@@ -1145,8 +1145,8 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.2 h1:YjHC5TgyMmHpicTgEqDN0Q96Xo8K6tLXPnmNOHXCgs0= github.com/yuin/goldmark v1.3.3 h1:37BdQwPx8VOSic8eDSWee6QL9mRpZRm9VJp/QugNrW0=
github.com/yuin/goldmark v1.3.2/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio= github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691 h1:VWSxtAiQNh3zgHJpdpkpVYjTPqRE3P6UZCOPa1nRDio=
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo= github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691/go.mod h1:YLF3kDffRfUH/bTxOxHhV6lxwIB3Vfj91rEwNMS9MXo=
github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM= github.com/yuin/goldmark-meta v1.0.0 h1:ScsatUIT2gFS6azqzLGUjgOnELsBOxMXerM3ogdJhAM=

View File

@@ -338,7 +338,7 @@ func FixCommentTypeLabelWithEmptyLabel() (int64, error) {
// CountCommentTypeLabelWithOutsideLabels count label comments with outside label // CountCommentTypeLabelWithOutsideLabels count label comments with outside label
func CountCommentTypeLabelWithOutsideLabels() (int64, error) { func CountCommentTypeLabelWithOutsideLabels() (int64, error) {
return x.Where("comment.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND repository.owner_id != label.org_id))", CommentTypeLabel). return x.Where("comment.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))", CommentTypeLabel).
Table("comment"). Table("comment").
Join("inner", "label", "label.id = comment.label_id"). Join("inner", "label", "label.id = comment.label_id").
Join("inner", "issue", "issue.id = comment.issue_id "). Join("inner", "issue", "issue.id = comment.issue_id ").
@@ -354,8 +354,9 @@ func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
FROM comment AS com FROM comment AS com
INNER JOIN label ON com.label_id = label.id INNER JOIN label ON com.label_id = label.id
INNER JOIN issue on issue.id = com.issue_id INNER JOIN issue on issue.id = com.issue_id
INNER JOIN repository ON issue.repo_id = repository.id
WHERE WHERE
com.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repo.owner_id)) com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
) AS il_too)`, CommentTypeLabel) ) AS il_too)`, CommentTypeLabel)
if err != nil { if err != nil {
return 0, err return 0, err
@@ -366,9 +367,9 @@ func FixCommentTypeLabelWithOutsideLabels() (int64, error) {
// CountIssueLabelWithOutsideLabels count label comments with outside label // CountIssueLabelWithOutsideLabels count label comments with outside label
func CountIssueLabelWithOutsideLabels() (int64, error) { func CountIssueLabelWithOutsideLabels() (int64, error) {
return x.Where(builder.Expr("issue.repo_id != label.repo_id OR (label.repo_id = 0 AND repository.owner_id != label.org_id)")). return x.Where(builder.Expr("(label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)")).
Table("issue_label"). Table("issue_label").
Join("inner", "label", "issue_label.id = label.id "). Join("inner", "label", "issue_label.label_id = label.id ").
Join("inner", "issue", "issue.id = issue_label.issue_id "). Join("inner", "issue", "issue.id = issue_label.issue_id ").
Join("inner", "repository", "issue.repo_id = repository.id"). Join("inner", "repository", "issue.repo_id = repository.id").
Count(new(IssueLabel)) Count(new(IssueLabel))
@@ -380,11 +381,11 @@ func FixIssueLabelWithOutsideLabels() (int64, error) {
SELECT il_too.id FROM ( SELECT il_too.id FROM (
SELECT il_too_too.id SELECT il_too_too.id
FROM issue_label AS il_too_too FROM issue_label AS il_too_too
INNER JOIN label ON il_too_too.id = label.id INNER JOIN label ON il_too_too.label_id = label.id
INNER JOIN issue on issue.id = il_too_too.issue_id INNER JOIN issue on issue.id = il_too_too.issue_id
INNER JOIN repository on repository.id = issue.repo_id INNER JOIN repository on repository.id = issue.repo_id
WHERE WHERE
issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id) (label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
) AS il_too )`) ) AS il_too )`)
if err != nil { if err != nil {

View File

@@ -48,11 +48,11 @@ func removeInvalidLabels(x *xorm.Engine) error {
SELECT il_too.id FROM ( SELECT il_too.id FROM (
SELECT il_too_too.id SELECT il_too_too.id
FROM issue_label AS il_too_too FROM issue_label AS il_too_too
INNER JOIN label ON il_too_too.id = label.id INNER JOIN label ON il_too_too.label_id = label.id
INNER JOIN issue on issue.id = il_too_too.issue_id INNER JOIN issue on issue.id = il_too_too.issue_id
INNER JOIN repository on repository.id = issue.repo_id INNER JOIN repository on repository.id = issue.repo_id
WHERE WHERE
issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id) (label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id)
) AS il_too )`); err != nil { ) AS il_too )`); err != nil {
return err return err
} }
@@ -65,7 +65,7 @@ func removeInvalidLabels(x *xorm.Engine) error {
INNER JOIN issue on issue.id = com.issue_id INNER JOIN issue on issue.id = com.issue_id
INNER JOIN repository on repository.id = issue.repo_id INNER JOIN repository on repository.id = issue.repo_id
WHERE WHERE
com.type = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != repository.owner_id)) com.type = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != repository.owner_id))
) AS il_too)`, 7); err != nil { ) AS il_too)`, 7); err != nil {
return err return err
} }

View File

@@ -330,10 +330,10 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
SELECT il_too.id FROM ( SELECT il_too.id FROM (
SELECT il_too_too.id SELECT il_too_too.id
FROM issue_label AS il_too_too FROM issue_label AS il_too_too
INNER JOIN label ON il_too_too.id = label.id INNER JOIN label ON il_too_too.label_id = label.id
INNER JOIN issue on issue.id = il_too_too.issue_id INNER JOIN issue on issue.id = il_too_too.issue_id
WHERE WHERE
issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?)) issue.repo_id = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != ?))
) AS il_too )`, repo.ID, newOwner.ID); err != nil { ) AS il_too )`, repo.ID, newOwner.ID); err != nil {
return fmt.Errorf("Unable to remove old org labels: %v", err) return fmt.Errorf("Unable to remove old org labels: %v", err)
} }
@@ -343,9 +343,9 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) (err e
SELECT com.id SELECT com.id
FROM comment AS com FROM comment AS com
INNER JOIN label ON com.label_id = label.id INNER JOIN label ON com.label_id = label.id
INNER JOIN issue on issue.id = com.issue_id INNER JOIN issue ON issue.id = com.issue_id
WHERE WHERE
com.type = ? AND issue.repo_id = ? AND (issue.repo_id != label.repo_id OR (label.repo_id = 0 AND label.org_id != ?)) com.type = ? AND issue.repo_id = ? AND ((label.org_id = 0 AND issue.repo_id != label.repo_id) OR (label.repo_id = 0 AND label.org_id != ?))
) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil { ) AS il_too)`, CommentTypeLabel, repo.ID, newOwner.ID); err != nil {
return fmt.Errorf("Unable to remove old org label comments: %v", err) return fmt.Errorf("Unable to remove old org label comments: %v", err)
} }

View File

@@ -327,7 +327,7 @@ func (ctx *postProcessCtx) postProcess(rawHTML []byte) ([]byte, error) {
_, _ = res.WriteString("<html><body>") _, _ = res.WriteString("<html><body>")
// Strip out nuls - they're always invalid // Strip out nuls - they're always invalid
_, _ = nulCleaner.WriteString(res, string(tagCleaner.ReplaceAll(rawHTML, []byte("&lt;$1")))) _, _ = res.Write(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("&lt;$1")))
// close the tags // close the tags
_, _ = res.WriteString("</body></html>") _, _ = res.WriteString("</body></html>")

View File

@@ -525,9 +525,6 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
headRepoName = pr.Head.Repository.Name headRepoName = pr.Head.Repository.Name
headCloneURL = pr.Head.Repository.CloneURL headCloneURL = pr.Head.Repository.CloneURL
} }
if err := fixPullHeadSha(g.client, pr); err != nil {
return nil, false, fmt.Errorf("error while resolving head git ref: %s for pull #%d. Error: %v", pr.Head.Ref, pr.Index, err)
}
headSHA = pr.Head.Sha headSHA = pr.Head.Sha
headRef = pr.Head.Ref headRef = pr.Head.Ref
} }
@@ -679,22 +676,3 @@ func (g *GiteaDownloader) GetReviews(index int64) ([]*base.Review, error) {
} }
return allReviews, nil return allReviews, nil
} }
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
// When no head sha is available, this is because the branch got deleted in the base repo.
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
// which stays available to resolve the commit sha.
func fixPullHeadSha(client *gitea_sdk.Client, pr *gitea_sdk.PullRequest) error {
owner := pr.Base.Repository.Owner.UserName
repo := pr.Base.Repository.Name
if pr.Head != nil && pr.Head.Sha == "" {
refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
if err != nil {
return err
} else if len(refs) == 0 {
return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
}
pr.Head.Sha = refs[0].Object.SHA
}
return nil
}

View File

@@ -332,7 +332,8 @@ func (a *actionNotifier) NotifyPushCommits(pusher *models.User, repo *models.Rep
func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) { func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
opType := models.ActionCommitRepo opType := models.ActionCommitRepo
if refType == "tag" { if refType == "tag" {
opType = models.ActionPushTag // has sent same action in `NotifyPushCommits`, so skip it.
return
} }
if err := models.NotifyWatchers(&models.Action{ if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID, ActUserID: doer.ID,
@@ -350,7 +351,8 @@ func (a *actionNotifier) NotifyCreateRef(doer *models.User, repo *models.Reposit
func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) { func (a *actionNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) {
opType := models.ActionDeleteBranch opType := models.ActionDeleteBranch
if refType == "tag" { if refType == "tag" {
opType = models.ActionDeleteTag // has sent same action in `NotifyPushCommits`, so skip it.
return
} }
if err := models.NotifyWatchers(&models.Action{ if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID, ActUserID: doer.ID,

View File

@@ -99,42 +99,10 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
} }
// Copy uploaded files into repository. // Copy uploaded files into repository.
for i, uploadInfo := range infos { for i := range infos {
file, err := os.Open(uploadInfo.upload.LocalPath()) if err := copyUploadedLFSFileIntoRepository(&infos[i], filename2attribute2info, t, opts.TreePath); err != nil {
if err != nil {
return err return err
} }
defer file.Close()
var objectHash string
if setting.LFS.StartServer && filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
// Handle LFS
// FIXME: Inefficient! this should probably happen in models.Upload
oid, err := models.GenerateLFSOid(file)
if err != nil {
return err
}
fileInfo, err := file.Stat()
if err != nil {
return err
}
uploadInfo.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID}
if objectHash, err = t.HashObject(strings.NewReader(uploadInfo.lfsMetaObject.Pointer())); err != nil {
return err
}
infos[i] = uploadInfo
} else if objectHash, err = t.HashObject(file); err != nil {
return err
}
// Add the object to the index
if err := t.AddObjectToIndex("100644", objectHash, path.Join(opts.TreePath, uploadInfo.upload.Name)); err != nil {
return err
}
} }
// Now write the tree // Now write the tree
@@ -154,11 +122,11 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
} }
// Now deal with LFS objects // Now deal with LFS objects
for _, uploadInfo := range infos { for i := range infos {
if uploadInfo.lfsMetaObject == nil { if infos[i].lfsMetaObject == nil {
continue continue
} }
uploadInfo.lfsMetaObject, err = models.NewLFSMetaObject(uploadInfo.lfsMetaObject) infos[i].lfsMetaObject, err = models.NewLFSMetaObject(infos[i].lfsMetaObject)
if err != nil { if err != nil {
// OK Now we need to cleanup // OK Now we need to cleanup
return cleanUpAfterFailure(&infos, t, err) return cleanUpAfterFailure(&infos, t, err)
@@ -170,28 +138,10 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
// OK now we can insert the data into the store - there's no way to clean up the store // OK now we can insert the data into the store - there's no way to clean up the store
// once it's in there, it's in there. // once it's in there, it's in there.
contentStore := &lfs.ContentStore{ObjectStorage: storage.LFS} contentStore := &lfs.ContentStore{ObjectStorage: storage.LFS}
for _, uploadInfo := range infos { for _, info := range infos {
if uploadInfo.lfsMetaObject == nil { if err := uploadToLFSContentStore(info, contentStore); err != nil {
continue
}
exist, err := contentStore.Exists(uploadInfo.lfsMetaObject)
if err != nil {
return cleanUpAfterFailure(&infos, t, err) return cleanUpAfterFailure(&infos, t, err)
} }
if !exist {
file, err := os.Open(uploadInfo.upload.LocalPath())
if err != nil {
return cleanUpAfterFailure(&infos, t, err)
}
defer file.Close()
// FIXME: Put regenerates the hash and copies the file over.
// I guess this strictly ensures the soundness of the store but this is inefficient.
if err := contentStore.Put(uploadInfo.lfsMetaObject, file); err != nil {
// OK Now we need to cleanup
// Can't clean up the store, once uploaded there they're there.
return cleanUpAfterFailure(&infos, t, err)
}
}
} }
// Then push this tree to NewBranch // Then push this tree to NewBranch
@@ -201,3 +151,62 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
return models.DeleteUploads(uploads...) return models.DeleteUploads(uploads...)
} }
func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error {
file, err := os.Open(info.upload.LocalPath())
if err != nil {
return err
}
defer file.Close()
var objectHash string
if setting.LFS.StartServer && filename2attribute2info[info.upload.Name] != nil && filename2attribute2info[info.upload.Name]["filter"] == "lfs" {
// Handle LFS
// FIXME: Inefficient! this should probably happen in models.Upload
oid, err := models.GenerateLFSOid(file)
if err != nil {
return err
}
fileInfo, err := file.Stat()
if err != nil {
return err
}
info.lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: fileInfo.Size(), RepositoryID: t.repo.ID}
if objectHash, err = t.HashObject(strings.NewReader(info.lfsMetaObject.Pointer())); err != nil {
return err
}
} else if objectHash, err = t.HashObject(file); err != nil {
return err
}
// Add the object to the index
return t.AddObjectToIndex("100644", objectHash, path.Join(treePath, info.upload.Name))
}
func uploadToLFSContentStore(info uploadInfo, contentStore *lfs.ContentStore) error {
if info.lfsMetaObject == nil {
return nil
}
exist, err := contentStore.Exists(info.lfsMetaObject)
if err != nil {
return err
}
if !exist {
file, err := os.Open(info.upload.LocalPath())
if err != nil {
return err
}
defer file.Close()
// FIXME: Put regenerates the hash and copies the file over.
// I guess this strictly ensures the soundness of the store but this is inefficient.
if err := contentStore.Put(info.lfsMetaObject, file); err != nil {
// OK Now we need to cleanup
// Can't clean up the store, once uploaded there they're there.
return err
}
}
return nil
}

View File

@@ -30,6 +30,17 @@ func Events(ctx *context.Context) {
ctx.Resp.Header().Set("X-Accel-Buffering", "no") ctx.Resp.Header().Set("X-Accel-Buffering", "no")
ctx.Resp.WriteHeader(http.StatusOK) ctx.Resp.WriteHeader(http.StatusOK)
if !ctx.IsSigned {
// Return unauthorized status event
event := (&eventsource.Event{
Name: "unauthorized",
Data: "sorry",
})
_, _ = event.WriteTo(ctx)
ctx.Resp.Flush()
return
}
// Listen to connection close and un-register messageChan // Listen to connection close and un-register messageChan
notify := ctx.Req.Context().Done() notify := ctx.Req.Context().Done()
ctx.Resp.Flush() ctx.Resp.Flush()

View File

@@ -11,6 +11,7 @@ import (
"net/http" "net/http"
"os" "os"
"path" "path"
"path/filepath"
"strings" "strings"
"time" "time"
@@ -87,13 +88,21 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
return return
} }
if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) { prefix := strings.Trim(prefix, "/")
if !strings.HasPrefix(req.URL.EscapedPath(), "/"+prefix+"/") {
next.ServeHTTP(w, req) next.ServeHTTP(w, req)
return return
} }
rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix) rPath := strings.TrimPrefix(req.URL.EscapedPath(), "/"+prefix+"/")
rPath = strings.TrimPrefix(rPath, "/") rPath = strings.TrimPrefix(rPath, "/")
if rPath == "" {
http.Error(w, "file not found", 404)
return
}
rPath = path.Clean("/" + filepath.ToSlash(rPath))
rPath = rPath[1:]
fi, err := objStore.Stat(rPath) fi, err := objStore.Stat(rPath)
if err == nil && httpcache.HandleTimeCache(req, w, fi) { if err == nil && httpcache.HandleTimeCache(req, w, fi) {

View File

@@ -400,7 +400,7 @@ func RegisterRoutes(m *web.Route) {
}) })
}, reqSignOut) }, reqSignOut)
m.Any("/user/events", reqSignIn, events.Events) m.Any("/user/events", events.Events)
m.Group("/login/oauth", func() { m.Group("/login/oauth", func() {
m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth) m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth)

View File

@@ -1288,6 +1288,14 @@ func CommentAsDiff(c *models.Comment) (*Diff, error) {
// CommentMustAsDiff executes AsDiff and logs the error instead of returning // CommentMustAsDiff executes AsDiff and logs the error instead of returning
func CommentMustAsDiff(c *models.Comment) *Diff { func CommentMustAsDiff(c *models.Comment) *Diff {
if c == nil {
return nil
}
defer func() {
if err := recover(); err != nil {
log.Error("PANIC whilst retrieving diff for comment[%d] Error: %v\nStack: %s", c.ID, err, log.Stack(2))
}
}()
diff, err := CommentAsDiff(c) diff, err := CommentAsDiff(c)
if err != nil { if err != nil {
log.Warn("CommentMustAsDiff: %v", err) log.Warn("CommentMustAsDiff: %v", err)

View File

@@ -4,8 +4,8 @@
<div class="ui container"> <div class="ui container">
<div class="ui three column stackable grid"> <div class="ui three column stackable grid">
<div class="column"> <div class="column">
<h3>{{.Milestone.Name}}</h3> <h1>{{.Milestone.Name}}</h1>
<div class="content"> <div class="markdown content">
{{.Milestone.RenderedContent|Str2html}} {{.Milestone.RenderedContent|Str2html}}
</div> </div>
</div> </div>

View File

@@ -43,7 +43,7 @@
<div class="milestone list"> <div class="milestone list">
{{range .Milestones}} {{range .Milestones}}
<li class="item"> <li class="item">
{{svg "octicon-milestone"}} <a href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a> {{svg "octicon-milestone" 16 "mr-2"}} <a href="{{$.RepoLink}}/milestone/{{.ID}}">{{.Name}}</a>
<div class="ui right green progress" data-percent="{{.Completeness}}"> <div class="ui right green progress" data-percent="{{.Completeness}}">
<div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}> <div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
<div class="progress"></div> <div class="progress"></div>
@@ -80,7 +80,7 @@
</div> </div>
{{end}} {{end}}
{{if .Content}} {{if .Content}}
<div class="content"> <div class="markdown content">
{{.RenderedContent|Str2html}} {{.RenderedContent|Str2html}}
</div> </div>
{{end}} {{end}}

View File

@@ -578,8 +578,6 @@
{{end}} {{end}}
</button> </button>
</div> </div>
<div class="ui tiny modal" id="lock"> <div class="ui tiny modal" id="lock">
<div class="header"> <div class="header">
{{ if .Issue.IsLocked }} {{ if .Issue.IsLocked }}
@@ -588,7 +586,6 @@
{{.i18n.Tr "repo.issues.lock.title"}} {{.i18n.Tr "repo.issues.lock.title"}}
{{end}} {{end}}
</div> </div>
</div>
<div class="content"> <div class="content">
<div class="ui warning message text left"> <div class="ui warning message text left">
{{ if .Issue.IsLocked }} {{ if .Issue.IsLocked }}

View File

@@ -39,6 +39,9 @@ func (c *Client) RunCronTasks(task string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, err return nil, err
} }
if err := escapeValidatePathSegments(&task); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/admin/cron/%s", task), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@@ -26,6 +26,9 @@ func (c *Client) AdminListOrgs(opt AdminListOrgsOptions) ([]*Organization, *Resp
// AdminCreateOrg create an organization // AdminCreateOrg create an organization
func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) { func (c *Client) AdminCreateOrg(user string, opt CreateOrgOption) (*Organization, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -12,6 +12,9 @@ import (
// AdminCreateRepo create a repo // AdminCreateRepo create a repo
func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) { func (c *Client) AdminCreateRepo(user string, opt CreateRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -65,12 +65,12 @@ func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, *Response, error)
type EditUserOption struct { type EditUserOption struct {
SourceID int64 `json:"source_id"` SourceID int64 `json:"source_id"`
LoginName string `json:"login_name"` LoginName string `json:"login_name"`
FullName string `json:"full_name"` Email *string `json:"email"`
Email string `json:"email"` FullName *string `json:"full_name"`
Password string `json:"password"` Password string `json:"password"`
MustChangePassword *bool `json:"must_change_password"` MustChangePassword *bool `json:"must_change_password"`
Website string `json:"website"` Website *string `json:"website"`
Location string `json:"location"` Location *string `json:"location"`
Active *bool `json:"active"` Active *bool `json:"active"`
Admin *bool `json:"admin"` Admin *bool `json:"admin"`
AllowGitHook *bool `json:"allow_git_hook"` AllowGitHook *bool `json:"allow_git_hook"`
@@ -82,6 +82,9 @@ type EditUserOption struct {
// AdminEditUser modify user informations // AdminEditUser modify user informations
func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) { func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -92,12 +95,18 @@ func (c *Client) AdminEditUser(user string, opt EditUserOption) (*Response, erro
// AdminDeleteUser delete one user according name // AdminDeleteUser delete one user according name
func (c *Client) AdminDeleteUser(user string) (*Response, error) { func (c *Client) AdminDeleteUser(user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
return resp, err return resp, err
} }
// AdminCreateUserPublicKey adds a public key for the user // AdminCreateUserPublicKey adds a public key for the user
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) { func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -109,6 +118,9 @@ func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*Pu
// AdminDeleteUserPublicKey deletes a user's public key // AdminDeleteUserPublicKey deletes a user's public key
func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) { func (c *Client) AdminDeleteUserPublicKey(user string, keyID int) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s/keys/%d", user, keyID), nil, nil)
return resp, err return resp, err
} }

View File

@@ -31,6 +31,9 @@ type ListReleaseAttachmentsOptions struct {
// ListReleaseAttachments list release's attachments // ListReleaseAttachments list release's attachments
func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) { func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt ListReleaseAttachmentsOptions) ([]*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
attachments := make([]*Attachment, 0, opt.PageSize) attachments := make([]*Attachment, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@@ -41,6 +44,9 @@ func (c *Client) ListReleaseAttachments(user, repo string, release int64, opt Li
// GetReleaseAttachment returns the requested attachment // GetReleaseAttachment returns the requested attachment
func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) { func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
a := new(Attachment) a := new(Attachment)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id),
@@ -50,6 +56,9 @@ func (c *Client) GetReleaseAttachment(user, repo string, release int64, id int64
// CreateReleaseAttachment creates an attachment for the given release // CreateReleaseAttachment creates an attachment for the given release
func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) { func (c *Client) CreateReleaseAttachment(user, repo string, release int64, file io.Reader, filename string) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
// Write file to body // Write file to body
body := new(bytes.Buffer) body := new(bytes.Buffer)
writer := multipart.NewWriter(body) writer := multipart.NewWriter(body)
@@ -80,6 +89,9 @@ type EditAttachmentOptions struct {
// EditReleaseAttachment updates the given attachment with the given options // EditReleaseAttachment updates the given attachment with the given options
func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) { func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachment int64, form EditAttachmentOptions) (*Attachment, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&form) body, err := json.Marshal(&form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -91,6 +103,9 @@ func (c *Client) EditReleaseAttachment(user, repo string, release int64, attachm
// DeleteReleaseAttachment deletes the given attachment including the uploaded file // DeleteReleaseAttachment deletes the given attachment including the uploaded file
func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) { func (c *Client) DeleteReleaseAttachment(user, repo string, release int64, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/releases/%d/assets/%d", user, repo, release, id), nil, nil)
return resp, err return resp, err
} }

View File

@@ -13,6 +13,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url"
"strings" "strings"
"sync" "sync"
@@ -23,10 +24,10 @@ var jsonHeader = http.Header{"content-type": []string{"application/json"}}
// Version return the library version // Version return the library version
func Version() string { func Version() string {
return "0.13.0" return "0.14.0"
} }
// Client represents a Gitea API client. // Client represents a thread-safe Gitea API client.
type Client struct { type Client struct {
url string url string
accessToken string accessToken string
@@ -37,8 +38,9 @@ type Client struct {
debug bool debug bool
client *http.Client client *http.Client
ctx context.Context ctx context.Context
mutex sync.RWMutex
serverVersion *version.Version serverVersion *version.Version
versionLock sync.RWMutex getVersionOnce sync.Once
} }
// Response represents the gitea response // Response represents the gitea response
@@ -47,6 +49,7 @@ type Response struct {
} }
// NewClient initializes and returns a API client. // NewClient initializes and returns a API client.
// Usage of all gitea.Client methods is concurrency-safe.
func NewClient(url string, options ...func(*Client)) (*Client, error) { func NewClient(url string, options ...func(*Client)) (*Client, error) {
client := &Client{ client := &Client{
url: strings.TrimSuffix(url, "/"), url: strings.TrimSuffix(url, "/"),
@@ -56,7 +59,7 @@ func NewClient(url string, options ...func(*Client)) (*Client, error) {
for _, opt := range options { for _, opt := range options {
opt(client) opt(client)
} }
if err := client.checkServerVersionGreaterThanOrEqual(version1_10_0); err != nil { if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
return nil, err return nil, err
} }
return client, nil return client, nil
@@ -72,14 +75,23 @@ func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
// SetHTTPClient is an option for NewClient to set custom http client // SetHTTPClient is an option for NewClient to set custom http client
func SetHTTPClient(httpClient *http.Client) func(client *Client) { func SetHTTPClient(httpClient *http.Client) func(client *Client) {
return func(client *Client) { return func(client *Client) {
client.client = httpClient client.SetHTTPClient(httpClient)
} }
} }
// SetHTTPClient replaces default http.Client with user given one.
func (c *Client) SetHTTPClient(client *http.Client) {
c.mutex.Lock()
c.client = client
c.mutex.Unlock()
}
// SetToken is an option for NewClient to set token // SetToken is an option for NewClient to set token
func SetToken(token string) func(client *Client) { func SetToken(token string) func(client *Client) {
return func(client *Client) { return func(client *Client) {
client.mutex.Lock()
client.accessToken = token client.accessToken = token
client.mutex.Unlock()
} }
} }
@@ -92,7 +104,9 @@ func SetBasicAuth(username, password string) func(client *Client) {
// SetBasicAuth sets username and password // SetBasicAuth sets username and password
func (c *Client) SetBasicAuth(username, password string) { func (c *Client) SetBasicAuth(username, password string) {
c.mutex.Lock()
c.username, c.password = username, password c.username, c.password = username, password
c.mutex.Unlock()
} }
// SetOTP is an option for NewClient to set OTP for 2FA // SetOTP is an option for NewClient to set OTP for 2FA
@@ -104,7 +118,9 @@ func SetOTP(otp string) func(client *Client) {
// SetOTP sets OTP for 2FA // SetOTP sets OTP for 2FA
func (c *Client) SetOTP(otp string) { func (c *Client) SetOTP(otp string) {
c.mutex.Lock()
c.otp = otp c.otp = otp
c.mutex.Unlock()
} }
// SetContext is an option for NewClient to set context // SetContext is an option for NewClient to set context
@@ -116,12 +132,9 @@ func SetContext(ctx context.Context) func(client *Client) {
// SetContext set context witch is used for http requests // SetContext set context witch is used for http requests
func (c *Client) SetContext(ctx context.Context) { func (c *Client) SetContext(ctx context.Context) {
c.mutex.Lock()
c.ctx = ctx c.ctx = ctx
} c.mutex.Unlock()
// SetHTTPClient replaces default http.Client with user given one.
func (c *Client) SetHTTPClient(client *http.Client) {
c.client = client
} }
// SetSudo is an option for NewClient to set sudo header // SetSudo is an option for NewClient to set sudo header
@@ -133,43 +146,57 @@ func SetSudo(sudo string) func(client *Client) {
// SetSudo sets username to impersonate. // SetSudo sets username to impersonate.
func (c *Client) SetSudo(sudo string) { func (c *Client) SetSudo(sudo string) {
c.mutex.Lock()
c.sudo = sudo c.sudo = sudo
c.mutex.Unlock()
} }
// SetDebugMode is an option for NewClient to enable debug mode // SetDebugMode is an option for NewClient to enable debug mode
func SetDebugMode() func(client *Client) { func SetDebugMode() func(client *Client) {
return func(client *Client) { return func(client *Client) {
client.mutex.Lock()
client.debug = true client.debug = true
client.mutex.Unlock()
} }
} }
func (c *Client) getWebResponse(method, path string, body io.Reader) ([]byte, *Response, error) { func (c *Client) getWebResponse(method, path string, body io.Reader) ([]byte, *Response, error) {
if c.debug { c.mutex.RLock()
debug := c.debug
if debug {
fmt.Printf("%s: %s\nBody: %v\n", method, c.url+path, body) fmt.Printf("%s: %s\nBody: %v\n", method, c.url+path, body)
} }
req, err := http.NewRequestWithContext(c.ctx, method, c.url+path, body) req, err := http.NewRequestWithContext(c.ctx, method, c.url+path, body)
client := c.client // client ref can change from this point on so safe it
c.mutex.RUnlock()
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
resp, err := c.client.Do(req)
resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) data, err := ioutil.ReadAll(resp.Body)
if c.debug { if debug {
fmt.Printf("Response: %v\n\n", resp) fmt.Printf("Response: %v\n\n", resp)
} }
return data, &Response{resp}, nil return data, &Response{resp}, nil
} }
func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) { func (c *Client) doRequest(method, path string, header http.Header, body io.Reader) (*Response, error) {
if c.debug { c.mutex.RLock()
debug := c.debug
if debug {
fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, body) fmt.Printf("%s: %s\nHeader: %v\nBody: %s\n", method, c.url+"/api/v1"+path, header, body)
} }
req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body) req, err := http.NewRequestWithContext(c.ctx, method, c.url+"/api/v1"+path, body)
if err != nil { if err != nil {
c.mutex.RUnlock()
return nil, err return nil, err
} }
if len(c.accessToken) != 0 { if len(c.accessToken) != 0 {
@@ -184,20 +211,66 @@ func (c *Client) doRequest(method, path string, header http.Header, body io.Read
if len(c.sudo) != 0 { if len(c.sudo) != 0 {
req.Header.Set("Sudo", c.sudo) req.Header.Set("Sudo", c.sudo)
} }
client := c.client // client ref can change from this point on so safe it
c.mutex.RUnlock()
for k, v := range header { for k, v := range header {
req.Header[k] = v req.Header[k] = v
} }
resp, err := c.client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if c.debug { if debug {
fmt.Printf("Response: %v\n\n", resp) fmt.Printf("Response: %v\n\n", resp)
} }
return &Response{resp}, nil return &Response{resp}, nil
} }
// Converts a response for a HTTP status code indicating an error condition
// (non-2XX) to a well-known error value and response body. For non-problematic
// (2XX) status codes nil will be returned. Note that on a non-2XX response, the
// response body stream will have been read and, hence, is closed on return.
func statusCodeToErr(resp *Response) (body []byte, err error) {
// no error
if resp.StatusCode/100 == 2 {
return nil, nil
}
//
// error: body will be read for details
//
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("body read on HTTP error %d: %v", resp.StatusCode, err)
}
switch resp.StatusCode {
case 403:
return data, errors.New("403 Forbidden")
case 404:
return data, errors.New("404 Not Found")
case 409:
return data, errors.New("409 Conflict")
case 422:
return data, fmt.Errorf("422 Unprocessable Entity: %s", string(data))
}
path := resp.Request.URL.Path
method := resp.Request.Method
header := resp.Request.Header
errMap := make(map[string]interface{})
if err = json.Unmarshal(data, &errMap); err != nil {
// when the JSON can't be parsed, data was probably empty or a
// plain string, so we try to return a helpful error anyway
return data, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
}
return data, errors.New(errMap["message"].(string))
}
func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) { func (c *Client) getResponse(method, path string, header http.Header, body io.Reader) ([]byte, *Response, error) {
resp, err := c.doRequest(method, path, header, body) resp, err := c.doRequest(method, path, header, body)
if err != nil { if err != nil {
@@ -205,32 +278,18 @@ func (c *Client) getResponse(method, path string, header http.Header, body io.Re
} }
defer resp.Body.Close() defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body) // check for errors
data, err := statusCodeToErr(resp)
if err != nil {
return data, resp, err
}
// success (2XX), read body
data, err = ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
switch resp.StatusCode {
case 403:
return data, resp, errors.New("403 Forbidden")
case 404:
return data, resp, errors.New("404 Not Found")
case 409:
return data, resp, errors.New("409 Conflict")
case 422:
return data, resp, fmt.Errorf("422 Unprocessable Entity: %s", string(data))
}
if resp.StatusCode/100 != 2 {
errMap := make(map[string]interface{})
if err = json.Unmarshal(data, &errMap); err != nil {
// when the JSON can't be parsed, data was probably empty or a plain string,
// so we try to return a helpful error anyway
return data, resp, fmt.Errorf("Unknown API Error: %d\nRequest: '%s' with '%s' method '%s' header and '%s' body", resp.StatusCode, path, method, header, string(data))
}
return data, resp, errors.New(errMap["message"].(string))
}
return data, resp, nil return data, resp, nil
} }
@@ -251,3 +310,24 @@ func (c *Client) getStatusCode(method, path string, header http.Header, body io.
return resp.StatusCode, resp, nil return resp.StatusCode, resp, nil
} }
// pathEscapeSegments escapes segments of a path while not escaping forward slash
func pathEscapeSegments(path string) string {
slice := strings.Split(path, "/")
for index := range slice {
slice[index] = url.PathEscape(slice[index])
}
escapedPath := strings.Join(slice, "/")
return escapedPath
}
// escapeValidatePathSegments is a help function to validate and encode url path segments
func escapeValidatePathSegments(seg ...*string) error {
for i := range seg {
if seg[i] == nil || len(*seg[i]) == 0 {
return fmt.Errorf("path segment [%d] is empty", i)
}
*seg[i] = url.PathEscape(*seg[i])
}
return nil
}

View File

@@ -17,6 +17,9 @@ type ListForksOptions struct {
// ListForks list a repository's forks // ListForks list a repository's forks
func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) { func (c *Client) ListForks(user string, repo string, opt ListForksOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
forks := make([]*Repository, opt.PageSize) forks := make([]*Repository, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@@ -33,6 +36,9 @@ type CreateForkOption struct {
// CreateFork create a fork of a repository // CreateFork create a fork of a repository
func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) { func (c *Client) CreateFork(user, repo string, form CreateForkOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(form) body, err := json.Marshal(form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@@ -19,6 +19,9 @@ type GitBlobResponse struct {
// GetBlob get the blob of a repository file // GetBlob get the blob of a repository file
func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) { func (c *Client) GetBlob(user, repo, sha string) (*GitBlobResponse, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &sha); err != nil {
return nil, nil, err
}
blob := new(GitBlobResponse) blob := new(GitBlobResponse)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/blobs/%s", user, repo, sha), nil, nil, blob)
return blob, resp, err return blob, resp, err

View File

@@ -24,6 +24,9 @@ type ListRepoGitHooksOptions struct {
// ListRepoGitHooks list all the Git hooks of one repository // ListRepoGitHooks list all the Git hooks of one repository
func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) { func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions) ([]*GitHook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*GitHook, 0, opt.PageSize) hooks := make([]*GitHook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
@@ -32,6 +35,9 @@ func (c *Client) ListRepoGitHooks(user, repo string, opt ListRepoGitHooksOptions
// GetRepoGitHook get a Git hook of a repository // GetRepoGitHook get a Git hook of a repository
func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) { func (c *Client) GetRepoGitHook(user, repo, id string) (*GitHook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, nil, err
}
h := new(GitHook) h := new(GitHook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil, h)
return h, resp, err return h, resp, err
@@ -44,6 +50,9 @@ type EditGitHookOption struct {
// EditRepoGitHook modify one Git hook of a repository // EditRepoGitHook modify one Git hook of a repository
func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) { func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -54,6 +63,9 @@ func (c *Client) EditRepoGitHook(user, repo, id string, opt EditGitHookOption) (
// DeleteRepoGitHook delete one Git hook from a repository // DeleteRepoGitHook delete one Git hook from a repository
func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) { func (c *Client) DeleteRepoGitHook(user, repo, id string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &id); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/git/%s", user, repo, id), nil, nil)
return resp, err return resp, err
} }

View File

@@ -1,6 +1,6 @@
module code.gitea.io/sdk/gitea module code.gitea.io/sdk/gitea
go 1.12 go 1.13
require ( require (
github.com/hashicorp/go-version v1.2.1 github.com/hashicorp/go-version v1.2.1

View File

@@ -2,6 +2,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

20
vendor/code.gitea.io/sdk/gitea/helper.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
// Copyright 2020 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 gitea
// OptionalBool convert a bool to a bool reference
func OptionalBool(v bool) *bool {
return &v
}
// OptionalString convert a string to a string reference
func OptionalString(v string) *string {
return &v
}
// OptionalInt64 convert a int64 to a int64 reference
func OptionalInt64(v int64) *int64 {
return &v
}

View File

@@ -31,6 +31,9 @@ type ListHooksOptions struct {
// ListOrgHooks list all the hooks of one organization // ListOrgHooks list all the hooks of one organization
func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) { func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*Hook, 0, opt.PageSize) hooks := make([]*Hook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks?%s", org, opt.getURLQuery().Encode()), nil, nil, &hooks)
@@ -39,6 +42,9 @@ func (c *Client) ListOrgHooks(org string, opt ListHooksOptions) ([]*Hook, *Respo
// ListRepoHooks list all the hooks of one repository // ListRepoHooks list all the hooks of one repository
func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) { func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
hooks := make([]*Hook, 0, opt.PageSize) hooks := make([]*Hook, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &hooks)
@@ -47,6 +53,9 @@ func (c *Client) ListRepoHooks(user, repo string, opt ListHooksOptions) ([]*Hook
// GetOrgHook get a hook of an organization // GetOrgHook get a hook of an organization
func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) { func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
h := new(Hook) h := new(Hook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil, h)
return h, resp, err return h, resp, err
@@ -54,6 +63,9 @@ func (c *Client) GetOrgHook(org string, id int64) (*Hook, *Response, error) {
// GetRepoHook get a hook of a repository // GetRepoHook get a hook of a repository
func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) { func (c *Client) GetRepoHook(user, repo string, id int64) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
h := new(Hook) h := new(Hook)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil, h)
return h, resp, err return h, resp, err
@@ -78,6 +90,9 @@ func (opt CreateHookOption) Validate() error {
// CreateOrgHook create one hook for an organization, with options // CreateOrgHook create one hook for an organization, with options
func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) { func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -92,6 +107,9 @@ func (c *Client) CreateOrgHook(org string, opt CreateHookOption) (*Hook, *Respon
// CreateRepoHook create one hook for a repository, with options // CreateRepoHook create one hook for a repository, with options
func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) { func (c *Client) CreateRepoHook(user, repo string, opt CreateHookOption) (*Hook, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -111,6 +129,9 @@ type EditHookOption struct {
// EditOrgHook modify one hook of an organization, with hook id and options // EditOrgHook modify one hook of an organization, with hook id and options
func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) { func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -121,6 +142,9 @@ func (c *Client) EditOrgHook(org string, id int64, opt EditHookOption) (*Respons
// EditRepoHook modify one hook of a repository, with hook id and options // EditRepoHook modify one hook of a repository, with hook id and options
func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) { func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -131,12 +155,18 @@ func (c *Client) EditRepoHook(user, repo string, id int64, opt EditHookOption) (
// DeleteOrgHook delete one hook from an organization, with hook id // DeleteOrgHook delete one hook from an organization, with hook id
func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) { func (c *Client) DeleteOrgHook(org string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/hooks/%d", org, id), nil, nil)
return resp, err return resp, err
} }
// DeleteRepoHook delete one hook from a repository, with hook id // DeleteRepoHook delete one hook from a repository, with hook id
func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) { func (c *Client) DeleteRepoHook(user, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/hooks/%d", user, repo, id), nil, nil)
return resp, err return resp, err
} }

View File

@@ -32,14 +32,18 @@ type RepositoryMeta struct {
type Issue struct { type Issue struct {
ID int64 `json:"id"` ID int64 `json:"id"`
URL string `json:"url"` URL string `json:"url"`
HTMLURL string `json:"html_url"`
Index int64 `json:"number"` Index int64 `json:"number"`
Poster *User `json:"user"` Poster *User `json:"user"`
OriginalAuthor string `json:"original_author"` OriginalAuthor string `json:"original_author"`
OriginalAuthorID int64 `json:"original_author_id"` OriginalAuthorID int64 `json:"original_author_id"`
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
Ref string `json:"ref"`
Labels []*Label `json:"labels"` Labels []*Label `json:"labels"`
Milestone *Milestone `json:"milestone"` Milestone *Milestone `json:"milestone"`
// deprecated
// TODO: rm on sdk 0.15.0
Assignee *User `json:"assignee"` Assignee *User `json:"assignee"`
Assignees []*User `json:"assignees"` Assignees []*User `json:"assignees"`
// Whether the issue is open or closed // Whether the issue is open or closed
@@ -128,11 +132,17 @@ func (c *Client) ListIssues(opt ListIssueOption) ([]*Issue, *Response, error) {
} }
} }
} }
for i := range issues {
c.issueBackwardsCompatibility(issues[i])
}
return issues, resp, err return issues, resp, err
} }
// ListRepoIssues returns all issues for a given repository // ListRepoIssues returns all issues for a given repository
func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) { func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
issues := make([]*Issue, 0, opt.PageSize) issues := make([]*Issue, 0, opt.PageSize)
@@ -146,16 +156,23 @@ func (c *Client) ListRepoIssues(owner, repo string, opt ListIssueOption) ([]*Iss
} }
} }
} }
for i := range issues {
c.issueBackwardsCompatibility(issues[i])
}
return issues, resp, err return issues, resp, err
} }
// GetIssue returns a single issue for a given repository // GetIssue returns a single issue for a given repository
func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) { func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
issue := new(Issue) issue := new(Issue)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), nil, nil, issue)
if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil { if e := c.checkServerVersionGreaterThanOrEqual(version1_12_0); e != nil && issue.Repository != nil {
issue.Repository.Owner = strings.Split(issue.Repository.FullName, "/")[0] issue.Repository.Owner = strings.Split(issue.Repository.FullName, "/")[0]
} }
c.issueBackwardsCompatibility(issue)
return issue, resp, err return issue, resp, err
} }
@@ -163,7 +180,9 @@ func (c *Client) GetIssue(owner, repo string, index int64) (*Issue, *Response, e
type CreateIssueOption struct { type CreateIssueOption struct {
Title string `json:"title"` Title string `json:"title"`
Body string `json:"body"` Body string `json:"body"`
// username of assignee Ref string `json:"ref"`
// deprecated
// TODO: rm on sdk 0.15.0
Assignee string `json:"assignee"` Assignee string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
@@ -184,6 +203,9 @@ func (opt CreateIssueOption) Validate() error {
// CreateIssue create a new issue for a given repository // CreateIssue create a new issue for a given repository
func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) { func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -194,6 +216,7 @@ func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue,
issue := new(Issue) issue := new(Issue)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues", owner, repo), resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/issues", owner, repo),
jsonHeader, bytes.NewReader(body), issue) jsonHeader, bytes.NewReader(body), issue)
c.issueBackwardsCompatibility(issue)
return issue, resp, err return issue, resp, err
} }
@@ -201,11 +224,15 @@ func (c *Client) CreateIssue(owner, repo string, opt CreateIssueOption) (*Issue,
type EditIssueOption struct { type EditIssueOption struct {
Title string `json:"title"` Title string `json:"title"`
Body *string `json:"body"` Body *string `json:"body"`
Ref *string `json:"ref"`
// deprecated
// TODO: rm on sdk 0.15.0
Assignee *string `json:"assignee"` Assignee *string `json:"assignee"`
Assignees []string `json:"assignees"` Assignees []string `json:"assignees"`
Milestone *int64 `json:"milestone"` Milestone *int64 `json:"milestone"`
State *StateType `json:"state"` State *StateType `json:"state"`
Deadline *time.Time `json:"due_date"` Deadline *time.Time `json:"due_date"`
RemoveDeadline *bool `json:"unset_due_date"`
} }
// Validate the EditIssueOption struct // Validate the EditIssueOption struct
@@ -218,6 +245,9 @@ func (opt EditIssueOption) Validate() error {
// EditIssue modify an existing issue for a given repository // EditIssue modify an existing issue for a given repository
func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) { func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption) (*Issue, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -229,5 +259,14 @@ func (c *Client) EditIssue(owner, repo string, index int64, opt EditIssueOption)
resp, err := c.getParsedResponse("PATCH", resp, err := c.getParsedResponse("PATCH",
fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index), fmt.Sprintf("/repos/%s/%s/issues/%d", owner, repo, index),
jsonHeader, bytes.NewReader(body), issue) jsonHeader, bytes.NewReader(body), issue)
c.issueBackwardsCompatibility(issue)
return issue, resp, err return issue, resp, err
} }
func (c *Client) issueBackwardsCompatibility(issue *Issue) {
if c.checkServerVersionGreaterThanOrEqual(version1_12_0) != nil {
c.mutex.RLock()
issue.HTMLURL = fmt.Sprintf("%s/%s/issues/%d", c.url, issue.Repository.FullName, issue.Index)
c.mutex.RUnlock()
}
}

View File

@@ -47,6 +47,9 @@ func (opt *ListIssueCommentOptions) QueryEncode() string {
// ListIssueComments list comments on an issue. // ListIssueComments list comments on an issue.
func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) { func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/comments", owner, repo, index))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@@ -57,6 +60,9 @@ func (c *Client) ListIssueComments(owner, repo string, index int64, opt ListIssu
// ListRepoIssueComments list comments for a given repo. // ListRepoIssueComments list comments for a given repo.
func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) { func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentOptions) ([]*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/comments", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@@ -67,6 +73,9 @@ func (c *Client) ListRepoIssueComments(owner, repo string, opt ListIssueCommentO
// GetIssueComment get a comment for a given repo by id. // GetIssueComment get a comment for a given repo by id.
func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) { func (c *Client) GetIssueComment(owner, repo string, id int64) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
comment := new(Comment) comment := new(Comment)
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return comment, nil, err return comment, nil, err
@@ -90,6 +99,9 @@ func (opt CreateIssueCommentOption) Validate() error {
// CreateIssueComment create comment on an issue. // CreateIssueComment create comment on an issue.
func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) { func (c *Client) CreateIssueComment(owner, repo string, index int64, opt CreateIssueCommentOption) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -117,6 +129,9 @@ func (opt EditIssueCommentOption) Validate() error {
// EditIssueComment edits an issue comment. // EditIssueComment edits an issue comment.
func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) { func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditIssueCommentOption) (*Comment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -131,6 +146,9 @@ func (c *Client) EditIssueComment(owner, repo string, commentID int64, opt EditI
// DeleteIssueComment deletes an issue comment. // DeleteIssueComment deletes an issue comment.
func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) { func (c *Client) DeleteIssueComment(owner, repo string, commentID int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/comments/%d", owner, repo, commentID), nil, nil)
return resp, err return resp, err
} }

View File

@@ -29,6 +29,9 @@ type ListLabelsOptions struct {
// ListRepoLabels list labels of one repository // ListRepoLabels list labels of one repository
func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) { func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
labels := make([]*Label, 0, opt.PageSize) labels := make([]*Label, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels?%s", owner, repo, opt.getURLQuery().Encode()), nil, nil, &labels)
@@ -37,6 +40,9 @@ func (c *Client) ListRepoLabels(owner, repo string, opt ListLabelsOptions) ([]*L
// GetRepoLabel get one label of repository by repo it // GetRepoLabel get one label of repository by repo it
func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) { func (c *Client) GetRepoLabel(owner, repo string, id int64) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
label := new(Label) label := new(Label)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil, label)
return label, resp, err return label, resp, err
@@ -67,6 +73,9 @@ func (opt CreateLabelOption) Validate() error {
// CreateLabel create one label of repository // CreateLabel create one label of repository
func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) { func (c *Client) CreateLabel(owner, repo string, opt CreateLabelOption) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -114,6 +123,9 @@ func (opt EditLabelOption) Validate() error {
// EditLabel modify one label with options // EditLabel modify one label with options
func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) { func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -128,12 +140,18 @@ func (c *Client) EditLabel(owner, repo string, id int64, opt EditLabelOption) (*
// DeleteLabel delete one label of repository by id // DeleteLabel delete one label of repository by id
func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) { func (c *Client) DeleteLabel(owner, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/labels/%d", owner, repo, id), nil, nil)
return resp, err return resp, err
} }
// GetIssueLabels get labels of one issue via issue id // GetIssueLabels get labels of one issue via issue id
func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) { func (c *Client) GetIssueLabels(owner, repo string, index int64, opts ListLabelsOptions) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
labels := make([]*Label, 0, 5) labels := make([]*Label, 0, 5)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/labels?%s", owner, repo, index, opts.getURLQuery().Encode()), nil, nil, &labels)
return labels, resp, err return labels, resp, err
@@ -147,6 +165,9 @@ type IssueLabelsOption struct {
// AddIssueLabels add one or more labels to one issue // AddIssueLabels add one or more labels to one issue
func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) { func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -158,6 +179,9 @@ func (c *Client) AddIssueLabels(owner, repo string, index int64, opt IssueLabels
// ReplaceIssueLabels replace old labels of issue with new labels // ReplaceIssueLabels replace old labels of issue with new labels
func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) { func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLabelsOption) ([]*Label, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -170,12 +194,18 @@ func (c *Client) ReplaceIssueLabels(owner, repo string, index int64, opt IssueLa
// DeleteIssueLabel delete one label of one issue by issue id and label id // DeleteIssueLabel delete one label of one issue by issue id and label id
// TODO: maybe we need delete by label name and issue id // TODO: maybe we need delete by label name and issue id
func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) { func (c *Client) DeleteIssueLabel(owner, repo string, index, label int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels/%d", owner, repo, index, label), nil, nil)
return resp, err return resp, err
} }
// ClearIssueLabels delete all the labels of one issue. // ClearIssueLabels delete all the labels of one issue.
func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) { func (c *Client) ClearIssueLabels(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/labels", owner, repo, index), nil, nil)
return resp, err return resp, err
} }

View File

@@ -49,6 +49,9 @@ func (opt *ListMilestoneOption) QueryEncode() string {
// ListRepoMilestones list all the milestones of one repository // ListRepoMilestones list all the milestones of one repository
func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) { func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption) ([]*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
milestones := make([]*Milestone, 0, opt.PageSize) milestones := make([]*Milestone, 0, opt.PageSize)
@@ -60,6 +63,9 @@ func (c *Client) ListRepoMilestones(owner, repo string, opt ListMilestoneOption)
// GetMilestone get one milestone by repo name and milestone id // GetMilestone get one milestone by repo name and milestone id
func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) { func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
milestone := new(Milestone) milestone := new(Milestone)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil, milestone)
return milestone, resp, err return milestone, resp, err
@@ -72,6 +78,9 @@ func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone
m, resp, err := c.resolveMilestoneByName(owner, repo, name) m, resp, err := c.resolveMilestoneByName(owner, repo, name)
return m, resp, err return m, resp, err
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
milestone := new(Milestone) milestone := new(Milestone)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil, milestone)
return milestone, resp, err return milestone, resp, err
@@ -95,6 +104,9 @@ func (opt CreateMilestoneOption) Validate() error {
// CreateMilestone create one milestone with options // CreateMilestone create one milestone with options
func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) { func (c *Client) CreateMilestone(owner, repo string, opt CreateMilestoneOption) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -135,6 +147,9 @@ func (opt EditMilestoneOption) Validate() error {
// EditMilestone modify milestone with options // EditMilestone modify milestone with options
func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) { func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOption) (*Milestone, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -157,6 +172,9 @@ func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMi
} }
return c.EditMilestone(owner, repo, m.ID, opt) return c.EditMilestone(owner, repo, m.ID, opt)
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -171,6 +189,9 @@ func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMi
// DeleteMilestone delete one milestone by id // DeleteMilestone delete one milestone by id
func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) { func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%d", owner, repo, id), nil, nil)
return resp, err return resp, err
} }
@@ -185,6 +206,9 @@ func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Respon
} }
return c.DeleteMilestone(owner, repo, m.ID) return c.DeleteMilestone(owner, repo, m.ID)
} }
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/milestones/%s", owner, repo, name), nil, nil)
return resp, err return resp, err
} }

View File

@@ -20,7 +20,7 @@ type Reaction struct {
// GetIssueReactions get a list reactions of an issue // GetIssueReactions get a list reactions of an issue
func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) { func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
reactions := make([]*Reaction, 0, 10) reactions := make([]*Reaction, 0, 10)
@@ -30,7 +30,7 @@ func (c *Client) GetIssueReactions(owner, repo string, index int64) ([]*Reaction
// GetIssueCommentReactions get a list of reactions from a comment of an issue // GetIssueCommentReactions get a list of reactions from a comment of an issue
func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) { func (c *Client) GetIssueCommentReactions(owner, repo string, commentID int64) ([]*Reaction, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
reactions := make([]*Reaction, 0, 10) reactions := make([]*Reaction, 0, 10)
@@ -45,7 +45,7 @@ type editReactionOption struct {
// PostIssueReaction add a reaction to an issue // PostIssueReaction add a reaction to an issue
func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) { func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction string) (*Reaction, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
reactionResponse := new(Reaction) reactionResponse := new(Reaction)
@@ -61,7 +61,7 @@ func (c *Client) PostIssueReaction(owner, repo string, index int64, reaction str
// DeleteIssueReaction remove a reaction from an issue // DeleteIssueReaction remove a reaction from an issue
func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) { func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err return nil, err
} }
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})
@@ -74,7 +74,7 @@ func (c *Client) DeleteIssueReaction(owner, repo string, index int64, reaction s
// PostIssueCommentReaction add a reaction to a comment of an issue // PostIssueCommentReaction add a reaction to a comment of an issue
func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) { func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Reaction, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
reactionResponse := new(Reaction) reactionResponse := new(Reaction)
@@ -90,7 +90,7 @@ func (c *Client) PostIssueCommentReaction(owner, repo string, commentID int64, r
// DeleteIssueCommentReaction remove a reaction from a comment of an issue // DeleteIssueCommentReaction remove a reaction from a comment of an issue
func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) { func (c *Client) DeleteIssueCommentReaction(owner, repo string, commentID int64, reaction string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err return nil, err
} }
body, err := json.Marshal(&editReactionOption{Reaction: reaction}) body, err := json.Marshal(&editReactionOption{Reaction: reaction})

View File

@@ -12,7 +12,12 @@ import (
// StopWatch represents a running stopwatch of an issue / pr // StopWatch represents a running stopwatch of an issue / pr
type StopWatch struct { type StopWatch struct {
Created time.Time `json:"created"` Created time.Time `json:"created"`
Seconds int64 `json:"seconds"`
Duration string `json:"duration"`
IssueIndex int64 `json:"issue_index"` IssueIndex int64 `json:"issue_index"`
IssueTitle string `json:"issue_title"`
RepoOwnerName string `json:"repo_owner_name"`
RepoName string `json:"repo_name"`
} }
// GetMyStopwatches list all stopwatches // GetMyStopwatches list all stopwatches
@@ -24,6 +29,9 @@ func (c *Client) GetMyStopwatches() ([]*StopWatch, *Response, error) {
// DeleteIssueStopwatch delete / cancel a specific stopwatch // DeleteIssueStopwatch delete / cancel a specific stopwatch
func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) { func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/delete", owner, repo, index), nil, nil)
return resp, err return resp, err
} }
@@ -31,6 +39,9 @@ func (c *Client) DeleteIssueStopwatch(owner, repo string, index int64) (*Respons
// StartIssueStopWatch starts a stopwatch for an existing issue for a given // StartIssueStopWatch starts a stopwatch for an existing issue for a given
// repository // repository
func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) { func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/start", owner, repo, index), nil, nil)
return resp, err return resp, err
} }
@@ -38,6 +49,9 @@ func (c *Client) StartIssueStopWatch(owner, repo string, index int64) (*Response
// StopIssueStopWatch stops an existing stopwatch for an issue in a given // StopIssueStopWatch stops an existing stopwatch for an issue in a given
// repository // repository
func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) { func (c *Client) StopIssueStopWatch(owner, repo string, index int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/issues/%d/stopwatch/stop", owner, repo, index), nil, nil)
return resp, err return resp, err
} }

View File

@@ -11,7 +11,7 @@ import (
// GetIssueSubscribers get list of users who subscribed on an issue // GetIssueSubscribers get list of users who subscribed on an issue
func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) { func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
subscribers := make([]*User, 0, 10) subscribers := make([]*User, 0, 10)
@@ -21,7 +21,7 @@ func (c *Client) GetIssueSubscribers(owner, repo string, index int64) ([]*User,
// AddIssueSubscription Subscribe user to issue // AddIssueSubscription Subscribe user to issue
func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) { func (c *Client) AddIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
return nil, err return nil, err
} }
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil) status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
@@ -39,7 +39,7 @@ func (c *Client) AddIssueSubscription(owner, repo string, index int64, user stri
// DeleteIssueSubscription unsubscribe user from issue // DeleteIssueSubscription unsubscribe user from issue
func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) { func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user string) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo, &user); err != nil {
return nil, err return nil, err
} }
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil) status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/subscriptions/%s", owner, repo, index, user), nil, nil)
@@ -57,6 +57,9 @@ func (c *Client) DeleteIssueSubscription(owner, repo string, index int64, user s
// CheckIssueSubscription check if current user is subscribed to an issue // CheckIssueSubscription check if current user is subscribed to an issue
func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) { func (c *Client) CheckIssueSubscription(owner, repo string, index int64) (*WatchInfo, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@@ -25,33 +26,50 @@ type TrackedTime struct {
Issue *Issue `json:"issue"` Issue *Issue `json:"issue"`
} }
// GetUserTrackedTimes list tracked times of a user // ListTrackedTimesOptions options for listing repository's tracked times
func (c *Client) GetUserTrackedTimes(owner, repo, user string) ([]*TrackedTime, *Response, error) { type ListTrackedTimesOptions struct {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { ListOptions
return nil, nil, err Since time.Time
} Before time.Time
times := make([]*TrackedTime, 0, 10) // User filter is only used by ListRepoTrackedTimes !!!
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/times/%s", owner, repo, user), nil, nil, &times) User string
return times, resp, err
} }
// GetRepoTrackedTimes list tracked times of a repository // QueryEncode turns options into querystring argument
func (c *Client) GetRepoTrackedTimes(owner, repo string) ([]*TrackedTime, *Response, error) { func (opt *ListTrackedTimesOptions) QueryEncode() string {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { query := opt.getURLQuery()
if !opt.Since.IsZero() {
query.Add("since", opt.Since.Format(time.RFC3339))
}
if !opt.Before.IsZero() {
query.Add("before", opt.Before.Format(time.RFC3339))
}
if len(opt.User) != 0 {
query.Add("user", opt.User)
}
return query.Encode()
}
// ListRepoTrackedTimes list tracked times of a repository
func (c *Client) ListRepoTrackedTimes(owner, repo string, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
times := make([]*TrackedTime, 0, 10) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/times", owner, repo))
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/times", owner, repo), nil, nil, &times) opt.setDefaults()
link.RawQuery = opt.QueryEncode()
times := make([]*TrackedTime, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &times)
return times, resp, err return times, resp, err
} }
// GetMyTrackedTimes list tracked times of the current user // GetMyTrackedTimes list tracked times of the current user
func (c *Client) GetMyTrackedTimes() ([]*TrackedTime, *Response, error) { func (c *Client) GetMyTrackedTimes() ([]*TrackedTime, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
return nil, nil, err
}
times := make([]*TrackedTime, 0, 10) times := make([]*TrackedTime, 0, 10)
resp, err := c.getParsedResponse("GET", "/user/times", nil, nil, &times) resp, err := c.getParsedResponse("GET", "/user/times", jsonHeader, nil, &times)
return times, resp, err return times, resp, err
} }
@@ -75,7 +93,7 @@ func (opt AddTimeOption) Validate() error {
// AddTime adds time to issue with the given index // AddTime adds time to issue with the given index
func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) { func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*TrackedTime, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
@@ -92,36 +110,33 @@ func (c *Client) AddTime(owner, repo string, index int64, opt AddTimeOption) (*T
return t, resp, err return t, resp, err
} }
// ListTrackedTimesOptions options for listing repository's tracked times // ListIssueTrackedTimes list tracked times of a single issue for a given repository
type ListTrackedTimesOptions struct { func (c *Client) ListIssueTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
ListOptions if err := escapeValidatePathSegments(&owner, &repo); err != nil {
}
// ListTrackedTimes list tracked times of a single issue for a given repository
func (c *Client) ListTrackedTimes(owner, repo string, index int64, opt ListTrackedTimesOptions) ([]*TrackedTime, *Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
return nil, nil, err return nil, nil, err
} }
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index))
opt.setDefaults() opt.setDefaults()
link.RawQuery = opt.QueryEncode()
times := make([]*TrackedTime, 0, opt.PageSize) times := make([]*TrackedTime, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/issues/%d/times?%s", owner, repo, index, opt.getURLQuery().Encode()), nil, nil, &times) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &times)
return times, resp, err return times, resp, err
} }
// ResetIssueTime reset tracked time of a single issue for a given repository // ResetIssueTime reset tracked time of a single issue for a given repository
func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) { func (c *Client) ResetIssueTime(owner, repo string, index int64) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err return nil, err
} }
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times", owner, repo, index), jsonHeader, nil)
return resp, err return resp, err
} }
// DeleteTime delete a specific tracked time by id of a single issue for a given repository // DeleteTime delete a specific tracked time by id of a single issue for a given repository
func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) { func (c *Client) DeleteTime(owner, repo string, index, timeID int64) (*Response, error) {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil { if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err return nil, err
} }
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/issues/%d/times/%d", owner, repo, index, timeID), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@@ -176,14 +176,17 @@ func (c *Client) ReadNotifications(opt MarkNotificationOptions) (*Response, erro
} }
// ListRepoNotifications list users's notification threads on a specific repo // ListRepoNotifications list users's notification threads on a specific repo
func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) { func (c *Client) ListRepoNotifications(owner, repo string, opt ListNotificationOptions) ([]*NotificationThread, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10) threads := make([]*NotificationThread, 0, 10)
resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads) resp, err := c.getParsedResponse("GET", link.String(), nil, nil, &threads)
@@ -191,14 +194,17 @@ func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificat
} }
// ReadRepoNotifications mark notification threads as read on a specific repo // ReadRepoNotifications mark notification threads as read on a specific repo
func (c *Client) ReadRepoNotifications(owner, reponame string, opt MarkNotificationOptions) (*Response, error) { func (c *Client) ReadRepoNotifications(owner, repo string, opt MarkNotificationOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, err return nil, err
} }
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
_, resp, err := c.getResponse("PUT", link.String(), nil, nil) _, resp, err := c.getResponse("PUT", link.String(), nil, nil)
return resp, err return resp, err

View File

@@ -52,6 +52,9 @@ func (c *Client) ListMyOrgs(opt ListOrgsOptions) ([]*Organization, *Response, er
// ListUserOrgs list all of some user's organizations // ListUserOrgs list all of some user's organizations
func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) { func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
orgs := make([]*Organization, 0, opt.PageSize) orgs := make([]*Organization, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/orgs?%s", user, opt.getURLQuery().Encode()), nil, nil, &orgs)
@@ -60,6 +63,9 @@ func (c *Client) ListUserOrgs(user string, opt ListOrgsOptions) ([]*Organization
// GetOrg get one organization by name // GetOrg get one organization by name
func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) { func (c *Client) GetOrg(orgname string) (*Organization, *Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, nil, err
}
org := new(Organization) org := new(Organization)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s", orgname), nil, nil, org)
return org, resp, err return org, resp, err
@@ -124,6 +130,9 @@ func (opt EditOrgOption) Validate() error {
// EditOrg modify one organization via options // EditOrg modify one organization via options
func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) { func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, err return nil, err
} }
@@ -137,6 +146,9 @@ func (c *Client) EditOrg(orgname string, opt EditOrgOption) (*Response, error) {
// DeleteOrg deletes an organization // DeleteOrg deletes an organization
func (c *Client) DeleteOrg(orgname string) (*Response, error) { func (c *Client) DeleteOrg(orgname string) (*Response, error) {
if err := escapeValidatePathSegments(&orgname); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s", orgname), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@@ -12,7 +12,10 @@ import (
// DeleteOrgMembership remove a member from an organization // DeleteOrgMembership remove a member from an organization
func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) { func (c *Client) DeleteOrgMembership(org, user string) (*Response, error) {
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
return resp, err return resp, err
} }
@@ -23,10 +26,13 @@ type ListOrgMembershipOption struct {
// ListOrgMembership list an organization's members // ListOrgMembership list an organization's members
func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) { func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", url.PathEscape(org))) link, _ := url.Parse(fmt.Sprintf("/orgs/%s/members", org))
link.RawQuery = opt.getURLQuery().Encode() link.RawQuery = opt.getURLQuery().Encode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
return users, resp, err return users, resp, err
@@ -34,10 +40,13 @@ func (c *Client) ListOrgMembership(org string, opt ListOrgMembershipOption) ([]*
// ListPublicOrgMembership list an organization's members // ListPublicOrgMembership list an organization's members
func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) { func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", url.PathEscape(org))) link, _ := url.Parse(fmt.Sprintf("/orgs/%s/public_members", org))
link.RawQuery = opt.getURLQuery().Encode() link.RawQuery = opt.getURLQuery().Encode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &users)
return users, resp, err return users, resp, err
@@ -45,7 +54,10 @@ func (c *Client) ListPublicOrgMembership(org string, opt ListOrgMembershipOption
// CheckOrgMembership Check if a user is a member of an organization // CheckOrgMembership Check if a user is a member of an organization
func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) { func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/members/%s", org, user), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@@ -61,7 +73,10 @@ func (c *Client) CheckOrgMembership(org, user string) (bool, *Response, error) {
// CheckPublicOrgMembership Check if a user is a member of an organization // CheckPublicOrgMembership Check if a user is a member of an organization
func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) { func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) if err := escapeValidatePathSegments(&org, &user); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@@ -77,15 +92,18 @@ func (c *Client) CheckPublicOrgMembership(org, user string) (bool, *Response, er
// SetPublicOrgMembership publicize/conceal a user's membership // SetPublicOrgMembership publicize/conceal a user's membership
func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) { func (c *Client) SetPublicOrgMembership(org, user string, visible bool) (*Response, error) {
if err := escapeValidatePathSegments(&org, &user); err != nil {
return nil, err
}
var ( var (
status int status int
err error err error
resp *Response resp *Response
) )
if visible { if visible {
status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) status, resp, err = c.getStatusCode("PUT", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
} else { } else {
status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", url.PathEscape(org), url.PathEscape(user)), nil, nil) status, resp, err = c.getStatusCode("DELETE", fmt.Sprintf("/orgs/%s/public_members/%s", org, user), nil, nil)
} }
if err != nil { if err != nil {
return resp, err return resp, err

View File

@@ -30,6 +30,9 @@ type ListTeamsOptions struct {
// ListOrgTeams lists all teams of an organization // ListOrgTeams lists all teams of an organization
func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) { func (c *Client) ListOrgTeams(org string, opt ListTeamsOptions) ([]*Team, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
teams := make([]*Team, 0, opt.PageSize) teams := make([]*Team, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/teams?%s", org, opt.getURLQuery().Encode()), nil, nil, &teams)
@@ -83,6 +86,9 @@ func (opt CreateTeamOption) Validate() error {
// CreateTeam creates a team for an organization // CreateTeam creates a team for an organization
func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) { func (c *Client) CreateTeam(org string, opt CreateTeamOption) (*Team, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -159,6 +165,9 @@ func (c *Client) ListTeamMembers(id int64, opt ListTeamMembersOptions) ([]*User,
// GetTeamMember gets a member of a team // GetTeamMember gets a member of a team
func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) { func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
m := new(User) m := new(User)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil, m)
return m, resp, err return m, resp, err
@@ -166,12 +175,18 @@ func (c *Client) GetTeamMember(id int64, user string) (*User, *Response, error)
// AddTeamMember adds a member to a team // AddTeamMember adds a member to a team
func (c *Client) AddTeamMember(id int64, user string) (*Response, error) { func (c *Client) AddTeamMember(id int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
return resp, err return resp, err
} }
// RemoveTeamMember removes a member from a team // RemoveTeamMember removes a member from a team
func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) { func (c *Client) RemoveTeamMember(id int64, user string) (*Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/members/%s", id, user), nil, nil)
return resp, err return resp, err
} }
@@ -191,12 +206,18 @@ func (c *Client) ListTeamRepositories(id int64, opt ListTeamRepositoriesOptions)
// AddTeamRepository adds a repository to a team // AddTeamRepository adds a repository to a team
func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) { func (c *Client) AddTeamRepository(id int64, org, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&org, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
return resp, err return resp, err
} }
// RemoveTeamRepository removes a repository from a team // RemoveTeamRepository removes a repository from a team
func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) { func (c *Client) RemoveTeamRepository(id int64, org, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&org, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/teams/%d/repos/%s/%s", id, org, repo), nil, nil)
return resp, err return resp, err
} }

View File

@@ -99,19 +99,37 @@ func (opt *ListPullRequestsOptions) QueryEncode() string {
// ListRepoPullRequests list PRs of one repository // ListRepoPullRequests list PRs of one repository
func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) { func (c *Client) ListRepoPullRequests(owner, repo string, opt ListPullRequestsOptions) ([]*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
prs := make([]*PullRequest, 0, opt.PageSize) prs := make([]*PullRequest, 0, opt.PageSize)
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/pulls", owner, repo))
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs) resp, err := c.getParsedResponse("GET", link.String(), jsonHeader, nil, &prs)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
for i := range prs {
if err := fixPullHeadSha(c, prs[i]); err != nil {
return prs, resp, err
}
}
}
return prs, resp, err return prs, resp, err
} }
// GetPullRequest get information of one PR // GetPullRequest get information of one PR
func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) { func (c *Client) GetPullRequest(owner, repo string, index int64) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
pr := new(PullRequest) pr := new(PullRequest)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d", owner, repo, index), nil, nil, pr)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
if err := fixPullHeadSha(c, pr); err != nil {
return pr, resp, err
}
}
return pr, resp, err return pr, resp, err
} }
@@ -130,6 +148,9 @@ type CreatePullRequestOption struct {
// CreatePullRequest create pull request with options // CreatePullRequest create pull request with options
func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) { func (c *Client) CreatePullRequest(owner, repo string, opt CreatePullRequestOption) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -169,6 +190,9 @@ func (opt EditPullRequestOption) Validate(c *Client) error {
// EditPullRequest modify pull request with PR id and options // EditPullRequest modify pull request with PR id and options
func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) { func (c *Client) EditPullRequest(owner, repo string, index int64, opt EditPullRequestOption) (*PullRequest, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -202,6 +226,9 @@ func (opt MergePullRequestOption) Validate(c *Client) error {
// MergePullRequest merge a PR to repository by PR id // MergePullRequest merge a PR to repository by PR id
func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) { func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePullRequestOption) (bool, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return false, nil, err return false, nil, err
} }
@@ -218,6 +245,9 @@ func (c *Client) MergePullRequest(owner, repo string, index int64, opt MergePull
// IsPullRequestMerged test if one PR is merged to one repository // IsPullRequestMerged test if one PR is merged to one repository
func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) { func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil) status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/pulls/%d/merge", owner, repo, index), nil, nil)
if err != nil { if err != nil {
@@ -229,6 +259,9 @@ func (c *Client) IsPullRequestMerged(owner, repo string, index int64) (bool, *Re
// getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR // getPullRequestDiffOrPatch gets the patch or diff file as bytes for a PR
func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) { func (c *Client) getPullRequestDiffOrPatch(owner, repo, kind string, index int64) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &kind); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
r, _, err2 := c.GetRepo(owner, repo) r, _, err2 := c.GetRepo(owner, repo)
if err2 != nil { if err2 != nil {
@@ -251,3 +284,23 @@ func (c *Client) GetPullRequestPatch(owner, repo string, index int64) ([]byte, *
func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) { func (c *Client) GetPullRequestDiff(owner, repo string, index int64) ([]byte, *Response, error) {
return c.getPullRequestDiffOrPatch(owner, repo, "diff", index) return c.getPullRequestDiffOrPatch(owner, repo, "diff", index)
} }
// fixPullHeadSha is a workaround for https://github.com/go-gitea/gitea/issues/12675
// When no head sha is available, this is because the branch got deleted in the base repo.
// pr.Head.Ref points in this case not to the head repo branch name, but the base repo ref,
// which stays available to resolve the commit sha. This is fixed for gitea >= 1.14.0
func fixPullHeadSha(client *Client, pr *PullRequest) error {
if pr.Base != nil && pr.Base.Repository != nil && pr.Base.Repository.Owner != nil &&
pr.Head != nil && pr.Head.Ref != "" && pr.Head.Sha == "" {
owner := pr.Base.Repository.Owner.UserName
repo := pr.Base.Repository.Name
refs, _, err := client.GetRepoRefs(owner, repo, pr.Head.Ref)
if err != nil {
return err
} else if len(refs) == 0 {
return fmt.Errorf("unable to resolve PR ref '%s'", pr.Head.Ref)
}
pr.Head.Sha = refs[0].Object.SHA
}
return nil
}

View File

@@ -35,11 +35,15 @@ const (
type PullReview struct { type PullReview struct {
ID int64 `json:"id"` ID int64 `json:"id"`
Reviewer *User `json:"user"` Reviewer *User `json:"user"`
ReviewerTeam *Team `json:"team"`
State ReviewStateType `json:"state"` State ReviewStateType `json:"state"`
Body string `json:"body"` Body string `json:"body"`
CommitID string `json:"commit_id"` CommitID string `json:"commit_id"`
// Stale indicates if the pull has changed since the review
Stale bool `json:"stale"` Stale bool `json:"stale"`
// Official indicates if the review counts towards the required approval limit, if PR base is a protected branch
Official bool `json:"official"` Official bool `json:"official"`
Dismissed bool `json:"dismissed"`
CodeCommentsCount int `json:"comments_count"` CodeCommentsCount int `json:"comments_count"`
Submitted time.Time `json:"submitted_at"` Submitted time.Time `json:"submitted_at"`
@@ -93,6 +97,17 @@ type SubmitPullReviewOptions struct {
Body string `json:"body"` Body string `json:"body"`
} }
// DismissPullReviewOptions are options to dismiss a pull review
type DismissPullReviewOptions struct {
Message string `json:"message"`
}
// PullReviewRequestOptions are options to add or remove pull review requests
type PullReviewRequestOptions struct {
Reviewers []string `json:"reviewers"`
TeamReviewers []string `json:"team_reviewers"`
}
// ListPullReviewsOptions options for listing PullReviews // ListPullReviewsOptions options for listing PullReviews
type ListPullReviewsOptions struct { type ListPullReviewsOptions struct {
ListOptions ListOptions
@@ -132,6 +147,9 @@ func (opt CreatePullReviewComment) Validate() error {
// ListPullReviews lists all reviews of a pull request // ListPullReviews lists all reviews of a pull request
func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) { func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullReviewsOptions) ([]*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -147,6 +165,9 @@ func (c *Client) ListPullReviews(owner, repo string, index int64, opt ListPullRe
// GetPullReview gets a specific review of a pull request // GetPullReview gets a specific review of a pull request
func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) { func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -158,6 +179,9 @@ func (c *Client) GetPullReview(owner, repo string, index, id int64) (*PullReview
// ListPullReviewComments lists all comments of a pull request review // ListPullReviewComments lists all comments of a pull request review
func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) { func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]*PullReviewComment, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -170,6 +194,9 @@ func (c *Client) ListPullReviewComments(owner, repo string, index, id int64) ([]
// DeletePullReview delete a specific review from a pull request // DeletePullReview delete a specific review from a pull request
func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) { func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }
@@ -180,6 +207,9 @@ func (c *Client) DeletePullReview(owner, repo string, index, id int64) (*Respons
// CreatePullReview create a review to an pull request // CreatePullReview create a review to an pull request
func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) { func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePullReviewOptions) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -200,6 +230,9 @@ func (c *Client) CreatePullReview(owner, repo string, index int64, opt CreatePul
// SubmitPullReview submit a pending review to an pull request // SubmitPullReview submit a pending review to an pull request
func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) { func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt SubmitPullReviewOptions) (*PullReview, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -217,3 +250,75 @@ func (c *Client) SubmitPullReview(owner, repo string, index, id int64, opt Submi
jsonHeader, bytes.NewReader(body), r) jsonHeader, bytes.NewReader(body), r)
return r, resp, err return r, resp, err
} }
// CreateReviewRequests create review requests to an pull request
func (c *Client) CreateReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST",
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
jsonHeader, bytes.NewReader(body))
return resp, err
}
// DeleteReviewRequests delete review requests to an pull request
func (c *Client) DeleteReviewRequests(owner, repo string, index int64, opt PullReviewRequestOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, index),
jsonHeader, bytes.NewReader(body))
return resp, err
}
// DismissPullReview dismiss a review for a pull request
func (c *Client) DismissPullReview(owner, repo string, index, id int64, opt DismissPullReviewOptions) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
body, err := json.Marshal(&opt)
if err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST",
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/dismissals", owner, repo, index, id),
jsonHeader, bytes.NewReader(body))
return resp, err
}
// UnDismissPullReview cancel to dismiss a review for a pull request
func (c *Client) UnDismissPullReview(owner, repo string, index, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST",
fmt.Sprintf("/repos/%s/%s/pulls/%d/reviews/%d/undismissals", owner, repo, index, id),
jsonHeader, nil)
return resp, err
}

View File

@@ -21,6 +21,7 @@ type Release struct {
Title string `json:"name"` Title string `json:"name"`
Note string `json:"body"` Note string `json:"body"`
URL string `json:"url"` URL string `json:"url"`
HTMLURL string `json:"html_url"`
TarURL string `json:"tarball_url"` TarURL string `json:"tarball_url"`
ZipURL string `json:"zipball_url"` ZipURL string `json:"zipball_url"`
IsDraft bool `json:"draft"` IsDraft bool `json:"draft"`
@@ -37,32 +38,41 @@ type ListReleasesOptions struct {
} }
// ListReleases list releases of a repository // ListReleases list releases of a repository
func (c *Client) ListReleases(user, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) { func (c *Client) ListReleases(owner, repo string, opt ListReleasesOptions) ([]*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
releases := make([]*Release, 0, opt.PageSize) releases := make([]*Release, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases?%s", user, repo, opt.getURLQuery().Encode()), fmt.Sprintf("/repos/%s/%s/releases?%s", owner, repo, opt.getURLQuery().Encode()),
nil, nil, &releases) nil, nil, &releases)
return releases, resp, err return releases, resp, err
} }
// GetRelease get a release of a repository by id // GetRelease get a release of a repository by id
func (c *Client) GetRelease(user, repo string, id int64) (*Release, *Response, error) { func (c *Client) GetRelease(owner, repo string, id int64) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
jsonHeader, nil, &r) jsonHeader, nil, &r)
return r, resp, err return r, resp, err
} }
// GetReleaseByTag get a release of a repository by tag // GetReleaseByTag get a release of a repository by tag
func (c *Client) GetReleaseByTag(user, repo string, tag string) (*Release, *Response, error) { func (c *Client) GetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
return c.fallbackGetReleaseByTag(user, repo, tag) return c.fallbackGetReleaseByTag(owner, repo, tag)
}
if err := escapeValidatePathSegments(&owner, &repo, &tag); err != nil {
return nil, nil, err
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", user, repo, tag), fmt.Sprintf("/repos/%s/%s/releases/tags/%s", owner, repo, tag),
nil, nil, &r) nil, nil, &r)
return r, resp, err return r, resp, err
} }
@@ -86,7 +96,10 @@ func (opt CreateReleaseOption) Validate() error {
} }
// CreateRelease create a release // CreateRelease create a release
func (c *Client) CreateRelease(user, repo string, opt CreateReleaseOption) (*Release, *Response, error) { func (c *Client) CreateRelease(owner, repo string, opt CreateReleaseOption) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -96,7 +109,7 @@ func (c *Client) CreateRelease(user, repo string, opt CreateReleaseOption) (*Rel
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("POST", resp, err := c.getParsedResponse("POST",
fmt.Sprintf("/repos/%s/%s/releases", user, repo), fmt.Sprintf("/repos/%s/%s/releases", owner, repo),
jsonHeader, bytes.NewReader(body), r) jsonHeader, bytes.NewReader(body), r)
return r, resp, err return r, resp, err
} }
@@ -112,30 +125,50 @@ type EditReleaseOption struct {
} }
// EditRelease edit a release // EditRelease edit a release
func (c *Client) EditRelease(user, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) { func (c *Client) EditRelease(owner, repo string, id int64, form EditReleaseOption) (*Release, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(form) body, err := json.Marshal(form)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
r := new(Release) r := new(Release)
resp, err := c.getParsedResponse("PATCH", resp, err := c.getParsedResponse("PATCH",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", owner, repo, id),
jsonHeader, bytes.NewReader(body), r) jsonHeader, bytes.NewReader(body), r)
return r, resp, err return r, resp, err
} }
// DeleteRelease delete a release from a repository // DeleteRelease delete a release from a repository, keeping its tag
func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) { func (c *Client) DeleteRelease(user, repo string, id int64) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", _, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id), fmt.Sprintf("/repos/%s/%s/releases/%d", user, repo, id),
nil, nil) nil, nil)
return resp, err return resp, err
} }
// DeleteReleaseByTag deletes a release frm a repository by tag
func (c *Client) DeleteReleaseByTag(user, repo string, tag string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/releases/tags/%s", user, repo, tag),
nil, nil)
return resp, err
}
// fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 ) // fallbackGetReleaseByTag is fallback for old gitea installations ( < 1.13.0 )
func (c *Client) fallbackGetReleaseByTag(user, repo string, tag string) (*Release, *Response, error) { func (c *Client) fallbackGetReleaseByTag(owner, repo string, tag string) (*Release, *Response, error) {
for i := 1; ; i++ { for i := 1; ; i++ {
rl, resp, err := c.ListReleases(user, repo, ListReleasesOptions{ListOptions{Page: i}}) rl, resp, err := c.ListReleases(owner, repo, ListReleasesOptions{ListOptions{Page: i}})
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }

View File

@@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/url" "net/url"
"strings" "strings"
"time" "time"
@@ -21,6 +22,32 @@ type Permission struct {
Pull bool `json:"pull"` Pull bool `json:"pull"`
} }
// InternalTracker represents settings for internal tracker
type InternalTracker struct {
// Enable time tracking (Built-in issue tracker)
EnableTimeTracker bool `json:"enable_time_tracker"`
// Let only contributors track time (Built-in issue tracker)
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
// Enable dependencies for issues and pull requests (Built-in issue tracker)
EnableIssueDependencies bool `json:"enable_issue_dependencies"`
}
// ExternalTracker represents settings for external tracker
type ExternalTracker struct {
// URL of external issue tracker.
ExternalTrackerURL string `json:"external_tracker_url"`
// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
ExternalTrackerFormat string `json:"external_tracker_format"`
// External Issue Tracker Number Format, either `numeric` or `alphanumeric`
ExternalTrackerStyle string `json:"external_tracker_style"`
}
// ExternalWiki represents setting for external wiki
type ExternalWiki struct {
// URL of external wiki.
ExternalWikiURL string `json:"external_wiki_url"`
}
// Repository represents a repository // Repository represents a repository
type Repository struct { type Repository struct {
ID int64 `json:"id"` ID int64 `json:"id"`
@@ -31,6 +58,7 @@ type Repository struct {
Empty bool `json:"empty"` Empty bool `json:"empty"`
Private bool `json:"private"` Private bool `json:"private"`
Fork bool `json:"fork"` Fork bool `json:"fork"`
Template bool `json:"template"`
Parent *Repository `json:"parent"` Parent *Repository `json:"parent"`
Mirror bool `json:"mirror"` Mirror bool `json:"mirror"`
Size int `json:"size"` Size int `json:"size"`
@@ -43,20 +71,28 @@ type Repository struct {
Forks int `json:"forks_count"` Forks int `json:"forks_count"`
Watchers int `json:"watchers_count"` Watchers int `json:"watchers_count"`
OpenIssues int `json:"open_issues_count"` OpenIssues int `json:"open_issues_count"`
OpenPulls int `json:"open_pr_counter"`
Releases int `json:"release_counter"`
DefaultBranch string `json:"default_branch"` DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"` Archived bool `json:"archived"`
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
Updated time.Time `json:"updated_at"` Updated time.Time `json:"updated_at"`
Permissions *Permission `json:"permissions,omitempty"` Permissions *Permission `json:"permissions,omitempty"`
HasIssues bool `json:"has_issues"` HasIssues bool `json:"has_issues"`
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
HasWiki bool `json:"has_wiki"` HasWiki bool `json:"has_wiki"`
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
HasPullRequests bool `json:"has_pull_requests"` HasPullRequests bool `json:"has_pull_requests"`
HasProjects bool `json:"has_projects"`
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
AllowMerge bool `json:"allow_merge_commits"` AllowMerge bool `json:"allow_merge_commits"`
AllowRebase bool `json:"allow_rebase"` AllowRebase bool `json:"allow_rebase"`
AllowRebaseMerge bool `json:"allow_rebase_explicit"` AllowRebaseMerge bool `json:"allow_rebase_explicit"`
AllowSquash bool `json:"allow_squash_merge"` AllowSquash bool `json:"allow_squash_merge"`
AvatarURL string `json:"avatar_url"` AvatarURL string `json:"avatar_url"`
Internal bool `json:"internal"`
MirrorInterval string `json:"mirror_interval"`
} }
// RepoType represent repo type // RepoType represent repo type
@@ -102,6 +138,9 @@ func (c *Client) ListMyRepos(opt ListReposOptions) ([]*Repository, *Response, er
// ListUserRepos list all repositories of one user by user's name // ListUserRepos list all repositories of one user by user's name
func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) { func (c *Client) ListUserRepos(user string, opt ListReposOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
repos := make([]*Repository, 0, opt.PageSize) repos := make([]*Repository, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/repos?%s", user, opt.getURLQuery().Encode()), nil, nil, &repos)
@@ -115,6 +154,9 @@ type ListOrgReposOptions struct {
// ListOrgRepos list all repositories of one organization by organization's name // ListOrgRepos list all repositories of one organization by organization's name
func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) { func (c *Client) ListOrgRepos(org string, opt ListOrgReposOptions) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
repos := make([]*Repository, 0, opt.PageSize) repos := make([]*Repository, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/orgs/%s/repos?%s", org, opt.getURLQuery().Encode()), nil, nil, &repos)
@@ -315,6 +357,9 @@ func (c *Client) CreateRepo(opt CreateRepoOption) (*Repository, *Response, error
// CreateOrgRepo creates an organization repository for authenticated user. // CreateOrgRepo creates an organization repository for authenticated user.
func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) { func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&org); err != nil {
return nil, nil, err
}
if err := opt.Validate(c); err != nil { if err := opt.Validate(c); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -329,6 +374,9 @@ func (c *Client) CreateOrgRepo(org string, opt CreateRepoOption) (*Repository, *
// GetRepo returns information of a repository of given owner. // GetRepo returns information of a repository of given owner.
func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) { func (c *Client) GetRepo(owner, reponame string) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
repo := new(Repository) repo := new(Repository)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s", owner, reponame), nil, nil, repo)
return repo, resp, err return repo, resp, err
@@ -346,14 +394,24 @@ type EditRepoOption struct {
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization // Note: you will get a 422 error if the organization restricts changing repository visibility to organization
// owners and a non-owner tries to change the value of private. // owners and a non-owner tries to change the value of private.
Private *bool `json:"private,omitempty"` Private *bool `json:"private,omitempty"`
// either `true` to make this repository a template or `false` to make it a normal repository
Template *bool `json:"template,omitempty"`
// either `true` to enable issues for this repository or `false` to disable them. // either `true` to enable issues for this repository or `false` to disable them.
HasIssues *bool `json:"has_issues,omitempty"` HasIssues *bool `json:"has_issues,omitempty"`
// set this structure to configure internal issue tracker (requires has_issues)
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
// set this structure to use external issue tracker (requires has_issues)
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
// either `true` to enable the wiki for this repository or `false` to disable it. // either `true` to enable the wiki for this repository or `false` to disable it.
HasWiki *bool `json:"has_wiki,omitempty"` HasWiki *bool `json:"has_wiki,omitempty"`
// set this structure to use external wiki instead of internal (requires has_wiki)
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
// sets the default branch for this repository. // sets the default branch for this repository.
DefaultBranch *string `json:"default_branch,omitempty"` DefaultBranch *string `json:"default_branch,omitempty"`
// either `true` to allow pull requests, or `false` to prevent pull request. // either `true` to allow pull requests, or `false` to prevent pull request.
HasPullRequests *bool `json:"has_pull_requests,omitempty"` HasPullRequests *bool `json:"has_pull_requests,omitempty"`
// either `true` to enable project unit, or `false` to disable them.
HasProjects *bool `json:"has_projects,omitempty"`
// either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`. // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"` IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
// either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`. // either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
@@ -366,10 +424,15 @@ type EditRepoOption struct {
AllowSquash *bool `json:"allow_squash_merge,omitempty"` AllowSquash *bool `json:"allow_squash_merge,omitempty"`
// set to `true` to archive this repository. // set to `true` to archive this repository.
Archived *bool `json:"archived,omitempty"` Archived *bool `json:"archived,omitempty"`
// set to a string like `8h30m0s` to set the mirror interval time
MirrorInterval *string `json:"mirror_interval,omitempty"`
} }
// EditRepo edit the properties of a repository // EditRepo edit the properties of a repository
func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) { func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -381,18 +444,27 @@ func (c *Client) EditRepo(owner, reponame string, opt EditRepoOption) (*Reposito
// DeleteRepo deletes a repository of user or organization. // DeleteRepo deletes a repository of user or organization.
func (c *Client) DeleteRepo(owner, repo string) (*Response, error) { func (c *Client) DeleteRepo(owner, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s", owner, repo), nil, nil)
return resp, err return resp, err
} }
// MirrorSync adds a mirrored repository to the mirror sync queue. // MirrorSync adds a mirrored repository to the mirror sync queue.
func (c *Client) MirrorSync(owner, repo string) (*Response, error) { func (c *Client) MirrorSync(owner, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil) _, resp, err := c.getResponse("POST", fmt.Sprintf("/repos/%s/%s/mirror-sync", owner, repo), nil, nil)
return resp, err return resp, err
} }
// GetRepoLanguages return language stats of a repo // GetRepoLanguages return language stats of a repo
func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) { func (c *Client) GetRepoLanguages(owner, repo string) (map[string]int64, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
langMap := make(map[string]int64) langMap := make(map[string]int64)
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil) data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/languages", owner, repo), jsonHeader, nil)
@@ -418,5 +490,30 @@ const (
// GetArchive get an archive of a repository by git reference // GetArchive get an archive of a repository by git reference
// e.g.: ref -> master, 70b7c74b33, v1.2.1, ... // e.g.: ref -> master, 70b7c74b33, v1.2.1, ...
func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) { func (c *Client) GetArchive(owner, repo, ref string, ext ArchiveType) ([]byte, *Response, error) {
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, url.PathEscape(ref), ext), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
ref = pathEscapeSegments(ref)
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
}
// GetArchiveReader gets a `git archive` for a particular tree-ish git reference
// such as a branch name (`master`), a commit hash (`70b7c74b33`), a tag
// (`v1.2.1`). The archive is returned as a byte stream in a ReadCloser. It is
// the responsibility of the client to close the reader.
func (c *Client) GetArchiveReader(owner, repo, ref string, ext ArchiveType) (io.ReadCloser, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
ref = pathEscapeSegments(ref)
resp, err := c.doRequest("GET", fmt.Sprintf("/repos/%s/%s/archive/%s%s", owner, repo, ref, ext), nil, nil)
if err != nil {
return nil, resp, err
}
if _, err := statusCodeToErr(resp); err != nil {
return nil, resp, err
}
return resp.Body, resp, nil
} }

View File

@@ -20,9 +20,6 @@ type PayloadUser struct {
UserName string `json:"username"` UserName string `json:"username"`
} }
// FIXME: consider using same format as API when commits API are added.
// applies to PayloadCommit and PayloadCommitVerification
// PayloadCommit represents a commit // PayloadCommit represents a commit
type PayloadCommit struct { type PayloadCommit struct {
// sha1 hash of the commit // sha1 hash of the commit
@@ -66,6 +63,9 @@ type ListRepoBranchesOptions struct {
// ListRepoBranches list all the branches of one repository // ListRepoBranches list all the branches of one repository
func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) { func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions) ([]*Branch, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
branches := make([]*Branch, 0, opt.PageSize) branches := make([]*Branch, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &branches)
@@ -74,6 +74,9 @@ func (c *Client) ListRepoBranches(user, repo string, opt ListRepoBranchesOptions
// GetRepoBranch get one branch's information of one repository // GetRepoBranch get one branch's information of one repository
func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) { func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
return nil, nil, err
}
b := new(Branch) b := new(Branch)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/branches/%s", user, repo, branch), nil, nil, &b)
if err != nil { if err != nil {
@@ -84,6 +87,9 @@ func (c *Client) GetRepoBranch(user, repo, branch string) (*Branch, *Response, e
// DeleteRepoBranch delete a branch in a repository // DeleteRepoBranch delete a branch in a repository
func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) { func (c *Client) DeleteRepoBranch(user, repo, branch string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &branch); err != nil {
return false, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return false, nil, err return false, nil, err
} }
@@ -118,6 +124,9 @@ func (opt CreateBranchOption) Validate() error {
// CreateBranch creates a branch for a user's repository // CreateBranch creates a branch for a user's repository
func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) { func (c *Client) CreateBranch(owner, repo string, opt CreateBranchOption) (*Branch, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -30,6 +30,7 @@ type BranchProtection struct {
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"` ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"` ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"` BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"` BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
DismissStaleApprovals bool `json:"dismiss_stale_approvals"` DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"` RequireSignedCommits bool `json:"require_signed_commits"`
@@ -56,6 +57,7 @@ type CreateBranchProtectionOption struct {
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"` ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"` ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"` BlockOnRejectedReviews bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"` BlockOnOutdatedBranch bool `json:"block_on_outdated_branch"`
DismissStaleApprovals bool `json:"dismiss_stale_approvals"` DismissStaleApprovals bool `json:"dismiss_stale_approvals"`
RequireSignedCommits bool `json:"require_signed_commits"` RequireSignedCommits bool `json:"require_signed_commits"`
@@ -79,6 +81,7 @@ type EditBranchProtectionOption struct {
ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"` ApprovalsWhitelistUsernames []string `json:"approvals_whitelist_username"`
ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"` ApprovalsWhitelistTeams []string `json:"approvals_whitelist_teams"`
BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"` BlockOnRejectedReviews *bool `json:"block_on_rejected_reviews"`
BlockOnOfficialReviewRequests *bool `json:"block_on_official_review_requests"`
BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"` BlockOnOutdatedBranch *bool `json:"block_on_outdated_branch"`
DismissStaleApprovals *bool `json:"dismiss_stale_approvals"` DismissStaleApprovals *bool `json:"dismiss_stale_approvals"`
RequireSignedCommits *bool `json:"require_signed_commits"` RequireSignedCommits *bool `json:"require_signed_commits"`
@@ -92,6 +95,9 @@ type ListBranchProtectionsOptions struct {
// ListBranchProtections list branch protections for a repo // ListBranchProtections list branch protections for a repo
func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) { func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtectionsOptions) ([]*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -104,6 +110,9 @@ func (c *Client) ListBranchProtections(owner, repo string, opt ListBranchProtect
// GetBranchProtection gets a branch protection // GetBranchProtection gets a branch protection
func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) { func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -114,6 +123,9 @@ func (c *Client) GetBranchProtection(owner, repo, name string) (*BranchProtectio
// CreateBranchProtection creates a branch protection for a repo // CreateBranchProtection creates a branch protection for a repo
func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) { func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProtectionOption) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -128,6 +140,9 @@ func (c *Client) CreateBranchProtection(owner, repo string, opt CreateBranchProt
// EditBranchProtection edits a branch protection for a repo // EditBranchProtection edits a branch protection for a repo
func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) { func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchProtectionOption) (*BranchProtection, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -142,6 +157,9 @@ func (c *Client) EditBranchProtection(owner, repo, name string, opt EditBranchPr
// DeleteBranchProtection deletes a branch protection for a repo // DeleteBranchProtection deletes a branch protection for a repo
func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) { func (c *Client) DeleteBranchProtection(owner, repo, name string) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &name); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, err return nil, err
} }

View File

@@ -17,6 +17,9 @@ type ListCollaboratorsOptions struct {
// ListCollaborators list a repository's collaborators // ListCollaborators list a repository's collaborators
func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) { func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
collaborators := make([]*User, 0, opt.PageSize) collaborators := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", resp, err := c.getParsedResponse("GET",
@@ -27,6 +30,9 @@ func (c *Client) ListCollaborators(user, repo string, opt ListCollaboratorsOptio
// IsCollaborator check if a user is a collaborator of a repository // IsCollaborator check if a user is a collaborator of a repository
func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) { func (c *Client) IsCollaborator(user, repo, collaborator string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil) status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
@@ -78,6 +84,9 @@ func (opt AddCollaboratorOption) Validate() error {
// AddCollaborator add some user as a collaborator of a repository // AddCollaborator add some user as a collaborator of a repository
func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) { func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollaboratorOption) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return nil, err
}
if err := opt.Validate(); err != nil { if err := opt.Validate(); err != nil {
return nil, err return nil, err
} }
@@ -91,6 +100,9 @@ func (c *Client) AddCollaborator(user, repo, collaborator string, opt AddCollabo
// DeleteCollaborator remove a collaborator from a repository // DeleteCollaborator remove a collaborator from a repository
func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) { func (c *Client) DeleteCollaborator(user, repo, collaborator string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &collaborator); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", _, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil) fmt.Sprintf("/repos/%s/%s/collaborators/%s", user, repo, collaborator), nil, nil)
return resp, err return resp, err

View File

@@ -47,6 +47,7 @@ type Commit struct {
Author *User `json:"author"` Author *User `json:"author"`
Committer *User `json:"committer"` Committer *User `json:"committer"`
Parents []*CommitMeta `json:"parents"` Parents []*CommitMeta `json:"parents"`
Files []*CommitAffectedFiles `json:"files"`
} }
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE // CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
@@ -55,8 +56,16 @@ type CommitDateOptions struct {
Committer time.Time `json:"committer"` Committer time.Time `json:"committer"`
} }
// CommitAffectedFiles store information about files affected by the commit
type CommitAffectedFiles struct {
Filename string `json:"filename"`
}
// GetSingleCommit returns a single commit // GetSingleCommit returns a single commit
func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) { func (c *Client) GetSingleCommit(user, repo, commitID string) (*Commit, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &commitID); err != nil {
return nil, nil, err
}
commit := new(Commit) commit := new(Commit)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/commits/%s", user, repo, commitID), nil, nil, &commit)
return commit, resp, err return commit, resp, err
@@ -80,6 +89,9 @@ func (opt *ListCommitOptions) QueryEncode() string {
// ListRepoCommits return list of commits from a repo // ListRepoCommits return list of commits from a repo
func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) { func (c *Client) ListRepoCommits(user, repo string, opt ListCommitOptions) ([]*Commit, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/commits", user, repo))
opt.setDefaults() opt.setDefaults()
commits := make([]*Commit, 0, opt.PageSize) commits := make([]*Commit, 0, opt.PageSize)

View File

@@ -9,6 +9,8 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"strings"
) )
// FileOptions options for all file APIs // FileOptions options for all file APIs
@@ -23,6 +25,8 @@ type FileOptions struct {
Author Identity `json:"author"` Author Identity `json:"author"`
Committer Identity `json:"committer"` Committer Identity `json:"committer"`
Dates CommitDateOptions `json:"dates"` Dates CommitDateOptions `json:"dates"`
// Add a Signed-off-by trailer by the committer at the end of the commit log message.
Signoff bool `json:"signoff"`
} }
// CreateFileOptions options for creating files // CreateFileOptions options for creating files
@@ -113,25 +117,65 @@ type FileDeleteResponse struct {
} }
// GetFile downloads a file of repository, ref can be branch/tag/commit. // GetFile downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, tree -> macaron.go(no leading slash) // e.g.: ref -> master, filepath -> README.md (no leading slash)
func (c *Client) GetFile(user, repo, ref, tree string) ([]byte, *Response, error) { func (c *Client) GetFile(owner, repo, ref, filepath string) ([]byte, *Response, error) {
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", user, repo, ref, tree), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath)
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
ref = pathEscapeSegments(ref)
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s/%s", owner, repo, ref, filepath), nil, nil)
}
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/raw/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), nil, nil)
} }
// GetContents get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir // GetContents get the metadata and contents of a file in a repository
// ref is optional // ref is optional
func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, *Response, error) { func (c *Client) GetContents(owner, repo, ref, filepath string) (*ContentsResponse, *Response, error) {
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
if err != nil {
return nil, resp, err
}
cr := new(ContentsResponse) cr := new(ContentsResponse)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, ref), jsonHeader, nil, cr) if json.Unmarshal(data, &cr) != nil {
return nil, resp, fmt.Errorf("expect file, got directory")
}
return cr, resp, err return cr, resp, err
} }
// ListContents gets a list of entries in a dir
// ref is optional
func (c *Client) ListContents(owner, repo, ref, filepath string) ([]*ContentsResponse, *Response, error) {
data, resp, err := c.getDirOrFileContents(owner, repo, ref, filepath)
if err != nil {
return nil, resp, err
}
crl := make([]*ContentsResponse, 0)
if json.Unmarshal(data, &crl) != nil {
return nil, resp, fmt.Errorf("expect directory, got file")
}
return crl, resp, err
}
func (c *Client) getDirOrFileContents(owner, repo, ref, filepath string) ([]byte, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(strings.TrimPrefix(filepath, "/"))
return c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/contents/%s?ref=%s", owner, repo, filepath, url.QueryEscape(ref)), jsonHeader, nil)
}
// CreateFile create a file in a repository // CreateFile create a file in a repository
func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) { func (c *Client) CreateFile(owner, repo, filepath string, opt CreateFileOptions) (*FileResponse, *Response, error) {
var err error var err error
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil { if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, nil, err return nil, nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
@@ -149,6 +193,11 @@ func (c *Client) UpdateFile(owner, repo, filepath string, opt UpdateFileOptions)
return nil, nil, err return nil, nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -164,6 +213,10 @@ func (c *Client) DeleteFile(owner, repo, filepath string, opt DeleteFileOptions)
if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil { if opt.BranchName, err = c.setDefaultBranchForOldVersions(owner, repo, opt.BranchName); err != nil {
return nil, err return nil, err
} }
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
filepath = pathEscapeSegments(filepath)
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {

View File

@@ -46,6 +46,9 @@ func (opt *ListDeployKeysOptions) QueryEncode() string {
// ListDeployKeys list all the deploy keys of one repository // ListDeployKeys list all the deploy keys of one repository
func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) { func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([]*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo)) link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/keys", user, repo))
opt.setDefaults() opt.setDefaults()
link.RawQuery = opt.QueryEncode() link.RawQuery = opt.QueryEncode()
@@ -56,6 +59,9 @@ func (c *Client) ListDeployKeys(user, repo string, opt ListDeployKeysOptions) ([
// GetDeployKey get one deploy key with key id // GetDeployKey get one deploy key with key id
func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) { func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
key := new(DeployKey) key := new(DeployKey)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/keys/%d", user, repo, keyID), nil, nil, &key)
return key, resp, err return key, resp, err
@@ -63,6 +69,9 @@ func (c *Client) GetDeployKey(user, repo string, keyID int64) (*DeployKey, *Resp
// CreateDeployKey options when create one deploy key // CreateDeployKey options when create one deploy key
func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) { func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*DeployKey, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@@ -74,6 +83,9 @@ func (c *Client) CreateDeployKey(user, repo string, opt CreateKeyOption) (*Deplo
// DeleteDeployKey delete deploy key with key id // DeleteDeployKey delete deploy key with key id
func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) { func (c *Client) DeleteDeployKey(owner, repo string, keyID int64) (*Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/keys/%d", owner, repo, keyID), nil, nil)
return resp, err return resp, err
} }

View File

@@ -22,10 +22,8 @@ const (
GitServiceGitlab GitServiceType = "gitlab" GitServiceGitlab GitServiceType = "gitlab"
// GitServiceGitea represents a gitea service // GitServiceGitea represents a gitea service
GitServiceGitea GitServiceType = "gitea" GitServiceGitea GitServiceType = "gitea"
// GitServiceGogs represents a gogs service
// Not supported jet GitServiceGogs GitServiceType = "gogs"
// // GitServiceGogs represents a gogs service
// GitServiceGogs GitServiceType = "gogs"
) )
// MigrateRepoOption options for migrating a repository from an external service // MigrateRepoOption options for migrating a repository from an external service
@@ -48,6 +46,7 @@ type MigrateRepoOption struct {
Issues bool `json:"issues"` Issues bool `json:"issues"`
PullRequests bool `json:"pull_requests"` PullRequests bool `json:"pull_requests"`
Releases bool `json:"releases"` Releases bool `json:"releases"`
MirrorInterval string `json:"mirror_interval"`
} }
// Validate the MigrateRepoOption struct // Validate the MigrateRepoOption struct
@@ -67,17 +66,24 @@ func (opt *MigrateRepoOption) Validate(c *Client) error {
switch opt.Service { switch opt.Service {
case GitServiceGithub: case GitServiceGithub:
if len(opt.AuthToken) == 0 { if len(opt.AuthToken) == 0 {
return fmt.Errorf("github require token authentication") return fmt.Errorf("github requires token authentication")
} }
case GitServiceGitlab, GitServiceGitea: case GitServiceGitlab, GitServiceGitea:
if len(opt.AuthToken) == 0 { if len(opt.AuthToken) == 0 {
return fmt.Errorf("%s require token authentication", opt.Service) return fmt.Errorf("%s requires token authentication", opt.Service)
} }
// Gitlab is supported since 1.12.0 but api cant handle it until 1.13.0 // Gitlab is supported since 1.12.0 but api cant handle it until 1.13.0
// https://github.com/go-gitea/gitea/pull/12672 // https://github.com/go-gitea/gitea/pull/12672
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil { if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
return fmt.Errorf("migrate from service %s need gitea >= 1.13.0", opt.Service) return fmt.Errorf("migrate from service %s need gitea >= 1.13.0", opt.Service)
} }
case GitServiceGogs:
if len(opt.AuthToken) == 0 {
return fmt.Errorf("gogs requires token authentication")
}
if c.checkServerVersionGreaterThanOrEqual(version1_14_0) != nil {
return fmt.Errorf("migrate from service gogs need gitea >= 1.14.0")
}
} }
return nil return nil
} }

View File

@@ -27,7 +27,11 @@ type GitObject struct {
// GetRepoRef get one ref's information of one repository // GetRepoRef get one ref's information of one repository
func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) { func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
ref = strings.TrimPrefix(ref, "refs/") ref = strings.TrimPrefix(ref, "refs/")
ref = pathEscapeSegments(ref)
r := new(Reference) r := new(Reference)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil, &r)
if _, ok := err.(*json.UnmarshalTypeError); ok { if _, ok := err.(*json.UnmarshalTypeError); ok {
@@ -42,7 +46,12 @@ func (c *Client) GetRepoRef(user, repo, ref string) (*Reference, *Response, erro
// GetRepoRefs get list of ref's information of one repository // GetRepoRefs get list of ref's information of one repository
func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) { func (c *Client) GetRepoRefs(user, repo, ref string) ([]*Reference, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
ref = strings.TrimPrefix(ref, "refs/") ref = strings.TrimPrefix(ref, "refs/")
ref = pathEscapeSegments(ref)
data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil) data, resp, err := c.getResponse("GET", fmt.Sprintf("/repos/%s/%s/git/refs/%s", user, repo, ref), nil, nil)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err

96
vendor/code.gitea.io/sdk/gitea/repo_stars.go generated vendored Normal file
View File

@@ -0,0 +1,96 @@
// Copyright 2021 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 gitea
import (
"fmt"
"net/http"
)
// ListStargazersOptions options for listing a repository's stargazers
type ListStargazersOptions struct {
ListOptions
}
// ListRepoStargazers list a repository's stargazers
func (c *Client) ListRepoStargazers(user, repo string, opt ListStargazersOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults()
stargazers := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/stargazers?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &stargazers)
return stargazers, resp, err
}
// GetStarredRepos returns the repos that the given user has starred
func (c *Client) GetStarredRepos(user string) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/starred", user), jsonHeader, nil, &repos)
return repos, resp, err
}
// GetMyStarredRepos returns the repos that the authenticated user has starred
func (c *Client) GetMyStarredRepos() ([]*Repository, *Response, error) {
repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", "/user/starred", jsonHeader, nil, &repos)
return repos, resp, err
}
// IsRepoStarring returns whether the authenticated user has starred the repo or not
func (c *Client) IsRepoStarring(user, repo string) (bool, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return false, nil, err
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil {
switch resp.StatusCode {
case http.StatusNotFound:
return false, resp, nil
case http.StatusNoContent:
return true, resp, nil
default:
return false, resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
}
}
return false, nil, err
}
// StarRepo star specified repo as the authenticated user
func (c *Client) StarRepo(user, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil {
switch resp.StatusCode {
case http.StatusNoContent:
return resp, nil
default:
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
}
}
return nil, err
}
// UnStarRepo remove star to specified repo as the authenticated user
func (c *Client) UnStarRepo(user, repo string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/starred/%s/%s", user, repo), jsonHeader, nil)
if resp != nil {
switch resp.StatusCode {
case http.StatusNoContent:
return resp, nil
default:
return resp, fmt.Errorf("unexpected status code '%d'", resp.StatusCode)
}
}
return nil, err
}

View File

@@ -24,8 +24,25 @@ type ListRepoTagsOptions struct {
// ListRepoTags list all the branches of one repository // ListRepoTags list all the branches of one repository
func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) { func (c *Client) ListRepoTags(user, repo string, opt ListRepoTagsOptions) ([]*Tag, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
tags := make([]*Tag, 0, opt.PageSize) tags := make([]*Tag, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/tags?%s", user, repo, opt.getURLQuery().Encode()), nil, nil, &tags)
return tags, resp, err return tags, resp, err
} }
// DeleteTag deletes a tag from a repository, if no release refers to it
func (c *Client) DeleteTag(user, repo string, tag string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &tag); err != nil {
return nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_14_0); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE",
fmt.Sprintf("/repos/%s/%s/tags/%s", user, repo, tag),
nil, nil)
return resp, err
}

View File

@@ -22,6 +22,9 @@ type topicsList struct {
// ListRepoTopics list all repository's topics // ListRepoTopics list all repository's topics
func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) { func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([]string, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
list := new(topicsList) list := new(topicsList)
@@ -34,9 +37,10 @@ func (c *Client) ListRepoTopics(user, repo string, opt ListRepoTopicsOptions) ([
// SetRepoTopics replaces the list of repo's topics // SetRepoTopics replaces the list of repo's topics
func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) { func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo); err != nil {
return nil, err
}
l := topicsList{Topics: list} l := topicsList{Topics: list}
body, err := json.Marshal(&l) body, err := json.Marshal(&l)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -47,12 +51,18 @@ func (c *Client) SetRepoTopics(user, repo string, list []string) (*Response, err
// AddRepoTopic adds a topic to a repo's topics list // AddRepoTopic adds a topic to a repo's topics list
func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) { func (c *Client) AddRepoTopic(user, repo, topic string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return resp, err return resp, err
} }
// DeleteRepoTopic deletes a topic from repo's topics list // DeleteRepoTopic deletes a topic from repo's topics list
func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) { func (c *Client) DeleteRepoTopic(user, repo, topic string) (*Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &topic); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/repos/%s/%s/topics/%s", user, repo, topic), nil, nil)
return resp, err return resp, err
} }

View File

@@ -20,6 +20,9 @@ type TransferRepoOption struct {
// TransferRepo transfers the ownership of a repository // TransferRepo transfers the ownership of a repository
func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) { func (c *Client) TransferRepo(owner, reponame string, opt TransferRepoOption) (*Repository, *Response, error) {
if err := escapeValidatePathSegments(&owner, &reponame); err != nil {
return nil, nil, err
}
if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil { if err := c.checkServerVersionGreaterThanOrEqual(version1_12_0); err != nil {
return nil, nil, err return nil, nil, err
} }

View File

@@ -31,6 +31,9 @@ type GitTreeResponse struct {
// GetTrees downloads a file of repository, ref can be branch/tag/commit. // GetTrees downloads a file of repository, ref can be branch/tag/commit.
// e.g.: ref -> master, tree -> macaron.go(no leading slash) // e.g.: ref -> master, tree -> macaron.go(no leading slash)
func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) { func (c *Client) GetTrees(user, repo, ref string, recursive bool) (*GitTreeResponse, *Response, error) {
if err := escapeValidatePathSegments(&user, &repo, &ref); err != nil {
return nil, nil, err
}
trees := new(GitTreeResponse) trees := new(GitTreeResponse)
var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref) var path = fmt.Sprintf("/repos/%s/%s/git/trees/%s", user, repo, ref)
if recursive { if recursive {

View File

@@ -22,6 +22,9 @@ type WatchInfo struct {
// GetWatchedRepos list all the watched repos of user // GetWatchedRepos list all the watched repos of user
func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) { func (c *Client) GetWatchedRepos(user string) ([]*Repository, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
repos := make([]*Repository, 0, 10) repos := make([]*Repository, 0, 10)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/subscriptions", user), nil, nil, &repos)
return repos, resp, err return repos, resp, err
@@ -35,8 +38,11 @@ func (c *Client) GetMyWatchedRepos() ([]*Repository, *Response, error) {
} }
// CheckRepoWatch check if the current user is watching a repo // CheckRepoWatch check if the current user is watching a repo
func (c *Client) CheckRepoWatch(repoUser, repoName string) (bool, *Response, error) { func (c *Client) CheckRepoWatch(owner, repo string) (bool, *Response, error) {
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return false, nil, err
}
status, resp, err := c.getStatusCode("GET", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return false, resp, err return false, resp, err
} }
@@ -51,8 +57,11 @@ func (c *Client) CheckRepoWatch(repoUser, repoName string) (bool, *Response, err
} }
// WatchRepo start to watch a repository // WatchRepo start to watch a repository
func (c *Client) WatchRepo(repoUser, repoName string) (*Response, error) { func (c *Client) WatchRepo(owner, repo string) (*Response, error) {
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("PUT", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
} }
@@ -63,8 +72,11 @@ func (c *Client) WatchRepo(repoUser, repoName string) (*Response, error) {
} }
// UnWatchRepo stop to watch a repository // UnWatchRepo stop to watch a repository
func (c *Client) UnWatchRepo(repoUser, repoName string) (*Response, error) { func (c *Client) UnWatchRepo(owner, repo string) (*Response, error) {
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", repoUser, repoName), nil, nil) if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, err
}
status, resp, err := c.getStatusCode("DELETE", fmt.Sprintf("/repos/%s/%s/subscription", owner, repo), nil, nil)
if err != nil { if err != nil {
return resp, err return resp, err
} }

View File

@@ -6,6 +6,7 @@ package gitea
// GlobalUISettings represent the global ui settings of a gitea instance witch is exposed by API // GlobalUISettings represent the global ui settings of a gitea instance witch is exposed by API
type GlobalUISettings struct { type GlobalUISettings struct {
DefaultTheme string `json:"default_theme"`
AllowedReactions []string `json:"allowed_reactions"` AllowedReactions []string `json:"allowed_reactions"`
} }
@@ -13,6 +14,7 @@ type GlobalUISettings struct {
type GlobalRepoSettings struct { type GlobalRepoSettings struct {
MirrorsDisabled bool `json:"mirrors_disabled"` MirrorsDisabled bool `json:"mirrors_disabled"`
HTTPGitDisabled bool `json:"http_git_disabled"` HTTPGitDisabled bool `json:"http_git_disabled"`
MigrationsDisabled bool `json:"migrations_disabled"`
} }
// GlobalAPISettings contains global api settings exposed by it // GlobalAPISettings contains global api settings exposed by it

View File

@@ -8,6 +8,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"time" "time"
) )
@@ -51,12 +52,15 @@ type CreateStatusOption struct {
// CreateStatus creates a new Status for a given Commit // CreateStatus creates a new Status for a given Commit
func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) { func (c *Client) CreateStatus(owner, repo, sha string, opts CreateStatusOption) (*Status, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo); err != nil {
return nil, nil, err
}
body, err := json.Marshal(&opts) body, err := json.Marshal(&opts)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
status := new(Status) status := new(Status)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, sha), jsonHeader, bytes.NewReader(body), status) resp, err := c.getParsedResponse("POST", fmt.Sprintf("/repos/%s/%s/statuses/%s", owner, repo, url.QueryEscape(sha)), jsonHeader, bytes.NewReader(body), status)
return status, resp, err return status, resp, err
} }
@@ -65,11 +69,14 @@ type ListStatusesOption struct {
ListOptions ListOptions
} }
// ListStatuses returns all statuses for a given Commit // ListStatuses returns all statuses for a given Commit by ref
func (c *Client) ListStatuses(owner, repo, sha string, opt ListStatusesOption) ([]*Status, *Response, error) { func (c *Client) ListStatuses(owner, repo, ref string, opt ListStatusesOption) ([]*Status, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
statuses := make([]*Status, 0, opt.PageSize) statuses := make([]*Status, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, sha, opt.getURLQuery().Encode()), nil, nil, &statuses) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/statuses?%s", owner, repo, ref, opt.getURLQuery().Encode()), jsonHeader, nil, &statuses)
return statuses, resp, err return statuses, resp, err
} }
@@ -85,8 +92,17 @@ type CombinedStatus struct {
} }
// GetCombinedStatus returns the CombinedStatus for a given Commit // GetCombinedStatus returns the CombinedStatus for a given Commit
func (c *Client) GetCombinedStatus(owner, repo, sha string) (*CombinedStatus, *Response, error) { func (c *Client) GetCombinedStatus(owner, repo, ref string) (*CombinedStatus, *Response, error) {
if err := escapeValidatePathSegments(&owner, &repo, &ref); err != nil {
return nil, nil, err
}
status := new(CombinedStatus) status := new(CombinedStatus)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, sha), nil, nil, status) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/repos/%s/%s/commits/%s/status", owner, repo, ref), jsonHeader, nil, status)
// gitea api return empty body if nothing here jet
if resp != nil && resp.StatusCode == 200 && err != nil {
return status, resp, nil
}
return status, resp, err return status, resp, err
} }

View File

@@ -30,6 +30,9 @@ type User struct {
// GetUserInfo get user info by user's name // GetUserInfo get user info by user's name
func (c *Client) GetUserInfo(user string) (*User, *Response, error) { func (c *Client) GetUserInfo(user string) (*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
u := new(User) u := new(User)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s", user), nil, nil, u)
return u, resp, err return u, resp, err

View File

@@ -9,6 +9,7 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/url"
"reflect" "reflect"
) )
@@ -27,12 +28,15 @@ type ListAccessTokensOptions struct {
// ListAccessTokens lists all the access tokens of user // ListAccessTokens lists all the access tokens of user
func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, *Response, error) { func (c *Client) ListAccessTokens(opts ListAccessTokensOptions) ([]*AccessToken, *Response, error) {
if len(c.username) == 0 { c.mutex.RLock()
username := c.username
c.mutex.RUnlock()
if len(username) == 0 {
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed") return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
} }
opts.setDefaults() opts.setDefaults()
tokens := make([]*AccessToken, 0, opts.PageSize) tokens := make([]*AccessToken, 0, opts.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", c.username, opts.getURLQuery().Encode()), jsonHeader, nil, &tokens) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/tokens?%s", url.PathEscape(username), opts.getURLQuery().Encode()), jsonHeader, nil, &tokens)
return tokens, resp, err return tokens, resp, err
} }
@@ -43,7 +47,10 @@ type CreateAccessTokenOption struct {
// CreateAccessToken create one access token with options // CreateAccessToken create one access token with options
func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *Response, error) { func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *Response, error) {
if len(c.username) == 0 { c.mutex.RLock()
username := c.username
c.mutex.RUnlock()
if len(username) == 0 {
return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed") return nil, nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
} }
body, err := json.Marshal(&opt) body, err := json.Marshal(&opt)
@@ -51,13 +58,16 @@ func (c *Client) CreateAccessToken(opt CreateAccessTokenOption) (*AccessToken, *
return nil, nil, err return nil, nil, err
} }
t := new(AccessToken) t := new(AccessToken)
resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", c.username), jsonHeader, bytes.NewReader(body), t) resp, err := c.getParsedResponse("POST", fmt.Sprintf("/users/%s/tokens", url.PathEscape(username)), jsonHeader, bytes.NewReader(body), t)
return t, resp, err return t, resp, err
} }
// DeleteAccessToken delete token, identified by ID and if not available by name // DeleteAccessToken delete token, identified by ID and if not available by name
func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) { func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
if len(c.username) == 0 { c.mutex.RLock()
username := c.username
c.mutex.RUnlock()
if len(username) == 0 {
return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed") return nil, fmt.Errorf("\"username\" not set: only BasicAuth allowed")
} }
@@ -75,6 +85,6 @@ func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
return nil, fmt.Errorf("only string and int64 supported") return nil, fmt.Errorf("only string and int64 supported")
} }
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", c.username, token), jsonHeader, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/users/%s/tokens/%s", url.PathEscape(username), url.PathEscape(token)), jsonHeader, nil)
return resp, err return resp, err
} }

View File

@@ -21,6 +21,9 @@ func (c *Client) ListMyFollowers(opt ListFollowersOptions) ([]*User, *Response,
// ListFollowers list all the followers of one user // ListFollowers list all the followers of one user
func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) { func (c *Client) ListFollowers(user string, opt ListFollowersOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/followers?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
@@ -42,6 +45,9 @@ func (c *Client) ListMyFollowing(opt ListFollowingOptions) ([]*User, *Response,
// ListFollowing list all the users the user followed // ListFollowing list all the users the user followed
func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) { func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
users := make([]*User, 0, opt.PageSize) users := make([]*User, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/following?%s", user, opt.getURLQuery().Encode()), nil, nil, &users)
@@ -50,24 +56,38 @@ func (c *Client) ListFollowing(user string, opt ListFollowingOptions) ([]*User,
// IsFollowing if current user followed the target // IsFollowing if current user followed the target
func (c *Client) IsFollowing(target string) (bool, *Response) { func (c *Client) IsFollowing(target string) (bool, *Response) {
if err := escapeValidatePathSegments(&target); err != nil {
// ToDo return err
return false, nil
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("GET", fmt.Sprintf("/user/following/%s", target), nil, nil)
return err == nil, resp return err == nil, resp
} }
// IsUserFollowing if the user followed the target // IsUserFollowing if the user followed the target
func (c *Client) IsUserFollowing(user, target string) (bool, *Response) { func (c *Client) IsUserFollowing(user, target string) (bool, *Response) {
if err := escapeValidatePathSegments(&user, &target); err != nil {
// ToDo return err
return false, nil
}
_, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil) _, resp, err := c.getResponse("GET", fmt.Sprintf("/users/%s/following/%s", user, target), nil, nil)
return err == nil, resp return err == nil, resp
} }
// Follow set current user follow the target // Follow set current user follow the target
func (c *Client) Follow(target string) (*Response, error) { func (c *Client) Follow(target string) (*Response, error) {
if err := escapeValidatePathSegments(&target); err != nil {
return nil, err
}
_, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("PUT", fmt.Sprintf("/user/following/%s", target), nil, nil)
return resp, err return resp, err
} }
// Unfollow set current user unfollow the target // Unfollow set current user unfollow the target
func (c *Client) Unfollow(target string) (*Response, error) { func (c *Client) Unfollow(target string) (*Response, error) {
if err := escapeValidatePathSegments(&target); err != nil {
return nil, err
}
_, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil) _, resp, err := c.getResponse("DELETE", fmt.Sprintf("/user/following/%s", target), nil, nil)
return resp, err return resp, err
} }

View File

@@ -40,6 +40,9 @@ type ListGPGKeysOptions struct {
// ListGPGKeys list all the GPG keys of the user // ListGPGKeys list all the GPG keys of the user
func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) { func (c *Client) ListGPGKeys(user string, opt ListGPGKeysOptions) ([]*GPGKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
keys := make([]*GPGKey, 0, opt.PageSize) keys := make([]*GPGKey, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/gpg_keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)

View File

@@ -31,6 +31,9 @@ type ListPublicKeysOptions struct {
// ListPublicKeys list all the public keys of the user // ListPublicKeys list all the public keys of the user
func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) { func (c *Client) ListPublicKeys(user string, opt ListPublicKeysOptions) ([]*PublicKey, *Response, error) {
if err := escapeValidatePathSegments(&user); err != nil {
return nil, nil, err
}
opt.setDefaults() opt.setDefaults()
keys := make([]*PublicKey, 0, opt.PageSize) keys := make([]*PublicKey, 0, opt.PageSize)
resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys) resp, err := c.getParsedResponse("GET", fmt.Sprintf("/users/%s/keys?%s", user, opt.getURLQuery().Encode()), nil, nil, &keys)

View File

@@ -22,63 +22,57 @@ func (c *Client) ServerVersion() (string, *Response, error) {
// CheckServerVersionConstraint validates that the login's server satisfies a // CheckServerVersionConstraint validates that the login's server satisfies a
// given version constraint such as ">= 1.11.0+dev" // given version constraint such as ">= 1.11.0+dev"
func (c *Client) CheckServerVersionConstraint(constraint string) error { func (c *Client) CheckServerVersionConstraint(constraint string) error {
c.versionLock.RLock() if err := c.loadServerVersion(); err != nil {
if c.serverVersion == nil {
c.versionLock.RUnlock()
if err := c.loadClientServerVersion(); err != nil {
return err return err
} }
} else {
c.versionLock.RUnlock()
}
check, err := version.NewConstraint(constraint) check, err := version.NewConstraint(constraint)
if err != nil { if err != nil {
return err return err
} }
if !check.Check(c.serverVersion) { if !check.Check(c.serverVersion) {
return fmt.Errorf("gitea server at %s does not satisfy version constraint %s", c.url, constraint) c.mutex.RLock()
url := c.url
c.mutex.RUnlock()
return fmt.Errorf("gitea server at %s does not satisfy version constraint %s", url, constraint)
} }
return nil return nil
} }
// predefined versions only have to be parsed by library once // predefined versions only have to be parsed by library once
var ( var (
version1_10_0, _ = version.NewVersion("1.10.0")
version1_11_0, _ = version.NewVersion("1.11.0") version1_11_0, _ = version.NewVersion("1.11.0")
version1_12_0, _ = version.NewVersion("1.12.0") version1_12_0, _ = version.NewVersion("1.12.0")
version1_13_0, _ = version.NewVersion("1.13.0") version1_13_0, _ = version.NewVersion("1.13.0")
version1_14_0, _ = version.NewVersion("1.14.0")
) )
// checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease // checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error { func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
c.versionLock.RLock() if err := c.loadServerVersion(); err != nil {
if c.serverVersion == nil {
c.versionLock.RUnlock()
if err := c.loadClientServerVersion(); err != nil {
return err return err
} }
} else {
c.versionLock.RUnlock()
}
if !c.serverVersion.GreaterThanOrEqual(v) { if !c.serverVersion.GreaterThanOrEqual(v) {
return fmt.Errorf("gitea server at %s is older than %s", c.url, v.Original()) c.mutex.RLock()
url := c.url
c.mutex.RUnlock()
return fmt.Errorf("gitea server at %s is older than %s", url, v.Original())
} }
return nil return nil
} }
// loadClientServerVersion init the serverVersion variable // loadServerVersion init the serverVersion variable
func (c *Client) loadClientServerVersion() error { func (c *Client) loadServerVersion() (err error) {
c.versionLock.Lock() c.getVersionOnce.Do(func() {
defer c.versionLock.Unlock() raw, _, err2 := c.ServerVersion()
if err2 != nil {
raw, _, err := c.ServerVersion() err = err2
if err != nil { return
return err
} }
if c.serverVersion, err = version.NewVersion(raw); err != nil { if c.serverVersion, err = version.NewVersion(raw); err != nil {
return err return
} }
return nil })
return
} }

View File

@@ -20,6 +20,7 @@ var escapedPipeCellListKey = parser.NewContextKey()
type escapedPipeCell struct { type escapedPipeCell struct {
Cell *ast.TableCell Cell *ast.TableCell
Pos []int Pos []int
Transformed bool
} }
// TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format. // TableCellAlignMethod indicates how are table cells aligned in HTML format.indicates how are table cells aligned in HTML format.
@@ -216,7 +217,7 @@ func (b *tableParagraphTransformer) parseRow(segment text.Segment, alignments []
break break
} else if hasBacktick { } else if hasBacktick {
if escapedCell == nil { if escapedCell == nil {
escapedCell = &escapedPipeCell{node, []int{}} escapedCell = &escapedPipeCell{node, []int{}, false}
escapedList := pc.ComputeIfAbsent(escapedPipeCellListKey, escapedList := pc.ComputeIfAbsent(escapedPipeCellListKey,
func() interface{} { func() interface{} {
return []*escapedPipeCell{} return []*escapedPipeCell{}
@@ -288,22 +289,34 @@ func (a *tableASTTransformer) Transform(node *gast.Document, reader text.Reader,
} }
pc.Set(escapedPipeCellListKey, nil) pc.Set(escapedPipeCellListKey, nil)
for _, v := range lst.([]*escapedPipeCell) { for _, v := range lst.([]*escapedPipeCell) {
if v.Transformed {
continue
}
_ = gast.Walk(v.Cell, func(n gast.Node, entering bool) (gast.WalkStatus, error) { _ = gast.Walk(v.Cell, func(n gast.Node, entering bool) (gast.WalkStatus, error) {
if n.Kind() != gast.KindCodeSpan { if !entering || n.Kind() != gast.KindCodeSpan {
return gast.WalkContinue, nil return gast.WalkContinue, nil
} }
c := n.FirstChild()
for c != nil { for c := n.FirstChild(); c != nil; {
next := c.NextSibling() next := c.NextSibling()
if c.Kind() == gast.KindText { if c.Kind() != gast.KindText {
t := c.(*gast.Text) c = next
continue
}
parent := c.Parent()
ts := &c.(*gast.Text).Segment
n := c
for _, v := range lst.([]*escapedPipeCell) {
for _, pos := range v.Pos { for _, pos := range v.Pos {
if t.Segment.Start <= pos && t.Segment.Stop > pos { if ts.Start <= pos && pos < ts.Stop {
n1 := gast.NewRawTextSegment(t.Segment.WithStop(pos)) segment := n.(*gast.Text).Segment
n2 := gast.NewRawTextSegment(t.Segment.WithStart(pos + 1)) n1 := gast.NewRawTextSegment(segment.WithStop(pos))
n.InsertAfter(n, c, n1) n2 := gast.NewRawTextSegment(segment.WithStart(pos + 1))
n.InsertAfter(n, n1, n2) parent.InsertAfter(parent, n, n1)
n.RemoveChild(n, c) parent.InsertAfter(parent, n1, n2)
parent.RemoveChild(parent, n)
n = n2
v.Transformed = true
} }
} }
} }

4
vendor/modules.txt vendored
View File

@@ -5,7 +5,7 @@ cloud.google.com/go/compute/metadata
## explicit ## explicit
code.gitea.io/gitea-vet code.gitea.io/gitea-vet
code.gitea.io/gitea-vet/checks code.gitea.io/gitea-vet/checks
# code.gitea.io/sdk/gitea v0.13.2 # code.gitea.io/sdk/gitea v0.14.0
## explicit ## explicit
code.gitea.io/sdk/gitea code.gitea.io/sdk/gitea
# gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7 # gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
@@ -798,7 +798,7 @@ github.com/xi2/xz
# github.com/yohcop/openid-go v1.0.0 # github.com/yohcop/openid-go v1.0.0
## explicit ## explicit
github.com/yohcop/openid-go github.com/yohcop/openid-go
# github.com/yuin/goldmark v1.3.2 # github.com/yuin/goldmark v1.3.3
## explicit ## explicit
github.com/yuin/goldmark github.com/yuin/goldmark
github.com/yuin/goldmark/ast github.com/yuin/goldmark/ast

View File

@@ -1167,6 +1167,7 @@
> a { > a {
padding-top: 5px; padding-top: 5px;
padding-right: 10px; padding-right: 10px;
font-size: 1.5rem;
color: var(--color-text); color: var(--color-text);
&:hover { &:hover {