mirror of
https://github.com/go-gitea/gitea
synced 2024-12-22 12:04:12 +01:00
[Refactor] CombinedStatus and CommitStatus related functions & structs (#14026)
* RM unused struct * rename (*CommitStatus) loadRepo() -> loadAttributes() * move ToCommitStatus into its own file * use CommitStatusState instead of StatusState * move CombinedStatus convertion into convert package * let models.GetLatestCommitStatus use repoID direct and accept ListOptions * update swagger docs * fix tests * Fix swagger docs * rm page * fix swagger docs!!! * return json null * always return json * rename api.Status to api.CommitStatus * fix swagger docs * sec swagger fix
This commit is contained in:
parent
27edc1aa19
commit
e483220ea3
17 changed files with 341 additions and 206 deletions
|
@ -70,7 +70,7 @@ func TestPullCreate_CommitStatus(t *testing.T) {
|
||||||
token := getTokenForLoggedInUser(t, session)
|
token := getTokenForLoggedInUser(t, session)
|
||||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token),
|
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/user1/repo1/statuses/%s?token=%s", commitID, token),
|
||||||
api.CreateStatusOption{
|
api.CreateStatusOption{
|
||||||
State: api.StatusState(status),
|
State: status,
|
||||||
TargetURL: "http://test.ci/",
|
TargetURL: "http://test.ci/",
|
||||||
Description: "",
|
Description: "",
|
||||||
Context: "testci",
|
Context: "testci",
|
||||||
|
|
|
@ -51,7 +51,7 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
||||||
// Call API to add status for commit
|
// Call API to add status for commit
|
||||||
req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/statuses/"+path.Base(commitURL)+"?token="+token,
|
req = NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/statuses/"+path.Base(commitURL)+"?token="+token,
|
||||||
api.CreateStatusOption{
|
api.CreateStatusOption{
|
||||||
State: api.StatusState(state),
|
State: api.CommitStatusState(state),
|
||||||
TargetURL: "http://test.ci/",
|
TargetURL: "http://test.ci/",
|
||||||
Description: "",
|
Description: "",
|
||||||
Context: "testci",
|
Context: "testci",
|
||||||
|
@ -83,11 +83,11 @@ func doTestRepoCommitWithStatus(t *testing.T, state string, classes ...string) {
|
||||||
|
|
||||||
func testRepoCommitsWithStatus(t *testing.T, resp *httptest.ResponseRecorder, state string) {
|
func testRepoCommitsWithStatus(t *testing.T, resp *httptest.ResponseRecorder, state string) {
|
||||||
decoder := json.NewDecoder(resp.Body)
|
decoder := json.NewDecoder(resp.Body)
|
||||||
statuses := []*api.Status{}
|
statuses := []*api.CommitStatus{}
|
||||||
assert.NoError(t, decoder.Decode(&statuses))
|
assert.NoError(t, decoder.Decode(&statuses))
|
||||||
assert.Len(t, statuses, 1)
|
assert.Len(t, statuses, 1)
|
||||||
for _, s := range statuses {
|
for _, s := range statuses {
|
||||||
assert.Equal(t, api.StatusState(state), s.State)
|
assert.Equal(t, api.CommitStatusState(state), s.State)
|
||||||
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/statuses/65f1bf27bc3bf70f64657658635e66094edbcb4d", s.URL)
|
assert.Equal(t, setting.AppURL+"api/v1/repos/user2/repo1/statuses/65f1bf27bc3bf70f64657658635e66094edbcb4d", s.URL)
|
||||||
assert.Equal(t, "http://test.ci/", s.TargetURL)
|
assert.Equal(t, "http://test.ci/", s.TargetURL)
|
||||||
assert.Equal(t, "", s.Description)
|
assert.Equal(t, "", s.Description)
|
||||||
|
|
|
@ -38,7 +38,7 @@ type CommitStatus struct {
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (status *CommitStatus) loadRepo(e Engine) (err error) {
|
func (status *CommitStatus) loadAttributes(e Engine) (err error) {
|
||||||
if status.Repo == nil {
|
if status.Repo == nil {
|
||||||
status.Repo, err = getRepositoryByID(e, status.RepoID)
|
status.Repo, err = getRepositoryByID(e, status.RepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -56,7 +56,7 @@ func (status *CommitStatus) loadRepo(e Engine) (err error) {
|
||||||
|
|
||||||
// APIURL returns the absolute APIURL to this commit-status.
|
// APIURL returns the absolute APIURL to this commit-status.
|
||||||
func (status *CommitStatus) APIURL() string {
|
func (status *CommitStatus) APIURL() string {
|
||||||
_ = status.loadRepo(x)
|
_ = status.loadAttributes(x)
|
||||||
return fmt.Sprintf("%sapi/v1/repos/%s/statuses/%s",
|
return fmt.Sprintf("%sapi/v1/repos/%s/statuses/%s",
|
||||||
setting.AppURL, status.Repo.FullName(), status.SHA)
|
setting.AppURL, status.Repo.FullName(), status.SHA)
|
||||||
}
|
}
|
||||||
|
@ -139,13 +139,20 @@ func sortCommitStatusesSession(sess *xorm.Session, sortType string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
|
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
|
||||||
func GetLatestCommitStatus(repo *Repository, sha string, page int) ([]*CommitStatus, error) {
|
func GetLatestCommitStatus(repoID int64, sha string, listOptions ListOptions) ([]*CommitStatus, error) {
|
||||||
|
return getLatestCommitStatus(x, repoID, sha, listOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLatestCommitStatus(e Engine, repoID int64, sha string, listOptions ListOptions) ([]*CommitStatus, error) {
|
||||||
ids := make([]int64, 0, 10)
|
ids := make([]int64, 0, 10)
|
||||||
err := x.Limit(10, page*10).
|
sess := e.Table(&CommitStatus{}).
|
||||||
Table(&CommitStatus{}).
|
Where("repo_id = ?", repoID).And("sha = ?", sha).
|
||||||
Where("repo_id = ?", repo.ID).And("sha = ?", sha).
|
|
||||||
Select("max( id ) as id").
|
Select("max( id ) as id").
|
||||||
GroupBy("context_hash").OrderBy("max( id ) desc").Find(&ids)
|
GroupBy("context_hash").OrderBy("max( id ) desc")
|
||||||
|
|
||||||
|
sess = listOptions.setSessionPagination(sess)
|
||||||
|
|
||||||
|
err := sess.Find(&ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -261,7 +268,7 @@ func ParseCommitsWithStatus(oldCommits *list.List, repo *Repository) *list.List
|
||||||
commit := SignCommitWithStatuses{
|
commit := SignCommitWithStatuses{
|
||||||
SignCommit: &c,
|
SignCommit: &c,
|
||||||
}
|
}
|
||||||
statuses, err := GetLatestCommitStatus(repo, commit.ID.String(), 0)
|
statuses, err := GetLatestCommitStatus(repo.ID, commit.ID.String(), ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetLatestCommitStatus: %v", err)
|
log.Error("GetLatestCommitStatus: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -347,27 +347,6 @@ func ToOAuth2Application(app *models.OAuth2Application) *api.OAuth2Application {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToCommitStatus converts models.CommitStatus to api.Status
|
|
||||||
func ToCommitStatus(status *models.CommitStatus) *api.Status {
|
|
||||||
apiStatus := &api.Status{
|
|
||||||
Created: status.CreatedUnix.AsTime(),
|
|
||||||
Updated: status.CreatedUnix.AsTime(),
|
|
||||||
State: api.StatusState(status.State),
|
|
||||||
TargetURL: status.TargetURL,
|
|
||||||
Description: status.Description,
|
|
||||||
ID: status.Index,
|
|
||||||
URL: status.APIURL(),
|
|
||||||
Context: status.Context,
|
|
||||||
}
|
|
||||||
|
|
||||||
if status.CreatorID != 0 {
|
|
||||||
creator, _ := models.GetUserByID(status.CreatorID)
|
|
||||||
apiStatus.Creator = ToUser(creator, false, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToLFSLock convert a LFSLock to api.LFSLock
|
// ToLFSLock convert a LFSLock to api.LFSLock
|
||||||
func ToLFSLock(l *models.LFSLock) *api.LFSLock {
|
func ToLFSLock(l *models.LFSLock) *api.LFSLock {
|
||||||
return &api.LFSLock{
|
return &api.LFSLock{
|
||||||
|
|
56
modules/convert/status.go
Normal file
56
modules/convert/status.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// 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 convert
|
||||||
|
|
||||||
|
import (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ToCommitStatus converts models.CommitStatus to api.CommitStatus
|
||||||
|
func ToCommitStatus(status *models.CommitStatus) *api.CommitStatus {
|
||||||
|
apiStatus := &api.CommitStatus{
|
||||||
|
Created: status.CreatedUnix.AsTime(),
|
||||||
|
Updated: status.CreatedUnix.AsTime(),
|
||||||
|
State: status.State,
|
||||||
|
TargetURL: status.TargetURL,
|
||||||
|
Description: status.Description,
|
||||||
|
ID: status.Index,
|
||||||
|
URL: status.APIURL(),
|
||||||
|
Context: status.Context,
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.CreatorID != 0 {
|
||||||
|
creator, _ := models.GetUserByID(status.CreatorID)
|
||||||
|
apiStatus.Creator = ToUser(creator, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToCombinedStatus converts List of CommitStatus to a CombinedStatus
|
||||||
|
func ToCombinedStatus(statuses []*models.CommitStatus, repo *api.Repository) *api.CombinedStatus {
|
||||||
|
|
||||||
|
if len(statuses) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
retStatus := &api.CombinedStatus{
|
||||||
|
SHA: statuses[0].SHA,
|
||||||
|
TotalCount: len(statuses),
|
||||||
|
Repository: repo,
|
||||||
|
URL: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
retStatus.Statuses = make([]*api.CommitStatus, 0, len(statuses))
|
||||||
|
for _, status := range statuses {
|
||||||
|
retStatus.Statuses = append(retStatus.Statuses, ToCommitStatus(status))
|
||||||
|
if status.State.NoBetterThan(retStatus.State) {
|
||||||
|
retStatus.State = status.State
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retStatus
|
||||||
|
}
|
|
@ -113,7 +113,7 @@ func (graph *Graph) LoadAndProcessCommits(repository *models.Repository, gitRepo
|
||||||
|
|
||||||
_ = models.CalculateTrustStatus(c.Verification, repository, &keyMap)
|
_ = models.CalculateTrustStatus(c.Verification, repository, &keyMap)
|
||||||
|
|
||||||
statuses, err := models.GetLatestCommitStatus(repository, c.Commit.ID.String(), 0)
|
statuses, err := models.GetLatestCommitStatus(repository.ID, c.Commit.ID.String(), models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetLatestCommitStatus: %v", err)
|
log.Error("GetLatestCommitStatus: %v", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,20 +4,20 @@
|
||||||
|
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
// CommitStatusState holds the state of a Status
|
// CommitStatusState holds the state of a CommitStatus
|
||||||
// It can be "pending", "success", "error", "failure", and "warning"
|
// It can be "pending", "success", "error", "failure", and "warning"
|
||||||
type CommitStatusState string
|
type CommitStatusState string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CommitStatusPending is for when the Status is Pending
|
// CommitStatusPending is for when the CommitStatus is Pending
|
||||||
CommitStatusPending CommitStatusState = "pending"
|
CommitStatusPending CommitStatusState = "pending"
|
||||||
// CommitStatusSuccess is for when the Status is Success
|
// CommitStatusSuccess is for when the CommitStatus is Success
|
||||||
CommitStatusSuccess CommitStatusState = "success"
|
CommitStatusSuccess CommitStatusState = "success"
|
||||||
// CommitStatusError is for when the Status is Error
|
// CommitStatusError is for when the CommitStatus is Error
|
||||||
CommitStatusError CommitStatusState = "error"
|
CommitStatusError CommitStatusState = "error"
|
||||||
// CommitStatusFailure is for when the Status is Failure
|
// CommitStatusFailure is for when the CommitStatus is Failure
|
||||||
CommitStatusFailure CommitStatusState = "failure"
|
CommitStatusFailure CommitStatusState = "failure"
|
||||||
// CommitStatusWarning is for when the Status is Warning
|
// CommitStatusWarning is for when the CommitStatus is Warning
|
||||||
CommitStatusWarning CommitStatusState = "warning"
|
CommitStatusWarning CommitStatusState = "warning"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,32 +8,15 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatusState holds the state of a Status
|
// CommitStatus holds a single status of a single Commit
|
||||||
// It can be "pending", "success", "error", "failure", and "warning"
|
type CommitStatus struct {
|
||||||
type StatusState string
|
ID int64 `json:"id"`
|
||||||
|
State CommitStatusState `json:"status"`
|
||||||
const (
|
TargetURL string `json:"target_url"`
|
||||||
// StatusPending is for when the Status is Pending
|
Description string `json:"description"`
|
||||||
StatusPending StatusState = "pending"
|
URL string `json:"url"`
|
||||||
// StatusSuccess is for when the Status is Success
|
Context string `json:"context"`
|
||||||
StatusSuccess StatusState = "success"
|
Creator *User `json:"creator"`
|
||||||
// StatusError is for when the Status is Error
|
|
||||||
StatusError StatusState = "error"
|
|
||||||
// StatusFailure is for when the Status is Failure
|
|
||||||
StatusFailure StatusState = "failure"
|
|
||||||
// StatusWarning is for when the Status is Warning
|
|
||||||
StatusWarning StatusState = "warning"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Status holds a single Status of a single Commit
|
|
||||||
type Status struct {
|
|
||||||
ID int64 `json:"id"`
|
|
||||||
State StatusState `json:"status"`
|
|
||||||
TargetURL string `json:"target_url"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
Context string `json:"context"`
|
|
||||||
Creator *User `json:"creator"`
|
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
Created time.Time `json:"created_at"`
|
Created time.Time `json:"created_at"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
|
@ -42,24 +25,19 @@ type Status struct {
|
||||||
|
|
||||||
// CombinedStatus holds the combined state of several statuses for a single commit
|
// CombinedStatus holds the combined state of several statuses for a single commit
|
||||||
type CombinedStatus struct {
|
type CombinedStatus struct {
|
||||||
State StatusState `json:"state"`
|
State CommitStatusState `json:"state"`
|
||||||
SHA string `json:"sha"`
|
SHA string `json:"sha"`
|
||||||
TotalCount int `json:"total_count"`
|
TotalCount int `json:"total_count"`
|
||||||
Statuses []*Status `json:"statuses"`
|
Statuses []*CommitStatus `json:"statuses"`
|
||||||
Repository *Repository `json:"repository"`
|
Repository *Repository `json:"repository"`
|
||||||
CommitURL string `json:"commit_url"`
|
CommitURL string `json:"commit_url"`
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateStatusOption holds the information needed to create a new Status for a Commit
|
// CreateStatusOption holds the information needed to create a new CommitStatus for a Commit
|
||||||
type CreateStatusOption struct {
|
type CreateStatusOption struct {
|
||||||
State StatusState `json:"state"`
|
State CommitStatusState `json:"state"`
|
||||||
TargetURL string `json:"target_url"`
|
TargetURL string `json:"target_url"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Context string `json:"context"`
|
Context string `json:"context"`
|
||||||
}
|
|
||||||
|
|
||||||
// ListStatusesOption holds pagination information
|
|
||||||
type ListStatusesOption struct {
|
|
||||||
Page int
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func NewCommitStatus(ctx *context.APIContext, form api.CreateStatusOption) {
|
||||||
// "$ref": "#/definitions/CreateStatusOption"
|
// "$ref": "#/definitions/CreateStatusOption"
|
||||||
// responses:
|
// responses:
|
||||||
// "201":
|
// "201":
|
||||||
// "$ref": "#/responses/Status"
|
// "$ref": "#/responses/CommitStatus"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func GetCommitStatuses(ctx *context.APIContext) {
|
||||||
// type: integer
|
// type: integer
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/StatusList"
|
// "$ref": "#/responses/CommitStatusList"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ func GetCommitStatusesByRef(ctx *context.APIContext) {
|
||||||
// type: integer
|
// type: integer
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/StatusList"
|
// "$ref": "#/responses/CommitStatusList"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatuses := make([]*api.Status, 0, len(statuses))
|
apiStatuses := make([]*api.CommitStatus, 0, len(statuses))
|
||||||
for _, status := range statuses {
|
for _, status := range statuses {
|
||||||
apiStatuses = append(apiStatuses, convert.ToCommitStatus(status))
|
apiStatuses = append(apiStatuses, convert.ToCommitStatus(status))
|
||||||
}
|
}
|
||||||
|
@ -233,19 +233,9 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
|
||||||
ctx.JSON(http.StatusOK, apiStatuses)
|
ctx.JSON(http.StatusOK, apiStatuses)
|
||||||
}
|
}
|
||||||
|
|
||||||
type combinedCommitStatus struct {
|
|
||||||
State api.CommitStatusState `json:"state"`
|
|
||||||
SHA string `json:"sha"`
|
|
||||||
TotalCount int `json:"total_count"`
|
|
||||||
Statuses []*api.Status `json:"statuses"`
|
|
||||||
Repo *api.Repository `json:"repository"`
|
|
||||||
CommitURL string `json:"commit_url"`
|
|
||||||
URL string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCombinedCommitStatusByRef returns the combined status for any given commit hash
|
// GetCombinedCommitStatusByRef returns the combined status for any given commit hash
|
||||||
func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
|
func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/commits/{ref}/statuses repository repoGetCombinedStatusByRef
|
// swagger:operation GET /repos/{owner}/{repo}/commits/{ref}/status repository repoGetCombinedStatusByRef
|
||||||
// ---
|
// ---
|
||||||
// summary: Get a commit's combined status, by branch/tag/commit reference
|
// summary: Get a commit's combined status, by branch/tag/commit reference
|
||||||
// produces:
|
// produces:
|
||||||
|
@ -268,12 +258,15 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
|
||||||
// required: true
|
// required: true
|
||||||
// - name: page
|
// - name: page
|
||||||
// in: query
|
// in: query
|
||||||
// description: page number of results
|
// description: page number of results to return (1-based)
|
||||||
|
// type: integer
|
||||||
|
// - name: limit
|
||||||
|
// in: query
|
||||||
|
// description: page size of results
|
||||||
// type: integer
|
// type: integer
|
||||||
// required: false
|
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/Status"
|
// "$ref": "#/responses/CombinedStatus"
|
||||||
// "400":
|
// "400":
|
||||||
// "$ref": "#/responses/error"
|
// "$ref": "#/responses/error"
|
||||||
|
|
||||||
|
@ -284,33 +277,18 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
repo := ctx.Repo.Repository
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
page := ctx.QueryInt("page")
|
statuses, err := models.GetLatestCommitStatus(repo.ID, sha, utils.GetListOptions(ctx))
|
||||||
|
|
||||||
statuses, err := models.GetLatestCommitStatus(repo, sha, page)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetLatestCommitStatus", fmt.Errorf("GetLatestCommitStatus[%s, %s, %d]: %v", repo.FullName(), sha, page, err))
|
ctx.Error(http.StatusInternalServerError, "GetLatestCommitStatus", fmt.Errorf("GetLatestCommitStatus[%s, %s]: %v", repo.FullName(), sha, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(statuses) == 0 {
|
if len(statuses) == 0 {
|
||||||
ctx.Status(http.StatusOK)
|
ctx.JSON(http.StatusOK, &api.CombinedStatus{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
retStatus := &combinedCommitStatus{
|
combiStatus := convert.ToCombinedStatus(statuses, convert.ToRepo(repo, ctx.Repo.AccessMode))
|
||||||
SHA: sha,
|
|
||||||
TotalCount: len(statuses),
|
|
||||||
Repo: convert.ToRepo(repo, ctx.Repo.AccessMode),
|
|
||||||
URL: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
retStatus.Statuses = make([]*api.Status, 0, len(statuses))
|
ctx.JSON(http.StatusOK, combiStatus)
|
||||||
for _, status := range statuses {
|
|
||||||
retStatus.Statuses = append(retStatus.Statuses, convert.ToCommitStatus(status))
|
|
||||||
if status.State.NoBetterThan(retStatus.State) {
|
|
||||||
retStatus.State = status.State
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, retStatus)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,18 +169,18 @@ type swaggerResponsePullReviewCommentList struct {
|
||||||
Body []api.PullReviewComment `json:"body"`
|
Body []api.PullReviewComment `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status
|
// CommitStatus
|
||||||
// swagger:response Status
|
// swagger:response CommitStatus
|
||||||
type swaggerResponseStatus struct {
|
type swaggerResponseStatus struct {
|
||||||
// in:body
|
// in:body
|
||||||
Body api.Status `json:"body"`
|
Body api.CommitStatus `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusList
|
// CommitStatusList
|
||||||
// swagger:response StatusList
|
// swagger:response CommitStatusList
|
||||||
type swaggerResponseStatusList struct {
|
type swaggerResponseCommitStatusList struct {
|
||||||
// in:body
|
// in:body
|
||||||
Body []api.Status `json:"body"`
|
Body []api.CommitStatus `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchInfo
|
// WatchInfo
|
||||||
|
@ -309,3 +309,10 @@ type swaggerLanguageStatistics struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body map[string]int64 `json:"body"`
|
Body map[string]int64 `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CombinedStatus
|
||||||
|
// swagger:response CombinedStatus
|
||||||
|
type swaggerCombinedStatus struct {
|
||||||
|
// in: body
|
||||||
|
Body api.CombinedStatus `json:"body"`
|
||||||
|
}
|
||||||
|
|
|
@ -87,7 +87,7 @@ func RefBlame(ctx *context.Context) {
|
||||||
ctx.Data["LatestCommitVerification"] = models.ParseCommitWithSignature(latestCommit)
|
ctx.Data["LatestCommitVerification"] = models.ParseCommitWithSignature(latestCommit)
|
||||||
ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
|
ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
|
||||||
|
|
||||||
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, ctx.Repo.Commit.ID.String(), 0)
|
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetLatestCommitStatus: %v", err)
|
log.Error("GetLatestCommitStatus: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,7 +296,7 @@ func Diff(ctx *context.Context) {
|
||||||
commitID = commit.ID.String()
|
commitID = commit.ID.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, commitID, 0)
|
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, commitID, models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetLatestCommitStatus: %v", err)
|
log.Error("GetLatestCommitStatus: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -368,7 +368,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
|
||||||
ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
|
ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
|
commitStatuses, err := models.GetLatestCommitStatus(repo.ID, sha, models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetLatestCommitStatus", err)
|
ctx.ServerError("GetLatestCommitStatus", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -449,7 +449,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *models.Issue) *git.Compare
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
commitStatuses, err := models.GetLatestCommitStatus(repo, sha, 0)
|
commitStatuses, err := models.GetLatestCommitStatus(repo.ID, sha, models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetLatestCommitStatus", err)
|
ctx.ServerError("GetLatestCommitStatus", err)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -353,7 +353,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
|
||||||
|
|
||||||
ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
|
ctx.Data["LatestCommitUser"] = models.ValidateCommitWithEmail(latestCommit)
|
||||||
|
|
||||||
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository, ctx.Repo.Commit.ID.String(), 0)
|
statuses, err := models.GetLatestCommitStatus(ctx.Repo.Repository.ID, ctx.Repo.Commit.ID.String(), models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("GetLatestCommitStatus: %v", err)
|
log.Error("GetLatestCommitStatus: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ func GetPullRequestCommitStatusState(pr *models.PullRequest) (structs.CommitStat
|
||||||
return "", errors.Wrap(err, "LoadBaseRepo")
|
return "", errors.Wrap(err, "LoadBaseRepo")
|
||||||
}
|
}
|
||||||
|
|
||||||
commitStatuses, err := models.GetLatestCommitStatus(pr.BaseRepo, sha, 0)
|
commitStatuses, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, sha, models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "GetLatestCommitStatus")
|
return "", errors.Wrap(err, "GetLatestCommitStatus")
|
||||||
}
|
}
|
||||||
|
|
|
@ -665,7 +665,7 @@ func GetLastCommitStatus(pr *models.PullRequest) (status *models.CommitStatus, e
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
statusList, err := models.GetLatestCommitStatus(pr.BaseRepo, lastCommitID, 0)
|
statusList, err := models.GetLatestCommitStatus(pr.BaseRepo.ID, lastCommitID, models.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -2867,7 +2867,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{owner}/{repo}/commits/{ref}/statuses": {
|
"/repos/{owner}/{repo}/commits/{ref}/status": {
|
||||||
"get": {
|
"get": {
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/json"
|
"application/json"
|
||||||
|
@ -2901,14 +2901,101 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "page number of results",
|
"description": "page number of results to return (1-based)",
|
||||||
"name": "page",
|
"name": "page",
|
||||||
"in": "query"
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page size of results",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/Status"
|
"$ref": "#/responses/CombinedStatus"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"$ref": "#/responses/error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/repos/{owner}/{repo}/commits/{ref}/statuses": {
|
||||||
|
"get": {
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Get a commit's statuses, by branch/tag/commit reference",
|
||||||
|
"operationId": "repoListStatusesByRef",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "owner of the repo",
|
||||||
|
"name": "owner",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the repo",
|
||||||
|
"name": "repo",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of branch/tag/commit",
|
||||||
|
"name": "ref",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"oldest",
|
||||||
|
"recentupdate",
|
||||||
|
"leastupdate",
|
||||||
|
"leastindex",
|
||||||
|
"highestindex"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"description": "type of sort",
|
||||||
|
"name": "sort",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"enum": [
|
||||||
|
"pending",
|
||||||
|
"success",
|
||||||
|
"error",
|
||||||
|
"failure",
|
||||||
|
"warning"
|
||||||
|
],
|
||||||
|
"type": "string",
|
||||||
|
"description": "type of state",
|
||||||
|
"name": "state",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page number of results to return (1-based)",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "page size of results",
|
||||||
|
"name": "limit",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"$ref": "#/responses/CommitStatusList"
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
|
@ -8462,7 +8549,7 @@
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"$ref": "#/responses/StatusList"
|
"$ref": "#/responses/CommitStatusList"
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
|
@ -8510,7 +8597,7 @@
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"201": {
|
"201": {
|
||||||
"$ref": "#/responses/Status"
|
"$ref": "#/responses/CommitStatus"
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"$ref": "#/responses/error"
|
"$ref": "#/responses/error"
|
||||||
|
@ -11431,6 +11518,43 @@
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"CombinedStatus": {
|
||||||
|
"description": "CombinedStatus holds the combined state of several statuses for a single commit",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"commit_url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "CommitURL"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"$ref": "#/definitions/Repository"
|
||||||
|
},
|
||||||
|
"sha": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "SHA"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"$ref": "#/definitions/CommitStatusState"
|
||||||
|
},
|
||||||
|
"statuses": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/CommitStatus"
|
||||||
|
},
|
||||||
|
"x-go-name": "Statuses"
|
||||||
|
},
|
||||||
|
"total_count": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"x-go-name": "TotalCount"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "URL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"Comment": {
|
"Comment": {
|
||||||
"description": "Comment represents a comment on a commit or issue",
|
"description": "Comment represents a comment on a commit or issue",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -11558,6 +11682,55 @@
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"CommitStatus": {
|
||||||
|
"description": "CommitStatus holds a single status of a single Commit",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"context": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "Context"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"x-go-name": "Created"
|
||||||
|
},
|
||||||
|
"creator": {
|
||||||
|
"$ref": "#/definitions/User"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "Description"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"x-go-name": "ID"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"$ref": "#/definitions/CommitStatusState"
|
||||||
|
},
|
||||||
|
"target_url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "TargetURL"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time",
|
||||||
|
"x-go-name": "Updated"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"x-go-name": "URL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
|
"CommitStatusState": {
|
||||||
|
"description": "CommitStatusState holds the state of a CommitStatus\nIt can be \"pending\", \"success\", \"error\", \"failure\", and \"warning\"",
|
||||||
|
"type": "string",
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"CommitUser": {
|
"CommitUser": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "CommitUser contains information of a user in the context of a commit.",
|
"title": "CommitUser contains information of a user in the context of a commit.",
|
||||||
|
@ -12341,7 +12514,7 @@
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
"CreateStatusOption": {
|
"CreateStatusOption": {
|
||||||
"description": "CreateStatusOption holds the information needed to create a new Status for a Commit",
|
"description": "CreateStatusOption holds the information needed to create a new CommitStatus for a Commit",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"context": {
|
"context": {
|
||||||
|
@ -12353,7 +12526,7 @@
|
||||||
"x-go-name": "Description"
|
"x-go-name": "Description"
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"$ref": "#/definitions/StatusState"
|
"$ref": "#/definitions/CommitStatusState"
|
||||||
},
|
},
|
||||||
"target_url": {
|
"target_url": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -15251,55 +15424,6 @@
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
"Status": {
|
|
||||||
"description": "Status holds a single Status of a single Commit",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"context": {
|
|
||||||
"type": "string",
|
|
||||||
"x-go-name": "Context"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time",
|
|
||||||
"x-go-name": "Created"
|
|
||||||
},
|
|
||||||
"creator": {
|
|
||||||
"$ref": "#/definitions/User"
|
|
||||||
},
|
|
||||||
"description": {
|
|
||||||
"type": "string",
|
|
||||||
"x-go-name": "Description"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64",
|
|
||||||
"x-go-name": "ID"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"$ref": "#/definitions/StatusState"
|
|
||||||
},
|
|
||||||
"target_url": {
|
|
||||||
"type": "string",
|
|
||||||
"x-go-name": "TargetURL"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"type": "string",
|
|
||||||
"format": "date-time",
|
|
||||||
"x-go-name": "Updated"
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"type": "string",
|
|
||||||
"x-go-name": "URL"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
|
||||||
},
|
|
||||||
"StatusState": {
|
|
||||||
"description": "StatusState holds the state of a Status\nIt can be \"pending\", \"success\", \"error\", \"failure\", and \"warning\"",
|
|
||||||
"type": "string",
|
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
|
||||||
},
|
|
||||||
"StopWatch": {
|
"StopWatch": {
|
||||||
"description": "StopWatch represent a running stopwatch",
|
"description": "StopWatch represent a running stopwatch",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
@ -15773,6 +15897,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"CombinedStatus": {
|
||||||
|
"description": "CombinedStatus",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/CombinedStatus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"Comment": {
|
"Comment": {
|
||||||
"description": "Comment",
|
"description": "Comment",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
@ -15829,6 +15959,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"CommitStatus": {
|
||||||
|
"description": "CommitStatus",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/CommitStatus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"CommitStatusList": {
|
||||||
|
"description": "CommitStatusList",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/CommitStatus"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ContentsListResponse": {
|
"ContentsListResponse": {
|
||||||
"description": "ContentsListResponse",
|
"description": "ContentsListResponse",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
@ -16235,21 +16380,6 @@
|
||||||
"$ref": "#/definitions/ServerVersion"
|
"$ref": "#/definitions/ServerVersion"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Status": {
|
|
||||||
"description": "Status",
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/Status"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"StatusList": {
|
|
||||||
"description": "StatusList",
|
|
||||||
"schema": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/Status"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"StopWatch": {
|
"StopWatch": {
|
||||||
"description": "StopWatch",
|
"description": "StopWatch",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
|
Loading…
Reference in a new issue