#1 Implements Notifications API

This commit is contained in:
Adphi 2018-10-17 11:15:27 +02:00
parent 4a1bec45bf
commit 75e83977e4
8 changed files with 256 additions and 20 deletions

View File

@ -5,7 +5,7 @@ GO_FILES := $(shell find . -name '*.go' | grep -v _test.go)
.PHONY: all dep build clean test coverage coverhtml lint .PHONY: all dep build clean test coverage coverhtml lint
all: build all: dep lint test race coverage
lint: ## Lint the files lint: ## Lint the files
@golint -set_exit_status ${PKG_LIST} @golint -set_exit_status ${PKG_LIST}
@ -28,5 +28,8 @@ dep: ## Get the dependencies
@mkdir -p vendor @mkdir -p vendor
@govendor add +external @govendor add +external
push: dep lint test coverage ## Push to git repository
@git push origin master
help: ## Display this help screen help: ## Display this help screen
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

View File

@ -478,6 +478,9 @@ func TestUserCreateWithoutPassword(t *testing.T) {
if err := initClient(); err != nil { if err := initClient(); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if c.version.Major < 14 {
t.SkipNow()
}
// Nextcloud does not seems to like recreating a deleted user // Nextcloud does not seems to like recreating a deleted user
err := c.UserCreateWithoutPassword(config.NotExistingUser, config.Email, strings.Title(config.NotExistingUser)) err := c.UserCreateWithoutPassword(config.NotExistingUser, config.Email, strings.Title(config.NotExistingUser))
assert.NoError(t, err) assert.NoError(t, err)

83
notifications.go Normal file
View File

@ -0,0 +1,83 @@
package gonextcloud
import (
"errors"
req "github.com/levigross/grequests"
"gitlab.adphi.fr/partitio/Nextcloud-Partitio/gonextcloud/types"
"net/http"
"strconv"
)
//NotificationsList returns all the notifications
func (c *Client) NotificationsList() ([]types.Notification, error) {
if err := c.notificationsAvailable(); err != nil {
return nil, err
}
res, err := c.baseRequest(http.MethodGet, routes.notifications, nil)
if err != nil {
return nil, err
}
var r types.NotificationsListResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//Notifications returns the notification corresponding to the id
func (c *Client) Notifications(id int) (types.Notification, error) {
if err := c.notificationsAvailable(); err != nil {
return types.Notification{}, err
}
res, err := c.baseRequest(http.MethodGet, routes.notifications, nil, strconv.Itoa(id))
if err != nil {
return types.Notification{}, err
}
var r types.NotificationResponse
res.JSON(&r)
return r.Ocs.Data, nil
}
//NotificationsDelete deletes the notification corresponding to the id
func (c *Client) NotificationsDelete(id int) error {
if err := c.notificationsAvailable(); err != nil {
return err
}
_, err := c.baseRequest(http.MethodDelete, routes.notifications, nil, strconv.Itoa(id))
return err
}
//NotificationsDeleteAll deletes all notifications
func (c *Client) NotificationsDeleteAll() error {
if err := c.notificationsAvailable(); err != nil {
return err
}
_, err := c.baseRequest(http.MethodDelete, routes.notifications, nil)
return err
}
//NotificationsCreate creates a notification (if the user is an admin)
func (c *Client) NotificationsCreate(userID, title, message string) error {
if err := c.adminNotificationsAvailable(); err != nil {
return err
}
ro := &req.RequestOptions{
Data: map[string]string{
"shortMessage": title,
"longMessage": message,
},
}
_, err := c.baseRequest(http.MethodPost, routes.adminNotifications, ro, userID)
return err
}
func (c *Client) adminNotificationsAvailable() error {
if len(c.capabilities.Notifications.AdminNotifications) == 0 {
return errors.New("'admin notifications' not available on this instance")
}
return nil
}
func (c *Client) notificationsAvailable() error {
if len(c.capabilities.Notifications.OcsEndpoints) == 0 {
return errors.New("notifications not available on this instance")
}
return nil
}

90
notifications_test.go Normal file
View File

@ -0,0 +1,90 @@
package gonextcloud
import (
"github.com/stretchr/testify/assert"
"testing"
)
var (
notificationID int
createdID int
title = "Short Message"
message = "Longer notification message"
tests = []struct {
string
test
}{
{
"notificationCreate",
func(t *testing.T) {
err := c.NotificationsCreate(config.Login, title, message)
assert.NoError(t, err)
},
}, {
"notificationDelete",
func(t *testing.T) {
// Get created Notification ID
ns, err := c.NotificationsList()
if err != nil {
t.SkipNow()
}
for _, n := range ns {
if n.Subject == title {
createdID = n.NotificationID
break
}
}
if createdID == 0 {
t.SkipNow()
}
err = c.NotificationsDelete(createdID)
assert.NoError(t, err)
},
},
}
)
func TestNotificationsList(t *testing.T) {
c = nil
if err := initClient(); err != nil {
t.Fatal(err)
}
if err := c.notificationsAvailable(); err != nil {
t.SkipNow()
}
ns, err := c.NotificationsList()
assert.NoError(t, err)
if len(ns) > 0 {
notificationID = ns[0].NotificationID
}
}
func TestNotifications(t *testing.T) {
if notificationID == 0 {
t.SkipNow()
}
c = nil
if err := initClient(); err != nil {
t.Fatal(err)
}
if err := c.notificationsAvailable(); err != nil {
t.SkipNow()
}
n, err := c.Notifications(notificationID)
assert.NoError(t, err)
assert.NotEmpty(t, n)
}
// Disable due to very long response time
//func TestNotificationsAdmin(t *testing.T) {
// c = nil
// if err := initClient(); err != nil {
// t.Fatal(err)
// }
// if err := c.adminNotificationsAvailable(); err != nil {
// t.SkipNow()
// }
// for _, test := range tests {
// t.Run(test.string, test.test)
// }
//}

View File

@ -12,6 +12,8 @@ type Routes struct {
shares *url.URL shares *url.URL
groupfolders *url.URL groupfolders *url.URL
appsConfig *url.URL appsConfig *url.URL
notifications *url.URL
adminNotifications *url.URL
} }
const badRequest = 998 const badRequest = 998
@ -25,7 +27,9 @@ var (
apps: &url.URL{Path: apiPath.Path + "/cloud/apps"}, apps: &url.URL{Path: apiPath.Path + "/cloud/apps"},
monitor: &url.URL{Path: apiPath.Path + "/apps/serverinfo/api/v1/info"}, monitor: &url.URL{Path: apiPath.Path + "/apps/serverinfo/api/v1/info"},
shares: &url.URL{Path: apiPath.Path + "/apps/files_sharing/api/v1/shares"}, shares: &url.URL{Path: apiPath.Path + "/apps/files_sharing/api/v1/shares"},
groupfolders: &url.URL{Path: "apps/groupfolders/folders"}, groupfolders: &url.URL{Path: "/apps/groupfolders/folders"},
appsConfig: &url.URL{Path: apiPath.Path + "/apps/provisioning_api/api/v1/config/apps"}, appsConfig: &url.URL{Path: apiPath.Path + "/apps/provisioning_api/api/v1/config/apps"},
notifications: &url.URL{Path: apiPath.Path + "/apps/notifications/api/v2/notifications"},
adminNotifications: &url.URL{Path: apiPath.Path + "/apps/admin_notifications/api/v2/notifications"},
} }
) )

