mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-17 19:10:22 +03:00
feat(api): Add assignees APIs (#37330)
Follow https://docs.github.com/en/enterprise-server@3.20/rest/issues/assignees?apiVersion=2022-11-28 Fix #33576 And it also fixed some possible dead-lock problem. --------- Signed-off-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Nicolas <bircni@icloud.com> Co-authored-by: Zettat123 <zettat123@gmail.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
auth_model "gitea.dev/models/auth"
|
||||
"gitea.dev/models/db"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unittest"
|
||||
@@ -35,6 +36,7 @@ func TestAPIIssue(t *testing.T) {
|
||||
t.Run("IssueContentVersion", testAPIIssueContentVersion)
|
||||
t.Run("CreateIssue", testAPICreateIssue)
|
||||
t.Run("CreateIssueParallel", testAPICreateIssueParallel)
|
||||
t.Run("IssueAssignees", testAPIIssueAssignees)
|
||||
t.Run("IssueProjects", testAPIIssueProjects)
|
||||
}
|
||||
|
||||
@@ -495,6 +497,91 @@ func testAPIIssueContentVersion(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func testAPIIssueAssignees(t *testing.T) {
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
|
||||
session := loginUser(t, owner.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue)
|
||||
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assignees", owner.Name, repo.Name, issue.Index)
|
||||
getAssigneeIDs := func(issueID int64) []int64 {
|
||||
assigneeIDs, err := issues_model.GetAssigneeIDsByIssue(t.Context(), issueID)
|
||||
assert.NoError(t, err)
|
||||
return assigneeIDs
|
||||
}
|
||||
|
||||
t.Run("NonWriter", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueAssigneesOption{Assignees: []string{"user40"}}).
|
||||
AddTokenAuth(getUserToken(t, "user5", auth_model.AccessTokenScopeWriteIssue))
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
})
|
||||
|
||||
t.Run("MissingIssue", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/99999/assignees", owner.Name, repo.Name), &api.IssueAssigneesOption{Assignees: []string{"user40"}}).
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
})
|
||||
|
||||
t.Run("UnknownAssignee", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueAssigneesOption{Assignees: []string{"does-not-exist"}}).
|
||||
AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||
apiErr := DecodeJSON(t, resp, &api.APIError{})
|
||||
assert.Equal(t, "user does not exist [uid: 0, name: does-not-exist]", apiErr.Message)
|
||||
})
|
||||
|
||||
t.Run("OrganizationAssignee", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueAssigneesOption{Assignees: []string{"org3"}}).
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
|
||||
checkReq := NewRequest(t, "GET", fmt.Sprintf("%s/%s", urlStr, "org3")).AddTokenAuth(token)
|
||||
MakeRequest(t, checkReq, http.StatusBadRequest)
|
||||
})
|
||||
|
||||
t.Run("BlockedAssigneeIsAtomic", func(t *testing.T) {
|
||||
blockedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5})
|
||||
blockedURL := fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assignees", owner.Name, repo.Name, blockedIssue.Index)
|
||||
blockedToken := getUserToken(t, "user40", auth_model.AccessTokenScopeWriteIssue)
|
||||
|
||||
assert.Empty(t, getAssigneeIDs(blockedIssue.ID))
|
||||
assert.NoError(t, db.Insert(t.Context(), &user_model.Blocking{
|
||||
BlockerID: owner.ID,
|
||||
BlockeeID: 40,
|
||||
}))
|
||||
|
||||
req := NewRequestWithJSON(t, "POST", blockedURL, &api.IssueAssigneesOption{Assignees: []string{"user1", "user2"}}).
|
||||
AddTokenAuth(blockedToken)
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
assert.Empty(t, getAssigneeIDs(blockedIssue.ID))
|
||||
})
|
||||
|
||||
t.Run("HappyPath", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.IssueAssigneesOption{Assignees: []string{"user40"}}).
|
||||
AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
apiIssue := DecodeJSON(t, resp, &api.Issue{})
|
||||
assert.Len(t, apiIssue.Assignees, 2)
|
||||
assert.ElementsMatch(t, []int64{1, 40}, []int64{apiIssue.Assignees[0].ID, apiIssue.Assignees[1].ID})
|
||||
|
||||
checkReq := NewRequest(t, "GET", fmt.Sprintf("%s/%s", urlStr, "user40")).AddTokenAuth(token)
|
||||
MakeRequest(t, checkReq, http.StatusNoContent)
|
||||
|
||||
// This endpoint checks assignability, not current assignment membership.
|
||||
checkReq = NewRequest(t, "GET", fmt.Sprintf("%s/%s", urlStr, "user5")).AddTokenAuth(token)
|
||||
MakeRequest(t, checkReq, http.StatusNoContent)
|
||||
|
||||
req = NewRequestWithJSON(t, "DELETE", urlStr, &api.IssueAssigneesOption{Assignees: []string{"user1"}}).
|
||||
AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
apiIssue = DecodeJSON(t, resp, &api.Issue{})
|
||||
assert.Len(t, apiIssue.Assignees, 1)
|
||||
assert.Equal(t, int64(40), apiIssue.Assignees[0].ID)
|
||||
})
|
||||
}
|
||||
|
||||
func testAPIIssueProjects(t *testing.T) {
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
|
||||
|
||||
@@ -721,11 +721,25 @@ func TestAPIRepoGetAssignees(t *testing.T) {
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
|
||||
|
||||
req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees", user.Name, repo.Name).
|
||||
AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
assignees := DecodeJSON(t, resp, []*api.User{})
|
||||
assert.Len(t, assignees, 2)
|
||||
assert.Len(t, assignees, 1)
|
||||
|
||||
assignee := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees/%s", user.Name, repo.Name, assignee.Name).
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
|
||||
nonAssignee := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees/%s", user.Name, repo.Name, nonAssignee.Name).
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
req = NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees/%s", user.Name, repo.Name, "org3").
|
||||
AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user