diff --git a/v2/constants.go b/v2/constants.go index 7c0817ab..1699108e 100644 --- a/v2/constants.go +++ b/v2/constants.go @@ -197,3 +197,10 @@ const ( REST = "REST" MQTT = "MQTT" ) + +// Constants for DeviceProfile +const ( + ReadWrite_R = "R" + ReadWrite_W = "W" + ReadWrite_RW = "RW" +) diff --git a/v2/dtos/command.go b/v2/dtos/command.go deleted file mode 100644 index f98e7605..00000000 --- a/v2/dtos/command.go +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (C) 2020-2021 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package dtos - -import "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models" - -// Command and its properties are defined in the APIv2 specification: -// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.x#/Command -type Command struct { - Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"` - Get bool `json:"get,omitempty" yaml:"get,omitempty" validate:"required_without=Set"` - Set bool `json:"set,omitempty" yaml:"set,omitempty" validate:"required_without=Get"` -} - -// ToCommandModel transforms the Command DTO to the Command model -func ToCommandModel(c Command) models.Command { - return models.Command{ - Name: c.Name, - Get: c.Get, - Set: c.Set, - } -} - -// ToCommandModels transforms the Command DTOs to the Command models -func ToCommandModels(commandDTOs []Command) []models.Command { - commandModels := make([]models.Command, len(commandDTOs)) - for i, c := range commandDTOs { - commandModels[i] = ToCommandModel(c) - } - return commandModels -} - -// FromCommandModelToDTO transforms the Command model to the Command DTO -func FromCommandModelToDTO(c models.Command) Command { - return Command{ - Name: c.Name, - Get: c.Get, - Set: c.Set, - } -} - -// FromCommandModelsToDTOs transforms the Command models to the Command DTOs -func FromCommandModelsToDTOs(commandModels []models.Command) []Command { - commandDTOs := make([]Command, len(commandModels)) - for i, c := range commandModels { - commandDTOs[i] = FromCommandModelToDTO(c) - } - return commandDTOs -} diff --git a/v2/dtos/deviceCommand.go b/v2/dtos/devicecommand.go similarity index 52% rename from v2/dtos/deviceCommand.go rename to v2/dtos/devicecommand.go index 639674ba..589c325b 100644 --- a/v2/dtos/deviceCommand.go +++ b/v2/dtos/devicecommand.go @@ -10,26 +10,23 @@ import "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models" // DeviceCommand and its properties are defined in the APIv2 specification: // https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.x#/DeviceCommand type DeviceCommand struct { - Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"` - Get []ResourceOperation `json:"get,omitempty" yaml:"get,omitempty" validate:"required_without=Set"` - Set []ResourceOperation `json:"set,omitempty" yaml:"set,omitempty" validate:"required_without=Get"` + Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"` + IsHidden bool `json:"isHidden,omitempty" yaml:"isHidden,omitempty"` + ReadWrite string `json:"readWrite" yaml:"readWrite" validate:"required,oneof='R' 'W' 'RW'"` + ResourceOperations []ResourceOperation `json:"resourceOperations" yaml:"resourceOperations" validate:"gt=0,dive"` } // ToDeviceCommandModel transforms the DeviceCommand DTO to the DeviceCommand model -func ToDeviceCommandModel(p DeviceCommand) models.DeviceCommand { - getResourceOperations := make([]models.ResourceOperation, len(p.Get)) - for i, ro := range p.Get { - getResourceOperations[i] = ToResourceOperationModel(ro) +func ToDeviceCommandModel(dto DeviceCommand) models.DeviceCommand { + resourceOperations := make([]models.ResourceOperation, len(dto.ResourceOperations)) + for i, ro := range dto.ResourceOperations { + resourceOperations[i] = ToResourceOperationModel(ro) } - setResourceOperations := make([]models.ResourceOperation, len(p.Set)) - for i, ro := range p.Set { - setResourceOperations[i] = ToResourceOperationModel(ro) - } - return models.DeviceCommand{ - Name: p.Name, - Get: getResourceOperations, - Set: setResourceOperations, + Name: dto.Name, + IsHidden: dto.IsHidden, + ReadWrite: dto.ReadWrite, + ResourceOperations: resourceOperations, } } @@ -43,20 +40,16 @@ func ToDeviceCommandModels(deviceCommandDTOs []DeviceCommand) []models.DeviceCom } // FromDeviceCommandModelToDTO transforms the DeviceCommand model to the DeviceCommand DTO -func FromDeviceCommandModelToDTO(p models.DeviceCommand) DeviceCommand { - getResourceOperations := make([]ResourceOperation, len(p.Get)) - for i, ro := range p.Get { - getResourceOperations[i] = FromResourceOperationModelToDTO(ro) +func FromDeviceCommandModelToDTO(d models.DeviceCommand) DeviceCommand { + resourceOperations := make([]ResourceOperation, len(d.ResourceOperations)) + for i, ro := range d.ResourceOperations { + resourceOperations[i] = FromResourceOperationModelToDTO(ro) } - setResourceOperations := make([]ResourceOperation, len(p.Set)) - for i, ro := range p.Set { - setResourceOperations[i] = FromResourceOperationModelToDTO(ro) - } - return DeviceCommand{ - Name: p.Name, - Get: getResourceOperations, - Set: setResourceOperations, + Name: d.Name, + IsHidden: d.IsHidden, + ReadWrite: d.ReadWrite, + ResourceOperations: resourceOperations, } } diff --git a/v2/dtos/deviceprofile.go b/v2/dtos/deviceprofile.go index 80916f76..036b6d46 100644 --- a/v2/dtos/deviceprofile.go +++ b/v2/dtos/deviceprofile.go @@ -26,7 +26,6 @@ type DeviceProfile struct { Labels []string `json:"labels,omitempty" yaml:"labels,flow,omitempty"` DeviceResources []DeviceResource `json:"deviceResources" yaml:"deviceResources" validate:"required,gt=0,dive"` DeviceCommands []DeviceCommand `json:"deviceCommands,omitempty" yaml:"deviceCommands,omitempty" validate:"dive"` - CoreCommands []Command `json:"coreCommands,omitempty" yaml:"coreCommands,omitempty" validate:"dive"` } // Validate satisfies the Validator interface @@ -50,7 +49,6 @@ func (dp *DeviceProfile) UnmarshalYAML(unmarshal func(interface{}) error) error Labels []string `yaml:"labels"` DeviceResources []DeviceResource `yaml:"deviceResources"` DeviceCommands []DeviceCommand `yaml:"deviceCommands"` - CoreCommands []Command `yaml:"coreCommands"` } if err := unmarshal(&alias); err != nil { return errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to unmarshal request body as YAML.", err) @@ -83,7 +81,6 @@ func ToDeviceProfileModel(deviceProfileDTO DeviceProfile) models.DeviceProfile { Labels: deviceProfileDTO.Labels, DeviceResources: ToDeviceResourceModels(deviceProfileDTO.DeviceResources), DeviceCommands: ToDeviceCommandModels(deviceProfileDTO.DeviceCommands), - CoreCommands: ToCommandModels(deviceProfileDTO.CoreCommands), } } @@ -99,7 +96,6 @@ func FromDeviceProfileModelToDTO(deviceProfile models.DeviceProfile) DeviceProfi Labels: deviceProfile.Labels, DeviceResources: FromDeviceResourceModelsToDTOs(deviceProfile.DeviceResources), DeviceCommands: FromDeviceCommandModelsToDTOs(deviceProfile.DeviceCommands), - CoreCommands: FromCommandModelsToDTOs(deviceProfile.CoreCommands), } } @@ -122,35 +118,18 @@ func ValidateDeviceProfileDTO(profile DeviceProfile) error { } dupCheck[command.Name] = true - // deviceResources referenced in deviceCommands must exist - getCommands := command.Get - for _, getCommand := range getCommands { - if !deviceResourcesContains(profile.DeviceResources, getCommand.DeviceResource) { - return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("device command's Get resource %s doesn't match any deivce resource", getCommand.DeviceResource), nil) + resourceOperations := command.ResourceOperations + for _, ro := range resourceOperations { + // ResourceOperations referenced in deviceCommands must exist + if !deviceResourcesContains(profile.DeviceResources, ro.DeviceResource) { + return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("device command's resource %s doesn't match any deivce resource", ro.DeviceResource), nil) } - } - setCommands := command.Set - for _, setCommand := range setCommands { - if !deviceResourcesContains(profile.DeviceResources, setCommand.DeviceResource) { - return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("device command's Set resource %s doesn't match any deivce resource", setCommand.DeviceResource), nil) + // Check the ReadWrite whether is align to the deviceResource + if !validReadWritePermission(profile.DeviceResources, ro.DeviceResource, command.ReadWrite) { + return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("device command's ReadWrite permission '%s' doesn't align the deivce resource", command.ReadWrite), nil) } } } - // coreCommands validation - dupCheck = make(map[string]bool) - for _, command := range profile.CoreCommands { - // coreCommand name should not duplicated - if dupCheck[command.Name] { - return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("core command %s is duplicated", command.Name), nil) - } - dupCheck[command.Name] = true - - // coreCommands name should match the one of deviceResources and deviceCommands - if !deviceCommandsContains(profile.DeviceCommands, command.Name) && - !deviceResourcesContains(profile.DeviceResources, command.Name) { - return errors.NewCommonEdgeX(errors.KindContractInvalid, fmt.Sprintf("core command %s doesn't match any deivce command or resource", command.Name), nil) - } - } return nil } @@ -165,13 +144,16 @@ func deviceResourcesContains(resources []DeviceResource, name string) bool { return contains } -func deviceCommandsContains(resources []DeviceCommand, name string) bool { - contains := false +func validReadWritePermission(resources []DeviceResource, name string, readWrite string) bool { + valid := true for _, resource := range resources { if resource.Name == name { - contains = true - break + if resource.Properties.ReadWrite != v2.ReadWrite_RW && + resource.Properties.ReadWrite != readWrite { + valid = false + break + } } } - return contains + return valid } diff --git a/v2/dtos/deviceprofile_test.go b/v2/dtos/deviceprofile_test.go index 40f0a32c..ade8bdc7 100644 --- a/v2/dtos/deviceprofile_test.go +++ b/v2/dtos/deviceprofile_test.go @@ -35,23 +35,16 @@ var testDeviceProfile = models.DeviceProfile{ Attributes: testAttributes, Properties: models.PropertyValue{ ValueType: v2.ValueTypeInt16, - ReadWrite: "RW", + ReadWrite: v2.ReadWrite_RW, }, }}, DeviceCommands: []models.DeviceCommand{{ - Name: TestDeviceCommandName, - Get: []models.ResourceOperation{{ - DeviceResource: TestDeviceResourceName, - }}, - Set: []models.ResourceOperation{{ + Name: TestDeviceCommandName, + ReadWrite: v2.ReadWrite_RW, + ResourceOperations: []models.ResourceOperation{{ DeviceResource: TestDeviceResourceName, }}, }}, - CoreCommands: []models.Command{{ - Name: TestDeviceCommandName, - Get: true, - Set: true, - }}, } func profileData() DeviceProfile { @@ -69,22 +62,15 @@ func profileData() DeviceProfile { Attributes: testAttributes, Properties: PropertyValue{ ValueType: v2.ValueTypeInt16, - ReadWrite: "RW", + ReadWrite: v2.ReadWrite_RW, }, }}, DeviceCommands: []DeviceCommand{{ - Name: TestDeviceCommandName, - Get: []ResourceOperation{{ + Name: TestDeviceCommandName, + ReadWrite: v2.ReadWrite_RW, + ResourceOperations: []ResourceOperation{{ DeviceResource: TestDeviceResourceName, }}, - Set: []ResourceOperation{{ - DeviceResource: TestDeviceResourceName, - }}, - }}, - CoreCommands: []Command{{ - Name: TestDeviceCommandName, - Get: true, - Set: true, }}, } } @@ -102,18 +88,11 @@ func TestValidateDeviceProfileDTO(t *testing.T) { duplicatedDeviceCommand := profileData() duplicatedDeviceCommand.DeviceCommands = append( duplicatedDeviceCommand.DeviceCommands, DeviceCommand{Name: TestDeviceCommandName}) - duplicatedCoreCommand := profileData() - duplicatedCoreCommand.CoreCommands = append( - duplicatedCoreCommand.CoreCommands, Command{Name: TestDeviceCommandName}) - mismatchedCoreCommand := profileData() - mismatchedCoreCommand.CoreCommands = append( - mismatchedCoreCommand.CoreCommands, Command{Name: "missMatchedCoreCommand"}) - mismatchedGetResource := profileData() - mismatchedGetResource.DeviceCommands[0].Get = append( - mismatchedGetResource.DeviceCommands[0].Get, ResourceOperation{DeviceResource: "missMatchedResource"}) - mismatchedSetResource := profileData() - mismatchedSetResource.DeviceCommands[0].Set = append( - mismatchedSetResource.DeviceCommands[0].Set, ResourceOperation{DeviceResource: "missMatchedResource"}) + mismatchedResource := profileData() + mismatchedResource.DeviceCommands[0].ResourceOperations = append( + mismatchedResource.DeviceCommands[0].ResourceOperations, ResourceOperation{DeviceResource: "missMatchedResource"}) + invalidReadWrite := profileData() + invalidReadWrite.DeviceResources[0].Properties.ReadWrite = v2.ReadWrite_R tests := []struct { name string @@ -123,10 +102,8 @@ func TestValidateDeviceProfileDTO(t *testing.T) { {"valid device profile", valid, false}, {"duplicated device resource", duplicatedDeviceResource, true}, {"duplicated device command", duplicatedDeviceCommand, true}, - {"duplicated core command", duplicatedCoreCommand, true}, - {"mismatched core command", mismatchedCoreCommand, true}, - {"mismatched Get resource", mismatchedGetResource, true}, - {"mismatched Set resource", mismatchedSetResource, true}, + {"mismatched resource", mismatchedResource, true}, + {"invalid ReadWrite permission", invalidReadWrite, true}, } for _, tt := range tests { @@ -153,7 +130,8 @@ deviceResources: deviceCommands: - name: "GenerateDeviceValue_Boolean_RW" - get: + readWrite: "RW" + resourceOperations: - { deviceResource: "DeviceValue_Boolean_RW" } ` inValid := ` @@ -166,6 +144,7 @@ deviceResources: deviceCommands: - name: "GenerateDeviceValue_Boolean_RW" + readWrite: "RW", get: - { deviceResource: "DeviceValue_Boolean_RW" } ` diff --git a/v2/dtos/deviceresource.go b/v2/dtos/deviceresource.go index c6df833e..0527f75c 100644 --- a/v2/dtos/deviceresource.go +++ b/v2/dtos/deviceresource.go @@ -12,6 +12,7 @@ import "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models" type DeviceResource struct { Description string `json:"description,omitempty" yaml:"description,omitempty"` Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string,edgex-dto-rfc3986-unreserved-chars"` + IsHidden bool `json:"isHidden,omitempty" yaml:"isHidden,omitempty"` Tag string `json:"tag,omitempty" yaml:"tag,omitempty"` Properties PropertyValue `json:"properties,omitempty" yaml:"properties,omitempty"` Attributes map[string]string `json:"attributes,omitempty" yaml:"attributes,omitempty"` @@ -22,6 +23,7 @@ func ToDeviceResourceModel(d DeviceResource) models.DeviceResource { return models.DeviceResource{ Description: d.Description, Name: d.Name, + IsHidden: d.IsHidden, Tag: d.Tag, Properties: ToPropertyValueModel(d.Properties), Attributes: d.Attributes, @@ -42,6 +44,7 @@ func FromDeviceResourceModelToDTO(d models.DeviceResource) DeviceResource { return DeviceResource{ Description: d.Description, Name: d.Name, + IsHidden: d.IsHidden, Tag: d.Tag, Properties: FromPropertyValueModelToDTO(d.Properties), Attributes: d.Attributes, diff --git a/v2/dtos/requests/deviceprofile_test.go b/v2/dtos/requests/deviceprofile_test.go index 65e2be78..2f07c30a 100644 --- a/v2/dtos/requests/deviceprofile_test.go +++ b/v2/dtos/requests/deviceprofile_test.go @@ -32,22 +32,15 @@ func profileData() DeviceProfileRequest { Attributes: testAttributes, Properties: dtos.PropertyValue{ ValueType: v2.ValueTypeInt16, - ReadWrite: "RW", + ReadWrite: v2.ReadWrite_RW, }, }} var testDeviceCommands = []dtos.DeviceCommand{{ - Name: TestDeviceCommandName, - Get: []dtos.ResourceOperation{{ + Name: TestDeviceCommandName, + ReadWrite: v2.ReadWrite_RW, + ResourceOperations: []dtos.ResourceOperation{{ DeviceResource: TestDeviceResourceName, }}, - Set: []dtos.ResourceOperation{{ - DeviceResource: TestDeviceResourceName, - }}, - }} - var testCoreCommands = []dtos.Command{{ - Name: TestDeviceCommandName, - Get: true, - Set: true, }} return DeviceProfileRequest{ BaseRequest: common.BaseRequest{ @@ -63,7 +56,6 @@ func profileData() DeviceProfileRequest { Labels: testLabels, DeviceResources: testDeviceResources, DeviceCommands: testDeviceCommands, - CoreCommands: testCoreCommands, }, } } @@ -81,22 +73,15 @@ var expectedDeviceProfile = models.DeviceProfile{ Attributes: testAttributes, Properties: models.PropertyValue{ ValueType: v2.ValueTypeInt16, - ReadWrite: "RW", + ReadWrite: v2.ReadWrite_RW, }, }}, DeviceCommands: []models.DeviceCommand{{ - Name: TestDeviceCommandName, - Get: []models.ResourceOperation{{ + Name: TestDeviceCommandName, + ReadWrite: v2.ReadWrite_RW, + ResourceOperations: []models.ResourceOperation{{ DeviceResource: TestDeviceResourceName, }}, - Set: []models.ResourceOperation{{ - DeviceResource: TestDeviceResourceName, - }}, - }}, - CoreCommands: []models.Command{{ - Name: TestDeviceCommandName, - Get: true, - Set: true, }}, } @@ -113,11 +98,6 @@ func TestDeviceProfileRequest_Validate(t *testing.T) { noDeviceResourcePropertyType.Profile.DeviceResources[0].Properties.ValueType = emptyString invalidDeviceResourcePropertyType := profileData() invalidDeviceResourcePropertyType.Profile.DeviceResources[0].Properties.ValueType = "BadType" - noCommandName := profileData() - noCommandName.Profile.CoreCommands[0].Name = emptyString - noEnabledCommand := profileData() - noEnabledCommand.Profile.CoreCommands[0].Get = false - noEnabledCommand.Profile.CoreCommands[0].Set = false tests := []struct { name string @@ -130,8 +110,6 @@ func TestDeviceProfileRequest_Validate(t *testing.T) { {"invalid DeviceProfileRequest, no deviceResource name", noDeviceResourceName, true}, {"invalid DeviceProfileRequest, no deviceResource property type", noDeviceResourcePropertyType, true}, {"invalid DeviceProfileRequest, invalid deviceResource property type", invalidDeviceResourcePropertyType, true}, - {"invalid DeviceProfileRequest, no command name", noCommandName, true}, - {"invalid DeviceProfileRequest, no enabled command ", noEnabledCommand, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/v2/dtos/resourceoperation.go b/v2/dtos/resourceoperation.go index 14f7c164..d8a3390b 100644 --- a/v2/dtos/resourceoperation.go +++ b/v2/dtos/resourceoperation.go @@ -11,7 +11,7 @@ import "github.com/edgexfoundry/go-mod-core-contracts/v2/v2/models" // https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.x#/ResourceOperation type ResourceOperation struct { DeviceResource string `json:"deviceResource" yaml:"deviceResource" validate:"required"` // The replacement of Object field - Parameter string `json:"parameter,omitempty" yaml:"parameter,omitempty"` + DefaultValue string `json:"defaultValue,omitempty" yaml:"defaultValue,omitempty"` Mappings map[string]string `json:"mappings,omitempty" yaml:"mappings,omitempty"` } @@ -19,7 +19,7 @@ type ResourceOperation struct { func ToResourceOperationModel(ro ResourceOperation) models.ResourceOperation { return models.ResourceOperation{ DeviceResource: ro.DeviceResource, - Parameter: ro.Parameter, + DefaultValue: ro.DefaultValue, Mappings: ro.Mappings, } } @@ -28,7 +28,7 @@ func ToResourceOperationModel(ro ResourceOperation) models.ResourceOperation { func FromResourceOperationModelToDTO(ro models.ResourceOperation) ResourceOperation { return ResourceOperation{ DeviceResource: ro.DeviceResource, - Parameter: ro.Parameter, + DefaultValue: ro.DefaultValue, Mappings: ro.Mappings, } } diff --git a/v2/models/command.go b/v2/models/command.go deleted file mode 100644 index 531ab2c4..00000000 --- a/v2/models/command.go +++ /dev/null @@ -1,15 +0,0 @@ -// -// Copyright (C) 2020 IOTech Ltd -// -// SPDX-License-Identifier: Apache-2.0 - -package models - -// Command and its properties are defined in the APIv2 specification: -// https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.x#/Command -// Model fields are same as the DTOs documented by this swagger. Exceptions, if any, are noted below. -type Command struct { - Name string - Get bool - Set bool -} diff --git a/v2/models/deviceCommand.go b/v2/models/deviceCommand.go index 4993a4fc..f603d46c 100644 --- a/v2/models/deviceCommand.go +++ b/v2/models/deviceCommand.go @@ -9,7 +9,8 @@ package models // https://app.swaggerhub.com/apis-docs/EdgeXFoundry1/core-metadata/2.x#/DeviceCommand // Model fields are same as the DTOs documented by this swagger. Exceptions, if any, are noted below. type DeviceCommand struct { - Name string - Get []ResourceOperation - Set []ResourceOperation + Name string + IsHidden bool + ReadWrite string + ResourceOperations []ResourceOperation } diff --git a/v2/models/deviceprofile.go b/v2/models/deviceprofile.go index d9d4d172..89a12f5c 100644 --- a/v2/models/deviceprofile.go +++ b/v2/models/deviceprofile.go @@ -18,5 +18,4 @@ type DeviceProfile struct { Labels []string DeviceResources []DeviceResource DeviceCommands []DeviceCommand - CoreCommands []Command } diff --git a/v2/models/deviceresource.go b/v2/models/deviceresource.go index 47af075c..2e688aac 100644 --- a/v2/models/deviceresource.go +++ b/v2/models/deviceresource.go @@ -11,6 +11,7 @@ package models type DeviceResource struct { Description string Name string + IsHidden bool Tag string Properties PropertyValue Attributes map[string]string diff --git a/v2/models/resourceoperation.go b/v2/models/resourceoperation.go index 4f3ce670..98918672 100644 --- a/v2/models/resourceoperation.go +++ b/v2/models/resourceoperation.go @@ -10,6 +10,6 @@ package models // Model fields are same as the DTOs documented by this swagger. Exceptions, if any, are noted below. type ResourceOperation struct { DeviceResource string - Parameter string + DefaultValue string Mappings map[string]string }