Skip to content

Commit 6baa5d7

Browse files
6543zeripath
authored andcommitted
[API] Add notification endpoint (#9488)
* [API] Add notification endpoints * add func GetNotifications(opts FindNotificationOptions) * add func (n *Notification) APIFormat() * add func (nl NotificationList) APIFormat() * add func (n *Notification) APIURL() * add func (nl NotificationList) APIFormat() * add LoadAttributes functions (loadRepo, loadIssue, loadComment, loadUser) * add func (c *Comment) APIURL() * add func (issue *Issue) GetLastComment() * add endpoint GET /notifications * add endpoint PUT /notifications * add endpoint GET /repos/{owner}/{repo}/notifications * add endpoint PUT /repos/{owner}/{repo}/notifications * add endpoint GET /notifications/threads/{id} * add endpoint PATCH /notifications/threads/{id} * Add TEST * code format * code format
1 parent ee9ce0c commit 6baa5d7

File tree

15 files changed

+1124
-28
lines changed

15 files changed

+1124
-28
lines changed

integrations/api_notification_test.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"fmt"
9+
"net/http"
10+
"testing"
11+
12+
"code.gitea.io/gitea/models"
13+
api "code.gitea.io/gitea/modules/structs"
14+
15+
"github.com/stretchr/testify/assert"
16+
)
17+
18+
func TestAPINotification(t *testing.T) {
19+
defer prepareTestEnv(t)()
20+
21+
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
22+
repo1 := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
23+
thread5 := models.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
24+
assert.NoError(t, thread5.LoadAttributes())
25+
session := loginUser(t, user2.Name)
26+
token := getTokenForLoggedInUser(t, session)
27+
28+
// -- GET /notifications --
29+
// test filter
30+
since := "2000-01-01T00%3A50%3A01%2B00%3A00" //946687801
31+
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?since=%s&token=%s", since, token))
32+
resp := session.MakeRequest(t, req, http.StatusOK)
33+
var apiNL []api.NotificationThread
34+
DecodeJSON(t, resp, &apiNL)
35+
36+
assert.Len(t, apiNL, 1)
37+
assert.EqualValues(t, 5, apiNL[0].ID)
38+
39+
// test filter
40+
before := "2000-01-01T01%3A06%3A59%2B00%3A00" //946688819
41+
42+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?all=%s&before=%s&token=%s", "true", before, token))
43+
resp = session.MakeRequest(t, req, http.StatusOK)
44+
DecodeJSON(t, resp, &apiNL)
45+
46+
assert.Len(t, apiNL, 3)
47+
assert.EqualValues(t, 4, apiNL[0].ID)
48+
assert.EqualValues(t, true, apiNL[0].Unread)
49+
assert.EqualValues(t, false, apiNL[0].Pinned)
50+
assert.EqualValues(t, 3, apiNL[1].ID)
51+
assert.EqualValues(t, false, apiNL[1].Unread)
52+
assert.EqualValues(t, true, apiNL[1].Pinned)
53+
assert.EqualValues(t, 2, apiNL[2].ID)
54+
assert.EqualValues(t, false, apiNL[2].Unread)
55+
assert.EqualValues(t, false, apiNL[2].Pinned)
56+
57+
// -- 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))
59+
resp = session.MakeRequest(t, req, http.StatusOK)
60+
DecodeJSON(t, resp, &apiNL)
61+
62+
assert.Len(t, apiNL, 1)
63+
assert.EqualValues(t, 4, apiNL[0].ID)
64+
65+
// -- GET /notifications/threads/{id} --
66+
// get forbidden
67+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", 1, token))
68+
resp = session.MakeRequest(t, req, http.StatusForbidden)
69+
70+
// get own
71+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token))
72+
resp = session.MakeRequest(t, req, http.StatusOK)
73+
var apiN api.NotificationThread
74+
DecodeJSON(t, resp, &apiN)
75+
76+
assert.EqualValues(t, 5, apiN.ID)
77+
assert.EqualValues(t, false, apiN.Pinned)
78+
assert.EqualValues(t, true, apiN.Unread)
79+
assert.EqualValues(t, "issue4", apiN.Subject.Title)
80+
assert.EqualValues(t, "Issue", apiN.Subject.Type)
81+
assert.EqualValues(t, thread5.Issue.APIURL(), apiN.Subject.URL)
82+
assert.EqualValues(t, thread5.Repository.HTMLURL(), apiN.Repository.HTMLURL)
83+
84+
// -- mark notifications as read --
85+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
86+
resp = session.MakeRequest(t, req, http.StatusOK)
87+
DecodeJSON(t, resp, &apiNL)
88+
assert.Len(t, apiNL, 2)
89+
90+
lastReadAt := "2000-01-01T00%3A50%3A01%2B00%3A00" //946687801 <- only Notification 4 is in this filter ...
91+
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/repos/%s/%s/notifications?last_read_at=%s&token=%s", user2.Name, repo1.Name, lastReadAt, token))
92+
resp = session.MakeRequest(t, req, http.StatusResetContent)
93+
94+
req = NewRequest(t, "GET", fmt.Sprintf("/api/v1/notifications?token=%s", token))
95+
resp = session.MakeRequest(t, req, http.StatusOK)
96+
DecodeJSON(t, resp, &apiNL)
97+
assert.Len(t, apiNL, 1)
98+
99+
// -- PATCH /notifications/threads/{id} --
100+
req = NewRequest(t, "PATCH", fmt.Sprintf("/api/v1/notifications/threads/%d?token=%s", thread5.ID, token))
101+
resp = session.MakeRequest(t, req, http.StatusResetContent)
102+
103+
assert.Equal(t, models.NotificationStatusUnread, thread5.Status)
104+
thread5 = models.AssertExistsAndLoadBean(t, &models.Notification{ID: 5}).(*models.Notification)
105+
assert.Equal(t, models.NotificationStatusRead, thread5.Status)
106+
}

