From 06daf4e863a90ce49a594c23ef85fcc694772aa8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 29 Oct 2019 11:17:45 +0100 Subject: [PATCH] add issue subscriber API --- .golangci.yml | 3 + modules/structs/issue.go | 6 + routers/api/v1/api.go | 5 + routers/api/v1/repo/issue.go | 205 +++++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 159 +++++++++++++++++++++++++ 5 files changed, 378 insertions(+) diff --git a/.golangci.yml b/.golangci.yml index fd7393372b..9b263918c9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -73,6 +73,9 @@ issues: - path: routers/routes/routes.go linters: - dupl + - path: routers/api/v1/repo/issue.go + linters: + - dupl - path: routers/repo/view.go linters: - dupl diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 58fd7344b4..fe74b5b1bc 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -113,3 +113,9 @@ type EditPriorityOption struct { // required:true Priority int `json:"priority"` } + +// IssueWatchers list of subscribers of an issue +type IssueWatchers struct { + // required:true + Subscribers []string `json:"subscribers"` +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index f8ab9025b7..4c4b126ee4 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -688,6 +688,11 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/start", reqToken(), repo.StartIssueStopwatch) m.Post("/stop", reqToken(), repo.StopIssueStopwatch) }) + m.Group("/subscriptions", func() { + m.Get("", reqToken(), bind(api.IssueWatchers{}), repo.GetIssueWatchers) + m.Put("/:user", reqToken(), repo.AddIssueSubscription) + m.Delete("/:user", reqToken(), repo.DelIssueSubscription) + }) }) }, mustEnableIssuesOrPulls) m.Group("/labels", func() { diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 426826653c..f37c972ac9 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -598,3 +598,208 @@ func StopIssueStopwatch(ctx *context.APIContext) { ctx.Status(201) } + +// AddIssueSubscription add user to subscription list +func AddIssueSubscription(ctx *context.APIContext) { + // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/subscriptions/{user} issue issueAddSubscription + // --- + // summary: Add user to subscription list + // consumes: + // - application/json + // 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 + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: user + // in: path + // description: user witch subscribe to issue + // type: string + // required: true + // responses: + // "201": + // "$ref": "#/responses/empty" + // "304": + // description: User has no right to add subscribe of other user + // "404": + // description: Issue not found + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + + return + } + + user, err := models.GetUserByName(ctx.Params(":user")) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(500, "GetUserByName", err) + return + } + } + + if user.ID != ctx.User.ID && !ctx.User.IsAdmin { + ctx.Error(403, "User", nil) + return + } + + if err := models.CreateOrUpdateIssueWatch(user.ID, issue.ID, true); err != nil { + ctx.Error(500, "CreateOrUpdateIssueWatch", err) + return + } + + ctx.Status(201) +} + +// DelIssueSubscription remove user to subscription list +func DelIssueSubscription(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/subscriptions/{user} issue issueDeleteSubscription + // --- + // summary: Delete user from subscription list + // consumes: + // - application/json + // 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 + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: user + // in: path + // description: user witch unsubscribe to issue + // type: string + // required: true + // responses: + // "201": + // "$ref": "#/responses/empty" + // "304": + // description: User has no right to remove subscribe of other user + // "404": + // description: Issue not found + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + + return + } + + user, err := models.GetUserByName(ctx.Params(":user")) + if err != nil { + if models.IsErrUserNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(500, "GetUserByName", err) + return + } + } + + if user.ID != ctx.User.ID && !ctx.User.IsAdmin { + ctx.Error(403, "User", nil) + return + } + + if err := models.CreateOrUpdateIssueWatch(user.ID, issue.ID, false); err != nil { + ctx.Error(500, "CreateOrUpdateIssueWatch", err) + return + } + + ctx.Status(201) +} + +// GetIssueWatchers return subscribers of an issue +func GetIssueWatchers(ctx *context.APIContext, form api.IssueWatchers) { + // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/subscriptions issue issueSubscriptions + // --- + // summary: Get users who subscribed on an issue. + // consumes: + // - application/json + // 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 + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // responses: + // "201": + // "$ref": "#/responses/empty" + // "404": + // description: Issue not found + issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) + if err != nil { + if models.IsErrIssueNotExist(err) { + ctx.NotFound() + } else { + ctx.Error(500, "GetIssueByIndex", err) + } + + return + } + + var subscribers []string + + iw, err := models.GetIssueWatchers(issue.ID) + if err != nil { + ctx.Error(500, "GetIssueWatchers", err) + return + } + + for _, s := range iw { + user, err := models.GetUserByID(s.UserID) + if err != nil { + continue + } + subscribers = append(subscribers, user.LoginName) + } + + ctx.JSON(200, api.IssueWatchers{Subscribers: subscribers}) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 5be36d23be..c933ac3edc 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3731,6 +3731,165 @@ } } }, + "/repos/{owner}/{repo}/issues/{index}/subscriptions": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "issue" + ], + "summary": "Get users who subscribed on an issue.", + "operationId": "issueSubscriptions", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "index of the issue", + "name": "index", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/empty" + }, + "404": { + "description": "Issue not found" + } + } + } + }, + "/repos/{owner}/{repo}/issues/{index}/subscriptions/{user}": { + "put": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "issue" + ], + "summary": "Add user to subscription list", + "operationId": "issueAddSubscription", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "index of the issue", + "name": "index", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user witch subscribe to issue", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/empty" + }, + "304": { + "description": "User has no right to add subscribe of other user" + }, + "404": { + "description": "Issue not found" + } + } + }, + "delete": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "issue" + ], + "summary": "Delete user from subscription list", + "operationId": "issueDeleteSubscription", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "format": "int64", + "description": "index of the issue", + "name": "index", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "user witch unsubscribe to issue", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "$ref": "#/responses/empty" + }, + "304": { + "description": "User has no right to remove subscribe of other user" + }, + "404": { + "description": "Issue not found" + } + } + } + }, "/repos/{owner}/{repo}/keys": { "get": { "produces": [