code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/issue_subscription.go (about) 1 // Copyright 2019 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "fmt" 8 "net/http" 9 10 issues_model "code.gitea.io/gitea/models/issues" 11 user_model "code.gitea.io/gitea/models/user" 12 "code.gitea.io/gitea/modules/context" 13 api "code.gitea.io/gitea/modules/structs" 14 "code.gitea.io/gitea/routers/api/v1/utils" 15 "code.gitea.io/gitea/services/convert" 16 ) 17 18 // AddIssueSubscription Subscribe user to issue 19 func AddIssueSubscription(ctx *context.APIContext) { 20 // swagger:operation PUT /repos/{owner}/{repo}/issues/{index}/subscriptions/{user} issue issueAddSubscription 21 // --- 22 // summary: Subscribe user to issue 23 // consumes: 24 // - application/json 25 // produces: 26 // - application/json 27 // parameters: 28 // - name: owner 29 // in: path 30 // description: owner of the repo 31 // type: string 32 // required: true 33 // - name: repo 34 // in: path 35 // description: name of the repo 36 // type: string 37 // required: true 38 // - name: index 39 // in: path 40 // description: index of the issue 41 // type: integer 42 // format: int64 43 // required: true 44 // - name: user 45 // in: path 46 // description: user to subscribe 47 // type: string 48 // required: true 49 // responses: 50 // "200": 51 // description: Already subscribed 52 // "201": 53 // description: Successfully Subscribed 54 // "304": 55 // description: User can only subscribe itself if he is no admin 56 // "404": 57 // "$ref": "#/responses/notFound" 58 59 setIssueSubscription(ctx, true) 60 } 61 62 // DelIssueSubscription Unsubscribe user from issue 63 func DelIssueSubscription(ctx *context.APIContext) { 64 // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/subscriptions/{user} issue issueDeleteSubscription 65 // --- 66 // summary: Unsubscribe user from issue 67 // consumes: 68 // - application/json 69 // produces: 70 // - application/json 71 // parameters: 72 // - name: owner 73 // in: path 74 // description: owner of the repo 75 // type: string 76 // required: true 77 // - name: repo 78 // in: path 79 // description: name of the repo 80 // type: string 81 // required: true 82 // - name: index 83 // in: path 84 // description: index of the issue 85 // type: integer 86 // format: int64 87 // required: true 88 // - name: user 89 // in: path 90 // description: user witch unsubscribe 91 // type: string 92 // required: true 93 // responses: 94 // "200": 95 // description: Already unsubscribed 96 // "201": 97 // description: Successfully Unsubscribed 98 // "304": 99 // description: User can only subscribe itself if he is no admin 100 // "404": 101 // "$ref": "#/responses/notFound" 102 103 setIssueSubscription(ctx, false) 104 } 105 106 func setIssueSubscription(ctx *context.APIContext, watch bool) { 107 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 108 if err != nil { 109 if issues_model.IsErrIssueNotExist(err) { 110 ctx.NotFound() 111 } else { 112 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 113 } 114 115 return 116 } 117 118 user, err := user_model.GetUserByName(ctx, ctx.Params(":user")) 119 if err != nil { 120 if user_model.IsErrUserNotExist(err) { 121 ctx.NotFound() 122 } else { 123 ctx.Error(http.StatusInternalServerError, "GetUserByName", err) 124 } 125 126 return 127 } 128 129 // only admin and user for itself can change subscription 130 if user.ID != ctx.Doer.ID && !ctx.Doer.IsAdmin { 131 ctx.Error(http.StatusForbidden, "User", fmt.Errorf("%s is not permitted to change subscriptions for %s", ctx.Doer.Name, user.Name)) 132 return 133 } 134 135 current, err := issues_model.CheckIssueWatch(ctx, user, issue) 136 if err != nil { 137 ctx.Error(http.StatusInternalServerError, "CheckIssueWatch", err) 138 return 139 } 140 141 // If watch state wont change 142 if current == watch { 143 ctx.Status(http.StatusOK) 144 return 145 } 146 147 // Update watch state 148 if err := issues_model.CreateOrUpdateIssueWatch(ctx, user.ID, issue.ID, watch); err != nil { 149 ctx.Error(http.StatusInternalServerError, "CreateOrUpdateIssueWatch", err) 150 return 151 } 152 153 ctx.Status(http.StatusCreated) 154 } 155 156 // CheckIssueSubscription check if user is subscribed to an issue 157 func CheckIssueSubscription(ctx *context.APIContext) { 158 // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/subscriptions/check issue issueCheckSubscription 159 // --- 160 // summary: Check if user is subscribed to an issue 161 // consumes: 162 // - application/json 163 // produces: 164 // - application/json 165 // parameters: 166 // - name: owner 167 // in: path 168 // description: owner of the repo 169 // type: string 170 // required: true 171 // - name: repo 172 // in: path 173 // description: name of the repo 174 // type: string 175 // required: true 176 // - name: index 177 // in: path 178 // description: index of the issue 179 // type: integer 180 // format: int64 181 // required: true 182 // responses: 183 // "200": 184 // "$ref": "#/responses/WatchInfo" 185 // "404": 186 // "$ref": "#/responses/notFound" 187 188 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 189 if err != nil { 190 if issues_model.IsErrIssueNotExist(err) { 191 ctx.NotFound() 192 } else { 193 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 194 } 195 196 return 197 } 198 199 watching, err := issues_model.CheckIssueWatch(ctx, ctx.Doer, issue) 200 if err != nil { 201 ctx.InternalServerError(err) 202 return 203 } 204 ctx.JSON(http.StatusOK, api.WatchInfo{ 205 Subscribed: watching, 206 Ignored: !watching, 207 Reason: nil, 208 CreatedAt: issue.CreatedUnix.AsTime(), 209 URL: issue.APIURL() + "/subscriptions", 210 RepositoryURL: ctx.Repo.Repository.APIURL(), 211 }) 212 } 213 214 // GetIssueSubscribers return subscribers of an issue 215 func GetIssueSubscribers(ctx *context.APIContext) { 216 // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/subscriptions issue issueSubscriptions 217 // --- 218 // summary: Get users who subscribed on an issue. 219 // consumes: 220 // - application/json 221 // produces: 222 // - application/json 223 // parameters: 224 // - name: owner 225 // in: path 226 // description: owner of the repo 227 // type: string 228 // required: true 229 // - name: repo 230 // in: path 231 // description: name of the repo 232 // type: string 233 // required: true 234 // - name: index 235 // in: path 236 // description: index of the issue 237 // type: integer 238 // format: int64 239 // required: true 240 // - name: page 241 // in: query 242 // description: page number of results to return (1-based) 243 // type: integer 244 // - name: limit 245 // in: query 246 // description: page size of results 247 // type: integer 248 // responses: 249 // "200": 250 // "$ref": "#/responses/UserList" 251 // "404": 252 // "$ref": "#/responses/notFound" 253 254 issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) 255 if err != nil { 256 if issues_model.IsErrIssueNotExist(err) { 257 ctx.NotFound() 258 } else { 259 ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) 260 } 261 262 return 263 } 264 265 iwl, err := issues_model.GetIssueWatchers(ctx, issue.ID, utils.GetListOptions(ctx)) 266 if err != nil { 267 ctx.Error(http.StatusInternalServerError, "GetIssueWatchers", err) 268 return 269 } 270 271 userIDs := make([]int64, 0, len(iwl)) 272 for _, iw := range iwl { 273 userIDs = append(userIDs, iw.UserID) 274 } 275 276 users, err := user_model.GetUsersByIDs(ctx, userIDs) 277 if err != nil { 278 ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) 279 return 280 } 281 apiUsers := make([]*api.User, 0, len(users)) 282 for _, v := range users { 283 apiUsers = append(apiUsers, convert.ToUser(ctx, v, ctx.Doer)) 284 } 285 286 count, err := issues_model.CountIssueWatchers(ctx, issue.ID) 287 if err != nil { 288 ctx.Error(http.StatusInternalServerError, "CountIssueWatchers", err) 289 return 290 } 291 292 ctx.SetTotalCountHeader(count) 293 ctx.JSON(http.StatusOK, apiUsers) 294 }