Skip to content

Commit

Permalink
feat: Upgrade to new V2 Clients and refactor PushToCore (#882)
Browse files Browse the repository at this point in the history
* feat: Upgrade to new V2 Clients and refactor PushToCore

closes #370 & #721

BREAKING CHANGE: PushToCore signatures and required parameters have changed

Signed-off-by: lenny <[email protected]>
  • Loading branch information
Lenny Goodell authored Jun 9, 2021
1 parent 06230dd commit 69a9f95
Show file tree
Hide file tree
Showing 29 changed files with 1,057 additions and 419 deletions.
2 changes: 1 addition & 1 deletion app-service-template/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ go 1.15

require (
github.com/edgexfoundry/app-functions-sdk-go/v2 v2.0.0-dev.52
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.91
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.98
github.com/google/uuid v1.2.0
github.com/stretchr/testify v1.7.0
)
Expand Down
15 changes: 15 additions & 0 deletions app-service-template/res/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ TokenFile = '/tmp/edgex/secrets/new-app-service/secrets-token.json'
Host = 'localhost'
Port = 59880

[Clients.core-metadata]
Protocol = 'http'
Host = 'localhost'
Port = 59881

[Clients.core-command]
Protocol = 'http'
Host = 'localhost'
Port = 59882

[Clients.support-notifications]
Protocol = 'http'
Host = 'localhost'
Port = 59860

[Trigger]
Type="edgex-messagebus"
[Trigger.EdgexMessageBus]
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ require (
github.com/diegoholiveira/jsonlogic v1.0.1-0.20200220175622-ab7989be08b9
github.com/eclipse/paho.mqtt.golang v1.3.4
github.com/edgexfoundry/go-mod-bootstrap/v2 v2.0.0-dev.61
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.91
github.com/edgexfoundry/go-mod-core-contracts/v2 v2.0.0-dev.98
github.com/edgexfoundry/go-mod-messaging/v2 v2.0.0-dev.13
github.com/edgexfoundry/go-mod-registry/v2 v2.0.0-dev.7
github.com/fxamacker/cbor/v2 v2.2.0
Expand Down
58 changes: 50 additions & 8 deletions internal/app/configurable.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/interfaces"
"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/transforms"
"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/util"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2"

"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
)
Expand All @@ -49,8 +50,11 @@ const (
Retain = "retain"
AutoReconnect = "autoreconnect"
ConnectTimeout = "connecttimeout"
ProfileName = "profilename"
DeviceName = "devicename"
ReadingName = "readingname"
ResourceName = "resourcename"
ValueType = "valuetype"
MediaType = "mediatype"
Rule = "rule"
BatchThreshold = "batchthreshold"
TimeInterval = "timeinterval"
Expand Down Expand Up @@ -189,22 +193,60 @@ func (app *Configurable) Transform(parameters map[string]string) interfaces.AppF
// CoreServices then your deviceName and readingName must exist in the CoreMetadata and be properly registered in EdgeX.
// This function is a configuration function and returns a function pointer.
func (app *Configurable) PushToCore(parameters map[string]string) interfaces.AppFunction {
profileName, ok := parameters[ProfileName]
if !ok {
app.lc.Errorf("Could not find %s", ProfileName)
return nil
}
deviceName, ok := parameters[DeviceName]
if !ok {
app.lc.Error("Could not find " + DeviceName)
app.lc.Errorf("Could not find %s", DeviceName)
return nil
}
resourceName, ok := parameters[ResourceName]
if !ok {
app.lc.Errorf("Could not find %s", ResourceName)
return nil
}
readingName, ok := parameters[ReadingName]
valueType, ok := parameters[ValueType]
if !ok {
app.lc.Error("Could not find " + ReadingName)
app.lc.Errorf("Could not find %s", ValueType)
return nil
}

profileName = strings.TrimSpace(profileName)
deviceName = strings.TrimSpace(deviceName)
readingName = strings.TrimSpace(readingName)
transform := transforms.CoreData{
DeviceName: deviceName,
ReadingName: readingName,
resourceName = strings.TrimSpace(resourceName)
valueType = strings.TrimSpace(valueType)

var transform *transforms.CoreData

// Converts to upper case and validates it is a validates ValueType
valueType, err := v2.NormalizeValueType(valueType)
if err != nil {
app.lc.Error(err.Error())
return nil
}

if valueType == v2.ValueTypeBinary {
mediaType, ok := parameters[MediaType]
if !ok {
app.lc.Error("Could not find " + MediaType)
return nil
}

mediaType = strings.TrimSpace(mediaType)

if len(mediaType) == 0 {
app.lc.Error("MediaType can not be empty when ValueType=Binary")
return nil
}

transform = transforms.NewCoreDataBinaryReading(profileName, deviceName, resourceName, mediaType)
} else {
transform = transforms.NewCoreDataSimpleReading(profileName, deviceName, resourceName, valueType)
}

return transform.PushToCoreData
}

Expand Down
57 changes: 57 additions & 0 deletions internal/app/configurable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,60 @@ func TestEncrypt(t *testing.T) {
})
}
}

func TestConfigurable_PushToCore(t *testing.T) {
configurable := Configurable{lc: lc}

profileName := "MyProfile"
deviceName := "MyDevice"
resourceName := "MyResource"
simpleValueType := "int64"
binaryValueType := "binary"
badValueType := "bogus"
mediaType := "application/mxl"
emptyMediaType := ""

tests := []struct {
Name string
ProfileName *string
DeviceName *string
ResourceName *string
ValueType *string
MediaType *string
ExpectNil bool
}{
{"Valid simple", &profileName, &deviceName, &resourceName, &simpleValueType, nil, false},
{"Invalid simple - missing profile", nil, &deviceName, &resourceName, &simpleValueType, nil, true},
{"Invalid simple - missing device", &profileName, nil, &resourceName, &simpleValueType, nil, true},
{"Invalid simple - missing resource", &profileName, &deviceName, nil, &simpleValueType, nil, true},
{"Invalid simple - missing value type", &profileName, &deviceName, &resourceName, nil, nil, true},
{"Invalid - bad value type", &profileName, &deviceName, &resourceName, &badValueType, nil, true},
{"Valid binary", &profileName, &deviceName, &resourceName, &binaryValueType, &mediaType, false},
{"Invalid binary - empty MediaType", &profileName, &deviceName, &resourceName, &binaryValueType, &emptyMediaType, true},
{"Invalid binary - missing MediaType", &profileName, &deviceName, &resourceName, &binaryValueType, nil, true},
}

for _, testCase := range tests {
t.Run(testCase.Name, func(t *testing.T) {
params := make(map[string]string)
if testCase.ProfileName != nil {
params[ProfileName] = *testCase.ProfileName
}
if testCase.DeviceName != nil {
params[DeviceName] = *testCase.DeviceName
}
if testCase.ResourceName != nil {
params[ResourceName] = *testCase.ResourceName
}
if testCase.ValueType != nil {
params[ValueType] = *testCase.ValueType
}
if testCase.MediaType != nil {
params[MediaType] = *testCase.MediaType
}

transform := configurable.PushToCore(params)
assert.Equal(t, testCase.ExpectNil, transform == nil)
})
}
}
40 changes: 29 additions & 11 deletions internal/app/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ import (
"sync"
"syscall"

"github.com/edgexfoundry/go-mod-core-contracts/v2/clients"
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/coredata"
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/logger"
"github.com/edgexfoundry/go-mod-core-contracts/v2/clients/notifications"
"github.com/edgexfoundry/go-mod-core-contracts/v2/models"
"github.com/edgexfoundry/go-mod-core-contracts/v2/v2"
clientInterfaces "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/clients/interfaces"
"github.com/edgexfoundry/go-mod-messaging/v2/pkg/types"
"github.com/edgexfoundry/go-mod-registry/v2/registry"
Expand Down Expand Up @@ -110,10 +108,10 @@ type contextGroup struct {

// AddRoute allows you to leverage the existing webserver to add routes.
func (svc *Service) AddRoute(route string, handler func(nethttp.ResponseWriter, *nethttp.Request), methods ...string) error {
if route == clients.ApiPingRoute ||
route == clients.ApiConfigRoute ||
route == clients.ApiMetricsRoute ||
route == clients.ApiVersionRoute ||
if route == v2.ApiPingRoute ||
route == v2.ApiConfigRoute ||
route == v2.ApiMetricsRoute ||
route == v2.ApiVersionRoute ||
route == internal.ApiTriggerRoute {
return errors.New("route is reserved")
}
Expand Down Expand Up @@ -471,7 +469,7 @@ func (svc *Service) RegistryClient() registry.Client {
}

// EventClient returns the Event client, which may be nil, from the dependency injection container
func (svc *Service) EventClient() coredata.EventClient {
func (svc *Service) EventClient() clientInterfaces.EventClient {
return container.EventClientFrom(svc.dic.Get)
}

Expand All @@ -480,9 +478,29 @@ func (svc *Service) CommandClient() clientInterfaces.CommandClient {
return container.CommandClientFrom(svc.dic.Get)
}

// NotificationsClient returns the Notifications client, which may be nil, from the dependency injection container
func (svc *Service) NotificationsClient() notifications.NotificationsClient {
return container.NotificationsClientFrom(svc.dic.Get)
// DeviceServiceClient returns the DeviceService client, which may be nil, from the dependency injection container
func (svc *Service) DeviceServiceClient() clientInterfaces.DeviceServiceClient {
return container.DeviceServiceClientFrom(svc.dic.Get)
}

// DeviceProfileClient returns the DeviceProfile client, which may be nil, from the dependency injection container
func (svc *Service) DeviceProfileClient() clientInterfaces.DeviceProfileClient {
return container.DeviceProfileClientFrom(svc.dic.Get)
}

// DeviceClient returns the Device client, which may be nil, from the dependency injection container
func (svc *Service) DeviceClient() clientInterfaces.DeviceClient {
return container.DeviceClientFrom(svc.dic.Get)
}

// NotificationClient returns the Notifications client, which may be nil, from the dependency injection container
func (svc *Service) NotificationClient() clientInterfaces.NotificationClient {
return container.NotificationClientFrom(svc.dic.Get)
}

// SubscriptionClient returns the Subscription client, which may be nil, from the dependency injection container
func (svc *Service) SubscriptionClient() clientInterfaces.SubscriptionClient {
return container.SubscriptionClientFrom(svc.dic.Get)
}

func listParameters(parameters map[string]string) string {
Expand Down
113 changes: 113 additions & 0 deletions internal/app/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/edgexfoundry/app-functions-sdk-go/v2/internal/trigger/messagebus"
"github.com/edgexfoundry/app-functions-sdk-go/v2/internal/webserver"
"github.com/edgexfoundry/app-functions-sdk-go/v2/pkg/interfaces"
v2clients "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/clients/http"

bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v2/bootstrap/container"
"github.com/edgexfoundry/go-mod-bootstrap/v2/di"
Expand All @@ -42,6 +43,8 @@ import (

var lc logger.LoggingClient
var dic *di.Container
var target *Service
var baseUrl = "http://localhost:"

func TestMain(m *testing.M) {
// No remote and no file results in STDOUT logging only
Expand All @@ -55,6 +58,10 @@ func TestMain(m *testing.M) {
},
})

target = NewService("unitTest", nil, "")
target.dic = dic
target.lc = lc

m.Run()
}

Expand Down Expand Up @@ -552,3 +559,109 @@ func TestFindMatchingFunction(t *testing.T) {
})
}
}

func TestService_EventClient(t *testing.T) {
actual := target.EventClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.EventClientName: func(get di.Get) interface{} {
return v2clients.NewEventClient(baseUrl + "59880")
},
})

actual = target.EventClient()
assert.NotNil(t, actual)
}