models/fixtures/notification.yml

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
updated_by: 2
88
issue_id: 1
99
created_unix: 946684800
10-
updated_unix: 946684800
10+
updated_unix: 946684820
1111

1212
-
1313
id: 2
@@ -17,8 +17,8 @@
1717
source: 1 # issue
1818
updated_by: 1
1919
issue_id: 2
20-
created_unix: 946684800
21-
updated_unix: 946684800
20+
created_unix: 946685800
21+
updated_unix: 946685820
2222

2323
-
2424
id: 3
@@ -27,9 +27,9 @@
2727
status: 3 # pinned
2828
source: 1 # issue
2929
updated_by: 1
30-
issue_id: 2
31-
created_unix: 946684800
32-
updated_unix: 946684800
30+
issue_id: 3
31+
created_unix: 946686800
32+
updated_unix: 946686800
3333

3434
-
3535
id: 4
@@ -38,6 +38,17 @@
3838
status: 1 # unread
3939
source: 1 # issue
4040
updated_by: 1
41-
issue_id: 2
42-
created_unix: 946684800
43-
updated_unix: 946684800
41+
issue_id: 5
42+
created_unix: 946687800
43+
updated_unix: 946687800
44+
45+
-
46+
id: 5
47+
user_id: 2
48+
repo_id: 2
49+
status: 1 # unread
50+
source: 1 # issue
51+
updated_by: 5
52+
issue_id: 4
53+
created_unix: 946688800
54+
updated_unix: 946688820

models/issue.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,20 @@ func (issue *Issue) GetLastEventLabel() string {
843843
return "repo.issues.opened_by"
844844
}
845845

846+
// GetLastComment return last comment for the current issue.
847+
func (issue *Issue) GetLastComment() (*Comment, error) {
848+
var c Comment
849+
exist, err := x.Where("type = ?", CommentTypeComment).
850+
And("issue_id = ?", issue.ID).Desc("id").Get(&c)
851+
if err != nil {
852+
return nil, err
853+
}
854+
if !exist {
855+
return nil, nil
856+
}
857+
return &c, nil
858+
}
859+
846860
// GetLastEventLabelFake returns the localization label for the current issue without providing a link in the username.
847861
func (issue *Issue) GetLastEventLabelFake() string {
848862
if issue.IsClosed {

models/issue_comment.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package models
88

99
import (
1010
"fmt"
11+
"path"
1112
"strings"
1213

1314
"code.gitea.io/gitea/modules/git"
@@ -235,6 +236,22 @@ func (c *Comment) HTMLURL() string {
235236
return fmt.Sprintf("%s#%s", c.Issue.HTMLURL(), c.HashTag())
236237
}
237238

239+
// APIURL formats a API-string to the issue-comment
240+
func (c *Comment) APIURL() string {
241+
err := c.LoadIssue()
242+
if err != nil { // Silently dropping errors :unamused:
243+
log.Error("LoadIssue(%d): %v", c.IssueID, err)
244+
return ""
245+
}
246+
err = c.Issue.loadRepo(x)
247+
if err != nil { // Silently dropping errors :unamused:
248+
log.Error("loadRepo(%d): %v", c.Issue.RepoID, err)
249+
return ""
250+
}
251+
252+
return c.Issue.Repo.APIURL() + "/" + path.Join("issues/comments", fmt.Sprint(c.ID))
253+
}
254+
238255
// IssueURL formats a URL-string to the issue
239256
func (c *Comment) IssueURL() string {
240257
err := c.LoadIssue()

0 commit comments

Comments
 (0)