Skip to content

Commit

Permalink
Merge pull request #935 from weichou1229/null-reading
Browse files Browse the repository at this point in the history
refactor: Using isNull instead of nil reading value
  • Loading branch information
cloudxxx8 authored Oct 16, 2024
2 parents 25dda26 + 43f1e99 commit 77124f1
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 62 deletions.
5 changes: 5 additions & 0 deletions dtos/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ func (e *Event) AddObjectReading(resourceName string, objectValue interface{}) {
e.Readings = append(e.Readings, NewObjectReading(e.ProfileName, e.DeviceName, resourceName, objectValue))
}

// AddNullReading adds a simple reading with null value to the Event
func (e *Event) AddNullReading(resourceName string, valueType string) {
e.Readings = append(e.Readings, NewNullReading(e.ProfileName, e.DeviceName, resourceName, valueType))
}

// ToXML provides a XML representation of the Event as a string
func (e *Event) ToXML() (string, error) {
eventXml, err := xml.Marshal(e)
Expand Down
57 changes: 56 additions & 1 deletion dtos/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package dtos

import (
"encoding/json"
"fmt"
"testing"

Expand Down Expand Up @@ -151,7 +152,7 @@ func TestEvent_AddSimpleReading(t *testing.T) {
assert.Equal(t, expectedDeviceName, actual.DeviceName)
assert.Equal(t, expectedReadingDetails[index].resourceName, actual.ResourceName)
assert.Equal(t, expectedReadingDetails[index].valueType, actual.ValueType)
assert.Equal(t, expectedReadingDetails[index].value, *actual.Value)
assert.Equal(t, expectedReadingDetails[index].value, actual.Value)
assert.NotZero(t, actual.Origin)
}
}
Expand Down Expand Up @@ -206,3 +207,57 @@ func TestEvent_AddObjectReading(t *testing.T) {
assert.Equal(t, expectedValue, actual.ObjectValue)
assert.NotZero(t, actual.Origin)
}

func TestEvent_MarshalNullReading(t *testing.T) {
testEvent := NewEvent(TestDeviceProfileName, TestDeviceName, TestSourceName)
testEvent.AddNullReading(common.ValueTypeInt8, common.ValueTypeInt8)
testEvent.AddNullReading(common.ValueTypeInt16, common.ValueTypeInt16)
testEvent.AddNullReading(common.ValueTypeInt32, common.ValueTypeInt32)
testEvent.AddNullReading(common.ValueTypeInt64, common.ValueTypeInt64)
testEvent.AddNullReading(common.ValueTypeUint8, common.ValueTypeUint8)
testEvent.AddNullReading(common.ValueTypeUint16, common.ValueTypeUint16)
testEvent.AddNullReading(common.ValueTypeUint32, common.ValueTypeUint32)
testEvent.AddNullReading(common.ValueTypeUint64, common.ValueTypeUint64)
testEvent.AddNullReading(common.ValueTypeInt8Array, common.ValueTypeInt8Array)
testEvent.AddNullReading(common.ValueTypeInt16Array, common.ValueTypeInt16Array)
testEvent.AddNullReading(common.ValueTypeInt32Array, common.ValueTypeInt32Array)
testEvent.AddNullReading(common.ValueTypeInt64Array, common.ValueTypeInt64Array)
testEvent.AddNullReading(common.ValueTypeUint8Array, common.ValueTypeUint8Array)
testEvent.AddNullReading(common.ValueTypeUint16Array, common.ValueTypeUint16Array)
testEvent.AddNullReading(common.ValueTypeUint32Array, common.ValueTypeUint32Array)
testEvent.AddNullReading(common.ValueTypeUint64Array, common.ValueTypeUint64Array)
testEvent.AddNullReading(common.ValueTypeFloat32, common.ValueTypeFloat32)
testEvent.AddNullReading(common.ValueTypeFloat64, common.ValueTypeFloat64)
testEvent.AddNullReading(common.ValueTypeFloat32Array, common.ValueTypeFloat32Array)
testEvent.AddNullReading(common.ValueTypeFloat64Array, common.ValueTypeFloat64Array)
testEvent.AddNullReading(common.ValueTypeObject, common.ValueTypeObject)
testEvent.AddNullReading(common.ValueTypeObjectArray, common.ValueTypeObjectArray)
testEvent.AddNullReading(common.ValueTypeBinary, common.ValueTypeBinary)

for _, actual := range testEvent.Readings {
assert.True(t, actual.isNull)
}

jsonEncoded, err := json.Marshal(testEvent)
assert.NoError(t, err)

var result Event
err = json.Unmarshal(jsonEncoded, &result)
assert.NoError(t, err)

assert.Equal(t, testEvent.DeviceName, result.DeviceName)
assert.Equal(t, testEvent.ProfileName, result.ProfileName)
assert.Equal(t, testEvent.SourceName, result.SourceName)
assert.Equal(t, len(testEvent.Readings), len(result.Readings))

for i, r := range result.Readings {
assert.Equal(t, testEvent.Readings[i].DeviceName, r.DeviceName)
assert.Equal(t, testEvent.Readings[i].ProfileName, r.ProfileName)
assert.Equal(t, testEvent.Readings[i].ResourceName, r.ResourceName)
assert.Equal(t, testEvent.Readings[i].ValueType, r.ValueType)
assert.True(t, r.isNull, "isNull should be true after marshaling")
assert.Empty(t, r.Value, "reading value should be empty after marshaling")
assert.Empty(t, r.ObjectValue, "reading objectValue should be empty after marshaling")
assert.Empty(t, r.BinaryValue, "reading binaryValue should be empty after marshaling")
}
}
149 changes: 134 additions & 15 deletions dtos/reading.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ type BaseReading struct {
BinaryReading `json:",inline" validate:"-"`
SimpleReading `json:",inline" validate:"-"`
ObjectReading `json:",inline" validate:"-"`
NullReading `json:",inline" validate:"-"`
}

type SimpleReading struct {
Value *string `json:"value"`
Value string `json:"value"`
}

type BinaryReading struct {
Expand All @@ -47,6 +48,10 @@ type ObjectReading struct {
ObjectValue any `json:"objectValue,omitempty"`
}

type NullReading struct {
isNull bool // indicate the reading value should be null in the JSON payload
}

func newBaseReading(profileName string, deviceName string, resourceName string, valueType string) BaseReading {
return BaseReading{
Id: uuid.NewString(),
Expand All @@ -60,20 +65,14 @@ func newBaseReading(profileName string, deviceName string, resourceName string,

// NewSimpleReading creates and returns a new initialized BaseReading with its SimpleReading initialized
func NewSimpleReading(profileName string, deviceName string, resourceName string, valueType string, value any) (BaseReading, error) {
var val *string
if value == nil {
val = nil
} else {
stringValue, err := convertInterfaceValue(valueType, value)
if err != nil {
return BaseReading{}, err
}
val = &stringValue
stringValue, err := convertInterfaceValue(valueType, value)
if err != nil {
return BaseReading{}, err
}

reading := newBaseReading(profileName, deviceName, resourceName, valueType)
reading.SimpleReading = SimpleReading{
Value: val,
Value: stringValue,
}
return reading, nil
}
Expand Down Expand Up @@ -106,6 +105,13 @@ func NewObjectReadingWithArray(profileName string, deviceName string, resourceNa
return reading
}

// NewNullReading creates and returns a new initialized BaseReading with null
func NewNullReading(profileName string, deviceName string, resourceName string, valueType string) BaseReading {
reading := newBaseReading(profileName, deviceName, resourceName, valueType)
reading.isNull = true
return reading
}

func convertInterfaceValue(valueType string, value any) (string, error) {
switch valueType {
case common.ValueTypeBool:
Expand Down Expand Up @@ -274,6 +280,9 @@ func validateType(valueType string, kind reflect.Kind, value any) error {

// Validate satisfies the Validator interface
func (b BaseReading) Validate() error {
if b.isNull {
return nil
}
if b.ValueType == common.ValueTypeBinary {
// validate the inner BinaryReading struct
binaryReading := b.BinaryReading
Expand All @@ -289,13 +298,10 @@ func (b BaseReading) Validate() error {
} else {
// validate the inner SimpleReading struct
simpleReading := b.SimpleReading
if simpleReading.Value == nil {
return nil
}
if err := common.Validate(simpleReading); err != nil {
return err
}
if err := ValidateValue(b.ValueType, *simpleReading.Value); err != nil {
if err := ValidateValue(b.ValueType, simpleReading.Value); err != nil {
return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("The value does not match the %v valueType", b.ValueType), nil)
}
}
Expand All @@ -316,6 +322,9 @@ func ToReadingModel(r BaseReading) models.Reading {
Units: r.Units,
Tags: r.Tags,
}
if r.NullReading.isNull {
return models.NewNullReading(br)
}
if r.ValueType == common.ValueTypeBinary {
readingModel = models.BinaryReading{
BaseReading: br,
Expand Down Expand Up @@ -375,6 +384,18 @@ func FromReadingModelToDTO(reading models.Reading) BaseReading {
Tags: r.Tags,
SimpleReading: SimpleReading{Value: r.Value},
}
case models.NullReading:
baseReading = BaseReading{
Id: r.Id,
Origin: r.Origin,
DeviceName: r.DeviceName,
ResourceName: r.ResourceName,
ProfileName: r.ProfileName,
ValueType: r.ValueType,
Units: r.Units,
Tags: r.Tags,
NullReading: NullReading{isNull: true},
}
}

return baseReading
Expand Down Expand Up @@ -484,3 +505,101 @@ func (b BaseReading) UnmarshalObjectValue(target any) error {

return nil
}

func (b BaseReading) MarshalJSON() ([]byte, error) {
type reading struct {
Id string `json:"id,omitempty"`
Origin int64 `json:"origin"`
DeviceName string `json:"deviceName"`
ResourceName string `json:"resourceName"`
ProfileName string `json:"profileName"`
ValueType string `json:"valueType"`
Units string `json:"units,omitempty"`
Tags Tags `json:"tags,omitempty"`
}
if b.isNull {
return json.Marshal(&struct {
reading `json:",inline"`
Value any `json:"value"`
BinaryValue any `json:"binaryValue"`
ObjectValue any `json:"objectValue"`
}{
reading: reading{
Id: b.Id,
Origin: b.Origin,
DeviceName: b.DeviceName,
ResourceName: b.ResourceName,
ProfileName: b.ProfileName,
ValueType: b.ValueType,
Units: b.Units,
Tags: b.Tags,
},
Value: nil,
BinaryValue: nil,
ObjectValue: nil,
})
}
return json.Marshal(&struct {
reading `json:",inline"`
BinaryReading `json:",inline" validate:"-"`
SimpleReading `json:",inline" validate:"-"`
ObjectReading `json:",inline" validate:"-"`
}{
reading: reading{
Id: b.Id,
Origin: b.Origin,
DeviceName: b.DeviceName,
ResourceName: b.ResourceName,
ProfileName: b.ProfileName,
ValueType: b.ValueType,
Units: b.Units,
Tags: b.Tags,
},
BinaryReading: b.BinaryReading,
SimpleReading: b.SimpleReading,
ObjectReading: b.ObjectReading,
})
}

func (b *BaseReading) UnmarshalJSON(data []byte) error {
var aux struct {
Id string
Origin int64
DeviceName string
ResourceName string
ProfileName string
ValueType string
Units string
Tags Tags
Value any
BinaryReading
ObjectReading
}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}

b.Id = aux.Id
b.Origin = aux.Origin
b.DeviceName = aux.DeviceName
b.ResourceName = aux.ResourceName
b.ProfileName = aux.ProfileName
b.ValueType = aux.ValueType
b.Units = aux.Units
b.Tags = aux.Tags
b.BinaryReading = aux.BinaryReading
if aux.Value != nil {
b.SimpleReading = SimpleReading{Value: fmt.Sprintf("%s", aux.Value)}
}
b.ObjectReading = aux.ObjectReading

if (aux.ValueType == common.ValueTypeObject || aux.ValueType == common.ValueTypeObjectArray) &&
aux.ObjectValue == nil {
b.isNull = true
} else if aux.ValueType == common.ValueTypeBinary && aux.BinaryValue == nil {
b.isNull = true
} else if aux.Value == nil {
b.isNull = true
}
return nil
}
Loading

0 comments on commit 77124f1

Please sign in to comment.