View File

@ -12,6 +12,17 @@ type Capabilities struct {
Activity struct { Activity struct {
Apiv2 []string `json:"apiv2"` Apiv2 []string `json:"apiv2"`
} `json:"activity"` } `json:"activity"`
Ocm struct {
Enabled bool `json:"enabled"`
APIVersion string `json:"apiVersion"`
EndPoint string `json:"endPoint"`
ShareTypes []struct {
Name string `json:"name"`
Protocols struct {
Webdav string `json:"webdav"`
} `json:"protocols"`
} `json:"shareTypes"`
} `json:"ocm"`
Dav struct { Dav struct {
Chunking string `json:"chunking"` Chunking string `json:"chunking"`
} `json:"dav"` } `json:"dav"`
@ -43,6 +54,7 @@ type Capabilities struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
} `json:"expire_date"` } `json:"expire_date"`
} `json:"group"` } `json:"group"`
DefaultPermissions int `json:"default_permissions"`
Federation struct { Federation struct {
Outgoing bool `json:"outgoing"` Outgoing bool `json:"outgoing"`
Incoming bool `json:"incoming"` Incoming bool `json:"incoming"`
@ -66,6 +78,7 @@ type Capabilities struct {
Notifications struct { Notifications struct {
OcsEndpoints []string `json:"ocs-endpoints"` OcsEndpoints []string `json:"ocs-endpoints"`
Push []string `json:"push"` Push []string `json:"push"`
AdminNotifications []string `json:"admin-notifications"`
} `json:"notifications"` } `json:"notifications"`
PasswordPolicy struct { PasswordPolicy struct {
MinLength int `json:"minLength"` MinLength int `json:"minLength"`
@ -92,4 +105,9 @@ type Capabilities struct {
Undelete bool `json:"undelete"` Undelete bool `json:"undelete"`
Versioning bool `json:"versioning"` Versioning bool `json:"versioning"`
} `json:"files"` } `json:"files"`
Registration struct {
Enabled bool `json:"enabled"`
APIRoot string `json:"apiRoot"`
APILevel string `json:"apiLevel"`
} `json:"registration"`
} }

21
types/notification.go Normal file
View File

@ -0,0 +1,21 @@
package types
import "time"
type Notification struct {
NotificationID int `json:"notification_id"`
App string `json:"app"`
User string `json:"user"`
Datetime time.Time `json:"datetime"`
ObjectType string `json:"object_type"`
ObjectID string `json:"object_id"`
Subject string `json:"subject"`
Message string `json:"message"`
Link string `json:"link"`
SubjectRich string `json:"subjectRich"`
SubjectRichParameters []interface{} `json:"subjectRichParameters"`
MessageRich string `json:"messageRich"`
MessageRichParameters []interface{} `json:"messageRichParameters"`
Icon string `json:"icon"`
Actions []interface{} `json:"actions"`
}

View File

@ -168,3 +168,17 @@ type GroupFoldersResponse struct {
Data GroupFolderBadFormatGroups `json:"data"` Data GroupFolderBadFormatGroups `json:"data"`
} `json:"ocs"` } `json:"ocs"`
} }
type NotificationsListResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Data []Notification `json:"data"`
} `json:"ocs"`
}
type NotificationResponse struct {
Ocs struct {
Meta Meta `json:"meta"`
Data Notification `json:"data"`
} `json:"ocs"`
}