Skip to content

Commit

Permalink
Merge pull request #752 from lenny-intel/system-event-dto
Browse files Browse the repository at this point in the history
feat: Add new SystemEvent DTO
  • Loading branch information
Lenny Goodell authored Jul 27, 2022
2 parents 0dd1dc3 + 05adad6 commit f00d2ae
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 0 deletions.
8 changes: 8 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,11 @@ const (
ContentTypeText = "text/plain"
ContentTypeXML = "application/xml"
)

// Constants related to System Events
const (
DeviceSystemEventType = "device"
DeviceSystemEventActionAdd = "add"
DeviceSystemEventActionUpdate = "update"
DeviceSystemEventActionDelete = "delete"
)
71 changes: 71 additions & 0 deletions dtos/systemevent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright 2022 Intel Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*******************************************************************************/

package dtos

import (
"encoding/json"
"errors"
"fmt"
"time"

"github.com/edgexfoundry/go-mod-core-contracts/v2/dtos/common"
)

// SystemEvent defines the data for a system event
type SystemEvent struct {
common.Versionable `json:",inline"`
Type string `json:"type"`
Action string `json:"action"`
Source string `json:"source"`
Owner string `json:"owner"`
Tags map[string]string `json:"tags"`
Details any `json:"details"`
Timestamp int64 `json:"timestamp"`
}

// NewSystemEvent creates a new SystemEvent for the specified data
func NewSystemEvent(eventType, action, source, owner string, tags map[string]string, details any) SystemEvent {
return SystemEvent{
Versionable: common.NewVersionable(),
Type: eventType,
Action: action,
Source: source,
Owner: owner,
Tags: tags,
Details: details,
Timestamp: time.Now().UnixNano(),
}
}

// DecodeDetails decodes the details (any type) into the passed in object
func (s *SystemEvent) DecodeDetails(details any) error {
if s.Details == nil {
return errors.New("unable to decode System Event details: Details are nil")
}

// Must encode the details to JSON since if the target SystemEvent was decoded from JSON the details are
// captured in a map[string]interface{}.
data, err := json.Marshal(s.Details)
if err != nil {
return fmt.Errorf("unable to encode System Event details to JSON: %s", err.Error())
}

err = json.Unmarshal(data, details)
if err != nil {
return fmt.Errorf("unable to decode System Event details from JSON: %s", err.Error())
}

return nil
}
100 changes: 100 additions & 0 deletions dtos/systemevent_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright 2022 Intel Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*******************************************************************************/

package dtos

import (
"encoding/json"
"testing"
"time"

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

"github.com/edgexfoundry/go-mod-core-contracts/v2/common"
)

var expectedApiVersion = common.ApiVersion
var expectedType = common.DeviceSystemEventType
var expectedAction = common.DeviceSystemEventActionAdd
var expectedSoonerTimestamp = time.Now().UnixNano()
var expectedSource = "core-metadata"
var expectedOwner = "device-onvif-camera"
var expectedTags = map[string]string{"device-profile": "onvif-camera"}
var expectedDetails = Device{
Id: TestUUID,
Name: "My-Camera-Device",
ServiceName: "device-onvif-camera",
ProfileName: "onvif-camera",
Protocols: map[string]ProtocolProperties{
"Onvif": {
"Address": "192.168.12.123",
"Port": "80",
},
},
}

func TestNewSystemEvent(t *testing.T) {
actual := NewSystemEvent(expectedType, expectedAction, expectedSource, expectedOwner, expectedTags, expectedDetails)

assert.Equal(t, expectedApiVersion, actual.ApiVersion)
assert.Equal(t, expectedType, actual.Type)
assert.Equal(t, expectedAction, actual.Action)
assert.Equal(t, expectedSource, actual.Source)
assert.Equal(t, expectedOwner, actual.Owner)
assert.Equal(t, expectedTags, actual.Tags)
assert.Equal(t, expectedDetails, actual.Details)

expectedLaterTimestamp := time.Now().UnixNano()
assert.LessOrEqual(t, expectedSoonerTimestamp, actual.Timestamp)
assert.GreaterOrEqual(t, expectedLaterTimestamp, actual.Timestamp)
}

func TestDecodeDetails(t *testing.T) {
systemEvent := NewSystemEvent(expectedType, expectedAction, expectedSource, expectedOwner, expectedTags, expectedDetails)

// Simulate the System Event was received as encoded JSON and has been decoded which results in the Details being
// decoded to a map[string]interface{} since decoder doesn't know the actual type.
data, err := json.Marshal(systemEvent)
require.NoError(t, err)
target := &SystemEvent{}
err = json.Unmarshal(data, target)
require.NoError(t, err)

actual := &Device{}
err = target.DecodeDetails(actual)
require.NoError(t, err)
assert.Equal(t, expectedDetails, *actual)
}

func TestDecodeDetailsError(t *testing.T) {
tests := []struct {
Name string
Details any
expectedError string
}{
{"Nil details", nil, "Details are nil"},
{"string details", "...", "unable to decode System Event details from JSON"},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
target := NewSystemEvent(expectedType, expectedAction, expectedSource, expectedOwner, expectedTags, test.Details)
actual := &Device{}
err := target.DecodeDetails(actual)
require.Error(t, err)
require.Contains(t, err.Error(), test.expectedError)
})
}
}

0 comments on commit f00d2ae

Please sign in to comment.