Skip to content

Commit

Permalink
Merge pull request #4768 from weichou1229/issue-4767
Browse files Browse the repository at this point in the history
feat: Check the auto events before adding or updating the Device
  • Loading branch information
cloudxxx8 committed Mar 14, 2024
2 parents 0d425ac + b5baed0 commit bb4aed9
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 3 deletions.
39 changes: 37 additions & 2 deletions internal/core/metadata/application/device.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/********************************************************************************
* Copyright (C) 2020-2023 IOTech Ltd
* Copyright (C) 2020-2024 IOTech Ltd
* Copyright 2023 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
Expand All @@ -19,6 +19,7 @@ import (
"context"
goErrors "errors"
"fmt"
"slices"
"time"

bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v3/bootstrap/container"
Expand Down Expand Up @@ -52,7 +53,12 @@ func AddDevice(d models.Device, ctx context.Context, dic *di.Container) (id stri
return id, errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("device service '%s' does not exists", d.ServiceName), nil)
}

err := validateDeviceCallback(dtos.FromDeviceModelToDTO(d), dic)
err := validateAutoEvent(dic, d)
if err != nil {
return "", errors.NewCommonEdgeXWrapper(err)
}

err = validateDeviceCallback(dtos.FromDeviceModelToDTO(d), dic)
if err != nil {
return "", errors.NewCommonEdgeXWrapper(err)
}
Expand Down Expand Up @@ -161,6 +167,11 @@ func PatchDevice(dto dtos.UpdateDevice, ctx context.Context, dic *di.Container)

requests.ReplaceDeviceModelFieldsWithDTO(&device, dto)

err = validateAutoEvent(dic, device)
if err != nil {
return errors.NewCommonEdgeXWrapper(err)
}

deviceDTO := dtos.FromDeviceModelToDTO(device)
err = validateDeviceCallback(deviceDTO, dic)
if err != nil {
Expand Down Expand Up @@ -262,3 +273,27 @@ func DevicesByProfileName(offset int, limit int, profileName string, dic *di.Con
}

var noMessagingClientError = goErrors.New("MessageBus Client not available. Please update RequireMessageBus and MessageBus configuration to enable sending System Events via the EdgeX MessageBus")

func validateAutoEvent(dic *di.Container, d models.Device) errors.EdgeX {
dbClient := container.DBClientFrom(dic.Get)
dp, err := dbClient.DeviceProfileByName(d.ProfileName)
if err != nil {
return errors.NewCommonEdgeX(errors.Kind(err), fmt.Sprintf("device profile '%s' not found during validating device '%s' auto event", d.ProfileName, d.Name), err)
}
for _, a := range d.AutoEvents {
_, err := time.ParseDuration(a.Interval)
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)
}
hasResource := slices.ContainsFunc(dp.DeviceResources, func(r models.DeviceResource) bool {
return r.Name == a.SourceName
})
hasCommand := slices.ContainsFunc(dp.DeviceCommands, func(c models.DeviceCommand) bool {
return c.Name == a.SourceName
})
if !hasResource && !hasCommand {
return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("auto event source '%s' cannot be found in the device profile '%s'", a.SourceName, dp.Name), nil)
}
}
return nil
}
78 changes: 78 additions & 0 deletions internal/core/metadata/application/device_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//
// Copyright (C) 2024 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

package application

import (
"github.com/edgexfoundry/edgex-go/internal/core/metadata/container"
"testing"

"github.com/edgexfoundry/edgex-go/internal/core/metadata/infrastructure/interfaces/mocks"
"github.com/edgexfoundry/go-mod-bootstrap/v3/di"
"github.com/edgexfoundry/go-mod-core-contracts/v3/models"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestValidateAutoEvents(t *testing.T) {
source1 := "source1"
command1 := "command1"
deviceProfile := models.DeviceProfile{
Name: "test-profile",
DeviceResources: []models.DeviceResource{{Name: source1}, {Name: "resource2"}},
DeviceCommands: []models.DeviceCommand{{Name: command1}, {Name: "command2"}},
}

dic := di.NewContainer(di.ServiceConstructorMap{})
dbClientMock := &mocks.DBClient{}
dbClientMock.On("DeviceProfileByName", mock.Anything).Return(deviceProfile, nil)
dic.Update(di.ServiceConstructorMap{
container.DBClientInterfaceName: func(get di.Get) interface{} {
return dbClientMock
},
})

tests := []struct {
name string
device models.Device
errorExpected bool
}{
{"resource exist",
models.Device{
AutoEvents: []models.AutoEvent{{SourceName: source1, Interval: "1s"}},
},
false,
},
{"command exist",
models.Device{
AutoEvents: []models.AutoEvent{{SourceName: source1, Interval: "1s"}, {SourceName: command1, Interval: "1s"}},
},
false,
},
{"resource not exist",
models.Device{
AutoEvents: []models.AutoEvent{{SourceName: "notFoundSource", Interval: "1s"}},
},
true,
},
{"interval format not valid",
models.Device{
AutoEvents: []models.AutoEvent{{SourceName: source1, Interval: "1"}},
},
true,
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
err := validateAutoEvent(dic, testCase.device)
if testCase.errorExpected {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
4 changes: 3 additions & 1 deletion internal/core/metadata/controller/http/device_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (C) 2020-2023 IOTech Ltd
// Copyright (C) 2020-2024 IOTech Ltd
//
// SPDX-License-Identifier: Apache-2.0

Expand Down Expand Up @@ -131,6 +131,7 @@ func TestAddDevice(t *testing.T) {
valid := testDevice
dbClientMock.On("DeviceServiceNameExists", deviceModel.ServiceName).Return(true, nil)
dbClientMock.On("AddDevice", deviceModel).Return(deviceModel, nil)
dbClientMock.On("DeviceProfileByName", mock.Anything).Return(models.DeviceProfile{Name: "test-profile", DeviceResources: []models.DeviceResource{{Name: "TestResource"}}}, nil)

notFoundProfile := testDevice
notFoundProfile.Device.ProfileName = "notFoundProfile"
Expand Down Expand Up @@ -514,6 +515,7 @@ func TestPatchDevice(t *testing.T) {
dbClientMock.On("DeviceServiceNameExists", *valid.Device.ServiceName).Return(true, nil)
dbClientMock.On("DeviceById", *valid.Device.Id).Return(dsModels, nil)
dbClientMock.On("UpdateDevice", dsModels).Return(nil)
dbClientMock.On("DeviceProfileByName", mock.Anything).Return(models.DeviceProfile{Name: "test-profile", DeviceResources: []models.DeviceResource{{Name: "TestResource"}}}, nil)

validWithNoReqID := testReq
validWithNoReqID.RequestId = ""
Expand Down

0 comments on commit bb4aed9

Please sign in to comment.