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:
parent
524bda1e14
commit
85ee8a79b6
2 changed files with 246 additions and 0 deletions
144
gitea/notifications.go
Normal file
144
gitea/notifications.go
Normal 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
102
gitea/notifications_test.go
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue