Add notifications (#226)

Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: spawn2kill <spawn2kill@noreply.gitea.io>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/gitea/go-sdk/pulls/226
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: lafriks <lafriks@noreply.gitea.io>
This commit is contained in:
6543 2020-02-04 08:03:15 +00:00 committed by lafriks
parent 524bda1e14
commit 85ee8a79b6
2 changed files with 246 additions and 0 deletions

144
gitea/notifications.go Normal file
View File

@ -0,0 +1,144 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"net/http"
"net/url"
"time"
)
// NotificationThread expose Notification on API
type NotificationThread struct {
ID int64 `json:"id"`
Repository *Repository `json:"repository"`
Subject *NotificationSubject `json:"subject"`
Unread bool `json:"unread"`
Pinned bool `json:"pinned"`
UpdatedAt time.Time `json:"updated_at"`
URL string `json:"url"`
}
// NotificationSubject contains the notification subject (Issue/Pull/Commit)
type NotificationSubject struct {
Title string `json:"title"`
URL string `json:"url"`
LatestCommentURL string `json:"latest_comment_url"`
Type string `json:"type" binding:"In(Issue,Pull,Commit)"`
}
// ListNotificationOptions represents the filter options
type ListNotificationOptions struct {
Since time.Time
Before time.Time
}
// MarkNotificationOptions represents the filter options
type MarkNotificationOptions struct {
LastReadAt time.Time
}
// QueryEncode encode options to url query
func (opt *ListNotificationOptions) QueryEncode() string {
query := make(url.Values)
if !opt.Since.IsZero() {
query.Add("since", opt.Since.Format(time.RFC3339))
}
if !opt.Before.IsZero() {
query.Add("before", opt.Before.Format(time.RFC3339))
}
return query.Encode()
}
// QueryEncode encode options to url query
func (opt *MarkNotificationOptions) QueryEncode() string {
query := make(url.Values)
if !opt.LastReadAt.IsZero() {
query.Add("last_read_at", opt.LastReadAt.Format(time.RFC3339))
}
return query.Encode()
}
// CheckNotifications list users's notification threads
func (c *Client) CheckNotifications() (int64, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return 0, err
}
status, err := c.getStatusCode("GET", "/notifications/new", nil, nil)
if err != nil || status == http.StatusNoContent {
return 0, err
}
new := struct {
New int64 `json:"new"`
}{}
if err := c.getParsedResponse("GET", "/notifications/new", nil, nil, &new); err != nil {
return 0, err
}
return new.New, nil
}
// GetNotification get notification thread by ID
func (c *Client) GetNotification(id int64) (*NotificationThread, error) {
thread := new(NotificationThread)
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return thread, err
}
return thread, c.getParsedResponse("GET", fmt.Sprintf("/notifications/threads/%d", id), nil, nil, thread)
}
// ReadNotification mark notification thread as read by ID
func (c *Client) ReadNotification(id int64) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
_, err := c.getResponse("PATCH", fmt.Sprintf("/notifications/threads/%d", id), nil, nil)
return err
}
// ListNotifications list users's notification threads
func (c *Client) ListNotifications(opt ListNotificationOptions) ([]*NotificationThread, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return make([]*NotificationThread, 0, 1), err
}
link, _ := url.Parse("/notifications")
link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10)
return threads, c.getParsedResponse("GET", link.String(), nil, nil, &threads)
}
// ReadNotifications mark notification threads as read
func (c *Client) ReadNotifications(opt MarkNotificationOptions) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
link, _ := url.Parse("/notifications")
link.RawQuery = opt.QueryEncode()
_, err := c.getResponse("PUT", link.String(), nil, nil)
return err
}
// ListRepoNotifications list users's notification threads on a specific repo
func (c *Client) ListRepoNotifications(owner, reponame string, opt ListNotificationOptions) ([]*NotificationThread, error) {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return make([]*NotificationThread, 0, 1), err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame))
link.RawQuery = opt.QueryEncode()
threads := make([]*NotificationThread, 0, 10)
return threads, c.getParsedResponse("GET", link.String(), nil, nil, &threads)
}
// ReadRepoNotifications mark notification threads as read on a specific repo
func (c *Client) ReadRepoNotifications(owner, reponame string, opt MarkNotificationOptions) error {
if err := c.CheckServerVersionConstraint(">=1.12.0"); err != nil {
return err
}
link, _ := url.Parse(fmt.Sprintf("/repos/%s/%s/notifications", owner, reponame))
link.RawQuery = opt.QueryEncode()
_, err := c.getResponse("PUT", link.String(), nil, nil)
return err
}

102
gitea/notifications_test.go Normal file
View File

@ -0,0 +1,102 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package gitea
import (
"fmt"
"log"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNotifications(t *testing.T) {
log.Println("== TestNotifications ==")
// init user2
c := newTestClient()
user1, err := c.GetMyUserInfo()
assert.NoError(t, err)
user2 := createTestUser(t, "notify2", c)
//create 2 repos
repoA, err := createTestRepo(t, "TestNotifications_A", c)
assert.NoError(t, err)
c.sudo = user2.UserName
repoB, err := createTestRepo(t, "TestNotifications_B", c)
assert.NoError(t, err)
err = c.WatchRepo(user1.UserName, repoA.Name)
c.sudo = ""
assert.NoError(t, err)
c.sudo = user2.UserName
assert.NoError(t, c.ReadNotifications(MarkNotificationOptions{}))
count, err := c.CheckNotifications()
assert.EqualValues(t, 0, count)
assert.NoError(t, err)
c.sudo = ""
_, err = c.CreateIssue(repoA.Owner.UserName, repoA.Name, CreateIssueOption{Title: "A Issue", Closed: false})
assert.NoError(t, err)
issue, err := c.CreateIssue(repoB.Owner.UserName, repoB.Name, CreateIssueOption{Title: "B Issue", Closed: false})
assert.NoError(t, err)
// CheckNotifications of user2
c.sudo = user2.UserName
count, err = c.CheckNotifications()
assert.NoError(t, err)
assert.EqualValues(t, 2, count)
// ListNotifications
nList, err := c.ListNotifications(ListNotificationOptions{})
assert.NoError(t, err)
assert.Len(t, nList, 2)
for _, n := range nList {
assert.EqualValues(t, true, n.Unread)
assert.EqualValues(t, "Issue", n.Subject.Type)
if n.Subject.Title == "A Issue" {
assert.EqualValues(t, repoA.Name, n.Repository.Name)
} else if n.Subject.Title == "B Issue" {
assert.EqualValues(t, repoB.Name, n.Repository.Name)
} else {
assert.Error(t, fmt.Errorf("ListNotifications returned a Issue witch should not"))
}
}
// ListRepoNotifications
nList, err = c.ListRepoNotifications(repoA.Owner.UserName, repoA.Name, ListNotificationOptions{})
assert.NoError(t, err)
assert.Len(t, nList, 1)
assert.EqualValues(t, "A Issue", nList[0].Subject.Title)
// ReadRepoNotifications
err = c.ReadRepoNotifications(repoA.Owner.UserName, repoA.Name, MarkNotificationOptions{})
assert.NoError(t, err)
// GetThread
n, err := c.GetNotification(nList[0].ID)
assert.NoError(t, err)
assert.EqualValues(t, false, n.Unread)
assert.EqualValues(t, "A Issue", n.Subject.Title)
// ReadNotifications
err = c.ReadNotifications(MarkNotificationOptions{})
assert.NoError(t, err)
nList, err = c.ListNotifications(ListNotificationOptions{})
assert.NoError(t, err)
assert.Len(t, nList, 0)
// ReadThread
iState := "closed"
c.sudo = ""
_, err = c.EditIssue(repoB.Owner.UserName, repoB.Name, issue.Index, EditIssueOption{State: &iState})
assert.NoError(t, err)
c.sudo = user2.UserName
nList, err = c.ListNotifications(ListNotificationOptions{})
assert.NoError(t, err)
assert.Len(t, nList, 1)
err = c.ReadNotification(nList[0].ID)
assert.NoError(t, err)
}