func TestService_CommandClient(t *testing.T) {
actual := target.CommandClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.CommandClientName: func(get di.Get) interface{} {
return v2clients.NewCommandClient(baseUrl + "59882")
},
})

actual = target.CommandClient()
assert.NotNil(t, actual)
}

func TestService_DeviceServiceClient(t *testing.T) {
actual := target.DeviceServiceClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.DeviceServiceClientName: func(get di.Get) interface{} {
return v2clients.NewDeviceServiceClient(baseUrl + "59881")
},
})

actual = target.DeviceServiceClient()
assert.NotNil(t, actual)

}

func TestService_DeviceProfileClient(t *testing.T) {
actual := target.DeviceProfileClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.DeviceProfileClientName: func(get di.Get) interface{} {
return v2clients.NewDeviceProfileClient(baseUrl + "59881")
},
})

actual = target.DeviceProfileClient()
assert.NotNil(t, actual)
}

func TestService_DeviceClient(t *testing.T) {
actual := target.DeviceClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.DeviceClientName: func(get di.Get) interface{} {
return v2clients.NewDeviceClient(baseUrl + "59881")
},
})

actual = target.DeviceClient()
assert.NotNil(t, actual)

}

func TestService_NotificationClient(t *testing.T) {
actual := target.NotificationClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.NotificationClientName: func(get di.Get) interface{} {
return v2clients.NewNotificationClient(baseUrl + "59860")
},
})

actual = target.NotificationClient()
assert.NotNil(t, actual)

}

func TestService_SubscriptionClient(t *testing.T) {
actual := target.SubscriptionClient()
assert.Nil(t, actual)

dic.Update(di.ServiceConstructorMap{
container.SubscriptionClientName: func(get di.Get) interface{} {
return v2clients.NewSubscriptionClient(baseUrl + "59860")
},
})

actual = target.SubscriptionClient()
assert.NotNil(t, actual)
}

func TestService_LoggingClient(t *testing.T) {
actual := target.LoggingClient()
assert.NotNil(t, actual)
}
Loading

0 comments on commit 69a9f95

Please sign in to comment.