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