Add ignoreVersion & manuall version set option (#560)

be able to skip version check if needed.

!!! Be careful, because using it incorrectly can result in infinite loops with pagination !!!

Reviewed-on: https://gitea.com/gitea/go-sdk/pulls/560
Reviewed-by: Andrew Thornton <art27@cantab.net>
Reviewed-by: Norwin <noerw@noreply.gitea.io>
This commit is contained in:
6543 2022-01-05 00:31:31 +08:00
parent a87a2c7390
commit 635de1b821
8 changed files with 92 additions and 41 deletions

View file

@ -30,18 +30,20 @@ func Version() string {
// Client represents a thread-safe Gitea API client.
type Client struct {
url string
accessToken string
username string
password string
otp string
sudo string
debug bool
client *http.Client
ctx context.Context
mutex sync.RWMutex
url string
accessToken string
username string
password string
otp string
sudo string
debug bool
client *http.Client
ctx context.Context
mutex sync.RWMutex
serverVersion *version.Version
getVersionOnce sync.Once
ignoreVersion bool // only set by SetGiteaVersion so don't need a mutex lock
}
// Response represents the gitea response
@ -49,16 +51,21 @@ type Response struct {
*http.Response
}
// ClientOption are functions used to init a new client
type ClientOption func(*Client) error
// 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 ...ClientOption) (*Client, error) {
client := &Client{
url: strings.TrimSuffix(url, "/"),
client: &http.Client{},
ctx: context.Background(),
}
for _, opt := range options {
opt(client)
if err := opt(client); err != nil {
return nil, err
}
}
if err := client.checkServerVersionGreaterThanOrEqual(version1_11_0); err != nil {
return nil, err
@ -74,9 +81,10 @@ func NewClientWithHTTP(url string, httpClient *http.Client) *Client {
}
// SetHTTPClient is an option for NewClient to set custom http client
func SetHTTPClient(httpClient *http.Client) func(client *Client) {
return func(client *Client) {
func SetHTTPClient(httpClient *http.Client) ClientOption {
return func(client *Client) error {
client.SetHTTPClient(httpClient)
return nil
}
}
@ -88,18 +96,20 @@ func (c *Client) SetHTTPClient(client *http.Client) {
}
// SetToken is an option for NewClient to set token
func SetToken(token string) func(client *Client) {
return func(client *Client) {
func SetToken(token string) ClientOption {
return func(client *Client) error {
client.mutex.Lock()
client.accessToken = token
client.mutex.Unlock()
return nil
}
}
// SetBasicAuth is an option for NewClient to set username and password
func SetBasicAuth(username, password string) func(client *Client) {
return func(client *Client) {
func SetBasicAuth(username, password string) ClientOption {
return func(client *Client) error {
client.SetBasicAuth(username, password)
return nil
}
}
@ -111,9 +121,10 @@ func (c *Client) SetBasicAuth(username, password string) {
}
// SetOTP is an option for NewClient to set OTP for 2FA
func SetOTP(otp string) func(client *Client) {
return func(client *Client) {
func SetOTP(otp string) ClientOption {
return func(client *Client) error {
client.SetOTP(otp)
return nil
}
}
@ -124,14 +135,15 @@ func (c *Client) SetOTP(otp string) {
c.mutex.Unlock()
}
// SetContext is an option for NewClient to set context
func SetContext(ctx context.Context) func(client *Client) {
return func(client *Client) {
// SetContext is an option for NewClient to set the default context
func SetContext(ctx context.Context) ClientOption {
return func(client *Client) error {
client.SetContext(ctx)
return nil
}
}
// SetContext set context witch is used for http requests
// SetContext set default context witch is used for http requests
func (c *Client) SetContext(ctx context.Context) {
c.mutex.Lock()
c.ctx = ctx
@ -139,9 +151,10 @@ func (c *Client) SetContext(ctx context.Context) {
}
// SetSudo is an option for NewClient to set sudo header
func SetSudo(sudo string) func(client *Client) {
return func(client *Client) {
func SetSudo(sudo string) ClientOption {
return func(client *Client) error {
client.SetSudo(sudo)
return nil
}
}
@ -153,11 +166,12 @@ func (c *Client) SetSudo(sudo string) {
}
// SetDebugMode is an option for NewClient to enable debug mode
func SetDebugMode() func(client *Client) {
return func(client *Client) {
func SetDebugMode() ClientOption {
return func(client *Client) error {
client.mutex.Lock()
client.debug = true
client.mutex.Unlock()
return nil
}
}

View file

@ -73,7 +73,7 @@ func (c *Client) GetMilestone(owner, repo string, id int64) (*Milestone, *Respon
// GetMilestoneByName get one milestone by repo and milestone name
func (c *Client) GetMilestoneByName(owner, repo string, name string) (*Milestone, *Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode
m, resp, err := c.resolveMilestoneByName(owner, repo, name)
return m, resp, err
@ -164,7 +164,7 @@ func (c *Client) EditMilestone(owner, repo string, id int64, opt EditMilestoneOp
// EditMilestoneByName modify milestone with options
func (c *Client) EditMilestoneByName(owner, repo string, name string, opt EditMilestoneOption) (*Milestone, *Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode
m, _, err := c.resolveMilestoneByName(owner, repo, name)
if err != nil {
@ -198,7 +198,7 @@ func (c *Client) DeleteMilestone(owner, repo string, id int64) (*Response, error
// DeleteMilestoneByName delete one milestone by name
func (c *Client) DeleteMilestoneByName(owner, repo string, name string) (*Response, error) {
if c.CheckServerVersionConstraint(">=1.13") != nil {
if c.checkServerVersionGreaterThanOrEqual(version1_13_0) != nil {
// backwards compatibility mode
m, _, err := c.resolveMilestoneByName(owner, repo, name)
if err != nil {

View file

@ -40,14 +40,16 @@ func enableRunGitea() bool {
}
func newTestClient() *Client {
c, _ := NewClient(getGiteaURL(), newTestClientAuth())
return c
}
func newTestClientAuth() ClientOption {
token := getGiteaToken()
if token == "" {
client := NewClientWithHTTP(getGiteaURL(), &http.Client{})
client.SetBasicAuth(getGiteaUsername(), getGiteaPassword())
return client
return SetBasicAuth(getGiteaUsername(), getGiteaPassword())
}
c, _ := NewClient(getGiteaURL(), SetToken(getGiteaToken()))
return c
return SetToken(getGiteaToken())
}
func giteaMasterPath() string {

View file

@ -12,6 +12,8 @@ import (
"net/url"
"strings"
"time"
"github.com/hashicorp/go-version"
)
// PRBranchInfo information about a branch
@ -217,10 +219,12 @@ type MergePullRequestOption struct {
ForceMerge bool `json:"force_merge"`
}
var version1_11_5, _ = version.NewVersion("1.11.5")
// Validate the MergePullRequestOption struct
func (opt MergePullRequestOption) Validate(c *Client) error {
if opt.Style == MergeStyleSquash {
if err := c.CheckServerVersionConstraint(">=1.11.5"); err != nil {
if err := c.checkServerVersionGreaterThanOrEqual(version1_11_5); err != nil {
return err
}
}

View file

@ -335,7 +335,7 @@ func (opt CreateRepoOption) Validate(c *Client) error {
return fmt.Errorf("name has more than 100 chars")
}
if len(opt.TrustModel) != 0 {
if err := c.CheckServerVersionConstraint(">=1.13.0"); err != nil {
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return err
}
}

View file

@ -77,7 +77,7 @@ func (c *Client) DeleteAccessToken(value interface{}) (*Response, error) {
case reflect.Int64:
token = fmt.Sprintf("%d", value.(int64))
case reflect.String:
if err := c.CheckServerVersionConstraint(">= 1.13.0"); err != nil {
if err := c.checkServerVersionGreaterThanOrEqual(version1_13_0); err != nil {
return nil, err
}
token = value.(string)

View file

@ -39,6 +39,25 @@ func (c *Client) CheckServerVersionConstraint(constraint string) error {
return nil
}
// SetGiteaVersion configures the Client to assume the given version of the
// Gitea server, instead of querying the server for it when initializing.
// Use "" to skip all canonical ways in the SDK to check for versions
func SetGiteaVersion(v string) ClientOption {
if v == "" {
return func(c *Client) error {
c.ignoreVersion = true
return nil
}
}
return func(c *Client) (err error) {
c.getVersionOnce.Do(func() {
c.serverVersion, err = version.NewVersion(v)
return
})
return
}
}
// predefined versions only have to be parsed by library once
var (
version1_11_0, _ = version.NewVersion("1.11.0")
@ -48,8 +67,11 @@ var (
version1_15_0, _ = version.NewVersion("1.15.0")
)
// checkServerVersionGreaterThanOrEqual is internally used to speed up things and ignore issues with prerelease
// checkServerVersionGreaterThanOrEqual is the canonical way in the SDK to check for versions for API compatibility reasons
func (c *Client) checkServerVersionGreaterThanOrEqual(v *version.Version) error {
if c.ignoreVersion {
return nil
}
if err := c.loadServerVersion(); err != nil {
return err
}

View file

@ -18,6 +18,15 @@ func TestVersion(t *testing.T) {
assert.NoError(t, err)
assert.True(t, true, rawVersion != "")
assert.NoError(t, c.CheckServerVersionConstraint(">= 1.11.0"))
assert.NoError(t, c.checkServerVersionGreaterThanOrEqual(version1_11_0))
assert.Error(t, c.CheckServerVersionConstraint("< 1.11.0"))
c.serverVersion = version1_11_0
assert.Error(t, c.checkServerVersionGreaterThanOrEqual(version1_15_0))
c.ignoreVersion = true
assert.NoError(t, c.checkServerVersionGreaterThanOrEqual(version1_15_0))
c, err = NewClient(getGiteaURL(), newTestClientAuth(), SetGiteaVersion("1.12.123"))
assert.NoError(t, err)
assert.NoError(t, c.CheckServerVersionConstraint("=1.12.123"))
}