From 6211aafc10b1e2f5b342071239385128acb78ae1 Mon Sep 17 00:00:00 2001 From: Cloud Tsai Date: Wed, 14 Aug 2024 16:41:44 +0800 Subject: [PATCH] fix: EDX-5196 Allow source name in regexp The current auto event validation in core-metadata doesn't support regular expression, but it is supported by Device SDK. This change allow the regular expression is defined in auto event. Close https://github.com/edgexfoundry/edgex-go/issues/4865 Signed-off-by: Cloud Tsai --- internal/core/metadata/application/device.go | 13 +++++++++++-- internal/core/metadata/application/device_test.go | 9 ++++++++- .../core/metadata/controller/http/device_test.go | 13 +++++++------ 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/internal/core/metadata/application/device.go b/internal/core/metadata/application/device.go index b01183d620..8caf62d33b 100644 --- a/internal/core/metadata/application/device.go +++ b/internal/core/metadata/application/device.go @@ -19,6 +19,7 @@ import ( "context" goErrors "errors" "fmt" + "regexp" "slices" "time" @@ -288,7 +289,8 @@ func validateAutoEvent(dic *di.Container, d models.Device) errors.EdgeX { return nil } if d.ProfileName == "" { - return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("no associated device profile during validating device '%s' auto event", d.Name), nil) + // if the profile is not set, skip the validation until we have the profile + return nil } dbClient := container.DBClientFrom(dic.Get) dp, err := dbClient.DeviceProfileByName(d.ProfileName) @@ -300,9 +302,16 @@ func validateAutoEvent(dic *di.Container, d models.Device) errors.EdgeX { if err != nil { return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("auto event interval '%s' not valid in the device '%s'", a.Interval, d.Name), err) } + + regex, regErr := regexp.CompilePOSIX(a.SourceName) + if regErr != nil { + return errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to CompilePOSIX the auto event source name: "+a.SourceName, regErr) + } hasResource := slices.ContainsFunc(dp.DeviceResources, func(r models.DeviceResource) bool { - return r.Name == a.SourceName + matchedString := regex.FindString(r.Name) + return (r.Name == regex.String()) || (r.Name == matchedString) }) + hasCommand := slices.ContainsFunc(dp.DeviceCommands, func(c models.DeviceCommand) bool { return c.Name == a.SourceName }) diff --git a/internal/core/metadata/application/device_test.go b/internal/core/metadata/application/device_test.go index e5b4ca2a93..bed0995f0a 100644 --- a/internal/core/metadata/application/device_test.go +++ b/internal/core/metadata/application/device_test.go @@ -79,7 +79,14 @@ func TestValidateAutoEvents(t *testing.T) { models.Device{ AutoEvents: []models.AutoEvent{{SourceName: source1, Interval: "1s"}}, }, - true, + false, + }, + {"resource match regex", + models.Device{ + ProfileName: profile, + AutoEvents: []models.AutoEvent{{SourceName: "res.*", Interval: "1s"}}, + }, + false, }, } for _, testCase := range tests { diff --git a/internal/core/metadata/controller/http/device_test.go b/internal/core/metadata/controller/http/device_test.go index 165b9bd1ba..0df310cfa2 100644 --- a/internal/core/metadata/controller/http/device_test.go +++ b/internal/core/metadata/controller/http/device_test.go @@ -161,6 +161,8 @@ func TestAddDevice(t *testing.T) { noServiceName.Device.ServiceName = "" noProfileName := testDevice noProfileName.Device.ProfileName = "" + np := dtos.ToDeviceModel(noProfileName.Device) + dbClientMock.On("AddDevice", np).Return(np, nil) noProfileAndAutoEvents := noProfileName noProfileAndAutoEvents.Device.AutoEvents = []dtos.AutoEvent{} dm := dtos.ToDeviceModel(noProfileAndAutoEvents.Device) @@ -171,7 +173,6 @@ func TestAddDevice(t *testing.T) { emptyProtocols.Device.Protocols = map[string]dtos.ProtocolProperties{} emptyProtocolsModel := dtos.ToDeviceModel(emptyProtocols.Device) dbClientMock.On("AddDevice", emptyProtocolsModel).Return(emptyProtocolsModel, nil) - invalidProtocols := testDevice invalidProtocols.Device.Protocols = map[string]dtos.ProtocolProperties{"others": {}} @@ -200,7 +201,7 @@ func TestAddDevice(t *testing.T) { {"Invalid - invalid adminState", []requests.AddDeviceRequest{invalidAdminState}, http.StatusBadRequest, http.StatusBadRequest, false, false}, {"Invalid - invalid operatingState", []requests.AddDeviceRequest{invalidOperatingState}, http.StatusBadRequest, http.StatusBadRequest, false, false}, {"Invalid - no service name", []requests.AddDeviceRequest{noServiceName}, http.StatusBadRequest, http.StatusBadRequest, false, false}, - {"Invalid - no profile name", []requests.AddDeviceRequest{noProfileName}, http.StatusMultiStatus, http.StatusBadRequest, false, false}, + {"Valid - no profile name", []requests.AddDeviceRequest{noProfileName}, http.StatusMultiStatus, http.StatusCreated, true, true}, {"Invalid - no protocols", []requests.AddDeviceRequest{noProtocols}, http.StatusBadRequest, http.StatusBadRequest, false, false}, {"Valid - empty protocols", []requests.AddDeviceRequest{emptyProtocols}, http.StatusMultiStatus, http.StatusCreated, true, true}, {"Invalid - invalid protocols", []requests.AddDeviceRequest{invalidProtocols}, http.StatusMultiStatus, http.StatusInternalServerError, true, false}, @@ -554,9 +555,9 @@ func TestPatchDevice(t *testing.T) { emptyProfile := testReq emptyProfile.Device.ProfileName = &emptyString - //dm := dsModels - //dm.ProfileName = *emptyProfile.Device.ProfileName - //dbClientMock.On("UpdateDevice", dm).Return(nil) + dm := dsModels + dm.ProfileName = *emptyProfile.Device.ProfileName + dbClientMock.On("UpdateDevice", dm).Return(nil) invalidProtocols := testReq invalidProtocols.Device.Protocols = map[string]dtos.ProtocolProperties{"others": {}} @@ -623,7 +624,7 @@ func TestPatchDevice(t *testing.T) { {"Invalid - invalid protocols", []requests.UpdateDeviceRequest{invalidProtocols}, http.StatusMultiStatus, http.StatusInternalServerError, true, false}, {"Invalid - not found device service", []requests.UpdateDeviceRequest{notFoundService}, http.StatusMultiStatus, http.StatusBadRequest, false, false}, {"Invalid - device service unavailable", []requests.UpdateDeviceRequest{valid}, http.StatusMultiStatus, http.StatusServiceUnavailable, true, false}, - {"Invalid - empty profile", []requests.UpdateDeviceRequest{emptyProfile}, http.StatusMultiStatus, http.StatusBadRequest, false, false}} + {"Valid - empty profile", []requests.UpdateDeviceRequest{emptyProfile}, http.StatusMultiStatus, http.StatusOK, true, true}} for _, testCase := range tests { t.Run(testCase.name, func(t *testing.T) { e := echo.New()