diff --git a/Makefile b/Makefile index 5e89480..ede0f4b 100644 --- a/Makefile +++ b/Makefile @@ -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}' diff --git a/gonextcloud_test.go b/gonextcloud_test.go index 564072a..8c00f8d 100644 --- a/gonextcloud_test.go +++ b/gonextcloud_test.go @@ -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) diff --git a/notifications.go b/notifications.go new file mode 100644 index 0000000..080917a --- /dev/null +++ b/notifications.go @@ -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 +} diff --git a/notifications_test.go b/notifications_test.go new file mode 100644 index 0000000..4c27002 --- /dev/null +++ b/notifications_test.go @@ -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) +// } +//} diff --git a/routes.go b/routes.go index eb2774d..84cc31b 100644 --- a/routes.go +++ b/routes.go @@ -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"}, } ) diff --git a/types/capabilities.go b/types/capabilities.go index a505fdc..32dd44e 100644 --- a/types/capabilities.go +++ b/types/capabilities.go @@ -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"` } diff --git a/types/notification.go b/types/notification.go new file mode 100644 index 0000000..931b6a6 --- /dev/null +++ b/types/notification.go @@ -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"` +} diff --git a/types/responses.go b/types/responses.go index 75b6eb8..98dea48 100644 --- a/types/responses.go +++ b/types/responses.go @@ -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"` +}