Skip to content

Commit

Permalink
feat: Add optional "parent" field to Device objects. (#887)
Browse files Browse the repository at this point in the history
* feat: Add optional Parent field to device objects.

Also add DeviceClient function to query a tree of devices.

Signed-off-by: Corey Mutter <CoreyMutter@eaton.com>

* feat: Add Parent field (revert change to timestamp propagation)

Signed-off-by: Corey Mutter <CoreyMutter@eaton.com>

* fix: Formatting.

Signed-off-by: Corey Mutter <CoreyMutter@eaton.com>

* feat: Add Parent field (update constant name, add maxLevels to query)

Signed-off-by: Corey Mutter <CoreyMutter@eaton.com>

---------

Signed-off-by: Corey Mutter <CoreyMutter@eaton.com>
  • Loading branch information
eaton-coreymutter authored Apr 11, 2024
1 parent 7c7fc6d commit 132cf7a
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 37 deletions.
16 changes: 16 additions & 0 deletions clients/http/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ func (dc DeviceClient) AllDevices(ctx context.Context, labels []string, offset i
return res, nil
}

func (dc DeviceClient) AllDevicesWithChildren(ctx context.Context, parent string, maxLevels uint, labels []string, offset int, limit int) (res responses.MultiDevicesResponse, err errors.EdgeX) {
requestParams := url.Values{}
if len(labels) > 0 {
requestParams.Set(common.Labels, strings.Join(labels, common.CommaSeparator))
}
requestParams.Set(common.DescendantsOf, parent)
requestParams.Set(common.MaxLevels, strconv.FormatUint(uint64(maxLevels), 10))
requestParams.Set(common.Offset, strconv.Itoa(offset))
requestParams.Set(common.Limit, strconv.Itoa(limit))
err = utils.GetRequest(ctx, &res, dc.baseUrl, common.ApiAllDeviceRoute, requestParams, dc.authInjector)
if err != nil {
return res, errors.NewCommonEdgeXWrapper(err)
}
return res, nil
}

func (dc DeviceClient) DeviceNameExists(ctx context.Context, name string) (res dtoCommon.BaseResponse, err errors.EdgeX) {
path := common.NewPathBuilder().EnableNameFieldEscape(dc.enableNameFieldEscape).
SetPath(common.ApiDeviceRoute).SetPath(common.Check).SetPath(common.Name).SetNameFieldPath(name).BuildPath()
Expand Down
9 changes: 9 additions & 0 deletions clients/http/device_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,12 @@ func TestQueryDevicesByServiceName(t *testing.T) {
require.NoError(t, err)
require.IsType(t, responses.MultiDevicesResponse{}, res)
}

func TestQueryDeviceTree(t *testing.T) {
ts := newTestServer(http.MethodGet, common.ApiAllDeviceRoute, responses.MultiDevicesResponse{})
defer ts.Close()
client := NewDeviceClient(ts.URL, NewNullAuthenticationInjector(), false)
res, err := client.AllDevicesWithChildren(context.Background(), "MyRoot", 3, []string{"label1", "label2"}, 1, 10)
require.NoError(t, err)
require.IsType(t, responses.MultiDevicesResponse{}, res)
}
7 changes: 7 additions & 0 deletions clients/interfaces/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ type DeviceClient interface {
// offset: The number of items to skip before starting to collect the result set. Default is 0.
// limit: The number of items to return. Specify -1 will return all remaining items after offset. The maximum will be the MaxResultCount as defined in the configuration of service. Default is 20.
AllDevices(ctx context.Context, labels []string, offset int, limit int) (responses.MultiDevicesResponse, errors.EdgeX)
// AllDevicesWithChildren returns all devices who have parent, grandparent, etc. of the
// given device name. Devices can also be filtered by labels.
// Device tree is descended at most maxLevels. If maxLevels is 0, there is no limit.
// The result can be limited in a certain range by specifying the offset and limit parameters.
// offset: The number of items to skip before starting to collect the result set. Default is 0.
// limit: The number of items to return. Specify -1 will return all remaining items after offset. The maximum will be the MaxResultCount as defined in the configuration of service. Default is 20.
AllDevicesWithChildren(ctx context.Context, parent string, maxLevels uint, labels []string, offset int, limit int) (responses.MultiDevicesResponse, errors.EdgeX)
// DeviceNameExists checks whether the device exists.
DeviceNameExists(ctx context.Context, name string) (common.BaseResponse, errors.EdgeX)
// DeviceByName returns a device by device name.
Expand Down
113 changes: 99 additions & 14 deletions clients/interfaces/mocks/DeviceClient.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 12 additions & 10 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,16 +185,18 @@ const (
Key = "key"
ServiceId = "serviceId"

Offset = "offset" //query string to specify the number of items to skip before starting to collect the result set.
Limit = "limit" //query string to specify the numbers of items to return
Labels = "labels" //query string to specify associated user-defined labels for querying a given object. More than one label may be specified via a comma-delimited list
PushEvent = "ds-pushevent" //query string to specify if an event should be pushed to the EdgeX system
ReturnEvent = "ds-returnevent" //query string to specify if an event should be returned from device service
RegexCommand = "ds-regexcmd" //query string to specify if the command name is in regular expression format
Flatten = "flatten" //query string to specify if the request json payload should be flattened to update multiple keys with the same prefix
KeyOnly = "keyOnly" //query string to specify if the response will only return the keys of the specified query key prefix, without values and metadata
Plaintext = "plaintext" //query string to specify if the response will return the stored plain text value of the key(s) without any encoding
Deregistered = "deregistered" //query string to specify if the response will return the registries of deregistered services
Offset = "offset" //query string to specify the number of items to skip before starting to collect the result set.
Limit = "limit" //query string to specify the numbers of items to return
Labels = "labels" //query string to specify associated user-defined labels for querying a given object. More than one label may be specified via a comma-delimited list
PushEvent = "ds-pushevent" //query string to specify if an event should be pushed to the EdgeX system
ReturnEvent = "ds-returnevent" //query string to specify if an event should be returned from device service
RegexCommand = "ds-regexcmd" //query string to specify if the command name is in regular expression format
DescendantsOf = "descendantsOf" //Limit returned devices to those who have parent, grandparent, etc. of the given device name
MaxLevels = "maxLevels" //Limit returned devices to this many levels below 'descendantsOf' (0=unlimited)
Flatten = "flatten" //query string to specify if the request json payload should be flattened to update multiple keys with the same prefix
KeyOnly = "keyOnly" //query string to specify if the response will only return the keys of the specified query key prefix, without values and metadata
Plaintext = "plaintext" //query string to specify if the response will return the stored plain text value of the key(s) without any encoding
Deregistered = "deregistered" //query string to specify if the response will return the registries of deregistered services
)

// Constants related to the default value of query strings in the v3 service APIs
Expand Down
5 changes: 5 additions & 0 deletions dtos/device.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Device struct {
DBTimestamp `json:",inline"`
Id string `json:"id,omitempty" yaml:"id,omitempty" validate:"omitempty,uuid"`
Name string `json:"name" yaml:"name" validate:"required,edgex-dto-none-empty-string"`
Parent string `json:"parent,omitempty" yaml:"parent,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
AdminState string `json:"adminState" yaml:"adminState" validate:"oneof='LOCKED' 'UNLOCKED'"`
OperatingState string `json:"operatingState" yaml:"operatingState" validate:"oneof='UP' 'DOWN' 'UNKNOWN'"`
Expand All @@ -29,6 +30,7 @@ type Device struct {
type UpdateDevice struct {
Id *string `json:"id" validate:"required_without=Name,edgex-dto-uuid"`
Name *string `json:"name" validate:"required_without=Id,edgex-dto-none-empty-string"`
Parent *string `json:"parent,omitempty" yaml:"parent,omitempty"`
Description *string `json:"description" validate:"omitempty"`
AdminState *string `json:"adminState" validate:"omitempty,oneof='LOCKED' 'UNLOCKED'"`
OperatingState *string `json:"operatingState" validate:"omitempty,oneof='UP' 'DOWN' 'UNKNOWN'"`
Expand All @@ -47,6 +49,7 @@ func ToDeviceModel(dto Device) models.Device {
var d models.Device
d.Id = dto.Id
d.Name = dto.Name
d.Parent = dto.Parent
d.Description = dto.Description
d.ServiceName = dto.ServiceName
d.ProfileName = dto.ProfileName
Expand All @@ -67,6 +70,7 @@ func FromDeviceModelToDTO(d models.Device) Device {
dto.DBTimestamp = DBTimestamp(d.DBTimestamp)
dto.Id = d.Id
dto.Name = d.Name
dto.Parent = d.Parent
dto.Description = d.Description
dto.ServiceName = d.ServiceName
dto.ProfileName = d.ProfileName
Expand All @@ -88,6 +92,7 @@ func FromDeviceModelToUpdateDTO(d models.Device) UpdateDevice {
dto := UpdateDevice{
Id: &d.Id,
Name: &d.Name,
Parent: &d.Parent,
Description: &d.Description,
AdminState: &adminState,
OperatingState: &operatingState,
Expand Down
Loading

0 comments on commit 132cf7a

Please sign in to comment.