Skip to content

Commit

Permalink
feat: Create client library for support-notifications (#626)
Browse files Browse the repository at this point in the history
* feat: Create client library for support-notifications

Create clients for Subscription, Notification, and Transmission APIs

Close #573

Signed-off-by: weichou <[email protected]>

* refactor: Rename the Notification sending func

Close #573

Signed-off-by: weichou <[email protected]>
  • Loading branch information
weichou1229 authored May 31, 2021
1 parent d9f4bb2 commit ee4e77d
Show file tree
Hide file tree
Showing 13 changed files with 1,508 additions and 0 deletions.
6 changes: 6 additions & 0 deletions v2/clients/http/const_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ const (
TestHost = "localhost"
TestPort = 48089
TestHTTPMethod = "GET"

TestSubscriptionName = "TestSubscriptionName"
TestReceiver = "user"
TestCategory = "health-check"
TestLabel = "rest"
ExampleUUID = "82eb2e26-0f24-48aa-ae4c-de9dac3fb9bc"
)
158 changes: 158 additions & 0 deletions v2/clients/http/notification.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//
// Copyright (C) 2021 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package http

import (
"context"
"net/url"
"path"
"strconv"

"github.com/edgexfoundry/go-mod-core-contracts/v2/errors"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/clients/http/utils"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/clients/interfaces"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/common"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/requests"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/responses"
)

type NotificationClient struct {
baseUrl string
}

// NewNotificationClient creates an instance of NotificationClient
func NewNotificationClient(baseUrl string) interfaces.NotificationClient {
return &NotificationClient{
baseUrl: baseUrl,
}
}

// SendNotification sends new notifications.
func (client *NotificationClient) SendNotification(ctx context.Context, reqs []requests.AddNotificationRequest) (res []common.BaseWithIdResponse, err errors.EdgeX) {
err = utils.PostRequestWithRawData(ctx, &res, client.baseUrl+v2.ApiNotificationRoute, reqs)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationById query notification by id.
func (client *NotificationClient) NotificationById(ctx context.Context, id string) (res responses.NotificationResponse, err errors.EdgeX) {
path := path.Join(v2.ApiNotificationRoute, v2.Id, url.QueryEscape(id))
err = utils.GetRequest(ctx, &res, client.baseUrl, path, nil)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// DeleteNotificationById deletes a notification by id.
func (client *NotificationClient) DeleteNotificationById(ctx context.Context, id string) (res common.BaseResponse, err errors.EdgeX) {
path := path.Join(v2.ApiNotificationRoute, v2.Id, url.QueryEscape(id))
err = utils.DeleteRequest(ctx, &res, client.baseUrl, path)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationsByCategory queries notifications with category, offset and limit
func (client *NotificationClient) NotificationsByCategory(ctx context.Context, category string, offset int, limit int) (res responses.MultiNotificationsResponse, err errors.EdgeX) {
requestPath := path.Join(v2.ApiNotificationRoute, v2.Category, url.QueryEscape(category))
requestParams := url.Values{}
requestParams.Set(v2.Offset, strconv.Itoa(offset))
requestParams.Set(v2.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationsByLabel queries notifications with label, offset and limit
func (client *NotificationClient) NotificationsByLabel(ctx context.Context, label string, offset int, limit int) (res responses.MultiNotificationsResponse, err errors.EdgeX) {
requestPath := path.Join(v2.ApiNotificationRoute, v2.Label, url.QueryEscape(label))
requestParams := url.Values{}
requestParams.Set(v2.Offset, strconv.Itoa(offset))
requestParams.Set(v2.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationsByStatus queries notifications with status, offset and limit
func (client *NotificationClient) NotificationsByStatus(ctx context.Context, status string, offset int, limit int) (res responses.MultiNotificationsResponse, err errors.EdgeX) {
requestPath := path.Join(v2.ApiNotificationRoute, v2.Status, url.QueryEscape(status))
requestParams := url.Values{}
requestParams.Set(v2.Offset, strconv.Itoa(offset))
requestParams.Set(v2.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationsByTimeRange query notifications with time range, offset and limit
func (client *NotificationClient) NotificationsByTimeRange(ctx context.Context, start int, end int, offset int, limit int) (res responses.MultiNotificationsResponse, err errors.EdgeX) {
requestPath := path.Join(v2.ApiNotificationRoute, v2.Start, strconv.Itoa(start), v2.End, strconv.Itoa(end))
requestParams := url.Values{}
requestParams.Set(v2.Offset, strconv.Itoa(offset))
requestParams.Set(v2.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// NotificationsBySubscriptionName query notifications with subscriptionName, offset and limit
func (client *NotificationClient) NotificationsBySubscriptionName(ctx context.Context, subscriptionName string, offset int, limit int) (res responses.MultiNotificationsResponse, err errors.EdgeX) {
requestPath := path.Join(v2.ApiNotificationRoute, v2.Subscription, v2.Name, url.QueryEscape(subscriptionName))
requestParams := url.Values{}
requestParams.Set(v2.Offset, strconv.Itoa(offset))
requestParams.Set(v2.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, client.baseUrl, requestPath, requestParams)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// CleanupNotificationsByAge removes notifications that are older than age. And the corresponding transmissions will also be deleted.
// Age is supposed in milliseconds since modified timestamp
func (client *NotificationClient) CleanupNotificationsByAge(ctx context.Context, age int) (res common.BaseResponse, err errors.EdgeX) {
path := path.Join(v2.ApiNotificationCleanupRoute, v2.Age, strconv.Itoa(age))
err = utils.DeleteRequest(ctx, &res, client.baseUrl, path)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// CleanupNotifications removes notifications and the corresponding transmissions.
func (client *NotificationClient) CleanupNotifications(ctx context.Context) (res common.BaseResponse, err errors.EdgeX) {
err = utils.DeleteRequest(ctx, &res, client.baseUrl, v2.ApiNotificationCleanupRoute)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

// DeleteProcessedNotificationsByAge removes processed notifications that are older than age. And the corresponding transmissions will also be deleted.
// Age is supposed in milliseconds since modified timestamp
// Please notice that this API is only for processed notifications (status = PROCESSED). If the deletion purpose includes each kind of notifications, please refer to cleanup API.
func (client *NotificationClient) DeleteProcessedNotificationsByAge(ctx context.Context, age int) (res common.BaseResponse, err errors.EdgeX) {
path := path.Join(v2.ApiNotificationRoute, v2.Age, strconv.Itoa(age))
err = utils.DeleteRequest(ctx, &res, client.baseUrl, path)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}
153 changes: 153 additions & 0 deletions v2/clients/http/notification_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//
// Copyright (C) 2021 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package http

import (
"context"
"net/http"
"path"
"strconv"
"testing"

"github.com/edgexfoundry/go-mod-core-contracts/v2/v2"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/common"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/requests"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/dtos/responses"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models"

"github.com/stretchr/testify/require"
)

func addNotificationRequest() requests.AddNotificationRequest {
return requests.NewAddNotificationRequest(
dtos.Notification{
Id: ExampleUUID,
Content: "testContent",
Sender: "testSender",
Labels: []string{TestLabel},
Severity: models.Critical,
},
)
}

func TestNotificationClient_SendNotification(t *testing.T) {
ts := newTestServer(http.MethodPost, v2.ApiNotificationRoute, []common.BaseWithIdResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.SendNotification(context.Background(), []requests.AddNotificationRequest{addNotificationRequest()})
require.NoError(t, err)
require.IsType(t, []common.BaseWithIdResponse{}, res)
}

func TestNotificationClient_NotificationById(t *testing.T) {
testId := ExampleUUID
path := path.Join(v2.ApiNotificationRoute, v2.Id, testId)
ts := newTestServer(http.MethodGet, path, responses.NotificationResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationById(context.Background(), testId)
require.NoError(t, err)
require.IsType(t, responses.NotificationResponse{}, res)
}

func TestNotificationClient_NotificationsByCategory(t *testing.T) {
category := TestCategory
urlPath := path.Join(v2.ApiNotificationRoute, v2.Category, category)
ts := newTestServer(http.MethodGet, urlPath, responses.MultiNotificationsResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationsByCategory(context.Background(), category, 0, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiNotificationsResponse{}, res)
}

func TestNotificationClient_NotificationsByLabel(t *testing.T) {
label := TestLabel
urlPath := path.Join(v2.ApiNotificationRoute, v2.Label, label)
ts := newTestServer(http.MethodGet, urlPath, responses.MultiNotificationsResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationsByLabel(context.Background(), label, 0, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiNotificationsResponse{}, res)
}

func TestNotificationClient_NotificationsByStatus(t *testing.T) {
status := models.Processed
urlPath := path.Join(v2.ApiNotificationRoute, v2.Status, status)
ts := newTestServer(http.MethodGet, urlPath, responses.MultiNotificationsResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationsByStatus(context.Background(), status, 0, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiNotificationsResponse{}, res)
}

func TestNotificationClient_NotificationsBySubscriptionName(t *testing.T) {
subscriptionName := TestSubscriptionName
urlPath := path.Join(v2.ApiNotificationRoute, v2.Subscription, v2.Name, subscriptionName)
ts := newTestServer(http.MethodGet, urlPath, responses.MultiNotificationsResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationsBySubscriptionName(context.Background(), subscriptionName, 0, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiNotificationsResponse{}, res)
}

func TestNotificationClient_NotificationsByTimeRange(t *testing.T) {
start := 1
end := 10
urlPath := path.Join(v2.ApiNotificationRoute, v2.Start, strconv.Itoa(start), v2.End, strconv.Itoa(end))
ts := newTestServer(http.MethodGet, urlPath, responses.MultiNotificationsResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.NotificationsByTimeRange(context.Background(), start, end, 0, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiNotificationsResponse{}, res)
}

func TestNotificationClient_CleanupNotifications(t *testing.T) {
ts := newTestServer(http.MethodDelete, v2.ApiNotificationCleanupRoute, common.BaseResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.CleanupNotifications(context.Background())
require.NoError(t, err)
require.IsType(t, common.BaseResponse{}, res)
}

func TestNotificationClient_CleanupNotificationsByAge(t *testing.T) {
age := 0
path := path.Join(v2.ApiNotificationCleanupRoute, v2.Age, strconv.Itoa(age))
ts := newTestServer(http.MethodDelete, path, common.BaseResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.CleanupNotificationsByAge(context.Background(), age)
require.NoError(t, err)
require.IsType(t, common.BaseResponse{}, res)
}

func TestNotificationClient_DeleteNotificationById(t *testing.T) {
id := ExampleUUID
path := path.Join(v2.ApiNotificationRoute, v2.Id, id)
ts := newTestServer(http.MethodDelete, path, common.BaseResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.DeleteNotificationById(context.Background(), id)
require.NoError(t, err)
require.IsType(t, common.BaseResponse{}, res)
}

func TestNotificationClient_DeleteProcessedNotificationsByAge(t *testing.T) {
age := 0
path := path.Join(v2.ApiNotificationRoute, v2.Age, strconv.Itoa(age))
ts := newTestServer(http.MethodDelete, path, common.BaseResponse{})
defer ts.Close()
client := NewNotificationClient(ts.URL)
res, err := client.DeleteProcessedNotificationsByAge(context.Background(), age)
require.NoError(t, err)
require.IsType(t, common.BaseResponse{}, res)
}
Loading

0 comments on commit ee4e77d

Please sign in to comment.