From b49bffd7c850e997a4b504fcd519fdd90acb14ea Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 16 Jun 2021 01:14:29 +0200 Subject: [PATCH 1/5] API: expose repo.GetReviewers() & repo.GetAssignees() --- modules/convert/user.go | 8 ++++ routers/api/v1/api.go | 2 + routers/api/v1/repo/collaborators.go | 60 +++++++++++++++++++++++++ routers/api/v1/user/user.go | 8 +--- templates/swagger/v1_json.tmpl | 66 ++++++++++++++++++++++++++++ 5 files changed, 137 insertions(+), 7 deletions(-) diff --git a/modules/convert/user.go b/modules/convert/user.go index 088ede5add507..423036a71f7c0 100644 --- a/modules/convert/user.go +++ b/modules/convert/user.go @@ -25,6 +25,14 @@ func ToUser(user, doer *models.User) *api.User { return toUser(user, signed, authed) } +func ToUsers(doer *models.User, users []*models.User) []*api.User { + result := make([]*api.User, len(users)) + for i := range users { + result[i] = ToUser(users[i], doer) + } + return result +} + // ToUserWithAccessMode convert models.User to api.User // AccessMode is not none show add some more information func ToUserWithAccessMode(user *models.User, accessMode models.AccessMode) *api.User { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index acee6329afd07..82aa9a1bc3c94 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -746,6 +746,8 @@ func Routes() *web.Route { Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) }, reqToken()) + m.Get("/assignees", reqToken(), repo.GetAssignees) + m.Get("/reviewers", reqToken(), repo.GetReviewers) m.Group("/teams", func() { m.Get("", reqAnyRepoReader(), repo.ListTeams) m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index d0936019faddf..078af1f6ff8e9 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -221,3 +221,63 @@ func DeleteCollaborator(ctx *context.APIContext) { } ctx.Status(http.StatusNoContent) } + +// GetReviewers return all users that can be requested to review in this repo +func GetReviewers(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/reviewers repository repoGetReviewers + // --- + // summary: Return all users that can be requested to review in this repo + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/UserList" + + reviewers, err := ctx.Repo.Repository.GetReviewers(ctx.User.ID, 0) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) + return + } + ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, reviewers)) +} + +// GetAssignees return all users that have write access and can be assigned to issues +func GetAssignees(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/assignees repository repoGetAssignees + // --- + // summary: Return all users that have write access and can be assigned to issues + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/UserList" + + assignees, err := ctx.Repo.Repository.GetAssignees() + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) + return + } + ctx.JSON(http.StatusOK, convert.ToUsers(ctx.User, assignees)) +} diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 6e811bf0f8a4d..4adae532fdfcf 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -13,7 +13,6 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/convert" - api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" ) @@ -73,18 +72,13 @@ func Search(ctx *context.APIContext) { return } - results := make([]*api.User, len(users)) - for i := range users { - results[i] = convert.ToUser(users[i], ctx.User) - } - ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", maxResults)) ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link") ctx.JSON(http.StatusOK, map[string]interface{}{ "ok": true, - "data": results, + "data": convert.ToUsers(ctx.User, users), }) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e3ac4a4c8a68c..1102d86a6c3b9 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -2255,6 +2255,39 @@ } } }, + "/repos/{owner}/{repo}/assignees": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Return all users that have write access and can be assigned to issues", + "operationId": "repoGetAssignees", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/UserList" + } + } + } + }, "/repos/{owner}/{repo}/branch_protections": { "get": { "produces": [ @@ -8547,6 +8580,39 @@ } } }, + "/repos/{owner}/{repo}/reviewers": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Return all users that can be requested to review in this repo", + "operationId": "repoGetReviewers", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/UserList" + } + } + } + }, "/repos/{owner}/{repo}/signing-key.gpg": { "get": { "produces": [ From 98aef00963c4a2836b9a06655b83c79b2b79af06 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 16 Jun 2021 01:24:24 +0200 Subject: [PATCH 2/5] Add tests --- integrations/api_repo_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/integrations/api_repo_test.go b/integrations/api_repo_test.go index cfd3b58d649d6..2c68d38846e74 100644 --- a/integrations/api_repo_test.go +++ b/integrations/api_repo_test.go @@ -494,3 +494,31 @@ func TestAPIRepoTransfer(t *testing.T) { repo = models.AssertExistsAndLoadBean(t, &models.Repository{ID: repo.ID}).(*models.Repository) _ = models.DeleteRepository(user, repo.OwnerID, repo.ID) } + +func TestAPIRepoGetReviewers(t *testing.T) { + defer prepareTestEnv(t)() + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/reviewers?token=%s", user.Name, repo.Name, token) + resp := session.MakeRequest(t, req, http.StatusOK) + var reviewers []*api.User + DecodeJSON(t, resp, &reviewers) + assert.Len(t, reviewers, 4) +} + +func TestAPIRepoGetAssignees(t *testing.T) { + defer prepareTestEnv(t)() + user := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session) + repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository) + + req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/assignees?token=%s", user.Name, repo.Name, token) + resp := session.MakeRequest(t, req, http.StatusOK) + var assignees []*api.User + DecodeJSON(t, resp, &assignees) + assert.Len(t, assignees, 1) +} From 6e1cf364facfae9fcf1743d2ce7220fdcc4c3ac8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 16 Jun 2021 02:26:51 +0200 Subject: [PATCH 3/5] fix lint --- modules/convert/user.go | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/convert/user.go b/modules/convert/user.go index 423036a71f7c0..83675b0eefa56 100644 --- a/modules/convert/user.go +++ b/modules/convert/user.go @@ -25,6 +25,7 @@ func ToUser(user, doer *models.User) *api.User { return toUser(user, signed, authed) } +// ToUsers convert list of models.User to list of api.User func ToUsers(doer *models.User, users []*models.User) []*api.User { result := make([]*api.User, len(users)) for i := range users { From 429c58a13001546076c4707bc04ce74403647479 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 16 Jun 2021 17:20:05 +0200 Subject: [PATCH 4/5] Apply suggestions from code review --- routers/api/v1/api.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 82aa9a1bc3c94..0b47953e5841d 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -746,8 +746,8 @@ func Routes() *web.Route { Put(reqAdmin(), bind(api.AddCollaboratorOption{}), repo.AddCollaborator). Delete(reqAdmin(), repo.DeleteCollaborator) }, reqToken()) - m.Get("/assignees", reqToken(), repo.GetAssignees) - m.Get("/reviewers", reqToken(), repo.GetReviewers) + m.Get("/assignees", reqToken(), reqAnyRepoReader(), repo.GetAssignees) + m.Get("/reviewers", reqToken(), reqAnyRepoReader(), repo.GetReviewers) m.Group("/teams", func() { m.Get("", reqAnyRepoReader(), repo.ListTeams) m.Combo("/{team}").Get(reqAnyRepoReader(), repo.IsTeam). From e36804cc08036685304b19b6a3f22f582624e491 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Wed, 16 Jun 2021 20:52:34 +0200 Subject: [PATCH 5/5] fix unrelated swagger query type --- routers/api/v1/notify/repo.go | 2 +- routers/api/v1/notify/user.go | 2 +- templates/swagger/v1_json.tmpl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 4deb16a227594..af55d1d49c05b 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -65,7 +65,7 @@ func ListRepoNotifications(ctx *context.APIContext) { // - name: all // in: query // description: If true, show notifications marked as read. Default value is false - // type: string + // type: boolean // - name: status-types // in: query // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned" diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 1ff62622b0015..475a541bdc603 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -27,7 +27,7 @@ func ListNotifications(ctx *context.APIContext) { // - name: all // in: query // description: If true, show notifications marked as read. Default value is false - // type: string + // type: boolean // - name: status-types // in: query // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned." diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index d73b7c9bea87c..5cc72e8e628be 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -630,7 +630,7 @@ "operationId": "notifyGetList", "parameters": [ { - "type": "string", + "type": "boolean", "description": "If true, show notifications marked as read. Default value is false", "name": "all", "in": "query" @@ -6839,7 +6839,7 @@ "required": true }, { - "type": "string", + "type": "boolean", "description": "If true, show notifications marked as read. Default value is false", "name": "all", "in": "query"