From 63591016b3ecd79ab1172cd98e2e830a09d6f515 Mon Sep 17 00:00:00 2001 From: zeripath Date: Sat, 11 Jul 2020 22:46:01 +0100 Subject: Extend Notifications API and return pinned notifications by default (#12164) * Extend notifications API and return pinned notifications in notifications list Signed-off-by: Andrew Thornton * fix swagger Signed-off-by: Andrew Thornton * Fix swagger again Signed-off-by: Andrew Thornton * fix test Signed-off-by: Andrew Thornton * remove spurious debugs * as per @6543 Signed-off-by: Andrew Thornton * Update models/notification.go * as per @6543 Signed-off-by: Andrew Thornton Co-authored-by: techknowlogick --- routers/api/v1/notify/repo.go | 78 ++++++++++++++++++++++++++++++++++++---- routers/api/v1/notify/threads.go | 13 ++++++- routers/api/v1/notify/user.go | 48 +++++++++++++++++++++---- 3 files changed, 125 insertions(+), 14 deletions(-) (limited to 'routers') diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go index 3050cca1e5..49b493aa4f 100644 --- a/routers/api/v1/notify/repo.go +++ b/routers/api/v1/notify/repo.go @@ -11,9 +11,37 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/routers/api/v1/utils" ) +func statusStringToNotificationStatus(status string) models.NotificationStatus { + switch strings.ToLower(strings.TrimSpace(status)) { + case "unread": + return models.NotificationStatusUnread + case "read": + return models.NotificationStatusRead + case "pinned": + return models.NotificationStatusPinned + default: + return 0 + } +} + +func statusStringsToNotificationStatuses(statuses []string, defaultStatuses []string) []models.NotificationStatus { + if len(statuses) == 0 { + statuses = defaultStatuses + } + results := make([]models.NotificationStatus, 0, len(statuses)) + for _, status := range statuses { + notificationStatus := statusStringToNotificationStatus(status) + if notificationStatus > 0 { + results = append(results, notificationStatus) + } + } + return results +} + // ListRepoNotifications list users's notification threads on a specific repo func ListRepoNotifications(ctx *context.APIContext) { // swagger:operation GET /repos/{owner}/{repo}/notifications notification notifyGetRepoList @@ -39,6 +67,14 @@ func ListRepoNotifications(ctx *context.APIContext) { // description: If true, show notifications marked as read. Default value is false // type: string // required: false + // - name: status-types + // in: query + // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned" + // type: array + // collectionFormat: multi + // items: + // type: string + // required: false // - name: since // in: query // description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format @@ -75,9 +111,10 @@ func ListRepoNotifications(ctx *context.APIContext) { UpdatedBeforeUnix: before, UpdatedAfterUnix: since, } - qAll := strings.Trim(ctx.Query("all"), " ") - if qAll != "true" { - opts.Status = models.NotificationStatusUnread + + if !ctx.QueryBool("all") { + statuses := ctx.QueryStrings("status-types") + opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"}) } nl, err := models.GetNotifications(opts) if err != nil { @@ -97,7 +134,7 @@ func ListRepoNotifications(ctx *context.APIContext) { func ReadRepoNotifications(ctx *context.APIContext) { // swagger:operation PUT /repos/{owner}/{repo}/notifications notification notifyReadRepoList // --- - // summary: Mark notification threads as read on a specific repo + // summary: Mark notification threads as read, pinned or unread on a specific repo // consumes: // - application/json // produces: @@ -113,6 +150,24 @@ func ReadRepoNotifications(ctx *context.APIContext) { // description: name of the repo // type: string // required: true + // - name: all + // in: query + // description: If true, mark all notifications on this repo. Default value is false + // type: string + // required: false + // - name: status-types + // in: query + // description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread." + // type: array + // collectionFormat: multi + // items: + // type: string + // required: false + // - name: to-status + // in: query + // description: Status to mark notifications as. Defaults to read. + // type: string + // required: false // - name: last_read_at // in: query // description: Describes the last point that notifications were checked. Anything updated since this time will not be updated. @@ -135,11 +190,17 @@ func ReadRepoNotifications(ctx *context.APIContext) { lastRead = tmpLastRead.Unix() } } + opts := models.FindNotificationOptions{ UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, UpdatedBeforeUnix: lastRead, - Status: models.NotificationStatusUnread, + } + + if !ctx.QueryBool("all") { + statuses := ctx.QueryStrings("status-types") + opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) + log.Error("%v", opts.Status) } nl, err := models.GetNotifications(opts) if err != nil { @@ -147,8 +208,13 @@ func ReadRepoNotifications(ctx *context.APIContext) { return } + targetStatus := statusStringToNotificationStatus(ctx.Query("to-status")) + if targetStatus == 0 { + targetStatus = models.NotificationStatusRead + } + for _, n := range nl { - err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead) + err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go index d0119e9938..86ae2dca31 100644 --- a/routers/api/v1/notify/threads.go +++ b/routers/api/v1/notify/threads.go @@ -62,6 +62,12 @@ func ReadThread(ctx *context.APIContext) { // description: id of notification thread // type: string // required: true + // - name: to-status + // in: query + // description: Status to mark notifications as + // type: string + // default: read + // required: false // responses: // "205": // "$ref": "#/responses/empty" @@ -75,7 +81,12 @@ func ReadThread(ctx *context.APIContext) { return } - err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead) + targetStatus := statusStringToNotificationStatus(ctx.Query("to-status")) + if targetStatus == 0 { + targetStatus = models.NotificationStatusRead + } + + err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus) if err != nil { ctx.InternalServerError(err) return diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go index 649eaa8a37..9c3f9b1472 100644 --- a/routers/api/v1/notify/user.go +++ b/routers/api/v1/notify/user.go @@ -29,6 +29,14 @@ func ListNotifications(ctx *context.APIContext) { // description: If true, show notifications marked as read. Default value is false // type: string // required: false + // - name: status-types + // in: query + // description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned." + // type: array + // collectionFormat: multi + // items: + // type: string + // required: false // - name: since // in: query // description: Only show notifications updated after the given time. This is a timestamp in RFC 3339 format @@ -64,9 +72,9 @@ func ListNotifications(ctx *context.APIContext) { UpdatedBeforeUnix: before, UpdatedAfterUnix: since, } - qAll := strings.Trim(ctx.Query("all"), " ") - if qAll != "true" { - opts.Status = models.NotificationStatusUnread + if !ctx.QueryBool("all") { + statuses := ctx.QueryStrings("status-types") + opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"}) } nl, err := models.GetNotifications(opts) if err != nil { @@ -82,11 +90,11 @@ func ListNotifications(ctx *context.APIContext) { ctx.JSON(http.StatusOK, nl.APIFormat()) } -// ReadNotifications mark notification threads as read +// ReadNotifications mark notification threads as read, unread, or pinned func ReadNotifications(ctx *context.APIContext) { // swagger:operation PUT /notifications notification notifyReadList // --- - // summary: Mark notification threads as read + // summary: Mark notification threads as read, pinned or unread // consumes: // - application/json // produces: @@ -98,6 +106,24 @@ func ReadNotifications(ctx *context.APIContext) { // type: string // format: date-time // required: false + // - name: all + // in: query + // description: If true, mark all notifications on this repo. Default value is false + // type: string + // required: false + // - name: status-types + // in: query + // description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread." + // type: array + // collectionFormat: multi + // items: + // type: string + // required: false + // - name: to-status + // in: query + // description: Status to mark notifications as, Defaults to read. + // type: string + // required: false // responses: // "205": // "$ref": "#/responses/empty" @@ -117,7 +143,10 @@ func ReadNotifications(ctx *context.APIContext) { opts := models.FindNotificationOptions{ UserID: ctx.User.ID, UpdatedBeforeUnix: lastRead, - Status: models.NotificationStatusUnread, + } + if !ctx.QueryBool("all") { + statuses := ctx.QueryStrings("status-types") + opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"}) } nl, err := models.GetNotifications(opts) if err != nil { @@ -125,8 +154,13 @@ func ReadNotifications(ctx *context.APIContext) { return } + targetStatus := statusStringToNotificationStatus(ctx.Query("to-status")) + if targetStatus == 0 { + targetStatus = models.NotificationStatusRead + } + for _, n := range nl { - err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead) + err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus) if err != nil { ctx.InternalServerError(err) return -- cgit v1.2.3