mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-27 17:06:28 +01:00 
			
		
		
		
	Move issues related files into models/issues (#19931)
* Move access and repo permission to models/perm/access * fix test * fix git test * Move functions sequence * Some improvements per @KN4CK3R and @delvh * Move issues related code to models/issues * Move some issues related sub package * Merge * Fix test * Fix test * Fix test * Fix test * Rename some files
This commit is contained in:
		| @@ -10,7 +10,7 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -23,9 +23,9 @@ import ( | |||||||
| func TestAPIListRepoComments(t *testing.T) { | func TestAPIListRepoComments(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{}, | ||||||
| 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) | 		unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -38,10 +38,10 @@ func TestAPIListRepoComments(t *testing.T) { | |||||||
| 	DecodeJSON(t, resp, &apiComments) | 	DecodeJSON(t, resp, &apiComments) | ||||||
| 	assert.Len(t, apiComments, 2) | 	assert.Len(t, apiComments, 2) | ||||||
| 	for _, apiComment := range apiComments { | 	for _, apiComment := range apiComments { | ||||||
| 		c := &models.Comment{ID: apiComment.ID} | 		c := &issues_model.Comment{ID: apiComment.ID} | ||||||
| 		unittest.AssertExistsAndLoadBean(t, c, | 		unittest.AssertExistsAndLoadBean(t, c, | ||||||
| 			unittest.Cond("type = ?", models.CommentTypeComment)) | 			unittest.Cond("type = ?", issues_model.CommentTypeComment)) | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: c.IssueID, RepoID: repo.ID}) | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: c.IssueID, RepoID: repo.ID}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// test before and since filters | 	// test before and since filters | ||||||
| @@ -69,9 +69,9 @@ func TestAPIListRepoComments(t *testing.T) { | |||||||
| func TestAPIListIssueComments(t *testing.T) { | func TestAPIListIssueComments(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{}, | ||||||
| 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) | 		unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -82,8 +82,8 @@ func TestAPIListIssueComments(t *testing.T) { | |||||||
|  |  | ||||||
| 	var comments []*api.Comment | 	var comments []*api.Comment | ||||||
| 	DecodeJSON(t, resp, &comments) | 	DecodeJSON(t, resp, &comments) | ||||||
| 	expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID}, | 	expectedCount := unittest.GetCount(t, &issues_model.Comment{IssueID: issue.ID}, | ||||||
| 		unittest.Cond("type = ?", models.CommentTypeComment)) | 		unittest.Cond("type = ?", issues_model.CommentTypeComment)) | ||||||
| 	assert.EqualValues(t, expectedCount, len(comments)) | 	assert.EqualValues(t, expectedCount, len(comments)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -91,7 +91,7 @@ func TestAPICreateComment(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	const commentBody = "Comment body" | 	const commentBody = "Comment body" | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -107,13 +107,13 @@ func TestAPICreateComment(t *testing.T) { | |||||||
| 	var updatedComment api.Comment | 	var updatedComment api.Comment | ||||||
| 	DecodeJSON(t, resp, &updatedComment) | 	DecodeJSON(t, resp, &updatedComment) | ||||||
| 	assert.EqualValues(t, commentBody, updatedComment.Body) | 	assert.EqualValues(t, commentBody, updatedComment.Body) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: updatedComment.ID, IssueID: issue.ID, Content: commentBody}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIGetComment(t *testing.T) { | func TestAPIGetComment(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment) | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}).(*issues_model.Comment) | ||||||
| 	assert.NoError(t, comment.LoadIssue()) | 	assert.NoError(t, comment.LoadIssue()) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: comment.Issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| @@ -141,9 +141,9 @@ func TestAPIEditComment(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	const newCommentBody = "This is the new comment body" | 	const newCommentBody = "This is the new comment body" | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{}, | ||||||
| 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) | 		unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -160,15 +160,15 @@ func TestAPIEditComment(t *testing.T) { | |||||||
| 	DecodeJSON(t, resp, &updatedComment) | 	DecodeJSON(t, resp, &updatedComment) | ||||||
| 	assert.EqualValues(t, comment.ID, updatedComment.ID) | 	assert.EqualValues(t, comment.ID, updatedComment.ID) | ||||||
| 	assert.EqualValues(t, newCommentBody, updatedComment.Body) | 	assert.EqualValues(t, newCommentBody, updatedComment.Body) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: comment.ID, IssueID: issue.ID, Content: newCommentBody}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIDeleteComment(t *testing.T) { | func TestAPIDeleteComment(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{}, | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{}, | ||||||
| 		unittest.Cond("type = ?", models.CommentTypeComment)).(*models.Comment) | 		unittest.Cond("type = ?", issues_model.CommentTypeComment)).(*issues_model.Comment) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: comment.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -178,14 +178,14 @@ func TestAPIDeleteComment(t *testing.T) { | |||||||
| 		repoOwner.Name, repo.Name, comment.ID, token) | 		repoOwner.Name, repo.Name, comment.ID, token) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	session.MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	unittest.AssertNotExistsBean(t, &models.Comment{ID: comment.ID}) | 	unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIListIssueTimeline(t *testing.T) { | func TestAPIListIssueTimeline(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	// load comment | 	// load comment | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -199,6 +199,6 @@ func TestAPIListIssueTimeline(t *testing.T) { | |||||||
| 	// lists extracted directly from DB are the same | 	// lists extracted directly from DB are the same | ||||||
| 	var comments []*api.TimelineComment | 	var comments []*api.TimelineComment | ||||||
| 	DecodeJSON(t, resp, &comments) | 	DecodeJSON(t, resp, &comments) | ||||||
| 	expectedCount := unittest.GetCount(t, &models.Comment{IssueID: issue.ID}) | 	expectedCount := unittest.GetCount(t, &issues_model.Comment{IssueID: issue.ID}) | ||||||
| 	assert.EqualValues(t, expectedCount, len(comments)) | 	assert.EqualValues(t, expectedCount, len(comments)) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,7 +10,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -37,7 +37,7 @@ func TestAPIModifyLabels(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusCreated) | 	resp := session.MakeRequest(t, req, http.StatusCreated) | ||||||
| 	apiLabel := new(api.Label) | 	apiLabel := new(api.Label) | ||||||
| 	DecodeJSON(t, resp, &apiLabel) | 	DecodeJSON(t, resp, &apiLabel) | ||||||
| 	dbLabel := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: apiLabel.ID, RepoID: repo.ID}).(*models.Label) | 	dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, RepoID: repo.ID}).(*issues_model.Label) | ||||||
| 	assert.EqualValues(t, dbLabel.Name, apiLabel.Name) | 	assert.EqualValues(t, dbLabel.Name, apiLabel.Name) | ||||||
| 	assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) | 	assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) | ||||||
|  |  | ||||||
| @@ -92,8 +92,8 @@ func TestAPIAddIssueLabels(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.LoadFixtures()) | 	assert.NoError(t, unittest.LoadFixtures()) | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}).(*issues_model.Issue) | ||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID, ID: 2}).(*models.Label) | 	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID, ID: 2}).(*issues_model.Label) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| @@ -106,17 +106,17 @@ func TestAPIAddIssueLabels(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiLabels []*api.Label | 	var apiLabels []*api.Label | ||||||
| 	DecodeJSON(t, resp, &apiLabels) | 	DecodeJSON(t, resp, &apiLabels) | ||||||
| 	assert.Len(t, apiLabels, unittest.GetCount(t, &models.IssueLabel{IssueID: issue.ID})) | 	assert.Len(t, apiLabels, unittest.GetCount(t, &issues_model.IssueLabel{IssueID: issue.ID})) | ||||||
|  |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: 2}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: 2}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIReplaceIssueLabels(t *testing.T) { | func TestAPIReplaceIssueLabels(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.LoadFixtures()) | 	assert.NoError(t, unittest.LoadFixtures()) | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repo.ID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}).(*issues_model.Issue) | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &models.Label{RepoID: repo.ID}).(*models.Label) | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{RepoID: repo.ID}).(*issues_model.Label) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| @@ -133,8 +133,8 @@ func TestAPIReplaceIssueLabels(t *testing.T) { | |||||||
| 		assert.EqualValues(t, label.ID, apiLabels[0].ID) | 		assert.EqualValues(t, label.ID, apiLabels[0].ID) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	unittest.AssertCount(t, &models.IssueLabel{IssueID: issue.ID}, 1) | 	unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issue.ID}, 1) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestAPIModifyOrgLabels(t *testing.T) { | func TestAPIModifyOrgLabels(t *testing.T) { | ||||||
| @@ -156,7 +156,7 @@ func TestAPIModifyOrgLabels(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusCreated) | 	resp := session.MakeRequest(t, req, http.StatusCreated) | ||||||
| 	apiLabel := new(api.Label) | 	apiLabel := new(api.Label) | ||||||
| 	DecodeJSON(t, resp, &apiLabel) | 	DecodeJSON(t, resp, &apiLabel) | ||||||
| 	dbLabel := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: apiLabel.ID, OrgID: owner.ID}).(*models.Label) | 	dbLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: apiLabel.ID, OrgID: owner.ID}).(*issues_model.Label) | ||||||
| 	assert.EqualValues(t, dbLabel.Name, apiLabel.Name) | 	assert.EqualValues(t, dbLabel.Name, apiLabel.Name) | ||||||
| 	assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) | 	assert.EqualValues(t, strings.TrimLeft(dbLabel.Color, "#"), apiLabel.Color) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| @@ -23,7 +23,7 @@ import ( | |||||||
| func TestAPIIssuesReactions(t *testing.T) { | func TestAPIIssuesReactions(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -80,7 +80,7 @@ func TestAPIIssuesReactions(t *testing.T) { | |||||||
| func TestAPICommentReactions(t *testing.T) { | func TestAPICommentReactions(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 2}).(*models.Comment) | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}).(*issues_model.Comment) | ||||||
| 	_ = comment.LoadIssue() | 	_ = comment.LoadIssue() | ||||||
| 	issue := comment.Issue | 	issue := comment.Issue | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
|   | |||||||
| @@ -8,8 +8,8 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -30,8 +30,8 @@ func TestAPIListStopWatches(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiWatches []*api.StopWatch | 	var apiWatches []*api.StopWatch | ||||||
| 	DecodeJSON(t, resp, &apiWatches) | 	DecodeJSON(t, resp, &apiWatches) | ||||||
| 	stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch) | 	stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}).(*issues_model.Stopwatch) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}).(*issues_model.Issue) | ||||||
| 	if assert.Len(t, apiWatches, 1) { | 	if assert.Len(t, apiWatches, 1) { | ||||||
| 		assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) | 		assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) | ||||||
| 		assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) | 		assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) | ||||||
| @@ -45,7 +45,7 @@ func TestAPIListStopWatches(t *testing.T) { | |||||||
| func TestAPIStopStopWatches(t *testing.T) { | func TestAPIStopStopWatches(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| @@ -61,7 +61,7 @@ func TestAPIStopStopWatches(t *testing.T) { | |||||||
| func TestAPICancelStopWatches(t *testing.T) { | func TestAPICancelStopWatches(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
| @@ -77,7 +77,7 @@ func TestAPICancelStopWatches(t *testing.T) { | |||||||
| func TestAPIStartStopWatches(t *testing.T) { | func TestAPIStartStopWatches(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue) | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -21,18 +21,18 @@ import ( | |||||||
| func TestAPIIssueSubscriptions(t *testing.T) { | func TestAPIIssueSubscriptions(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue1 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 1}).(*models.Issue) | 	issue1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | 	issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	issue3 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) | 	issue3 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue) | ||||||
| 	issue4 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 4}).(*models.Issue) | 	issue4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue) | ||||||
| 	issue5 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 8}).(*models.Issue) | 	issue5 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 8}).(*issues_model.Issue) | ||||||
|  |  | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue1.PosterID}).(*user_model.User) | ||||||
|  |  | ||||||
| 	session := loginUser(t, owner.Name) | 	session := loginUser(t, owner.Name) | ||||||
| 	token := getTokenForLoggedInUser(t, session) | 	token := getTokenForLoggedInUser(t, session) | ||||||
|  |  | ||||||
| 	testSubscription := func(issue *models.Issue, isWatching bool) { | 	testSubscription := func(issue *issues_model.Issue, isWatching bool) { | ||||||
| 		issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 		issueRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/subscriptions/check?token=%s", issueRepo.OwnerName, issueRepo.Name, issue.Index, token) | 		urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/subscriptions/check?token=%s", issueRepo.OwnerName, issueRepo.Name, issue.Index, token) | ||||||
|   | |||||||
| @@ -11,7 +11,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -34,9 +35,9 @@ func TestAPIListIssues(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) | 	resp := session.MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK) | ||||||
| 	var apiIssues []*api.Issue | 	var apiIssues []*api.Issue | ||||||
| 	DecodeJSON(t, resp, &apiIssues) | 	DecodeJSON(t, resp, &apiIssues) | ||||||
| 	assert.Len(t, apiIssues, unittest.GetCount(t, &models.Issue{RepoID: repo.ID})) | 	assert.Len(t, apiIssues, unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID})) | ||||||
| 	for _, apiIssue := range apiIssues { | 	for _, apiIssue := range apiIssues { | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: apiIssue.ID, RepoID: repo.ID}) | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: apiIssue.ID, RepoID: repo.ID}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// test milestone filter | 	// test milestone filter | ||||||
| @@ -91,7 +92,7 @@ func TestAPICreateIssue(t *testing.T) { | |||||||
| 	assert.Equal(t, body, apiIssue.Body) | 	assert.Equal(t, body, apiIssue.Body) | ||||||
| 	assert.Equal(t, title, apiIssue.Title) | 	assert.Equal(t, title, apiIssue.Title) | ||||||
|  |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Issue{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ | ||||||
| 		RepoID:     repoBefore.ID, | 		RepoID:     repoBefore.ID, | ||||||
| 		AssigneeID: owner.ID, | 		AssigneeID: owner.ID, | ||||||
| 		Content:    body, | 		Content:    body, | ||||||
| @@ -106,10 +107,10 @@ func TestAPICreateIssue(t *testing.T) { | |||||||
| func TestAPIEditIssue(t *testing.T) { | func TestAPIEditIssue(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) | 	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue) | ||||||
| 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) | ||||||
| 	assert.NoError(t, issueBefore.LoadAttributes()) | 	assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) | 	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) | ||||||
| 	assert.Equal(t, api.StateOpen, issueBefore.State()) | 	assert.Equal(t, api.StateOpen, issueBefore.State()) | ||||||
|  |  | ||||||
| @@ -137,12 +138,12 @@ func TestAPIEditIssue(t *testing.T) { | |||||||
| 	var apiIssue api.Issue | 	var apiIssue api.Issue | ||||||
| 	DecodeJSON(t, resp, &apiIssue) | 	DecodeJSON(t, resp, &apiIssue) | ||||||
|  |  | ||||||
| 	issueAfter := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) | 	issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue) | ||||||
| 	repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | 	repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	// check deleted user | 	// check deleted user | ||||||
| 	assert.Equal(t, int64(500), issueAfter.PosterID) | 	assert.Equal(t, int64(500), issueAfter.PosterID) | ||||||
| 	assert.NoError(t, issueAfter.LoadAttributes()) | 	assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.Equal(t, int64(-1), issueAfter.PosterID) | 	assert.Equal(t, int64(-1), issueAfter.PosterID) | ||||||
| 	assert.Equal(t, int64(-1), issueBefore.PosterID) | 	assert.Equal(t, int64(-1), issueBefore.PosterID) | ||||||
| 	assert.Equal(t, int64(-1), apiIssue.Poster.ID) | 	assert.Equal(t, int64(-1), apiIssue.Poster.ID) | ||||||
|   | |||||||
| @@ -10,8 +10,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -23,7 +23,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | 	issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | ||||||
|  |  | ||||||
| 	session := loginUser(t, user2.Name) | 	session := loginUser(t, user2.Name) | ||||||
| @@ -33,7 +33,7 @@ func TestAPIGetTrackedTimes(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiTimes api.TrackedTimeList | 	var apiTimes api.TrackedTimeList | ||||||
| 	DecodeJSON(t, resp, &apiTimes) | 	DecodeJSON(t, resp, &apiTimes) | ||||||
| 	expect, err := models.GetTrackedTimes(db.DefaultContext, &models.FindTrackedTimesOptions{IssueID: issue2.ID}) | 	expect, err := issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: issue2.ID}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, apiTimes, 3) | 	assert.Len(t, apiTimes, 3) | ||||||
|  |  | ||||||
| @@ -64,8 +64,8 @@ func TestAPIGetTrackedTimes(t *testing.T) { | |||||||
| func TestAPIDeleteTrackedTime(t *testing.T) { | func TestAPIDeleteTrackedTime(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	time6 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 6}).(*models.TrackedTime) | 	time6 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 6}).(*issues_model.TrackedTime) | ||||||
| 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | 	issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
|  |  | ||||||
| @@ -76,14 +76,14 @@ func TestAPIDeleteTrackedTime(t *testing.T) { | |||||||
| 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token) | 	req := NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time6.ID, token) | ||||||
| 	session.MakeRequest(t, req, http.StatusForbidden) | 	session.MakeRequest(t, req, http.StatusForbidden) | ||||||
|  |  | ||||||
| 	time3 := unittest.AssertExistsAndLoadBean(t, &models.TrackedTime{ID: 3}).(*models.TrackedTime) | 	time3 := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{ID: 3}).(*issues_model.TrackedTime) | ||||||
| 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time3.ID, token) | 	req = NewRequestf(t, "DELETE", "/api/v1/repos/%s/%s/issues/%d/times/%d?token=%s", user2.Name, issue2.Repo.Name, issue2.Index, time3.ID, token) | ||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	session.MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	// Delete non existing time | 	// Delete non existing time | ||||||
| 	session.MakeRequest(t, req, http.StatusNotFound) | 	session.MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 	// Reset time of user 2 on issue 2 | 	// Reset time of user 2 on issue 2 | ||||||
| 	trackedSeconds, err := models.GetTrackedSeconds(db.DefaultContext, models.FindTrackedTimesOptions{IssueID: 2, UserID: 2}) | 	trackedSeconds, err := issues_model.GetTrackedSeconds(db.DefaultContext, issues_model.FindTrackedTimesOptions{IssueID: 2, UserID: 2}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(3661), trackedSeconds) | 	assert.Equal(t, int64(3661), trackedSeconds) | ||||||
|  |  | ||||||
| @@ -91,7 +91,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { | |||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	session.MakeRequest(t, req, http.StatusNoContent) | ||||||
| 	session.MakeRequest(t, req, http.StatusNotFound) | 	session.MakeRequest(t, req, http.StatusNotFound) | ||||||
|  |  | ||||||
| 	trackedSeconds, err = models.GetTrackedSeconds(db.DefaultContext, models.FindTrackedTimesOptions{IssueID: 2, UserID: 2}) | 	trackedSeconds, err = issues_model.GetTrackedSeconds(db.DefaultContext, issues_model.FindTrackedTimesOptions{IssueID: 2, UserID: 2}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(0), trackedSeconds) | 	assert.Equal(t, int64(0), trackedSeconds) | ||||||
| } | } | ||||||
| @@ -99,7 +99,7 @@ func TestAPIDeleteTrackedTime(t *testing.T) { | |||||||
| func TestAPIAddTrackedTimes(t *testing.T) { | func TestAPIAddTrackedTimes(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue2 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 2}).(*models.Issue) | 	issue2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | 	assert.NoError(t, issue2.LoadRepo(db.DefaultContext)) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| @@ -18,7 +18,7 @@ import ( | |||||||
|  |  | ||||||
| func TestAPIPullCommits(t *testing.T) { | func TestAPIPullCommits(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	pullIssue := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) | 	pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pullIssue.LoadIssue()) | 	assert.NoError(t, pullIssue.LoadIssue()) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.HeadRepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.HeadRepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,8 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| @@ -20,8 +21,8 @@ import ( | |||||||
|  |  | ||||||
| func TestAPIPullReview(t *testing.T) { | func TestAPIPullReview(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) | 	pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, pullIssue.LoadAttributes()) | 	assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext)) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	// test ListPullReviews | 	// test ListPullReviews | ||||||
| @@ -64,7 +65,7 @@ func TestAPIPullReview(t *testing.T) { | |||||||
| 	assert.EqualValues(t, *reviews[5], review) | 	assert.EqualValues(t, *reviews[5], review) | ||||||
|  |  | ||||||
| 	// test GetPullReviewComments | 	// test GetPullReviewComments | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &models.Comment{ID: 7}).(*models.Comment) | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 7}).(*issues_model.Comment) | ||||||
| 	req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, 10, token) | 	req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews/%d/comments?token=%s", repo.OwnerName, repo.Name, pullIssue.Index, 10, token) | ||||||
| 	resp = session.MakeRequest(t, req, http.StatusOK) | 	resp = session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	var reviewComments []*api.PullReviewComment | 	var reviewComments []*api.PullReviewComment | ||||||
| @@ -199,8 +200,8 @@ func TestAPIPullReview(t *testing.T) { | |||||||
|  |  | ||||||
| 	// test get review requests | 	// test get review requests | ||||||
| 	// to make it simple, use same api with get review | 	// to make it simple, use same api with get review | ||||||
| 	pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue) | 	pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, pullIssue12.LoadAttributes()) | 	assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext)) | ||||||
| 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) | 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token) | 	req = NewRequestf(t, http.MethodGet, "/api/v1/repos/%s/%s/pulls/%d/reviews?token=%s", repo3.OwnerName, repo3.Name, pullIssue12.Index, token) | ||||||
| @@ -223,8 +224,8 @@ func TestAPIPullReview(t *testing.T) { | |||||||
|  |  | ||||||
| func TestAPIPullReviewRequest(t *testing.T) { | func TestAPIPullReviewRequest(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	pullIssue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 3}).(*models.Issue) | 	pullIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, pullIssue.LoadAttributes()) | 	assert.NoError(t, pullIssue.LoadAttributes(db.DefaultContext)) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	// Test add Review Request | 	// Test add Review Request | ||||||
| @@ -268,8 +269,8 @@ func TestAPIPullReviewRequest(t *testing.T) { | |||||||
| 	session.MakeRequest(t, req, http.StatusNoContent) | 	session.MakeRequest(t, req, http.StatusNoContent) | ||||||
|  |  | ||||||
| 	// Test team review request | 	// Test team review request | ||||||
| 	pullIssue12 := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 12}).(*models.Issue) | 	pullIssue12 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 12}).(*issues_model.Issue) | ||||||
| 	assert.NoError(t, pullIssue12.LoadAttributes()) | 	assert.NoError(t, pullIssue12.LoadAttributes(db.DefaultContext)) | ||||||
| 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) | 	repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pullIssue12.RepoID}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	// Test add Team Review Request | 	// Test add Team Review Request | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -33,7 +33,7 @@ func TestAPIViewPulls(t *testing.T) { | |||||||
|  |  | ||||||
| 	var pulls []*api.PullRequest | 	var pulls []*api.PullRequest | ||||||
| 	DecodeJSON(t, resp, &pulls) | 	DecodeJSON(t, resp, &pulls) | ||||||
| 	expectedLen := unittest.GetCount(t, &models.Issue{RepoID: repo.ID}, unittest.Cond("is_pull = ?", true)) | 	expectedLen := unittest.GetCount(t, &issues_model.Issue{RepoID: repo.ID}, unittest.Cond("is_pull = ?", true)) | ||||||
| 	assert.Len(t, pulls, expectedLen) | 	assert.Len(t, pulls, expectedLen) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -42,7 +42,7 @@ func TestAPIMergePullWIP(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{Status: models.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false)).(*models.PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{Status: issues_model.PullRequestStatusMergeable}, unittest.Cond("has_merged = ?", false)).(*issues_model.PullRequest) | ||||||
| 	pr.LoadIssue() | 	pr.LoadIssue() | ||||||
| 	issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title) | 	issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -24,7 +24,7 @@ func assertUserDeleted(t *testing.T, userID int64) { | |||||||
| 	unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerID: userID}) | 	unittest.AssertNotExistsBean(t, &repo_model.Repository{OwnerID: userID}) | ||||||
| 	unittest.AssertNotExistsBean(t, &access_model.Access{UserID: userID}) | 	unittest.AssertNotExistsBean(t, &access_model.Access{UserID: userID}) | ||||||
| 	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: userID}) | 	unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: userID}) | ||||||
| 	unittest.AssertNotExistsBean(t, &models.IssueUser{UID: userID}) | 	unittest.AssertNotExistsBean(t, &issues_model.IssueUser{UID: userID}) | ||||||
| 	unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID}) | 	unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID}) | ||||||
| 	unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID}) | 	unittest.AssertNotExistsBean(t, &repo_model.Star{UID: userID}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,8 +17,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -715,7 +715,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 		defer gitRepo.Close() | 		defer gitRepo.Close() | ||||||
|  |  | ||||||
| 		var ( | 		var ( | ||||||
| 			pr1, pr2 *models.PullRequest | 			pr1, pr2 *issues_model.PullRequest | ||||||
| 			commit   string | 			commit   string | ||||||
| 		) | 		) | ||||||
| 		repo, err := repo_model.GetRepositoryByOwnerAndName(ctx.Username, ctx.Reponame) | 		repo, err := repo_model.GetRepositoryByOwnerAndName(ctx.Username, ctx.Reponame) | ||||||
| @@ -723,7 +723,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		pullNum := unittest.GetCount(t, &models.PullRequest{}) | 		pullNum := unittest.GetCount(t, &issues_model.PullRequest{}) | ||||||
|  |  | ||||||
| 		t.Run("CreateHeadBranch", doGitCreateBranch(dstPath, headBranch)) | 		t.Run("CreateHeadBranch", doGitCreateBranch(dstPath, headBranch)) | ||||||
|  |  | ||||||
| @@ -759,11 +759,11 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			unittest.AssertCount(t, &models.PullRequest{}, pullNum+1) | 			unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+1) | ||||||
| 			pr1 = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ | 			pr1 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ | ||||||
| 				HeadRepoID: repo.ID, | 				HeadRepoID: repo.ID, | ||||||
| 				Flow:       models.PullRequestFlowAGit, | 				Flow:       issues_model.PullRequestFlowAGit, | ||||||
| 			}).(*models.PullRequest) | 			}).(*issues_model.PullRequest) | ||||||
| 			if !assert.NotEmpty(t, pr1) { | 			if !assert.NotEmpty(t, pr1) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| @@ -780,12 +780,12 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			unittest.AssertCount(t, &models.PullRequest{}, pullNum+2) | 			unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2) | ||||||
| 			pr2 = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ | 			pr2 = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ | ||||||
| 				HeadRepoID: repo.ID, | 				HeadRepoID: repo.ID, | ||||||
| 				Index:      pr1.Index + 1, | 				Index:      pr1.Index + 1, | ||||||
| 				Flow:       models.PullRequestFlowAGit, | 				Flow:       issues_model.PullRequestFlowAGit, | ||||||
| 			}).(*models.PullRequest) | 			}).(*issues_model.PullRequest) | ||||||
| 			if !assert.NotEmpty(t, pr2) { | 			if !assert.NotEmpty(t, pr2) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| @@ -833,7 +833,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			unittest.AssertCount(t, &models.PullRequest{}, pullNum+2) | 			unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2) | ||||||
| 			prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) | 			prMsg, err := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)(t) | ||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| @@ -845,7 +845,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB | |||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			unittest.AssertCount(t, &models.PullRequest{}, pullNum+2) | 			unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+2) | ||||||
| 			prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) | 			prMsg, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr2.Index)(t) | ||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
|   | |||||||
| @@ -14,7 +14,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -34,16 +35,16 @@ func getIssuesSelection(t testing.TB, htmlDoc *HTMLDoc) *goquery.Selection { | |||||||
| 	return issueList.Find("li").Find(".title") | 	return issueList.Find("li").Find(".title") | ||||||
| } | } | ||||||
|  |  | ||||||
| func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *models.Issue { | func getIssue(t *testing.T, repoID int64, issueSelection *goquery.Selection) *issues_model.Issue { | ||||||
| 	href, exists := issueSelection.Attr("href") | 	href, exists := issueSelection.Attr("href") | ||||||
| 	assert.True(t, exists) | 	assert.True(t, exists) | ||||||
| 	indexStr := href[strings.LastIndexByte(href, '/')+1:] | 	indexStr := href[strings.LastIndexByte(href, '/')+1:] | ||||||
| 	index, err := strconv.Atoi(indexStr) | 	index, err := strconv.Atoi(indexStr) | ||||||
| 	assert.NoError(t, err, "Invalid issue href: %s", href) | 	assert.NoError(t, err, "Invalid issue href: %s", href) | ||||||
| 	return unittest.AssertExistsAndLoadBean(t, &models.Issue{RepoID: repoID, Index: int64(index)}).(*models.Issue) | 	return unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repoID, Index: int64(index)}).(*issues_model.Issue) | ||||||
| } | } | ||||||
|  |  | ||||||
| func assertMatch(t testing.TB, issue *models.Issue, keyword string) { | func assertMatch(t testing.TB, issue *issues_model.Issue, keyword string) { | ||||||
| 	matches := strings.Contains(strings.ToLower(issue.Title), keyword) || | 	matches := strings.Contains(strings.ToLower(issue.Title), keyword) || | ||||||
| 		strings.Contains(strings.ToLower(issue.Content), keyword) | 		strings.Contains(strings.ToLower(issue.Content), keyword) | ||||||
| 	for _, comment := range issue.Comments { | 	for _, comment := range issue.Comments { | ||||||
| @@ -75,7 +76,7 @@ func TestViewIssuesSortByType(t *testing.T) { | |||||||
| 	htmlDoc := NewHTMLParser(t, resp.Body) | 	htmlDoc := NewHTMLParser(t, resp.Body) | ||||||
| 	issuesSelection := getIssuesSelection(t, htmlDoc) | 	issuesSelection := getIssuesSelection(t, htmlDoc) | ||||||
| 	expectedNumIssues := unittest.GetCount(t, | 	expectedNumIssues := unittest.GetCount(t, | ||||||
| 		&models.Issue{RepoID: repo.ID, PosterID: user.ID}, | 		&issues_model.Issue{RepoID: repo.ID, PosterID: user.ID}, | ||||||
| 		unittest.Cond("is_closed=?", false), | 		unittest.Cond("is_closed=?", false), | ||||||
| 		unittest.Cond("is_pull=?", false), | 		unittest.Cond("is_pull=?", false), | ||||||
| 	) | 	) | ||||||
| @@ -94,10 +95,10 @@ func TestViewIssuesKeyword(t *testing.T) { | |||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ | ||||||
| 		RepoID: repo.ID, | 		RepoID: repo.ID, | ||||||
| 		Index:  1, | 		Index:  1, | ||||||
| 	}).(*models.Issue) | 	}).(*issues_model.Issue) | ||||||
| 	issues.UpdateIssueIndexer(issue) | 	issues.UpdateIssueIndexer(issue) | ||||||
| 	time.Sleep(time.Second * 1) | 	time.Sleep(time.Second * 1) | ||||||
| 	const keyword = "first" | 	const keyword = "first" | ||||||
| @@ -238,7 +239,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Ref from issue title | 	// Ref from issue title | ||||||
| 	issueRefURL, issueRef := testIssueWithBean(t, "user2", 1, fmt.Sprintf("Title ref #%d", issueBase.Index), "Description") | 	issueRefURL, issueRef := testIssueWithBean(t, "user2", 1, fmt.Sprintf("Title ref #%d", issueBase.Index), "Description") | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    1, | 		RefRepoID:    1, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -249,7 +250,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Edit title, neuter ref | 	// Edit title, neuter ref | ||||||
| 	testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref") | 	testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref") | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    1, | 		RefRepoID:    1, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -260,7 +261,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Ref from issue content | 	// Ref from issue content | ||||||
| 	issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index)) | 	issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index)) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    1, | 		RefRepoID:    1, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -271,7 +272,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Edit content, neuter ref | 	// Edit content, neuter ref | ||||||
| 	testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref") | 	testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref") | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    1, | 		RefRepoID:    1, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -283,7 +284,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
| 	// Ref from a comment | 	// Ref from a comment | ||||||
| 	session := loginUser(t, "user2") | 	session := loginUser(t, "user2") | ||||||
| 	commentID := testIssueAddComment(t, session, issueRefURL, fmt.Sprintf("Adding ref from comment #%d", issueBase.Index), "") | 	commentID := testIssueAddComment(t, session, issueRefURL, fmt.Sprintf("Adding ref from comment #%d", issueBase.Index), "") | ||||||
| 	comment := &models.Comment{ | 	comment := &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    1, | 		RefRepoID:    1, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -295,7 +296,7 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
|  |  | ||||||
| 	// Ref from a different repository | 	// Ref from a different repository | ||||||
| 	_, issueRef = testIssueWithBean(t, "user12", 10, "TitleXRef", fmt.Sprintf("Description ref user2/repo1#%d", issueBase.Index)) | 	_, issueRef = testIssueWithBean(t, "user12", 10, "TitleXRef", fmt.Sprintf("Description ref user2/repo1#%d", issueBase.Index)) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &models.Comment{ | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
| 		IssueID:      issueBase.ID, | 		IssueID:      issueBase.ID, | ||||||
| 		RefRepoID:    10, | 		RefRepoID:    10, | ||||||
| 		RefIssueID:   issueRef.ID, | 		RefIssueID:   issueRef.ID, | ||||||
| @@ -305,13 +306,13 @@ func TestIssueCrossReference(t *testing.T) { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *models.Issue) { | func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *issues_model.Issue) { | ||||||
| 	session := loginUser(t, user) | 	session := loginUser(t, user) | ||||||
| 	issueURL := testNewIssue(t, session, user, fmt.Sprintf("repo%d", repoID), title, content) | 	issueURL := testNewIssue(t, session, user, fmt.Sprintf("repo%d", repoID), title, content) | ||||||
| 	indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:] | 	indexStr := issueURL[strings.LastIndexByte(issueURL, '/')+1:] | ||||||
| 	index, err := strconv.Atoi(indexStr) | 	index, err := strconv.Atoi(indexStr) | ||||||
| 	assert.NoError(t, err, "Invalid issue href: %s", issueURL) | 	assert.NoError(t, err, "Invalid issue href: %s", issueURL) | ||||||
| 	issue := &models.Issue{RepoID: repoID, Index: int64(index)} | 	issue := &issues_model.Issue{RepoID: repoID, Index: int64(index)} | ||||||
| 	unittest.AssertExistsAndLoadBean(t, issue) | 	unittest.AssertExistsAndLoadBean(t, issue) | ||||||
| 	return issueURL, issue | 	return issueURL, issue | ||||||
| } | } | ||||||
| @@ -511,10 +512,10 @@ func TestSearchIssuesWithLabels(t *testing.T) { | |||||||
| func TestGetIssueInfo(t *testing.T) { | func TestGetIssueInfo(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| 	assert.NoError(t, issue.LoadAttributes()) | 	assert.NoError(t, issue.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix)) | 	assert.Equal(t, int64(1019307200), int64(issue.DeadlineUnix)) | ||||||
| 	assert.Equal(t, api.StateOpen, issue.State()) | 	assert.Equal(t, api.StateOpen, issue.State()) | ||||||
|  |  | ||||||
| @@ -532,10 +533,10 @@ func TestGetIssueInfo(t *testing.T) { | |||||||
| func TestUpdateIssueDeadline(t *testing.T) { | func TestUpdateIssueDeadline(t *testing.T) { | ||||||
| 	defer prepareTestEnv(t)() | 	defer prepareTestEnv(t)() | ||||||
|  |  | ||||||
| 	issueBefore := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: 10}).(*models.Issue) | 	issueBefore := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}).(*issues_model.Issue) | ||||||
| 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | 	repoBefore := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repoBefore.OwnerID}).(*user_model.User) | ||||||
| 	assert.NoError(t, issueBefore.LoadAttributes()) | 	assert.NoError(t, issueBefore.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) | 	assert.Equal(t, int64(1019307200), int64(issueBefore.DeadlineUnix)) | ||||||
| 	assert.Equal(t, api.StateOpen, issueBefore.State()) | 	assert.Equal(t, api.StateOpen, issueBefore.State()) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -233,12 +234,12 @@ func TestCantMergeConflict(t *testing.T) { | |||||||
| 			Name:    "repo1", | 			Name:    "repo1", | ||||||
| 		}).(*repo_model.Repository) | 		}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 		pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ | 		pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ | ||||||
| 			HeadRepoID: repo1.ID, | 			HeadRepoID: repo1.ID, | ||||||
| 			BaseRepoID: repo1.ID, | 			BaseRepoID: repo1.ID, | ||||||
| 			HeadBranch: "conflict", | 			HeadBranch: "conflict", | ||||||
| 			BaseBranch: "base", | 			BaseBranch: "base", | ||||||
| 		}).(*models.PullRequest) | 		}).(*issues_model.PullRequest) | ||||||
|  |  | ||||||
| 		gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) | 		gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| @@ -335,12 +336,12 @@ func TestCantMergeUnrelated(t *testing.T) { | |||||||
| 		// Now this PR could be marked conflict - or at least a race may occur - so drop down to pure code at this point... | 		// Now this PR could be marked conflict - or at least a race may occur - so drop down to pure code at this point... | ||||||
| 		gitRepo, err := git.OpenRepository(git.DefaultContext, path) | 		gitRepo, err := git.OpenRepository(git.DefaultContext, path) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ | 		pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ | ||||||
| 			HeadRepoID: repo1.ID, | 			HeadRepoID: repo1.ID, | ||||||
| 			BaseRepoID: repo1.ID, | 			BaseRepoID: repo1.ID, | ||||||
| 			HeadBranch: "unrelated", | 			HeadBranch: "unrelated", | ||||||
| 			BaseBranch: "base", | 			BaseBranch: "base", | ||||||
| 		}).(*models.PullRequest) | 		}).(*issues_model.PullRequest) | ||||||
|  |  | ||||||
| 		err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED") | 		err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleMerge, "", "UNRELATED") | ||||||
| 		assert.Error(t, err, "Merge should return an error due to unrelated") | 		assert.Error(t, err, "Merge should return an error due to unrelated") | ||||||
| @@ -387,7 +388,7 @@ func TestConflictChecking(t *testing.T) { | |||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
|  |  | ||||||
| 		// create Pull to merge the important-secrets branch into main branch. | 		// create Pull to merge the important-secrets branch into main branch. | ||||||
| 		pullIssue := &models.Issue{ | 		pullIssue := &issues_model.Issue{ | ||||||
| 			RepoID:   baseRepo.ID, | 			RepoID:   baseRepo.ID, | ||||||
| 			Title:    "PR with conflict!", | 			Title:    "PR with conflict!", | ||||||
| 			PosterID: user.ID, | 			PosterID: user.ID, | ||||||
| @@ -395,26 +396,26 @@ func TestConflictChecking(t *testing.T) { | |||||||
| 			IsPull:   true, | 			IsPull:   true, | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		pullRequest := &models.PullRequest{ | 		pullRequest := &issues_model.PullRequest{ | ||||||
| 			HeadRepoID: baseRepo.ID, | 			HeadRepoID: baseRepo.ID, | ||||||
| 			BaseRepoID: baseRepo.ID, | 			BaseRepoID: baseRepo.ID, | ||||||
| 			HeadBranch: "important-secrets", | 			HeadBranch: "important-secrets", | ||||||
| 			BaseBranch: "main", | 			BaseBranch: "main", | ||||||
| 			HeadRepo:   baseRepo, | 			HeadRepo:   baseRepo, | ||||||
| 			BaseRepo:   baseRepo, | 			BaseRepo:   baseRepo, | ||||||
| 			Type:       models.PullRequestGitea, | 			Type:       issues_model.PullRequestGitea, | ||||||
| 		} | 		} | ||||||
| 		err = pull.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) | 		err = pull.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
|  |  | ||||||
| 		issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "PR with conflict!"}).(*models.Issue) | 		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "PR with conflict!"}).(*issues_model.Issue) | ||||||
| 		conflictingPR, err := models.GetPullRequestByIssueID(db.DefaultContext, issue.ID) | 		conflictingPR, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
|  |  | ||||||
| 		// Ensure conflictedFiles is populated. | 		// Ensure conflictedFiles is populated. | ||||||
| 		assert.Equal(t, 1, len(conflictingPR.ConflictedFiles)) | 		assert.Equal(t, 1, len(conflictingPR.ConflictedFiles)) | ||||||
| 		// Check if status is correct. | 		// Check if status is correct. | ||||||
| 		assert.Equal(t, models.PullRequestStatusConflict, conflictingPR.Status) | 		assert.Equal(t, issues_model.PullRequestStatusConflict, conflictingPR.Status) | ||||||
| 		// Ensure that mergeable returns false | 		// Ensure that mergeable returns false | ||||||
| 		assert.False(t, conflictingPR.Mergeable()) | 		assert.False(t, conflictingPR.Mergeable()) | ||||||
| 	}) | 	}) | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| @@ -78,7 +79,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { | |||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.PullRequest { | func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest { | ||||||
| 	baseRepo, err := repo_service.CreateRepository(actor, actor, models.CreateRepoOptions{ | 	baseRepo, err := repo_service.CreateRepository(actor, actor, models.CreateRepoOptions{ | ||||||
| 		Name:        "repo-pr-update", | 		Name:        "repo-pr-update", | ||||||
| 		Description: "repo-tmp-pr-update description", | 		Description: "repo-tmp-pr-update description", | ||||||
| @@ -146,27 +147,27 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *models.Pul | |||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	// create Pull | 	// create Pull | ||||||
| 	pullIssue := &models.Issue{ | 	pullIssue := &issues_model.Issue{ | ||||||
| 		RepoID:   baseRepo.ID, | 		RepoID:   baseRepo.ID, | ||||||
| 		Title:    "Test Pull -to-update-", | 		Title:    "Test Pull -to-update-", | ||||||
| 		PosterID: actor.ID, | 		PosterID: actor.ID, | ||||||
| 		Poster:   actor, | 		Poster:   actor, | ||||||
| 		IsPull:   true, | 		IsPull:   true, | ||||||
| 	} | 	} | ||||||
| 	pullRequest := &models.PullRequest{ | 	pullRequest := &issues_model.PullRequest{ | ||||||
| 		HeadRepoID: headRepo.ID, | 		HeadRepoID: headRepo.ID, | ||||||
| 		BaseRepoID: baseRepo.ID, | 		BaseRepoID: baseRepo.ID, | ||||||
| 		HeadBranch: "newBranch", | 		HeadBranch: "newBranch", | ||||||
| 		BaseBranch: "master", | 		BaseBranch: "master", | ||||||
| 		HeadRepo:   headRepo, | 		HeadRepo:   headRepo, | ||||||
| 		BaseRepo:   baseRepo, | 		BaseRepo:   baseRepo, | ||||||
| 		Type:       models.PullRequestGitea, | 		Type:       issues_model.PullRequestGitea, | ||||||
| 	} | 	} | ||||||
| 	err = pull_service.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) | 	err = pull_service.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{Title: "Test Pull -to-update-"}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: "Test Pull -to-update-"}).(*issues_model.Issue) | ||||||
| 	pr, err := models.GetPullRequestByIssueID(db.DefaultContext, issue.ID) | 	pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, issue.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	return pr | 	return pr | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -237,8 +237,8 @@ func TestListStopWatches(t *testing.T) { | |||||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||||
| 	var apiWatches []*api.StopWatch | 	var apiWatches []*api.StopWatch | ||||||
| 	DecodeJSON(t, resp, &apiWatches) | 	DecodeJSON(t, resp, &apiWatches) | ||||||
| 	stopwatch := unittest.AssertExistsAndLoadBean(t, &models.Stopwatch{UserID: owner.ID}).(*models.Stopwatch) | 	stopwatch := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: owner.ID}).(*issues_model.Stopwatch) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &models.Issue{ID: stopwatch.IssueID}).(*models.Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: stopwatch.IssueID}).(*issues_model.Issue) | ||||||
| 	if assert.Len(t, apiWatches, 1) { | 	if assert.Len(t, apiWatches, 1) { | ||||||
| 		assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) | 		assert.EqualValues(t, stopwatch.CreatedUnix.AsTime().Unix(), apiWatches[0].Created.Unix()) | ||||||
| 		assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) | 		assert.EqualValues(t, issue.Index, apiWatches[0].IssueIndex) | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -76,7 +77,7 @@ type Action struct { | |||||||
| 	RepoID      int64                  `xorm:"INDEX"` | 	RepoID      int64                  `xorm:"INDEX"` | ||||||
| 	Repo        *repo_model.Repository `xorm:"-"` | 	Repo        *repo_model.Repository `xorm:"-"` | ||||||
| 	CommentID   int64                  `xorm:"INDEX"` | 	CommentID   int64                  `xorm:"INDEX"` | ||||||
| 	Comment     *Comment               `xorm:"-"` | 	Comment     *issues_model.Comment  `xorm:"-"` | ||||||
| 	IsDeleted   bool                   `xorm:"INDEX NOT NULL DEFAULT false"` | 	IsDeleted   bool                   `xorm:"INDEX NOT NULL DEFAULT false"` | ||||||
| 	RefName     string | 	RefName     string | ||||||
| 	IsPrivate   bool               `xorm:"INDEX NOT NULL DEFAULT false"` | 	IsPrivate   bool               `xorm:"INDEX NOT NULL DEFAULT false"` | ||||||
| @@ -223,7 +224,7 @@ func (a *Action) getCommentLink(ctx context.Context) string { | |||||||
| 		return "#" | 		return "#" | ||||||
| 	} | 	} | ||||||
| 	if a.Comment == nil && a.CommentID != 0 { | 	if a.Comment == nil && a.CommentID != 0 { | ||||||
| 		a.Comment, _ = GetCommentByID(ctx, a.CommentID) | 		a.Comment, _ = issues_model.GetCommentByID(ctx, a.CommentID) | ||||||
| 	} | 	} | ||||||
| 	if a.Comment != nil { | 	if a.Comment != nil { | ||||||
| 		return a.Comment.HTMLURL() | 		return a.Comment.HTMLURL() | ||||||
| @@ -238,7 +239,7 @@ func (a *Action) getCommentLink(ctx context.Context) string { | |||||||
| 		return "#" | 		return "#" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	issue, err := getIssueByID(ctx, issueID) | 	issue, err := issues_model.GetIssueByID(ctx, issueID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "#" | 		return "#" | ||||||
| 	} | 	} | ||||||
| @@ -295,7 +296,7 @@ func (a *Action) GetIssueInfos() []string { | |||||||
| // with the action. | // with the action. | ||||||
| func (a *Action) GetIssueTitle() string { | func (a *Action) GetIssueTitle() string { | ||||||
| 	index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) | 	index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) | ||||||
| 	issue, err := GetIssueByIndex(a.RepoID, index) | 	issue, err := issues_model.GetIssueByIndex(a.RepoID, index) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("GetIssueByIndex: %v", err) | 		log.Error("GetIssueByIndex: %v", err) | ||||||
| 		return "500 when get issue" | 		return "500 when get issue" | ||||||
| @@ -307,7 +308,7 @@ func (a *Action) GetIssueTitle() string { | |||||||
| // this action. | // this action. | ||||||
| func (a *Action) GetIssueContent() string { | func (a *Action) GetIssueContent() string { | ||||||
| 	index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) | 	index, _ := strconv.ParseInt(a.GetIssueInfos()[0], 10, 64) | ||||||
| 	issue, err := GetIssueByIndex(a.RepoID, index) | 	issue, err := issues_model.GetIssueByIndex(a.RepoID, index) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("GetIssueByIndex: %v", err) | 		log.Error("GetIssueByIndex: %v", err) | ||||||
| 		return "500 when get issue" | 		return "500 when get issue" | ||||||
| @@ -572,3 +573,20 @@ func NotifyWatchersActions(acts []*Action) error { | |||||||
| 	} | 	} | ||||||
| 	return committer.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // DeleteIssueActions delete all actions related with issueID | ||||||
|  | func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error { | ||||||
|  | 	// delete actions assigned to this issue | ||||||
|  | 	subQuery := builder.Select("`id`"). | ||||||
|  | 		From("`comment`"). | ||||||
|  | 		Where(builder.Eq{"`issue_id`": issueID}) | ||||||
|  | 	if _, err := db.GetEngine(ctx).In("comment_id", subQuery).Delete(&Action{}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := db.GetEngine(ctx).Table("action").Where("repo_id = ?", repoID). | ||||||
|  | 		In("op_type", ActionCreateIssue, ActionCreatePullRequest). | ||||||
|  | 		Where("content LIKE ?", strconv.FormatInt(issueID, 10)+"|%"). | ||||||
|  | 		Delete(&Action{}) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|   | |||||||
| @@ -228,3 +228,46 @@ func TestGetFeedsCorrupted(t *testing.T) { | |||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, actions, 0) | 	assert.Len(t, actions, 0) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestConsistencyUpdateAction(t *testing.T) { | ||||||
|  | 	if !setting.Database.UseSQLite3 { | ||||||
|  | 		t.Skip("Test is only for SQLite database.") | ||||||
|  | 	} | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	id := 8 | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &Action{ | ||||||
|  | 		ID: int64(id), | ||||||
|  | 	}) | ||||||
|  | 	_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	actions := make([]*Action, 0, 1) | ||||||
|  | 	// | ||||||
|  | 	// XORM returns an error when created_unix is a string | ||||||
|  | 	// | ||||||
|  | 	err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions) | ||||||
|  | 	if assert.Error(t, err) { | ||||||
|  | 		assert.Contains(t, err.Error(), "type string to a int64: invalid syntax") | ||||||
|  | 	} | ||||||
|  | 	// | ||||||
|  | 	// Get rid of incorrectly set created_unix | ||||||
|  | 	// | ||||||
|  | 	count, err := CountActionCreatedUnixString() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 1, count) | ||||||
|  | 	count, err = FixActionCreatedUnixString() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 1, count) | ||||||
|  |  | ||||||
|  | 	count, err = CountActionCreatedUnixString() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 0, count) | ||||||
|  | 	count, err = FixActionCreatedUnixString() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 0, count) | ||||||
|  |  | ||||||
|  | 	// | ||||||
|  | 	// XORM must be happy now | ||||||
|  | 	// | ||||||
|  | 	assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &Action{}) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,80 +0,0 @@ | |||||||
| // Copyright 2022 The Gitea Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	git_model "code.gitea.io/gitea/models/git" |  | ||||||
| 	"code.gitea.io/gitea/modules/log" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // HasEnoughApprovals returns true if pr has enough granted approvals. |  | ||||||
| func HasEnoughApprovals(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { |  | ||||||
| 	if protectBranch.RequiredApprovals == 0 { |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
| 	return GetGrantedApprovalsCount(ctx, protectBranch, pr) >= protectBranch.RequiredApprovals |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist. |  | ||||||
| func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) int64 { |  | ||||||
| 	sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). |  | ||||||
| 		And("type = ?", ReviewTypeApprove). |  | ||||||
| 		And("official = ?", true). |  | ||||||
| 		And("dismissed = ?", false) |  | ||||||
| 	if protectBranch.DismissStaleApprovals { |  | ||||||
| 		sess = sess.And("stale = ?", false) |  | ||||||
| 	} |  | ||||||
| 	approvals, err := sess.Count(new(Review)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("GetGrantedApprovalsCount: %v", err) |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return approvals |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews |  | ||||||
| func MergeBlockedByRejectedReview(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { |  | ||||||
| 	if !protectBranch.BlockOnRejectedReviews { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). |  | ||||||
| 		And("type = ?", ReviewTypeReject). |  | ||||||
| 		And("official = ?", true). |  | ||||||
| 		And("dismissed = ?", false). |  | ||||||
| 		Exist(new(Review)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("MergeBlockedByRejectedReview: %v", err) |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return rejectExist |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer |  | ||||||
| // of from official review |  | ||||||
| func MergeBlockedByOfficialReviewRequests(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { |  | ||||||
| 	if !protectBranch.BlockOnOfficialReviewRequests { |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). |  | ||||||
| 		And("type = ?", ReviewTypeRequest). |  | ||||||
| 		And("official = ?", true). |  | ||||||
| 		Exist(new(Review)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("MergeBlockedByOfficialReviewRequests: %v", err) |  | ||||||
| 		return true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return has |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch |  | ||||||
| func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { |  | ||||||
| 	return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0 |  | ||||||
| } |  | ||||||
| @@ -5,7 +5,6 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	admin_model "code.gitea.io/gitea/models/admin" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -14,151 +13,6 @@ import ( | |||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore |  | ||||||
| func CountOrphanedLabels() (int64, error) { |  | ||||||
| 	noref, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count("label.id") |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	norepo, err := db.GetEngine(db.DefaultContext).Table("label"). |  | ||||||
| 		Where(builder.And( |  | ||||||
| 			builder.Gt{"repo_id": 0}, |  | ||||||
| 			builder.NotIn("repo_id", builder.Select("id").From("repository")), |  | ||||||
| 		)). |  | ||||||
| 		Count() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	noorg, err := db.GetEngine(db.DefaultContext).Table("label"). |  | ||||||
| 		Where(builder.And( |  | ||||||
| 			builder.Gt{"org_id": 0}, |  | ||||||
| 			builder.NotIn("org_id", builder.Select("id").From("user")), |  | ||||||
| 		)). |  | ||||||
| 		Count() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return noref + norepo + noorg, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore |  | ||||||
| func DeleteOrphanedLabels() error { |  | ||||||
| 	// delete labels with no reference |  | ||||||
| 	if _, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// delete labels with none existing repos |  | ||||||
| 	if _, err := db.GetEngine(db.DefaultContext). |  | ||||||
| 		Where(builder.And( |  | ||||||
| 			builder.Gt{"repo_id": 0}, |  | ||||||
| 			builder.NotIn("repo_id", builder.Select("id").From("repository")), |  | ||||||
| 		)). |  | ||||||
| 		Delete(Label{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// delete labels with none existing orgs |  | ||||||
| 	if _, err := db.GetEngine(db.DefaultContext). |  | ||||||
| 		Where(builder.And( |  | ||||||
| 			builder.Gt{"org_id": 0}, |  | ||||||
| 			builder.NotIn("org_id", builder.Select("id").From("user")), |  | ||||||
| 		)). |  | ||||||
| 		Delete(Label{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore |  | ||||||
| func CountOrphanedIssueLabels() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).Table("issue_label"). |  | ||||||
| 		NotIn("label_id", builder.Select("id").From("label")). |  | ||||||
| 		Count() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore |  | ||||||
| func DeleteOrphanedIssueLabels() error { |  | ||||||
| 	_, err := db.GetEngine(db.DefaultContext). |  | ||||||
| 		NotIn("label_id", builder.Select("id").From("label")). |  | ||||||
| 		Delete(IssueLabel{}) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountOrphanedIssues count issues without a repo |  | ||||||
| func CountOrphanedIssues() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).Table("issue"). |  | ||||||
| 		Join("LEFT", "repository", "issue.repo_id=repository.id"). |  | ||||||
| 		Where(builder.IsNull{"repository.id"}). |  | ||||||
| 		Select("COUNT(`issue`.`id`)"). |  | ||||||
| 		Count() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeleteOrphanedIssues delete issues without a repo |  | ||||||
| func DeleteOrphanedIssues() error { |  | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
|  |  | ||||||
| 	var ids []int64 |  | ||||||
|  |  | ||||||
| 	if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id"). |  | ||||||
| 		Join("LEFT", "repository", "issue.repo_id=repository.id"). |  | ||||||
| 		Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id"). |  | ||||||
| 		Find(&ids); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var attachmentPaths []string |  | ||||||
| 	for i := range ids { |  | ||||||
| 		paths, err := deleteIssuesByRepoID(ctx, ids[i]) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		attachmentPaths = append(attachmentPaths, paths...) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := committer.Commit(); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	committer.Close() |  | ||||||
|  |  | ||||||
| 	// Remove issue attachment files. |  | ||||||
| 	for i := range attachmentPaths { |  | ||||||
| 		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i]) |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountOrphanedObjects count subjects with have no existing refobject anymore |  | ||||||
| func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).Table("`"+subject+"`"). |  | ||||||
| 		Join("LEFT", "`"+refobject+"`", joinCond). |  | ||||||
| 		Where(builder.IsNull{"`" + refobject + "`.id"}). |  | ||||||
| 		Select("COUNT(`" + subject + "`.`id`)"). |  | ||||||
| 		Count() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DeleteOrphanedObjects delete subjects with have no existing refobject anymore |  | ||||||
| func DeleteOrphanedObjects(subject, refobject, joinCond string) error { |  | ||||||
| 	subQuery := builder.Select("`"+subject+"`.id"). |  | ||||||
| 		From("`"+subject+"`"). |  | ||||||
| 		Join("LEFT", "`"+refobject+"`", joinCond). |  | ||||||
| 		Where(builder.IsNull{"`" + refobject + "`.id"}) |  | ||||||
| 	sql, args, err := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`").ToSQL() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).Exec(append([]interface{}{sql}, args...)...) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountNullArchivedRepository counts the number of repositories with is_archived is null | // CountNullArchivedRepository counts the number of repositories with is_archived is null | ||||||
| func CountNullArchivedRepository() (int64, error) { | func CountNullArchivedRepository() (int64, error) { | ||||||
| 	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository)) | 	return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository)) | ||||||
| @@ -181,74 +35,6 @@ func FixWrongUserType() (int64, error) { | |||||||
| 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1}) | 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1}) | ||||||
| } | } | ||||||
|  |  | ||||||
| // CountCommentTypeLabelWithEmptyLabel count label comments with empty label |  | ||||||
| func CountCommentTypeLabelWithEmptyLabel() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // FixCommentTypeLabelWithEmptyLabel count label comments with empty label |  | ||||||
| func FixCommentTypeLabelWithEmptyLabel() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountCommentTypeLabelWithOutsideLabels count label comments with outside label |  | ||||||
| func CountCommentTypeLabelWithOutsideLabels() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).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"). |  | ||||||
| 		Join("inner", "label", "label.id = comment.label_id"). |  | ||||||
| 		Join("inner", "issue", "issue.id = comment.issue_id "). |  | ||||||
| 		Join("inner", "repository", "issue.repo_id = repository.id"). |  | ||||||
| 		Count(new(Comment)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // FixCommentTypeLabelWithOutsideLabels count label comments with outside label |  | ||||||
| func FixCommentTypeLabelWithOutsideLabels() (int64, error) { |  | ||||||
| 	res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM comment WHERE comment.id IN ( |  | ||||||
| 		SELECT il_too.id FROM ( |  | ||||||
| 			SELECT com.id |  | ||||||
| 				FROM comment AS com |  | ||||||
| 					INNER JOIN label ON com.label_id = label.id |  | ||||||
| 					INNER JOIN issue on issue.id = com.issue_id |  | ||||||
| 					INNER JOIN repository ON issue.repo_id = repository.id |  | ||||||
| 				WHERE |  | ||||||
| 					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) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return res.RowsAffected() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountIssueLabelWithOutsideLabels count label comments with outside label |  | ||||||
| func CountIssueLabelWithOutsideLabels() (int64, error) { |  | ||||||
| 	return db.GetEngine(db.DefaultContext).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"). |  | ||||||
| 		Join("inner", "label", "issue_label.label_id = label.id "). |  | ||||||
| 		Join("inner", "issue", "issue.id = issue_label.issue_id "). |  | ||||||
| 		Join("inner", "repository", "issue.repo_id = repository.id"). |  | ||||||
| 		Count(new(IssueLabel)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // FixIssueLabelWithOutsideLabels fix label comments with outside label |  | ||||||
| func FixIssueLabelWithOutsideLabels() (int64, error) { |  | ||||||
| 	res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM issue_label WHERE issue_label.id IN ( |  | ||||||
| 		SELECT il_too.id FROM ( |  | ||||||
| 			SELECT il_too_too.id |  | ||||||
| 				FROM issue_label AS il_too_too |  | ||||||
| 					INNER JOIN label ON il_too_too.label_id = label.id |  | ||||||
| 					INNER JOIN issue on issue.id = il_too_too.issue_id |  | ||||||
| 					INNER JOIN repository on repository.id = issue.repo_id |  | ||||||
| 				WHERE |  | ||||||
| 					(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 )`) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return res.RowsAffected() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountActionCreatedUnixString count actions where created_unix is an empty string | // CountActionCreatedUnixString count actions where created_unix is an empty string | ||||||
| func CountActionCreatedUnixString() (int64, error) { | func CountActionCreatedUnixString() (int64, error) { | ||||||
| 	if setting.Database.UseSQLite3 { | 	if setting.Database.UseSQLite3 { | ||||||
|   | |||||||
| @@ -1,149 +0,0 @@ | |||||||
| // Copyright 2021 Gitea. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
| 	"code.gitea.io/gitea/modules/setting" |  | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestDeleteOrphanedObjects(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	countBefore, err := db.GetEngine(db.DefaultContext).Count(&PullRequest{}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).Insert(&PullRequest{IssueID: 1000}, &PullRequest{IssueID: 1001}, &PullRequest{IssueID: 1003}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	orphaned, err := CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 3, orphaned) |  | ||||||
|  |  | ||||||
| 	err = DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	countAfter, err := db.GetEngine(db.DefaultContext).Count(&PullRequest{}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, countBefore, countAfter) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestNewMilestone(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	milestone := &issues_model.Milestone{ |  | ||||||
| 		RepoID:  1, |  | ||||||
| 		Name:    "milestoneName", |  | ||||||
| 		Content: "milestoneContent", |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, issues_model.NewMilestone(milestone)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, milestone) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestChangeMilestoneStatus(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, true)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1") |  | ||||||
| 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, false)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0") |  | ||||||
| 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeleteMilestoneByRepoID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	assert.NoError(t, issues_model.DeleteMilestoneByRepoID(1, 1)) |  | ||||||
| 	unittest.AssertNotExistsBean(t, &issues_model.Milestone{ID: 1}) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1}) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, issues_model.DeleteMilestoneByRepoID(unittest.NonexistentID, unittest.NonexistentID)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdateMilestone(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) |  | ||||||
| 	milestone.Name = " newMilestoneName  " |  | ||||||
| 	milestone.Content = "newMilestoneContent" |  | ||||||
| 	assert.NoError(t, issues_model.UpdateMilestone(milestone, milestone.IsClosed)) |  | ||||||
| 	milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) |  | ||||||
| 	assert.EqualValues(t, "newMilestoneName", milestone.Name) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdateMilestoneCounters(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1}, |  | ||||||
| 		"is_closed=0").(*Issue) |  | ||||||
|  |  | ||||||
| 	issue.IsClosed = true |  | ||||||
| 	issue.ClosedUnix = timeutil.TimeStampNow() |  | ||||||
| 	_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) |  | ||||||
|  |  | ||||||
| 	issue.IsClosed = false |  | ||||||
| 	issue.ClosedUnix = 0 |  | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestConsistencyUpdateAction(t *testing.T) { |  | ||||||
| 	if !setting.Database.UseSQLite3 { |  | ||||||
| 		t.Skip("Test is only for SQLite database.") |  | ||||||
| 	} |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	id := 8 |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Action{ |  | ||||||
| 		ID: int64(id), |  | ||||||
| 	}) |  | ||||||
| 	_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	actions := make([]*Action, 0, 1) |  | ||||||
| 	// |  | ||||||
| 	// XORM returns an error when created_unix is a string |  | ||||||
| 	// |  | ||||||
| 	err = db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions) |  | ||||||
| 	if assert.Error(t, err) { |  | ||||||
| 		assert.Contains(t, err.Error(), "type string to a int64: invalid syntax") |  | ||||||
| 	} |  | ||||||
| 	// |  | ||||||
| 	// Get rid of incorrectly set created_unix |  | ||||||
| 	// |  | ||||||
| 	count, err := CountActionCreatedUnixString() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 1, count) |  | ||||||
| 	count, err = FixActionCreatedUnixString() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 1, count) |  | ||||||
|  |  | ||||||
| 	count, err = CountActionCreatedUnixString() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 0, count) |  | ||||||
| 	count, err = FixActionCreatedUnixString() |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 0, count) |  | ||||||
|  |  | ||||||
| 	// |  | ||||||
| 	// XORM must be happy now |  | ||||||
| 	// |  | ||||||
| 	assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions)) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Action{}) |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								models/db/consistency.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								models/db/consistency.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | // Copyright 2022 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 db | ||||||
|  |  | ||||||
|  | import "xorm.io/builder" | ||||||
|  |  | ||||||
|  | // CountOrphanedObjects count subjects with have no existing refobject anymore | ||||||
|  | func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) { | ||||||
|  | 	return GetEngine(DefaultContext).Table("`"+subject+"`"). | ||||||
|  | 		Join("LEFT", "`"+refobject+"`", joinCond). | ||||||
|  | 		Where(builder.IsNull{"`" + refobject + "`.id"}). | ||||||
|  | 		Select("COUNT(`" + subject + "`.`id`)"). | ||||||
|  | 		Count() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteOrphanedObjects delete subjects with have no existing refobject anymore | ||||||
|  | func DeleteOrphanedObjects(subject, refobject, joinCond string) error { | ||||||
|  | 	subQuery := builder.Select("`"+subject+"`.id"). | ||||||
|  | 		From("`"+subject+"`"). | ||||||
|  | 		Join("LEFT", "`"+refobject+"`", joinCond). | ||||||
|  | 		Where(builder.IsNull{"`" + refobject + "`.id"}) | ||||||
|  | 	b := builder.Delete(builder.In("id", subQuery)).From("`" + subject + "`") | ||||||
|  | 	_, err := GetEngine(DefaultContext).Exec(b) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
| @@ -20,21 +20,21 @@ type ResourceIndex struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // UpsertResourceIndex the function will not return until it acquires the lock or receives an error. | // UpsertResourceIndex the function will not return until it acquires the lock or receives an error. | ||||||
| func UpsertResourceIndex(e Engine, tableName string, groupID int64) (err error) { | func UpsertResourceIndex(ctx context.Context, tableName string, groupID int64) (err error) { | ||||||
| 	// An atomic UPSERT operation (INSERT/UPDATE) is the only operation | 	// An atomic UPSERT operation (INSERT/UPDATE) is the only operation | ||||||
| 	// that ensures that the key is actually locked. | 	// that ensures that the key is actually locked. | ||||||
| 	switch { | 	switch { | ||||||
| 	case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL: | 	case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL: | ||||||
| 		_, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ | 		_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ | ||||||
| 			"VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1", | 			"VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1", | ||||||
| 			tableName, tableName), groupID) | 			tableName, tableName), groupID) | ||||||
| 	case setting.Database.UseMySQL: | 	case setting.Database.UseMySQL: | ||||||
| 		_, err = e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ | 		_, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+ | ||||||
| 			"VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName), | 			"VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName), | ||||||
| 			groupID) | 			groupID) | ||||||
| 	case setting.Database.UseMSSQL: | 	case setting.Database.UseMSSQL: | ||||||
| 		// https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ | 		// https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/ | ||||||
| 		_, err = e.Exec(fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+ | 		_, err = Exec(ctx, fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+ | ||||||
| 			"USING (SELECT ? AS group_id) AS src "+ | 			"USING (SELECT ? AS group_id) AS src "+ | ||||||
| 			"ON src.group_id = target.group_id "+ | 			"ON src.group_id = target.group_id "+ | ||||||
| 			"WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+ | 			"WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+ | ||||||
| @@ -82,30 +82,29 @@ func DeleteResouceIndex(ctx context.Context, tableName string, groupID int64) er | |||||||
|  |  | ||||||
| // getNextResourceIndex return the next index | // getNextResourceIndex return the next index | ||||||
| func getNextResourceIndex(tableName string, groupID int64) (int64, error) { | func getNextResourceIndex(tableName string, groupID int64) (int64, error) { | ||||||
| 	sess := x.NewSession() | 	ctx, commiter, err := TxContext() | ||||||
| 	defer sess.Close() |  | ||||||
| 	if err := sess.Begin(); err != nil { |  | ||||||
| 		return 0, err |  | ||||||
| 	} |  | ||||||
| 	var preIdx int64 |  | ||||||
| 	_, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx) |  | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
|  | 	defer commiter.Close() | ||||||
|  | 	var preIdx int64 | ||||||
|  | 	if _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx); err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if err := UpsertResourceIndex(sess, tableName, groupID); err != nil { | 	if err := UpsertResourceIndex(ctx, tableName, groupID); err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var curIdx int64 | 	var curIdx int64 | ||||||
| 	has, err := sess.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx) | 	has, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
| 	if !has { | 	if !has { | ||||||
| 		return 0, ErrResouceOutdated | 		return 0, ErrResouceOutdated | ||||||
| 	} | 	} | ||||||
| 	if err := sess.Commit(); err != nil { | 	if err := commiter.Commit(); err != nil { | ||||||
| 		return 0, err | 		return 0, err | ||||||
| 	} | 	} | ||||||
| 	return curIdx, nil | 	return curIdx, nil | ||||||
|   | |||||||
| @@ -10,6 +10,11 @@ import ( | |||||||
| 	"xorm.io/xorm" | 	"xorm.io/xorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | const ( | ||||||
|  | 	// DefaultMaxInSize represents default variables number on IN () in SQL | ||||||
|  | 	DefaultMaxInSize = 50 | ||||||
|  | ) | ||||||
|  |  | ||||||
| // Paginator is the base for different ListOptions types | // Paginator is the base for different ListOptions types | ||||||
| type Paginator interface { | type Paginator interface { | ||||||
| 	GetSkipTake() (skip, take int) | 	GetSkipTake() (skip, take int) | ||||||
|   | |||||||
							
								
								
									
										376
									
								
								models/error.go
									
									
									
									
									
								
							
							
						
						
									
										376
									
								
								models/error.go
									
									
									
									
									
								
							| @@ -405,22 +405,6 @@ func (err ErrFilePathProtected) Error() string { | |||||||
| 	return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path) | 	return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path) | ||||||
| } | } | ||||||
|  |  | ||||||
| // ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo. |  | ||||||
| type ErrUserDoesNotHaveAccessToRepo struct { |  | ||||||
| 	UserID   int64 |  | ||||||
| 	RepoName string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists. |  | ||||||
| func IsErrUserDoesNotHaveAccessToRepo(err error) bool { |  | ||||||
| 	_, ok := err.(ErrUserDoesNotHaveAccessToRepo) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrUserDoesNotHaveAccessToRepo) Error() string { |  | ||||||
| 	return fmt.Sprintf("user doesn't have access to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // __________                             .__ | // __________                             .__ | ||||||
| // \______   \____________    ____   ____ |  |__ | // \______   \____________    ____   ____ |  |__ | ||||||
| //  |    |  _/\_  __ \__  \  /    \_/ ___\|  |  \ | //  |    |  _/\_  __ \__  \  /    \_/ ___\|  |  \ | ||||||
| @@ -580,162 +564,6 @@ func (err ErrSHAOrCommitIDNotProvided) Error() string { | |||||||
| 	return "a SHA or commit ID must be proved when updating a file" | 	return "a SHA or commit ID must be proved when updating a file" | ||||||
| } | } | ||||||
|  |  | ||||||
| // .___ |  | ||||||
| // |   | ______ ________ __   ____ |  | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \ |  | ||||||
| // |   |\___ \ \___ \|  |  /\  ___/ |  | ||||||
| // |___/____  >____  >____/  \___  > |  | ||||||
| //          \/     \/            \/ |  | ||||||
|  |  | ||||||
| // ErrIssueNotExist represents a "IssueNotExist" kind of error. |  | ||||||
| type ErrIssueNotExist struct { |  | ||||||
| 	ID     int64 |  | ||||||
| 	RepoID int64 |  | ||||||
| 	Index  int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrIssueNotExist checks if an error is a ErrIssueNotExist. |  | ||||||
| func IsErrIssueNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrIssueNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrIssueNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrIssueIsClosed represents a "IssueIsClosed" kind of error. |  | ||||||
| type ErrIssueIsClosed struct { |  | ||||||
| 	ID     int64 |  | ||||||
| 	RepoID int64 |  | ||||||
| 	Index  int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrIssueIsClosed checks if an error is a ErrIssueNotExist. |  | ||||||
| func IsErrIssueIsClosed(err error) bool { |  | ||||||
| 	_, ok := err.(ErrIssueIsClosed) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrIssueIsClosed) Error() string { |  | ||||||
| 	return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrNewIssueInsert is used when the INSERT statement in newIssue fails |  | ||||||
| type ErrNewIssueInsert struct { |  | ||||||
| 	OriginalError error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert. |  | ||||||
| func IsErrNewIssueInsert(err error) bool { |  | ||||||
| 	_, ok := err.(ErrNewIssueInsert) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrNewIssueInsert) Error() string { |  | ||||||
| 	return err.OriginalError.Error() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrIssueWasClosed is used when close a closed issue |  | ||||||
| type ErrIssueWasClosed struct { |  | ||||||
| 	ID    int64 |  | ||||||
| 	Index int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed. |  | ||||||
| func IsErrIssueWasClosed(err error) bool { |  | ||||||
| 	_, ok := err.(ErrIssueWasClosed) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrIssueWasClosed) Error() string { |  | ||||||
| 	return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrPullWasClosed is used close a closed pull request |  | ||||||
| type ErrPullWasClosed struct { |  | ||||||
| 	ID    int64 |  | ||||||
| 	Index int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed. |  | ||||||
| func IsErrPullWasClosed(err error) bool { |  | ||||||
| 	_, ok := err.(ErrPullWasClosed) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrPullWasClosed) Error() string { |  | ||||||
| 	return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // __________      .__  .__ __________                                     __ |  | ||||||
| // \______   \__ __|  | |  |\______   \ ____  ________ __   ____   _______/  |_ |  | ||||||
| //  |     ___/  |  \  | |  | |       _// __ \/ ____/  |  \_/ __ \ /  ___/\   __\ |  | ||||||
| //  |    |   |  |  /  |_|  |_|    |   \  ___< <_|  |  |  /\  ___/ \___ \  |  | |  | ||||||
| //  |____|   |____/|____/____/____|_  /\___  >__   |____/  \___  >____  > |__| |  | ||||||
| //                                  \/     \/   |__|           \/     \/ |  | ||||||
|  |  | ||||||
| // ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error. |  | ||||||
| type ErrPullRequestNotExist struct { |  | ||||||
| 	ID         int64 |  | ||||||
| 	IssueID    int64 |  | ||||||
| 	HeadRepoID int64 |  | ||||||
| 	BaseRepoID int64 |  | ||||||
| 	HeadBranch string |  | ||||||
| 	BaseBranch string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist. |  | ||||||
| func IsErrPullRequestNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrPullRequestNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrPullRequestNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("pull request does not exist [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", |  | ||||||
| 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error |  | ||||||
| type ErrPullRequestAlreadyExists struct { |  | ||||||
| 	ID         int64 |  | ||||||
| 	IssueID    int64 |  | ||||||
| 	HeadRepoID int64 |  | ||||||
| 	BaseRepoID int64 |  | ||||||
| 	HeadBranch string |  | ||||||
| 	BaseBranch string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrPullRequestAlreadyExists checks if an error is a ErrPullRequestAlreadyExists. |  | ||||||
| func IsErrPullRequestAlreadyExists(err error) bool { |  | ||||||
| 	_, ok := err.(ErrPullRequestAlreadyExists) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Error does pretty-printing :D |  | ||||||
| func (err ErrPullRequestAlreadyExists) Error() string { |  | ||||||
| 	return fmt.Sprintf("pull request already exists for these targets [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", |  | ||||||
| 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error |  | ||||||
| type ErrPullRequestHeadRepoMissing struct { |  | ||||||
| 	ID         int64 |  | ||||||
| 	HeadRepoID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrErrPullRequestHeadRepoMissing checks if an error is a ErrPullRequestHeadRepoMissing. |  | ||||||
| func IsErrErrPullRequestHeadRepoMissing(err error) bool { |  | ||||||
| 	_, ok := err.(ErrPullRequestHeadRepoMissing) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Error does pretty-printing :D |  | ||||||
| func (err ErrPullRequestHeadRepoMissing) Error() string { |  | ||||||
| 	return fmt.Sprintf("pull request head repo missing [id: %d, head_repo_id: %d]", |  | ||||||
| 		err.ID, err.HeadRepoID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrInvalidMergeStyle represents an error if merging with disabled merge strategy | // ErrInvalidMergeStyle represents an error if merging with disabled merge strategy | ||||||
| type ErrInvalidMergeStyle struct { | type ErrInvalidMergeStyle struct { | ||||||
| 	ID    int64 | 	ID    int64 | ||||||
| @@ -830,29 +658,6 @@ func (err ErrPullRequestHasMerged) Error() string { | |||||||
| 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) | 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) | ||||||
| } | } | ||||||
|  |  | ||||||
| // _________                                       __ |  | ||||||
| // \_   ___ \  ____   _____   _____   ____   _____/  |_ |  | ||||||
| // /    \  \/ /  _ \ /     \ /     \_/ __ \ /    \   __\ |  | ||||||
| // \     \___(  <_> )  Y Y  \  Y Y  \  ___/|   |  \  | |  | ||||||
| //  \______  /\____/|__|_|  /__|_|  /\___  >___|  /__| |  | ||||||
| //         \/             \/      \/     \/     \/ |  | ||||||
|  |  | ||||||
| // ErrCommentNotExist represents a "CommentNotExist" kind of error. |  | ||||||
| type ErrCommentNotExist struct { |  | ||||||
| 	ID      int64 |  | ||||||
| 	IssueID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrCommentNotExist checks if an error is a ErrCommentNotExist. |  | ||||||
| func IsErrCommentNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrCommentNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrCommentNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| //  _________ __                                __         .__ | //  _________ __                                __         .__ | ||||||
| //  /   _____//  |_  ____ ________  _  _______ _/  |_  ____ |  |__ | //  /   _____//  |_  ____ ________  _  _______ _/  |_  ____ |  |__ | ||||||
| //  \_____  \\   __\/  _ \\____ \ \/ \/ /\__  \\   __\/ ___\|  |  \ | //  \_____  \\   __\/  _ \\____ \ \/ \/ /\__  \\   __\/ ___\|  |  \ | ||||||
| @@ -897,60 +702,6 @@ func (err ErrTrackedTimeNotExist) Error() string { | |||||||
| 	return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID) | 	return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID) | ||||||
| } | } | ||||||
|  |  | ||||||
| // .____          ___.          .__ |  | ||||||
| // |    |   _____ \_ |__   ____ |  | |  | ||||||
| // |    |   \__  \ | __ \_/ __ \|  | |  | ||||||
| // |    |___ / __ \| \_\ \  ___/|  |__ |  | ||||||
| // |_______ (____  /___  /\___  >____/ |  | ||||||
| //         \/    \/    \/     \/ |  | ||||||
|  |  | ||||||
| // ErrRepoLabelNotExist represents a "RepoLabelNotExist" kind of error. |  | ||||||
| type ErrRepoLabelNotExist struct { |  | ||||||
| 	LabelID int64 |  | ||||||
| 	RepoID  int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrRepoLabelNotExist checks if an error is a RepoErrLabelNotExist. |  | ||||||
| func IsErrRepoLabelNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrRepoLabelNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrRepoLabelNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error. |  | ||||||
| type ErrOrgLabelNotExist struct { |  | ||||||
| 	LabelID int64 |  | ||||||
| 	OrgID   int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrOrgLabelNotExist checks if an error is a OrgErrLabelNotExist. |  | ||||||
| func IsErrOrgLabelNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrOrgLabelNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrOrgLabelNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrLabelNotExist represents a "LabelNotExist" kind of error. |  | ||||||
| type ErrLabelNotExist struct { |  | ||||||
| 	LabelID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrLabelNotExist checks if an error is a ErrLabelNotExist. |  | ||||||
| func IsErrLabelNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrLabelNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrLabelNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| //  ____ ___        .__                    .___ | //  ____ ___        .__                    .___ | ||||||
| // |    |   \______ |  |   _________     __| _/ | // |    |   \______ |  |   _________     __| _/ | ||||||
| // |    |   /\____ \|  |  /  _ \__  \   / __ | | // |    |   /\____ \|  |  /  _ \__  \   / __ | | ||||||
| @@ -974,130 +725,3 @@ func IsErrUploadNotExist(err error) bool { | |||||||
| func (err ErrUploadNotExist) Error() string { | func (err ErrUploadNotExist) Error() string { | ||||||
| 	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) | 	return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID) | ||||||
| } | } | ||||||
|  |  | ||||||
| // .___                            ________                                   .___                   .__ |  | ||||||
| // |   | ______ ________ __   ____ \______ \   ____ ______   ____   ____    __| _/____   ____   ____ |__| ____   ______ |  | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \ |    |  \_/ __ \\____ \_/ __ \ /    \  / __ |/ __ \ /    \_/ ___\|  |/ __ \ /  ___/ |  | ||||||
| // |   |\___ \ \___ \|  |  /\  ___/ |    `   \  ___/|  |_> >  ___/|   |  \/ /_/ \  ___/|   |  \  \___|  \  ___/ \___ \ |  | ||||||
| // |___/____  >____  >____/  \___  >_______  /\___  >   __/ \___  >___|  /\____ |\___  >___|  /\___  >__|\___  >____  > |  | ||||||
| //          \/     \/            \/        \/     \/|__|        \/     \/      \/    \/     \/     \/        \/     \/ |  | ||||||
|  |  | ||||||
| // ErrDependencyExists represents a "DependencyAlreadyExists" kind of error. |  | ||||||
| type ErrDependencyExists struct { |  | ||||||
| 	IssueID      int64 |  | ||||||
| 	DependencyID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrDependencyExists checks if an error is a ErrDependencyExists. |  | ||||||
| func IsErrDependencyExists(err error) bool { |  | ||||||
| 	_, ok := err.(ErrDependencyExists) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrDependencyExists) Error() string { |  | ||||||
| 	return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error. |  | ||||||
| type ErrDependencyNotExists struct { |  | ||||||
| 	IssueID      int64 |  | ||||||
| 	DependencyID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrDependencyNotExists checks if an error is a ErrDependencyExists. |  | ||||||
| func IsErrDependencyNotExists(err error) bool { |  | ||||||
| 	_, ok := err.(ErrDependencyNotExists) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrDependencyNotExists) Error() string { |  | ||||||
| 	return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrCircularDependency represents a "DependencyCircular" kind of error. |  | ||||||
| type ErrCircularDependency struct { |  | ||||||
| 	IssueID      int64 |  | ||||||
| 	DependencyID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrCircularDependency checks if an error is a ErrCircularDependency. |  | ||||||
| func IsErrCircularDependency(err error) bool { |  | ||||||
| 	_, ok := err.(ErrCircularDependency) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrCircularDependency) Error() string { |  | ||||||
| 	return fmt.Sprintf("circular dependencies exists (two issues blocking each other) [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrDependenciesLeft represents an error where the issue you're trying to close still has dependencies left. |  | ||||||
| type ErrDependenciesLeft struct { |  | ||||||
| 	IssueID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrDependenciesLeft checks if an error is a ErrDependenciesLeft. |  | ||||||
| func IsErrDependenciesLeft(err error) bool { |  | ||||||
| 	_, ok := err.(ErrDependenciesLeft) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrDependenciesLeft) Error() string { |  | ||||||
| 	return fmt.Sprintf("issue has open dependencies [issue id: %d]", err.IssueID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrUnknownDependencyType represents an error where an unknown dependency type was passed |  | ||||||
| type ErrUnknownDependencyType struct { |  | ||||||
| 	Type DependencyType |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType |  | ||||||
| func IsErrUnknownDependencyType(err error) bool { |  | ||||||
| 	_, ok := err.(ErrUnknownDependencyType) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrUnknownDependencyType) Error() string { |  | ||||||
| 	return fmt.Sprintf("unknown dependency type [type: %d]", err.Type) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| //  __________            .__ |  | ||||||
| //  \______   \ _______  _|__| ______  _  __ |  | ||||||
| //  |       _// __ \  \/ /  |/ __ \ \/ \/ / |  | ||||||
| //  |    |   \  ___/\   /|  \  ___/\     / |  | ||||||
| //  |____|_  /\___  >\_/ |__|\___  >\/\_/ |  | ||||||
| //  \/     \/             \/ |  | ||||||
|  |  | ||||||
| // ErrReviewNotExist represents a "ReviewNotExist" kind of error. |  | ||||||
| type ErrReviewNotExist struct { |  | ||||||
| 	ID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrReviewNotExist checks if an error is a ErrReviewNotExist. |  | ||||||
| func IsErrReviewNotExist(err error) bool { |  | ||||||
| 	_, ok := err.(ErrReviewNotExist) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrReviewNotExist) Error() string { |  | ||||||
| 	return fmt.Sprintf("review does not exist [id: %d]", err.ID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ErrNotValidReviewRequest an not allowed review request modify |  | ||||||
| type ErrNotValidReviewRequest struct { |  | ||||||
| 	Reason string |  | ||||||
| 	UserID int64 |  | ||||||
| 	RepoID int64 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // IsErrNotValidReviewRequest checks if an error is a ErrNotValidReviewRequest. |  | ||||||
| func IsErrNotValidReviewRequest(err error) bool { |  | ||||||
| 	_, ok := err.(ErrNotValidReviewRequest) |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (err ErrNotValidReviewRequest) Error() string { |  | ||||||
| 	return fmt.Sprintf("%s [user_id: %d, repo_id: %d]", |  | ||||||
| 		err.Reason, |  | ||||||
| 		err.UserID, |  | ||||||
| 		err.RepoID) |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -7,9 +7,9 @@ package git_test | |||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
| @@ -120,10 +120,10 @@ func TestRenameBranch(t *testing.T) { | |||||||
| 	repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	assert.Equal(t, "main", repo1.DefaultBranch) | 	assert.Equal(t, "main", repo1.DefaultBranch) | ||||||
|  |  | ||||||
| 	pull := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) // merged | 	pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) // merged | ||||||
| 	assert.Equal(t, "master", pull.BaseBranch) | 	assert.Equal(t, "master", pull.BaseBranch) | ||||||
|  |  | ||||||
| 	pull = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 2}).(*models.PullRequest) // open | 	pull = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) // open | ||||||
| 	assert.Equal(t, "main", pull.BaseBranch) | 	assert.Equal(t, "main", pull.BaseBranch) | ||||||
|  |  | ||||||
| 	renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch) | 	renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch) | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,394 +0,0 @@ | |||||||
| // Copyright 2017 The Gitea Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"html/template" |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // TODO TestGetLabelTemplateFile |  | ||||||
|  |  | ||||||
| func TestLabel_CalOpenIssues(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	label.CalOpenIssues() |  | ||||||
| 	assert.EqualValues(t, 2, label.NumOpenIssues) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestLabel_ForegroundColor(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	assert.Equal(t, template.CSS("#000"), label.ForegroundColor()) |  | ||||||
|  |  | ||||||
| 	label = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) |  | ||||||
| 	assert.Equal(t, template.CSS("#fff"), label.ForegroundColor()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestNewLabels(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labels := []*Label{ |  | ||||||
| 		{RepoID: 2, Name: "labelName2", Color: "#123456"}, |  | ||||||
| 		{RepoID: 3, Name: "labelName3", Color: "#123"}, |  | ||||||
| 		{RepoID: 4, Name: "labelName4", Color: "ABCDEF"}, |  | ||||||
| 		{RepoID: 5, Name: "labelName5", Color: "DEF"}, |  | ||||||
| 	} |  | ||||||
| 	assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: ""})) |  | ||||||
| 	assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "#45G"})) |  | ||||||
| 	assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "#12345G"})) |  | ||||||
| 	assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "45G"})) |  | ||||||
| 	assert.Error(t, NewLabel(db.DefaultContext, &Label{RepoID: 3, Name: "invalid Color", Color: "12345G"})) |  | ||||||
| 	for _, label := range labels { |  | ||||||
| 		unittest.AssertNotExistsBean(t, label) |  | ||||||
| 	} |  | ||||||
| 	assert.NoError(t, NewLabels(labels...)) |  | ||||||
| 	for _, label := range labels { |  | ||||||
| 		unittest.AssertExistsAndLoadBean(t, label, unittest.Cond("id = ?", label.ID)) |  | ||||||
| 	} |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelByID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label, err := GetLabelByID(db.DefaultContext, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 1, label.ID) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelByID(db.DefaultContext, unittest.NonexistentID) |  | ||||||
| 	assert.True(t, IsErrLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInRepoByName(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label, err := GetLabelInRepoByName(db.DefaultContext, 1, "label1") |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 1, label.ID) |  | ||||||
| 	assert.Equal(t, "label1", label.Name) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInRepoByName(db.DefaultContext, 1, "") |  | ||||||
| 	assert.True(t, IsErrRepoLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInRepoByName(db.DefaultContext, unittest.NonexistentID, "nonexistent") |  | ||||||
| 	assert.True(t, IsErrRepoLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInRepoByNames(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labelIDs, err := GetLabelIDsInRepoByNames(1, []string{"label1", "label2"}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	assert.Len(t, labelIDs, 2) |  | ||||||
|  |  | ||||||
| 	assert.Equal(t, int64(1), labelIDs[0]) |  | ||||||
| 	assert.Equal(t, int64(2), labelIDs[1]) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInRepoByNamesDiscardsNonExistentLabels(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	// label3 doesn't exists.. See labels.yml |  | ||||||
| 	labelIDs, err := GetLabelIDsInRepoByNames(1, []string{"label1", "label2", "label3"}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	assert.Len(t, labelIDs, 2) |  | ||||||
|  |  | ||||||
| 	assert.Equal(t, int64(1), labelIDs[0]) |  | ||||||
| 	assert.Equal(t, int64(2), labelIDs[1]) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInRepoByID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label, err := GetLabelInRepoByID(db.DefaultContext, 1, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 1, label.ID) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInRepoByID(db.DefaultContext, 1, -1) |  | ||||||
| 	assert.True(t, IsErrRepoLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInRepoByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) |  | ||||||
| 	assert.True(t, IsErrRepoLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelsInRepoByIDs(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labels, err := GetLabelsInRepoByIDs(1, []int64{1, 2, unittest.NonexistentID}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	if assert.Len(t, labels, 2) { |  | ||||||
| 		assert.EqualValues(t, 1, labels[0].ID) |  | ||||||
| 		assert.EqualValues(t, 2, labels[1].ID) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelsByRepoID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) { |  | ||||||
| 		labels, err := GetLabelsByRepoID(db.DefaultContext, repoID, sortType, db.ListOptions{}) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 		assert.Len(t, labels, len(expectedIssueIDs)) |  | ||||||
| 		for i, label := range labels { |  | ||||||
| 			assert.EqualValues(t, expectedIssueIDs[i], label.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	testSuccess(1, "leastissues", []int64{2, 1}) |  | ||||||
| 	testSuccess(1, "mostissues", []int64{1, 2}) |  | ||||||
| 	testSuccess(1, "reversealphabetically", []int64{2, 1}) |  | ||||||
| 	testSuccess(1, "default", []int64{1, 2}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Org versions |  | ||||||
|  |  | ||||||
| func TestGetLabelInOrgByName(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label, err := GetLabelInOrgByName(db.DefaultContext, 3, "orglabel3") |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 3, label.ID) |  | ||||||
| 	assert.Equal(t, "orglabel3", label.Name) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByName(db.DefaultContext, 3, "") |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByName(db.DefaultContext, 0, "orglabel3") |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByName(db.DefaultContext, -1, "orglabel3") |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByName(db.DefaultContext, unittest.NonexistentID, "nonexistent") |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInOrgByNames(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labelIDs, err := GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4"}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	assert.Len(t, labelIDs, 2) |  | ||||||
|  |  | ||||||
| 	assert.Equal(t, int64(3), labelIDs[0]) |  | ||||||
| 	assert.Equal(t, int64(4), labelIDs[1]) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInOrgByNamesDiscardsNonExistentLabels(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	// orglabel99 doesn't exists.. See labels.yml |  | ||||||
| 	labelIDs, err := GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4", "orglabel99"}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	assert.Len(t, labelIDs, 2) |  | ||||||
|  |  | ||||||
| 	assert.Equal(t, int64(3), labelIDs[0]) |  | ||||||
| 	assert.Equal(t, int64(4), labelIDs[1]) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelInOrgByID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label, err := GetLabelInOrgByID(db.DefaultContext, 3, 3) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 3, label.ID) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByID(db.DefaultContext, 3, -1) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByID(db.DefaultContext, 0, 3) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByID(db.DefaultContext, -1, 3) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelInOrgByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelsInOrgByIDs(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labels, err := GetLabelsInOrgByIDs(3, []int64{3, 4, unittest.NonexistentID}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	if assert.Len(t, labels, 2) { |  | ||||||
| 		assert.EqualValues(t, 3, labels[0].ID) |  | ||||||
| 		assert.EqualValues(t, 4, labels[1].ID) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestGetLabelsByOrgID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	testSuccess := func(orgID int64, sortType string, expectedIssueIDs []int64) { |  | ||||||
| 		labels, err := GetLabelsByOrgID(db.DefaultContext, orgID, sortType, db.ListOptions{}) |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 		assert.Len(t, labels, len(expectedIssueIDs)) |  | ||||||
| 		for i, label := range labels { |  | ||||||
| 			assert.EqualValues(t, expectedIssueIDs[i], label.ID) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	testSuccess(3, "leastissues", []int64{3, 4}) |  | ||||||
| 	testSuccess(3, "mostissues", []int64{4, 3}) |  | ||||||
| 	testSuccess(3, "reversealphabetically", []int64{4, 3}) |  | ||||||
| 	testSuccess(3, "default", []int64{3, 4}) |  | ||||||
|  |  | ||||||
| 	var err error |  | ||||||
| 	_, err = GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{}) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
|  |  | ||||||
| 	_, err = GetLabelsByOrgID(db.DefaultContext, -1, "leastissues", db.ListOptions{}) |  | ||||||
| 	assert.True(t, IsErrOrgLabelNotExist(err)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // |  | ||||||
|  |  | ||||||
| func TestGetLabelsByIssueID(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	labels, err := GetLabelsByIssueID(db.DefaultContext, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	if assert.Len(t, labels, 1) { |  | ||||||
| 		assert.EqualValues(t, 1, labels[0].ID) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	labels, err = GetLabelsByIssueID(db.DefaultContext, unittest.NonexistentID) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.Len(t, labels, 0) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdateLabel(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	// make sure update wont overwrite it |  | ||||||
| 	update := &Label{ |  | ||||||
| 		ID:          label.ID, |  | ||||||
| 		Color:       "#ffff00", |  | ||||||
| 		Name:        "newLabelName", |  | ||||||
| 		Description: label.Description, |  | ||||||
| 	} |  | ||||||
| 	label.Color = update.Color |  | ||||||
| 	label.Name = update.Name |  | ||||||
| 	assert.NoError(t, UpdateLabel(update)) |  | ||||||
| 	newLabel := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	assert.EqualValues(t, label.ID, newLabel.ID) |  | ||||||
| 	assert.EqualValues(t, label.Color, newLabel.Color) |  | ||||||
| 	assert.EqualValues(t, label.Name, newLabel.Name) |  | ||||||
| 	assert.EqualValues(t, label.Description, newLabel.Description) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeleteLabel(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	assert.NoError(t, DeleteLabel(label.RepoID, label.ID)) |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Label{ID: label.ID, RepoID: label.RepoID}) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, DeleteLabel(label.RepoID, label.ID)) |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Label{ID: label.ID}) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, DeleteLabel(unittest.NonexistentID, unittest.NonexistentID)) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Label{}, &repo_model.Repository{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestHasIssueLabel(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	assert.True(t, HasIssueLabel(db.DefaultContext, 1, 1)) |  | ||||||
| 	assert.False(t, HasIssueLabel(db.DefaultContext, 1, 2)) |  | ||||||
| 	assert.False(t, HasIssueLabel(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestNewIssueLabel(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) |  | ||||||
| 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) |  | ||||||
|  |  | ||||||
| 	// add new IssueLabel |  | ||||||
| 	prevNumIssues := label.NumIssues |  | ||||||
| 	assert.NoError(t, NewIssueLabel(issue, label, doer)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label.ID}) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Comment{ |  | ||||||
| 		Type:     CommentTypeLabel, |  | ||||||
| 		PosterID: doer.ID, |  | ||||||
| 		IssueID:  issue.ID, |  | ||||||
| 		LabelID:  label.ID, |  | ||||||
| 		Content:  "1", |  | ||||||
| 	}) |  | ||||||
| 	label = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) |  | ||||||
| 	assert.EqualValues(t, prevNumIssues+1, label.NumIssues) |  | ||||||
|  |  | ||||||
| 	// re-add existing IssueLabel |  | ||||||
| 	assert.NoError(t, NewIssueLabel(issue, label, doer)) |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{}, &Label{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestNewIssueLabels(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	label1 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	label2 := unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 5}).(*Issue) |  | ||||||
| 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, NewIssueLabels(issue, []*Label{label1, label2}, doer)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Comment{ |  | ||||||
| 		Type:     CommentTypeLabel, |  | ||||||
| 		PosterID: doer.ID, |  | ||||||
| 		IssueID:  issue.ID, |  | ||||||
| 		LabelID:  label1.ID, |  | ||||||
| 		Content:  "1", |  | ||||||
| 	}) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) |  | ||||||
| 	label1 = unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) |  | ||||||
| 	assert.EqualValues(t, 3, label1.NumIssues) |  | ||||||
| 	assert.EqualValues(t, 1, label1.NumClosedIssues) |  | ||||||
| 	label2 = unittest.AssertExistsAndLoadBean(t, &Label{ID: 2}).(*Label) |  | ||||||
| 	assert.EqualValues(t, 1, label2.NumIssues) |  | ||||||
| 	assert.EqualValues(t, 1, label2.NumClosedIssues) |  | ||||||
|  |  | ||||||
| 	// corner case: test empty slice |  | ||||||
| 	assert.NoError(t, NewIssueLabels(issue, []*Label{}, doer)) |  | ||||||
|  |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{}, &Label{}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDeleteIssueLabel(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	testSuccess := func(labelID, issueID, doerID int64) { |  | ||||||
| 		label := unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label) |  | ||||||
| 		issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue) |  | ||||||
| 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User) |  | ||||||
|  |  | ||||||
| 		expectedNumIssues := label.NumIssues |  | ||||||
| 		expectedNumClosedIssues := label.NumClosedIssues |  | ||||||
| 		if unittest.BeanExists(t, &IssueLabel{IssueID: issueID, LabelID: labelID}) { |  | ||||||
| 			expectedNumIssues-- |  | ||||||
| 			if issue.IsClosed { |  | ||||||
| 				expectedNumClosedIssues-- |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ctx, committer, err := db.TxContext() |  | ||||||
| 		defer committer.Close() |  | ||||||
| 		assert.NoError(t, err) |  | ||||||
| 		assert.NoError(t, DeleteIssueLabel(ctx, issue, label, doer)) |  | ||||||
| 		assert.NoError(t, committer.Commit()) |  | ||||||
|  |  | ||||||
| 		unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID}) |  | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Comment{ |  | ||||||
| 			Type:     CommentTypeLabel, |  | ||||||
| 			PosterID: doerID, |  | ||||||
| 			IssueID:  issueID, |  | ||||||
| 			LabelID:  labelID, |  | ||||||
| 		}, `content=""`) |  | ||||||
| 		label = unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label) |  | ||||||
| 		assert.EqualValues(t, expectedNumIssues, label.NumIssues) |  | ||||||
| 		assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues) |  | ||||||
| 	} |  | ||||||
| 	testSuccess(1, 1, 2) |  | ||||||
| 	testSuccess(2, 5, 2) |  | ||||||
| 	testSuccess(1, 1, 2) // delete non-existent IssueLabel |  | ||||||
|  |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{}, &Label{}) |  | ||||||
| } |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| // 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 models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
| 	user_model "code.gitea.io/gitea/models/user" |  | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestCancelStopwatch(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	user1, err := user_model.GetUserByID(1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	issue1, err := GetIssueByID(1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issue2, err := GetIssueByID(2) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	err = CancelStopwatch(user1, issue1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Stopwatch{UserID: user1.ID, IssueID: issue1.ID}) |  | ||||||
|  |  | ||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID}) |  | ||||||
|  |  | ||||||
| 	assert.Nil(t, CancelStopwatch(user1, issue2)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestStopwatchExists(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	assert.True(t, StopwatchExists(1, 1)) |  | ||||||
| 	assert.False(t, StopwatchExists(1, 2)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestHasUserStopwatch(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	exists, sw, err := HasUserStopwatch(db.DefaultContext, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, exists) |  | ||||||
| 	assert.Equal(t, int64(1), sw.ID) |  | ||||||
|  |  | ||||||
| 	exists, _, err = HasUserStopwatch(db.DefaultContext, 3) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.False(t, exists) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCreateOrStopIssueStopwatch(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	user2, err := user_model.GetUserByID(2) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	user3, err := user_model.GetUserByID(3) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	issue1, err := GetIssueByID(1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issue2, err := GetIssueByID(2) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, CreateOrStopIssueStopwatch(user3, issue1)) |  | ||||||
| 	sw := unittest.AssertExistsAndLoadBean(t, &Stopwatch{UserID: 3, IssueID: 1}).(*Stopwatch) |  | ||||||
| 	assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow()) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, CreateOrStopIssueStopwatch(user2, issue2)) |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Stopwatch{UserID: 2, IssueID: 2}) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &TrackedTime{UserID: 2, IssueID: 2}) |  | ||||||
| } |  | ||||||
| @@ -1,61 +0,0 @@ | |||||||
| // Copyright 2017 The Gogs Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package models |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" |  | ||||||
| 	"code.gitea.io/gitea/models/unittest" |  | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test_newIssueUsers(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
|  |  | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) |  | ||||||
| 	newIssue := &Issue{ |  | ||||||
| 		RepoID:   repo.ID, |  | ||||||
| 		PosterID: 4, |  | ||||||
| 		Index:    6, |  | ||||||
| 		Title:    "newTestIssueTitle", |  | ||||||
| 		Content:  "newTestIssueContent", |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// artificially insert new issue |  | ||||||
| 	unittest.AssertSuccessfulInsert(t, newIssue) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, newIssueUsers(db.DefaultContext, repo, newIssue)) |  | ||||||
|  |  | ||||||
| 	// issue_user table should now have entries for new issue |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID}) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID}) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdateIssueUserByRead(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, UpdateIssueUserByRead(4, issue.ID)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1") |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, UpdateIssueUserByRead(4, issue.ID)) |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1") |  | ||||||
|  |  | ||||||
| 	assert.NoError(t, UpdateIssueUserByRead(unittest.NonexistentID, unittest.NonexistentID)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestUpdateIssueUsersByMentions(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) |  | ||||||
|  |  | ||||||
| 	uids := []int64{2, 5} |  | ||||||
| 	assert.NoError(t, UpdateIssueUsersByMentions(db.DefaultContext, issue.ID, uids)) |  | ||||||
| 	for _, uid := range uids { |  | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &IssueUser{IssueID: issue.ID, UID: uid}, "is_mentioned=1") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 
 | 
 | ||||||
| @@ -18,27 +19,27 @@ func TestUpdateAssignee(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	// Fake issue with assignees | 	// Fake issue with assignees | ||||||
| 	issue, err := GetIssueWithAttrsByID(1) | 	issue, err := issues_model.GetIssueWithAttrsByID(1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	// Assign multiple users | 	// Assign multiple users | ||||||
| 	user2, err := user_model.GetUserByID(2) | 	user2, err := user_model.GetUserByID(2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user2.ID) | 	_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user2.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	user3, err := user_model.GetUserByID(3) | 	user3, err := user_model.GetUserByID(3) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user3.ID) | 	_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user3.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	user1, err := user_model.GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running  UpdateAssignee should unassign him | 	user1, err := user_model.GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running  UpdateAssignee should unassign him | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	_, _, err = ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user1.ID) | 	_, _, err = issues_model.ToggleIssueAssignee(issue, &user_model.User{ID: 1}, user1.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	// Check if he got removed | 	// Check if he got removed | ||||||
| 	isAssigned, err := IsUserAssignedToIssue(db.DefaultContext, issue, user1) | 	isAssigned, err := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.False(t, isAssigned) | 	assert.False(t, isAssigned) | ||||||
| 
 | 
 | ||||||
| @@ -54,12 +55,12 @@ func TestUpdateAssignee(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Check if the user is assigned | 	// Check if the user is assigned | ||||||
| 	isAssigned, err = IsUserAssignedToIssue(db.DefaultContext, issue, user2) | 	isAssigned, err = issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.True(t, isAssigned) | 	assert.True(t, isAssigned) | ||||||
| 
 | 
 | ||||||
| 	// This user should not be assigned | 	// This user should not be assigned | ||||||
| 	isAssigned, err = IsUserAssignedToIssue(db.DefaultContext, issue, &user_model.User{ID: 4}) | 	isAssigned, err = issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, &user_model.User{ID: 4}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.False(t, isAssigned) | 	assert.False(t, isAssigned) | ||||||
| } | } | ||||||
| @@ -70,22 +71,22 @@ func TestMakeIDsFromAPIAssigneesToAdd(t *testing.T) { | |||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	_ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	IDs, err := MakeIDsFromAPIAssigneesToAdd("", []string{""}) | 	IDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{""}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, []int64{}, IDs) | 	assert.Equal(t, []int64{}, IDs) | ||||||
| 
 | 
 | ||||||
| 	_, err = MakeIDsFromAPIAssigneesToAdd("", []string{"none_existing_user"}) | 	_, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"none_existing_user"}) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 
 | 
 | ||||||
| 	IDs, err = MakeIDsFromAPIAssigneesToAdd("user1", []string{"user1"}) | 	IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user1", []string{"user1"}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, []int64{1}, IDs) | 	assert.Equal(t, []int64{1}, IDs) | ||||||
| 
 | 
 | ||||||
| 	IDs, err = MakeIDsFromAPIAssigneesToAdd("user2", []string{""}) | 	IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("user2", []string{""}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, []int64{2}, IDs) | 	assert.Equal(t, []int64{2}, IDs) | ||||||
| 
 | 
 | ||||||
| 	IDs, err = MakeIDsFromAPIAssigneesToAdd("", []string{"user1", "user2"}) | 	IDs, err = issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{"user1", "user2"}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, []int64{1, 2}, IDs) | 	assert.Equal(t, []int64{1, 2}, IDs) | ||||||
| } | } | ||||||
| @@ -4,7 +4,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -16,7 +16,6 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	project_model "code.gitea.io/gitea/models/project" | 	project_model "code.gitea.io/gitea/models/project" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -34,6 +33,22 @@ import ( | |||||||
| 	"xorm.io/xorm" | 	"xorm.io/xorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrCommentNotExist represents a "CommentNotExist" kind of error. | ||||||
|  | type ErrCommentNotExist struct { | ||||||
|  | 	ID      int64 | ||||||
|  | 	IssueID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrCommentNotExist checks if an error is a ErrCommentNotExist. | ||||||
|  | func IsErrCommentNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrCommentNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrCommentNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. | // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. | ||||||
| type CommentType int | type CommentType int | ||||||
| 
 | 
 | ||||||
| @@ -216,8 +231,8 @@ type Comment struct { | |||||||
| 	Project          *project_model.Project `xorm:"-"` | 	Project          *project_model.Project `xorm:"-"` | ||||||
| 	OldMilestoneID   int64 | 	OldMilestoneID   int64 | ||||||
| 	MilestoneID      int64 | 	MilestoneID      int64 | ||||||
| 	OldMilestone     *issues_model.Milestone `xorm:"-"` | 	OldMilestone     *Milestone `xorm:"-"` | ||||||
| 	Milestone        *issues_model.Milestone `xorm:"-"` | 	Milestone        *Milestone `xorm:"-"` | ||||||
| 	TimeID           int64 | 	TimeID           int64 | ||||||
| 	Time             *TrackedTime `xorm:"-"` | 	Time             *TrackedTime `xorm:"-"` | ||||||
| 	AssigneeID       int64 | 	AssigneeID       int64 | ||||||
| @@ -251,7 +266,7 @@ type Comment struct { | |||||||
| 	CommitSHA string `xorm:"VARCHAR(40)"` | 	CommitSHA string `xorm:"VARCHAR(40)"` | ||||||
| 
 | 
 | ||||||
| 	Attachments []*repo_model.Attachment `xorm:"-"` | 	Attachments []*repo_model.Attachment `xorm:"-"` | ||||||
| 	Reactions   issues_model.ReactionList `xorm:"-"` | 	Reactions   ReactionList             `xorm:"-"` | ||||||
| 
 | 
 | ||||||
| 	// For view issue page. | 	// For view issue page. | ||||||
| 	ShowRole RoleDescriptor `xorm:"-"` | 	ShowRole RoleDescriptor `xorm:"-"` | ||||||
| @@ -299,7 +314,7 @@ func (c *Comment) LoadIssueCtx(ctx context.Context) (err error) { | |||||||
| 	if c.Issue != nil { | 	if c.Issue != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	c.Issue, err = getIssueByID(ctx, c.IssueID) | 	c.Issue, err = GetIssueByID(ctx, c.IssueID) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -503,7 +518,7 @@ func (c *Comment) LoadProject() error { | |||||||
| // LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone | // LoadMilestone if comment.Type is CommentTypeMilestone, then load milestone | ||||||
| func (c *Comment) LoadMilestone() error { | func (c *Comment) LoadMilestone() error { | ||||||
| 	if c.OldMilestoneID > 0 { | 	if c.OldMilestoneID > 0 { | ||||||
| 		var oldMilestone issues_model.Milestone | 		var oldMilestone Milestone | ||||||
| 		has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone) | 		has, err := db.GetEngine(db.DefaultContext).ID(c.OldMilestoneID).Get(&oldMilestone) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| @@ -513,7 +528,7 @@ func (c *Comment) LoadMilestone() error { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if c.MilestoneID > 0 { | 	if c.MilestoneID > 0 { | ||||||
| 		var milestone issues_model.Milestone | 		var milestone Milestone | ||||||
| 		has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone) | 		has, err := db.GetEngine(db.DefaultContext).ID(c.MilestoneID).Get(&milestone) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| @@ -625,7 +640,7 @@ func (c *Comment) LoadDepIssueDetails() (err error) { | |||||||
| 	if c.DependentIssueID <= 0 || c.DependentIssue != nil { | 	if c.DependentIssueID <= 0 || c.DependentIssue != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	c.DependentIssue, err = getIssueByID(db.DefaultContext, c.DependentIssueID) | 	c.DependentIssue, err = GetIssueByID(db.DefaultContext, c.DependentIssueID) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -643,7 +658,7 @@ func (c *Comment) loadReactions(ctx context.Context, repo *repo_model.Repository | |||||||
| 	if c.Reactions != nil { | 	if c.Reactions != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	c.Reactions, _, err = issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{ | 	c.Reactions, _, err = FindReactions(ctx, FindReactionsOptions{ | ||||||
| 		IssueID:   c.IssueID, | 		IssueID:   c.IssueID, | ||||||
| 		CommentID: c.ID, | 		CommentID: c.ID, | ||||||
| 	}) | 	}) | ||||||
| @@ -823,7 +838,7 @@ func CreateCommentCtx(ctx context.Context, opts *CreateCommentOptions) (_ *Comme | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = comment.addCrossReferences(ctx, opts.Doer, false); err != nil { | 	if err = comment.AddCrossReferences(ctx, opts.Doer, false); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -1128,7 +1143,7 @@ func UpdateComment(c *Comment, doer *user_model.User) error { | |||||||
| 	if err := c.LoadIssueCtx(ctx); err != nil { | 	if err := c.LoadIssueCtx(ctx); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := c.addCrossReferences(ctx, doer, true); err != nil { | 	if err := c.AddCrossReferences(ctx, doer, true); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if err := committer.Commit(); err != nil { | 	if err := committer.Commit(); err != nil { | ||||||
| @@ -1139,27 +1154,13 @@ func UpdateComment(c *Comment, doer *user_model.User) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteComment deletes the comment | // DeleteComment deletes the comment | ||||||
| func DeleteComment(comment *Comment) error { | func DeleteComment(ctx context.Context, comment *Comment) error { | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 
 |  | ||||||
| 	if err := deleteComment(ctx, comment); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return committer.Commit() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func deleteComment(ctx context.Context, comment *Comment) error { |  | ||||||
| 	e := db.GetEngine(ctx) | 	e := db.GetEngine(ctx) | ||||||
| 	if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil { | 	if _, err := e.ID(comment.ID).NoAutoCondition().Delete(comment); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if _, err := db.DeleteByBean(ctx, &issues_model.ContentHistory{ | 	if _, err := db.DeleteByBean(ctx, &ContentHistory{ | ||||||
| 		CommentID: comment.ID, | 		CommentID: comment.ID, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -1170,7 +1171,11 @@ func deleteComment(ctx context.Context, comment *Comment) error { | |||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if _, err := e.Where("comment_id = ?", comment.ID).Cols("is_deleted").Update(&Action{IsDeleted: true}); err != nil { | 	if _, err := e.Table("action"). | ||||||
|  | 		Where("comment_id = ?", comment.ID). | ||||||
|  | 		Update(map[string]interface{}{ | ||||||
|  | 			"is_deleted": true, | ||||||
|  | 		}); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -1178,7 +1183,7 @@ func deleteComment(ctx context.Context, comment *Comment) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return issues_model.DeleteReaction(ctx, &issues_model.ReactionOptions{CommentID: comment.ID}) | 	return DeleteReaction(ctx, &ReactionOptions{CommentID: comment.ID}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS | // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS | ||||||
| @@ -1500,3 +1505,42 @@ func (c *Comment) GetExternalName() string { return c.OriginalAuthor } | |||||||
| 
 | 
 | ||||||
| // GetExternalID ExternalUserRemappable interface | // GetExternalID ExternalUserRemappable interface | ||||||
| func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID } | func (c *Comment) GetExternalID() int64 { return c.OriginalAuthorID } | ||||||
|  | 
 | ||||||
|  | // CountCommentTypeLabelWithEmptyLabel count label comments with empty label | ||||||
|  | func CountCommentTypeLabelWithEmptyLabel() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Count(new(Comment)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FixCommentTypeLabelWithEmptyLabel count label comments with empty label | ||||||
|  | func FixCommentTypeLabelWithEmptyLabel() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": CommentTypeLabel, "label_id": 0}).Delete(new(Comment)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CountCommentTypeLabelWithOutsideLabels count label comments with outside label | ||||||
|  | func CountCommentTypeLabelWithOutsideLabels() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).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"). | ||||||
|  | 		Join("inner", "label", "label.id = comment.label_id"). | ||||||
|  | 		Join("inner", "issue", "issue.id = comment.issue_id "). | ||||||
|  | 		Join("inner", "repository", "issue.repo_id = repository.id"). | ||||||
|  | 		Count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FixCommentTypeLabelWithOutsideLabels count label comments with outside label | ||||||
|  | func FixCommentTypeLabelWithOutsideLabels() (int64, error) { | ||||||
|  | 	res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM comment WHERE comment.id IN ( | ||||||
|  | 		SELECT il_too.id FROM ( | ||||||
|  | 			SELECT com.id | ||||||
|  | 				FROM comment AS com | ||||||
|  | 					INNER JOIN label ON com.label_id = label.id | ||||||
|  | 					INNER JOIN issue on issue.id = com.issue_id | ||||||
|  | 					INNER JOIN repository ON issue.repo_id = repository.id | ||||||
|  | 				WHERE | ||||||
|  | 					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) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res.RowsAffected() | ||||||
|  | } | ||||||
| @@ -2,13 +2,12 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/container" | 	"code.gitea.io/gitea/modules/container" | ||||||
| @@ -36,7 +35,7 @@ func (comments CommentList) loadPosters(ctx context.Context) error { | |||||||
| 	posterMaps := make(map[int64]*user_model.User, len(posterIDs)) | 	posterMaps := make(map[int64]*user_model.User, len(posterIDs)) | ||||||
| 	left := len(posterIDs) | 	left := len(posterIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -80,7 +79,7 @@ func (comments CommentList) getLabelIDs() []int64 { | |||||||
| 	return container.KeysInt64(ids) | 	return container.KeysInt64(ids) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (comments CommentList) loadLabels(ctx context.Context) error { | func (comments CommentList) loadLabels(ctx context.Context) error { //nolint | ||||||
| 	if len(comments) == 0 { | 	if len(comments) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| @@ -89,7 +88,7 @@ func (comments CommentList) loadLabels(ctx context.Context) error { | |||||||
| 	commentLabels := make(map[int64]*Label, len(labelIDs)) | 	commentLabels := make(map[int64]*Label, len(labelIDs)) | ||||||
| 	left := len(labelIDs) | 	left := len(labelIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -140,10 +139,10 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) | 	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) | ||||||
| 	left := len(milestoneIDs) | 	left := len(milestoneIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -183,10 +182,10 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) | 	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) | ||||||
| 	left := len(milestoneIDs) | 	left := len(milestoneIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -225,7 +224,7 @@ func (comments CommentList) loadAssignees(ctx context.Context) error { | |||||||
| 	assignees := make(map[int64]*user_model.User, len(assigneeIDs)) | 	assignees := make(map[int64]*user_model.User, len(assigneeIDs)) | ||||||
| 	left := len(assigneeIDs) | 	left := len(assigneeIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -299,7 +298,7 @@ func (comments CommentList) loadIssues(ctx context.Context) error { | |||||||
| 	issues := make(map[int64]*Issue, len(issueIDs)) | 	issues := make(map[int64]*Issue, len(issueIDs)) | ||||||
| 	left := len(issueIDs) | 	left := len(issueIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -357,7 +356,7 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { | |||||||
| 	issues := make(map[int64]*Issue, len(issueIDs)) | 	issues := make(map[int64]*Issue, len(issueIDs)) | ||||||
| 	left := len(issueIDs) | 	left := len(issueIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -406,7 +405,7 @@ func (comments CommentList) loadAttachments(ctx context.Context) (err error) { | |||||||
| 	commentsIDs := comments.getCommentIDs() | 	commentsIDs := comments.getCommentIDs() | ||||||
| 	left := len(commentsIDs) | 	left := len(commentsIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -449,7 +448,7 @@ func (comments CommentList) getReviewIDs() []int64 { | |||||||
| 	return container.KeysInt64(ids) | 	return container.KeysInt64(ids) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (comments CommentList) loadReviews(ctx context.Context) error { | func (comments CommentList) loadReviews(ctx context.Context) error { //nolint | ||||||
| 	if len(comments) == 0 { | 	if len(comments) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| @@ -458,7 +457,7 @@ func (comments CommentList) loadReviews(ctx context.Context) error { | |||||||
| 	reviews := make(map[int64]*Review, len(reviewIDs)) | 	reviews := make(map[int64]*Review, len(reviewIDs)) | ||||||
| 	left := len(reviewIDs) | 	left := len(reviewIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -2,13 +2,14 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -19,13 +20,13 @@ import ( | |||||||
| func TestCreateComment(t *testing.T) { | func TestCreateComment(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	now := time.Now().Unix() | 	now := time.Now().Unix() | ||||||
| 	comment, err := CreateComment(&CreateCommentOptions{ | 	comment, err := issues_model.CreateComment(&issues_model.CreateCommentOptions{ | ||||||
| 		Type:    CommentTypeComment, | 		Type:    issues_model.CommentTypeComment, | ||||||
| 		Doer:    doer, | 		Doer:    doer, | ||||||
| 		Repo:    repo, | 		Repo:    repo, | ||||||
| 		Issue:   issue, | 		Issue:   issue, | ||||||
| @@ -34,23 +35,23 @@ func TestCreateComment(t *testing.T) { | |||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	then := time.Now().Unix() | 	then := time.Now().Unix() | ||||||
| 
 | 
 | ||||||
| 	assert.EqualValues(t, CommentTypeComment, comment.Type) | 	assert.EqualValues(t, issues_model.CommentTypeComment, comment.Type) | ||||||
| 	assert.EqualValues(t, "Hello", comment.Content) | 	assert.EqualValues(t, "Hello", comment.Content) | ||||||
| 	assert.EqualValues(t, issue.ID, comment.IssueID) | 	assert.EqualValues(t, issue.ID, comment.IssueID) | ||||||
| 	assert.EqualValues(t, doer.ID, comment.PosterID) | 	assert.EqualValues(t, doer.ID, comment.PosterID) | ||||||
| 	unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix)) | 	unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix)) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB | 	unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB | ||||||
| 
 | 
 | ||||||
| 	updatedIssue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue) | 	updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue) | ||||||
| 	unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) | 	unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFetchCodeComments(t *testing.T) { | func TestFetchCodeComments(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
| 	res, err := FetchCodeComments(db.DefaultContext, issue, user) | 	res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Contains(t, res, "README.md") | 	assert.Contains(t, res, "README.md") | ||||||
| 	assert.Contains(t, res["README.md"], int64(4)) | 	assert.Contains(t, res["README.md"], int64(4)) | ||||||
| @@ -58,7 +59,7 @@ func TestFetchCodeComments(t *testing.T) { | |||||||
| 	assert.Equal(t, int64(4), res["README.md"][4][0].ID) | 	assert.Equal(t, int64(4), res["README.md"][4][0].ID) | ||||||
| 
 | 
 | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	res, err = FetchCodeComments(db.DefaultContext, issue, user2) | 	res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, res, 1) | 	assert.Len(t, res, 1) | ||||||
| } | } | ||||||
| @@ -53,13 +53,13 @@ func SaveIssueContentHistory(ctx context.Context, posterID, issueID, commentID i | |||||||
| 	} | 	} | ||||||
| 	// We only keep at most 20 history revisions now. It is enough in most cases. | 	// We only keep at most 20 history revisions now. It is enough in most cases. | ||||||
| 	// If there is a special requirement to keep more, we can consider introducing a new setting option then, but not now. | 	// If there is a special requirement to keep more, we can consider introducing a new setting option then, but not now. | ||||||
| 	keepLimitedContentHistory(ctx, issueID, commentID, 20) | 	KeepLimitedContentHistory(ctx, issueID, commentID, 20) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // keepLimitedContentHistory keeps at most `limit` history revisions, it will hard delete out-dated revisions, sorting by revision interval | // KeepLimitedContentHistory keeps at most `limit` history revisions, it will hard delete out-dated revisions, sorting by revision interval | ||||||
| // we can ignore all errors in this function, so we just log them | // we can ignore all errors in this function, so we just log them | ||||||
| func keepLimitedContentHistory(ctx context.Context, issueID, commentID int64, limit int) { | func KeepLimitedContentHistory(ctx context.Context, issueID, commentID int64, limit int) { | ||||||
| 	type IDEditTime struct { | 	type IDEditTime struct { | ||||||
| 		ID         int64 | 		ID         int64 | ||||||
| 		EditedUnix timeutil.TimeStamp | 		EditedUnix timeutil.TimeStamp | ||||||
|   | |||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
|  |  | ||||||
| package issues | package issues_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  |  | ||||||
| @@ -20,20 +21,20 @@ func TestContentHistory(t *testing.T) { | |||||||
| 	dbCtx := db.DefaultContext | 	dbCtx := db.DefaultContext | ||||||
| 	timeStampNow := timeutil.TimeStampNow() | 	timeStampNow := timeutil.TimeStampNow() | ||||||
|  |  | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow, "i-a", true) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow, "i-a", true) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(2), "i-b", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(2), "i-b", false) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(7), "i-c", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 0, timeStampNow.Add(7), "i-c", false) | ||||||
|  |  | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow, "c-a", true) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow, "c-a", true) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(5), "c-b", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(5), "c-b", false) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(20), "c-c", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(20), "c-c", false) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(50), "c-d", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(50), "c-d", false) | ||||||
| 	_ = SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(51), "c-e", false) | 	_ = issues_model.SaveIssueContentHistory(dbCtx, 1, 10, 100, timeStampNow.Add(51), "c-e", false) | ||||||
|  |  | ||||||
| 	h1, _ := GetIssueContentHistoryByID(dbCtx, 1) | 	h1, _ := issues_model.GetIssueContentHistoryByID(dbCtx, 1) | ||||||
| 	assert.EqualValues(t, 1, h1.ID) | 	assert.EqualValues(t, 1, h1.ID) | ||||||
|  |  | ||||||
| 	m, _ := QueryIssueContentHistoryEditedCountMap(dbCtx, 10) | 	m, _ := issues_model.QueryIssueContentHistoryEditedCountMap(dbCtx, 10) | ||||||
| 	assert.Equal(t, 3, m[0]) | 	assert.Equal(t, 3, m[0]) | ||||||
| 	assert.Equal(t, 5, m[100]) | 	assert.Equal(t, 5, m[100]) | ||||||
|  |  | ||||||
| @@ -48,31 +49,31 @@ func TestContentHistory(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	_ = db.GetEngine(dbCtx).Sync2(&User{}) | 	_ = db.GetEngine(dbCtx).Sync2(&User{}) | ||||||
|  |  | ||||||
| 	list1, _ := FetchIssueContentHistoryList(dbCtx, 10, 0) | 	list1, _ := issues_model.FetchIssueContentHistoryList(dbCtx, 10, 0) | ||||||
| 	assert.Len(t, list1, 3) | 	assert.Len(t, list1, 3) | ||||||
| 	list2, _ := FetchIssueContentHistoryList(dbCtx, 10, 100) | 	list2, _ := issues_model.FetchIssueContentHistoryList(dbCtx, 10, 100) | ||||||
| 	assert.Len(t, list2, 5) | 	assert.Len(t, list2, 5) | ||||||
|  |  | ||||||
| 	hasHistory1, _ := HasIssueContentHistory(dbCtx, 10, 0) | 	hasHistory1, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 0) | ||||||
| 	assert.True(t, hasHistory1) | 	assert.True(t, hasHistory1) | ||||||
| 	hasHistory2, _ := HasIssueContentHistory(dbCtx, 10, 1) | 	hasHistory2, _ := issues_model.HasIssueContentHistory(dbCtx, 10, 1) | ||||||
| 	assert.False(t, hasHistory2) | 	assert.False(t, hasHistory2) | ||||||
|  |  | ||||||
| 	h6, h6Prev, _ := GetIssueContentHistoryAndPrev(dbCtx, 6) | 	h6, h6Prev, _ := issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6) | ||||||
| 	assert.EqualValues(t, 6, h6.ID) | 	assert.EqualValues(t, 6, h6.ID) | ||||||
| 	assert.EqualValues(t, 5, h6Prev.ID) | 	assert.EqualValues(t, 5, h6Prev.ID) | ||||||
|  |  | ||||||
| 	// soft-delete | 	// soft-delete | ||||||
| 	_ = SoftDeleteIssueContentHistory(dbCtx, 5) | 	_ = issues_model.SoftDeleteIssueContentHistory(dbCtx, 5) | ||||||
| 	h6, h6Prev, _ = GetIssueContentHistoryAndPrev(dbCtx, 6) | 	h6, h6Prev, _ = issues_model.GetIssueContentHistoryAndPrev(dbCtx, 6) | ||||||
| 	assert.EqualValues(t, 6, h6.ID) | 	assert.EqualValues(t, 6, h6.ID) | ||||||
| 	assert.EqualValues(t, 4, h6Prev.ID) | 	assert.EqualValues(t, 4, h6Prev.ID) | ||||||
|  |  | ||||||
| 	// only keep 3 history revisions for comment_id=100, the first and the last should never be deleted | 	// only keep 3 history revisions for comment_id=100, the first and the last should never be deleted | ||||||
| 	keepLimitedContentHistory(dbCtx, 10, 100, 3) | 	issues_model.KeepLimitedContentHistory(dbCtx, 10, 100, 3) | ||||||
| 	list1, _ = FetchIssueContentHistoryList(dbCtx, 10, 0) | 	list1, _ = issues_model.FetchIssueContentHistoryList(dbCtx, 10, 0) | ||||||
| 	assert.Len(t, list1, 3) | 	assert.Len(t, list1, 3) | ||||||
| 	list2, _ = FetchIssueContentHistoryList(dbCtx, 10, 100) | 	list2, _ = issues_model.FetchIssueContentHistoryList(dbCtx, 10, 100) | ||||||
| 	assert.Len(t, list2, 3) | 	assert.Len(t, list2, 3) | ||||||
| 	assert.EqualValues(t, 8, list2[0].HistoryID) | 	assert.EqualValues(t, 8, list2[0].HistoryID) | ||||||
| 	assert.EqualValues(t, 7, list2[1].HistoryID) | 	assert.EqualValues(t, 7, list2[1].HistoryID) | ||||||
|   | |||||||
| @@ -2,16 +2,95 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrDependencyExists represents a "DependencyAlreadyExists" kind of error. | ||||||
|  | type ErrDependencyExists struct { | ||||||
|  | 	IssueID      int64 | ||||||
|  | 	DependencyID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrDependencyExists checks if an error is a ErrDependencyExists. | ||||||
|  | func IsErrDependencyExists(err error) bool { | ||||||
|  | 	_, ok := err.(ErrDependencyExists) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrDependencyExists) Error() string { | ||||||
|  | 	return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error. | ||||||
|  | type ErrDependencyNotExists struct { | ||||||
|  | 	IssueID      int64 | ||||||
|  | 	DependencyID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrDependencyNotExists checks if an error is a ErrDependencyExists. | ||||||
|  | func IsErrDependencyNotExists(err error) bool { | ||||||
|  | 	_, ok := err.(ErrDependencyNotExists) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrDependencyNotExists) Error() string { | ||||||
|  | 	return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrCircularDependency represents a "DependencyCircular" kind of error. | ||||||
|  | type ErrCircularDependency struct { | ||||||
|  | 	IssueID      int64 | ||||||
|  | 	DependencyID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrCircularDependency checks if an error is a ErrCircularDependency. | ||||||
|  | func IsErrCircularDependency(err error) bool { | ||||||
|  | 	_, ok := err.(ErrCircularDependency) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrCircularDependency) Error() string { | ||||||
|  | 	return fmt.Sprintf("circular dependencies exists (two issues blocking each other) [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrDependenciesLeft represents an error where the issue you're trying to close still has dependencies left. | ||||||
|  | type ErrDependenciesLeft struct { | ||||||
|  | 	IssueID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrDependenciesLeft checks if an error is a ErrDependenciesLeft. | ||||||
|  | func IsErrDependenciesLeft(err error) bool { | ||||||
|  | 	_, ok := err.(ErrDependenciesLeft) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrDependenciesLeft) Error() string { | ||||||
|  | 	return fmt.Sprintf("issue has open dependencies [issue id: %d]", err.IssueID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrUnknownDependencyType represents an error where an unknown dependency type was passed | ||||||
|  | type ErrUnknownDependencyType struct { | ||||||
|  | 	Type DependencyType | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType | ||||||
|  | func IsErrUnknownDependencyType(err error) bool { | ||||||
|  | 	_, ok := err.(ErrUnknownDependencyType) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrUnknownDependencyType) Error() string { | ||||||
|  | 	return fmt.Sprintf("unknown dependency type [type: %d]", err.Type) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // IssueDependency represents an issue dependency | // IssueDependency represents an issue dependency | ||||||
| type IssueDependency struct { | type IssueDependency struct { | ||||||
| 	ID           int64              `xorm:"pk autoincr"` | 	ID           int64              `xorm:"pk autoincr"` | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 
 | 
 | ||||||
| @@ -21,42 +22,42 @@ func TestCreateIssueDependency(t *testing.T) { | |||||||
| 	user1, err := user_model.GetUserByID(1) | 	user1, err := user_model.GetUserByID(1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	issue1, err := GetIssueByID(1) | 	issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	issue2, err := GetIssueByID(2) | 	issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	// Create a dependency and check if it was successful | 	// Create a dependency and check if it was successful | ||||||
| 	err = CreateIssueDependency(user1, issue1, issue2) | 	err = issues_model.CreateIssueDependency(user1, issue1, issue2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	// Do it again to see if it will check if the dependency already exists | 	// Do it again to see if it will check if the dependency already exists | ||||||
| 	err = CreateIssueDependency(user1, issue1, issue2) | 	err = issues_model.CreateIssueDependency(user1, issue1, issue2) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrDependencyExists(err)) | 	assert.True(t, issues_model.IsErrDependencyExists(err)) | ||||||
| 
 | 
 | ||||||
| 	// Check for circular dependencies | 	// Check for circular dependencies | ||||||
| 	err = CreateIssueDependency(user1, issue2, issue1) | 	err = issues_model.CreateIssueDependency(user1, issue2, issue1) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrCircularDependency(err)) | 	assert.True(t, issues_model.IsErrCircularDependency(err)) | ||||||
| 
 | 
 | ||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddDependency, PosterID: user1.ID, IssueID: issue1.ID}) | 	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddDependency, PosterID: user1.ID, IssueID: issue1.ID}) | ||||||
| 
 | 
 | ||||||
| 	// Check if dependencies left is correct | 	// Check if dependencies left is correct | ||||||
| 	left, err := IssueNoDependenciesLeft(db.DefaultContext, issue1) | 	left, err := issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.False(t, left) | 	assert.False(t, left) | ||||||
| 
 | 
 | ||||||
| 	// Close #2 and check again | 	// Close #2 and check again | ||||||
| 	_, err = ChangeIssueStatus(db.DefaultContext, issue2, user1, true) | 	_, err = issues_model.ChangeIssueStatus(db.DefaultContext, issue2, user1, true) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	left, err = IssueNoDependenciesLeft(db.DefaultContext, issue1) | 	left, err = issues_model.IssueNoDependenciesLeft(db.DefaultContext, issue1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.True(t, left) | 	assert.True(t, left) | ||||||
| 
 | 
 | ||||||
| 	// Test removing the dependency | 	// Test removing the dependency | ||||||
| 	err = RemoveIssueDependency(user1, issue1, issue2, DependencyTypeBlockedBy) | 	err = issues_model.RemoveIssueDependency(user1, issue1, issue2, issues_model.DependencyTypeBlockedBy) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| } | } | ||||||
| @@ -3,7 +3,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -16,7 +16,6 @@ import ( | |||||||
| 	admin_model "code.gitea.io/gitea/models/admin" | 	admin_model "code.gitea.io/gitea/models/admin" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	"code.gitea.io/gitea/models/foreignreference" | 	"code.gitea.io/gitea/models/foreignreference" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| @@ -29,7 +28,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	"code.gitea.io/gitea/modules/references" | 	"code.gitea.io/gitea/modules/references" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	"code.gitea.io/gitea/modules/storage" |  | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| @@ -38,6 +36,71 @@ import ( | |||||||
| 	"xorm.io/xorm" | 	"xorm.io/xorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrIssueNotExist represents a "IssueNotExist" kind of error. | ||||||
|  | type ErrIssueNotExist struct { | ||||||
|  | 	ID     int64 | ||||||
|  | 	RepoID int64 | ||||||
|  | 	Index  int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrIssueNotExist checks if an error is a ErrIssueNotExist. | ||||||
|  | func IsErrIssueNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrIssueNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrIssueNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrIssueIsClosed represents a "IssueIsClosed" kind of error. | ||||||
|  | type ErrIssueIsClosed struct { | ||||||
|  | 	ID     int64 | ||||||
|  | 	RepoID int64 | ||||||
|  | 	Index  int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrIssueIsClosed checks if an error is a ErrIssueNotExist. | ||||||
|  | func IsErrIssueIsClosed(err error) bool { | ||||||
|  | 	_, ok := err.(ErrIssueIsClosed) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrIssueIsClosed) Error() string { | ||||||
|  | 	return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrNewIssueInsert is used when the INSERT statement in newIssue fails | ||||||
|  | type ErrNewIssueInsert struct { | ||||||
|  | 	OriginalError error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrNewIssueInsert checks if an error is a ErrNewIssueInsert. | ||||||
|  | func IsErrNewIssueInsert(err error) bool { | ||||||
|  | 	_, ok := err.(ErrNewIssueInsert) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrNewIssueInsert) Error() string { | ||||||
|  | 	return err.OriginalError.Error() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrIssueWasClosed is used when close a closed issue | ||||||
|  | type ErrIssueWasClosed struct { | ||||||
|  | 	ID    int64 | ||||||
|  | 	Index int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed. | ||||||
|  | func IsErrIssueWasClosed(err error) bool { | ||||||
|  | 	_, ok := err.(ErrIssueWasClosed) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrIssueWasClosed) Error() string { | ||||||
|  | 	return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Issue represents an issue or pull request of repository. | // Issue represents an issue or pull request of repository. | ||||||
| type Issue struct { | type Issue struct { | ||||||
| 	ID               int64                  `xorm:"pk autoincr"` | 	ID               int64                  `xorm:"pk autoincr"` | ||||||
| @@ -53,7 +116,7 @@ type Issue struct { | |||||||
| 	RenderedContent  string                 `xorm:"-"` | 	RenderedContent  string                 `xorm:"-"` | ||||||
| 	Labels           []*Label               `xorm:"-"` | 	Labels           []*Label               `xorm:"-"` | ||||||
| 	MilestoneID      int64                  `xorm:"INDEX"` | 	MilestoneID      int64                  `xorm:"INDEX"` | ||||||
| 	Milestone        *issues_model.Milestone `xorm:"-"` | 	Milestone        *Milestone             `xorm:"-"` | ||||||
| 	Project          *project_model.Project `xorm:"-"` | 	Project          *project_model.Project `xorm:"-"` | ||||||
| 	Priority         int | 	Priority         int | ||||||
| 	AssigneeID       int64            `xorm:"-"` | 	AssigneeID       int64            `xorm:"-"` | ||||||
| @@ -73,7 +136,7 @@ type Issue struct { | |||||||
| 
 | 
 | ||||||
| 	Attachments      []*repo_model.Attachment           `xorm:"-"` | 	Attachments      []*repo_model.Attachment           `xorm:"-"` | ||||||
| 	Comments         []*Comment                         `xorm:"-"` | 	Comments         []*Comment                         `xorm:"-"` | ||||||
| 	Reactions        issues_model.ReactionList          `xorm:"-"` | 	Reactions        ReactionList                       `xorm:"-"` | ||||||
| 	TotalTrackedTime int64                              `xorm:"-"` | 	TotalTrackedTime int64                              `xorm:"-"` | ||||||
| 	Assignees        []*user_model.User                 `xorm:"-"` | 	Assignees        []*user_model.User                 `xorm:"-"` | ||||||
| 	ForeignReference *foreignreference.ForeignReference `xorm:"-"` | 	ForeignReference *foreignreference.ForeignReference `xorm:"-"` | ||||||
| @@ -107,7 +170,8 @@ func init() { | |||||||
| 	db.RegisterModel(new(IssueIndex)) | 	db.RegisterModel(new(IssueIndex)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (issue *Issue) loadTotalTimes(ctx context.Context) (err error) { | // LoadTotalTimes load total tracked time | ||||||
|  | func (issue *Issue) LoadTotalTimes(ctx context.Context) (err error) { | ||||||
| 	opts := FindTrackedTimesOptions{IssueID: issue.ID} | 	opts := FindTrackedTimesOptions{IssueID: issue.ID} | ||||||
| 	issue.TotalTrackedTime, err = opts.toSession(db.GetEngine(ctx)).SumInt(&TrackedTime{}, "time") | 	issue.TotalTrackedTime, err = opts.toSession(db.GetEngine(ctx)).SumInt(&TrackedTime{}, "time") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -237,7 +301,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) { | |||||||
| 	if issue.Reactions != nil { | 	if issue.Reactions != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	reactions, _, err := issues_model.FindReactions(ctx, issues_model.FindReactionsOptions{ | 	reactions, _, err := FindReactions(ctx, FindReactionsOptions{ | ||||||
| 		IssueID: issue.ID, | 		IssueID: issue.ID, | ||||||
| 	}) | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -247,7 +311,7 @@ func (issue *Issue) loadReactions(ctx context.Context) (err error) { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	// Load reaction user data | 	// Load reaction user data | ||||||
| 	if _, err := issues_model.ReactionList(reactions).LoadUsers(ctx, issue.Repo); err != nil { | 	if _, err := ReactionList(reactions).LoadUsers(ctx, issue.Repo); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -292,15 +356,16 @@ func (issue *Issue) loadForeignReference(ctx context.Context) (err error) { | |||||||
| 
 | 
 | ||||||
| func (issue *Issue) loadMilestone(ctx context.Context) (err error) { | func (issue *Issue) loadMilestone(ctx context.Context) (err error) { | ||||||
| 	if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 { | 	if (issue.Milestone == nil || issue.Milestone.ID != issue.MilestoneID) && issue.MilestoneID > 0 { | ||||||
| 		issue.Milestone, err = issues_model.GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) | 		issue.Milestone, err = GetMilestoneByRepoID(ctx, issue.RepoID, issue.MilestoneID) | ||||||
| 		if err != nil && !issues_model.IsErrMilestoneNotExist(err) { | 		if err != nil && !IsErrMilestoneNotExist(err) { | ||||||
| 			return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err) | 			return fmt.Errorf("getMilestoneByRepoID [repo_id: %d, milestone_id: %d]: %v", issue.RepoID, issue.MilestoneID, err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (issue *Issue) loadAttributes(ctx context.Context) (err error) { | // LoadAttributes loads the attribute of this issue. | ||||||
|  | func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { | ||||||
| 	if err = issue.LoadRepo(ctx); err != nil { | 	if err = issue.LoadRepo(ctx); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -345,7 +410,7 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if issue.isTimetrackerEnabled(ctx) { | 	if issue.isTimetrackerEnabled(ctx) { | ||||||
| 		if err = issue.loadTotalTimes(ctx); err != nil { | 		if err = issue.LoadTotalTimes(ctx); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -357,11 +422,6 @@ func (issue *Issue) loadAttributes(ctx context.Context) (err error) { | |||||||
| 	return issue.loadReactions(ctx) | 	return issue.loadReactions(ctx) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // LoadAttributes loads the attribute of this issue. |  | ||||||
| func (issue *Issue) LoadAttributes() error { |  | ||||||
| 	return issue.loadAttributes(db.DefaultContext) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // LoadMilestone load milestone of this issue. | // LoadMilestone load milestone of this issue. | ||||||
| func (issue *Issue) LoadMilestone() error { | func (issue *Issue) LoadMilestone() error { | ||||||
| 	return issue.loadMilestone(db.DefaultContext) | 	return issue.loadMilestone(db.DefaultContext) | ||||||
| @@ -590,15 +650,6 @@ func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (e | |||||||
| 	return committer.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ReadBy sets issue to be read by given user. |  | ||||||
| func (issue *Issue) ReadBy(ctx context.Context, userID int64) error { |  | ||||||
| 	if err := UpdateIssueUserByRead(userID, issue.ID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return setIssueNotificationStatusReadIfUnread(ctx, userID, issue.ID) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // UpdateIssueCols updates cols of issue | // UpdateIssueCols updates cols of issue | ||||||
| func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { | func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { | ||||||
| 	if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil { | 	if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil { | ||||||
| @@ -609,7 +660,7 @@ func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { | |||||||
| 
 | 
 | ||||||
| func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { | func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { | ||||||
| 	// Reload the issue | 	// Reload the issue | ||||||
| 	currentIssue, err := getIssueByID(ctx, issue.ID) | 	currentIssue, err := GetIssueByID(ctx, issue.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -666,7 +717,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use | |||||||
| 
 | 
 | ||||||
| 	// Update issue count of milestone | 	// Update issue count of milestone | ||||||
| 	if issue.MilestoneID > 0 { | 	if issue.MilestoneID > 0 { | ||||||
| 		if err := issues_model.UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { | 		if err := UpdateMilestoneCounters(ctx, issue.MilestoneID); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -730,7 +781,7 @@ func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err | |||||||
| 	if _, err = CreateCommentCtx(ctx, opts); err != nil { | 	if _, err = CreateCommentCtx(ctx, opts); err != nil { | ||||||
| 		return fmt.Errorf("createComment: %v", err) | 		return fmt.Errorf("createComment: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err = issue.addCrossReferences(ctx, doer, true); err != nil { | 	if err = issue.AddCrossReferences(ctx, doer, true); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -772,7 +823,7 @@ func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err err | |||||||
| 
 | 
 | ||||||
| // AddDeletePRBranchComment adds delete branch comment for pull request issue | // AddDeletePRBranchComment adds delete branch comment for pull request issue | ||||||
| func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issueID int64, branchName string) error { | func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issueID int64, branchName string) error { | ||||||
| 	issue, err := getIssueByID(ctx, issueID) | 	issue, err := GetIssueByID(ctx, issueID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -815,12 +866,12 @@ func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (er | |||||||
| 	} | 	} | ||||||
| 	defer committer.Close() | 	defer committer.Close() | ||||||
| 
 | 
 | ||||||
| 	hasContentHistory, err := issues_model.HasIssueContentHistory(ctx, issue.ID, 0) | 	hasContentHistory, err := HasIssueContentHistory(ctx, issue.ID, 0) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("HasIssueContentHistory: %v", err) | 		return fmt.Errorf("HasIssueContentHistory: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if !hasContentHistory { | 	if !hasContentHistory { | ||||||
| 		if err = issues_model.SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, | 		if err = SaveIssueContentHistory(ctx, issue.PosterID, issue.ID, 0, | ||||||
| 			issue.CreatedUnix, issue.Content, true); err != nil { | 			issue.CreatedUnix, issue.Content, true); err != nil { | ||||||
| 			return fmt.Errorf("SaveIssueContentHistory: %v", err) | 			return fmt.Errorf("SaveIssueContentHistory: %v", err) | ||||||
| 		} | 		} | ||||||
| @@ -832,12 +883,12 @@ func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (er | |||||||
| 		return fmt.Errorf("UpdateIssueCols: %v", err) | 		return fmt.Errorf("UpdateIssueCols: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = issues_model.SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, | 	if err = SaveIssueContentHistory(ctx, doer.ID, issue.ID, 0, | ||||||
| 		timeutil.TimeStampNow(), issue.Content, false); err != nil { | 		timeutil.TimeStampNow(), issue.Content, false); err != nil { | ||||||
| 		return fmt.Errorf("SaveIssueContentHistory: %v", err) | 		return fmt.Errorf("SaveIssueContentHistory: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = issue.addCrossReferences(ctx, doer, true); err != nil { | 	if err = issue.AddCrossReferences(ctx, doer, true); err != nil { | ||||||
| 		return fmt.Errorf("addCrossReferences: %v", err) | 		return fmt.Errorf("addCrossReferences: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -907,13 +958,14 @@ type NewIssueOptions struct { | |||||||
| 	IsPull      bool | 	IsPull      bool | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) (err error) { | // NewIssueWithIndex creates issue with given index | ||||||
|  | func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssueOptions) (err error) { | ||||||
| 	e := db.GetEngine(ctx) | 	e := db.GetEngine(ctx) | ||||||
| 	opts.Issue.Title = strings.TrimSpace(opts.Issue.Title) | 	opts.Issue.Title = strings.TrimSpace(opts.Issue.Title) | ||||||
| 
 | 
 | ||||||
| 	if opts.Issue.MilestoneID > 0 { | 	if opts.Issue.MilestoneID > 0 { | ||||||
| 		milestone, err := issues_model.GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID) | 		milestone, err := GetMilestoneByRepoID(ctx, opts.Issue.RepoID, opts.Issue.MilestoneID) | ||||||
| 		if err != nil && !issues_model.IsErrMilestoneNotExist(err) { | 		if err != nil && !IsErrMilestoneNotExist(err) { | ||||||
| 			return fmt.Errorf("getMilestoneByID: %v", err) | 			return fmt.Errorf("getMilestoneByID: %v", err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @@ -937,7 +989,7 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if opts.Issue.MilestoneID > 0 { | 	if opts.Issue.MilestoneID > 0 { | ||||||
| 		if err := issues_model.UpdateMilestoneCounters(ctx, opts.Issue.MilestoneID); err != nil { | 		if err := UpdateMilestoneCounters(ctx, opts.Issue.MilestoneID); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| @@ -987,7 +1039,7 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err = newIssueUsers(ctx, opts.Repo, opts.Issue); err != nil { | 	if err = NewIssueUsers(ctx, opts.Repo, opts.Issue); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -1004,36 +1056,11 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err = opts.Issue.loadAttributes(ctx); err != nil { | 	if err = opts.Issue.LoadAttributes(ctx); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return opts.Issue.addCrossReferences(ctx, doer, false) | 	return opts.Issue.AddCrossReferences(ctx, doer, false) | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // RecalculateIssueIndexForRepo create issue_index for repo if not exist and |  | ||||||
| // update it based on highest index of existing issues assigned to a repo |  | ||||||
| func RecalculateIssueIndexForRepo(repoID int64) error { |  | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 
 |  | ||||||
| 	if err := db.UpsertResourceIndex(db.GetEngine(ctx), "issue_index", repoID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	var max int64 |  | ||||||
| 	if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return committer.Commit() |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewIssue creates new issue with labels for repository. | // NewIssue creates new issue with labels for repository. | ||||||
| @@ -1051,13 +1078,13 @@ func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids | |||||||
| 	} | 	} | ||||||
| 	defer committer.Close() | 	defer committer.Close() | ||||||
| 
 | 
 | ||||||
| 	if err = newIssue(ctx, issue.Poster, NewIssueOptions{ | 	if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ | ||||||
| 		Repo:        repo, | 		Repo:        repo, | ||||||
| 		Issue:       issue, | 		Issue:       issue, | ||||||
| 		LabelIDs:    labelIDs, | 		LabelIDs:    labelIDs, | ||||||
| 		Attachments: uuids, | 		Attachments: uuids, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { | 		if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		return fmt.Errorf("newIssue: %v", err) | 		return fmt.Errorf("newIssue: %v", err) | ||||||
| @@ -1114,10 +1141,11 @@ func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return issue, issue.LoadAttributes() | 	return issue, issue.LoadAttributes(db.DefaultContext) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getIssueByID(ctx context.Context, id int64) (*Issue, error) { | // GetIssueByID returns an issue by given ID. | ||||||
|  | func GetIssueByID(ctx context.Context, id int64) (*Issue, error) { | ||||||
| 	issue := new(Issue) | 	issue := new(Issue) | ||||||
| 	has, err := db.GetEngine(ctx).ID(id).Get(issue) | 	has, err := db.GetEngine(ctx).ID(id).Get(issue) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -1130,16 +1158,11 @@ func getIssueByID(ctx context.Context, id int64) (*Issue, error) { | |||||||
| 
 | 
 | ||||||
| // GetIssueWithAttrsByID returns an issue with attributes by given ID. | // GetIssueWithAttrsByID returns an issue with attributes by given ID. | ||||||
| func GetIssueWithAttrsByID(id int64) (*Issue, error) { | func GetIssueWithAttrsByID(id int64) (*Issue, error) { | ||||||
| 	issue, err := getIssueByID(db.DefaultContext, id) | 	issue, err := GetIssueByID(db.DefaultContext, id) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	return issue, issue.loadAttributes(db.DefaultContext) | 	return issue, issue.LoadAttributes(db.DefaultContext) | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // GetIssueByID returns an issue by given ID. |  | ||||||
| func GetIssueByID(id int64) (*Issue, error) { |  | ||||||
| 	return getIssueByID(db.DefaultContext, id) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetIssuesByIDs return issues with the given IDs. | // GetIssuesByIDs return issues with the given IDs. | ||||||
| @@ -1156,7 +1179,7 @@ func GetIssueIDsByRepoID(ctx context.Context, repoID int64) ([]int64, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // IssuesOptions represents options of an issue. | // IssuesOptions represents options of an issue. | ||||||
| type IssuesOptions struct { | type IssuesOptions struct { //nolint | ||||||
| 	db.ListOptions | 	db.ListOptions | ||||||
| 	RepoID             int64 // overwrites RepoCond if not 0 | 	RepoID             int64 // overwrites RepoCond if not 0 | ||||||
| 	RepoCond           builder.Cond | 	RepoCond           builder.Cond | ||||||
| @@ -1534,7 +1557,7 @@ func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) { | |||||||
| 
 | 
 | ||||||
| // IsUserParticipantsOfIssue return true if user is participants of an issue | // IsUserParticipantsOfIssue return true if user is participants of an issue | ||||||
| func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool { | func IsUserParticipantsOfIssue(user *user_model.User, issue *Issue) bool { | ||||||
| 	userIDs, err := issue.getParticipantIDsByIssue(db.DefaultContext) | 	userIDs, err := issue.GetParticipantIDsByIssue(db.DefaultContext) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error(err.Error()) | 		log.Error(err.Error()) | ||||||
| 		return false | 		return false | ||||||
| @@ -1577,17 +1600,6 @@ const ( | |||||||
| 	FilterModeYourRepositories | 	FilterModeYourRepositories | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func parseCountResult(results []map[string][]byte) int64 { |  | ||||||
| 	if len(results) == 0 { |  | ||||||
| 		return 0 |  | ||||||
| 	} |  | ||||||
| 	for _, result := range results[0] { |  | ||||||
| 		c, _ := strconv.ParseInt(string(result), 10, 64) |  | ||||||
| 		return c |  | ||||||
| 	} |  | ||||||
| 	return 0 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // IssueStatsOptions contains parameters accepted by GetIssueStats. | // IssueStatsOptions contains parameters accepted by GetIssueStats. | ||||||
| type IssueStatsOptions struct { | type IssueStatsOptions struct { | ||||||
| 	RepoID            int64 | 	RepoID            int64 | ||||||
| @@ -1602,14 +1614,15 @@ type IssueStatsOptions struct { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | 	// MaxQueryParameters represents the max query parameters | ||||||
| 	// When queries are broken down in parts because of the number | 	// When queries are broken down in parts because of the number | ||||||
| 	// of parameters, attempt to break by this amount | 	// of parameters, attempt to break by this amount | ||||||
| 	maxQueryParameters = 300 | 	MaxQueryParameters = 300 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // GetIssueStats returns issue statistic information by given conditions. | // GetIssueStats returns issue statistic information by given conditions. | ||||||
| func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | ||||||
| 	if len(opts.IssueIDs) <= maxQueryParameters { | 	if len(opts.IssueIDs) <= MaxQueryParameters { | ||||||
| 		return getIssueStatsChunk(opts, opts.IssueIDs) | 		return getIssueStatsChunk(opts, opts.IssueIDs) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -1619,7 +1632,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) { | |||||||
| 	// ids in a temporary table and join from them. | 	// ids in a temporary table and join from them. | ||||||
| 	accum := &IssueStats{} | 	accum := &IssueStats{} | ||||||
| 	for i := 0; i < len(opts.IssueIDs); { | 	for i := 0; i < len(opts.IssueIDs); { | ||||||
| 		chunk := i + maxQueryParameters | 		chunk := i + MaxQueryParameters | ||||||
| 		if chunk > len(opts.IssueIDs) { | 		if chunk > len(opts.IssueIDs) { | ||||||
| 			chunk = len(opts.IssueIDs) | 			chunk = len(opts.IssueIDs) | ||||||
| 		} | 		} | ||||||
| @@ -1950,7 +1963,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Reload the issue | 	// Reload the issue | ||||||
| 	currentIssue, err := getIssueByID(ctx, issue.ID) | 	currentIssue, err := GetIssueByID(ctx, issue.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, false, err | 		return nil, false, err | ||||||
| 	} | 	} | ||||||
| @@ -1985,7 +1998,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := issue.addCrossReferences(ctx, doer, true); err != nil { | 	if err := issue.AddCrossReferences(ctx, doer, true); err != nil { | ||||||
| 		return nil, false, err | 		return nil, false, err | ||||||
| 	} | 	} | ||||||
| 	return statusChangeComment, titleChanged, committer.Commit() | 	return statusChangeComment, titleChanged, committer.Commit() | ||||||
| @@ -2016,22 +2029,8 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us | |||||||
| 	return committer.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // DeleteIssue deletes the issue | // DeleteInIssue delete records in beans with external key issue_id = ? | ||||||
| func DeleteIssue(issue *Issue) error { | func DeleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error { | ||||||
| 	ctx, committer, err := db.TxContext() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	defer committer.Close() |  | ||||||
| 
 |  | ||||||
| 	if err := deleteIssue(ctx, issue); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return committer.Commit() |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func deleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error { |  | ||||||
| 	e := db.GetEngine(ctx) | 	e := db.GetEngine(ctx) | ||||||
| 	for _, bean := range beans { | 	for _, bean := range beans { | ||||||
| 		if _, err := e.In("issue_id", issueID).Delete(bean); err != nil { | 		if _, err := e.In("issue_id", issueID).Delete(bean); err != nil { | ||||||
| @@ -2041,103 +2040,14 @@ func deleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) err | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func deleteIssue(ctx context.Context, issue *Issue) error { |  | ||||||
| 	e := db.GetEngine(ctx) |  | ||||||
| 	if _, err := e.ID(issue.ID).NoAutoCondition().Delete(issue); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if issue.IsPull { |  | ||||||
| 		if _, err := e.ID(issue.RepoID).Decr("num_pulls").Update(new(repo_model.Repository)); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if issue.IsClosed { |  | ||||||
| 			if _, err := e.ID(issue.RepoID).Decr("num_closed_pulls").Update(new(repo_model.Repository)); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		if _, err := e.ID(issue.RepoID).Decr("num_issues").Update(new(repo_model.Repository)); err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 		if issue.IsClosed { |  | ||||||
| 			if _, err := e.ID(issue.RepoID).Decr("num_closed_issues").Update(new(repo_model.Repository)); err != nil { |  | ||||||
| 				return err |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// delete actions assigned to this issue |  | ||||||
| 	subQuery := builder.Select("`id`"). |  | ||||||
| 		From("`comment`"). |  | ||||||
| 		Where(builder.Eq{"`issue_id`": issue.ID}) |  | ||||||
| 	if _, err := e.In("comment_id", subQuery).Delete(&Action{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if _, err := e.Table("action").Where("repo_id = ?", issue.RepoID). |  | ||||||
| 		In("op_type", ActionCreateIssue, ActionCreatePullRequest). |  | ||||||
| 		Where("content LIKE ?", strconv.FormatInt(issue.ID, 10)+"|%"). |  | ||||||
| 		Delete(&Action{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// find attachments related to this issue and remove them |  | ||||||
| 	var attachments []*repo_model.Attachment |  | ||||||
| 	if err := e.In("issue_id", issue.ID).Find(&attachments); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	for i := range attachments { |  | ||||||
| 		admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", attachments[i].RelativePath()) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// delete all database data still assigned to this issue |  | ||||||
| 	if err := deleteInIssue(ctx, issue.ID, |  | ||||||
| 		&issues_model.ContentHistory{}, |  | ||||||
| 		&Comment{}, |  | ||||||
| 		&IssueLabel{}, |  | ||||||
| 		&IssueDependency{}, |  | ||||||
| 		&IssueAssignees{}, |  | ||||||
| 		&IssueUser{}, |  | ||||||
| 		&Notification{}, |  | ||||||
| 		&issues_model.Reaction{}, |  | ||||||
| 		&IssueWatch{}, |  | ||||||
| 		&Stopwatch{}, |  | ||||||
| 		&TrackedTime{}, |  | ||||||
| 		&project_model.ProjectIssue{}, |  | ||||||
| 		&repo_model.Attachment{}, |  | ||||||
| 		&PullRequest{}, |  | ||||||
| 	); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// References to this issue in other issues |  | ||||||
| 	if _, err := e.In("ref_issue_id", issue.ID).Delete(&Comment{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Delete dependencies for issues in other repositories |  | ||||||
| 	if _, err := e.In("dependency_id", issue.ID).Delete(&IssueDependency{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// delete from dependent issues |  | ||||||
| 	if _, err := e.In("dependent_issue_id", issue.ID).Delete(&Comment{}); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // DependencyInfo represents high level information about an issue which is a dependency of another issue. | // DependencyInfo represents high level information about an issue which is a dependency of another issue. | ||||||
| type DependencyInfo struct { | type DependencyInfo struct { | ||||||
| 	Issue                 `xorm:"extends"` | 	Issue                 `xorm:"extends"` | ||||||
| 	repo_model.Repository `xorm:"extends"` | 	repo_model.Repository `xorm:"extends"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author | // GetParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author | ||||||
| func (issue *Issue) getParticipantIDsByIssue(ctx context.Context) ([]int64, error) { | func (issue *Issue) GetParticipantIDsByIssue(ctx context.Context) ([]int64, error) { | ||||||
| 	if issue == nil { | 	if issue == nil { | ||||||
| 		return nil, nil | 		return nil, nil | ||||||
| 	} | 	} | ||||||
| @@ -2196,9 +2106,9 @@ func (issue *Issue) BlockingDependencies(ctx context.Context) (issueDeps []*Depe | |||||||
| 
 | 
 | ||||||
| func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) { | func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) { | ||||||
| 	if issue.IsPull { | 	if issue.IsPull { | ||||||
| 		err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") | 		err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") | ||||||
| 	} else { | 	} else { | ||||||
| 		err = repoStatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues") | 		err = repo_model.StatsCorrectNumClosed(ctx, issue.RepoID, false, "num_closed_issues") | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @@ -2363,6 +2273,17 @@ func UpdateIssuesMigrationsByType(gitServiceType api.GitServiceType, originalAut | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func migratedIssueCond(tp api.GitServiceType) builder.Cond { | ||||||
|  | 	return builder.In("issue_id", | ||||||
|  | 		builder.Select("issue.id"). | ||||||
|  | 			From("issue"). | ||||||
|  | 			InnerJoin("repository", "issue.repo_id = repository.id"). | ||||||
|  | 			Where(builder.Eq{ | ||||||
|  | 				"repository.original_service_type": tp, | ||||||
|  | 			}), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // UpdateReactionsMigrationsByType updates all migrated repositories' reactions from gitServiceType to replace originalAuthorID to posterID | // UpdateReactionsMigrationsByType updates all migrated repositories' reactions from gitServiceType to replace originalAuthorID to posterID | ||||||
| func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, originalAuthorID string, userID int64) error { | func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, originalAuthorID string, userID int64) error { | ||||||
| 	_, err := db.GetEngine(db.DefaultContext).Table("reaction"). | 	_, err := db.GetEngine(db.DefaultContext).Table("reaction"). | ||||||
| @@ -2376,13 +2297,14 @@ func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, original | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func deleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) { | // DeleteIssuesByRepoID deletes issues by repositories id | ||||||
|  | func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths []string, err error) { | ||||||
| 	deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"issue.repo_id": repoID}) | 	deleteCond := builder.Select("id").From("issue").Where(builder.Eq{"issue.repo_id": repoID}) | ||||||
| 
 | 
 | ||||||
| 	sess := db.GetEngine(ctx) | 	sess := db.GetEngine(ctx) | ||||||
| 	// Delete content histories | 	// Delete content histories | ||||||
| 	if _, err = sess.In("issue_id", deleteCond). | 	if _, err = sess.In("issue_id", deleteCond). | ||||||
| 		Delete(&issues_model.ContentHistory{}); err != nil { | 		Delete(&ContentHistory{}); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -2410,7 +2332,7 @@ func deleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths [] | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if _, err = sess.In("issue_id", deleteCond). | 	if _, err = sess.In("issue_id", deleteCond). | ||||||
| 		Delete(&issues_model.Reaction{}); err != nil { | 		Delete(&Reaction{}); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -2477,3 +2399,50 @@ func (issue *Issue) GetExternalName() string { return issue.OriginalAuthor } | |||||||
| 
 | 
 | ||||||
| // GetExternalID ExternalUserRemappable interface | // GetExternalID ExternalUserRemappable interface | ||||||
| func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID } | func (issue *Issue) GetExternalID() int64 { return issue.OriginalAuthorID } | ||||||
|  | 
 | ||||||
|  | // CountOrphanedIssues count issues without a repo | ||||||
|  | func CountOrphanedIssues() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).Table("issue"). | ||||||
|  | 		Join("LEFT", "repository", "issue.repo_id=repository.id"). | ||||||
|  | 		Where(builder.IsNull{"repository.id"}). | ||||||
|  | 		Select("COUNT(`issue`.`id`)"). | ||||||
|  | 		Count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DeleteOrphanedIssues delete issues without a repo | ||||||
|  | func DeleteOrphanedIssues() error { | ||||||
|  | 	ctx, committer, err := db.TxContext() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  | 
 | ||||||
|  | 	var ids []int64 | ||||||
|  | 
 | ||||||
|  | 	if err := db.GetEngine(ctx).Table("issue").Distinct("issue.repo_id"). | ||||||
|  | 		Join("LEFT", "repository", "issue.repo_id=repository.id"). | ||||||
|  | 		Where(builder.IsNull{"repository.id"}).GroupBy("issue.repo_id"). | ||||||
|  | 		Find(&ids); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var attachmentPaths []string | ||||||
|  | 	for i := range ids { | ||||||
|  | 		paths, err := DeleteIssuesByRepoID(ctx, ids[i]) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		attachmentPaths = append(attachmentPaths, paths...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := committer.Commit(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	committer.Close() | ||||||
|  | 
 | ||||||
|  | 	// Remove issue attachment files. | ||||||
|  | 	for i := range attachmentPaths { | ||||||
|  | 		admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i]) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								models/issues/issue_index.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								models/issues/issue_index.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | // Copyright 2017 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 issues | ||||||
|  |  | ||||||
|  | import "code.gitea.io/gitea/models/db" | ||||||
|  |  | ||||||
|  | // RecalculateIssueIndexForRepo create issue_index for repo if not exist and | ||||||
|  | // update it based on highest index of existing issues assigned to a repo | ||||||
|  | func RecalculateIssueIndexForRepo(repoID int64) error { | ||||||
|  | 	ctx, committer, err := db.TxContext() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer committer.Close() | ||||||
|  |  | ||||||
|  | 	if err := db.UpsertResourceIndex(ctx, "issue_index", repoID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var max int64 | ||||||
|  | 	if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return committer.Commit() | ||||||
|  | } | ||||||
| @@ -2,14 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/container" | 	"code.gitea.io/gitea/modules/container" | ||||||
| @@ -20,11 +19,6 @@ import ( | |||||||
| // IssueList defines a list of issues | // IssueList defines a list of issues | ||||||
| type IssueList []*Issue | type IssueList []*Issue | ||||||
| 
 | 
 | ||||||
| const ( |  | ||||||
| 	// default variables number on IN () in SQL |  | ||||||
| 	defaultMaxInSize = 50 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo | // get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo | ||||||
| func (issues IssueList) getRepoIDs() []int64 { | func (issues IssueList) getRepoIDs() []int64 { | ||||||
| 	repoIDs := make(map[int64]struct{}, len(issues)) | 	repoIDs := make(map[int64]struct{}, len(issues)) | ||||||
| @@ -48,7 +42,7 @@ func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Rep | |||||||
| 	repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs)) | 	repoMaps := make(map[int64]*repo_model.Repository, len(repoIDs)) | ||||||
| 	left := len(repoIDs) | 	left := len(repoIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -102,7 +96,7 @@ func (issues IssueList) loadPosters(ctx context.Context) error { | |||||||
| 	posterMaps := make(map[int64]*user_model.User, len(posterIDs)) | 	posterMaps := make(map[int64]*user_model.User, len(posterIDs)) | ||||||
| 	left := len(posterIDs) | 	left := len(posterIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -150,7 +144,7 @@ func (issues IssueList) loadLabels(ctx context.Context) error { | |||||||
| 	issueIDs := issues.getIssueIDs() | 	issueIDs := issues.getIssueIDs() | ||||||
| 	left := len(issueIDs) | 	left := len(issueIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -205,10 +199,10 @@ func (issues IssueList) loadMilestones(ctx context.Context) error { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	milestoneMaps := make(map[int64]*issues_model.Milestone, len(milestoneIDs)) | 	milestoneMaps := make(map[int64]*Milestone, len(milestoneIDs)) | ||||||
| 	left := len(milestoneIDs) | 	left := len(milestoneIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -242,7 +236,7 @@ func (issues IssueList) loadAssignees(ctx context.Context) error { | |||||||
| 	issueIDs := issues.getIssueIDs() | 	issueIDs := issues.getIssueIDs() | ||||||
| 	left := len(issueIDs) | 	left := len(issueIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -298,7 +292,7 @@ func (issues IssueList) loadPullRequests(ctx context.Context) error { | |||||||
| 	pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs)) | 	pullRequestMaps := make(map[int64]*PullRequest, len(issuesIDs)) | ||||||
| 	left := len(issuesIDs) | 	left := len(issuesIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -342,7 +336,7 @@ func (issues IssueList) loadAttachments(ctx context.Context) (err error) { | |||||||
| 	issuesIDs := issues.getIssueIDs() | 	issuesIDs := issues.getIssueIDs() | ||||||
| 	left := len(issuesIDs) | 	left := len(issuesIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -387,7 +381,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er | |||||||
| 	issuesIDs := issues.getIssueIDs() | 	issuesIDs := issues.getIssueIDs() | ||||||
| 	left := len(issuesIDs) | 	left := len(issuesIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -443,7 +437,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { | |||||||
| 
 | 
 | ||||||
| 	left := len(ids) | 	left := len(ids) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -2,11 +2,12 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 
 | 
 | ||||||
| @@ -16,10 +17,10 @@ import ( | |||||||
| func TestIssueList_LoadRepositories(t *testing.T) { | func TestIssueList_LoadRepositories(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	issueList := IssueList{ | 	issueList := issues_model.IssueList{ | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue), | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue), | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	repos, err := issueList.LoadRepositories() | 	repos, err := issueList.LoadRepositories() | ||||||
| @@ -33,9 +34,9 @@ func TestIssueList_LoadRepositories(t *testing.T) { | |||||||
| func TestIssueList_LoadAttributes(t *testing.T) { | func TestIssueList_LoadAttributes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	setting.Service.EnableTimetracking = true | 	setting.Service.EnableTimetracking = true | ||||||
| 	issueList := IssueList{ | 	issueList := issues_model.IssueList{ | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue), | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue), | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, issueList.LoadAttributes()) | 	assert.NoError(t, issueList.LoadAttributes()) | ||||||
| @@ -43,7 +44,7 @@ func TestIssueList_LoadAttributes(t *testing.T) { | |||||||
| 		assert.EqualValues(t, issue.RepoID, issue.Repo.ID) | 		assert.EqualValues(t, issue.RepoID, issue.Repo.ID) | ||||||
| 		for _, label := range issue.Labels { | 		for _, label := range issue.Labels { | ||||||
| 			assert.EqualValues(t, issue.RepoID, label.RepoID) | 			assert.EqualValues(t, issue.RepoID, label.RepoID) | ||||||
| 			unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issue.ID, LabelID: label.ID}) | 			unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) | ||||||
| 		} | 		} | ||||||
| 		if issue.PosterID > 0 { | 		if issue.PosterID > 0 { | ||||||
| 			assert.EqualValues(t, issue.PosterID, issue.Poster.ID) | 			assert.EqualValues(t, issue.PosterID, issue.Poster.ID) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -29,18 +29,18 @@ func TestIssue_ReplaceLabels(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	testSuccess := func(issueID int64, labelIDs []int64) { | 	testSuccess := func(issueID int64, labelIDs []int64) { | ||||||
| 		issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issueID}).(*Issue) | 		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue) | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository) | ||||||
| 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 		labels := make([]*Label, len(labelIDs)) | 		labels := make([]*issues_model.Label, len(labelIDs)) | ||||||
| 		for i, labelID := range labelIDs { | 		for i, labelID := range labelIDs { | ||||||
| 			labels[i] = unittest.AssertExistsAndLoadBean(t, &Label{ID: labelID, RepoID: repo.ID}).(*Label) | 			labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID}).(*issues_model.Label) | ||||||
| 		} | 		} | ||||||
| 		assert.NoError(t, ReplaceIssueLabels(issue, labels, doer)) | 		assert.NoError(t, issues_model.ReplaceIssueLabels(issue, labels, doer)) | ||||||
| 		unittest.AssertCount(t, &IssueLabel{IssueID: issueID}, len(labelIDs)) | 		unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(labelIDs)) | ||||||
| 		for _, labelID := range labelIDs { | 		for _, labelID := range labelIDs { | ||||||
| 			unittest.AssertExistsAndLoadBean(t, &IssueLabel{IssueID: issueID, LabelID: labelID}) | 			unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -52,15 +52,15 @@ func TestIssue_ReplaceLabels(t *testing.T) { | |||||||
| func Test_GetIssueIDsByRepoID(t *testing.T) { | func Test_GetIssueIDsByRepoID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	ids, err := GetIssueIDsByRepoID(db.DefaultContext, 1) | 	ids, err := issues_model.GetIssueIDsByRepoID(db.DefaultContext, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, ids, 5) | 	assert.Len(t, ids, 5) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestIssueAPIURL(t *testing.T) { | func TestIssueAPIURL(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	err := issue.LoadAttributes() | 	err := issue.LoadAttributes(db.DefaultContext) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/issues/1", issue.APIURL()) | 	assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/issues/1", issue.APIURL()) | ||||||
| @@ -69,7 +69,7 @@ func TestIssueAPIURL(t *testing.T) { | |||||||
| func TestGetIssuesByIDs(t *testing.T) { | func TestGetIssuesByIDs(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	testSuccess := func(expectedIssueIDs, nonExistentIssueIDs []int64) { | 	testSuccess := func(expectedIssueIDs, nonExistentIssueIDs []int64) { | ||||||
| 		issues, err := GetIssuesByIDs(db.DefaultContext, append(expectedIssueIDs, nonExistentIssueIDs...)) | 		issues, err := issues_model.GetIssuesByIDs(db.DefaultContext, append(expectedIssueIDs, nonExistentIssueIDs...)) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		actualIssueIDs := make([]int64, len(issues)) | 		actualIssueIDs := make([]int64, len(issues)) | ||||||
| 		for i, issue := range issues { | 		for i, issue := range issues { | ||||||
| @@ -85,9 +85,9 @@ func TestGetParticipantIDsByIssue(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	checkParticipants := func(issueID int64, userIDs []int) { | 	checkParticipants := func(issueID int64, userIDs []int) { | ||||||
| 		issue, err := GetIssueByID(issueID) | 		issue, err := issues_model.GetIssueByID(db.DefaultContext, issueID) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		participants, err := issue.getParticipantIDsByIssue(db.DefaultContext) | 		participants, err := issue.GetParticipantIDsByIssue(db.DefaultContext) | ||||||
| 		if assert.NoError(t, err) { | 		if assert.NoError(t, err) { | ||||||
| 			participantsIDs := make([]int, len(participants)) | 			participantsIDs := make([]int, len(participants)) | ||||||
| 			for i, uid := range participants { | 			for i, uid := range participants { | ||||||
| @@ -117,16 +117,16 @@ func TestIssue_ClearLabels(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| 	for _, test := range tests { | 	for _, test := range tests { | ||||||
| 		assert.NoError(t, unittest.PrepareTestDatabase()) | 		assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 		issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue) | 		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID}).(*issues_model.Issue) | ||||||
| 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User) | 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User) | ||||||
| 		assert.NoError(t, ClearIssueLabels(issue, doer)) | 		assert.NoError(t, issues_model.ClearIssueLabels(issue, doer)) | ||||||
| 		unittest.AssertNotExistsBean(t, &IssueLabel{IssueID: test.issueID}) | 		unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: test.issueID}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestUpdateIssueCols(t *testing.T) { | func TestUpdateIssueCols(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue) | ||||||
| 
 | 
 | ||||||
| 	const newTitle = "New Title for unit test" | 	const newTitle = "New Title for unit test" | ||||||
| 	issue.Title = newTitle | 	issue.Title = newTitle | ||||||
| @@ -135,10 +135,10 @@ func TestUpdateIssueCols(t *testing.T) { | |||||||
| 	issue.Content = "This should have no effect" | 	issue.Content = "This should have no effect" | ||||||
| 
 | 
 | ||||||
| 	now := time.Now().Unix() | 	now := time.Now().Unix() | ||||||
| 	assert.NoError(t, UpdateIssueCols(db.DefaultContext, issue, "name")) | 	assert.NoError(t, issues_model.UpdateIssueCols(db.DefaultContext, issue, "name")) | ||||||
| 	then := time.Now().Unix() | 	then := time.Now().Unix() | ||||||
| 
 | 
 | ||||||
| 	updatedIssue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue) | 	updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue) | ||||||
| 	assert.EqualValues(t, newTitle, updatedIssue.Title) | 	assert.EqualValues(t, newTitle, updatedIssue.Title) | ||||||
| 	assert.EqualValues(t, prevContent, updatedIssue.Content) | 	assert.EqualValues(t, prevContent, updatedIssue.Content) | ||||||
| 	unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) | 	unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) | ||||||
| @@ -147,18 +147,18 @@ func TestUpdateIssueCols(t *testing.T) { | |||||||
| func TestIssues(t *testing.T) { | func TestIssues(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	for _, test := range []struct { | 	for _, test := range []struct { | ||||||
| 		Opts             IssuesOptions | 		Opts             issues_model.IssuesOptions | ||||||
| 		ExpectedIssueIDs []int64 | 		ExpectedIssueIDs []int64 | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				AssigneeID: 1, | 				AssigneeID: 1, | ||||||
| 				SortType:   "oldest", | 				SortType:   "oldest", | ||||||
| 			}, | 			}, | ||||||
| 			[]int64{1, 6}, | 			[]int64{1, 6}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				RepoCond: builder.In("repo_id", 1, 3), | 				RepoCond: builder.In("repo_id", 1, 3), | ||||||
| 				SortType: "oldest", | 				SortType: "oldest", | ||||||
| 				ListOptions: db.ListOptions{ | 				ListOptions: db.ListOptions{ | ||||||
| @@ -169,7 +169,7 @@ func TestIssues(t *testing.T) { | |||||||
| 			[]int64{1, 2, 3, 5}, | 			[]int64{1, 2, 3, 5}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				LabelIDs: []int64{1}, | 				LabelIDs: []int64{1}, | ||||||
| 				ListOptions: db.ListOptions{ | 				ListOptions: db.ListOptions{ | ||||||
| 					Page:     1, | 					Page:     1, | ||||||
| @@ -179,7 +179,7 @@ func TestIssues(t *testing.T) { | |||||||
| 			[]int64{2, 1}, | 			[]int64{2, 1}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				LabelIDs: []int64{1, 2}, | 				LabelIDs: []int64{1, 2}, | ||||||
| 				ListOptions: db.ListOptions{ | 				ListOptions: db.ListOptions{ | ||||||
| 					Page:     1, | 					Page:     1, | ||||||
| @@ -189,7 +189,7 @@ func TestIssues(t *testing.T) { | |||||||
| 			[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests | 			[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests | ||||||
| 		}, | 		}, | ||||||
| 	} { | 	} { | ||||||
| 		issues, err := Issues(&test.Opts) | 		issues, err := issues_model.Issues(&test.Opts) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		if assert.Len(t, issues, len(test.ExpectedIssueIDs)) { | 		if assert.Len(t, issues, len(test.ExpectedIssueIDs)) { | ||||||
| 			for i, issue := range issues { | 			for i, issue := range issues { | ||||||
| @@ -202,16 +202,16 @@ func TestIssues(t *testing.T) { | |||||||
| func TestGetUserIssueStats(t *testing.T) { | func TestGetUserIssueStats(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	for _, test := range []struct { | 	for _, test := range []struct { | ||||||
| 		Opts               UserIssueStatsOptions | 		Opts               issues_model.UserIssueStatsOptions | ||||||
| 		ExpectedIssueStats IssueStats | 		ExpectedIssueStats issues_model.IssueStats | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				RepoIDs:    []int64{1}, | 				RepoIDs:    []int64{1}, | ||||||
| 				FilterMode: FilterModeAll, | 				FilterMode: issues_model.FilterModeAll, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 6 | 				YourRepositoriesCount: 1, // 6 | ||||||
| 				AssignCount:           1, // 6 | 				AssignCount:           1, // 6 | ||||||
| 				CreateCount:           1, // 6 | 				CreateCount:           1, // 6 | ||||||
| @@ -220,13 +220,13 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				RepoIDs:    []int64{1}, | 				RepoIDs:    []int64{1}, | ||||||
| 				FilterMode: FilterModeAll, | 				FilterMode: issues_model.FilterModeAll, | ||||||
| 				IsClosed:   true, | 				IsClosed:   true, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 6 | 				YourRepositoriesCount: 1, // 6 | ||||||
| 				AssignCount:           0, | 				AssignCount:           0, | ||||||
| 				CreateCount:           0, | 				CreateCount:           0, | ||||||
| @@ -235,11 +235,11 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				FilterMode: FilterModeAssign, | 				FilterMode: issues_model.FilterModeAssign, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 6 | 				YourRepositoriesCount: 1, // 6 | ||||||
| 				AssignCount:           1, // 6 | 				AssignCount:           1, // 6 | ||||||
| 				CreateCount:           1, // 6 | 				CreateCount:           1, // 6 | ||||||
| @@ -248,11 +248,11 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				FilterMode: FilterModeCreate, | 				FilterMode: issues_model.FilterModeCreate, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 6 | 				YourRepositoriesCount: 1, // 6 | ||||||
| 				AssignCount:           1, // 6 | 				AssignCount:           1, // 6 | ||||||
| 				CreateCount:           1, // 6 | 				CreateCount:           1, // 6 | ||||||
| @@ -261,11 +261,11 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				FilterMode: FilterModeMention, | 				FilterMode: issues_model.FilterModeMention, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 6 | 				YourRepositoriesCount: 1, // 6 | ||||||
| 				AssignCount:           1, // 6 | 				AssignCount:           1, // 6 | ||||||
| 				CreateCount:           1, // 6 | 				CreateCount:           1, // 6 | ||||||
| @@ -275,12 +275,12 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     1, | 				UserID:     1, | ||||||
| 				FilterMode: FilterModeCreate, | 				FilterMode: issues_model.FilterModeCreate, | ||||||
| 				IssueIDs:   []int64{1}, | 				IssueIDs:   []int64{1}, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 1, // 1 | 				YourRepositoriesCount: 1, // 1 | ||||||
| 				AssignCount:           1, // 1 | 				AssignCount:           1, // 1 | ||||||
| 				CreateCount:           1, // 1 | 				CreateCount:           1, // 1 | ||||||
| @@ -289,13 +289,13 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 			}, | 			}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			UserIssueStatsOptions{ | 			issues_model.UserIssueStatsOptions{ | ||||||
| 				UserID:     2, | 				UserID:     2, | ||||||
| 				Org:        unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization), | 				Org:        unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization), | ||||||
| 				Team:       unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 7}).(*organization.Team), | 				Team:       unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 7}).(*organization.Team), | ||||||
| 				FilterMode: FilterModeAll, | 				FilterMode: issues_model.FilterModeAll, | ||||||
| 			}, | 			}, | ||||||
| 			IssueStats{ | 			issues_model.IssueStats{ | ||||||
| 				YourRepositoriesCount: 2, | 				YourRepositoriesCount: 2, | ||||||
| 				AssignCount:           1, | 				AssignCount:           1, | ||||||
| 				CreateCount:           1, | 				CreateCount:           1, | ||||||
| @@ -304,7 +304,7 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 		}, | 		}, | ||||||
| 	} { | 	} { | ||||||
| 		t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) { | 		t.Run(fmt.Sprintf("%#v", test.Opts), func(t *testing.T) { | ||||||
| 			stats, err := GetUserIssueStats(test.Opts) | 			stats, err := issues_model.GetUserIssueStats(test.Opts) | ||||||
| 			if !assert.NoError(t, err) { | 			if !assert.NoError(t, err) { | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| @@ -315,31 +315,31 @@ func TestGetUserIssueStats(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestIssue_loadTotalTimes(t *testing.T) { | func TestIssue_loadTotalTimes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	ms, err := GetIssueByID(2) | 	ms, err := issues_model.GetIssueByID(db.DefaultContext, 2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NoError(t, ms.loadTotalTimes(db.DefaultContext)) | 	assert.NoError(t, ms.LoadTotalTimes(db.DefaultContext)) | ||||||
| 	assert.Equal(t, int64(3682), ms.TotalTrackedTime) | 	assert.Equal(t, int64(3682), ms.TotalTrackedTime) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestIssue_SearchIssueIDsByKeyword(t *testing.T) { | func TestIssue_SearchIssueIDsByKeyword(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0) | 	total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "issue2", []int64{1}, 10, 0) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 1, total) | 	assert.EqualValues(t, 1, total) | ||||||
| 	assert.EqualValues(t, []int64{2}, ids) | 	assert.EqualValues(t, []int64{2}, ids) | ||||||
| 
 | 
 | ||||||
| 	total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0) | 	total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "first", []int64{1}, 10, 0) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 1, total) | 	assert.EqualValues(t, 1, total) | ||||||
| 	assert.EqualValues(t, []int64{1}, ids) | 	assert.EqualValues(t, []int64{1}, ids) | ||||||
| 
 | 
 | ||||||
| 	total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0) | 	total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "for", []int64{1}, 10, 0) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 5, total) | 	assert.EqualValues(t, 5, total) | ||||||
| 	assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) | 	assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) | ||||||
| 
 | 
 | ||||||
| 	// issue1's comment id 2 | 	// issue1's comment id 2 | ||||||
| 	total, ids, err = SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0) | 	total, ids, err = issues_model.SearchIssueIDsByKeyword(context.TODO(), "good", []int64{1}, 10, 0) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 1, total) | 	assert.EqualValues(t, 1, total) | ||||||
| 	assert.EqualValues(t, []int64{1}, ids) | 	assert.EqualValues(t, []int64{1}, ids) | ||||||
| @@ -349,23 +349,23 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	for _, test := range []struct { | 	for _, test := range []struct { | ||||||
| 		Opts            IssuesOptions | 		Opts            issues_model.IssuesOptions | ||||||
| 		ExpectedRepoIDs []int64 | 		ExpectedRepoIDs []int64 | ||||||
| 	}{ | 	}{ | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				AssigneeID: 2, | 				AssigneeID: 2, | ||||||
| 			}, | 			}, | ||||||
| 			[]int64{3, 32}, | 			[]int64{3, 32}, | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			IssuesOptions{ | 			issues_model.IssuesOptions{ | ||||||
| 				RepoCond: builder.In("repo_id", 1, 2), | 				RepoCond: builder.In("repo_id", 1, 2), | ||||||
| 			}, | 			}, | ||||||
| 			[]int64{1, 2}, | 			[]int64{1, 2}, | ||||||
| 		}, | 		}, | ||||||
| 	} { | 	} { | ||||||
| 		repoIDs, err := GetRepoIDsForIssuesOptions(&test.Opts, user) | 		repoIDs, err := issues_model.GetRepoIDsForIssuesOptions(&test.Opts, user) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		if assert.Len(t, repoIDs, len(test.ExpectedRepoIDs)) { | 		if assert.Len(t, repoIDs, len(test.ExpectedRepoIDs)) { | ||||||
| 			for i, repoID := range repoIDs { | 			for i, repoID := range repoIDs { | ||||||
| @@ -375,20 +375,20 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *Issue { | func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *issues_model.Issue { | ||||||
| 	var newIssue Issue | 	var newIssue issues_model.Issue | ||||||
| 	t.Run(title, func(t *testing.T) { | 	t.Run(title, func(t *testing.T) { | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 		user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 		issue := Issue{ | 		issue := issues_model.Issue{ | ||||||
| 			RepoID:   repo.ID, | 			RepoID:   repo.ID, | ||||||
| 			PosterID: user.ID, | 			PosterID: user.ID, | ||||||
| 			Poster:   user, | 			Poster:   user, | ||||||
| 			Title:    title, | 			Title:    title, | ||||||
| 			Content:  content, | 			Content:  content, | ||||||
| 		} | 		} | ||||||
| 		err := NewIssue(repo, &issue, nil, nil) | 		err := issues_model.NewIssue(repo, &issue, nil, nil) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 		has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue) | 		has, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Get(&newIssue) | ||||||
| @@ -408,75 +408,23 @@ func TestIssue_InsertIssue(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// there are 5 issues and max index is 5 on repository 1, so this one should 6 | 	// there are 5 issues and max index is 5 on repository 1, so this one should 6 | ||||||
| 	issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6) | 	issue := testInsertIssue(t, "my issue1", "special issue's comments?", 6) | ||||||
| 	_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue)) | 	_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(issues_model.Issue)) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7) | 	issue = testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?", 7) | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(Issue)) | 	_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Delete(new(issues_model.Issue)) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestIssue_DeleteIssue(t *testing.T) { |  | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) |  | ||||||
| 
 |  | ||||||
| 	issueIDs, err := GetIssueIDsByRepoID(db.DefaultContext, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 5, len(issueIDs)) |  | ||||||
| 
 |  | ||||||
| 	issue := &Issue{ |  | ||||||
| 		RepoID: 1, |  | ||||||
| 		ID:     issueIDs[2], |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	err = DeleteIssue(issue) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issueIDs, err = GetIssueIDsByRepoID(db.DefaultContext, 1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 4, len(issueIDs)) |  | ||||||
| 
 |  | ||||||
| 	// check attachment removal |  | ||||||
| 	attachments, err := repo_model.GetAttachmentsByIssueID(db.DefaultContext, 4) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issue, err = GetIssueByID(4) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	err = DeleteIssue(issue) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.EqualValues(t, 2, len(attachments)) |  | ||||||
| 	for i := range attachments { |  | ||||||
| 		attachment, err := repo_model.GetAttachmentByUUID(db.DefaultContext, attachments[i].UUID) |  | ||||||
| 		assert.Error(t, err) |  | ||||||
| 		assert.True(t, repo_model.IsErrAttachmentNotExist(err)) |  | ||||||
| 		assert.Nil(t, attachment) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// check issue dependencies |  | ||||||
| 	user, err := user_model.GetUserByID(1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issue1, err := GetIssueByID(1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	issue2, err := GetIssueByID(2) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	err = CreateIssueDependency(user, issue1, issue2) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	left, err := IssueNoDependenciesLeft(db.DefaultContext, issue1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.False(t, left) |  | ||||||
| 	err = DeleteIssue(&Issue{ID: 2}) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	left, err = IssueNoDependenciesLeft(db.DefaultContext, issue1) |  | ||||||
| 	assert.NoError(t, err) |  | ||||||
| 	assert.True(t, left) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func TestIssue_ResolveMentions(t *testing.T) { | func TestIssue_ResolveMentions(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) { | 	testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) { | ||||||
| 		o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User) | 		o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User) | ||||||
| 		r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo}).(*repo_model.Repository) | 		r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo}).(*repo_model.Repository) | ||||||
| 		issue := &Issue{RepoID: r.ID} | 		issue := &issues_model.Issue{RepoID: r.ID} | ||||||
| 		d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User) | 		d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User) | ||||||
| 		resolved, err := ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions) | 		resolved, err := issues_model.ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		ids := make([]int64, len(resolved)) | 		ids := make([]int64, len(resolved)) | ||||||
| 		for i, user := range resolved { | 		for i, user := range resolved { | ||||||
| @@ -523,7 +471,7 @@ func TestCorrectIssueStats(t *testing.T) { | |||||||
| 	// Each new issues will have a constant description "Bugs are nasty" | 	// Each new issues will have a constant description "Bugs are nasty" | ||||||
| 	// Which will be used later on. | 	// Which will be used later on. | ||||||
| 
 | 
 | ||||||
| 	issueAmount := maxQueryParameters + 10 | 	issueAmount := issues_model.MaxQueryParameters + 10 | ||||||
| 
 | 
 | ||||||
| 	var wg sync.WaitGroup | 	var wg sync.WaitGroup | ||||||
| 	for i := 0; i < issueAmount; i++ { | 	for i := 0; i < issueAmount; i++ { | ||||||
| @@ -536,7 +484,7 @@ func TestCorrectIssueStats(t *testing.T) { | |||||||
| 	wg.Wait() | 	wg.Wait() | ||||||
| 
 | 
 | ||||||
| 	// Now we will get all issueID's that match the "Bugs are nasty" query. | 	// Now we will get all issueID's that match the "Bugs are nasty" query. | ||||||
| 	total, ids, err := SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0) | 	total, ids, err := issues_model.SearchIssueIDsByKeyword(context.TODO(), "Bugs are nasty", []int64{1}, issueAmount, 0) | ||||||
| 
 | 
 | ||||||
| 	// Just to be sure. | 	// Just to be sure. | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| @@ -544,7 +492,7 @@ func TestCorrectIssueStats(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	// Now we will call the GetIssueStats with these IDs and if working, | 	// Now we will call the GetIssueStats with these IDs and if working, | ||||||
| 	// get the correct stats back. | 	// get the correct stats back. | ||||||
| 	issueStats, err := GetIssueStats(&IssueStatsOptions{ | 	issueStats, err := issues_model.GetIssueStats(&issues_model.IssueStatsOptions{ | ||||||
| 		RepoID:   1, | 		RepoID:   1, | ||||||
| 		IssueIDs: ids, | 		IssueIDs: ids, | ||||||
| 	}) | 	}) | ||||||
| @@ -556,19 +504,19 @@ func TestCorrectIssueStats(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestIssueForeignReference(t *testing.T) { | func TestIssueForeignReference(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 4}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue) | ||||||
| 	assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive | 	assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive | ||||||
| 
 | 
 | ||||||
| 	// it is fine for an issue to not have a foreign reference | 	// it is fine for an issue to not have a foreign reference | ||||||
| 	err := issue.LoadAttributes() | 	err := issue.LoadAttributes(db.DefaultContext) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Nil(t, issue.ForeignReference) | 	assert.Nil(t, issue.ForeignReference) | ||||||
| 
 | 
 | ||||||
| 	var foreignIndex int64 = 12345 | 	var foreignIndex int64 = 12345 | ||||||
| 	_, err = GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) | 	_, err = issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) | ||||||
| 	assert.True(t, foreignreference.IsErrLocalIndexNotExist(err)) | 	assert.True(t, foreignreference.IsErrLocalIndexNotExist(err)) | ||||||
| 
 | 
 | ||||||
| 	_, err = db.GetEngine(db.DefaultContext).Insert(&foreignreference.ForeignReference{ | 	err = db.Insert(db.DefaultContext, &foreignreference.ForeignReference{ | ||||||
| 		LocalIndex:   issue.Index, | 		LocalIndex:   issue.Index, | ||||||
| 		ForeignIndex: strconv.FormatInt(foreignIndex, 10), | 		ForeignIndex: strconv.FormatInt(foreignIndex, 10), | ||||||
| 		RepoID:       issue.RepoID, | 		RepoID:       issue.RepoID, | ||||||
| @@ -576,12 +524,12 @@ func TestIssueForeignReference(t *testing.T) { | |||||||
| 	}) | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	err = issue.LoadAttributes() | 	err = issue.LoadAttributes(db.DefaultContext) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	assert.EqualValues(t, issue.ForeignReference.ForeignIndex, strconv.FormatInt(foreignIndex, 10)) | 	assert.EqualValues(t, issue.ForeignReference.ForeignIndex, strconv.FormatInt(foreignIndex, 10)) | ||||||
| 
 | 
 | ||||||
| 	found, err := GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) | 	found, err := issues_model.GetIssueByForeignIndex(context.Background(), issue.RepoID, foreignIndex) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, found.Index, issue.Index) | 	assert.EqualValues(t, found.Index, issue.Index) | ||||||
| } | } | ||||||
| @@ -608,7 +556,7 @@ func TestLoadTotalTrackedTime(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestCountIssues(t *testing.T) { | func TestCountIssues(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	count, err := CountIssues(&IssuesOptions{}) | 	count, err := issues_model.CountIssues(&issues_model.IssuesOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 17, count) | 	assert.EqualValues(t, 17, count) | ||||||
| } | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -25,7 +25,8 @@ func init() { | |||||||
| 	db.RegisterModel(new(IssueUser)) | 	db.RegisterModel(new(IssueUser)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func newIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error { | // NewIssueUsers inserts an issue related users | ||||||
|  | func NewIssueUsers(ctx context.Context, repo *repo_model.Repository, issue *Issue) error { | ||||||
| 	assignees, err := repo_model.GetRepoAssignees(ctx, repo) | 	assignees, err := repo_model.GetRepoAssignees(ctx, repo) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fmt.Errorf("getAssignees: %v", err) | 		return fmt.Errorf("getAssignees: %v", err) | ||||||
							
								
								
									
										62
									
								
								models/issues/issue_user_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								models/issues/issue_user_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | // Copyright 2017 The Gogs 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 issues_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
|  | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Test_NewIssueUsers(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
|  | 	newIssue := &issues_model.Issue{ | ||||||
|  | 		RepoID:   repo.ID, | ||||||
|  | 		PosterID: 4, | ||||||
|  | 		Index:    6, | ||||||
|  | 		Title:    "newTestIssueTitle", | ||||||
|  | 		Content:  "newTestIssueContent", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// artificially insert new issue | ||||||
|  | 	unittest.AssertSuccessfulInsert(t, newIssue) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue)) | ||||||
|  |  | ||||||
|  | 	// issue_user table should now have entries for new issue | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID}) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: repo.OwnerID}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateIssueUserByRead(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.UpdateIssueUserByRead(4, issue.ID)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1") | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.UpdateIssueUserByRead(4, issue.ID)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1") | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.UpdateIssueUserByRead(unittest.NonexistentID, unittest.NonexistentID)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateIssueUsersByMentions(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
|  |  | ||||||
|  | 	uids := []int64{2, 5} | ||||||
|  | 	assert.NoError(t, issues_model.UpdateIssueUsersByMentions(db.DefaultContext, issue.ID, uids)) | ||||||
|  | 	for _, uid := range uids { | ||||||
|  | 		unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: uid}, "is_mentioned=1") | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -125,7 +125,8 @@ func CountIssueWatchers(ctx context.Context, issueID int64) (int64, error) { | |||||||
| 		Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id").Count(new(IssueWatch)) | 		Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id").Count(new(IssueWatch)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func removeIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error { | // RemoveIssueWatchersByRepoID remove issue watchers by repoID | ||||||
|  | func RemoveIssueWatchersByRepoID(ctx context.Context, userID, repoID int64) error { | ||||||
| 	_, err := db.GetEngine(ctx). | 	_, err := db.GetEngine(ctx). | ||||||
| 		Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID). | 		Join("INNER", "issue", "`issue`.id = `issue_watch`.issue_id AND `issue`.repo_id = ?", repoID). | ||||||
| 		Where("`issue_watch`.user_id = ?", userID). | 		Where("`issue_watch`.user_id = ?", userID). | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -16,28 +17,28 @@ import ( | |||||||
| func TestCreateOrUpdateIssueWatch(t *testing.T) { | func TestCreateOrUpdateIssueWatch(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, CreateOrUpdateIssueWatch(3, 1, true)) | 	assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(3, 1, true)) | ||||||
| 	iw := unittest.AssertExistsAndLoadBean(t, &IssueWatch{UserID: 3, IssueID: 1}).(*IssueWatch) | 	iw := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 3, IssueID: 1}).(*issues_model.IssueWatch) | ||||||
| 	assert.True(t, iw.IsWatching) | 	assert.True(t, iw.IsWatching) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, CreateOrUpdateIssueWatch(1, 1, false)) | 	assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(1, 1, false)) | ||||||
| 	iw = unittest.AssertExistsAndLoadBean(t, &IssueWatch{UserID: 1, IssueID: 1}).(*IssueWatch) | 	iw = unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 1, IssueID: 1}).(*issues_model.IssueWatch) | ||||||
| 	assert.False(t, iw.IsWatching) | 	assert.False(t, iw.IsWatching) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestGetIssueWatch(t *testing.T) { | func TestGetIssueWatch(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	_, exists, err := GetIssueWatch(db.DefaultContext, 9, 1) | 	_, exists, err := issues_model.GetIssueWatch(db.DefaultContext, 9, 1) | ||||||
| 	assert.True(t, exists) | 	assert.True(t, exists) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	iw, exists, err := GetIssueWatch(db.DefaultContext, 2, 2) | 	iw, exists, err := issues_model.GetIssueWatch(db.DefaultContext, 2, 2) | ||||||
| 	assert.True(t, exists) | 	assert.True(t, exists) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.False(t, iw.IsWatching) | 	assert.False(t, iw.IsWatching) | ||||||
| 
 | 
 | ||||||
| 	_, exists, err = GetIssueWatch(db.DefaultContext, 3, 1) | 	_, exists, err = issues_model.GetIssueWatch(db.DefaultContext, 3, 1) | ||||||
| 	assert.False(t, exists) | 	assert.False(t, exists) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| } | } | ||||||
| @@ -45,22 +46,22 @@ func TestGetIssueWatch(t *testing.T) { | |||||||
| func TestGetIssueWatchers(t *testing.T) { | func TestGetIssueWatchers(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	iws, err := GetIssueWatchers(db.DefaultContext, 1, db.ListOptions{}) | 	iws, err := issues_model.GetIssueWatchers(db.DefaultContext, 1, db.ListOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	// Watcher is inactive, thus 0 | 	// Watcher is inactive, thus 0 | ||||||
| 	assert.Len(t, iws, 0) | 	assert.Len(t, iws, 0) | ||||||
| 
 | 
 | ||||||
| 	iws, err = GetIssueWatchers(db.DefaultContext, 2, db.ListOptions{}) | 	iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 2, db.ListOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	// Watcher is explicit not watching | 	// Watcher is explicit not watching | ||||||
| 	assert.Len(t, iws, 0) | 	assert.Len(t, iws, 0) | ||||||
| 
 | 
 | ||||||
| 	iws, err = GetIssueWatchers(db.DefaultContext, 5, db.ListOptions{}) | 	iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 5, db.ListOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	// Issue has no Watchers | 	// Issue has no Watchers | ||||||
| 	assert.Len(t, iws, 0) | 	assert.Len(t, iws, 0) | ||||||
| 
 | 
 | ||||||
| 	iws, err = GetIssueWatchers(db.DefaultContext, 7, db.ListOptions{}) | 	iws, err = issues_model.GetIssueWatchers(db.DefaultContext, 7, db.ListOptions{}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	// Issue has one watcher | 	// Issue has one watcher | ||||||
| 	assert.Len(t, iws, 1) | 	assert.Len(t, iws, 1) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -55,15 +55,8 @@ func neuterCrossReferencesIds(ctx context.Context, ids []int64) error { | |||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // .___ | // AddCrossReferences add cross repositories references. | ||||||
| // |   | ______ ________ __   ____ | func (issue *Issue) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { | ||||||
| // |   |/  ___//  ___/  |  \_/ __ \ |  | ||||||
| // |   |\___ \ \___ \|  |  /\  ___/ |  | ||||||
| // |___/____  >____  >____/  \___  > |  | ||||||
| //          \/     \/            \/ |  | ||||||
| // |  | ||||||
| 
 |  | ||||||
| func (issue *Issue) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { |  | ||||||
| 	var commentType CommentType | 	var commentType CommentType | ||||||
| 	if issue.IsPull { | 	if issue.IsPull { | ||||||
| 		commentType = CommentTypePullRef | 		commentType = CommentTypePullRef | ||||||
| @@ -237,15 +230,8 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe | |||||||
| 	return refIssue, refAction, nil | 	return refIssue, refAction, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // _________                                       __ | // AddCrossReferences add cross references | ||||||
| // \_   ___ \  ____   _____   _____   ____   _____/  |_ | func (comment *Comment) AddCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { | ||||||
| // /    \  \/ /  _ \ /     \ /     \_/ __ \ /    \   __\ |  | ||||||
| // \     \___(  <_> )  Y Y  \  Y Y  \  ___/|   |  \  | |  | ||||||
| //  \______  /\____/|__|_|  /__|_|  /\___  >___|  /__| |  | ||||||
| //         \/             \/      \/     \/     \/ |  | ||||||
| // |  | ||||||
| 
 |  | ||||||
| func (comment *Comment) addCrossReferences(stdCtx context.Context, doer *user_model.User, removeOld bool) error { |  | ||||||
| 	if comment.Type != CommentTypeCode && comment.Type != CommentTypeComment { | 	if comment.Type != CommentTypeCode && comment.Type != CommentTypeComment { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| @@ -280,7 +266,7 @@ func (comment *Comment) LoadRefIssue() (err error) { | |||||||
| 	if comment.RefIssue != nil { | 	if comment.RefIssue != nil { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 	comment.RefIssue, err = GetIssueByID(comment.RefIssueID) | 	comment.RefIssue, err = GetIssueByID(db.DefaultContext, comment.RefIssueID) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		err = comment.RefIssue.LoadRepo(db.DefaultContext) | 		err = comment.RefIssue.LoadRepo(db.DefaultContext) | ||||||
| 	} | 	} | ||||||
| @@ -2,13 +2,14 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -26,8 +27,8 @@ func TestXRef_AddCrossReferences(t *testing.T) { | |||||||
| 	// PR to close issue #1 | 	// PR to close issue #1 | ||||||
| 	content := fmt.Sprintf("content2, closes #%d", itarget.Index) | 	content := fmt.Sprintf("content2, closes #%d", itarget.Index) | ||||||
| 	pr := testCreateIssue(t, 1, 2, "title2", content, true) | 	pr := testCreateIssue(t, 1, 2, "title2", content, true) | ||||||
| 	ref := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*Comment) | 	ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypePullRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypePullRef, ref.Type) | ||||||
| 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||||||
| 	assert.True(t, ref.RefIsPull) | 	assert.True(t, ref.RefIsPull) | ||||||
| 	assert.Equal(t, references.XRefActionCloses, ref.RefAction) | 	assert.Equal(t, references.XRefActionCloses, ref.RefAction) | ||||||
| @@ -35,8 +36,8 @@ func TestXRef_AddCrossReferences(t *testing.T) { | |||||||
| 	// Comment on PR to reopen issue #1 | 	// Comment on PR to reopen issue #1 | ||||||
| 	content = fmt.Sprintf("content2, reopens #%d", itarget.Index) | 	content = fmt.Sprintf("content2, reopens #%d", itarget.Index) | ||||||
| 	c := testCreateComment(t, 1, 2, pr.ID, content) | 	c := testCreateComment(t, 1, 2, pr.ID, content) | ||||||
| 	ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*Comment) | 	ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypeCommentRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type) | ||||||
| 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||||||
| 	assert.True(t, ref.RefIsPull) | 	assert.True(t, ref.RefIsPull) | ||||||
| 	assert.Equal(t, references.XRefActionReopens, ref.RefAction) | 	assert.Equal(t, references.XRefActionReopens, ref.RefAction) | ||||||
| @@ -44,8 +45,8 @@ func TestXRef_AddCrossReferences(t *testing.T) { | |||||||
| 	// Issue mentioning issue #1 | 	// Issue mentioning issue #1 | ||||||
| 	content = fmt.Sprintf("content3, mentions #%d", itarget.Index) | 	content = fmt.Sprintf("content3, mentions #%d", itarget.Index) | ||||||
| 	i := testCreateIssue(t, 1, 2, "title3", content, false) | 	i := testCreateIssue(t, 1, 2, "title3", content, false) | ||||||
| 	ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | 	ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypeIssueRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type) | ||||||
| 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | 	assert.Equal(t, pr.RepoID, ref.RefRepoID) | ||||||
| 	assert.False(t, ref.RefIsPull) | 	assert.False(t, ref.RefIsPull) | ||||||
| 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||||||
| @@ -56,8 +57,8 @@ func TestXRef_AddCrossReferences(t *testing.T) { | |||||||
| 	// Cross-reference to issue #4 by admin | 	// Cross-reference to issue #4 by admin | ||||||
| 	content = fmt.Sprintf("content5, mentions user3/repo3#%d", itarget.Index) | 	content = fmt.Sprintf("content5, mentions user3/repo3#%d", itarget.Index) | ||||||
| 	i = testCreateIssue(t, 2, 1, "title5", content, false) | 	i = testCreateIssue(t, 2, 1, "title5", content, false) | ||||||
| 	ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | 	ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypeIssueRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type) | ||||||
| 	assert.Equal(t, i.RepoID, ref.RefRepoID) | 	assert.Equal(t, i.RepoID, ref.RefRepoID) | ||||||
| 	assert.False(t, ref.RefIsPull) | 	assert.False(t, ref.RefIsPull) | ||||||
| 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||||||
| @@ -65,7 +66,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { | |||||||
| 	// Cross-reference to issue #4 with no permission | 	// Cross-reference to issue #4 with no permission | ||||||
| 	content = fmt.Sprintf("content6, mentions user3/repo3#%d", itarget.Index) | 	content = fmt.Sprintf("content6, mentions user3/repo3#%d", itarget.Index) | ||||||
| 	i = testCreateIssue(t, 4, 5, "title6", content, false) | 	i = testCreateIssue(t, 4, 5, "title6", content, false) | ||||||
| 	unittest.AssertNotExistsBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}) | 	unittest.AssertNotExistsBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestXRef_NeuterCrossReferences(t *testing.T) { | func TestXRef_NeuterCrossReferences(t *testing.T) { | ||||||
| @@ -77,16 +78,16 @@ func TestXRef_NeuterCrossReferences(t *testing.T) { | |||||||
| 	// Issue mentioning issue #1 | 	// Issue mentioning issue #1 | ||||||
| 	title := fmt.Sprintf("title2, mentions #%d", itarget.Index) | 	title := fmt.Sprintf("title2, mentions #%d", itarget.Index) | ||||||
| 	i := testCreateIssue(t, 1, 2, title, "content2", false) | 	i := testCreateIssue(t, 1, 2, title, "content2", false) | ||||||
| 	ref := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | 	ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypeIssueRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type) | ||||||
| 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | 	assert.Equal(t, references.XRefActionNone, ref.RefAction) | ||||||
| 
 | 
 | ||||||
| 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	i.Title = "title2, no mentions" | 	i.Title = "title2, no mentions" | ||||||
| 	assert.NoError(t, ChangeIssueTitle(i, d, title)) | 	assert.NoError(t, issues_model.ChangeIssueTitle(i, d, title)) | ||||||
| 
 | 
 | ||||||
| 	ref = unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*Comment) | 	ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, CommentTypeIssueRef, ref.Type) | 	assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type) | ||||||
| 	assert.Equal(t, references.XRefActionNeutered, ref.RefAction) | 	assert.Equal(t, references.XRefActionNeutered, ref.RefAction) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -98,25 +99,25 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { | |||||||
| 	i1 := testCreateIssue(t, 1, 2, "title1", "content1", false) | 	i1 := testCreateIssue(t, 1, 2, "title1", "content1", false) | ||||||
| 	i2 := testCreateIssue(t, 1, 2, "title2", "content2", false) | 	i2 := testCreateIssue(t, 1, 2, "title2", "content2", false) | ||||||
| 	i3 := testCreateIssue(t, 1, 2, "title3", "content3", false) | 	i3 := testCreateIssue(t, 1, 2, "title3", "content3", false) | ||||||
| 	_, err := ChangeIssueStatus(db.DefaultContext, i3, d, true) | 	_, err := issues_model.ChangeIssueStatus(db.DefaultContext, i3, d, true) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index)) | 	pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index)) | ||||||
| 	rp := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*Comment) | 	rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*issues_model.Comment) | ||||||
| 
 | 
 | ||||||
| 	c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index)) | 	c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index)) | ||||||
| 	r1 := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*Comment) | 	r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*issues_model.Comment) | ||||||
| 
 | 
 | ||||||
| 	// Must be ignored | 	// Must be ignored | ||||||
| 	c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index)) | 	c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index)) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID}) | ||||||
| 
 | 
 | ||||||
| 	// Must be superseded by c4/r4 | 	// Must be superseded by c4/r4 | ||||||
| 	c3 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index)) | 	c3 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index)) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID}) | ||||||
| 
 | 
 | ||||||
| 	c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index)) | 	c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index)) | ||||||
| 	r4 := unittest.AssertExistsAndLoadBean(t, &Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*Comment) | 	r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*issues_model.Comment) | ||||||
| 
 | 
 | ||||||
| 	refs, err := pr.ResolveCrossReferences(db.DefaultContext) | 	refs, err := pr.ResolveCrossReferences(db.DefaultContext) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| @@ -126,13 +127,13 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { | |||||||
| 	assert.Equal(t, r4.ID, refs[2].ID, "bad ref r4: %+v", refs[2]) | 	assert.Equal(t, r4.ID, refs[2].ID, "bad ref r4: %+v", refs[2]) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *Issue { | func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *issues_model.Issue { | ||||||
| 	r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository) | 	r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository) | ||||||
| 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	idx, err := db.GetNextResourceIndex("issue_index", r.ID) | 	idx, err := db.GetNextResourceIndex("issue_index", r.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	i := &Issue{ | 	i := &issues_model.Issue{ | ||||||
| 		RepoID:   r.ID, | 		RepoID:   r.ID, | ||||||
| 		PosterID: d.ID, | 		PosterID: d.ID, | ||||||
| 		Poster:   d, | 		Poster:   d, | ||||||
| @@ -145,39 +146,39 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu | |||||||
| 	ctx, committer, err := db.TxContext() | 	ctx, committer, err := db.TxContext() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	defer committer.Close() | 	defer committer.Close() | ||||||
| 	err = newIssue(ctx, d, NewIssueOptions{ | 	err = issues_model.NewIssueWithIndex(ctx, d, issues_model.NewIssueOptions{ | ||||||
| 		Repo:  r, | 		Repo:  r, | ||||||
| 		Issue: i, | 		Issue: i, | ||||||
| 	}) | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	i, err = getIssueByID(ctx, i.ID) | 	i, err = issues_model.GetIssueByID(ctx, i.ID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NoError(t, i.addCrossReferences(ctx, d, false)) | 	assert.NoError(t, i.AddCrossReferences(ctx, d, false)) | ||||||
| 	assert.NoError(t, committer.Commit()) | 	assert.NoError(t, committer.Commit()) | ||||||
| 	return i | 	return i | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testCreatePR(t *testing.T, repo, doer int64, title, content string) *PullRequest { | func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues_model.PullRequest { | ||||||
| 	r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository) | 	r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository) | ||||||
| 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | ||||||
| 	i := &Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true} | 	i := &issues_model.Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true} | ||||||
| 	pr := &PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: PullRequestStatusMergeable} | 	pr := &issues_model.PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: issues_model.PullRequestStatusMergeable} | ||||||
| 	assert.NoError(t, NewPullRequest(db.DefaultContext, r, i, nil, nil, pr)) | 	assert.NoError(t, issues_model.NewPullRequest(db.DefaultContext, r, i, nil, nil, pr)) | ||||||
| 	pr.Issue = i | 	pr.Issue = i | ||||||
| 	return pr | 	return pr | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *Comment { | func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *issues_model.Comment { | ||||||
| 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | 	d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User) | ||||||
| 	i := unittest.AssertExistsAndLoadBean(t, &Issue{ID: issue}).(*Issue) | 	i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue}).(*issues_model.Issue) | ||||||
| 	c := &Comment{Type: CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} | 	c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} | ||||||
| 
 | 
 | ||||||
| 	ctx, committer, err := db.TxContext() | 	ctx, committer, err := db.TxContext() | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	defer committer.Close() | 	defer committer.Close() | ||||||
| 	err = db.Insert(ctx, c) | 	err = db.Insert(ctx, c) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NoError(t, c.addCrossReferences(ctx, d, false)) | 	assert.NoError(t, c.AddCrossReferences(ctx, d, false)) | ||||||
| 	assert.NoError(t, committer.Commit()) | 	assert.NoError(t, committer.Commit()) | ||||||
| 	return c | 	return c | ||||||
| } | } | ||||||
| @@ -3,7 +3,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -21,6 +21,53 @@ import ( | |||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrRepoLabelNotExist represents a "RepoLabelNotExist" kind of error. | ||||||
|  | type ErrRepoLabelNotExist struct { | ||||||
|  | 	LabelID int64 | ||||||
|  | 	RepoID  int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrRepoLabelNotExist checks if an error is a RepoErrLabelNotExist. | ||||||
|  | func IsErrRepoLabelNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrRepoLabelNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrRepoLabelNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error. | ||||||
|  | type ErrOrgLabelNotExist struct { | ||||||
|  | 	LabelID int64 | ||||||
|  | 	OrgID   int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrOrgLabelNotExist checks if an error is a OrgErrLabelNotExist. | ||||||
|  | func IsErrOrgLabelNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrOrgLabelNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrOrgLabelNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrLabelNotExist represents a "LabelNotExist" kind of error. | ||||||
|  | type ErrLabelNotExist struct { | ||||||
|  | 	LabelID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrLabelNotExist checks if an error is a ErrLabelNotExist. | ||||||
|  | func IsErrLabelNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrLabelNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrLabelNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // LabelColorPattern is a regexp witch can validate LabelColor | // LabelColorPattern is a regexp witch can validate LabelColor | ||||||
| var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") | var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$") | ||||||
| 
 | 
 | ||||||
| @@ -671,7 +718,8 @@ func DeleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use | |||||||
| 	return issue.LoadLabels(ctx) | 	return issue.LoadLabels(ctx) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func deleteLabelsByRepoID(ctx context.Context, repoID int64) error { | // DeleteLabelsByRepoID  deletes labels of some repository | ||||||
|  | func DeleteLabelsByRepoID(ctx context.Context, repoID int64) error { | ||||||
| 	deleteCond := builder.Select("id").From("label").Where(builder.Eq{"label.repo_id": repoID}) | 	deleteCond := builder.Select("id").From("label").Where(builder.Eq{"label.repo_id": repoID}) | ||||||
| 
 | 
 | ||||||
| 	if _, err := db.GetEngine(ctx).In("label_id", deleteCond). | 	if _, err := db.GetEngine(ctx).In("label_id", deleteCond). | ||||||
| @@ -682,3 +730,107 @@ func deleteLabelsByRepoID(ctx context.Context, repoID int64) error { | |||||||
| 	_, err := db.DeleteByBean(ctx, &Label{RepoID: repoID}) | 	_, err := db.DeleteByBean(ctx, &Label{RepoID: repoID}) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // CountOrphanedLabels return count of labels witch are broken and not accessible via ui anymore | ||||||
|  | func CountOrphanedLabels() (int64, error) { | ||||||
|  | 	noref, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Count("label.id") | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	norepo, err := db.GetEngine(db.DefaultContext).Table("label"). | ||||||
|  | 		Where(builder.And( | ||||||
|  | 			builder.Gt{"repo_id": 0}, | ||||||
|  | 			builder.NotIn("repo_id", builder.Select("id").From("repository")), | ||||||
|  | 		)). | ||||||
|  | 		Count() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	noorg, err := db.GetEngine(db.DefaultContext).Table("label"). | ||||||
|  | 		Where(builder.And( | ||||||
|  | 			builder.Gt{"org_id": 0}, | ||||||
|  | 			builder.NotIn("org_id", builder.Select("id").From("user")), | ||||||
|  | 		)). | ||||||
|  | 		Count() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return noref + norepo + noorg, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DeleteOrphanedLabels delete labels witch are broken and not accessible via ui anymore | ||||||
|  | func DeleteOrphanedLabels() error { | ||||||
|  | 	// delete labels with no reference | ||||||
|  | 	if _, err := db.GetEngine(db.DefaultContext).Table("label").Where("repo_id=? AND org_id=?", 0, 0).Delete(new(Label)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// delete labels with none existing repos | ||||||
|  | 	if _, err := db.GetEngine(db.DefaultContext). | ||||||
|  | 		Where(builder.And( | ||||||
|  | 			builder.Gt{"repo_id": 0}, | ||||||
|  | 			builder.NotIn("repo_id", builder.Select("id").From("repository")), | ||||||
|  | 		)). | ||||||
|  | 		Delete(Label{}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// delete labels with none existing orgs | ||||||
|  | 	if _, err := db.GetEngine(db.DefaultContext). | ||||||
|  | 		Where(builder.And( | ||||||
|  | 			builder.Gt{"org_id": 0}, | ||||||
|  | 			builder.NotIn("org_id", builder.Select("id").From("user")), | ||||||
|  | 		)). | ||||||
|  | 		Delete(Label{}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CountOrphanedIssueLabels return count of IssueLabels witch have no label behind anymore | ||||||
|  | func CountOrphanedIssueLabels() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).Table("issue_label"). | ||||||
|  | 		NotIn("label_id", builder.Select("id").From("label")). | ||||||
|  | 		Count() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // DeleteOrphanedIssueLabels delete IssueLabels witch have no label behind anymore | ||||||
|  | func DeleteOrphanedIssueLabels() error { | ||||||
|  | 	_, err := db.GetEngine(db.DefaultContext). | ||||||
|  | 		NotIn("label_id", builder.Select("id").From("label")). | ||||||
|  | 		Delete(IssueLabel{}) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CountIssueLabelWithOutsideLabels count label comments with outside label | ||||||
|  | func CountIssueLabelWithOutsideLabels() (int64, error) { | ||||||
|  | 	return db.GetEngine(db.DefaultContext).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"). | ||||||
|  | 		Join("inner", "label", "issue_label.label_id = label.id "). | ||||||
|  | 		Join("inner", "issue", "issue.id = issue_label.issue_id "). | ||||||
|  | 		Join("inner", "repository", "issue.repo_id = repository.id"). | ||||||
|  | 		Count(new(IssueLabel)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FixIssueLabelWithOutsideLabels fix label comments with outside label | ||||||
|  | func FixIssueLabelWithOutsideLabels() (int64, error) { | ||||||
|  | 	res, err := db.GetEngine(db.DefaultContext).Exec(`DELETE FROM issue_label WHERE issue_label.id IN ( | ||||||
|  | 		SELECT il_too.id FROM ( | ||||||
|  | 			SELECT il_too_too.id | ||||||
|  | 				FROM issue_label AS il_too_too | ||||||
|  | 					INNER JOIN label ON il_too_too.label_id = label.id | ||||||
|  | 					INNER JOIN issue on issue.id = il_too_too.issue_id | ||||||
|  | 					INNER JOIN repository on repository.id = issue.repo_id | ||||||
|  | 				WHERE | ||||||
|  | 					(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 )`) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return 0, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return res.RowsAffected() | ||||||
|  | } | ||||||
							
								
								
									
										395
									
								
								models/issues/label_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								models/issues/label_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,395 @@ | |||||||
|  | // Copyright 2017 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 issues_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"html/template" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
|  | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // TODO TestGetLabelTemplateFile | ||||||
|  |  | ||||||
|  | func TestLabel_CalOpenIssues(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	label.CalOpenIssues() | ||||||
|  | 	assert.EqualValues(t, 2, label.NumOpenIssues) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestLabel_ForegroundColor(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	assert.Equal(t, template.CSS("#000"), label.ForegroundColor()) | ||||||
|  |  | ||||||
|  | 	label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label) | ||||||
|  | 	assert.Equal(t, template.CSS("#fff"), label.ForegroundColor()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewLabels(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labels := []*issues_model.Label{ | ||||||
|  | 		{RepoID: 2, Name: "labelName2", Color: "#123456"}, | ||||||
|  | 		{RepoID: 3, Name: "labelName3", Color: "#123"}, | ||||||
|  | 		{RepoID: 4, Name: "labelName4", Color: "ABCDEF"}, | ||||||
|  | 		{RepoID: 5, Name: "labelName5", Color: "DEF"}, | ||||||
|  | 	} | ||||||
|  | 	assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: ""})) | ||||||
|  | 	assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "#45G"})) | ||||||
|  | 	assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "#12345G"})) | ||||||
|  | 	assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "45G"})) | ||||||
|  | 	assert.Error(t, issues_model.NewLabel(db.DefaultContext, &issues_model.Label{RepoID: 3, Name: "invalid Color", Color: "12345G"})) | ||||||
|  | 	for _, label := range labels { | ||||||
|  | 		unittest.AssertNotExistsBean(t, label) | ||||||
|  | 	} | ||||||
|  | 	assert.NoError(t, issues_model.NewLabels(labels...)) | ||||||
|  | 	for _, label := range labels { | ||||||
|  | 		unittest.AssertExistsAndLoadBean(t, label, unittest.Cond("id = ?", label.ID)) | ||||||
|  | 	} | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelByID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label, err := issues_model.GetLabelByID(db.DefaultContext, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 1, label.ID) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelByID(db.DefaultContext, unittest.NonexistentID) | ||||||
|  | 	assert.True(t, issues_model.IsErrLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInRepoByName(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label, err := issues_model.GetLabelInRepoByName(db.DefaultContext, 1, "label1") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 1, label.ID) | ||||||
|  | 	assert.Equal(t, "label1", label.Name) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInRepoByName(db.DefaultContext, 1, "") | ||||||
|  | 	assert.True(t, issues_model.IsErrRepoLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInRepoByName(db.DefaultContext, unittest.NonexistentID, "nonexistent") | ||||||
|  | 	assert.True(t, issues_model.IsErrRepoLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInRepoByNames(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labelIDs, err := issues_model.GetLabelIDsInRepoByNames(1, []string{"label1", "label2"}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	assert.Len(t, labelIDs, 2) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, int64(1), labelIDs[0]) | ||||||
|  | 	assert.Equal(t, int64(2), labelIDs[1]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInRepoByNamesDiscardsNonExistentLabels(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	// label3 doesn't exists.. See labels.yml | ||||||
|  | 	labelIDs, err := issues_model.GetLabelIDsInRepoByNames(1, []string{"label1", "label2", "label3"}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	assert.Len(t, labelIDs, 2) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, int64(1), labelIDs[0]) | ||||||
|  | 	assert.Equal(t, int64(2), labelIDs[1]) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInRepoByID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label, err := issues_model.GetLabelInRepoByID(db.DefaultContext, 1, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 1, label.ID) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInRepoByID(db.DefaultContext, 1, -1) | ||||||
|  | 	assert.True(t, issues_model.IsErrRepoLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInRepoByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) | ||||||
|  | 	assert.True(t, issues_model.IsErrRepoLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelsInRepoByIDs(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labels, err := issues_model.GetLabelsInRepoByIDs(1, []int64{1, 2, unittest.NonexistentID}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, labels, 2) { | ||||||
|  | 		assert.EqualValues(t, 1, labels[0].ID) | ||||||
|  | 		assert.EqualValues(t, 2, labels[1].ID) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelsByRepoID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) { | ||||||
|  | 		labels, err := issues_model.GetLabelsByRepoID(db.DefaultContext, repoID, sortType, db.ListOptions{}) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 		assert.Len(t, labels, len(expectedIssueIDs)) | ||||||
|  | 		for i, label := range labels { | ||||||
|  | 			assert.EqualValues(t, expectedIssueIDs[i], label.ID) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	testSuccess(1, "leastissues", []int64{2, 1}) | ||||||
|  | 	testSuccess(1, "mostissues", []int64{1, 2}) | ||||||
|  | 	testSuccess(1, "reversealphabetically", []int64{2, 1}) | ||||||
|  | 	testSuccess(1, "default", []int64{1, 2}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Org versions | ||||||
|  |  | ||||||
|  | func TestGetLabelInOrgByName(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label, err := issues_model.GetLabelInOrgByName(db.DefaultContext, 3, "orglabel3") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 3, label.ID) | ||||||
|  | 	assert.Equal(t, "orglabel3", label.Name) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, 3, "") | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, 0, "orglabel3") | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, -1, "orglabel3") | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByName(db.DefaultContext, unittest.NonexistentID, "nonexistent") | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInOrgByNames(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labelIDs, err := issues_model.GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4"}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	assert.Len(t, labelIDs, 2) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, int64(3), labelIDs[0]) | ||||||
|  | 	assert.Equal(t, int64(4), labelIDs[1]) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInOrgByNamesDiscardsNonExistentLabels(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	// orglabel99 doesn't exists.. See labels.yml | ||||||
|  | 	labelIDs, err := issues_model.GetLabelIDsInOrgByNames(3, []string{"orglabel3", "orglabel4", "orglabel99"}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	assert.Len(t, labelIDs, 2) | ||||||
|  |  | ||||||
|  | 	assert.Equal(t, int64(3), labelIDs[0]) | ||||||
|  | 	assert.Equal(t, int64(4), labelIDs[1]) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelInOrgByID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label, err := issues_model.GetLabelInOrgByID(db.DefaultContext, 3, 3) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 3, label.ID) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, 3, -1) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, 0, 3) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, -1, 3) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelInOrgByID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelsInOrgByIDs(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labels, err := issues_model.GetLabelsInOrgByIDs(3, []int64{3, 4, unittest.NonexistentID}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, labels, 2) { | ||||||
|  | 		assert.EqualValues(t, 3, labels[0].ID) | ||||||
|  | 		assert.EqualValues(t, 4, labels[1].ID) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestGetLabelsByOrgID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	testSuccess := func(orgID int64, sortType string, expectedIssueIDs []int64) { | ||||||
|  | 		labels, err := issues_model.GetLabelsByOrgID(db.DefaultContext, orgID, sortType, db.ListOptions{}) | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 		assert.Len(t, labels, len(expectedIssueIDs)) | ||||||
|  | 		for i, label := range labels { | ||||||
|  | 			assert.EqualValues(t, expectedIssueIDs[i], label.ID) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	testSuccess(3, "leastissues", []int64{3, 4}) | ||||||
|  | 	testSuccess(3, "mostissues", []int64{4, 3}) | ||||||
|  | 	testSuccess(3, "reversealphabetically", []int64{4, 3}) | ||||||
|  | 	testSuccess(3, "default", []int64{3, 4}) | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{}) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  |  | ||||||
|  | 	_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, -1, "leastissues", db.ListOptions{}) | ||||||
|  | 	assert.True(t, issues_model.IsErrOrgLabelNotExist(err)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // | ||||||
|  |  | ||||||
|  | func TestGetLabelsByIssueID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	labels, err := issues_model.GetLabelsByIssueID(db.DefaultContext, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	if assert.Len(t, labels, 1) { | ||||||
|  | 		assert.EqualValues(t, 1, labels[0].ID) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	labels, err = issues_model.GetLabelsByIssueID(db.DefaultContext, unittest.NonexistentID) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.Len(t, labels, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateLabel(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	// make sure update wont overwrite it | ||||||
|  | 	update := &issues_model.Label{ | ||||||
|  | 		ID:          label.ID, | ||||||
|  | 		Color:       "#ffff00", | ||||||
|  | 		Name:        "newLabelName", | ||||||
|  | 		Description: label.Description, | ||||||
|  | 	} | ||||||
|  | 	label.Color = update.Color | ||||||
|  | 	label.Name = update.Name | ||||||
|  | 	assert.NoError(t, issues_model.UpdateLabel(update)) | ||||||
|  | 	newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	assert.EqualValues(t, label.ID, newLabel.ID) | ||||||
|  | 	assert.EqualValues(t, label.Color, newLabel.Color) | ||||||
|  | 	assert.EqualValues(t, label.Name, newLabel.Name) | ||||||
|  | 	assert.EqualValues(t, label.Description, newLabel.Description) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDeleteLabel(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	assert.NoError(t, issues_model.DeleteLabel(label.RepoID, label.ID)) | ||||||
|  | 	unittest.AssertNotExistsBean(t, &issues_model.Label{ID: label.ID, RepoID: label.RepoID}) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.DeleteLabel(label.RepoID, label.ID)) | ||||||
|  | 	unittest.AssertNotExistsBean(t, &issues_model.Label{ID: label.ID}) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.DeleteLabel(unittest.NonexistentID, unittest.NonexistentID)) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Label{}, &repo_model.Repository{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHasIssueLabel(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	assert.True(t, issues_model.HasIssueLabel(db.DefaultContext, 1, 1)) | ||||||
|  | 	assert.False(t, issues_model.HasIssueLabel(db.DefaultContext, 1, 2)) | ||||||
|  | 	assert.False(t, issues_model.HasIssueLabel(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewIssueLabel(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label) | ||||||
|  | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
|  | 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
|  |  | ||||||
|  | 	// add new IssueLabel | ||||||
|  | 	prevNumIssues := label.NumIssues | ||||||
|  | 	assert.NoError(t, issues_model.NewIssueLabel(issue, label, doer)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label.ID}) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
|  | 		Type:     issues_model.CommentTypeLabel, | ||||||
|  | 		PosterID: doer.ID, | ||||||
|  | 		IssueID:  issue.ID, | ||||||
|  | 		LabelID:  label.ID, | ||||||
|  | 		Content:  "1", | ||||||
|  | 	}) | ||||||
|  | 	label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label) | ||||||
|  | 	assert.EqualValues(t, prevNumIssues+1, label.NumIssues) | ||||||
|  |  | ||||||
|  | 	// re-add existing IssueLabel | ||||||
|  | 	assert.NoError(t, issues_model.NewIssueLabel(issue, label, doer)) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestNewIssueLabels(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	label1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	label2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label) | ||||||
|  | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5}).(*issues_model.Issue) | ||||||
|  | 	doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.NewIssueLabels(issue, []*issues_model.Label{label1, label2}, doer)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
|  | 		Type:     issues_model.CommentTypeLabel, | ||||||
|  | 		PosterID: doer.ID, | ||||||
|  | 		IssueID:  issue.ID, | ||||||
|  | 		LabelID:  label1.ID, | ||||||
|  | 		Content:  "1", | ||||||
|  | 	}) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID}) | ||||||
|  | 	label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
|  | 	assert.EqualValues(t, 3, label1.NumIssues) | ||||||
|  | 	assert.EqualValues(t, 1, label1.NumClosedIssues) | ||||||
|  | 	label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label) | ||||||
|  | 	assert.EqualValues(t, 1, label2.NumIssues) | ||||||
|  | 	assert.EqualValues(t, 1, label2.NumClosedIssues) | ||||||
|  |  | ||||||
|  | 	// corner case: test empty slice | ||||||
|  | 	assert.NoError(t, issues_model.NewIssueLabels(issue, []*issues_model.Label{}, doer)) | ||||||
|  |  | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDeleteIssueLabel(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	testSuccess := func(labelID, issueID, doerID int64) { | ||||||
|  | 		label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label) | ||||||
|  | 		issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue) | ||||||
|  | 		doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User) | ||||||
|  |  | ||||||
|  | 		expectedNumIssues := label.NumIssues | ||||||
|  | 		expectedNumClosedIssues := label.NumClosedIssues | ||||||
|  | 		if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) { | ||||||
|  | 			expectedNumIssues-- | ||||||
|  | 			if issue.IsClosed { | ||||||
|  | 				expectedNumClosedIssues-- | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ctx, committer, err := db.TxContext() | ||||||
|  | 		defer committer.Close() | ||||||
|  | 		assert.NoError(t, err) | ||||||
|  | 		assert.NoError(t, issues_model.DeleteIssueLabel(ctx, issue, label, doer)) | ||||||
|  | 		assert.NoError(t, committer.Commit()) | ||||||
|  |  | ||||||
|  | 		unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) | ||||||
|  | 		unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ | ||||||
|  | 			Type:     issues_model.CommentTypeLabel, | ||||||
|  | 			PosterID: doerID, | ||||||
|  | 			IssueID:  issueID, | ||||||
|  | 			LabelID:  labelID, | ||||||
|  | 		}, `content=""`) | ||||||
|  | 		label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label) | ||||||
|  | 		assert.EqualValues(t, expectedNumIssues, label.NumIssues) | ||||||
|  | 		assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues) | ||||||
|  | 	} | ||||||
|  | 	testSuccess(1, 1, 2) | ||||||
|  | 	testSuccess(2, 5, 2) | ||||||
|  | 	testSuccess(1, 1, 2) // delete non-existent IssueLabel | ||||||
|  |  | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.Label{}) | ||||||
|  | } | ||||||
| @@ -2,14 +2,20 @@ | |||||||
| // 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. | ||||||
|  |  | ||||||
| package issues | package issues_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
|  | 	_ "code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
|  | 	_ "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	_ "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| @@ -17,14 +23,18 @@ func init() { | |||||||
| 	setting.LoadForTest() | 	setting.LoadForTest() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestFixturesAreConsistent(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	unittest.CheckConsistencyFor(t, | ||||||
|  | 		&issues_model.Issue{}, | ||||||
|  | 		&issues_model.PullRequest{}, | ||||||
|  | 		&issues_model.Milestone{}, | ||||||
|  | 		&issues_model.Label{}, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestMain(m *testing.M) { | func TestMain(m *testing.M) { | ||||||
| 	unittest.MainTest(m, &unittest.TestOptions{ | 	unittest.MainTest(m, &unittest.TestOptions{ | ||||||
| 		GiteaRootPath: filepath.Join("..", ".."), | 		GiteaRootPath: filepath.Join("..", ".."), | ||||||
| 		FixtureFiles: []string{ |  | ||||||
| 			"reaction.yml", |  | ||||||
| 			"user.yml", |  | ||||||
| 			"repository.yml", |  | ||||||
| 			"milestone.yml", |  | ||||||
| 		}, |  | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -292,11 +292,17 @@ func DeleteMilestoneByRepoID(repoID, id int64) error { | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	numMilestones, err := countRepoMilestones(ctx, repo.ID) | 	numMilestones, err := CountMilestones(ctx, GetMilestonesOption{ | ||||||
|  | 		RepoID: repo.ID, | ||||||
|  | 		State:  api.StateAll, | ||||||
|  | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	numClosedMilestones, err := countRepoClosedMilestones(ctx, repo.ID) | 	numClosedMilestones, err := CountMilestones(ctx, GetMilestonesOption{ | ||||||
|  | 		RepoID: repo.ID, | ||||||
|  | 		State:  api.StateClosed, | ||||||
|  | 	}) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -428,13 +434,6 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s | |||||||
| 	) | 	) | ||||||
| } | } | ||||||
|  |  | ||||||
| //  ____  _        _ |  | ||||||
| // / ___|| |_ __ _| |_ ___ |  | ||||||
| // \___ \| __/ _` | __/ __| |  | ||||||
| //  ___) | || (_| | |_\__ \ |  | ||||||
| // |____/ \__\__,_|\__|___/ |  | ||||||
| // |  | ||||||
|  |  | ||||||
| // MilestonesStats represents milestone statistic information. | // MilestonesStats represents milestone statistic information. | ||||||
| type MilestonesStats struct { | type MilestonesStats struct { | ||||||
| 	OpenCount, ClosedCount int64 | 	OpenCount, ClosedCount int64 | ||||||
| @@ -503,23 +502,13 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* | |||||||
| 	return stats, nil | 	return stats, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func countRepoMilestones(ctx context.Context, repoID int64) (int64, error) { | // CountMilestones returns number of milestones in given repository with other options | ||||||
|  | func CountMilestones(ctx context.Context, opts GetMilestonesOption) (int64, error) { | ||||||
| 	return db.GetEngine(ctx). | 	return db.GetEngine(ctx). | ||||||
| 		Where("repo_id=?", repoID). | 		Where(opts.toCond()). | ||||||
| 		Count(new(Milestone)) | 		Count(new(Milestone)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func countRepoClosedMilestones(ctx context.Context, repoID int64) (int64, error) { |  | ||||||
| 	return db.GetEngine(ctx). |  | ||||||
| 		Where("repo_id=? AND is_closed=?", repoID, true). |  | ||||||
| 		Count(new(Milestone)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountRepoClosedMilestones returns number of closed milestones in given repository. |  | ||||||
| func CountRepoClosedMilestones(repoID int64) (int64, error) { |  | ||||||
| 	return countRepoClosedMilestones(db.DefaultContext, repoID) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CountMilestonesByRepoCond map from repo conditions to number of milestones matching the options` | // CountMilestonesByRepoCond map from repo conditions to number of milestones matching the options` | ||||||
| func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) { | func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64]int64, error) { | ||||||
| 	sess := db.GetEngine(db.DefaultContext).Where("is_closed = ?", isClosed) | 	sess := db.GetEngine(db.DefaultContext).Where("is_closed = ?", isClosed) | ||||||
|   | |||||||
| @@ -2,44 +2,46 @@ | |||||||
| // 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. | ||||||
|  |  | ||||||
| package issues | package issues_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	"code.gitea.io/gitea/modules/setting" | 	"code.gitea.io/gitea/modules/setting" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
|  | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  |  | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func TestMilestone_State(t *testing.T) { | func TestMilestone_State(t *testing.T) { | ||||||
| 	assert.Equal(t, api.StateOpen, (&Milestone{IsClosed: false}).State()) | 	assert.Equal(t, api.StateOpen, (&issues_model.Milestone{IsClosed: false}).State()) | ||||||
| 	assert.Equal(t, api.StateClosed, (&Milestone{IsClosed: true}).State()) | 	assert.Equal(t, api.StateClosed, (&issues_model.Milestone{IsClosed: true}).State()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestGetMilestoneByRepoID(t *testing.T) { | func TestGetMilestoneByRepoID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
| 	milestone, err := GetMilestoneByRepoID(db.DefaultContext, 1, 1) | 	milestone, err := issues_model.GetMilestoneByRepoID(db.DefaultContext, 1, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 1, milestone.ID) | 	assert.EqualValues(t, 1, milestone.ID) | ||||||
| 	assert.EqualValues(t, 1, milestone.RepoID) | 	assert.EqualValues(t, 1, milestone.RepoID) | ||||||
|  |  | ||||||
| 	_, err = GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) | 	_, err = issues_model.GetMilestoneByRepoID(db.DefaultContext, unittest.NonexistentID, unittest.NonexistentID) | ||||||
| 	assert.True(t, IsErrMilestoneNotExist(err)) | 	assert.True(t, issues_model.IsErrMilestoneNotExist(err)) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestGetMilestonesByRepoID(t *testing.T) { | func TestGetMilestonesByRepoID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	test := func(repoID int64, state api.StateType) { | 	test := func(repoID int64, state api.StateType) { | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | ||||||
| 		milestones, _, err := GetMilestones(GetMilestonesOption{ | 		milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{ | ||||||
| 			RepoID: repo.ID, | 			RepoID: repo.ID, | ||||||
| 			State:  state, | 			State:  state, | ||||||
| 		}) | 		}) | ||||||
| @@ -76,7 +78,7 @@ func TestGetMilestonesByRepoID(t *testing.T) { | |||||||
| 	test(3, api.StateClosed) | 	test(3, api.StateClosed) | ||||||
| 	test(3, api.StateAll) | 	test(3, api.StateAll) | ||||||
|  |  | ||||||
| 	milestones, _, err := GetMilestones(GetMilestonesOption{ | 	milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{ | ||||||
| 		RepoID: unittest.NonexistentID, | 		RepoID: unittest.NonexistentID, | ||||||
| 		State:  api.StateOpen, | 		State:  api.StateOpen, | ||||||
| 	}) | 	}) | ||||||
| @@ -87,9 +89,9 @@ func TestGetMilestonesByRepoID(t *testing.T) { | |||||||
| func TestGetMilestones(t *testing.T) { | func TestGetMilestones(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	test := func(sortType string, sortCond func(*Milestone) int) { | 	test := func(sortType string, sortCond func(*issues_model.Milestone) int) { | ||||||
| 		for _, page := range []int{0, 1} { | 		for _, page := range []int{0, 1} { | ||||||
| 			milestones, _, err := GetMilestones(GetMilestonesOption{ | 			milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{ | ||||||
| 				ListOptions: db.ListOptions{ | 				ListOptions: db.ListOptions{ | ||||||
| 					Page:     page, | 					Page:     page, | ||||||
| 					PageSize: setting.UI.IssuePagingNum, | 					PageSize: setting.UI.IssuePagingNum, | ||||||
| @@ -106,7 +108,7 @@ func TestGetMilestones(t *testing.T) { | |||||||
| 			} | 			} | ||||||
| 			assert.True(t, sort.IntsAreSorted(values)) | 			assert.True(t, sort.IntsAreSorted(values)) | ||||||
|  |  | ||||||
| 			milestones, _, err = GetMilestones(GetMilestonesOption{ | 			milestones, _, err = issues_model.GetMilestones(issues_model.GetMilestonesOption{ | ||||||
| 				ListOptions: db.ListOptions{ | 				ListOptions: db.ListOptions{ | ||||||
| 					Page:     page, | 					Page:     page, | ||||||
| 					PageSize: setting.UI.IssuePagingNum, | 					PageSize: setting.UI.IssuePagingNum, | ||||||
| @@ -125,22 +127,22 @@ func TestGetMilestones(t *testing.T) { | |||||||
| 			assert.True(t, sort.IntsAreSorted(values)) | 			assert.True(t, sort.IntsAreSorted(values)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	test("furthestduedate", func(milestone *Milestone) int { | 	test("furthestduedate", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -int(milestone.DeadlineUnix) | 		return -int(milestone.DeadlineUnix) | ||||||
| 	}) | 	}) | ||||||
| 	test("leastcomplete", func(milestone *Milestone) int { | 	test("leastcomplete", func(milestone *issues_model.Milestone) int { | ||||||
| 		return milestone.Completeness | 		return milestone.Completeness | ||||||
| 	}) | 	}) | ||||||
| 	test("mostcomplete", func(milestone *Milestone) int { | 	test("mostcomplete", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -milestone.Completeness | 		return -milestone.Completeness | ||||||
| 	}) | 	}) | ||||||
| 	test("leastissues", func(milestone *Milestone) int { | 	test("leastissues", func(milestone *issues_model.Milestone) int { | ||||||
| 		return milestone.NumIssues | 		return milestone.NumIssues | ||||||
| 	}) | 	}) | ||||||
| 	test("mostissues", func(milestone *Milestone) int { | 	test("mostissues", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -milestone.NumIssues | 		return -milestone.NumIssues | ||||||
| 	}) | 	}) | ||||||
| 	test("soonestduedate", func(milestone *Milestone) int { | 	test("soonestduedate", func(milestone *issues_model.Milestone) int { | ||||||
| 		return int(milestone.DeadlineUnix) | 		return int(milestone.DeadlineUnix) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @@ -149,7 +151,10 @@ func TestCountRepoMilestones(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	test := func(repoID int64) { | 	test := func(repoID int64) { | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | ||||||
| 		count, err := countRepoMilestones(db.DefaultContext, repoID) | 		count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{ | ||||||
|  | 			RepoID: repoID, | ||||||
|  | 			State:  api.StateAll, | ||||||
|  | 		}) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		assert.EqualValues(t, repo.NumMilestones, count) | 		assert.EqualValues(t, repo.NumMilestones, count) | ||||||
| 	} | 	} | ||||||
| @@ -157,7 +162,10 @@ func TestCountRepoMilestones(t *testing.T) { | |||||||
| 	test(2) | 	test(2) | ||||||
| 	test(3) | 	test(3) | ||||||
|  |  | ||||||
| 	count, err := countRepoMilestones(db.DefaultContext, unittest.NonexistentID) | 	count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{ | ||||||
|  | 		RepoID: unittest.NonexistentID, | ||||||
|  | 		State:  api.StateAll, | ||||||
|  | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 0, count) | 	assert.EqualValues(t, 0, count) | ||||||
| } | } | ||||||
| @@ -166,7 +174,10 @@ func TestCountRepoClosedMilestones(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	test := func(repoID int64) { | 	test := func(repoID int64) { | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | ||||||
| 		count, err := CountRepoClosedMilestones(repoID) | 		count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{ | ||||||
|  | 			RepoID: repoID, | ||||||
|  | 			State:  api.StateClosed, | ||||||
|  | 		}) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		assert.EqualValues(t, repo.NumClosedMilestones, count) | 		assert.EqualValues(t, repo.NumClosedMilestones, count) | ||||||
| 	} | 	} | ||||||
| @@ -174,7 +185,10 @@ func TestCountRepoClosedMilestones(t *testing.T) { | |||||||
| 	test(2) | 	test(2) | ||||||
| 	test(3) | 	test(3) | ||||||
|  |  | ||||||
| 	count, err := CountRepoClosedMilestones(unittest.NonexistentID) | 	count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{ | ||||||
|  | 		RepoID: unittest.NonexistentID, | ||||||
|  | 		State:  api.StateClosed, | ||||||
|  | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 0, count) | 	assert.EqualValues(t, 0, count) | ||||||
| } | } | ||||||
| @@ -188,12 +202,12 @@ func TestCountMilestonesByRepoIDs(t *testing.T) { | |||||||
| 	repo1OpenCount, repo1ClosedCount := milestonesCount(1) | 	repo1OpenCount, repo1ClosedCount := milestonesCount(1) | ||||||
| 	repo2OpenCount, repo2ClosedCount := milestonesCount(2) | 	repo2OpenCount, repo2ClosedCount := milestonesCount(2) | ||||||
|  |  | ||||||
| 	openCounts, err := CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), false) | 	openCounts, err := issues_model.CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), false) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, repo1OpenCount, openCounts[1]) | 	assert.EqualValues(t, repo1OpenCount, openCounts[1]) | ||||||
| 	assert.EqualValues(t, repo2OpenCount, openCounts[2]) | 	assert.EqualValues(t, repo2OpenCount, openCounts[2]) | ||||||
|  |  | ||||||
| 	closedCounts, err := CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), true) | 	closedCounts, err := issues_model.CountMilestonesByRepoCond(builder.In("repo_id", []int64{1, 2}), true) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, repo1ClosedCount, closedCounts[1]) | 	assert.EqualValues(t, repo1ClosedCount, closedCounts[1]) | ||||||
| 	assert.EqualValues(t, repo2ClosedCount, closedCounts[2]) | 	assert.EqualValues(t, repo2ClosedCount, closedCounts[2]) | ||||||
| @@ -203,9 +217,9 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) | 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) | ||||||
| 	test := func(sortType string, sortCond func(*Milestone) int) { | 	test := func(sortType string, sortCond func(*issues_model.Milestone) int) { | ||||||
| 		for _, page := range []int{0, 1} { | 		for _, page := range []int{0, 1} { | ||||||
| 			openMilestones, err := GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, false, sortType) | 			openMilestones, err := issues_model.GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, false, sortType) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 			assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones) | 			assert.Len(t, openMilestones, repo1.NumOpenMilestones+repo2.NumOpenMilestones) | ||||||
| 			values := make([]int, len(openMilestones)) | 			values := make([]int, len(openMilestones)) | ||||||
| @@ -214,7 +228,7 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { | |||||||
| 			} | 			} | ||||||
| 			assert.True(t, sort.IntsAreSorted(values)) | 			assert.True(t, sort.IntsAreSorted(values)) | ||||||
|  |  | ||||||
| 			closedMilestones, err := GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, true, sortType) | 			closedMilestones, err := issues_model.GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, true, sortType) | ||||||
| 			assert.NoError(t, err) | 			assert.NoError(t, err) | ||||||
| 			assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones) | 			assert.Len(t, closedMilestones, repo1.NumClosedMilestones+repo2.NumClosedMilestones) | ||||||
| 			values = make([]int, len(closedMilestones)) | 			values = make([]int, len(closedMilestones)) | ||||||
| @@ -224,22 +238,22 @@ func TestGetMilestonesByRepoIDs(t *testing.T) { | |||||||
| 			assert.True(t, sort.IntsAreSorted(values)) | 			assert.True(t, sort.IntsAreSorted(values)) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	test("furthestduedate", func(milestone *Milestone) int { | 	test("furthestduedate", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -int(milestone.DeadlineUnix) | 		return -int(milestone.DeadlineUnix) | ||||||
| 	}) | 	}) | ||||||
| 	test("leastcomplete", func(milestone *Milestone) int { | 	test("leastcomplete", func(milestone *issues_model.Milestone) int { | ||||||
| 		return milestone.Completeness | 		return milestone.Completeness | ||||||
| 	}) | 	}) | ||||||
| 	test("mostcomplete", func(milestone *Milestone) int { | 	test("mostcomplete", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -milestone.Completeness | 		return -milestone.Completeness | ||||||
| 	}) | 	}) | ||||||
| 	test("leastissues", func(milestone *Milestone) int { | 	test("leastissues", func(milestone *issues_model.Milestone) int { | ||||||
| 		return milestone.NumIssues | 		return milestone.NumIssues | ||||||
| 	}) | 	}) | ||||||
| 	test("mostissues", func(milestone *Milestone) int { | 	test("mostissues", func(milestone *issues_model.Milestone) int { | ||||||
| 		return -milestone.NumIssues | 		return -milestone.NumIssues | ||||||
| 	}) | 	}) | ||||||
| 	test("soonestduedate", func(milestone *Milestone) int { | 	test("soonestduedate", func(milestone *issues_model.Milestone) int { | ||||||
| 		return int(milestone.DeadlineUnix) | 		return int(milestone.DeadlineUnix) | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| @@ -249,7 +263,7 @@ func TestGetMilestonesStats(t *testing.T) { | |||||||
|  |  | ||||||
| 	test := func(repoID int64) { | 	test := func(repoID int64) { | ||||||
| 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | 		repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository) | ||||||
| 		stats, err := GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": repoID})) | 		stats, err := issues_model.GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": repoID})) | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount) | 		assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount) | ||||||
| 		assert.EqualValues(t, repo.NumClosedMilestones, stats.ClosedCount) | 		assert.EqualValues(t, repo.NumClosedMilestones, stats.ClosedCount) | ||||||
| @@ -258,7 +272,7 @@ func TestGetMilestonesStats(t *testing.T) { | |||||||
| 	test(2) | 	test(2) | ||||||
| 	test(3) | 	test(3) | ||||||
|  |  | ||||||
| 	stats, err := GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": unittest.NonexistentID})) | 	stats, err := issues_model.GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": unittest.NonexistentID})) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, 0, stats.OpenCount) | 	assert.EqualValues(t, 0, stats.OpenCount) | ||||||
| 	assert.EqualValues(t, 0, stats.ClosedCount) | 	assert.EqualValues(t, 0, stats.ClosedCount) | ||||||
| @@ -266,8 +280,75 @@ func TestGetMilestonesStats(t *testing.T) { | |||||||
| 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) | 	repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository) | ||||||
|  |  | ||||||
| 	milestoneStats, err := GetMilestonesStatsByRepoCond(builder.In("repo_id", []int64{repo1.ID, repo2.ID})) | 	milestoneStats, err := issues_model.GetMilestonesStatsByRepoCond(builder.In("repo_id", []int64{repo1.ID, repo2.ID})) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount) | 	assert.EqualValues(t, repo1.NumOpenMilestones+repo2.NumOpenMilestones, milestoneStats.OpenCount) | ||||||
| 	assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount) | 	assert.EqualValues(t, repo1.NumClosedMilestones+repo2.NumClosedMilestones, milestoneStats.ClosedCount) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestNewMilestone(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	milestone := &issues_model.Milestone{ | ||||||
|  | 		RepoID:  1, | ||||||
|  | 		Name:    "milestoneName", | ||||||
|  | 		Content: "milestoneContent", | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.NewMilestone(milestone)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, milestone) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestChangeMilestoneStatus(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, true)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1") | ||||||
|  | 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, false)) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=0") | ||||||
|  | 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: milestone.RepoID}, &issues_model.Milestone{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestDeleteMilestoneByRepoID(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	assert.NoError(t, issues_model.DeleteMilestoneByRepoID(1, 1)) | ||||||
|  | 	unittest.AssertNotExistsBean(t, &issues_model.Milestone{ID: 1}) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: 1}) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.DeleteMilestoneByRepoID(unittest.NonexistentID, unittest.NonexistentID)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateMilestone(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) | ||||||
|  | 	milestone.Name = " newMilestoneName  " | ||||||
|  | 	milestone.Content = "newMilestoneContent" | ||||||
|  | 	assert.NoError(t, issues_model.UpdateMilestone(milestone, milestone.IsClosed)) | ||||||
|  | 	milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) | ||||||
|  | 	assert.EqualValues(t, "newMilestoneName", milestone.Name) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestUpdateMilestoneCounters(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{MilestoneID: 1}, | ||||||
|  | 		"is_closed=0").(*issues_model.Issue) | ||||||
|  |  | ||||||
|  | 	issue.IsClosed = true | ||||||
|  | 	issue.ClosedUnix = timeutil.TimeStampNow() | ||||||
|  | 	_, err := db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) | ||||||
|  |  | ||||||
|  | 	issue.IsClosed = false | ||||||
|  | 	issue.ClosedUnix = 0 | ||||||
|  | 	_, err = db.GetEngine(db.DefaultContext).ID(issue.ID).Cols("is_closed", "closed_unix").Update(issue) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.NoError(t, issues_model.UpdateMilestoneCounters(db.DefaultContext, issue.MilestoneID)) | ||||||
|  | 	unittest.CheckConsistencyFor(t, &issues_model.Milestone{}) | ||||||
|  | } | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -25,6 +25,83 @@ import ( | |||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrPullRequestNotExist represents a "PullRequestNotExist" kind of error. | ||||||
|  | type ErrPullRequestNotExist struct { | ||||||
|  | 	ID         int64 | ||||||
|  | 	IssueID    int64 | ||||||
|  | 	HeadRepoID int64 | ||||||
|  | 	BaseRepoID int64 | ||||||
|  | 	HeadBranch string | ||||||
|  | 	BaseBranch string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrPullRequestNotExist checks if an error is a ErrPullRequestNotExist. | ||||||
|  | func IsErrPullRequestNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrPullRequestNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrPullRequestNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("pull request does not exist [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", | ||||||
|  | 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error | ||||||
|  | type ErrPullRequestAlreadyExists struct { | ||||||
|  | 	ID         int64 | ||||||
|  | 	IssueID    int64 | ||||||
|  | 	HeadRepoID int64 | ||||||
|  | 	BaseRepoID int64 | ||||||
|  | 	HeadBranch string | ||||||
|  | 	BaseBranch string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrPullRequestAlreadyExists checks if an error is a ErrPullRequestAlreadyExists. | ||||||
|  | func IsErrPullRequestAlreadyExists(err error) bool { | ||||||
|  | 	_, ok := err.(ErrPullRequestAlreadyExists) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error does pretty-printing :D | ||||||
|  | func (err ErrPullRequestAlreadyExists) Error() string { | ||||||
|  | 	return fmt.Sprintf("pull request already exists for these targets [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]", | ||||||
|  | 		err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error | ||||||
|  | type ErrPullRequestHeadRepoMissing struct { | ||||||
|  | 	ID         int64 | ||||||
|  | 	HeadRepoID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrErrPullRequestHeadRepoMissing checks if an error is a ErrPullRequestHeadRepoMissing. | ||||||
|  | func IsErrErrPullRequestHeadRepoMissing(err error) bool { | ||||||
|  | 	_, ok := err.(ErrPullRequestHeadRepoMissing) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Error does pretty-printing :D | ||||||
|  | func (err ErrPullRequestHeadRepoMissing) Error() string { | ||||||
|  | 	return fmt.Sprintf("pull request head repo missing [id: %d, head_repo_id: %d]", | ||||||
|  | 		err.ID, err.HeadRepoID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrPullWasClosed is used close a closed pull request | ||||||
|  | type ErrPullWasClosed struct { | ||||||
|  | 	ID    int64 | ||||||
|  | 	Index int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed. | ||||||
|  | func IsErrPullWasClosed(err error) bool { | ||||||
|  | 	_, ok := err.(ErrPullWasClosed) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrPullWasClosed) Error() string { | ||||||
|  | 	return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // PullRequestType defines pull request type | // PullRequestType defines pull request type | ||||||
| type PullRequestType int | type PullRequestType int | ||||||
| 
 | 
 | ||||||
| @@ -98,7 +175,8 @@ func init() { | |||||||
| 	db.RegisterModel(new(PullRequest)) | 	db.RegisterModel(new(PullRequest)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func deletePullsByBaseRepoID(ctx context.Context, repoID int64) error { | // DeletePullsByBaseRepoID deletes all pull requests by the base repository ID | ||||||
|  | func DeletePullsByBaseRepoID(ctx context.Context, repoID int64) error { | ||||||
| 	deleteCond := builder.Select("id").From("pull_request").Where(builder.Eq{"pull_request.base_repo_id": repoID}) | 	deleteCond := builder.Select("id").From("pull_request").Where(builder.Eq{"pull_request.base_repo_id": repoID}) | ||||||
| 
 | 
 | ||||||
| 	// Delete scheduled auto merges | 	// Delete scheduled auto merges | ||||||
| @@ -219,7 +297,7 @@ func (pr *PullRequest) LoadIssueCtx(ctx context.Context) (err error) { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pr.Issue, err = getIssueByID(ctx, pr.IssueID) | 	pr.Issue, err = GetIssueByID(ctx, pr.IssueID) | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		pr.Issue.PullRequest = pr | 		pr.Issue.PullRequest = pr | ||||||
| 	} | 	} | ||||||
| @@ -420,14 +498,14 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue | |||||||
| 	defer committer.Close() | 	defer committer.Close() | ||||||
| 	ctx.WithContext(outerCtx) | 	ctx.WithContext(outerCtx) | ||||||
| 
 | 
 | ||||||
| 	if err = newIssue(ctx, issue.Poster, NewIssueOptions{ | 	if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{ | ||||||
| 		Repo:        repo, | 		Repo:        repo, | ||||||
| 		Issue:       issue, | 		Issue:       issue, | ||||||
| 		LabelIDs:    labelIDs, | 		LabelIDs:    labelIDs, | ||||||
| 		Attachments: uuids, | 		Attachments: uuids, | ||||||
| 		IsPull:      true, | 		IsPull:      true, | ||||||
| 	}); err != nil { | 	}); err != nil { | ||||||
| 		if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { | 		if repo_model.IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		return fmt.Errorf("newIssue: %v", err) | 		return fmt.Errorf("newIssue: %v", err) | ||||||
| @@ -691,3 +769,70 @@ func (pr *PullRequest) Mergeable() bool { | |||||||
| 	return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict && | 	return pr.Status != PullRequestStatusChecking && pr.Status != PullRequestStatusConflict && | ||||||
| 		pr.Status != PullRequestStatusError && !pr.IsWorkInProgress() | 		pr.Status != PullRequestStatusError && !pr.IsWorkInProgress() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // HasEnoughApprovals returns true if pr has enough granted approvals. | ||||||
|  | func HasEnoughApprovals(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { | ||||||
|  | 	if protectBranch.RequiredApprovals == 0 { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return GetGrantedApprovalsCount(ctx, protectBranch, pr) >= protectBranch.RequiredApprovals | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetGrantedApprovalsCount returns the number of granted approvals for pr. A granted approval must be authored by a user in an approval whitelist. | ||||||
|  | func GetGrantedApprovalsCount(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) int64 { | ||||||
|  | 	sess := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). | ||||||
|  | 		And("type = ?", ReviewTypeApprove). | ||||||
|  | 		And("official = ?", true). | ||||||
|  | 		And("dismissed = ?", false) | ||||||
|  | 	if protectBranch.DismissStaleApprovals { | ||||||
|  | 		sess = sess.And("stale = ?", false) | ||||||
|  | 	} | ||||||
|  | 	approvals, err := sess.Count(new(Review)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("GetGrantedApprovalsCount: %v", err) | ||||||
|  | 		return 0 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return approvals | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MergeBlockedByRejectedReview returns true if merge is blocked by rejected reviews | ||||||
|  | func MergeBlockedByRejectedReview(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { | ||||||
|  | 	if !protectBranch.BlockOnRejectedReviews { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	rejectExist, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). | ||||||
|  | 		And("type = ?", ReviewTypeReject). | ||||||
|  | 		And("official = ?", true). | ||||||
|  | 		And("dismissed = ?", false). | ||||||
|  | 		Exist(new(Review)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("MergeBlockedByRejectedReview: %v", err) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return rejectExist | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MergeBlockedByOfficialReviewRequests block merge because of some review request to official reviewer | ||||||
|  | // of from official review | ||||||
|  | func MergeBlockedByOfficialReviewRequests(ctx context.Context, protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { | ||||||
|  | 	if !protectBranch.BlockOnOfficialReviewRequests { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	has, err := db.GetEngine(ctx).Where("issue_id = ?", pr.IssueID). | ||||||
|  | 		And("type = ?", ReviewTypeRequest). | ||||||
|  | 		And("official = ?", true). | ||||||
|  | 		Exist(new(Review)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("MergeBlockedByOfficialReviewRequests: %v", err) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return has | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // MergeBlockedByOutdatedBranch returns true if merge is blocked by an outdated head branch | ||||||
|  | func MergeBlockedByOutdatedBranch(protectBranch *git_model.ProtectedBranch, pr *PullRequest) bool { | ||||||
|  | 	return protectBranch.BlockOnOutdatedBranch && pr.CommitsBehind > 0 | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| @@ -15,7 +16,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| func TestPullRequest_LoadAttributes(t *testing.T) { | func TestPullRequest_LoadAttributes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadAttributes()) | 	assert.NoError(t, pr.LoadAttributes()) | ||||||
| 	assert.NotNil(t, pr.Merger) | 	assert.NotNil(t, pr.Merger) | ||||||
| 	assert.Equal(t, pr.MergerID, pr.Merger.ID) | 	assert.Equal(t, pr.MergerID, pr.Merger.ID) | ||||||
| @@ -23,7 +24,7 @@ func TestPullRequest_LoadAttributes(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequest_LoadIssue(t *testing.T) { | func TestPullRequest_LoadIssue(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadIssue()) | 	assert.NoError(t, pr.LoadIssue()) | ||||||
| 	assert.NotNil(t, pr.Issue) | 	assert.NotNil(t, pr.Issue) | ||||||
| 	assert.Equal(t, int64(2), pr.Issue.ID) | 	assert.Equal(t, int64(2), pr.Issue.ID) | ||||||
| @@ -34,7 +35,7 @@ func TestPullRequest_LoadIssue(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequest_LoadBaseRepo(t *testing.T) { | func TestPullRequest_LoadBaseRepo(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadBaseRepo()) | 	assert.NoError(t, pr.LoadBaseRepo()) | ||||||
| 	assert.NotNil(t, pr.BaseRepo) | 	assert.NotNil(t, pr.BaseRepo) | ||||||
| 	assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | 	assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID) | ||||||
| @@ -45,7 +46,7 @@ func TestPullRequest_LoadBaseRepo(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequest_LoadHeadRepo(t *testing.T) { | func TestPullRequest_LoadHeadRepo(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadHeadRepo()) | 	assert.NoError(t, pr.LoadHeadRepo()) | ||||||
| 	assert.NotNil(t, pr.HeadRepo) | 	assert.NotNil(t, pr.HeadRepo) | ||||||
| 	assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) | 	assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID) | ||||||
| @@ -57,7 +58,7 @@ func TestPullRequest_LoadHeadRepo(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequestsNewest(t *testing.T) { | func TestPullRequestsNewest(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	prs, count, err := PullRequests(1, &PullRequestsOptions{ | 	prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{ | ||||||
| 		ListOptions: db.ListOptions{ | 		ListOptions: db.ListOptions{ | ||||||
| 			Page: 1, | 			Page: 1, | ||||||
| 		}, | 		}, | ||||||
| @@ -76,7 +77,7 @@ func TestPullRequestsNewest(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequestsOldest(t *testing.T) { | func TestPullRequestsOldest(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	prs, count, err := PullRequests(1, &PullRequestsOptions{ | 	prs, count, err := issues_model.PullRequests(1, &issues_model.PullRequestsOptions{ | ||||||
| 		ListOptions: db.ListOptions{ | 		ListOptions: db.ListOptions{ | ||||||
| 			Page: 1, | 			Page: 1, | ||||||
| 		}, | 		}, | ||||||
| @@ -95,30 +96,30 @@ func TestPullRequestsOldest(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestGetUnmergedPullRequest(t *testing.T) { | func TestGetUnmergedPullRequest(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr, err := GetUnmergedPullRequest(1, 1, "branch2", "master", PullRequestFlowGithub) | 	pr, err := issues_model.GetUnmergedPullRequest(1, 1, "branch2", "master", issues_model.PullRequestFlowGithub) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(2), pr.ID) | 	assert.Equal(t, int64(2), pr.ID) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master", PullRequestFlowGithub) | 	_, err = issues_model.GetUnmergedPullRequest(1, 9223372036854775807, "branch1", "master", issues_model.PullRequestFlowGithub) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrPullRequestNotExist(err)) | 	assert.True(t, issues_model.IsErrPullRequestNotExist(err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) { | func TestHasUnmergedPullRequestsByHeadInfo(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	exist, err := HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "branch2") | 	exist, err := issues_model.HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "branch2") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, true, exist) | 	assert.Equal(t, true, exist) | ||||||
| 
 | 
 | ||||||
| 	exist, err = HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "not_exist_branch") | 	exist, err = issues_model.HasUnmergedPullRequestsByHeadInfo(db.DefaultContext, 1, "not_exist_branch") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, false, exist) | 	assert.Equal(t, false, exist) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { | func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	prs, err := GetUnmergedPullRequestsByHeadInfo(1, "branch2") | 	prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(1, "branch2") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, prs, 1) | 	assert.Len(t, prs, 1) | ||||||
| 	for _, pr := range prs { | 	for _, pr := range prs { | ||||||
| @@ -129,7 +130,7 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { | func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	prs, err := GetUnmergedPullRequestsByBaseInfo(1, "master") | 	prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(1, "master") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, prs, 1) | 	assert.Len(t, prs, 1) | ||||||
| 	pr := prs[0] | 	pr := prs[0] | ||||||
| @@ -140,51 +141,51 @@ func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestGetPullRequestByIndex(t *testing.T) { | func TestGetPullRequestByIndex(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr, err := GetPullRequestByIndex(db.DefaultContext, 1, 2) | 	pr, err := issues_model.GetPullRequestByIndex(db.DefaultContext, 1, 2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(1), pr.BaseRepoID) | 	assert.Equal(t, int64(1), pr.BaseRepoID) | ||||||
| 	assert.Equal(t, int64(2), pr.Index) | 	assert.Equal(t, int64(2), pr.Index) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetPullRequestByIndex(db.DefaultContext, 9223372036854775807, 9223372036854775807) | 	_, err = issues_model.GetPullRequestByIndex(db.DefaultContext, 9223372036854775807, 9223372036854775807) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrPullRequestNotExist(err)) | 	assert.True(t, issues_model.IsErrPullRequestNotExist(err)) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetPullRequestByIndex(db.DefaultContext, 1, 0) | 	_, err = issues_model.GetPullRequestByIndex(db.DefaultContext, 1, 0) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrPullRequestNotExist(err)) | 	assert.True(t, issues_model.IsErrPullRequestNotExist(err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestGetPullRequestByID(t *testing.T) { | func TestGetPullRequestByID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr, err := GetPullRequestByID(db.DefaultContext, 1) | 	pr, err := issues_model.GetPullRequestByID(db.DefaultContext, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(1), pr.ID) | 	assert.Equal(t, int64(1), pr.ID) | ||||||
| 	assert.Equal(t, int64(2), pr.IssueID) | 	assert.Equal(t, int64(2), pr.IssueID) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetPullRequestByID(db.DefaultContext, 9223372036854775807) | 	_, err = issues_model.GetPullRequestByID(db.DefaultContext, 9223372036854775807) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrPullRequestNotExist(err)) | 	assert.True(t, issues_model.IsErrPullRequestNotExist(err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestGetPullRequestByIssueID(t *testing.T) { | func TestGetPullRequestByIssueID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr, err := GetPullRequestByIssueID(db.DefaultContext, 2) | 	pr, err := issues_model.GetPullRequestByIssueID(db.DefaultContext, 2) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(2), pr.IssueID) | 	assert.Equal(t, int64(2), pr.IssueID) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807) | 	_, err = issues_model.GetPullRequestByIssueID(db.DefaultContext, 9223372036854775807) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrPullRequestNotExist(err)) | 	assert.True(t, issues_model.IsErrPullRequestNotExist(err)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestPullRequest_Update(t *testing.T) { | func TestPullRequest_Update(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	pr.BaseBranch = "baseBranch" | 	pr.BaseBranch = "baseBranch" | ||||||
| 	pr.HeadBranch = "headBranch" | 	pr.HeadBranch = "headBranch" | ||||||
| 	pr.Update() | 	pr.Update() | ||||||
| 
 | 
 | ||||||
| 	pr = unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: pr.ID}).(*PullRequest) | 	pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}).(*issues_model.PullRequest) | ||||||
| 	assert.Equal(t, "baseBranch", pr.BaseBranch) | 	assert.Equal(t, "baseBranch", pr.BaseBranch) | ||||||
| 	assert.Equal(t, "headBranch", pr.HeadBranch) | 	assert.Equal(t, "headBranch", pr.HeadBranch) | ||||||
| 	unittest.CheckConsistencyFor(t, pr) | 	unittest.CheckConsistencyFor(t, pr) | ||||||
| @@ -192,14 +193,14 @@ func TestPullRequest_Update(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestPullRequest_UpdateCols(t *testing.T) { | func TestPullRequest_UpdateCols(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	pr := &PullRequest{ | 	pr := &issues_model.PullRequest{ | ||||||
| 		ID:         1, | 		ID:         1, | ||||||
| 		BaseBranch: "baseBranch", | 		BaseBranch: "baseBranch", | ||||||
| 		HeadBranch: "headBranch", | 		HeadBranch: "headBranch", | ||||||
| 	} | 	} | ||||||
| 	assert.NoError(t, pr.UpdateCols("head_branch")) | 	assert.NoError(t, pr.UpdateCols("head_branch")) | ||||||
| 
 | 
 | ||||||
| 	pr = unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest) | 	pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.Equal(t, "master", pr.BaseBranch) | 	assert.Equal(t, "master", pr.BaseBranch) | ||||||
| 	assert.Equal(t, "headBranch", pr.HeadBranch) | 	assert.Equal(t, "headBranch", pr.HeadBranch) | ||||||
| 	unittest.CheckConsistencyFor(t, pr) | 	unittest.CheckConsistencyFor(t, pr) | ||||||
| @@ -208,17 +209,17 @@ func TestPullRequest_UpdateCols(t *testing.T) { | |||||||
| func TestPullRequestList_LoadAttributes(t *testing.T) { | func TestPullRequestList_LoadAttributes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	prs := []*PullRequest{ | 	prs := []*issues_model.PullRequest{ | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 1}).(*PullRequest), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest), | ||||||
| 		unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest), | 		unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest), | ||||||
| 	} | 	} | ||||||
| 	assert.NoError(t, PullRequestList(prs).LoadAttributes()) | 	assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes()) | ||||||
| 	for _, pr := range prs { | 	for _, pr := range prs { | ||||||
| 		assert.NotNil(t, pr.Issue) | 		assert.NotNil(t, pr.Issue) | ||||||
| 		assert.Equal(t, pr.IssueID, pr.Issue.ID) | 		assert.Equal(t, pr.IssueID, pr.Issue.ID) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, PullRequestList([]*PullRequest{}).LoadAttributes()) | 	assert.NoError(t, issues_model.PullRequestList([]*issues_model.PullRequest{}).LoadAttributes()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO TestAddTestPullRequestTask | // TODO TestAddTestPullRequestTask | ||||||
| @@ -226,7 +227,7 @@ func TestPullRequestList_LoadAttributes(t *testing.T) { | |||||||
| func TestPullRequest_IsWorkInProgress(t *testing.T) { | func TestPullRequest_IsWorkInProgress(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) | ||||||
| 	pr.LoadIssue() | 	pr.LoadIssue() | ||||||
| 
 | 
 | ||||||
| 	assert.False(t, pr.IsWorkInProgress()) | 	assert.False(t, pr.IsWorkInProgress()) | ||||||
| @@ -241,7 +242,7 @@ func TestPullRequest_IsWorkInProgress(t *testing.T) { | |||||||
| func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) { | func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) | ||||||
| 	pr.LoadIssue() | 	pr.LoadIssue() | ||||||
| 
 | 
 | ||||||
| 	assert.Empty(t, pr.GetWorkInProgressPrefix()) | 	assert.Empty(t, pr.GetWorkInProgressPrefix()) | ||||||
| @@ -253,3 +254,24 @@ func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) { | |||||||
| 	pr.Issue.Title = "[wip] " + original | 	pr.Issue.Title = "[wip] " + original | ||||||
| 	assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix()) | 	assert.Equal(t, "[wip]", pr.GetWorkInProgressPrefix()) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestDeleteOrphanedObjects(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  | 
 | ||||||
|  | 	countBefore, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	_, err = db.GetEngine(db.DefaultContext).Insert(&issues_model.PullRequest{IssueID: 1000}, &issues_model.PullRequest{IssueID: 1001}, &issues_model.PullRequest{IssueID: 1003}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	orphaned, err := db.CountOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, 3, orphaned) | ||||||
|  | 
 | ||||||
|  | 	err = db.DeleteOrphanedObjects("pull_request", "issue", "pull_request.issue_id=issue.id") | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	countAfter, err := db.GetEngine(db.DefaultContext).Count(&issues_model.PullRequest{}) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.EqualValues(t, countBefore, countAfter) | ||||||
|  | } | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
|  |  | ||||||
| package issues | package issues_test | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -17,12 +18,12 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func addReaction(t *testing.T, doerID, issueID, commentID int64, content string) { | func addReaction(t *testing.T, doerID, issueID, commentID int64, content string) { | ||||||
| 	var reaction *Reaction | 	var reaction *issues_model.Reaction | ||||||
| 	var err error | 	var err error | ||||||
| 	if commentID == 0 { | 	if commentID == 0 { | ||||||
| 		reaction, err = CreateIssueReaction(doerID, issueID, content) | 		reaction, err = issues_model.CreateIssueReaction(doerID, issueID, content) | ||||||
| 	} else { | 	} else { | ||||||
| 		reaction, err = CreateCommentReaction(doerID, issueID, commentID, content) | 		reaction, err = issues_model.CreateCommentReaction(doerID, issueID, commentID, content) | ||||||
| 	} | 	} | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NotNil(t, reaction) | 	assert.NotNil(t, reaction) | ||||||
| @@ -37,7 +38,7 @@ func TestIssueAddReaction(t *testing.T) { | |||||||
|  |  | ||||||
| 	addReaction(t, user1.ID, issue1ID, 0, "heart") | 	addReaction(t, user1.ID, issue1ID, 0, "heart") | ||||||
|  |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestIssueAddDuplicateReaction(t *testing.T) { | func TestIssueAddDuplicateReaction(t *testing.T) { | ||||||
| @@ -49,15 +50,15 @@ func TestIssueAddDuplicateReaction(t *testing.T) { | |||||||
|  |  | ||||||
| 	addReaction(t, user1.ID, issue1ID, 0, "heart") | 	addReaction(t, user1.ID, issue1ID, 0, "heart") | ||||||
|  |  | ||||||
| 	reaction, err := CreateReaction(&ReactionOptions{ | 	reaction, err := issues_model.CreateReaction(&issues_model.ReactionOptions{ | ||||||
| 		DoerID:  user1.ID, | 		DoerID:  user1.ID, | ||||||
| 		IssueID: issue1ID, | 		IssueID: issue1ID, | ||||||
| 		Type:    "heart", | 		Type:    "heart", | ||||||
| 	}) | 	}) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.Equal(t, ErrReactionAlreadyExist{Reaction: "heart"}, err) | 	assert.Equal(t, issues_model.ErrReactionAlreadyExist{Reaction: "heart"}, err) | ||||||
|  |  | ||||||
| 	existingR := unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}).(*Reaction) | 	existingR := unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}).(*issues_model.Reaction) | ||||||
| 	assert.Equal(t, existingR.ID, reaction.ID) | 	assert.Equal(t, existingR.ID, reaction.ID) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -70,10 +71,10 @@ func TestIssueDeleteReaction(t *testing.T) { | |||||||
|  |  | ||||||
| 	addReaction(t, user1.ID, issue1ID, 0, "heart") | 	addReaction(t, user1.ID, issue1ID, 0, "heart") | ||||||
|  |  | ||||||
| 	err := DeleteIssueReaction(user1.ID, issue1ID, "heart") | 	err := issues_model.DeleteIssueReaction(user1.ID, issue1ID, "heart") | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}) | 	unittest.AssertNotExistsBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestIssueReactionCount(t *testing.T) { | func TestIssueReactionCount(t *testing.T) { | ||||||
| @@ -98,7 +99,7 @@ func TestIssueReactionCount(t *testing.T) { | |||||||
| 	addReaction(t, user4.ID, issueID, 0, "heart") | 	addReaction(t, user4.ID, issueID, 0, "heart") | ||||||
| 	addReaction(t, ghost.ID, issueID, 0, "-1") | 	addReaction(t, ghost.ID, issueID, 0, "-1") | ||||||
|  |  | ||||||
| 	reactionsList, _, err := FindReactions(db.DefaultContext, FindReactionsOptions{ | 	reactionsList, _, err := issues_model.FindReactions(db.DefaultContext, issues_model.FindReactionsOptions{ | ||||||
| 		IssueID: issueID, | 		IssueID: issueID, | ||||||
| 	}) | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| @@ -128,7 +129,7 @@ func TestIssueCommentAddReaction(t *testing.T) { | |||||||
|  |  | ||||||
| 	addReaction(t, user1.ID, issue1ID, comment1ID, "heart") | 	addReaction(t, user1.ID, issue1ID, comment1ID, "heart") | ||||||
|  |  | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestIssueCommentDeleteReaction(t *testing.T) { | func TestIssueCommentDeleteReaction(t *testing.T) { | ||||||
| @@ -147,7 +148,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) { | |||||||
| 	addReaction(t, user3.ID, issue1ID, comment1ID, "heart") | 	addReaction(t, user3.ID, issue1ID, comment1ID, "heart") | ||||||
| 	addReaction(t, user4.ID, issue1ID, comment1ID, "+1") | 	addReaction(t, user4.ID, issue1ID, comment1ID, "+1") | ||||||
|  |  | ||||||
| 	reactionsList, _, err := FindReactions(db.DefaultContext, FindReactionsOptions{ | 	reactionsList, _, err := issues_model.FindReactions(db.DefaultContext, issues_model.FindReactionsOptions{ | ||||||
| 		IssueID:   issue1ID, | 		IssueID:   issue1ID, | ||||||
| 		CommentID: comment1ID, | 		CommentID: comment1ID, | ||||||
| 	}) | 	}) | ||||||
| @@ -168,7 +169,7 @@ func TestIssueCommentReactionCount(t *testing.T) { | |||||||
| 	var comment1ID int64 = 1 | 	var comment1ID int64 = 1 | ||||||
|  |  | ||||||
| 	addReaction(t, user1.ID, issue1ID, comment1ID, "heart") | 	addReaction(t, user1.ID, issue1ID, comment1ID, "heart") | ||||||
| 	assert.NoError(t, DeleteCommentReaction(user1.ID, issue1ID, comment1ID, "heart")) | 	assert.NoError(t, issues_model.DeleteCommentReaction(user1.ID, issue1ID, comment1ID, "heart")) | ||||||
|  |  | ||||||
| 	unittest.AssertNotExistsBean(t, &Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID}) | 	unittest.AssertNotExistsBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID, CommentID: comment1ID}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -17,11 +17,47 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/base" | 	"code.gitea.io/gitea/modules/base" | ||||||
|  | 	"code.gitea.io/gitea/modules/structs" | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
| 
 | 
 | ||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ErrReviewNotExist represents a "ReviewNotExist" kind of error. | ||||||
|  | type ErrReviewNotExist struct { | ||||||
|  | 	ID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrReviewNotExist checks if an error is a ErrReviewNotExist. | ||||||
|  | func IsErrReviewNotExist(err error) bool { | ||||||
|  | 	_, ok := err.(ErrReviewNotExist) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrReviewNotExist) Error() string { | ||||||
|  | 	return fmt.Sprintf("review does not exist [id: %d]", err.ID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ErrNotValidReviewRequest an not allowed review request modify | ||||||
|  | type ErrNotValidReviewRequest struct { | ||||||
|  | 	Reason string | ||||||
|  | 	UserID int64 | ||||||
|  | 	RepoID int64 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsErrNotValidReviewRequest checks if an error is a ErrNotValidReviewRequest. | ||||||
|  | func IsErrNotValidReviewRequest(err error) bool { | ||||||
|  | 	_, ok := err.(ErrNotValidReviewRequest) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (err ErrNotValidReviewRequest) Error() string { | ||||||
|  | 	return fmt.Sprintf("%s [user_id: %d, repo_id: %d]", | ||||||
|  | 		err.Reason, | ||||||
|  | 		err.UserID, | ||||||
|  | 		err.RepoID) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // ReviewType defines the sort of feedback a review gives | // ReviewType defines the sort of feedback a review gives | ||||||
| type ReviewType int | type ReviewType int | ||||||
| 
 | 
 | ||||||
| @@ -105,7 +141,7 @@ func (r *Review) loadIssue(ctx context.Context) (err error) { | |||||||
| 	if r.Issue != nil { | 	if r.Issue != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	r.Issue, err = getIssueByID(ctx, r.IssueID) | 	r.Issue, err = GetIssueByID(ctx, r.IssueID) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -967,3 +1003,16 @@ func (r *Review) GetExternalName() string { return r.OriginalAuthor } | |||||||
| 
 | 
 | ||||||
| // GetExternalID ExternalUserRemappable interface | // GetExternalID ExternalUserRemappable interface | ||||||
| func (r *Review) GetExternalID() int64 { return r.OriginalAuthorID } | func (r *Review) GetExternalID() int64 { return r.OriginalAuthorID } | ||||||
|  | 
 | ||||||
|  | // UpdateReviewsMigrationsByType updates reviews' migrations information via given git service type and original id and poster id | ||||||
|  | func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error { | ||||||
|  | 	_, err := db.GetEngine(db.DefaultContext).Table("review"). | ||||||
|  | 		Where("original_author_id = ?", originalAuthorID). | ||||||
|  | 		And(migratedIssueCond(tp)). | ||||||
|  | 		Update(map[string]interface{}{ | ||||||
|  | 			"reviewer_id":        posterID, | ||||||
|  | 			"original_author":    "", | ||||||
|  | 			"original_author_id": 0, | ||||||
|  | 		}) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
| @@ -2,12 +2,13 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 
 | 
 | ||||||
| @@ -16,34 +17,34 @@ import ( | |||||||
| 
 | 
 | ||||||
| func TestGetReviewByID(t *testing.T) { | func TestGetReviewByID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	review, err := GetReviewByID(db.DefaultContext, 1) | 	review, err := issues_model.GetReviewByID(db.DefaultContext, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, "Demo Review", review.Content) | 	assert.Equal(t, "Demo Review", review.Content) | ||||||
| 	assert.Equal(t, ReviewTypeApprove, review.Type) | 	assert.Equal(t, issues_model.ReviewTypeApprove, review.Type) | ||||||
| 
 | 
 | ||||||
| 	_, err = GetReviewByID(db.DefaultContext, 23892) | 	_, err = issues_model.GetReviewByID(db.DefaultContext, 23892) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrReviewNotExist(err), "IsErrReviewNotExist") | 	assert.True(t, issues_model.IsErrReviewNotExist(err), "IsErrReviewNotExist") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestReview_LoadAttributes(t *testing.T) { | func TestReview_LoadAttributes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	review := unittest.AssertExistsAndLoadBean(t, &Review{ID: 1}).(*Review) | 	review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1}).(*issues_model.Review) | ||||||
| 	assert.NoError(t, review.LoadAttributes(db.DefaultContext)) | 	assert.NoError(t, review.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.NotNil(t, review.Issue) | 	assert.NotNil(t, review.Issue) | ||||||
| 	assert.NotNil(t, review.Reviewer) | 	assert.NotNil(t, review.Reviewer) | ||||||
| 
 | 
 | ||||||
| 	invalidReview1 := unittest.AssertExistsAndLoadBean(t, &Review{ID: 2}).(*Review) | 	invalidReview1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 2}).(*issues_model.Review) | ||||||
| 	assert.Error(t, invalidReview1.LoadAttributes(db.DefaultContext)) | 	assert.Error(t, invalidReview1.LoadAttributes(db.DefaultContext)) | ||||||
| 
 | 
 | ||||||
| 	invalidReview2 := unittest.AssertExistsAndLoadBean(t, &Review{ID: 3}).(*Review) | 	invalidReview2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 3}).(*issues_model.Review) | ||||||
| 	assert.Error(t, invalidReview2.LoadAttributes(db.DefaultContext)) | 	assert.Error(t, invalidReview2.LoadAttributes(db.DefaultContext)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestReview_LoadCodeComments(t *testing.T) { | func TestReview_LoadCodeComments(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	review := unittest.AssertExistsAndLoadBean(t, &Review{ID: 4}).(*Review) | 	review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4}).(*issues_model.Review) | ||||||
| 	assert.NoError(t, review.LoadAttributes(db.DefaultContext)) | 	assert.NoError(t, review.LoadAttributes(db.DefaultContext)) | ||||||
| 	assert.NoError(t, review.LoadCodeComments(db.DefaultContext)) | 	assert.NoError(t, review.LoadCodeComments(db.DefaultContext)) | ||||||
| 	assert.Len(t, review.CodeComments, 1) | 	assert.Len(t, review.CodeComments, 1) | ||||||
| @@ -51,18 +52,18 @@ func TestReview_LoadCodeComments(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestReviewType_Icon(t *testing.T) { | func TestReviewType_Icon(t *testing.T) { | ||||||
| 	assert.Equal(t, "check", ReviewTypeApprove.Icon()) | 	assert.Equal(t, "check", issues_model.ReviewTypeApprove.Icon()) | ||||||
| 	assert.Equal(t, "diff", ReviewTypeReject.Icon()) | 	assert.Equal(t, "diff", issues_model.ReviewTypeReject.Icon()) | ||||||
| 	assert.Equal(t, "comment", ReviewTypeComment.Icon()) | 	assert.Equal(t, "comment", issues_model.ReviewTypeComment.Icon()) | ||||||
| 	assert.Equal(t, "comment", ReviewTypeUnknown.Icon()) | 	assert.Equal(t, "comment", issues_model.ReviewTypeUnknown.Icon()) | ||||||
| 	assert.Equal(t, "dot-fill", ReviewTypeRequest.Icon()) | 	assert.Equal(t, "dot-fill", issues_model.ReviewTypeRequest.Icon()) | ||||||
| 	assert.Equal(t, "comment", ReviewType(6).Icon()) | 	assert.Equal(t, "comment", issues_model.ReviewType(6).Icon()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestFindReviews(t *testing.T) { | func TestFindReviews(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	reviews, err := FindReviews(db.DefaultContext, FindReviewOptions{ | 	reviews, err := issues_model.FindReviews(db.DefaultContext, issues_model.FindReviewOptions{ | ||||||
| 		Type:       ReviewTypeApprove, | 		Type:       issues_model.ReviewTypeApprove, | ||||||
| 		IssueID:    2, | 		IssueID:    2, | ||||||
| 		ReviewerID: 1, | 		ReviewerID: 1, | ||||||
| 	}) | 	}) | ||||||
| @@ -73,66 +74,66 @@ func TestFindReviews(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| func TestGetCurrentReview(t *testing.T) { | func TestGetCurrentReview(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	review, err := GetCurrentReview(db.DefaultContext, user, issue) | 	review, err := issues_model.GetCurrentReview(db.DefaultContext, user, issue) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.NotNil(t, review) | 	assert.NotNil(t, review) | ||||||
| 	assert.Equal(t, ReviewTypePending, review.Type) | 	assert.Equal(t, issues_model.ReviewTypePending, review.Type) | ||||||
| 	assert.Equal(t, "Pending Review", review.Content) | 	assert.Equal(t, "Pending Review", review.Content) | ||||||
| 
 | 
 | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User) | ||||||
| 	review2, err := GetCurrentReview(db.DefaultContext, user2, issue) | 	review2, err := issues_model.GetCurrentReview(db.DefaultContext, user2, issue) | ||||||
| 	assert.Error(t, err) | 	assert.Error(t, err) | ||||||
| 	assert.True(t, IsErrReviewNotExist(err)) | 	assert.True(t, issues_model.IsErrReviewNotExist(err)) | ||||||
| 	assert.Nil(t, review2) | 	assert.Nil(t, review2) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestCreateReview(t *testing.T) { | func TestCreateReview(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 2}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue) | ||||||
| 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | 	user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	review, err := CreateReview(db.DefaultContext, CreateReviewOptions{ | 	review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{ | ||||||
| 		Content:  "New Review", | 		Content:  "New Review", | ||||||
| 		Type:     ReviewTypePending, | 		Type:     issues_model.ReviewTypePending, | ||||||
| 		Issue:    issue, | 		Issue:    issue, | ||||||
| 		Reviewer: user, | 		Reviewer: user, | ||||||
| 	}) | 	}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, "New Review", review.Content) | 	assert.Equal(t, "New Review", review.Content) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &Review{Content: "New Review"}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Review{Content: "New Review"}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestGetReviewersByIssueID(t *testing.T) { | func TestGetReviewersByIssueID(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 3}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue) | ||||||
| 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | 	user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User) | ||||||
| 	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) | 	user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User) | ||||||
| 	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) | 	user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User) | ||||||
| 
 | 
 | ||||||
| 	expectedReviews := []*Review{} | 	expectedReviews := []*issues_model.Review{} | ||||||
| 	expectedReviews = append(expectedReviews, | 	expectedReviews = append(expectedReviews, | ||||||
| 		&Review{ | 		&issues_model.Review{ | ||||||
| 			Reviewer:    user3, | 			Reviewer:    user3, | ||||||
| 			Type:        ReviewTypeReject, | 			Type:        issues_model.ReviewTypeReject, | ||||||
| 			UpdatedUnix: 946684812, | 			UpdatedUnix: 946684812, | ||||||
| 		}, | 		}, | ||||||
| 		&Review{ | 		&issues_model.Review{ | ||||||
| 			Reviewer:    user4, | 			Reviewer:    user4, | ||||||
| 			Type:        ReviewTypeApprove, | 			Type:        issues_model.ReviewTypeApprove, | ||||||
| 			UpdatedUnix: 946684813, | 			UpdatedUnix: 946684813, | ||||||
| 		}, | 		}, | ||||||
| 		&Review{ | 		&issues_model.Review{ | ||||||
| 			Reviewer:    user2, | 			Reviewer:    user2, | ||||||
| 			Type:        ReviewTypeReject, | 			Type:        issues_model.ReviewTypeReject, | ||||||
| 			UpdatedUnix: 946684814, | 			UpdatedUnix: 946684814, | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 	allReviews, err := GetReviewersByIssueID(issue.ID) | 	allReviews, err := issues_model.GetReviewersByIssueID(issue.ID) | ||||||
| 	for _, reviewer := range allReviews { | 	for _, reviewer := range allReviews { | ||||||
| 		assert.NoError(t, reviewer.LoadReviewer()) | 		assert.NoError(t, reviewer.LoadReviewer()) | ||||||
| 	} | 	} | ||||||
| @@ -149,53 +150,53 @@ func TestGetReviewersByIssueID(t *testing.T) { | |||||||
| func TestDismissReview(t *testing.T) { | func TestDismissReview(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	approveReviewExample := unittest.AssertExistsAndLoadBean(t, &Review{ID: 8}).(*Review) | 	approveReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 8}).(*issues_model.Review) | ||||||
| 	assert.False(t, rejectReviewExample.Dismissed) | 	assert.False(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(rejectReviewExample, true)) | 	assert.NoError(t, issues_model.DismissReview(rejectReviewExample, true)) | ||||||
| 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	assert.True(t, rejectReviewExample.Dismissed) | 	assert.True(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(requestReviewExample, true)) | 	assert.NoError(t, issues_model.DismissReview(requestReviewExample, true)) | ||||||
| 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	assert.True(t, rejectReviewExample.Dismissed) | 	assert.True(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(requestReviewExample, true)) | 	assert.NoError(t, issues_model.DismissReview(requestReviewExample, true)) | ||||||
| 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	assert.True(t, rejectReviewExample.Dismissed) | 	assert.True(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(requestReviewExample, false)) | 	assert.NoError(t, issues_model.DismissReview(requestReviewExample, false)) | ||||||
| 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	assert.True(t, rejectReviewExample.Dismissed) | 	assert.True(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(requestReviewExample, false)) | 	assert.NoError(t, issues_model.DismissReview(requestReviewExample, false)) | ||||||
| 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 9}).(*Review) | 	rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review) | ||||||
| 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &Review{ID: 11}).(*Review) | 	requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review) | ||||||
| 	assert.True(t, rejectReviewExample.Dismissed) | 	assert.True(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(rejectReviewExample, false)) | 	assert.NoError(t, issues_model.DismissReview(rejectReviewExample, false)) | ||||||
| 	assert.False(t, rejectReviewExample.Dismissed) | 	assert.False(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.False(t, approveReviewExample.Dismissed) | 	assert.False(t, approveReviewExample.Dismissed) | ||||||
| 
 | 
 | ||||||
| 	assert.NoError(t, DismissReview(approveReviewExample, true)) | 	assert.NoError(t, issues_model.DismissReview(approveReviewExample, true)) | ||||||
| 	assert.False(t, rejectReviewExample.Dismissed) | 	assert.False(t, rejectReviewExample.Dismissed) | ||||||
| 	assert.False(t, requestReviewExample.Dismissed) | 	assert.False(t, requestReviewExample.Dismissed) | ||||||
| 	assert.True(t, approveReviewExample.Dismissed) | 	assert.True(t, approveReviewExample.Dismissed) | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -215,7 +215,7 @@ func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if exists { | 	if exists { | ||||||
| 		issue, err := getIssueByID(ctx, sw.IssueID) | 		issue, err := GetIssueByID(ctx, sw.IssueID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
							
								
								
									
										79
									
								
								models/issues/stopwatch_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								models/issues/stopwatch_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | // 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 issues_test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
|  | 	"code.gitea.io/gitea/models/unittest" | ||||||
|  | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  |  | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestCancelStopwatch(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	user1, err := user_model.GetUserByID(1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	err = issues_model.CancelStopwatch(user1, issue1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: user1.ID, IssueID: issue1.ID}) | ||||||
|  |  | ||||||
|  | 	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeCancelTracking, PosterID: user1.ID, IssueID: issue1.ID}) | ||||||
|  |  | ||||||
|  | 	assert.Nil(t, issues_model.CancelStopwatch(user1, issue2)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStopwatchExists(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	assert.True(t, issues_model.StopwatchExists(1, 1)) | ||||||
|  | 	assert.False(t, issues_model.StopwatchExists(1, 2)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestHasUserStopwatch(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	exists, sw, err := issues_model.HasUserStopwatch(db.DefaultContext, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.True(t, exists) | ||||||
|  | 	assert.Equal(t, int64(1), sw.ID) | ||||||
|  |  | ||||||
|  | 	exists, _, err = issues_model.HasUserStopwatch(db.DefaultContext, 3) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	assert.False(t, exists) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestCreateOrStopIssueStopwatch(t *testing.T) { | ||||||
|  | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
|  |  | ||||||
|  | 	user2, err := user_model.GetUserByID(2) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	user3, err := user_model.GetUserByID(3) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	issue2, err := issues_model.GetIssueByID(db.DefaultContext, 2) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user3, issue1)) | ||||||
|  | 	sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1}).(*issues_model.Stopwatch) | ||||||
|  | 	assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow()) | ||||||
|  |  | ||||||
|  | 	assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user2, issue2)) | ||||||
|  | 	unittest.AssertNotExistsBean(t, &issues_model.Stopwatch{UserID: 2, IssueID: 2}) | ||||||
|  | 	unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 2, IssueID: 2}) | ||||||
|  | } | ||||||
| @@ -2,7 +2,7 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| @@ -48,7 +48,7 @@ func (t *TrackedTime) LoadAttributes() (err error) { | |||||||
| 
 | 
 | ||||||
| func (t *TrackedTime) loadAttributes(ctx context.Context) (err error) { | func (t *TrackedTime) loadAttributes(ctx context.Context) (err error) { | ||||||
| 	if t.Issue == nil { | 	if t.Issue == nil { | ||||||
| 		t.Issue, err = getIssueByID(ctx, t.IssueID) | 		t.Issue, err = GetIssueByID(ctx, t.IssueID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -2,13 +2,14 @@ | |||||||
| // 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. | ||||||
| 
 | 
 | ||||||
| package models | package issues_test | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 
 | 
 | ||||||
| @@ -21,20 +22,20 @@ func TestAddTime(t *testing.T) { | |||||||
| 	user3, err := user_model.GetUserByID(3) | 	user3, err := user_model.GetUserByID(3) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	issue1, err := GetIssueByID(1) | 	issue1, err := issues_model.GetIssueByID(db.DefaultContext, 1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 
 | 
 | ||||||
| 	// 3661 = 1h 1min 1s | 	// 3661 = 1h 1min 1s | ||||||
| 	trackedTime, err := AddTime(user3, issue1, 3661, time.Now()) | 	trackedTime, err := issues_model.AddTime(user3, issue1, 3661, time.Now()) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, int64(3), trackedTime.UserID) | 	assert.Equal(t, int64(3), trackedTime.UserID) | ||||||
| 	assert.Equal(t, int64(1), trackedTime.IssueID) | 	assert.Equal(t, int64(1), trackedTime.IssueID) | ||||||
| 	assert.Equal(t, int64(3661), trackedTime.Time) | 	assert.Equal(t, int64(3661), trackedTime.Time) | ||||||
| 
 | 
 | ||||||
| 	tt := unittest.AssertExistsAndLoadBean(t, &TrackedTime{UserID: 3, IssueID: 1}).(*TrackedTime) | 	tt := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 3, IssueID: 1}).(*issues_model.TrackedTime) | ||||||
| 	assert.Equal(t, int64(3661), tt.Time) | 	assert.Equal(t, int64(3661), tt.Time) | ||||||
| 
 | 
 | ||||||
| 	comment := unittest.AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*Comment) | 	comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*issues_model.Comment) | ||||||
| 	assert.Equal(t, comment.Content, "1 hour 1 minute") | 	assert.Equal(t, comment.Content, "1 hour 1 minute") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -42,39 +43,39 @@ func TestGetTrackedTimes(t *testing.T) { | |||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	// by Issue | 	// by Issue | ||||||
| 	times, err := GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{IssueID: 1}) | 	times, err := issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: 1}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 1) | 	assert.Len(t, times, 1) | ||||||
| 	assert.Equal(t, int64(400), times[0].Time) | 	assert.Equal(t, int64(400), times[0].Time) | ||||||
| 
 | 
 | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{IssueID: -1}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{IssueID: -1}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 0) | 	assert.Len(t, times, 0) | ||||||
| 
 | 
 | ||||||
| 	// by User | 	// by User | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{UserID: 1}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 1}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 3) | 	assert.Len(t, times, 3) | ||||||
| 	assert.Equal(t, int64(400), times[0].Time) | 	assert.Equal(t, int64(400), times[0].Time) | ||||||
| 
 | 
 | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{UserID: 3}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{UserID: 3}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 0) | 	assert.Len(t, times, 0) | ||||||
| 
 | 
 | ||||||
| 	// by Repo | 	// by Repo | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 2}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 2}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 3) | 	assert.Len(t, times, 3) | ||||||
| 	assert.Equal(t, int64(1), times[0].Time) | 	assert.Equal(t, int64(1), times[0].Time) | ||||||
| 	issue, err := GetIssueByID(times[0].IssueID) | 	issue, err := issues_model.GetIssueByID(db.DefaultContext, times[0].IssueID) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Equal(t, issue.RepoID, int64(2)) | 	assert.Equal(t, issue.RepoID, int64(2)) | ||||||
| 
 | 
 | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 1}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 1}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 5) | 	assert.Len(t, times, 5) | ||||||
| 
 | 
 | ||||||
| 	times, err = GetTrackedTimes(db.DefaultContext, &FindTrackedTimesOptions{RepositoryID: 10}) | 	times, err = issues_model.GetTrackedTimes(db.DefaultContext, &issues_model.FindTrackedTimesOptions{RepositoryID: 10}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, times, 0) | 	assert.Len(t, times, 0) | ||||||
| } | } | ||||||
| @@ -82,7 +83,7 @@ func TestGetTrackedTimes(t *testing.T) { | |||||||
| func TestTotalTimes(t *testing.T) { | func TestTotalTimes(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 
 | 
 | ||||||
| 	total, err := TotalTimes(&FindTrackedTimesOptions{IssueID: 1}) | 	total, err := issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 1}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, total, 1) | 	assert.Len(t, total, 1) | ||||||
| 	for user, time := range total { | 	for user, time := range total { | ||||||
| @@ -90,7 +91,7 @@ func TestTotalTimes(t *testing.T) { | |||||||
| 		assert.Equal(t, "6 minutes 40 seconds", time) | 		assert.Equal(t, "6 minutes 40 seconds", time) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 2}) | 	total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 2}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, total, 2) | 	assert.Len(t, total, 2) | ||||||
| 	for user, time := range total { | 	for user, time := range total { | ||||||
| @@ -103,7 +104,7 @@ func TestTotalTimes(t *testing.T) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 5}) | 	total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 5}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, total, 1) | 	assert.Len(t, total, 1) | ||||||
| 	for user, time := range total { | 	for user, time := range total { | ||||||
| @@ -111,7 +112,7 @@ func TestTotalTimes(t *testing.T) { | |||||||
| 		assert.Equal(t, "1 second", time) | 		assert.Equal(t, "1 second", time) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	total, err = TotalTimes(&FindTrackedTimesOptions{IssueID: 4}) | 	total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 4}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.Len(t, total, 2) | 	assert.Len(t, total, 2) | ||||||
| } | } | ||||||
| @@ -7,7 +7,6 @@ package models | |||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" |  | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -28,10 +27,6 @@ func TestFixturesAreConsistent(t *testing.T) { | |||||||
| 	unittest.CheckConsistencyFor(t, | 	unittest.CheckConsistencyFor(t, | ||||||
| 		&user_model.User{}, | 		&user_model.User{}, | ||||||
| 		&repo_model.Repository{}, | 		&repo_model.Repository{}, | ||||||
| 		&Issue{}, |  | ||||||
| 		&PullRequest{}, |  | ||||||
| 		&issues_model.Milestone{}, |  | ||||||
| 		&Label{}, |  | ||||||
| 		&organization.Team{}, | 		&organization.Team{}, | ||||||
| 		&Action{}) | 		&Action{}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,8 +10,6 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/modules/structs" | 	"code.gitea.io/gitea/modules/structs" | ||||||
|  |  | ||||||
| 	"xorm.io/builder" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // InsertMilestones creates milestones of repository. | // InsertMilestones creates milestones of repository. | ||||||
| @@ -41,7 +39,7 @@ func InsertMilestones(ms ...*issues_model.Milestone) (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // InsertIssues insert issues to database | // InsertIssues insert issues to database | ||||||
| func InsertIssues(issues ...*Issue) error { | func InsertIssues(issues ...*issues_model.Issue) error { | ||||||
| 	ctx, committer, err := db.TxContext() | 	ctx, committer, err := db.TxContext() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -56,14 +54,14 @@ func InsertIssues(issues ...*Issue) error { | |||||||
| 	return committer.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
| func insertIssue(ctx context.Context, issue *Issue) error { | func insertIssue(ctx context.Context, issue *issues_model.Issue) error { | ||||||
| 	sess := db.GetEngine(ctx) | 	sess := db.GetEngine(ctx) | ||||||
| 	if _, err := sess.NoAutoTime().Insert(issue); err != nil { | 	if _, err := sess.NoAutoTime().Insert(issue); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	issueLabels := make([]IssueLabel, 0, len(issue.Labels)) | 	issueLabels := make([]issues_model.IssueLabel, 0, len(issue.Labels)) | ||||||
| 	for _, label := range issue.Labels { | 	for _, label := range issue.Labels { | ||||||
| 		issueLabels = append(issueLabels, IssueLabel{ | 		issueLabels = append(issueLabels, issues_model.IssueLabel{ | ||||||
| 			IssueID: issue.ID, | 			IssueID: issue.ID, | ||||||
| 			LabelID: label.ID, | 			LabelID: label.ID, | ||||||
| 		}) | 		}) | ||||||
| @@ -95,7 +93,7 @@ func insertIssue(ctx context.Context, issue *Issue) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // InsertIssueComments inserts many comments of issues. | // InsertIssueComments inserts many comments of issues. | ||||||
| func InsertIssueComments(comments []*Comment) error { | func InsertIssueComments(comments []*issues_model.Comment) error { | ||||||
| 	if len(comments) == 0 { | 	if len(comments) == 0 { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| @@ -127,7 +125,8 @@ func InsertIssueComments(comments []*Comment) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for issueID := range issueIDs { | 	for issueID := range issueIDs { | ||||||
| 		if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?", issueID, CommentTypeComment, issueID); err != nil { | 		if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?", | ||||||
|  | 			issueID, issues_model.CommentTypeComment, issueID); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -135,7 +134,7 @@ func InsertIssueComments(comments []*Comment) error { | |||||||
| } | } | ||||||
|  |  | ||||||
| // InsertPullRequests inserted pull requests | // InsertPullRequests inserted pull requests | ||||||
| func InsertPullRequests(prs ...*PullRequest) error { | func InsertPullRequests(prs ...*issues_model.PullRequest) error { | ||||||
| 	ctx, committer, err := db.TxContext() | 	ctx, committer, err := db.TxContext() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -182,37 +181,13 @@ func InsertReleases(rels ...*Release) error { | |||||||
| 	return committer.Commit() | 	return committer.Commit() | ||||||
| } | } | ||||||
|  |  | ||||||
| func migratedIssueCond(tp structs.GitServiceType) builder.Cond { |  | ||||||
| 	return builder.In("issue_id", |  | ||||||
| 		builder.Select("issue.id"). |  | ||||||
| 			From("issue"). |  | ||||||
| 			InnerJoin("repository", "issue.repo_id = repository.id"). |  | ||||||
| 			Where(builder.Eq{ |  | ||||||
| 				"repository.original_service_type": tp, |  | ||||||
| 			}), |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UpdateReviewsMigrationsByType updates reviews' migrations information via given git service type and original id and poster id |  | ||||||
| func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error { |  | ||||||
| 	_, err := db.GetEngine(db.DefaultContext).Table("review"). |  | ||||||
| 		Where("original_author_id = ?", originalAuthorID). |  | ||||||
| 		And(migratedIssueCond(tp)). |  | ||||||
| 		Update(map[string]interface{}{ |  | ||||||
| 			"reviewer_id":        posterID, |  | ||||||
| 			"original_author":    "", |  | ||||||
| 			"original_author_id": 0, |  | ||||||
| 		}) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID | // UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID | ||||||
| func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error { | func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error { | ||||||
| 	if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil { | 	if err := issues_model.UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil { | 	if err := issues_model.UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -220,8 +195,8 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if err := UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil { | 	if err := issues_model.UpdateReactionsMigrationsByType(tp, externalUserID, userID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return UpdateReviewsMigrationsByType(tp, externalUserID, userID) | 	return issues_model.UpdateReviewsMigrationsByType(tp, externalUserID, userID) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ func assertCreateIssues(t *testing.T, isPull bool) { | |||||||
| 	reponame := "repo1" | 	reponame := "repo1" | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &Label{ID: 1}).(*Label) | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
| 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) | 	milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone) | ||||||
| 	assert.EqualValues(t, milestone.ID, 1) | 	assert.EqualValues(t, milestone.ID, 1) | ||||||
| 	reaction := &issues_model.Reaction{ | 	reaction := &issues_model.Reaction{ | ||||||
| @@ -51,7 +51,7 @@ func assertCreateIssues(t *testing.T, isPull bool) { | |||||||
|  |  | ||||||
| 	foreignIndex := int64(12345) | 	foreignIndex := int64(12345) | ||||||
| 	title := "issuetitle1" | 	title := "issuetitle1" | ||||||
| 	is := &Issue{ | 	is := &issues_model.Issue{ | ||||||
| 		RepoID:      repo.ID, | 		RepoID:      repo.ID, | ||||||
| 		MilestoneID: milestone.ID, | 		MilestoneID: milestone.ID, | ||||||
| 		Repo:        repo, | 		Repo:        repo, | ||||||
| @@ -61,7 +61,7 @@ func assertCreateIssues(t *testing.T, isPull bool) { | |||||||
| 		PosterID:    owner.ID, | 		PosterID:    owner.ID, | ||||||
| 		Poster:      owner, | 		Poster:      owner, | ||||||
| 		IsClosed:    true, | 		IsClosed:    true, | ||||||
| 		Labels:      []*Label{label}, | 		Labels:      []*issues_model.Label{label}, | ||||||
| 		Reactions:   []*issues_model.Reaction{reaction}, | 		Reactions:   []*issues_model.Reaction{reaction}, | ||||||
| 		ForeignReference: &foreignreference.ForeignReference{ | 		ForeignReference: &foreignreference.ForeignReference{ | ||||||
| 			ForeignIndex: strconv.FormatInt(foreignIndex, 10), | 			ForeignIndex: strconv.FormatInt(foreignIndex, 10), | ||||||
| @@ -72,9 +72,9 @@ func assertCreateIssues(t *testing.T, isPull bool) { | |||||||
| 	err := InsertIssues(is) | 	err := InsertIssues(is) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	i := unittest.AssertExistsAndLoadBean(t, &Issue{Title: title}).(*Issue) | 	i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: title}).(*issues_model.Issue) | ||||||
| 	assert.Nil(t, i.ForeignReference) | 	assert.Nil(t, i.ForeignReference) | ||||||
| 	err = i.LoadAttributes() | 	err = i.LoadAttributes(db.DefaultContext) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
| 	assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex) | 	assert.EqualValues(t, strconv.FormatInt(foreignIndex, 10), i.ForeignReference.ForeignIndex) | ||||||
| 	unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: owner.ID, IssueID: i.ID}) | 	unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: owner.ID, IssueID: i.ID}) | ||||||
| @@ -90,7 +90,7 @@ func TestMigrate_CreateIssuesIsPullTrue(t *testing.T) { | |||||||
|  |  | ||||||
| func TestMigrate_InsertIssueComments(t *testing.T) { | func TestMigrate_InsertIssueComments(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	_ = issue.LoadRepo(db.DefaultContext) | 	_ = issue.LoadRepo(db.DefaultContext) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User) | ||||||
| 	reaction := &issues_model.Reaction{ | 	reaction := &issues_model.Reaction{ | ||||||
| @@ -98,7 +98,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) { | |||||||
| 		UserID: owner.ID, | 		UserID: owner.ID, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	comment := &Comment{ | 	comment := &issues_model.Comment{ | ||||||
| 		PosterID:  owner.ID, | 		PosterID:  owner.ID, | ||||||
| 		Poster:    owner, | 		Poster:    owner, | ||||||
| 		IssueID:   issue.ID, | 		IssueID:   issue.ID, | ||||||
| @@ -106,13 +106,13 @@ func TestMigrate_InsertIssueComments(t *testing.T) { | |||||||
| 		Reactions: []*issues_model.Reaction{reaction}, | 		Reactions: []*issues_model.Reaction{reaction}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err := InsertIssueComments([]*Comment{comment}) | 	err := InsertIssueComments([]*issues_model.Comment{comment}) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	issueModified := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | 	issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
| 	assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments) | 	assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments) | ||||||
|  |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{}) | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestMigrate_InsertPullRequests(t *testing.T) { | func TestMigrate_InsertPullRequests(t *testing.T) { | ||||||
| @@ -121,7 +121,7 @@ func TestMigrate_InsertPullRequests(t *testing.T) { | |||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository) | ||||||
| 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | 	owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User) | ||||||
|  |  | ||||||
| 	i := &Issue{ | 	i := &issues_model.Issue{ | ||||||
| 		RepoID:   repo.ID, | 		RepoID:   repo.ID, | ||||||
| 		Repo:     repo, | 		Repo:     repo, | ||||||
| 		Title:    "title1", | 		Title:    "title1", | ||||||
| @@ -131,16 +131,16 @@ func TestMigrate_InsertPullRequests(t *testing.T) { | |||||||
| 		Poster:   owner, | 		Poster:   owner, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	p := &PullRequest{ | 	p := &issues_model.PullRequest{ | ||||||
| 		Issue: i, | 		Issue: i, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err := InsertPullRequests(p) | 	err := InsertPullRequests(p) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  |  | ||||||
| 	_ = unittest.AssertExistsAndLoadBean(t, &PullRequest{IssueID: i.ID}).(*PullRequest) | 	_ = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: i.ID}).(*issues_model.PullRequest) | ||||||
|  |  | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{}, &PullRequest{}) | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.PullRequest{}) | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestMigrate_InsertReleases(t *testing.T) { | func TestMigrate_InsertReleases(t *testing.T) { | ||||||
|   | |||||||
| @@ -131,7 +131,7 @@ func addBranchProtectionCanPushAndEnableWhitelist(x *xorm.Engine) error { | |||||||
| 		Authorize int | 		Authorize int | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// getUserRepoPermission static function based on models.IsOfficialReviewer at 5d78792385 | 	// getUserRepoPermission static function based on issues_model.IsOfficialReviewer at 5d78792385 | ||||||
| 	getUserRepoPermission := func(sess *xorm.Session, repo *Repository, user *User) (Permission, error) { | 	getUserRepoPermission := func(sess *xorm.Session, repo *Repository, user *User) (Permission, error) { | ||||||
| 		var perm Permission | 		var perm Permission | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"strconv" | 	"strconv" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unit" | 	"code.gitea.io/gitea/models/unit" | ||||||
| @@ -66,9 +67,9 @@ type Notification struct { | |||||||
|  |  | ||||||
| 	UpdatedBy int64 `xorm:"INDEX NOT NULL"` | 	UpdatedBy int64 `xorm:"INDEX NOT NULL"` | ||||||
|  |  | ||||||
| 	Issue      *Issue                 `xorm:"-"` | 	Issue      *issues_model.Issue    `xorm:"-"` | ||||||
| 	Repository *repo_model.Repository `xorm:"-"` | 	Repository *repo_model.Repository `xorm:"-"` | ||||||
| 	Comment    *Comment               `xorm:"-"` | 	Comment    *issues_model.Comment  `xorm:"-"` | ||||||
| 	User       *user_model.User       `xorm:"-"` | 	User       *user_model.User       `xorm:"-"` | ||||||
|  |  | ||||||
| 	CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"` | 	CreatedUnix timeutil.TimeStamp `xorm:"created INDEX NOT NULL"` | ||||||
| @@ -204,7 +205,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n | |||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	issue, err := getIssueByID(ctx, issueID) | 	issue, err := issues_model.GetIssueByID(ctx, issueID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -214,14 +215,14 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n | |||||||
| 		toNotify[receiverID] = struct{}{} | 		toNotify[receiverID] = struct{}{} | ||||||
| 	} else { | 	} else { | ||||||
| 		toNotify = make(map[int64]struct{}, 32) | 		toNotify = make(map[int64]struct{}, 32) | ||||||
| 		issueWatches, err := GetIssueWatchersIDs(ctx, issueID, true) | 		issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		for _, id := range issueWatches { | 		for _, id := range issueWatches { | ||||||
| 			toNotify[id] = struct{}{} | 			toNotify[id] = struct{}{} | ||||||
| 		} | 		} | ||||||
| 		if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) { | 		if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) { | ||||||
| 			repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) | 			repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return err | 				return err | ||||||
| @@ -230,7 +231,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n | |||||||
| 				toNotify[id] = struct{}{} | 				toNotify[id] = struct{}{} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		issueParticipants, err := issue.getParticipantIDsByIssue(ctx) | 		issueParticipants, err := issue.GetParticipantIDsByIssue(ctx) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -241,7 +242,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n | |||||||
| 		// dont notify user who cause notification | 		// dont notify user who cause notification | ||||||
| 		delete(toNotify, notificationAuthorID) | 		delete(toNotify, notificationAuthorID) | ||||||
| 		// explicit unwatch on issue | 		// explicit unwatch on issue | ||||||
| 		issueUnWatches, err := GetIssueWatchersIDs(ctx, issueID, false) | 		issueUnWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, false) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -303,7 +304,7 @@ func notificationExists(notifications []*Notification, issueID, userID int64) bo | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| func createIssueNotification(ctx context.Context, userID int64, issue *Issue, commentID, updatedByID int64) error { | func createIssueNotification(ctx context.Context, userID int64, issue *issues_model.Issue, commentID, updatedByID int64) error { | ||||||
| 	notification := &Notification{ | 	notification := &Notification{ | ||||||
| 		UserID:    userID, | 		UserID:    userID, | ||||||
| 		RepoID:    issue.RepoID, | 		RepoID:    issue.RepoID, | ||||||
| @@ -415,21 +416,21 @@ func (n *Notification) loadRepo(ctx context.Context) (err error) { | |||||||
|  |  | ||||||
| func (n *Notification) loadIssue(ctx context.Context) (err error) { | func (n *Notification) loadIssue(ctx context.Context) (err error) { | ||||||
| 	if n.Issue == nil && n.IssueID != 0 { | 	if n.Issue == nil && n.IssueID != 0 { | ||||||
| 		n.Issue, err = getIssueByID(ctx, n.IssueID) | 		n.Issue, err = issues_model.GetIssueByID(ctx, n.IssueID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err) | 			return fmt.Errorf("getIssueByID [%d]: %v", n.IssueID, err) | ||||||
| 		} | 		} | ||||||
| 		return n.Issue.loadAttributes(ctx) | 		return n.Issue.LoadAttributes(ctx) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (n *Notification) loadComment(ctx context.Context) (err error) { | func (n *Notification) loadComment(ctx context.Context) (err error) { | ||||||
| 	if n.Comment == nil && n.CommentID != 0 { | 	if n.Comment == nil && n.CommentID != 0 { | ||||||
| 		n.Comment, err = GetCommentByID(ctx, n.CommentID) | 		n.Comment, err = issues_model.GetCommentByID(ctx, n.CommentID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if IsErrCommentNotExist(err) { | 			if issues_model.IsErrCommentNotExist(err) { | ||||||
| 				return ErrCommentNotExist{ | 				return issues_model.ErrCommentNotExist{ | ||||||
| 					ID:      n.CommentID, | 					ID:      n.CommentID, | ||||||
| 					IssueID: n.IssueID, | 					IssueID: n.IssueID, | ||||||
| 				} | 				} | ||||||
| @@ -456,7 +457,7 @@ func (n *Notification) GetRepo() (*repo_model.Repository, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // GetIssue returns the issue of the notification | // GetIssue returns the issue of the notification | ||||||
| func (n *Notification) GetIssue() (*Issue, error) { | func (n *Notification) GetIssue() (*issues_model.Issue, error) { | ||||||
| 	return n.Issue, n.loadIssue(db.DefaultContext) | 	return n.Issue, n.loadIssue(db.DefaultContext) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -489,7 +490,7 @@ func (nl NotificationList) LoadAttributes() error { | |||||||
| 	var err error | 	var err error | ||||||
| 	for i := 0; i < len(nl); i++ { | 	for i := 0; i < len(nl); i++ { | ||||||
| 		err = nl[i].LoadAttributes() | 		err = nl[i].LoadAttributes() | ||||||
| 		if err != nil && !IsErrCommentNotExist(err) { | 		if err != nil && !issues_model.IsErrCommentNotExist(err) { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -519,7 +520,7 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error) | |||||||
| 	repos := make(map[int64]*repo_model.Repository, len(repoIDs)) | 	repos := make(map[int64]*repo_model.Repository, len(repoIDs)) | ||||||
| 	left := len(repoIDs) | 	left := len(repoIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| @@ -592,22 +593,22 @@ func (nl NotificationList) LoadIssues() ([]int, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	issueIDs := nl.getPendingIssueIDs() | 	issueIDs := nl.getPendingIssueIDs() | ||||||
| 	issues := make(map[int64]*Issue, len(issueIDs)) | 	issues := make(map[int64]*issues_model.Issue, len(issueIDs)) | ||||||
| 	left := len(issueIDs) | 	left := len(issueIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| 		rows, err := db.GetEngine(db.DefaultContext). | 		rows, err := db.GetEngine(db.DefaultContext). | ||||||
| 			In("id", issueIDs[:limit]). | 			In("id", issueIDs[:limit]). | ||||||
| 			Rows(new(Issue)) | 			Rows(new(issues_model.Issue)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		for rows.Next() { | 		for rows.Next() { | ||||||
| 			var issue Issue | 			var issue issues_model.Issue | ||||||
| 			err = rows.Scan(&issue) | 			err = rows.Scan(&issue) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				rows.Close() | 				rows.Close() | ||||||
| @@ -678,22 +679,22 @@ func (nl NotificationList) LoadComments() ([]int, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	commentIDs := nl.getPendingCommentIDs() | 	commentIDs := nl.getPendingCommentIDs() | ||||||
| 	comments := make(map[int64]*Comment, len(commentIDs)) | 	comments := make(map[int64]*issues_model.Comment, len(commentIDs)) | ||||||
| 	left := len(commentIDs) | 	left := len(commentIDs) | ||||||
| 	for left > 0 { | 	for left > 0 { | ||||||
| 		limit := defaultMaxInSize | 		limit := db.DefaultMaxInSize | ||||||
| 		if left < limit { | 		if left < limit { | ||||||
| 			limit = left | 			limit = left | ||||||
| 		} | 		} | ||||||
| 		rows, err := db.GetEngine(db.DefaultContext). | 		rows, err := db.GetEngine(db.DefaultContext). | ||||||
| 			In("id", commentIDs[:limit]). | 			In("id", commentIDs[:limit]). | ||||||
| 			Rows(new(Comment)) | 			Rows(new(issues_model.Comment)) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		for rows.Next() { | 		for rows.Next() { | ||||||
| 			var comment Comment | 			var comment issues_model.Comment | ||||||
| 			err = rows.Scan(&comment) | 			err = rows.Scan(&comment) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				rows.Close() | 				rows.Close() | ||||||
| @@ -747,6 +748,15 @@ func GetUIDsAndNotificationCounts(since, until timeutil.TimeStamp) ([]UserIDCoun | |||||||
| 	return res, db.GetEngine(db.DefaultContext).SQL(sql, since, until, NotificationStatusUnread).Find(&res) | 	return res, db.GetEngine(db.DefaultContext).SQL(sql, since, until, NotificationStatusUnread).Find(&res) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // SetIssueReadBy sets issue to be read by given user. | ||||||
|  | func SetIssueReadBy(ctx context.Context, issueID, userID int64) error { | ||||||
|  | 	if err := issues_model.UpdateIssueUserByRead(userID, issueID); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return setIssueNotificationStatusReadIfUnread(ctx, userID, issueID) | ||||||
|  | } | ||||||
|  |  | ||||||
| func setIssueNotificationStatusReadIfUnread(ctx context.Context, userID, issueID int64) error { | func setIssueNotificationStatusReadIfUnread(ctx context.Context, userID, issueID int64) error { | ||||||
| 	notification, err := getIssueNotification(ctx, userID, issueID) | 	notification, err := getIssueNotification(ctx, userID, issueID) | ||||||
| 	// ignore if not exists | 	// ignore if not exists | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
|  |  | ||||||
| @@ -16,14 +17,14 @@ import ( | |||||||
|  |  | ||||||
| func TestCreateOrUpdateIssueNotifications(t *testing.T) { | func TestCreateOrUpdateIssueNotifications(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	issue := unittest.AssertExistsAndLoadBean(t, &Issue{ID: 1}).(*Issue) | 	issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue) | ||||||
|  |  | ||||||
| 	assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0)) | 	assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0)) | ||||||
|  |  | ||||||
| 	// User 9 is inactive, thus notifications for user 1 and 4 are created | 	// User 9 is inactive, thus notifications for user 1 and 4 are created | ||||||
| 	notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID}).(*Notification) | 	notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID}).(*Notification) | ||||||
| 	assert.Equal(t, NotificationStatusUnread, notf.Status) | 	assert.Equal(t, NotificationStatusUnread, notf.Status) | ||||||
| 	unittest.CheckConsistencyFor(t, &Issue{ID: issue.ID}) | 	unittest.CheckConsistencyFor(t, &issues_model.Issue{ID: issue.ID}) | ||||||
|  |  | ||||||
| 	notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID}).(*Notification) | 	notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID}).(*Notification) | ||||||
| 	assert.Equal(t, NotificationStatusUnread, notf.Status) | 	assert.Equal(t, NotificationStatusUnread, notf.Status) | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -153,7 +154,7 @@ func removeAllRepositories(ctx context.Context, t *organization.Team) (err error | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Remove all IssueWatches a user has subscribed to in the repositories | 			// Remove all IssueWatches a user has subscribed to in the repositories | ||||||
| 			if err = removeIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil { | 			if err = issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID); err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -216,7 +217,7 @@ func removeRepository(ctx context.Context, t *organization.Team, repo *repo_mode | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Remove all IssueWatches a user has subscribed to in the repositories | 		// Remove all IssueWatches a user has subscribed to in the repositories | ||||||
| 		if err := removeIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil { | 		if err := issues_model.RemoveIssueWatchersByRepoID(ctx, teamUser.UID, repo.ID); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -281,7 +281,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { | |||||||
| 		&access_model.Access{RepoID: repo.ID}, | 		&access_model.Access{RepoID: repo.ID}, | ||||||
| 		&Action{RepoID: repo.ID}, | 		&Action{RepoID: repo.ID}, | ||||||
| 		&repo_model.Collaboration{RepoID: repoID}, | 		&repo_model.Collaboration{RepoID: repoID}, | ||||||
| 		&Comment{RefRepoID: repoID}, | 		&issues_model.Comment{RefRepoID: repoID}, | ||||||
| 		&git_model.CommitStatus{RepoID: repoID}, | 		&git_model.CommitStatus{RepoID: repoID}, | ||||||
| 		&git_model.DeletedBranch{RepoID: repoID}, | 		&git_model.DeletedBranch{RepoID: repoID}, | ||||||
| 		&webhook.HookTask{RepoID: repoID}, | 		&webhook.HookTask{RepoID: repoID}, | ||||||
| @@ -306,18 +306,18 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Delete Labels and related objects | 	// Delete Labels and related objects | ||||||
| 	if err := deleteLabelsByRepoID(ctx, repoID); err != nil { | 	if err := issues_model.DeleteLabelsByRepoID(ctx, repoID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Delete Pulls and related objects | 	// Delete Pulls and related objects | ||||||
| 	if err := deletePullsByBaseRepoID(ctx, repoID); err != nil { | 	if err := issues_model.DeletePullsByBaseRepoID(ctx, repoID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Delete Issues and related objects | 	// Delete Issues and related objects | ||||||
| 	var attachmentPaths []string | 	var attachmentPaths []string | ||||||
| 	if attachmentPaths, err = deleteIssuesByRepoID(ctx, repoID); err != nil { | 	if attachmentPaths, err = issues_model.DeleteIssuesByRepoID(ctx, repoID); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -576,16 +576,11 @@ func repoStatsCorrectNum(ctx context.Context, id int64, isPull bool, field strin | |||||||
| } | } | ||||||
|  |  | ||||||
| func repoStatsCorrectNumClosedIssues(ctx context.Context, id int64) error { | func repoStatsCorrectNumClosedIssues(ctx context.Context, id int64) error { | ||||||
| 	return repoStatsCorrectNumClosed(ctx, id, false, "num_closed_issues") | 	return repo_model.StatsCorrectNumClosed(ctx, id, false, "num_closed_issues") | ||||||
| } | } | ||||||
|  |  | ||||||
| func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error { | func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error { | ||||||
| 	return repoStatsCorrectNumClosed(ctx, id, true, "num_closed_pulls") | 	return repo_model.StatsCorrectNumClosed(ctx, id, true, "num_closed_pulls") | ||||||
| } |  | ||||||
|  |  | ||||||
| func repoStatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error { |  | ||||||
| 	_, err := db.GetEngine(ctx).Exec("UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id) |  | ||||||
| 	return err |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) { | func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) { | ||||||
| @@ -687,12 +682,11 @@ func CheckRepoStats(ctx context.Context) error { | |||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			rawResult, err := e.Query("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID) | 			_, err = e.SQL("SELECT COUNT(*) FROM `repository` WHERE fork_id=?", repo.ID).Get(&repo.NumForks) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				log.Error("Select count of forks[%d]: %v", repo.ID, err) | 				log.Error("Select count of forks[%d]: %v", repo.ID, err) | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			repo.NumForks = int(parseCountResult(rawResult)) |  | ||||||
|  |  | ||||||
| 			if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil { | 			if _, err = e.ID(repo.ID).Cols("num_forks").Update(repo); err != nil { | ||||||
| 				log.Error("UpdateRepository[%d]: %v", id, err) | 				log.Error("UpdateRepository[%d]: %v", id, err) | ||||||
|   | |||||||
| @@ -25,6 +25,22 @@ import ( | |||||||
| 	"code.gitea.io/gitea/modules/util" | 	"code.gitea.io/gitea/modules/util" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo. | ||||||
|  | type ErrUserDoesNotHaveAccessToRepo struct { | ||||||
|  | 	UserID   int64 | ||||||
|  | 	RepoName string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // IsErrUserDoesNotHaveAccessToRepo checks if an error is a ErrRepoFileAlreadyExists. | ||||||
|  | func IsErrUserDoesNotHaveAccessToRepo(err error) bool { | ||||||
|  | 	_, ok := err.(ErrUserDoesNotHaveAccessToRepo) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (err ErrUserDoesNotHaveAccessToRepo) Error() string { | ||||||
|  | 	return fmt.Sprintf("user doesn't have access to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName) | ||||||
|  | } | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	reservedRepoNames    = []string{".", "..", "-"} | 	reservedRepoNames    = []string{".", "..", "-"} | ||||||
| 	reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"} | 	reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"} | ||||||
| @@ -743,3 +759,34 @@ func CountRepositories(ctx context.Context, opts CountRepositoryOptions) (int64, | |||||||
| 	} | 	} | ||||||
| 	return count, nil | 	return count, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // StatsCorrectNumClosed update repository's issue related numbers | ||||||
|  | func StatsCorrectNumClosed(ctx context.Context, id int64, isPull bool, field string) error { | ||||||
|  | 	_, err := db.Exec(ctx, "UPDATE `repository` SET "+field+"=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_closed=? AND is_pull=?) WHERE id=?", id, true, isPull, id) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UpdateRepoIssueNumbers update repository issue numbers | ||||||
|  | func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed bool) error { | ||||||
|  | 	e := db.GetEngine(ctx) | ||||||
|  | 	if isPull { | ||||||
|  | 		if _, err := e.ID(repoID).Decr("num_pulls").Update(new(Repository)); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if isClosed { | ||||||
|  | 			if _, err := e.ID(repoID).Decr("num_closed_pulls").Update(new(Repository)); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if _, err := e.ID(repoID).Decr("num_issues").Update(new(Repository)); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if isClosed { | ||||||
|  | 			if _, err := e.ID(repoID).Decr("num_closed_issues").Update(new(Repository)); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| @@ -29,15 +30,15 @@ type ActivityAuthorData struct { | |||||||
|  |  | ||||||
| // ActivityStats represets issue and pull request information. | // ActivityStats represets issue and pull request information. | ||||||
| type ActivityStats struct { | type ActivityStats struct { | ||||||
| 	OpenedPRs                   PullRequestList | 	OpenedPRs                   issues_model.PullRequestList | ||||||
| 	OpenedPRAuthorCount         int64 | 	OpenedPRAuthorCount         int64 | ||||||
| 	MergedPRs                   PullRequestList | 	MergedPRs                   issues_model.PullRequestList | ||||||
| 	MergedPRAuthorCount         int64 | 	MergedPRAuthorCount         int64 | ||||||
| 	OpenedIssues                IssueList | 	OpenedIssues                issues_model.IssueList | ||||||
| 	OpenedIssueAuthorCount      int64 | 	OpenedIssueAuthorCount      int64 | ||||||
| 	ClosedIssues                IssueList | 	ClosedIssues                issues_model.IssueList | ||||||
| 	ClosedIssueAuthorCount      int64 | 	ClosedIssueAuthorCount      int64 | ||||||
| 	UnresolvedIssues            IssueList | 	UnresolvedIssues            issues_model.IssueList | ||||||
| 	PublishedReleases           []*Release | 	PublishedReleases           []*Release | ||||||
| 	PublishedReleaseAuthorCount int64 | 	PublishedReleaseAuthorCount int64 | ||||||
| 	Code                        *git.CodeActivityStats | 	Code                        *git.CodeActivityStats | ||||||
| @@ -212,7 +213,7 @@ func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) e | |||||||
| 	// Merged pull requests | 	// Merged pull requests | ||||||
| 	sess := pullRequestsForActivityStatement(repoID, fromTime, true) | 	sess := pullRequestsForActivityStatement(repoID, fromTime, true) | ||||||
| 	sess.OrderBy("pull_request.merged_unix DESC") | 	sess.OrderBy("pull_request.merged_unix DESC") | ||||||
| 	stats.MergedPRs = make(PullRequestList, 0) | 	stats.MergedPRs = make(issues_model.PullRequestList, 0) | ||||||
| 	if err = sess.Find(&stats.MergedPRs); err != nil { | 	if err = sess.Find(&stats.MergedPRs); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -230,7 +231,7 @@ func (stats *ActivityStats) FillPullRequests(repoID int64, fromTime time.Time) e | |||||||
| 	// Opened pull requests | 	// Opened pull requests | ||||||
| 	sess = pullRequestsForActivityStatement(repoID, fromTime, false) | 	sess = pullRequestsForActivityStatement(repoID, fromTime, false) | ||||||
| 	sess.OrderBy("issue.created_unix ASC") | 	sess.OrderBy("issue.created_unix ASC") | ||||||
| 	stats.OpenedPRs = make(PullRequestList, 0) | 	stats.OpenedPRs = make(issues_model.PullRequestList, 0) | ||||||
| 	if err = sess.Find(&stats.OpenedPRs); err != nil { | 	if err = sess.Find(&stats.OpenedPRs); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -271,7 +272,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { | |||||||
| 	// Closed issues | 	// Closed issues | ||||||
| 	sess := issuesForActivityStatement(repoID, fromTime, true, false) | 	sess := issuesForActivityStatement(repoID, fromTime, true, false) | ||||||
| 	sess.OrderBy("issue.closed_unix DESC") | 	sess.OrderBy("issue.closed_unix DESC") | ||||||
| 	stats.ClosedIssues = make(IssueList, 0) | 	stats.ClosedIssues = make(issues_model.IssueList, 0) | ||||||
| 	if err = sess.Find(&stats.ClosedIssues); err != nil { | 	if err = sess.Find(&stats.ClosedIssues); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -286,7 +287,7 @@ func (stats *ActivityStats) FillIssues(repoID int64, fromTime time.Time) error { | |||||||
| 	// New issues | 	// New issues | ||||||
| 	sess = issuesForActivityStatement(repoID, fromTime, false, false) | 	sess = issuesForActivityStatement(repoID, fromTime, false, false) | ||||||
| 	sess.OrderBy("issue.created_unix ASC") | 	sess.OrderBy("issue.created_unix ASC") | ||||||
| 	stats.OpenedIssues = make(IssueList, 0) | 	stats.OpenedIssues = make(issues_model.IssueList, 0) | ||||||
| 	if err = sess.Find(&stats.OpenedIssues); err != nil { | 	if err = sess.Find(&stats.OpenedIssues); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -312,7 +313,7 @@ func (stats *ActivityStats) FillUnresolvedIssues(repoID int64, fromTime time.Tim | |||||||
| 		sess.And("issue.is_pull = ?", prs) | 		sess.And("issue.is_pull = ?", prs) | ||||||
| 	} | 	} | ||||||
| 	sess.OrderBy("issue.updated_unix DESC") | 	sess.OrderBy("issue.updated_unix DESC") | ||||||
| 	stats.UnresolvedIssues = make(IssueList, 0) | 	stats.UnresolvedIssues = make(issues_model.IssueList, 0) | ||||||
| 	return sess.Find(&stats.UnresolvedIssues) | 	return sess.Find(&stats.UnresolvedIssues) | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -101,7 +102,7 @@ func reconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Reposito | |||||||
|  |  | ||||||
| 	if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": uid}). | 	if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": uid}). | ||||||
| 		In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})). | 		In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})). | ||||||
| 		Delete(&IssueAssignees{}); err != nil { | 		Delete(&issues_model.IssueAssignees{}); err != nil { | ||||||
| 		return fmt.Errorf("Could not delete assignee[%d] %v", uid, err) | 		return fmt.Errorf("Could not delete assignee[%d] %v", uid, err) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| @@ -116,5 +117,5 @@ func reconsiderWatches(ctx context.Context, repo *repo_model.Repository, uid int | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Remove all IssueWatches a user has subscribed to in the repository | 	// Remove all IssueWatches a user has subscribed to in the repository | ||||||
| 	return removeIssueWatchersByRepoID(ctx, uid, repo.ID) | 	return issues_model.RemoveIssueWatchersByRepoID(ctx, uid, repo.ID) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -376,7 +377,7 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo | |||||||
| 						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 ((label.org_id = 0 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)`, issues_model.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) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ func GetStatistic() (stats Statistic) { | |||||||
|  |  | ||||||
| 	stats.Counter.Issue = stats.Counter.IssueClosed + stats.Counter.IssueOpen | 	stats.Counter.Issue = stats.Counter.IssueClosed + stats.Counter.IssueOpen | ||||||
|  |  | ||||||
| 	stats.Counter.Comment, _ = e.Count(new(Comment)) | 	stats.Counter.Comment, _ = e.Count(new(issues_model.Comment)) | ||||||
| 	stats.Counter.Oauth = 0 | 	stats.Counter.Oauth = 0 | ||||||
| 	stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) | 	stats.Counter.Follow, _ = e.Count(new(user_model.Follow)) | ||||||
| 	stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) | 	stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror)) | ||||||
| @@ -105,7 +105,7 @@ func GetStatistic() (stats Statistic) { | |||||||
| 	stats.Counter.AuthSource = auth.CountSources() | 	stats.Counter.AuthSource = auth.CountSources() | ||||||
| 	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) | 	stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook)) | ||||||
| 	stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) | 	stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone)) | ||||||
| 	stats.Counter.Label, _ = e.Count(new(Label)) | 	stats.Counter.Label, _ = e.Count(new(issues_model.Label)) | ||||||
| 	stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask)) | 	stats.Counter.HookTask, _ = e.Count(new(webhook.HookTask)) | ||||||
| 	stats.Counter.Team, _ = e.Count(new(organization.Team)) | 	stats.Counter.Team, _ = e.Count(new(organization.Team)) | ||||||
| 	stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment)) | 	stats.Counter.Attachment, _ = e.Count(new(repo_model.Attachment)) | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ import ( | |||||||
| 	auth_model "code.gitea.io/gitea/models/auth" | 	auth_model "code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
| 	"code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	pull_model "code.gitea.io/gitea/models/pull" | 	pull_model "code.gitea.io/gitea/models/pull" | ||||||
| @@ -78,12 +78,12 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { | |||||||
| 		&user_model.Follow{UserID: u.ID}, | 		&user_model.Follow{UserID: u.ID}, | ||||||
| 		&user_model.Follow{FollowID: u.ID}, | 		&user_model.Follow{FollowID: u.ID}, | ||||||
| 		&Action{UserID: u.ID}, | 		&Action{UserID: u.ID}, | ||||||
| 		&IssueUser{UID: u.ID}, | 		&issues_model.IssueUser{UID: u.ID}, | ||||||
| 		&user_model.EmailAddress{UID: u.ID}, | 		&user_model.EmailAddress{UID: u.ID}, | ||||||
| 		&user_model.UserOpenID{UID: u.ID}, | 		&user_model.UserOpenID{UID: u.ID}, | ||||||
| 		&issues.Reaction{UserID: u.ID}, | 		&issues_model.Reaction{UserID: u.ID}, | ||||||
| 		&organization.TeamUser{UID: u.ID}, | 		&organization.TeamUser{UID: u.ID}, | ||||||
| 		&Stopwatch{UserID: u.ID}, | 		&issues_model.Stopwatch{UserID: u.ID}, | ||||||
| 		&user_model.Setting{UserID: u.ID}, | 		&user_model.Setting{UserID: u.ID}, | ||||||
| 		&pull_model.AutoMerge{DoerID: u.ID}, | 		&pull_model.AutoMerge{DoerID: u.ID}, | ||||||
| 		&pull_model.ReviewState{UserID: u.ID}, | 		&pull_model.ReviewState{UserID: u.ID}, | ||||||
| @@ -101,8 +101,8 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { | |||||||
| 		// Delete Comments | 		// Delete Comments | ||||||
| 		const batchSize = 50 | 		const batchSize = 50 | ||||||
| 		for start := 0; ; start += batchSize { | 		for start := 0; ; start += batchSize { | ||||||
| 			comments := make([]*Comment, 0, batchSize) | 			comments := make([]*issues_model.Comment, 0, batchSize) | ||||||
| 			if err = e.Where("type=? AND poster_id=?", CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil { | 			if err = e.Where("type=? AND poster_id=?", issues_model.CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil { | ||||||
| 				return err | 				return err | ||||||
| 			} | 			} | ||||||
| 			if len(comments) == 0 { | 			if len(comments) == 0 { | ||||||
| @@ -110,14 +110,14 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			for _, comment := range comments { | 			for _, comment := range comments { | ||||||
| 				if err = deleteComment(ctx, comment); err != nil { | 				if err = issues_model.DeleteComment(ctx, comment); err != nil { | ||||||
| 					return err | 					return err | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Delete Reactions | 		// Delete Reactions | ||||||
| 		if err = issues.DeleteReaction(ctx, &issues.ReactionOptions{DoerID: u.ID}); err != nil { | 		if err = issues_model.DeleteReaction(ctx, &issues_model.ReactionOptions{DoerID: u.ID}); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -189,7 +189,7 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) { | |||||||
| 	// ***** END: GPGPublicKey ***** | 	// ***** END: GPGPublicKey ***** | ||||||
|  |  | ||||||
| 	// Clear assignee. | 	// Clear assignee. | ||||||
| 	if _, err = db.DeleteByBean(ctx, &IssueAssignees{AssigneeID: u.ID}); err != nil { | 	if _, err = db.DeleteByBean(ctx, &issues_model.IssueAssignees{AssigneeID: u.ID}); err != nil { | ||||||
| 		return fmt.Errorf("clear assignee: %v", err) | 		return fmt.Errorf("clear assignee: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,6 +18,7 @@ import ( | |||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	unit_model "code.gitea.io/gitea/models/unit" | 	unit_model "code.gitea.io/gitea/models/unit" | ||||||
| @@ -83,7 +84,7 @@ type Repository struct { | |||||||
|  |  | ||||||
| // CanWriteToBranch checks if the branch is writable by the user | // CanWriteToBranch checks if the branch is writable by the user | ||||||
| func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool { | func (r *Repository) CanWriteToBranch(user *user_model.User, branch string) bool { | ||||||
| 	return models.CanMaintainerWriteToBranch(r.Permission, branch, user) | 	return issues_model.CanMaintainerWriteToBranch(r.Permission, branch, user) | ||||||
| } | } | ||||||
|  |  | ||||||
| // CanEnableEditor returns true if repository is editable and user has proper access level. | // CanEnableEditor returns true if repository is editable and user has proper access level. | ||||||
| @@ -158,11 +159,11 @@ func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.Use | |||||||
| } | } | ||||||
|  |  | ||||||
| // CanUseTimetracker returns whether or not a user can use the timetracker. | // CanUseTimetracker returns whether or not a user can use the timetracker. | ||||||
| func (r *Repository) CanUseTimetracker(issue *models.Issue, user *user_model.User) bool { | func (r *Repository) CanUseTimetracker(issue *issues_model.Issue, user *user_model.User) bool { | ||||||
| 	// Checking for following: | 	// Checking for following: | ||||||
| 	// 1. Is timetracker enabled | 	// 1. Is timetracker enabled | ||||||
| 	// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? | 	// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? | ||||||
| 	isAssigned, _ := models.IsUserAssignedToIssue(db.DefaultContext, issue, user) | 	isAssigned, _ := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user) | ||||||
| 	return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() || | 	return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() || | ||||||
| 		r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) | 		r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -11,11 +11,11 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	asymkey_model "code.gitea.io/gitea/models/asymkey" | 	asymkey_model "code.gitea.io/gitea/models/asymkey" | ||||||
| 	"code.gitea.io/gitea/models/auth" | 	"code.gitea.io/gitea/models/auth" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	git_model "code.gitea.io/gitea/models/git" | 	git_model "code.gitea.io/gitea/models/git" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/organization" | 	"code.gitea.io/gitea/models/organization" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| @@ -55,7 +55,7 @@ func ToBranch(repo *repo_model.Repository, b *git.Branch, c *git.Commit, bp *git | |||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| 			canPush = models.CanMaintainerWriteToBranch(perms, b.Name, user) | 			canPush = issues_model.CanMaintainerWriteToBranch(perms, b.Name, user) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		return &api.Branch{ | 		return &api.Branch{ | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import ( | |||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| @@ -23,7 +22,7 @@ import ( | |||||||
| // it assumes some fields assigned with values: | // it assumes some fields assigned with values: | ||||||
| // Required - Poster, Labels, | // Required - Poster, Labels, | ||||||
| // Optional - Milestone, Assignee, PullRequest | // Optional - Milestone, Assignee, PullRequest | ||||||
| func ToAPIIssue(issue *models.Issue) *api.Issue { | func ToAPIIssue(issue *issues_model.Issue) *api.Issue { | ||||||
| 	if err := issue.LoadLabels(db.DefaultContext); err != nil { | 	if err := issue.LoadLabels(db.DefaultContext); err != nil { | ||||||
| 		return &api.Issue{} | 		return &api.Issue{} | ||||||
| 	} | 	} | ||||||
| @@ -100,7 +99,7 @@ func ToAPIIssue(issue *models.Issue) *api.Issue { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToAPIIssueList converts an IssueList to API format | // ToAPIIssueList converts an IssueList to API format | ||||||
| func ToAPIIssueList(il models.IssueList) []*api.Issue { | func ToAPIIssueList(il issues_model.IssueList) []*api.Issue { | ||||||
| 	result := make([]*api.Issue, len(il)) | 	result := make([]*api.Issue, len(il)) | ||||||
| 	for i := range il { | 	for i := range il { | ||||||
| 		result[i] = ToAPIIssue(il[i]) | 		result[i] = ToAPIIssue(il[i]) | ||||||
| @@ -109,7 +108,7 @@ func ToAPIIssueList(il models.IssueList) []*api.Issue { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToTrackedTime converts TrackedTime to API format | // ToTrackedTime converts TrackedTime to API format | ||||||
| func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) { | func ToTrackedTime(t *issues_model.TrackedTime) (apiT *api.TrackedTime) { | ||||||
| 	apiT = &api.TrackedTime{ | 	apiT = &api.TrackedTime{ | ||||||
| 		ID:       t.ID, | 		ID:       t.ID, | ||||||
| 		IssueID:  t.IssueID, | 		IssueID:  t.IssueID, | ||||||
| @@ -128,13 +127,13 @@ func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToStopWatches convert Stopwatch list to api.StopWatches | // ToStopWatches convert Stopwatch list to api.StopWatches | ||||||
| func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) { | func ToStopWatches(sws []*issues_model.Stopwatch) (api.StopWatches, error) { | ||||||
| 	result := api.StopWatches(make([]api.StopWatch, 0, len(sws))) | 	result := api.StopWatches(make([]api.StopWatch, 0, len(sws))) | ||||||
|  |  | ||||||
| 	issueCache := make(map[int64]*models.Issue) | 	issueCache := make(map[int64]*issues_model.Issue) | ||||||
| 	repoCache := make(map[int64]*repo_model.Repository) | 	repoCache := make(map[int64]*repo_model.Repository) | ||||||
| 	var ( | 	var ( | ||||||
| 		issue *models.Issue | 		issue *issues_model.Issue | ||||||
| 		repo  *repo_model.Repository | 		repo  *repo_model.Repository | ||||||
| 		ok    bool | 		ok    bool | ||||||
| 		err   error | 		err   error | ||||||
| @@ -143,7 +142,7 @@ func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) { | |||||||
| 	for _, sw := range sws { | 	for _, sw := range sws { | ||||||
| 		issue, ok = issueCache[sw.IssueID] | 		issue, ok = issueCache[sw.IssueID] | ||||||
| 		if !ok { | 		if !ok { | ||||||
| 			issue, err = models.GetIssueByID(sw.IssueID) | 			issue, err = issues_model.GetIssueByID(db.DefaultContext, sw.IssueID) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| @@ -170,7 +169,7 @@ func ToStopWatches(sws []*models.Stopwatch) (api.StopWatches, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToTrackedTimeList converts TrackedTimeList to API format | // ToTrackedTimeList converts TrackedTimeList to API format | ||||||
| func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList { | func ToTrackedTimeList(tl issues_model.TrackedTimeList) api.TrackedTimeList { | ||||||
| 	result := make([]*api.TrackedTime, 0, len(tl)) | 	result := make([]*api.TrackedTime, 0, len(tl)) | ||||||
| 	for _, t := range tl { | 	for _, t := range tl { | ||||||
| 		result = append(result, ToTrackedTime(t)) | 		result = append(result, ToTrackedTime(t)) | ||||||
| @@ -179,7 +178,7 @@ func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList { | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToLabel converts Label to API format | // ToLabel converts Label to API format | ||||||
| func ToLabel(label *models.Label, repo *repo_model.Repository, org *user_model.User) *api.Label { | func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_model.User) *api.Label { | ||||||
| 	result := &api.Label{ | 	result := &api.Label{ | ||||||
| 		ID:          label.ID, | 		ID:          label.ID, | ||||||
| 		Name:        label.Name, | 		Name:        label.Name, | ||||||
| @@ -206,7 +205,7 @@ func ToLabel(label *models.Label, repo *repo_model.Repository, org *user_model.U | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToLabelList converts list of Label to API format | // ToLabelList converts list of Label to API format | ||||||
| func ToLabelList(labels []*models.Label, repo *repo_model.Repository, org *user_model.User) []*api.Label { | func ToLabelList(labels []*issues_model.Label, repo *repo_model.Repository, org *user_model.User) []*api.Label { | ||||||
| 	result := make([]*api.Label, len(labels)) | 	result := make([]*api.Label, len(labels)) | ||||||
| 	for i := range labels { | 	for i := range labels { | ||||||
| 		result[i] = ToLabel(labels[i], repo, org) | 		result[i] = ToLabel(labels[i], repo, org) | ||||||
|   | |||||||
| @@ -5,16 +5,16 @@ | |||||||
| package convert | package convert | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ToComment converts a models.Comment to the api.Comment format | // ToComment converts a issues_model.Comment to the api.Comment format | ||||||
| func ToComment(c *models.Comment) *api.Comment { | func ToComment(c *issues_model.Comment) *api.Comment { | ||||||
| 	return &api.Comment{ | 	return &api.Comment{ | ||||||
| 		ID:       c.ID, | 		ID:       c.ID, | ||||||
| 		Poster:   ToUser(c.Poster, nil), | 		Poster:   ToUser(c.Poster, nil), | ||||||
| @@ -27,8 +27,8 @@ func ToComment(c *models.Comment) *api.Comment { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // ToTimelineComment converts a models.Comment to the api.TimelineComment format | // ToTimelineComment converts a issues_model.Comment to the api.TimelineComment format | ||||||
| func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineComment { | func ToTimelineComment(c *issues_model.Comment, doer *user_model.User) *api.TimelineComment { | ||||||
| 	err := c.LoadMilestone() | 	err := c.LoadMilestone() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("LoadMilestone: %v", err) | 		log.Error("LoadMilestone: %v", err) | ||||||
| @@ -105,7 +105,7 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if c.RefIssueID != 0 { | 	if c.RefIssueID != 0 { | ||||||
| 		issue, err := models.GetIssueByID(c.RefIssueID) | 		issue, err := issues_model.GetIssueByID(db.DefaultContext, c.RefIssueID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetIssueByID(%d): %v", c.RefIssueID, err) | 			log.Error("GetIssueByID(%d): %v", c.RefIssueID, err) | ||||||
| 			return nil | 			return nil | ||||||
| @@ -114,7 +114,7 @@ func ToTimelineComment(c *models.Comment, doer *user_model.User) *api.TimelineCo | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if c.RefCommentID != 0 { | 	if c.RefCommentID != 0 { | ||||||
| 		com, err := models.GetCommentByID(db.DefaultContext, c.RefCommentID) | 		com, err := issues_model.GetCommentByID(db.DefaultContext, c.RefCommentID) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetCommentByID(%d): %v", c.RefCommentID, err) | 			log.Error("GetCommentByID(%d): %v", c.RefCommentID, err) | ||||||
| 			return nil | 			return nil | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	issues_model "code.gitea.io/gitea/models/issues" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -22,7 +21,7 @@ import ( | |||||||
|  |  | ||||||
| func TestLabel_ToLabel(t *testing.T) { | func TestLabel_ToLabel(t *testing.T) { | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	label := unittest.AssertExistsAndLoadBean(t, &models.Label{ID: 1}).(*models.Label) | 	label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label) | ||||||
| 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}).(*repo_model.Repository) | 	repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}).(*repo_model.Repository) | ||||||
| 	assert.Equal(t, &api.Label{ | 	assert.Equal(t, &api.Label{ | ||||||
| 		ID:    label.ID, | 		ID:    label.ID, | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	access_model "code.gitea.io/gitea/models/perm/access" | 	access_model "code.gitea.io/gitea/models/perm/access" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -20,7 +20,7 @@ import ( | |||||||
| // ToAPIPullRequest assumes following fields have been assigned with valid values: | // ToAPIPullRequest assumes following fields have been assigned with valid values: | ||||||
| // Required - Issue | // Required - Issue | ||||||
| // Optional - Merger | // Optional - Merger | ||||||
| func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_model.User) *api.PullRequest { | func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) *api.PullRequest { | ||||||
| 	var ( | 	var ( | ||||||
| 		baseBranch *git.Branch | 		baseBranch *git.Branch | ||||||
| 		headBranch *git.Branch | 		headBranch *git.Branch | ||||||
| @@ -114,7 +114,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if pr.Flow == models.PullRequestFlowAGit { | 	if pr.Flow == issues_model.PullRequestFlowAGit { | ||||||
| 		gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath()) | 		gitRepo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err) | 			log.Error("OpenRepository[%s]: %v", pr.GetGitRefName(), err) | ||||||
| @@ -132,7 +132,7 @@ func ToAPIPullRequest(ctx context.Context, pr *models.PullRequest, doer *user_mo | |||||||
| 		apiPullRequest.Head.Name = "" | 		apiPullRequest.Head.Name = "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if pr.HeadRepo != nil && pr.Flow == models.PullRequestFlowGithub { | 	if pr.HeadRepo != nil && pr.Flow == issues_model.PullRequestFlowGithub { | ||||||
| 		p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer) | 		p, err := access_model.GetUserRepoPermission(ctx, pr.HeadRepo, doer) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err) | 			log.Error("GetUserRepoPermission[%d]: %v", pr.HeadRepoID, err) | ||||||
|   | |||||||
| @@ -8,13 +8,13 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	api "code.gitea.io/gitea/modules/structs" | 	api "code.gitea.io/gitea/modules/structs" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // ToPullReview convert a review to api format | // ToPullReview convert a review to api format | ||||||
| func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User) (*api.PullReview, error) { | func ToPullReview(ctx context.Context, r *issues_model.Review, doer *user_model.User) (*api.PullReview, error) { | ||||||
| 	if err := r.LoadAttributes(ctx); err != nil { | 	if err := r.LoadAttributes(ctx); err != nil { | ||||||
| 		if !user_model.IsErrUserNotExist(err) { | 		if !user_model.IsErrUserNotExist(err) { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| @@ -44,15 +44,15 @@ func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User) | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch r.Type { | 	switch r.Type { | ||||||
| 	case models.ReviewTypeApprove: | 	case issues_model.ReviewTypeApprove: | ||||||
| 		result.State = api.ReviewStateApproved | 		result.State = api.ReviewStateApproved | ||||||
| 	case models.ReviewTypeReject: | 	case issues_model.ReviewTypeReject: | ||||||
| 		result.State = api.ReviewStateRequestChanges | 		result.State = api.ReviewStateRequestChanges | ||||||
| 	case models.ReviewTypeComment: | 	case issues_model.ReviewTypeComment: | ||||||
| 		result.State = api.ReviewStateComment | 		result.State = api.ReviewStateComment | ||||||
| 	case models.ReviewTypePending: | 	case issues_model.ReviewTypePending: | ||||||
| 		result.State = api.ReviewStatePending | 		result.State = api.ReviewStatePending | ||||||
| 	case models.ReviewTypeRequest: | 	case issues_model.ReviewTypeRequest: | ||||||
| 		result.State = api.ReviewStateRequestReview | 		result.State = api.ReviewStateRequestReview | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -60,11 +60,11 @@ func ToPullReview(ctx context.Context, r *models.Review, doer *user_model.User) | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToPullReviewList convert a list of review to it's api format | // ToPullReviewList convert a list of review to it's api format | ||||||
| func ToPullReviewList(ctx context.Context, rl []*models.Review, doer *user_model.User) ([]*api.PullReview, error) { | func ToPullReviewList(ctx context.Context, rl []*issues_model.Review, doer *user_model.User) ([]*api.PullReview, error) { | ||||||
| 	result := make([]*api.PullReview, 0, len(rl)) | 	result := make([]*api.PullReview, 0, len(rl)) | ||||||
| 	for i := range rl { | 	for i := range rl { | ||||||
| 		// show pending reviews only for the user who created them | 		// show pending reviews only for the user who created them | ||||||
| 		if rl[i].Type == models.ReviewTypePending && !(doer.IsAdmin || doer.ID == rl[i].ReviewerID) { | 		if rl[i].Type == issues_model.ReviewTypePending && !(doer.IsAdmin || doer.ID == rl[i].ReviewerID) { | ||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
| 		r, err := ToPullReview(ctx, rl[i], doer) | 		r, err := ToPullReview(ctx, rl[i], doer) | ||||||
| @@ -77,7 +77,7 @@ func ToPullReviewList(ctx context.Context, rl []*models.Review, doer *user_model | |||||||
| } | } | ||||||
|  |  | ||||||
| // ToPullReviewCommentList convert the CodeComments of an review to it's api format | // ToPullReviewCommentList convert the CodeComments of an review to it's api format | ||||||
| func ToPullReviewCommentList(ctx context.Context, review *models.Review, doer *user_model.User) ([]*api.PullReviewComment, error) { | func ToPullReviewCommentList(ctx context.Context, review *issues_model.Review, doer *user_model.User) ([]*api.PullReviewComment, error) { | ||||||
| 	if err := review.LoadAttributes(ctx); err != nil { | 	if err := review.LoadAttributes(ctx); err != nil { | ||||||
| 		if !user_model.IsErrUserNotExist(err) { | 		if !user_model.IsErrUserNotExist(err) { | ||||||
| 			return nil, err | 			return nil, err | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ package convert | |||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/perm" | 	"code.gitea.io/gitea/models/perm" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/models/unittest" | 	"code.gitea.io/gitea/models/unittest" | ||||||
| @@ -21,7 +21,7 @@ func TestPullRequest_APIFormat(t *testing.T) { | |||||||
| 	// with HeadRepo | 	// with HeadRepo | ||||||
| 	assert.NoError(t, unittest.PrepareTestDatabase()) | 	assert.NoError(t, unittest.PrepareTestDatabase()) | ||||||
| 	headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | 	headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository) | ||||||
| 	pr := unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) | 	pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadAttributes()) | 	assert.NoError(t, pr.LoadAttributes()) | ||||||
| 	assert.NoError(t, pr.LoadIssue()) | 	assert.NoError(t, pr.LoadIssue()) | ||||||
| 	apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil) | 	apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil) | ||||||
| @@ -35,7 +35,7 @@ func TestPullRequest_APIFormat(t *testing.T) { | |||||||
| 	}, apiPullRequest.Head) | 	}, apiPullRequest.Head) | ||||||
|  |  | ||||||
| 	// withOut HeadRepo | 	// withOut HeadRepo | ||||||
| 	pr = unittest.AssertExistsAndLoadBean(t, &models.PullRequest{ID: 1}).(*models.PullRequest) | 	pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) | ||||||
| 	assert.NoError(t, pr.LoadIssue()) | 	assert.NoError(t, pr.LoadIssue()) | ||||||
| 	assert.NoError(t, pr.LoadAttributes()) | 	assert.NoError(t, pr.LoadAttributes()) | ||||||
| 	// simulate fork deletion | 	// simulate fork deletion | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/models/migrations" | 	"code.gitea.io/gitea/models/migrations" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -64,10 +65,10 @@ func genericOrphanCheck(name, subject, refobject, joincond string) consistencyCh | |||||||
| 	return consistencyCheck{ | 	return consistencyCheck{ | ||||||
| 		Name: name, | 		Name: name, | ||||||
| 		Counter: func() (int64, error) { | 		Counter: func() (int64, error) { | ||||||
| 			return models.CountOrphanedObjects(subject, refobject, joincond) | 			return db.CountOrphanedObjects(subject, refobject, joincond) | ||||||
| 		}, | 		}, | ||||||
| 		Fixer: func() (int64, error) { | 		Fixer: func() (int64, error) { | ||||||
| 			err := models.DeleteOrphanedObjects(subject, refobject, joincond) | 			err := db.DeleteOrphanedObjects(subject, refobject, joincond) | ||||||
| 			return -1, err | 			return -1, err | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| @@ -84,20 +85,20 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er | |||||||
| 		{ | 		{ | ||||||
| 			// find labels without existing repo or org | 			// find labels without existing repo or org | ||||||
| 			Name:    "Orphaned Labels without existing repository or organisation", | 			Name:    "Orphaned Labels without existing repository or organisation", | ||||||
| 			Counter: models.CountOrphanedLabels, | 			Counter: issues_model.CountOrphanedLabels, | ||||||
| 			Fixer:   asFixer(models.DeleteOrphanedLabels), | 			Fixer:   asFixer(issues_model.DeleteOrphanedLabels), | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			// find IssueLabels without existing label | 			// find IssueLabels without existing label | ||||||
| 			Name:    "Orphaned Issue Labels without existing label", | 			Name:    "Orphaned Issue Labels without existing label", | ||||||
| 			Counter: models.CountOrphanedIssueLabels, | 			Counter: issues_model.CountOrphanedIssueLabels, | ||||||
| 			Fixer:   asFixer(models.DeleteOrphanedIssueLabels), | 			Fixer:   asFixer(issues_model.DeleteOrphanedIssueLabels), | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			// find issues without existing repository | 			// find issues without existing repository | ||||||
| 			Name:    "Orphaned Issues without existing repository", | 			Name:    "Orphaned Issues without existing repository", | ||||||
| 			Counter: models.CountOrphanedIssues, | 			Counter: issues_model.CountOrphanedIssues, | ||||||
| 			Fixer:   asFixer(models.DeleteOrphanedIssues), | 			Fixer:   asFixer(issues_model.DeleteOrphanedIssues), | ||||||
| 		}, | 		}, | ||||||
| 		// find releases without existing repository | 		// find releases without existing repository | ||||||
| 		genericOrphanCheck("Orphaned Releases without existing repository", | 		genericOrphanCheck("Orphaned Releases without existing repository", | ||||||
| @@ -127,22 +128,22 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er | |||||||
| 		// find label comments with empty labels | 		// find label comments with empty labels | ||||||
| 		{ | 		{ | ||||||
| 			Name:         "Label comments with empty labels", | 			Name:         "Label comments with empty labels", | ||||||
| 			Counter:      models.CountCommentTypeLabelWithEmptyLabel, | 			Counter:      issues_model.CountCommentTypeLabelWithEmptyLabel, | ||||||
| 			Fixer:        models.FixCommentTypeLabelWithEmptyLabel, | 			Fixer:        issues_model.FixCommentTypeLabelWithEmptyLabel, | ||||||
| 			FixedMessage: "Fixed", | 			FixedMessage: "Fixed", | ||||||
| 		}, | 		}, | ||||||
| 		// find label comments with labels from outside the repository | 		// find label comments with labels from outside the repository | ||||||
| 		{ | 		{ | ||||||
| 			Name:         "Label comments with labels from outside the repository", | 			Name:         "Label comments with labels from outside the repository", | ||||||
| 			Counter:      models.CountCommentTypeLabelWithOutsideLabels, | 			Counter:      issues_model.CountCommentTypeLabelWithOutsideLabels, | ||||||
| 			Fixer:        models.FixCommentTypeLabelWithOutsideLabels, | 			Fixer:        issues_model.FixCommentTypeLabelWithOutsideLabels, | ||||||
| 			FixedMessage: "Removed", | 			FixedMessage: "Removed", | ||||||
| 		}, | 		}, | ||||||
| 		// find issue_label with labels from outside the repository | 		// find issue_label with labels from outside the repository | ||||||
| 		{ | 		{ | ||||||
| 			Name:         "IssueLabels with Labels from outside the repository", | 			Name:         "IssueLabels with Labels from outside the repository", | ||||||
| 			Counter:      models.CountIssueLabelWithOutsideLabels, | 			Counter:      issues_model.CountIssueLabelWithOutsideLabels, | ||||||
| 			Fixer:        models.FixIssueLabelWithOutsideLabels, | 			Fixer:        issues_model.FixIssueLabelWithOutsideLabels, | ||||||
| 			FixedMessage: "Removed", | 			FixedMessage: "Removed", | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -18,13 +18,13 @@ import ( | |||||||
| 	"xorm.io/builder" | 	"xorm.io/builder" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*repo_model.Repository, *models.PullRequest) error) error { | func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*repo_model.Repository, *issues_model.PullRequest) error) error { | ||||||
| 	return db.Iterate( | 	return db.Iterate( | ||||||
| 		ctx, | 		ctx, | ||||||
| 		new(models.PullRequest), | 		new(issues_model.PullRequest), | ||||||
| 		builder.Eq{"base_repo_id": repo.ID}, | 		builder.Eq{"base_repo_id": repo.ID}, | ||||||
| 		func(idx int, bean interface{}) error { | 		func(idx int, bean interface{}) error { | ||||||
| 			return each(repo, bean.(*models.PullRequest)) | 			return each(repo, bean.(*issues_model.PullRequest)) | ||||||
| 		}, | 		}, | ||||||
| 	) | 	) | ||||||
| } | } | ||||||
| @@ -35,7 +35,7 @@ func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) erro | |||||||
| 	numPRsUpdated := 0 | 	numPRsUpdated := 0 | ||||||
| 	err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { | 	err := iterateRepositories(ctx, func(repo *repo_model.Repository) error { | ||||||
| 		numRepos++ | 		numRepos++ | ||||||
| 		return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *models.PullRequest) error { | 		return iteratePRs(ctx, repo, func(repo *repo_model.Repository, pr *issues_model.PullRequest) error { | ||||||
| 			numPRs++ | 			numPRs++ | ||||||
| 			pr.BaseRepo = repo | 			pr.BaseRepo = repo | ||||||
| 			repoPath := repo.RepoPath() | 			repoPath := repo.RepoPath() | ||||||
|   | |||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	"code.gitea.io/gitea/modules/convert" | 	"code.gitea.io/gitea/modules/convert" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| 	"code.gitea.io/gitea/modules/json" | 	"code.gitea.io/gitea/modules/json" | ||||||
| @@ -84,7 +85,7 @@ loop: | |||||||
| 			then = now | 			then = now | ||||||
|  |  | ||||||
| 			if setting.Service.EnableTimetracking { | 			if setting.Service.EnableTimetracking { | ||||||
| 				usersStopwatches, err := models.GetUIDsAndStopwatch() | 				usersStopwatches, err := issues_model.GetUIDsAndStopwatch() | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					log.Error("Unable to get GetUIDsAndStopwatch: %v", err) | 					log.Error("Unable to get GetUIDsAndStopwatch: %v", err) | ||||||
| 					return | 					return | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ package issues | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DBIndexer implements Indexer interface to use database's like search | // DBIndexer implements Indexer interface to use database's like search | ||||||
| @@ -44,7 +44,7 @@ func (i *DBIndexer) Close() { | |||||||
|  |  | ||||||
| // Search dummy function | // Search dummy function | ||||||
| func (i *DBIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) { | func (i *DBIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) { | ||||||
| 	total, ids, err := models.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start) | 	total, ids, err := issues_model.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -12,8 +12,8 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" |  | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| @@ -320,7 +320,7 @@ func populateIssueIndexer(ctx context.Context) { | |||||||
|  |  | ||||||
| // UpdateRepoIndexer add/update all issues of the repositories | // UpdateRepoIndexer add/update all issues of the repositories | ||||||
| func UpdateRepoIndexer(repo *repo_model.Repository) { | func UpdateRepoIndexer(repo *repo_model.Repository) { | ||||||
| 	is, err := models.Issues(&models.IssuesOptions{ | 	is, err := issues_model.Issues(&issues_model.IssuesOptions{ | ||||||
| 		RepoID:   repo.ID, | 		RepoID:   repo.ID, | ||||||
| 		IsClosed: util.OptionalBoolNone, | 		IsClosed: util.OptionalBoolNone, | ||||||
| 		IsPull:   util.OptionalBoolNone, | 		IsPull:   util.OptionalBoolNone, | ||||||
| @@ -329,7 +329,7 @@ func UpdateRepoIndexer(repo *repo_model.Repository) { | |||||||
| 		log.Error("Issues: %v", err) | 		log.Error("Issues: %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if err = models.IssueList(is).LoadDiscussComments(); err != nil { | 	if err = issues_model.IssueList(is).LoadDiscussComments(); err != nil { | ||||||
| 		log.Error("LoadComments: %v", err) | 		log.Error("LoadComments: %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| @@ -339,10 +339,10 @@ func UpdateRepoIndexer(repo *repo_model.Repository) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // UpdateIssueIndexer add/update an issue to the issue indexer | // UpdateIssueIndexer add/update an issue to the issue indexer | ||||||
| func UpdateIssueIndexer(issue *models.Issue) { | func UpdateIssueIndexer(issue *issues_model.Issue) { | ||||||
| 	var comments []string | 	var comments []string | ||||||
| 	for _, comment := range issue.Comments { | 	for _, comment := range issue.Comments { | ||||||
| 		if comment.Type == models.CommentTypeComment { | 		if comment.Type == issues_model.CommentTypeComment { | ||||||
| 			comments = append(comments, comment.Content) | 			comments = append(comments, comment.Content) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -362,7 +362,7 @@ func UpdateIssueIndexer(issue *models.Issue) { | |||||||
| // DeleteRepoIssueIndexer deletes repo's all issues indexes | // DeleteRepoIssueIndexer deletes repo's all issues indexes | ||||||
| func DeleteRepoIssueIndexer(repo *repo_model.Repository) { | func DeleteRepoIssueIndexer(repo *repo_model.Repository) { | ||||||
| 	var ids []int64 | 	var ids []int64 | ||||||
| 	ids, err := models.GetIssueIDsByRepoID(db.DefaultContext, repo.ID) | 	ids, err := issues_model.GetIssueIDsByRepoID(db.DefaultContext, repo.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error("getIssueIDsByRepoID failed: %v", err) | 		log.Error("getIssueIDsByRepoID failed: %v", err) | ||||||
| 		return | 		return | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import ( | |||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/models/db" | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| @@ -33,7 +34,7 @@ func NewNotifier() base.Notifier { | |||||||
| 	return &actionNotifier{} | 	return &actionNotifier{} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) { | func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) { | ||||||
| 	if err := issue.LoadPoster(); err != nil { | 	if err := issue.LoadPoster(); err != nil { | ||||||
| 		log.Error("issue.LoadPoster: %v", err) | 		log.Error("issue.LoadPoster: %v", err) | ||||||
| 		return | 		return | ||||||
| @@ -58,7 +59,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_mo | |||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeStatus notifies close or reopen issue to notifiers | // NotifyIssueChangeStatus notifies close or reopen issue to notifiers | ||||||
| func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) { | func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { | ||||||
| 	// Compose comment action, could be plain comment, close or reopen issue/pull request. | 	// Compose comment action, could be plain comment, close or reopen issue/pull request. | ||||||
| 	// This object will be used to notify watchers in the end of function. | 	// This object will be used to notify watchers in the end of function. | ||||||
| 	act := &models.Action{ | 	act := &models.Action{ | ||||||
| @@ -92,7 +93,7 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *m | |||||||
|  |  | ||||||
| // NotifyCreateIssueComment notifies comment on an issue to notifiers | // NotifyCreateIssueComment notifies comment on an issue to notifiers | ||||||
| func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 	issue *models.Issue, comment *models.Comment, mentions []*user_model.User, | 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, | ||||||
| ) { | ) { | ||||||
| 	act := &models.Action{ | 	act := &models.Action{ | ||||||
| 		ActUserID: doer.ID, | 		ActUserID: doer.ID, | ||||||
| @@ -126,7 +127,7 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *r | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *actionNotifier) NotifyNewPullRequest(pull *models.PullRequest, mentions []*user_model.User) { | func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, mentions []*user_model.User) { | ||||||
| 	if err := pull.LoadIssue(); err != nil { | 	if err := pull.LoadIssue(); err != nil { | ||||||
| 		log.Error("pull.LoadIssue: %v", err) | 		log.Error("pull.LoadIssue: %v", err) | ||||||
| 		return | 		return | ||||||
| @@ -207,7 +208,7 @@ func (a *actionNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, re | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) { | func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("actionNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("actionNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
| @@ -239,7 +240,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if review.Type != models.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" { | 	if review.Type != issues_model.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" { | ||||||
| 		action := &models.Action{ | 		action := &models.Action{ | ||||||
| 			ActUserID: review.Reviewer.ID, | 			ActUserID: review.Reviewer.ID, | ||||||
| 			ActUser:   review.Reviewer, | 			ActUser:   review.Reviewer, | ||||||
| @@ -252,9 +253,9 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		switch review.Type { | 		switch review.Type { | ||||||
| 		case models.ReviewTypeApprove: | 		case issues_model.ReviewTypeApprove: | ||||||
| 			action.OpType = models.ActionApprovePullRequest | 			action.OpType = models.ActionApprovePullRequest | ||||||
| 		case models.ReviewTypeReject: | 		case issues_model.ReviewTypeReject: | ||||||
| 			action.OpType = models.ActionRejectPullRequest | 			action.OpType = models.ActionRejectPullRequest | ||||||
| 		default: | 		default: | ||||||
| 			action.OpType = models.ActionCommentPull | 			action.OpType = models.ActionCommentPull | ||||||
| @@ -268,7 +269,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *models.PullRequest, review | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) { | func (*actionNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) { | ||||||
| 	if err := models.NotifyWatchers(&models.Action{ | 	if err := models.NotifyWatchers(&models.Action{ | ||||||
| 		ActUserID: doer.ID, | 		ActUserID: doer.ID, | ||||||
| 		ActUser:   doer, | 		ActUser:   doer, | ||||||
| @@ -282,7 +283,7 @@ func (*actionNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) { | func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { | ||||||
| 	reviewerName := review.Reviewer.Name | 	reviewerName := review.Reviewer.Name | ||||||
| 	if len(review.OriginalAuthor) > 0 { | 	if len(review.OriginalAuthor) > 0 { | ||||||
| 		reviewerName = review.OriginalAuthor | 		reviewerName = review.OriginalAuthor | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package base | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -21,30 +22,30 @@ type Notifier interface { | |||||||
| 	NotifyForkRepository(doer *user_model.User, oldRepo, repo *repo_model.Repository) | 	NotifyForkRepository(doer *user_model.User, oldRepo, repo *repo_model.Repository) | ||||||
| 	NotifyRenameRepository(doer *user_model.User, repo *repo_model.Repository, oldRepoName string) | 	NotifyRenameRepository(doer *user_model.User, repo *repo_model.Repository, oldRepoName string) | ||||||
| 	NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) | 	NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) | ||||||
| 	NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) | 	NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) | ||||||
| 	NotifyIssueChangeStatus(*user_model.User, *models.Issue, *models.Comment, bool) | 	NotifyIssueChangeStatus(*user_model.User, *issues_model.Issue, *issues_model.Comment, bool) | ||||||
| 	NotifyDeleteIssue(*user_model.User, *models.Issue) | 	NotifyDeleteIssue(*user_model.User, *issues_model.Issue) | ||||||
| 	NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) | 	NotifyIssueChangeMilestone(doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) | ||||||
| 	NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) | 	NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) | ||||||
| 	NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) | 	NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) | ||||||
| 	NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) | 	NotifyIssueChangeContent(doer *user_model.User, issue *issues_model.Issue, oldContent string) | ||||||
| 	NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) | 	NotifyIssueClearLabels(doer *user_model.User, issue *issues_model.Issue) | ||||||
| 	NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) | 	NotifyIssueChangeTitle(doer *user_model.User, issue *issues_model.Issue, oldTitle string) | ||||||
| 	NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string) | 	NotifyIssueChangeRef(doer *user_model.User, issue *issues_model.Issue, oldRef string) | ||||||
| 	NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue, | 	NotifyIssueChangeLabels(doer *user_model.User, issue *issues_model.Issue, | ||||||
| 		addedLabels, removedLabels []*models.Label) | 		addedLabels, removedLabels []*issues_model.Label) | ||||||
| 	NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) | 	NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) | ||||||
| 	NotifyMergePullRequest(*models.PullRequest, *user_model.User) | 	NotifyMergePullRequest(*issues_model.PullRequest, *user_model.User) | ||||||
| 	NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) | 	NotifyPullRequestSynchronized(doer *user_model.User, pr *issues_model.PullRequest) | ||||||
| 	NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) | 	NotifyPullRequestReview(pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) | ||||||
| 	NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) | 	NotifyPullRequestCodeComment(pr *issues_model.PullRequest, comment *issues_model.Comment, mentions []*user_model.User) | ||||||
| 	NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) | 	NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *issues_model.PullRequest, oldBranch string) | ||||||
| 	NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) | 	NotifyPullRequestPushCommits(doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) | ||||||
| 	NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) | 	NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) | ||||||
| 	NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | 	NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 		issue *models.Issue, comment *models.Comment, mentions []*user_model.User) | 		issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User) | ||||||
| 	NotifyUpdateComment(*user_model.User, *models.Comment, string) | 	NotifyUpdateComment(*user_model.User, *issues_model.Comment, string) | ||||||
| 	NotifyDeleteComment(*user_model.User, *models.Comment) | 	NotifyDeleteComment(*user_model.User, *issues_model.Comment) | ||||||
| 	NotifyNewRelease(rel *models.Release) | 	NotifyNewRelease(rel *models.Release) | ||||||
| 	NotifyUpdateRelease(doer *user_model.User, rel *models.Release) | 	NotifyUpdateRelease(doer *user_model.User, rel *models.Release) | ||||||
| 	NotifyDeleteRelease(doer *user_model.User, rel *models.Release) | 	NotifyDeleteRelease(doer *user_model.User, rel *models.Release) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package base | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -23,59 +24,59 @@ func (*NullNotifier) Run() { | |||||||
|  |  | ||||||
| // NotifyCreateIssueComment places a place holder function | // NotifyCreateIssueComment places a place holder function | ||||||
| func (*NullNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | func (*NullNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 	issue *models.Issue, comment *models.Comment, mentions []*user_model.User) { | 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyNewIssue places a place holder function | // NotifyNewIssue places a place holder function | ||||||
| func (*NullNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) { | func (*NullNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeStatus places a place holder function | // NotifyIssueChangeStatus places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) { | func (*NullNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyDeleteIssue notify when some issue deleted | // NotifyDeleteIssue notify when some issue deleted | ||||||
| func (*NullNotifier) NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) { | func (*NullNotifier) NotifyDeleteIssue(doer *user_model.User, issue *issues_model.Issue) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyNewPullRequest places a place holder function | // NotifyNewPullRequest places a place holder function | ||||||
| func (*NullNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) { | func (*NullNotifier) NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestReview places a place holder function | // NotifyPullRequestReview places a place holder function | ||||||
| func (*NullNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*user_model.User) { | func (*NullNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestCodeComment places a place holder function | // NotifyPullRequestCodeComment places a place holder function | ||||||
| func (*NullNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) { | func (*NullNotifier) NotifyPullRequestCodeComment(pr *issues_model.PullRequest, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyMergePullRequest places a place holder function | // NotifyMergePullRequest places a place holder function | ||||||
| func (*NullNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) { | func (*NullNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestSynchronized places a place holder function | // NotifyPullRequestSynchronized places a place holder function | ||||||
| func (*NullNotifier) NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) { | func (*NullNotifier) NotifyPullRequestSynchronized(doer *user_model.User, pr *issues_model.PullRequest) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestChangeTargetBranch places a place holder function | // NotifyPullRequestChangeTargetBranch places a place holder function | ||||||
| func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) { | func (*NullNotifier) NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *issues_model.PullRequest, oldBranch string) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | ||||||
| func (*NullNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) { | func (*NullNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin | // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin | ||||||
| func (*NullNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) { | func (*NullNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyUpdateComment places a place holder function | // NotifyUpdateComment places a place holder function | ||||||
| func (*NullNotifier) NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) { | func (*NullNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyDeleteComment places a place holder function | // NotifyDeleteComment places a place holder function | ||||||
| func (*NullNotifier) NotifyDeleteComment(doer *user_model.User, c *models.Comment) { | func (*NullNotifier) NotifyDeleteComment(doer *user_model.User, c *issues_model.Comment) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyNewRelease places a place holder function | // NotifyNewRelease places a place holder function | ||||||
| @@ -91,36 +92,36 @@ func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Rele | |||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeMilestone places a place holder function | // NotifyIssueChangeMilestone places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) { | func (*NullNotifier) NotifyIssueChangeMilestone(doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeContent places a place holder function | // NotifyIssueChangeContent places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) { | func (*NullNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *issues_model.Issue, oldContent string) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeAssignee places a place holder function | // NotifyIssueChangeAssignee places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) { | func (*NullNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullReviewRequest places a place holder function | // NotifyPullReviewRequest places a place holder function | ||||||
| func (*NullNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) { | func (*NullNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueClearLabels places a place holder function | // NotifyIssueClearLabels places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) { | func (*NullNotifier) NotifyIssueClearLabels(doer *user_model.User, issue *issues_model.Issue) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeTitle places a place holder function | // NotifyIssueChangeTitle places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) { | func (*NullNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *issues_model.Issue, oldTitle string) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeRef places a place holder function | // NotifyIssueChangeRef places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldTitle string) { | func (*NullNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *issues_model.Issue, oldTitle string) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeLabels places a place holder function | // NotifyIssueChangeLabels places a place holder function | ||||||
| func (*NullNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue, | func (*NullNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *issues_model.Issue, | ||||||
| 	addedLabels, removedLabels []*models.Label) { | 	addedLabels, removedLabels []*issues_model.Label) { | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyCreateRepository places a place holder function | // NotifyCreateRepository places a place holder function | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
| package indexer | package indexer | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/git" | 	"code.gitea.io/gitea/modules/git" | ||||||
| @@ -30,9 +30,9 @@ func NewNotifier() base.Notifier { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | func (r *indexerNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 	issue *models.Issue, comment *models.Comment, mentions []*user_model.User, | 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, | ||||||
| ) { | ) { | ||||||
| 	if comment.Type == models.CommentTypeComment { | 	if comment.Type == issues_model.CommentTypeComment { | ||||||
| 		if issue.Comments == nil { | 		if issue.Comments == nil { | ||||||
| 			if err := issue.LoadDiscussComments(); err != nil { | 			if err := issue.LoadDiscussComments(); err != nil { | ||||||
| 				log.Error("LoadComments failed: %v", err) | 				log.Error("LoadComments failed: %v", err) | ||||||
| @@ -46,16 +46,16 @@ func (r *indexerNotifier) NotifyCreateIssueComment(doer *user_model.User, repo * | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) { | func (r *indexerNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) { | ||||||
| 	issue_indexer.UpdateIssueIndexer(issue) | 	issue_indexer.UpdateIssueIndexer(issue) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) { | func (r *indexerNotifier) NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) { | ||||||
| 	issue_indexer.UpdateIssueIndexer(pr.Issue) | 	issue_indexer.UpdateIssueIndexer(pr.Issue) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) { | func (r *indexerNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) { | ||||||
| 	if c.Type == models.CommentTypeComment { | 	if c.Type == issues_model.CommentTypeComment { | ||||||
| 		var found bool | 		var found bool | ||||||
| 		if c.Issue.Comments != nil { | 		if c.Issue.Comments != nil { | ||||||
| 			for i := 0; i < len(c.Issue.Comments); i++ { | 			for i := 0; i < len(c.Issue.Comments); i++ { | ||||||
| @@ -78,8 +78,8 @@ func (r *indexerNotifier) NotifyUpdateComment(doer *user_model.User, c *models.C | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyDeleteComment(doer *user_model.User, comment *models.Comment) { | func (r *indexerNotifier) NotifyDeleteComment(doer *user_model.User, comment *issues_model.Comment) { | ||||||
| 	if comment.Type == models.CommentTypeComment { | 	if comment.Type == issues_model.CommentTypeComment { | ||||||
| 		if err := comment.LoadIssue(); err != nil { | 		if err := comment.LoadIssue(); err != nil { | ||||||
| 			log.Error("LoadIssue: %v", err) | 			log.Error("LoadIssue: %v", err) | ||||||
| 			return | 			return | ||||||
| @@ -142,14 +142,14 @@ func (r *indexerNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) { | func (r *indexerNotifier) NotifyIssueChangeContent(doer *user_model.User, issue *issues_model.Issue, oldContent string) { | ||||||
| 	issue_indexer.UpdateIssueIndexer(issue) | 	issue_indexer.UpdateIssueIndexer(issue) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) { | func (r *indexerNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *issues_model.Issue, oldTitle string) { | ||||||
| 	issue_indexer.UpdateIssueIndexer(issue) | 	issue_indexer.UpdateIssueIndexer(issue) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *indexerNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string) { | func (r *indexerNotifier) NotifyIssueChangeRef(doer *user_model.User, issue *issues_model.Issue, oldRef string) { | ||||||
| 	issue_indexer.UpdateIssueIndexer(issue) | 	issue_indexer.UpdateIssueIndexer(issue) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| 	"code.gitea.io/gitea/modules/graceful" | 	"code.gitea.io/gitea/modules/graceful" | ||||||
| @@ -29,21 +30,21 @@ func NewNotifier() base.Notifier { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 	issue *models.Issue, comment *models.Comment, mentions []*user_model.User, | 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, | ||||||
| ) { | ) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyCreateIssueComment Issue[%d] #%d in [%d]", issue.ID, issue.Index, issue.RepoID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyCreateIssueComment Issue[%d] #%d in [%d]", issue.ID, issue.Index, issue.RepoID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
| 	var act models.ActionType | 	var act models.ActionType | ||||||
| 	if comment.Type == models.CommentTypeClose { | 	if comment.Type == issues_model.CommentTypeClose { | ||||||
| 		act = models.ActionCloseIssue | 		act = models.ActionCloseIssue | ||||||
| 	} else if comment.Type == models.CommentTypeReopen { | 	} else if comment.Type == issues_model.CommentTypeReopen { | ||||||
| 		act = models.ActionReopenIssue | 		act = models.ActionReopenIssue | ||||||
| 	} else if comment.Type == models.CommentTypeComment { | 	} else if comment.Type == issues_model.CommentTypeComment { | ||||||
| 		act = models.ActionCommentIssue | 		act = models.ActionCommentIssue | ||||||
| 	} else if comment.Type == models.CommentTypeCode { | 	} else if comment.Type == issues_model.CommentTypeCode { | ||||||
| 		act = models.ActionCommentIssue | 		act = models.ActionCommentIssue | ||||||
| 	} else if comment.Type == models.CommentTypePullRequestPush { | 	} else if comment.Type == issues_model.CommentTypePullRequestPush { | ||||||
| 		act = 0 | 		act = 0 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -52,13 +53,13 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) { | func (m *mailNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) { | ||||||
| 	if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue, mentions); err != nil { | 	if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue, mentions); err != nil { | ||||||
| 		log.Error("MailParticipants: %v", err) | 		log.Error("MailParticipants: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, isClosed bool) { | func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { | ||||||
| 	var actionType models.ActionType | 	var actionType models.ActionType | ||||||
| 	if issue.IsPull { | 	if issue.IsPull { | ||||||
| 		if isClosed { | 		if isClosed { | ||||||
| @@ -79,34 +80,34 @@ func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *mod | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) { | func (m *mailNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *issues_model.Issue, oldTitle string) { | ||||||
| 	if err := issue.LoadPullRequest(); err != nil { | 	if err := issue.LoadPullRequest(); err != nil { | ||||||
| 		log.Error("issue.LoadPullRequest: %v", err) | 		log.Error("issue.LoadPullRequest: %v", err) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	if issue.IsPull && models.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { | 	if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { | ||||||
| 		if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil { | 		if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil { | ||||||
| 			log.Error("MailParticipants: %v", err) | 			log.Error("MailParticipants: %v", err) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) { | func (m *mailNotifier) NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) { | ||||||
| 	if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil { | 	if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil { | ||||||
| 		log.Error("MailParticipants: %v", err) | 		log.Error("MailParticipants: %v", err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models.Review, comment *models.Comment, mentions []*user_model.User) { | func (m *mailNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
| 	var act models.ActionType | 	var act models.ActionType | ||||||
| 	if comment.Type == models.CommentTypeClose { | 	if comment.Type == issues_model.CommentTypeClose { | ||||||
| 		act = models.ActionCloseIssue | 		act = models.ActionCloseIssue | ||||||
| 	} else if comment.Type == models.CommentTypeReopen { | 	} else if comment.Type == issues_model.CommentTypeReopen { | ||||||
| 		act = models.ActionReopenIssue | 		act = models.ActionReopenIssue | ||||||
| 	} else if comment.Type == models.CommentTypeComment { | 	} else if comment.Type == issues_model.CommentTypeComment { | ||||||
| 		act = models.ActionCommentPull | 		act = models.ActionCommentPull | ||||||
| 	} | 	} | ||||||
| 	if err := mailer.MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil { | 	if err := mailer.MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil { | ||||||
| @@ -114,7 +115,7 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) { | func (m *mailNotifier) NotifyPullRequestCodeComment(pr *issues_model.PullRequest, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestCodeComment Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestCodeComment Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
| @@ -123,7 +124,7 @@ func (m *mailNotifier) NotifyPullRequestCodeComment(pr *models.PullRequest, comm | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) { | func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { | ||||||
| 	// mail only sent to added assignees and not self-assignee | 	// mail only sent to added assignees and not self-assignee | ||||||
| 	if !removed && doer.ID != assignee.ID && (assignee.EmailNotifications() == user_model.EmailNotificationsEnabled || assignee.EmailNotifications() == user_model.EmailNotificationsOnMention) { | 	if !removed && doer.ID != assignee.ID && (assignee.EmailNotifications() == user_model.EmailNotificationsEnabled || assignee.EmailNotifications() == user_model.EmailNotificationsOnMention) { | ||||||
| 		ct := fmt.Sprintf("Assigned #%d.", issue.Index) | 		ct := fmt.Sprintf("Assigned #%d.", issue.Index) | ||||||
| @@ -133,7 +134,7 @@ func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *m | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) { | func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { | ||||||
| 	if isRequest && doer.ID != reviewer.ID && (reviewer.EmailNotifications() == user_model.EmailNotificationsEnabled || reviewer.EmailNotifications() == user_model.EmailNotificationsOnMention) { | 	if isRequest && doer.ID != reviewer.ID && (reviewer.EmailNotifications() == user_model.EmailNotificationsEnabled || reviewer.EmailNotifications() == user_model.EmailNotificationsOnMention) { | ||||||
| 		ct := fmt.Sprintf("Requested to review %s.", issue.HTMLURL()) | 		ct := fmt.Sprintf("Requested to review %s.", issue.HTMLURL()) | ||||||
| 		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{reviewer}); err != nil { | 		if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{reviewer}); err != nil { | ||||||
| @@ -142,7 +143,7 @@ func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *mod | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) { | func (m *mailNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) { | ||||||
| 	if err := pr.LoadIssue(); err != nil { | 	if err := pr.LoadIssue(); err != nil { | ||||||
| 		log.Error("pr.LoadIssue: %v", err) | 		log.Error("pr.LoadIssue: %v", err) | ||||||
| 		return | 		return | ||||||
| @@ -152,7 +153,7 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *models.PullRequest, doer *user | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) { | func (m *mailNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestPushCommits Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestPushCommits Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
| @@ -179,7 +180,7 @@ func (m *mailNotifier) NotifyPullRequestPushCommits(doer *user_model.User, pr *m | |||||||
| 	m.NotifyCreateIssueComment(doer, comment.Issue.Repo, comment.Issue, comment, nil) | 	m.NotifyCreateIssueComment(doer, comment.Issue.Repo, comment.Issue, comment, nil) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (m *mailNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) { | func (m *mailNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { | ||||||
| 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRevieweDismiss Review[%d] in Issue[%d]", review.ID, review.IssueID)) | 	ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRevieweDismiss Review[%d] in Issue[%d]", review.ID, review.IssueID)) | ||||||
| 	defer finished() | 	defer finished() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ package notification | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
|  | 	issues_model "code.gitea.io/gitea/models/issues" | ||||||
| 	packages_model "code.gitea.io/gitea/models/packages" | 	packages_model "code.gitea.io/gitea/models/packages" | ||||||
| 	repo_model "code.gitea.io/gitea/models/repo" | 	repo_model "code.gitea.io/gitea/models/repo" | ||||||
| 	user_model "code.gitea.io/gitea/models/user" | 	user_model "code.gitea.io/gitea/models/user" | ||||||
| @@ -40,7 +41,7 @@ func NewContext() { | |||||||
|  |  | ||||||
| // NotifyCreateIssueComment notifies issue comment related message to notifiers | // NotifyCreateIssueComment notifies issue comment related message to notifiers | ||||||
| func NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | func NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository, | ||||||
| 	issue *models.Issue, comment *models.Comment, mentions []*user_model.User, | 	issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, | ||||||
| ) { | ) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyCreateIssueComment(doer, repo, issue, comment, mentions) | 		notifier.NotifyCreateIssueComment(doer, repo, issue, comment, mentions) | ||||||
| @@ -48,91 +49,91 @@ func NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository | |||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyNewIssue notifies new issue to notifiers | // NotifyNewIssue notifies new issue to notifiers | ||||||
| func NotifyNewIssue(issue *models.Issue, mentions []*user_model.User) { | func NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyNewIssue(issue, mentions) | 		notifier.NotifyNewIssue(issue, mentions) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeStatus notifies close or reopen issue to notifiers | // NotifyIssueChangeStatus notifies close or reopen issue to notifiers | ||||||
| func NotifyIssueChangeStatus(doer *user_model.User, issue *models.Issue, actionComment *models.Comment, closeOrReopen bool) { | func NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeStatus(doer, issue, actionComment, closeOrReopen) | 		notifier.NotifyIssueChangeStatus(doer, issue, actionComment, closeOrReopen) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyDeleteIssue notify when some issue deleted | // NotifyDeleteIssue notify when some issue deleted | ||||||
| func NotifyDeleteIssue(doer *user_model.User, issue *models.Issue) { | func NotifyDeleteIssue(doer *user_model.User, issue *issues_model.Issue) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyDeleteIssue(doer, issue) | 		notifier.NotifyDeleteIssue(doer, issue) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyMergePullRequest notifies merge pull request to notifiers | // NotifyMergePullRequest notifies merge pull request to notifiers | ||||||
| func NotifyMergePullRequest(pr *models.PullRequest, doer *user_model.User) { | func NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyMergePullRequest(pr, doer) | 		notifier.NotifyMergePullRequest(pr, doer) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyNewPullRequest notifies new pull request to notifiers | // NotifyNewPullRequest notifies new pull request to notifiers | ||||||
| func NotifyNewPullRequest(pr *models.PullRequest, mentions []*user_model.User) { | func NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyNewPullRequest(pr, mentions) | 		notifier.NotifyNewPullRequest(pr, mentions) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestSynchronized notifies Synchronized pull request | // NotifyPullRequestSynchronized notifies Synchronized pull request | ||||||
| func NotifyPullRequestSynchronized(doer *user_model.User, pr *models.PullRequest) { | func NotifyPullRequestSynchronized(doer *user_model.User, pr *issues_model.PullRequest) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRequestSynchronized(doer, pr) | 		notifier.NotifyPullRequestSynchronized(doer, pr) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestReview notifies new pull request review | // NotifyPullRequestReview notifies new pull request review | ||||||
| func NotifyPullRequestReview(pr *models.PullRequest, review *models.Review, comment *models.Comment, mentions []*user_model.User) { | func NotifyPullRequestReview(pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRequestReview(pr, review, comment, mentions) | 		notifier.NotifyPullRequestReview(pr, review, comment, mentions) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestCodeComment notifies new pull request code comment | // NotifyPullRequestCodeComment notifies new pull request code comment | ||||||
| func NotifyPullRequestCodeComment(pr *models.PullRequest, comment *models.Comment, mentions []*user_model.User) { | func NotifyPullRequestCodeComment(pr *issues_model.PullRequest, comment *issues_model.Comment, mentions []*user_model.User) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRequestCodeComment(pr, comment, mentions) | 		notifier.NotifyPullRequestCodeComment(pr, comment, mentions) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestChangeTargetBranch notifies when a pull request's target branch was changed | // NotifyPullRequestChangeTargetBranch notifies when a pull request's target branch was changed | ||||||
| func NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *models.PullRequest, oldBranch string) { | func NotifyPullRequestChangeTargetBranch(doer *user_model.User, pr *issues_model.PullRequest, oldBranch string) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRequestChangeTargetBranch(doer, pr, oldBranch) | 		notifier.NotifyPullRequestChangeTargetBranch(doer, pr, oldBranch) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | // NotifyPullRequestPushCommits notifies when push commits to pull request's head branch | ||||||
| func NotifyPullRequestPushCommits(doer *user_model.User, pr *models.PullRequest, comment *models.Comment) { | func NotifyPullRequestPushCommits(doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRequestPushCommits(doer, pr, comment) | 		notifier.NotifyPullRequestPushCommits(doer, pr, comment) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin | // NotifyPullRevieweDismiss notifies when a review was dismissed by repo admin | ||||||
| func NotifyPullRevieweDismiss(doer *user_model.User, review *models.Review, comment *models.Comment) { | func NotifyPullRevieweDismiss(doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullRevieweDismiss(doer, review, comment) | 		notifier.NotifyPullRevieweDismiss(doer, review, comment) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyUpdateComment notifies update comment to notifiers | // NotifyUpdateComment notifies update comment to notifiers | ||||||
| func NotifyUpdateComment(doer *user_model.User, c *models.Comment, oldContent string) { | func NotifyUpdateComment(doer *user_model.User, c *issues_model.Comment, oldContent string) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyUpdateComment(doer, c, oldContent) | 		notifier.NotifyUpdateComment(doer, c, oldContent) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyDeleteComment notifies delete comment to notifiers | // NotifyDeleteComment notifies delete comment to notifiers | ||||||
| func NotifyDeleteComment(doer *user_model.User, c *models.Comment) { | func NotifyDeleteComment(doer *user_model.User, c *issues_model.Comment) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyDeleteComment(doer, c) | 		notifier.NotifyDeleteComment(doer, c) | ||||||
| 	} | 	} | ||||||
| @@ -160,57 +161,57 @@ func NotifyDeleteRelease(doer *user_model.User, rel *models.Release) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeMilestone notifies change milestone to notifiers | // NotifyIssueChangeMilestone notifies change milestone to notifiers | ||||||
| func NotifyIssueChangeMilestone(doer *user_model.User, issue *models.Issue, oldMilestoneID int64) { | func NotifyIssueChangeMilestone(doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeMilestone(doer, issue, oldMilestoneID) | 		notifier.NotifyIssueChangeMilestone(doer, issue, oldMilestoneID) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeContent notifies change content to notifiers | // NotifyIssueChangeContent notifies change content to notifiers | ||||||
| func NotifyIssueChangeContent(doer *user_model.User, issue *models.Issue, oldContent string) { | func NotifyIssueChangeContent(doer *user_model.User, issue *issues_model.Issue, oldContent string) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeContent(doer, issue, oldContent) | 		notifier.NotifyIssueChangeContent(doer, issue, oldContent) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeAssignee notifies change content to notifiers | // NotifyIssueChangeAssignee notifies change content to notifiers | ||||||
| func NotifyIssueChangeAssignee(doer *user_model.User, issue *models.Issue, assignee *user_model.User, removed bool, comment *models.Comment) { | func NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeAssignee(doer, issue, assignee, removed, comment) | 		notifier.NotifyIssueChangeAssignee(doer, issue, assignee, removed, comment) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyPullReviewRequest notifies Request Review change | // NotifyPullReviewRequest notifies Request Review change | ||||||
| func NotifyPullReviewRequest(doer *user_model.User, issue *models.Issue, reviewer *user_model.User, isRequest bool, comment *models.Comment) { | func NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyPullReviewRequest(doer, issue, reviewer, isRequest, comment) | 		notifier.NotifyPullReviewRequest(doer, issue, reviewer, isRequest, comment) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueClearLabels notifies clear labels to notifiers | // NotifyIssueClearLabels notifies clear labels to notifiers | ||||||
| func NotifyIssueClearLabels(doer *user_model.User, issue *models.Issue) { | func NotifyIssueClearLabels(doer *user_model.User, issue *issues_model.Issue) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueClearLabels(doer, issue) | 		notifier.NotifyIssueClearLabels(doer, issue) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeTitle notifies change title to notifiers | // NotifyIssueChangeTitle notifies change title to notifiers | ||||||
| func NotifyIssueChangeTitle(doer *user_model.User, issue *models.Issue, oldTitle string) { | func NotifyIssueChangeTitle(doer *user_model.User, issue *issues_model.Issue, oldTitle string) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeTitle(doer, issue, oldTitle) | 		notifier.NotifyIssueChangeTitle(doer, issue, oldTitle) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeRef notifies change reference to notifiers | // NotifyIssueChangeRef notifies change reference to notifiers | ||||||
| func NotifyIssueChangeRef(doer *user_model.User, issue *models.Issue, oldRef string) { | func NotifyIssueChangeRef(doer *user_model.User, issue *issues_model.Issue, oldRef string) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeRef(doer, issue, oldRef) | 		notifier.NotifyIssueChangeRef(doer, issue, oldRef) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| // NotifyIssueChangeLabels notifies change labels to notifiers | // NotifyIssueChangeLabels notifies change labels to notifiers | ||||||
| func NotifyIssueChangeLabels(doer *user_model.User, issue *models.Issue, | func NotifyIssueChangeLabels(doer *user_model.User, issue *issues_model.Issue, | ||||||
| 	addedLabels, removedLabels []*models.Label, | 	addedLabels, removedLabels []*issues_model.Label, | ||||||
| ) { | ) { | ||||||
| 	for _, notifier := range notifiers { | 	for _, notifier := range notifiers { | ||||||
| 		notifier.NotifyIssueChangeLabels(doer, issue, addedLabels, removedLabels) | 		notifier.NotifyIssueChangeLabels(doer, issue, addedLabels, removedLabels) | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user