From 9dc7c52aaae2bc179433c3e4ab4b5c7f213db5ce Mon Sep 17 00:00:00 2001 From: Akram Ahmad Date: Wed, 11 Mar 2020 11:37:53 -0500 Subject: [PATCH] Fix the Content Header-related issue with CBOR. Signed-off-by: Akram Ahmad --- internal/autoevent/executor.go | 5 +- internal/common/types.go | 3 +- internal/common/utils.go | 90 ++++++++++++++++ internal/controller/restfuncs.go | 5 +- internal/handler/command.go | 177 +++++++++++++++++++++++++------ internal/handler/command_test.go | 32 ++++-- pkg/models/event.go | 1 + 7 files changed, 270 insertions(+), 43 deletions(-) diff --git a/internal/autoevent/executor.go b/internal/autoevent/executor.go index 384582cbc..74093e103 100644 --- a/internal/autoevent/executor.go +++ b/internal/autoevent/executor.go @@ -11,11 +11,12 @@ import ( "sync" "time" - "github.com/OneOfOne/xxhash" "github.com/edgexfoundry/device-sdk-go/internal/common" "github.com/edgexfoundry/device-sdk-go/internal/handler" dsModels "github.com/edgexfoundry/device-sdk-go/pkg/models" contract "github.com/edgexfoundry/go-mod-core-contracts/models" + + "github.com/OneOfOne/xxhash" ) type Executor interface { @@ -75,7 +76,7 @@ func readResource(e *executor) (*dsModels.Event, common.AppError) { vars[common.NameVar] = e.deviceName vars[common.CommandVar] = e.autoEvent.Resource - evt, appErr := handler.CommandHandler(vars, "", common.GetCmdMethod, "") + evt, appErr := handler.CommandHandler(vars, "", nil) return evt, appErr } diff --git a/internal/common/types.go b/internal/common/types.go index 8be4225c1..76b5f1812 100644 --- a/internal/common/types.go +++ b/internal/common/types.go @@ -17,7 +17,8 @@ import ( // WritableInfo is a struct which contains configuration settings that can be changed in the Registry . type WritableInfo struct { // Level is the logging level of writing log message - LogLevel string + LogLevel string + ChecksumAlgo string } // ServiceInfo is a struct which contains service related configuration diff --git a/internal/common/utils.go b/internal/common/utils.go index 7cde8f57b..4fc1f6d66 100644 --- a/internal/common/utils.go +++ b/internal/common/utils.go @@ -10,7 +10,12 @@ package common import ( "bytes" "context" + "crypto/md5" + "encoding/json" "fmt" + "io" + "io/ioutil" + "net/http" "net/url" "reflect" "strings" @@ -21,6 +26,14 @@ import ( "github.com/edgexfoundry/go-mod-core-contracts/clients" contract "github.com/edgexfoundry/go-mod-core-contracts/models" "github.com/google/uuid" + + "github.com/OneOfOne/xxhash" + "github.com/ugorji/go/codec" +) + +const ( + ChecksumAlgoxxHash = "xxHash" + checksumContextKey = "payload-checksum" ) var ( @@ -286,3 +299,80 @@ func FilterQueryParams(queryParams string) url.Values { return m } + +// Read reads and converts the request's JSON event data into an Event struct +func (jsonReader) Read(reader io.Reader, ctx *context.Context) (dsModels.Event, error) { + c := context.WithValue(*ctx, clients.ContentType, clients.ContentTypeJSON) + *ctx = c + + event := dsModels.Event{} + err := json.NewDecoder(reader).Decode(&event) + if err != nil { + return event, err + } + + return event, nil +} + +// Read reads and converts the request's CBOR event data into an Event struct +func (cr cborReader) Read(reader io.Reader, ctx *context.Context) (dsModels.Event, error) { + c := context.WithValue(*ctx, clients.ContentType, clients.ContentTypeCBOR) + event := dsModels.Event{} + bytes, err := ioutil.ReadAll(reader) + if err != nil { + return event, err + } + + x := codec.CborHandle{} + err = codec.NewDecoderBytes(bytes, &x).Decode(&event) + if err != nil { + return event, err + } + + switch cr.configuration.Writable.ChecksumAlgo { + case ChecksumAlgoxxHash: + event.Checksum = fmt.Sprintf("%x", xxhash.Checksum64(bytes)) + default: + event.Checksum = fmt.Sprintf("%x", md5.Sum(bytes)) + } + c = context.WithValue(c, checksumContextKey, event.Checksum) + *ctx = c + event.EncodedEvent = bytes + + return event, nil +} + +// jsonReader handles unmarshaling of a JSON request body payload +type jsonReader struct{} + +// NewJsonReader creates a new instance of jsonReader. +func NewJsonReader() jsonReader { + return jsonReader{} +} + +// cborReader handles unmarshaling of a CBOR request body payload +type cborReader struct { + configuration *Config +} + +// NewCborReader creates a new instance of cborReader. +func NewCborReader(configuration *Config) cborReader { + return cborReader{configuration: configuration} +} + +// EventReader unmarshals a request body into an Event type +type EventReader interface { + Read(reader io.Reader, ctx *context.Context) (dsModels.Event, error) +} + +// NewRequestReader returns a BodyReader capable of processing the request body +func NewRequestReader(request *http.Request, configuration *Config) EventReader { + contentType := request.Header.Get(clients.ContentType) + + switch strings.ToLower(contentType) { + case clients.ContentTypeCBOR: + return NewCborReader(configuration) + default: + return NewJsonReader() + } +} diff --git a/internal/controller/restfuncs.go b/internal/controller/restfuncs.go index 7f8037ed7..bf55a6473 100644 --- a/internal/controller/restfuncs.go +++ b/internal/controller/restfuncs.go @@ -20,6 +20,7 @@ import ( "github.com/edgexfoundry/device-sdk-go/internal/handler/callback" "github.com/edgexfoundry/go-mod-core-contracts/clients" contract "github.com/edgexfoundry/go-mod-core-contracts/models" + "github.com/gorilla/mux" ) @@ -105,7 +106,7 @@ func commandFunc(w http.ResponseWriter, req *http.Request) { return } - event, appErr := handler.CommandHandler(vars, body, req.Method, req.URL.RawQuery) + event, appErr := handler.CommandHandler(vars, body, req) if appErr != nil { http.Error(w, fmt.Sprintf("%s %s", appErr.Message(), req.URL.Path), appErr.Code()) @@ -149,7 +150,7 @@ func commandAllFunc(w http.ResponseWriter, req *http.Request) { return } - events, appErr := handler.CommandAllHandler(vars[common.CommandVar], body, req.Method, req.URL.RawQuery) + events, appErr := handler.CommandAllHandler(vars[common.CommandVar], body, req) if appErr != nil { http.Error(w, appErr.Message(), appErr.Code()) } else if len(events) > 0 { diff --git a/internal/handler/command.go b/internal/handler/command.go index 85dd2cbf4..17cb8d478 100644 --- a/internal/handler/command.go +++ b/internal/handler/command.go @@ -10,6 +10,7 @@ package handler import ( "encoding/json" "fmt" + "net/http" "strconv" "strings" "sync" @@ -19,12 +20,25 @@ import ( "github.com/edgexfoundry/device-sdk-go/internal/common" "github.com/edgexfoundry/device-sdk-go/internal/transformer" dsModels "github.com/edgexfoundry/device-sdk-go/pkg/models" + + "github.com/edgexfoundry/go-mod-core-contracts/clients" contract "github.com/edgexfoundry/go-mod-core-contracts/models" + + "github.com/ugorji/go/codec" ) // Note, every HTTP request to ServeHTTP is made in a separate goroutine, which // means care needs to be taken with respect to shared data accessed through *Server. -func CommandHandler(vars map[string]string, body string, method string, queryParams string) (*dsModels.Event, common.AppError) { +func CommandHandler(vars map[string]string, body string, req *http.Request) (*dsModels.Event, common.AppError) { + + var method string + var queryParams string + + if req != nil { + method = req.Method + queryParams = req.URL.RawQuery + } + dKey := vars[common.IdVar] cmd := vars[common.CommandVar] @@ -76,7 +90,7 @@ func CommandHandler(vars map[string]string, body string, method string, queryPar if strings.ToLower(method) == common.GetCmdMethod { return execReadDeviceResource(&d, &dr, queryParams) } else { - appErr := execWriteDeviceResource(&d, &dr, body) + appErr := execWriteDeviceResource(&d, &dr, body, req) return nil, appErr } } @@ -84,12 +98,16 @@ func CommandHandler(vars map[string]string, body string, method string, queryPar if strings.ToLower(method) == common.GetCmdMethod { return execReadCmd(&d, cmd, queryParams) } else { - appErr := execWriteCmd(&d, cmd, body) + appErr := execWriteCmd(&d, cmd, body, req) return nil, appErr } } -func execReadDeviceResource(device *contract.Device, dr *contract.DeviceResource, queryParams string) (*dsModels.Event, common.AppError) { +func execReadDeviceResource( + device *contract.Device, + dr *contract.DeviceResource, + queryParams string) (*dsModels.Event, common.AppError) { + var reqs []dsModels.CommandRequest var req dsModels.CommandRequest common.LoggingClient.Debug(fmt.Sprintf("Handler - execReadCmd: deviceResource: %s", dr.Name)) @@ -108,7 +126,11 @@ func execReadDeviceResource(device *contract.Device, dr *contract.DeviceResource results, err := common.Driver.HandleReadCommands(device.Name, device.Protocols, reqs) if err != nil { - msg := fmt.Sprintf("Handler - execReadCmd: error for Device: %s DeviceResource: %s, %v", device.Name, dr.Name, err) + msg := fmt.Sprintf( + "Handler - execReadCmd: error for Device: %s DeviceResource: %s, %v", + device.Name, + dr.Name, + err) return nil, common.NewServerError(msg, err) } @@ -124,7 +146,11 @@ func cvsToEvent(device *contract.Device, cvs []*dsModels.CommandValue, cmd strin // get the device resource associated with the rsp.RO dr, ok := cache.Profiles().DeviceResource(device.Profile.Name, cv.DeviceResourceName) if !ok { - msg := fmt.Sprintf("Handler - execReadCmd: no deviceResource: %s for dev: %s in Command Result %v", cv.DeviceResourceName, device.Name, cv) + msg := fmt.Sprintf( + "Handler - execReadCmd: no deviceResource: %s for dev: %s in Command Result %v", + cv.DeviceResourceName, + device.Name, + cv) common.LoggingClient.Error(msg) return nil, common.NewServerError(msg, nil) } @@ -132,15 +158,27 @@ func cvsToEvent(device *contract.Device, cvs []*dsModels.CommandValue, cmd strin if common.CurrentConfig.Device.DataTransform { err = transformer.TransformReadResult(cv, dr.Properties.Value) if err != nil { - common.LoggingClient.Error(fmt.Sprintf("Handler - execReadCmd: CommandValue (%s) transformed failed: %v", cv.String(), err)) + common.LoggingClient.Error(fmt.Sprintf( + "Handler - execReadCmd: CommandValue (%s) transformed failed: %v", + cv.String(), + err)) transformsOK = false } } err = transformer.CheckAssertion(cv, dr.Properties.Value.Assertion, device) if err != nil { - common.LoggingClient.Error(fmt.Sprintf("Handler - execReadCmd: Assertion failed for device resource: %s, with value: %v", cv.String(), err)) - cv = dsModels.NewStringValue(cv.DeviceResourceName, cv.Origin, fmt.Sprintf("Assertion failed for device resource, with value: %s and assertion: %s", cv.String(), dr.Properties.Value.Assertion)) + common.LoggingClient.Error(fmt.Sprintf( + "Handler - execReadCmd: Assertion failed for device resource: %s, with value: %v", + cv.String(), + err)) + cv = dsModels.NewStringValue( + cv.DeviceResourceName, + cv.Origin, + fmt.Sprintf( + "Assertion failed for device resource, with value: %s and assertion: %s", + cv.String(), + dr.Properties.Value.Assertion)) } ro, err := cache.Profiles().ResourceOperation(device.Profile.Name, cv.DeviceResourceName, common.GetCmdMethod) @@ -151,7 +189,11 @@ func cvsToEvent(device *contract.Device, cvs []*dsModels.CommandValue, cmd strin if ok { cv = newCV } else { - common.LoggingClient.Warn(fmt.Sprintf("Handler - execReadCmd: Resource Operation (%s) mapping value (%s) failed with the mapping table: %v", ro.DeviceCommand, cv.String(), ro.Mappings)) + common.LoggingClient.Warn(fmt.Sprintf( + "Handler - execReadCmd: Resource Operation (%s) mapping value (%s) failed with the mapping table: %v", + ro.DeviceCommand, + cv.String(), + ro.Mappings)) //transformsOK = false // issue #89 will discuss how to handle there is no mapping matched } } @@ -166,7 +208,11 @@ func cvsToEvent(device *contract.Device, cvs []*dsModels.CommandValue, cmd strin reading := common.CommandValueToReading(cv, device.Name, dr.Properties.Value.FloatEncoding) readings = append(readings, *reading) - common.LoggingClient.Debug(fmt.Sprintf("Handler - execReadCmd: device: %s DeviceResource: %v reading: %v", device.Name, cv.DeviceResourceName, reading)) + common.LoggingClient.Debug(fmt.Sprintf( + "Handler - execReadCmd: device: %s DeviceResource: %v reading: %v", + device.Name, + cv.DeviceResourceName, + reading)) } if !transformsOK { @@ -216,7 +262,11 @@ func execReadCmd(device *contract.Device, cmd string, queryParams string) (*dsMo dr, ok := cache.Profiles().DeviceResource(device.Profile.Name, drName) common.LoggingClient.Debug(fmt.Sprintf("Handler - execReadCmd: deviceResource: %v", dr)) if !ok { - msg := fmt.Sprintf("Handler - execReadCmd: no deviceResource: %s for dev: %s cmd: %s method: GET", drName, device.Name, cmd) + msg := fmt.Sprintf( + "Handler - execReadCmd: no deviceResource: %s for dev: %s cmd: %s method: GET", + drName, + device.Name, + cmd) common.LoggingClient.Error(msg) return nil, common.NewServerError(msg, nil) } @@ -242,8 +292,13 @@ func execReadCmd(device *contract.Device, cmd string, queryParams string) (*dsMo return cvsToEvent(device, results, cmd) } -func execWriteDeviceResource(device *contract.Device, dr *contract.DeviceResource, params string) common.AppError { - paramMap, err := parseParams(params) +func execWriteDeviceResource( + device *contract.Device, + dr *contract.DeviceResource, + params string, + r *http.Request) common.AppError { + + paramMap, err := parseParams(params, r) if err != nil { msg := fmt.Sprintf("Handler - execWriteDeviceResource: Put parameters parsing failed: %s", params) common.LoggingClient.Error(msg) @@ -275,7 +330,10 @@ func execWriteDeviceResource(device *contract.Device, dr *contract.DeviceResourc if common.CurrentConfig.Device.DataTransform { err = transformer.TransformWriteParameter(cv, dr.Properties.Value) if err != nil { - msg := fmt.Sprintf("Handler - execWriteDeviceResource: CommandValue (%s) transformed failed: %v", cv.String(), err) + msg := fmt.Sprintf( + "Handler - execWriteDeviceResource: CommandValue (%s) transformed failed: %v", + cv.String(), + err) common.LoggingClient.Error(msg) return common.NewServerError(msg, err) } @@ -283,17 +341,25 @@ func execWriteDeviceResource(device *contract.Device, dr *contract.DeviceResourc err = common.Driver.HandleWriteCommands(device.Name, device.Protocols, reqs, []*dsModels.CommandValue{cv}) if err != nil { - msg := fmt.Sprintf("Handler - execWriteDeviceResource: error for Device: %s Device Resource: %s, %v", device.Name, dr.Name, err) + msg := fmt.Sprintf( + "Handler - execWriteDeviceResource: error for Device: %s Device Resource: %s, %v", + device.Name, + dr.Name, + err) return common.NewServerError(msg, err) } return nil } -func execWriteCmd(device *contract.Device, cmd string, params string) common.AppError { +func execWriteCmd(device *contract.Device, cmd string, params string, r *http.Request) common.AppError { ros, err := cache.Profiles().ResourceOperations(device.Profile.Name, cmd, common.SetCmdMethod) if err != nil { - msg := fmt.Sprintf("Handler - execWriteCmd: can't find ResrouceOperations in Profile(%s) and Command(%s), %v", device.Profile.Name, cmd, err) + msg := fmt.Sprintf( + "Handler - execWriteCmd: can't find ResrouceOperations in Profile(%s) and Command(%s), %v", + device.Profile.Name, + cmd, + err) common.LoggingClient.Error(msg) return common.NewBadRequestError(msg, err) } @@ -305,7 +371,7 @@ func execWriteCmd(device *contract.Device, cmd string, params string) common.App return common.NewServerError(msg, nil) } - cvs, err := parseWriteParams(device.Profile.Name, ros, params) + cvs, err := parseWriteParams(device.Profile.Name, ros, params, r) if err != nil { msg := fmt.Sprintf("Handler - execWriteCmd: Put parameters parsing failed: %s", params) common.LoggingClient.Error(msg) @@ -324,7 +390,11 @@ func execWriteCmd(device *contract.Device, cmd string, params string) common.App dr, ok := cache.Profiles().DeviceResource(device.Profile.Name, drName) common.LoggingClient.Debug(fmt.Sprintf("Handler - execWriteCmd: putting deviceResource: %s", drName)) if !ok { - msg := fmt.Sprintf("Handler - execWriteCmd: no deviceResource: %s for dev: %s cmd: %s method: GET", drName, device.Name, cmd) + msg := fmt.Sprintf( + "Handler - execWriteCmd: no deviceResource: %s for dev: %s cmd: %s method: GET", + drName, + device.Name, + cmd) common.LoggingClient.Error(msg) return common.NewServerError(msg, nil) } @@ -352,8 +422,12 @@ func execWriteCmd(device *contract.Device, cmd string, params string) common.App return nil } -func parseWriteParams(profileName string, ros []contract.ResourceOperation, params string) ([]*dsModels.CommandValue, error) { - paramMap, err := parseParams(params) +func parseWriteParams(profileName string, + ros []contract.ResourceOperation, + params string, + r *http.Request) ([]*dsModels.CommandValue, error) { + + paramMap, err := parseParams(params, r) if err != nil { return []*dsModels.CommandValue{}, err } @@ -365,7 +439,9 @@ func parseWriteParams(profileName string, ros []contract.ResourceOperation, para if !ok { dr, ok := cache.Profiles().DeviceResource(profileName, ro.DeviceResource) if !ok { - err := fmt.Errorf("the parameter %s does not match any DeviceResource in DeviceProfile", ro.DeviceResource) + err := fmt.Errorf( + "the parameter %s does not match any DeviceResource in DeviceProfile", + ro.DeviceResource) return []*dsModels.CommandValue{}, err } @@ -376,7 +452,9 @@ func parseWriteParams(profileName string, ros []contract.ResourceOperation, para common.LoggingClient.Debug(fmt.Sprintf("there is no %s in the request parameters, retrieving value from the DefaultValue field from the ValueProperty", ro.DeviceResource)) p = dr.Properties.Value.DefaultValue } else { - err := fmt.Errorf("the parameter %s is not defined in the request body and there is no default value", ro.DeviceResource) + err := fmt.Errorf( + "the parameter %s is not defined in the request body and there is no default value", + ro.DeviceResource) return []*dsModels.CommandValue{}, err } } @@ -386,7 +464,11 @@ func parseWriteParams(profileName string, ros []contract.ResourceOperation, para if ok { p = newP } else { - msg := fmt.Sprintf("parseWriteParams: Resource (%s) mapping value (%s) failed with the mapping table: %v", ro.DeviceResource, p, ro.Mappings) + msg := fmt.Sprintf( + "parseWriteParams: Resource (%s) mapping value (%s) failed with the mapping table: %v", + ro.DeviceResource, + p, + ro.Mappings) common.LoggingClient.Warn(msg) //return result, fmt.Errorf(msg) // issue #89 will discuss how to handle there is no mapping matched } @@ -403,8 +485,19 @@ func parseWriteParams(profileName string, ros []contract.ResourceOperation, para return result, nil } -func parseParams(params string) (paramMap map[string]string, err error) { - err = json.Unmarshal([]byte(params), ¶mMap) +func parseParams(params string, r *http.Request) (paramMap map[string]string, err error) { + + // Check the header value + switch r.Header.Get(clients.ContentType) { + case clients.ContentTypeCBOR: + err = codec.NewDecoderBytes([]byte(params), &codec.CborHandle{}).Decode(¶mMap) + case clients.ContentTypeJSON: + err = json.Unmarshal([]byte(params), ¶mMap) + default: + common.LoggingClient.Error(fmt.Sprintf("header value was neither JSON nor CBOR, instead was: %s", r.Header.Get(clients.ContentType))) + + } + if err != nil { common.LoggingClient.Error(fmt.Sprintf("parsing Write parameters failed %s, %v", params, err)) return @@ -417,7 +510,11 @@ func parseParams(params string) (paramMap map[string]string, err error) { return } -func createCommandValueFromRO(profileName string, ro *contract.ResourceOperation, v string) (*dsModels.CommandValue, error) { +func createCommandValueFromRO( + profileName string, + ro *contract.ResourceOperation, + v string) (*dsModels.CommandValue, error) { + dr, ok := cache.Profiles().DeviceResource(profileName, ro.DeviceResource) if !ok { msg := fmt.Sprintf("createCommandValueForParam: no deviceResource: %s", ro.DeviceResource) @@ -489,7 +586,11 @@ func createCommandValueFromDR(dr *contract.DeviceResource, v string) (*dsModels. } if err != nil { - common.LoggingClient.Error(fmt.Sprintf("Handler - Command: Parsing parameter value (%s) to %s failed: %v", v, dr.Properties.Value.Type, err)) + common.LoggingClient.Error(fmt.Sprintf( + "Handler - Command: Parsing parameter value (%s) to %s failed: %v", + v, + dr.Properties.Value.Type, + err)) return result, err } @@ -498,8 +599,20 @@ func createCommandValueFromDR(dr *contract.DeviceResource, v string) (*dsModels. return result, err } -func CommandAllHandler(cmd string, body string, method string, queryParams string) ([]*dsModels.Event, common.AppError) { - common.LoggingClient.Debug(fmt.Sprintf("Handler - CommandAll: execute the %s command %s from all operational devices", method, cmd)) +func CommandAllHandler(cmd string, body string, req *http.Request) ([]*dsModels.Event, common.AppError) { + + var method string + var queryParams string + + if req != nil { + method = req.Method + queryParams = req.URL.RawQuery + } + + common.LoggingClient.Debug(fmt.Sprintf( + "Handler - CommandAll: execute the %s command %s from all operational devices", + method, + cmd)) devices := filterOperationalDevices(cache.Devices().All()) devCount := len(devices) @@ -518,7 +631,7 @@ func CommandAllHandler(cmd string, body string, method string, queryParams strin if strings.ToLower(method) == common.GetCmdMethod { event, appErr = execReadCmd(device, cmd, queryParams) } else { - appErr = execWriteCmd(device, cmd, body) + appErr = execWriteCmd(device, cmd, body, req) } cmdResults <- struct { event *dsModels.Event diff --git a/internal/handler/command_test.go b/internal/handler/command_test.go index 3687df1ee..42e890e87 100644 --- a/internal/handler/command_test.go +++ b/internal/handler/command_test.go @@ -8,6 +8,8 @@ package handler import ( "context" + "net/http" + "net/http/httptest" "strconv" "strings" "testing" @@ -16,8 +18,11 @@ import ( "github.com/edgexfoundry/device-sdk-go/internal/common" "github.com/edgexfoundry/device-sdk-go/internal/mock" dsModels "github.com/edgexfoundry/device-sdk-go/pkg/models" + + "github.com/edgexfoundry/go-mod-core-contracts/clients" "github.com/edgexfoundry/go-mod-core-contracts/clients/logger" contract "github.com/edgexfoundry/go-mod-core-contracts/models" + "github.com/google/uuid" "github.com/stretchr/testify/assert" ) @@ -82,8 +87,9 @@ func TestParseWriteParamsWrongParamName(t *testing.T) { profileName := "notFound" ro := []contract.ResourceOperation{{Index: ""}} params := "{ \"key\": \"value\" }" + r := newRequestWithContentType(clients.ContentTypeJSON) - _, err := parseWriteParams(profileName, ro, params) + _, err := parseWriteParams(profileName, ro, params, r) if err == nil { t.Error("expected error") @@ -95,7 +101,8 @@ func TestParseWriteParamsNoParams(t *testing.T) { ro := []contract.ResourceOperation{{Index: ""}} params := "{ }" - _, err := parseWriteParams(profileName, ro, params) + r := newRequestWithContentType(clients.ContentTypeJSON) + _, err := parseWriteParams(profileName, ro, params, r) if err == nil { t.Error("expected error") @@ -236,6 +243,7 @@ func TestParseWriteParams(t *testing.T) { rosTestDefaultValue, _ := cache.Profiles().ResourceOperations(profileName, "RandomValue_Int32", common.SetCmdMethod) rosTestMappingPass, _ := cache.Profiles().ResourceOperations(profileName, "ResourceTestMapping_Pass", common.SetCmdMethod) rosTestMappingFail, _ := cache.Profiles().ResourceOperations(profileName, "ResourceTestMapping_Fail", common.SetCmdMethod) + r := newRequestWithContentType(clients.ContentTypeJSON) tests := []struct { testName string @@ -257,7 +265,7 @@ func TestParseWriteParams(t *testing.T) { } for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { - _, err := parseWriteParams(tt.profile, tt.resourceOps, tt.params) + _, err := parseWriteParams(tt.profile, tt.resourceOps, tt.params, r) if !tt.expectErr && err != nil { t.Errorf("unexpected parse error params:%s %s", tt.params, err.Error()) return @@ -334,6 +342,9 @@ func TestExecWriteCmd(t *testing.T) { paramsTransformFail = `{"ResourceTestTransform_Fail":"123"}` paramsNoDeviceResourceForResult = `{"error":""}` ) + + r := newRequestWithContentType(clients.ContentTypeJSON) + tests := []struct { testName string device *contract.Device @@ -357,7 +368,7 @@ func TestExecWriteCmd(t *testing.T) { common.CurrentConfig.Device.MaxCmdOps = 128 }() } - appErr := execWriteCmd(tt.device, tt.cmd, tt.params) + appErr := execWriteCmd(tt.device, tt.cmd, tt.params, r) if !tt.expectErr && appErr != nil { t.Errorf("%s expectErr:%v error:%v", tt.testName, tt.expectErr, appErr.Error()) return @@ -385,9 +396,10 @@ func TestCommandAllHandler(t *testing.T) { {"PartOfWriteCommandExecutionSuccess", "RandomValue_Uint8", `{"RandomValue_Uint8":"123"}`, "", methodSet, false}, {"PartOfWriteCommandExecutionFail", "error", `{"RandomValue_Uint8":"123"}`, "", methodSet, true}, } + r := newRequestWithContentType(clients.ContentTypeJSON) for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { - _, appErr := CommandAllHandler(tt.cmd, tt.body, tt.method, tt.queryParams) + _, appErr := CommandAllHandler(tt.cmd, tt.body, r) if !tt.expectErr && appErr != nil { t.Errorf("%s expectErr:%v error:%v", tt.testName, tt.expectErr, appErr.Error()) return @@ -420,6 +432,8 @@ func TestCommandHandler(t *testing.T) { t.Errorf("Fail to update device, error: %v", err) } + r := newRequestWithContentType(clients.ContentTypeJSON) + tests := []struct { testName string vars map[string]string @@ -440,7 +454,7 @@ func TestCommandHandler(t *testing.T) { } for _, tt := range tests { t.Run(tt.testName, func(t *testing.T) { - _, appErr := CommandHandler(tt.vars, tt.body, tt.method, tt.queryParams) + _, appErr := CommandHandler(tt.vars, tt.body, r) if !tt.expectErr && appErr != nil { t.Errorf("%s expectErr:%v error:%v", tt.testName, tt.expectErr, appErr.Error()) return @@ -452,3 +466,9 @@ func TestCommandHandler(t *testing.T) { }) } } + +func newRequestWithContentType(contentType string) *http.Request { + req := httptest.NewRequest(http.MethodGet, "/", strings.NewReader("Test body")) + req.Header.Set(clients.ContentType, contentType) + return req +} diff --git a/pkg/models/event.go b/pkg/models/event.go index 8742cc61a..186f69f60 100644 --- a/pkg/models/event.go +++ b/pkg/models/event.go @@ -14,6 +14,7 @@ import ( type Event struct { contract.Event EncodedEvent []byte + Checksum string } // HasBinaryValue confirms whether an event contains one or more