dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts@v1.0.2/dtos/deviceprofile.go (about) 1 // 2 // Copyright (C) 2020-2021 IOTech Ltd 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 6 package dtos 7 8 import ( 9 "errors" 10 "fmt" 11 "strings" 12 13 "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/common" 14 edgexErrors "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/errors" 15 "dev.azure.com/aidainnovazione0090/DeviceManager/_git/go-mod-core-contracts/models" 16 ) 17 18 type DeviceProfile struct { 19 DBTimestamp `json:",inline"` 20 DeviceProfileBasicInfo `json:",inline" yaml:",inline"` 21 DeviceResources []DeviceResource `json:"deviceResources" yaml:"deviceResources" validate:"dive"` 22 DeviceCommands []DeviceCommand `json:"deviceCommands" yaml:"deviceCommands" validate:"dive"` 23 } 24 25 // Validate satisfies the Validator interface 26 func (dp *DeviceProfile) Validate() error { 27 err := common.Validate(dp) 28 if err != nil { 29 // The DeviceProfileBasicInfo is the internal struct in Golang programming, not in the Profile model, 30 // so it should be hidden from the error messages. 31 err = errors.New(strings.ReplaceAll(err.Error(), ".DeviceProfileBasicInfo", "")) 32 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, "Invalid DeviceProfile.", err) 33 } 34 return ValidateDeviceProfileDTO(*dp) 35 } 36 37 // UnmarshalYAML implements the Unmarshaler interface for the DeviceProfile type 38 func (dp *DeviceProfile) UnmarshalYAML(unmarshal func(interface{}) error) error { 39 var alias struct { 40 DBTimestamp 41 DeviceProfileBasicInfo `yaml:",inline"` 42 DeviceResources []DeviceResource `yaml:"deviceResources"` 43 DeviceCommands []DeviceCommand `yaml:"deviceCommands"` 44 } 45 if err := unmarshal(&alias); err != nil { 46 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, "failed to unmarshal request body as YAML.", err) 47 } 48 *dp = DeviceProfile(alias) 49 50 if err := dp.Validate(); err != nil { 51 return edgexErrors.NewCommonEdgeXWrapper(err) 52 } 53 54 // Normalize resource's value type 55 for i, resource := range dp.DeviceResources { 56 valueType, err := common.NormalizeValueType(resource.Properties.ValueType) 57 if err != nil { 58 return edgexErrors.NewCommonEdgeXWrapper(err) 59 } 60 dp.DeviceResources[i].Properties.ValueType = valueType 61 } 62 return nil 63 } 64 65 // ToDeviceProfileModel transforms the DeviceProfile DTO to the DeviceProfile model 66 func ToDeviceProfileModel(deviceProfileDTO DeviceProfile) models.DeviceProfile { 67 return models.DeviceProfile{ 68 DBTimestamp: models.DBTimestamp(deviceProfileDTO.DBTimestamp), 69 Id: deviceProfileDTO.Id, 70 Name: deviceProfileDTO.Name, 71 Description: deviceProfileDTO.Description, 72 Manufacturer: deviceProfileDTO.Manufacturer, 73 Model: deviceProfileDTO.Model, 74 Labels: deviceProfileDTO.Labels, 75 DeviceResources: ToDeviceResourceModels(deviceProfileDTO.DeviceResources), 76 DeviceCommands: ToDeviceCommandModels(deviceProfileDTO.DeviceCommands), 77 } 78 } 79 80 // FromDeviceProfileModelToDTO transforms the DeviceProfile Model to the DeviceProfile DTO 81 func FromDeviceProfileModelToDTO(deviceProfile models.DeviceProfile) DeviceProfile { 82 return DeviceProfile{ 83 DBTimestamp: DBTimestamp(deviceProfile.DBTimestamp), 84 DeviceProfileBasicInfo: DeviceProfileBasicInfo{ 85 Id: deviceProfile.Id, 86 Name: deviceProfile.Name, 87 Description: deviceProfile.Description, 88 Manufacturer: deviceProfile.Manufacturer, 89 Model: deviceProfile.Model, 90 Labels: deviceProfile.Labels, 91 }, 92 DeviceResources: FromDeviceResourceModelsToDTOs(deviceProfile.DeviceResources), 93 DeviceCommands: FromDeviceCommandModelsToDTOs(deviceProfile.DeviceCommands), 94 } 95 } 96 97 func ValidateDeviceProfileDTO(profile DeviceProfile) error { 98 // deviceResources validation 99 dupCheck := make(map[string]bool) 100 for _, resource := range profile.DeviceResources { 101 if resource.Properties.ValueType == common.ValueTypeBinary && 102 strings.Contains(resource.Properties.ReadWrite, common.ReadWrite_W) { 103 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("write permission not support %s value type for resource '%s'", common.ValueTypeBinary, resource.Name), nil) 104 } 105 // deviceResource name should not duplicated 106 if dupCheck[resource.Name] { 107 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("device resource %s is duplicated", resource.Name), nil) 108 } 109 dupCheck[resource.Name] = true 110 } 111 // deviceCommands validation 112 dupCheck = make(map[string]bool) 113 for _, command := range profile.DeviceCommands { 114 // deviceCommand name should not duplicated 115 if dupCheck[command.Name] { 116 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("device command %s is duplicated", command.Name), nil) 117 } 118 dupCheck[command.Name] = true 119 120 resourceOperations := command.ResourceOperations 121 for _, ro := range resourceOperations { 122 // ResourceOperations referenced in deviceCommands must exist 123 if !deviceResourcesContains(profile.DeviceResources, ro.DeviceResource) { 124 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("device command's resource %s doesn't match any device resource", ro.DeviceResource), nil) 125 } 126 // Check the ReadWrite whether is align to the deviceResource 127 if !validReadWritePermission(profile.DeviceResources, ro.DeviceResource, command.ReadWrite) { 128 return edgexErrors.NewCommonEdgeX(edgexErrors.KindContractInvalid, fmt.Sprintf("device command's ReadWrite permission '%s' doesn't align the device resource", command.ReadWrite), nil) 129 } 130 } 131 } 132 return nil 133 } 134 135 func deviceResourcesContains(resources []DeviceResource, name string) bool { 136 contains := false 137 for _, resource := range resources { 138 if resource.Name == name { 139 contains = true 140 break 141 } 142 } 143 return contains 144 } 145 146 func validReadWritePermission(resources []DeviceResource, name string, readWrite string) bool { 147 valid := true 148 for _, resource := range resources { 149 if resource.Name == name { 150 if resource.Properties.ReadWrite != common.ReadWrite_RW && resource.Properties.ReadWrite != common.ReadWrite_WR && 151 resource.Properties.ReadWrite != readWrite { 152 valid = false 153 break 154 } 155 } 156 } 157 return valid 158 }