Skip to content

Commit 6359101

Browse files
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 <[email protected]> * fix swagger Signed-off-by: Andrew Thornton <[email protected]> * Fix swagger again Signed-off-by: Andrew Thornton <[email protected]> * fix test Signed-off-by: Andrew Thornton <[email protected]> * remove spurious debugs * as per @6543 Signed-off-by: Andrew Thornton <[email protected]> * Update models/notification.go * as per @6543 Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: techknowlogick <[email protected]>
1 parent d08996c commit 6359101

File tree

7 files changed

+206
-24
lines changed

7 files changed

+206
-24
lines changed

integrations/api_notification_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func TestAPINotification(t *testing.T) {
5555
assert.EqualValues(t, false, apiNL[2].Pinned)
5656

5757
// -- GET /repos/{owner}/{repo}/notifications --
58-
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?token=%s", user2.Name, repo1.Name, token))
58+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?status-types=unread&token=%s", user2.Name, repo1.Name, token))
5959
resp = session.MakeRequest(t, req, http.StatusOK)
6060
DecodeJSON(t, resp, &apiNL)
6161

@@ -92,7 +92,7 @@ func TestAPINotification(t *testing.T) {
9292
assert.True(t, new.New > 0)
9393

9494
// -- mark notifications as read --
95-
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
95+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
9696
resp = session.MakeRequest(t, req, http.StatusOK)
9797
DecodeJSON(t, resp, &apiNL)
9898
assert.Len(t, apiNL, 2)
@@ -101,7 +101,7 @@ func TestAPINotification(t *testing.T) {
101101
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
102102
resp = session.MakeRequest(t, req, http.StatusResetContent)
103103

104-
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
104+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
105105
resp = session.MakeRequest(t, req, http.StatusOK)
106106
DecodeJSON(t, resp, &apiNL)
107107
assert.Len(t, apiNL, 1)

integrations/eventsource_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func TestEventSourceManagerRun(t *testing.T) {
5959
var apiNL []api.NotificationThread
6060

6161
// -- mark notifications as read --
62-
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
62+
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?status-types=unread&token=%s", token))
6363
resp := session.MakeRequest(t, req, http.StatusOK)
6464

6565
DecodeJSON(t, resp, &apiNL)
@@ -69,7 +69,7 @@ func TestEventSourceManagerRun(t *testing.T) {
6969
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
7070
resp = session.MakeRequest(t, req, http.StatusResetContent)
7171

72-
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
72+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s&status-types=unread", token))
7373
resp = session.MakeRequest(t, req, http.StatusOK)
7474
DecodeJSON(t, resp, &apiNL)
7575
assert.Len(t, apiNL, 1)

models/notification.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ type FindNotificationOptions struct {
7272
UserID int64
7373
RepoID int64
7474
IssueID int64
75-
Status NotificationStatus
75+
Status []NotificationStatus
7676
UpdatedAfterUnix int64
7777
UpdatedBeforeUnix int64
7878
}
@@ -89,8 +89,8 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
8989
if opts.IssueID != 0 {
9090
cond = cond.And(builder.Eq{"notification.issue_id": opts.IssueID})
9191
}
92-
if opts.Status != 0 {
93-
cond = cond.And(builder.Eq{"notification.status": opts.Status})
92+
if len(opts.Status) > 0 {
93+
cond = cond.And(builder.In("notification.status", opts.Status))
9494
}
9595
if opts.UpdatedAfterUnix != 0 {
9696
cond = cond.And(builder.Gte{"notification.updated_unix": opts.UpdatedAfterUnix})

routers/api/v1/notify/repo.go

Lines changed: 72 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,37 @@ import (
1111

1212
"code.gitea.io/gitea/models"
1313
"code.gitea.io/gitea/modules/context"
14+
"code.gitea.io/gitea/modules/log"
1415
"code.gitea.io/gitea/routers/api/v1/utils"
1516
)
1617

18+
func statusStringToNotificationStatus(status string) models.NotificationStatus {
19+
switch strings.ToLower(strings.TrimSpace(status)) {
20+
case "unread":
21+
return models.NotificationStatusUnread
22+
case "read":
23+
return models.NotificationStatusRead
24+
case "pinned":
25+
return models.NotificationStatusPinned
26+
default:
27+
return 0
28+
}
29+
}
30+
31+
func statusStringsToNotificationStatuses(statuses []string, defaultStatuses []string) []models.NotificationStatus {
32+
if len(statuses) == 0 {
33+
statuses = defaultStatuses
34+
}
35+
results := make([]models.NotificationStatus, 0, len(statuses))
36+
for _, status := range statuses {
37+
notificationStatus := statusStringToNotificationStatus(status)
38+
if notificationStatus > 0 {
39+
results = append(results, notificationStatus)
40+
}
41+
}
42+
return results
43+
}
44+
1745
// ListRepoNotifications list users's notification threads on a specific repo
1846
func ListRepoNotifications(ctx *context.APIContext) {
1947
// swagger:operation GET /repos/{owner}/{repo}/notifications notification notifyGetRepoList
@@ -39,6 +67,14 @@ func ListRepoNotifications(ctx *context.APIContext) {
3967
// description: If true, show notifications marked as read. Default value is false
4068
// type: string
4169
// required: false
70+
// - name: status-types
71+
// in: query
72+
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned"
73+
// type: array
74+
// collectionFormat: multi
75+
// items:
76+
// type: string
77+
// required: false
4278
// - name: since
4379
// in: query
4480
// 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) {
75111
UpdatedBeforeUnix: before,
76112
UpdatedAfterUnix: since,
77113
}
78-
qAll := strings.Trim(ctx.Query("all"), " ")
79-
if qAll != "true" {
80-
opts.Status = models.NotificationStatusUnread
114+
115+
if !ctx.QueryBool("all") {
116+
statuses := ctx.QueryStrings("status-types")
117+
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"})
81118
}
82119
nl, err := models.GetNotifications(opts)
83120
if err != nil {
@@ -97,7 +134,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
97134
func ReadRepoNotifications(ctx *context.APIContext) {
98135
// swagger:operation PUT /repos/{owner}/{repo}/notifications notification notifyReadRepoList
99136
// ---
100-
// summary: Mark notification threads as read on a specific repo
137+
// summary: Mark notification threads as read, pinned or unread on a specific repo
101138
// consumes:
102139
// - application/json
103140
// produces:
@@ -113,6 +150,24 @@ func ReadRepoNotifications(ctx *context.APIContext) {
113150
// description: name of the repo
114151
// type: string
115152
// required: true
153+
// - name: all
154+
// in: query
155+
// description: If true, mark all notifications on this repo. Default value is false
156+
// type: string
157+
// required: false
158+
// - name: status-types
159+
// in: query
160+
// description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread."
161+
// type: array
162+
// collectionFormat: multi
163+
// items:
164+
// type: string
165+
// required: false
166+
// - name: to-status
167+
// in: query
168+
// description: Status to mark notifications as. Defaults to read.
169+
// type: string
170+
// required: false
116171
// - name: last_read_at
117172
// in: query
118173
// description: Describes the last point that notifications were checked. Anything updated since this time will not be updated.
@@ -135,20 +190,31 @@ func ReadRepoNotifications(ctx *context.APIContext) {
135190
lastRead = tmpLastRead.Unix()
136191
}
137192
}
193+
138194
opts := models.FindNotificationOptions{
139195
UserID: ctx.User.ID,
140196
RepoID: ctx.Repo.Repository.ID,
141197
UpdatedBeforeUnix: lastRead,
142-
Status: models.NotificationStatusUnread,
198+
}
199+
200+
if !ctx.QueryBool("all") {
201+
statuses := ctx.QueryStrings("status-types")
202+
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
203+
log.Error("%v", opts.Status)
143204
}
144205
nl, err := models.GetNotifications(opts)
145206
if err != nil {
146207
ctx.InternalServerError(err)
147208
return
148209
}
149210

211+
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
212+
if targetStatus == 0 {
213+
targetStatus = models.NotificationStatusRead
214+
}
215+
150216
for _, n := range nl {
151-
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
217+
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
152218
if err != nil {
153219
ctx.InternalServerError(err)
154220
return

routers/api/v1/notify/threads.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ func ReadThread(ctx *context.APIContext) {
6262
// description: id of notification thread
6363
// type: string
6464
// required: true
65+
// - name: to-status
66+
// in: query
67+
// description: Status to mark notifications as
68+
// type: string
69+
// default: read
70+
// required: false
6571
// responses:
6672
// "205":
6773
// "$ref": "#/responses/empty"
@@ -75,7 +81,12 @@ func ReadThread(ctx *context.APIContext) {
7581
return
7682
}
7783

78-
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
84+
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
85+
if targetStatus == 0 {
86+
targetStatus = models.NotificationStatusRead
87+
}
88+
89+
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
7990
if err != nil {
8091
ctx.InternalServerError(err)
8192
return

routers/api/v1/notify/user.go

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ func ListNotifications(ctx *context.APIContext) {
2929
// description: If true, show notifications marked as read. Default value is false
3030
// type: string
3131
// required: false
32+
// - name: status-types
33+
// in: query
34+
// description: "Show notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread & pinned."
35+
// type: array
36+
// collectionFormat: multi
37+
// items:
38+
// type: string
39+
// required: false
3240
// - name: since
3341
// in: query
3442
// 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) {
6472
UpdatedBeforeUnix: before,
6573
UpdatedAfterUnix: since,
6674
}
67-
qAll := strings.Trim(ctx.Query("all"), " ")
68-
if qAll != "true" {
69-
opts.Status = models.NotificationStatusUnread
75+
if !ctx.QueryBool("all") {
76+
statuses := ctx.QueryStrings("status-types")
77+
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread", "pinned"})
7078
}
7179
nl, err := models.GetNotifications(opts)
7280
if err != nil {
@@ -82,11 +90,11 @@ func ListNotifications(ctx *context.APIContext) {
8290
ctx.JSON(http.StatusOK, nl.APIFormat())
8391
}
8492

85-
// ReadNotifications mark notification threads as read
93+
// ReadNotifications mark notification threads as read, unread, or pinned
8694
func ReadNotifications(ctx *context.APIContext) {
8795
// swagger:operation PUT /notifications notification notifyReadList
8896
// ---
89-
// summary: Mark notification threads as read
97+
// summary: Mark notification threads as read, pinned or unread
9098
// consumes:
9199
// - application/json
92100
// produces:
@@ -98,6 +106,24 @@ func ReadNotifications(ctx *context.APIContext) {
98106
// type: string
99107
// format: date-time
100108
// required: false
109+
// - name: all
110+
// in: query
111+
// description: If true, mark all notifications on this repo. Default value is false
112+
// type: string
113+
// required: false
114+
// - name: status-types
115+
// in: query
116+
// description: "Mark notifications with the provided status types. Options are: unread, read and/or pinned. Defaults to unread."
117+
// type: array
118+
// collectionFormat: multi
119+
// items:
120+
// type: string
121+
// required: false
122+
// - name: to-status
123+
// in: query
124+
// description: Status to mark notifications as, Defaults to read.
125+
// type: string
126+
// required: false
101127
// responses:
102128
// "205":
103129
// "$ref": "#/responses/empty"
@@ -117,16 +143,24 @@ func ReadNotifications(ctx *context.APIContext) {
117143
opts := models.FindNotificationOptions{
118144
UserID: ctx.User.ID,
119145
UpdatedBeforeUnix: lastRead,
120-
Status: models.NotificationStatusUnread,
146+
}
147+
if !ctx.QueryBool("all") {
148+
statuses := ctx.QueryStrings("status-types")
149+
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
121150
}
122151
nl, err := models.GetNotifications(opts)
123152
if err != nil {
124153
ctx.InternalServerError(err)
125154
return
126155
}
127156

157+
targetStatus := statusStringToNotificationStatus(ctx.Query("to-status"))
158+
if targetStatus == 0 {
159+
targetStatus = models.NotificationStatusRead
160+
}
161+
128162
for _, n := range nl {
129-
err := models.SetNotificationStatus(n.ID, ctx.User, models.NotificationStatusRead)
163+
err := models.SetNotificationStatus(n.ID, ctx.User, targetStatus)
130164
if err != nil {
131165
ctx.InternalServerError(err)
132166
return

0 commit comments

Comments
 (0)