* Fix #9189 - API Allow only specific Colums to be updated on Issue (#9539) * dont insert "-1" in any case to issue.poster_id * Make sure API cant override importand fields * code format * add Test for IssueEdit * load milestone and return it on IssueEdit via API * extend Test for TestAPIEditIssue * fix TEST * make sure Poster is loaded * keep code format on backport as it is
This commit is contained in:
parent
cb3fe4cbf1
commit
17c691f8aa
7 changed files with 116 additions and 23 deletions
|
@ -8,6 +8,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
@ -62,3 +63,61 @@ func TestAPICreateIssue(t *testing.T) {
|
||||||
Title: title,
|
Title: title,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIEditIssue(t *testing.T) {
|
||||||
|
prepareTestEnv(t)
|
||||||
|
|
||||||
|
issueBefore := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
|
||||||
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: issueBefore.RepoID}).(*models.Repository)
|
||||||
|
owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
|
||||||
|
assert.NoError(t, issueBefore.LoadAttributes())
|
||||||
|
assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix))
|
||||||
|
assert.Equal(t, api.StateOpen, issueBefore.State())
|
||||||
|
|
||||||
|
session := loginUser(t, owner.Name)
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
|
// update values of issue
|
||||||
|
issueState := "closed"
|
||||||
|
removeDeadline := time.Unix(0, 0)
|
||||||
|
milestone := int64(4)
|
||||||
|
body := "new content!"
|
||||||
|
title := "new title from api set"
|
||||||
|
|
||||||
|
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d?token=%s", owner.Name, repo.Name, issueBefore.Index, token)
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditIssueOption{
|
||||||
|
State: &issueState,
|
||||||
|
Deadline: &removeDeadline,
|
||||||
|
Milestone: &milestone,
|
||||||
|
Body: &body,
|
||||||
|
Title: title,
|
||||||
|
|
||||||
|
// ToDo change more
|
||||||
|
})
|
||||||
|
resp := session.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
var apiIssue api.Issue
|
||||||
|
DecodeJSON(t, resp, &apiIssue)
|
||||||
|
|
||||||
|
issueAfter := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 9}).(*models.Issue)
|
||||||
|
|
||||||
|
// check deleted user
|
||||||
|
assert.Equal(t, int64(500), issueAfter.PosterID)
|
||||||
|
assert.NoError(t, issueAfter.LoadAttributes())
|
||||||
|
assert.Equal(t, int64(-1), issueAfter.PosterID)
|
||||||
|
assert.Equal(t, int64(-1), issueBefore.PosterID)
|
||||||
|
assert.Equal(t, int64(-1), apiIssue.Poster.ID)
|
||||||
|
|
||||||
|
// API response
|
||||||
|
assert.Equal(t, api.StateClosed, apiIssue.State)
|
||||||
|
assert.Equal(t, milestone, apiIssue.Milestone.ID)
|
||||||
|
assert.Equal(t, body, apiIssue.Body)
|
||||||
|
assert.True(t, apiIssue.Deadline == nil)
|
||||||
|
assert.Equal(t, title, apiIssue.Title)
|
||||||
|
|
||||||
|
// in database
|
||||||
|
assert.Equal(t, api.StateClosed, issueAfter.State())
|
||||||
|
assert.Equal(t, milestone, issueAfter.MilestoneID)
|
||||||
|
assert.Equal(t, int64(0), int64(issueAfter.DeadlineUnix))
|
||||||
|
assert.Equal(t, body, issueAfter.Content)
|
||||||
|
assert.Equal(t, title, issueAfter.Title)
|
||||||
|
}
|
||||||
|
|
|
@ -96,4 +96,17 @@
|
||||||
is_closed: false
|
is_closed: false
|
||||||
is_pull: true
|
is_pull: true
|
||||||
created_unix: 946684820
|
created_unix: 946684820
|
||||||
updated_unix: 978307180
|
updated_unix: 978307180
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 9
|
||||||
|
repo_id: 42
|
||||||
|
index: 1
|
||||||
|
poster_id: 500
|
||||||
|
name: issue from deleted account
|
||||||
|
content: content from deleted account
|
||||||
|
is_closed: false
|
||||||
|
is_pull: false
|
||||||
|
created_unix: 946684830
|
||||||
|
updated_unix: 999307200
|
||||||
|
deadline_unix: 1019307200
|
||||||
|
|
|
@ -21,3 +21,11 @@
|
||||||
content: content3
|
content: content3
|
||||||
is_closed: true
|
is_closed: true
|
||||||
num_issues: 0
|
num_issues: 0
|
||||||
|
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
repo_id: 42
|
||||||
|
name: milestone of repo42
|
||||||
|
content: content random
|
||||||
|
is_closed: false
|
||||||
|
num_issues: 0
|
||||||
|
|
|
@ -547,7 +547,8 @@
|
||||||
is_private: false
|
is_private: false
|
||||||
num_stars: 0
|
num_stars: 0
|
||||||
num_forks: 0
|
num_forks: 0
|
||||||
num_issues: 0
|
num_issues: 1
|
||||||
|
num_milestones: 1
|
||||||
is_mirror: false
|
is_mirror: false
|
||||||
|
|
||||||
-
|
-
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -238,6 +239,16 @@ func (issue *Issue) loadReactions(e Engine) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (issue *Issue) loadMilestone(e Engine) (err error) {
|
||||||
|
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
||||||
|
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
||||||
|
if err != nil && !IsErrMilestoneNotExist(err) {
|
||||||
|
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||||
if err = issue.loadRepo(e); err != nil {
|
if err = issue.loadRepo(e); err != nil {
|
||||||
return
|
return
|
||||||
|
@ -251,11 +262,8 @@ func (issue *Issue) loadAttributes(e Engine) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if issue.Milestone == nil && issue.MilestoneID > 0 {
|
if err = issue.loadMilestone(e); err != nil {
|
||||||
issue.Milestone, err = getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
|
return
|
||||||
if err != nil && !IsErrMilestoneNotExist(err) {
|
|
||||||
return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.loadAssignees(e); err != nil {
|
if err = issue.loadAssignees(e); err != nil {
|
||||||
|
@ -295,6 +303,11 @@ func (issue *Issue) LoadAttributes() error {
|
||||||
return issue.loadAttributes(x)
|
return issue.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadMilestone load milestone of this issue.
|
||||||
|
func (issue *Issue) LoadMilestone() error {
|
||||||
|
return issue.loadMilestone(x)
|
||||||
|
}
|
||||||
|
|
||||||
// GetIsRead load the `IsRead` field of the issue
|
// GetIsRead load the `IsRead` field of the issue
|
||||||
func (issue *Issue) GetIsRead(userID int64) error {
|
func (issue *Issue) GetIsRead(userID int64) error {
|
||||||
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
|
issueUser := &IssueUser{IssueID: issue.ID, UID: userID}
|
||||||
|
@ -1730,22 +1743,17 @@ func SearchIssueIDsByKeyword(kw string, repoID int64, limit, start int) (int64,
|
||||||
return total, ids, nil
|
return total, ids, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateIssue(e Engine, issue *Issue) error {
|
// UpdateIssueByAPI updates all allowed fields of given issue.
|
||||||
_, err := e.ID(issue.ID).AllCols().Update(issue)
|
func UpdateIssueByAPI(issue *Issue) error {
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateIssue updates all fields of given issue.
|
|
||||||
func UpdateIssue(issue *Issue) error {
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := updateIssue(sess, issue); err != nil {
|
if _, err := sess.ID(issue.ID).Cols(
|
||||||
|
"name", "is_closed", "content", "milestone_id", "priority",
|
||||||
|
"deadline_unix", "updated_unix", "closed_unix", "is_locked").
|
||||||
|
Update(issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := issue.neuterCrossReferences(sess); err != nil {
|
if err := issue.neuterCrossReferences(sess); err != nil {
|
||||||
|
|
|
@ -352,8 +352,8 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.UpdateIssue(issue); err != nil {
|
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||||
ctx.Error(500, "UpdateIssue", err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
|
@ -372,7 +372,11 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
// Refetch from database to assign some automatic values
|
// Refetch from database to assign some automatic values
|
||||||
issue, err = models.GetIssueByID(issue.ID)
|
issue, err = models.GetIssueByID(issue.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "GetIssueByID", err)
|
ctx.InternalServerError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = issue.LoadMilestone(); err != nil {
|
||||||
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.JSON(201, issue.APIFormat())
|
ctx.JSON(201, issue.APIFormat())
|
||||||
|
|
|
@ -420,8 +420,8 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = models.UpdateIssue(issue); err != nil {
|
if err = models.UpdateIssueByAPI(issue); err != nil {
|
||||||
ctx.Error(500, "UpdateIssue", err)
|
ctx.InternalServerError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
|
|
Reference in a new issue