Commit 75e83977 authored by Adphi's avatar Adphi
Browse files

#1 Implements Notifications API

parent 4a1bec45
Pipeline #143 passed with stage
in 4 minutes and 24 seconds
......@@ -5,7 +5,7 @@ GO_FILES := $(shell find . -name '*.go' | grep -v _test.go)
.PHONY: all dep build clean test coverage coverhtml lint
all: build
all: dep lint test race coverage
lint: ## Lint the files
@golint -set_exit_status ${PKG_LIST}
......@@ -28,5 +28,8 @@ dep: ## Get the dependencies
@mkdir -p vendor
@govendor add +external
push: dep lint test coverage ## Push to git repository
@git push origin master
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}'
......@@ -478,6 +478,9 @@ func TestUserCreateWithoutPassword(t *testing.T) {
if err := initClient(); err != nil {
t.Fatal(err)
}
if c.version.Major < 14 {
t.SkipNow()
}
// Nextcloud does not seems to like recreating a deleted user
err := c.UserCreateWithoutPassword(config.NotExistingUser, config.Email, strings.Title(config.NotExistingUser))
assert.NoError(t, err)
......
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
}
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)
// }
//}
......@@ -4,14 +4,16 @@ import "net/url"
// Routes references the available routes
type Routes struct {
capabilities *url.URL
users *url.URL
groups *url.URL
apps *url.URL
monitor *url.URL
shares *url.URL
groupfolders *url.URL
appsConfig *url.URL
capabilities *url.URL
users *url.URL
groups *url.URL
apps *url.URL
monitor *url.URL
shares *url.URL
groupfolders *url.URL
appsConfig *url.URL
notifications *url.URL
adminNotifications *url.URL
}
const badRequest = 998
......@@ -19,13 +21,15 @@ const badRequest = 998
var (
apiPath = &url.URL{Path: "/ocs/v2.php"}
routes = Routes{
capabilities: &url.URL{Path: apiPath.Path + "/cloud/capabilities"},
users: &url.URL{Path: apiPath.Path + "/cloud/users"},
groups: &url.URL{Path: apiPath.Path + "/cloud/groups"},
apps: &url.URL{Path: apiPath.Path + "/cloud/apps"},
monitor: &url.URL{Path: apiPath.Path + "/apps/serverinfo/api/v1/info"},
shares: &url.URL{Path: apiPath.Path + "/apps/files_sharing/api/v1/shares"},
groupfolders: &url.URL{Path: "apps/groupfolders/folders"},
appsConfig: &url.URL{Path: apiPath.Path + "/apps/provisioning_api/api/v1/config/apps"},
capabilities: &url.URL{Path: apiPath.Path + "/cloud/capabilities"},
users: &url.URL{Path: apiPath.Path + "/cloud/users"},
groups: &url.URL{Path: apiPath.Path + "/cloud/groups"},
apps: &url.URL{Path: apiPath.Path + "/cloud/apps"},
monitor: &url.URL{Path: apiPath.Path + "/apps/serverinfo/api/v1/info"},
shares: &url.URL{Path: apiPath.Path + "/apps/files_sharing/api/v1/shares"},
groupfolders: &url.URL{Path: "/apps/groupfolders/folders"},
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"},
}
)
......@@ -12,6 +12,17 @@ type Capabilities struct {
Activity struct {
Apiv2 []string `json:"apiv2"`
} `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 {
Chunking string `json:"chunking"`
} `json:"dav"`
......@@ -43,7 +54,8 @@ type Capabilities struct {
Enabled bool `json:"enabled"`
} `json:"expire_date"`
} `json:"group"`
Federation struct {
DefaultPermissions int `json:"default_permissions"`
Federation struct {
Outgoing bool `json:"outgoing"`
Incoming bool `json:"incoming"`
ExpireDate struct {
......@@ -64,8 +76,9 @@ type Capabilities struct {
} `json:"sharebymail"`
} `json:"files_sharing"`
Notifications struct {
OcsEndpoints []string `json:"ocs-endpoints"`
Push []string `json:"push"`
OcsEndpoints []string `json:"ocs-endpoints"`
Push []string `json:"push"`
AdminNotifications []string `json:"admin-notifications"`
} `json:"notifications"`
PasswordPolicy struct {
MinLength int `json:"minLength"`
......@@ -92,4 +105,9 @@ type Capabilities struct {
Undelete bool `json:"undelete"`
Versioning bool `json:"versioning"`
} `json:"files"`
Registration struct {
Enabled bool `json:"enabled"`
APIRoot string `json:"apiRoot"`
APILevel string `json:"apiLevel"`
} `json:"registration"`
}
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"`
}
......@@ -168,3 +168,17 @@ type GroupFoldersResponse struct {
Data GroupFolderBadFormatGroups `json:"data"`
} `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"`
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment