Skip to content

Commit c42918a

Browse files
Paolo Calaopolldo
Paolo Calao
authored andcommitted
Filter resources by tags (#55)
* Filter things by tags * Filter devices by tags * Update filter command * Update readme filter * Update mocks
1 parent cbf2d62 commit c42918a

File tree

7 files changed

+83
-30
lines changed

7 files changed

+83
-30
lines changed

README.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ Use this command to provision a device:
6565

6666
## Device commands
6767

68-
Once a device has been created thorugh the provisioning procedure, it can be deleted by using the following command:
69-
`$ arduino-cloud-cli device delete --id <deviceID>`
70-
71-
Devices currently present on Arduino IoT Cloud can be retrieved by using this command:
68+
Devices currently present on Arduino IoT Cloud can be retrieved with:
7269
`$ arduino-cloud-cli device list`
7370

71+
It has an optional `--tags` flag that allows to list only the devices having all the provided tags:
72+
`$ arduino-cloud-cli device list --tags <key0>=<value0>,<key1>=<value1>`
73+
7474
Add tags to a device. Tags should be passed as a comma-separated list of `<key>=<value>` items:
7575

7676
`$ arduino-cloud-cli device create-tags --id <deviceID> --tags <key0>=<value0>,<key1>=<value1>`
@@ -106,6 +106,12 @@ Print only the thing associated to the passed device:
106106

107107
`$ arduino-cloud-cli thing list --device-id <deviceID>`
108108

109+
Print only the things that have all the passed tags:
110+
111+
`$ arduino-cloud-cli thing list --tags <key0>=<value0>,<key1>=<value1>`
112+
113+
Things can be deleted using the thing delete command. This command accepts two mutually exclusive flags: `--id` and `--tags`. Only one of them must be passed. When the `--id` is passed, the thing having such ID gets deleted:
114+
109115
Delete a thing with the following command:
110116

111117
`$ arduino-cloud-cli thing delete --id <thingID>`

cli/device/list.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,32 @@ import (
2828
"github.com/spf13/cobra"
2929
)
3030

31+
var listFlags struct {
32+
tags map[string]string
33+
}
34+
3135
func initListCommand() *cobra.Command {
3236
listCommand := &cobra.Command{
3337
Use: "list",
3438
Short: "List devices",
3539
Long: "List devices on Arduino IoT Cloud",
3640
Run: runListCommand,
3741
}
42+
listCommand.Flags().StringToStringVar(
43+
&listFlags.tags,
44+
"tags",
45+
nil,
46+
"Comma-separated list of tags with format <key>=<value>.\n"+
47+
"List only devices that match the provided tags.",
48+
)
3849
return listCommand
3950
}
4051

4152
func runListCommand(cmd *cobra.Command, args []string) {
4253
logrus.Info("Listing devices")
4354

44-
devs, err := device.List()
55+
params := &device.ListParams{Tags: listFlags.tags}
56+
devs, err := device.List(params)
4557
if err != nil {
4658
feedback.Errorf("Error during device list: %v", err)
4759
os.Exit(errorcodes.ErrGeneric)

cli/thing/list.go

+9
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var listFlags struct {
3333
ids []string
3434
deviceID string
3535
variables bool
36+
tags map[string]string
3637
}
3738

3839
func initListCommand() *cobra.Command {
@@ -47,6 +48,13 @@ func initListCommand() *cobra.Command {
4748
// list only the thing associated to the passed device id
4849
listCommand.Flags().StringVarP(&listFlags.deviceID, "device-id", "d", "", "ID of Device associated to the thing to be retrieved")
4950
listCommand.Flags().BoolVarP(&listFlags.variables, "show-variables", "s", false, "Show thing variables")
51+
listCommand.Flags().StringToStringVar(
52+
&listFlags.tags,
53+
"tags",
54+
nil,
55+
"Comma-separated list of tags with format <key>=<value>.\n"+
56+
"List only things that match the provided tags.",
57+
)
5058
return listCommand
5159
}
5260

@@ -56,6 +64,7 @@ func runListCommand(cmd *cobra.Command, args []string) {
5664
params := &thing.ListParams{
5765
IDs: listFlags.ids,
5866
Variables: listFlags.variables,
67+
Tags: listFlags.tags,
5968
}
6069
if listFlags.deviceID != "" {
6170
params.DeviceID = &listFlags.deviceID

command/device/list.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ type DeviceInfo struct {
3232
FQBN string `json:"fqbn"`
3333
}
3434

35+
// ListParams contains the optional parameters needed
36+
// to filter the devices to be listed.
37+
type ListParams struct {
38+
Tags map[string]string // If tags are provided, only devices that have all these tags are listed.
39+
}
40+
3541
// List command is used to list
3642
// the devices of Arduino IoT Cloud.
37-
func List() ([]DeviceInfo, error) {
43+
func List(params *ListParams) ([]DeviceInfo, error) {
3844
conf, err := config.Retrieve()
3945
if err != nil {
4046
return nil, err
@@ -44,7 +50,7 @@ func List() ([]DeviceInfo, error) {
4450
return nil, err
4551
}
4652

47-
foundDevices, err := iotClient.DeviceList()
53+
foundDevices, err := iotClient.DeviceList(params.Tags)
4854
if err != nil {
4955
return nil, err
5056
}

command/thing/list.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ import (
2525
// ListParams contains the optional parameters needed
2626
// to filter the things to be listed.
2727
type ListParams struct {
28-
IDs []string // If IDs is not nil, only things belonging to that list are returned
29-
DeviceID *string // If DeviceID is provided, only the thing associated to that device is listed.
30-
Variables bool // If Variables is true, variable names are retrieved.
28+
IDs []string // If IDs is not nil, only things belonging to that list are returned
29+
DeviceID *string // If DeviceID is provided, only the thing associated to that device is listed.
30+
Variables bool // If Variables is true, variable names are retrieved.
31+
Tags map[string]string // If tags are provided, only things that have all these tags are listed.
3132
}
3233

3334
// List command is used to list
@@ -42,7 +43,7 @@ func List(params *ListParams) ([]ThingInfo, error) {
4243
return nil, err
4344
}
4445

45-
foundThings, err := iotClient.ThingList(params.IDs, params.DeviceID, params.Variables)
46+
foundThings, err := iotClient.ThingList(params.IDs, params.DeviceID, params.Variables, params.Tags)
4647
if err != nil {
4748
return nil, err
4849
}

internal/iot/client.go

+24-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
type Client interface {
3131
DeviceCreate(fqbn, name, serial, devType string) (*iotclient.ArduinoDevicev2, error)
3232
DeviceDelete(id string) error
33-
DeviceList() ([]iotclient.ArduinoDevicev2, error)
33+
DeviceList(tags map[string]string) ([]iotclient.ArduinoDevicev2, error)
3434
DeviceShow(id string) (*iotclient.ArduinoDevicev2, error)
3535
DeviceOTA(id string, file *os.File, expireMins int) error
3636
DeviceTagsCreate(id string, tags map[string]string) error
@@ -40,7 +40,7 @@ type Client interface {
4040
ThingUpdate(id string, thing *iotclient.Thing, force bool) error
4141
ThingDelete(id string) error
4242
ThingShow(id string) (*iotclient.ArduinoThing, error)
43-
ThingList(ids []string, device *string, props bool) ([]iotclient.ArduinoThing, error)
43+
ThingList(ids []string, device *string, props bool, tags map[string]string) ([]iotclient.ArduinoThing, error)
4444
ThingTagsCreate(id string, tags map[string]string) error
4545
ThingTagsDelete(id string, keys []string) error
4646
DashboardCreate(dashboard *iotclient.Dashboardv2) (*iotclient.ArduinoDashboardv2, error)
@@ -96,8 +96,18 @@ func (cl *client) DeviceDelete(id string) error {
9696

9797
// DeviceList retrieves and returns a list of all Arduino IoT Cloud devices
9898
// belonging to the user performing the request.
99-
func (cl *client) DeviceList() ([]iotclient.ArduinoDevicev2, error) {
100-
devices, _, err := cl.api.DevicesV2Api.DevicesV2List(cl.ctx, nil)
99+
func (cl *client) DeviceList(tags map[string]string) ([]iotclient.ArduinoDevicev2, error) {
100+
opts := &iotclient.DevicesV2ListOpts{}
101+
if tags != nil {
102+
t := make([]string, 0, len(tags))
103+
for key, val := range tags {
104+
// Use the 'key:value' format required from the backend
105+
t = append(t, key+":"+val)
106+
}
107+
opts.Tags = optional.NewInterface(t)
108+
}
109+
110+
devices, _, err := cl.api.DevicesV2Api.DevicesV2List(cl.ctx, opts)
101111
if err != nil {
102112
err = fmt.Errorf("listing devices: %w", errorDetail(err))
103113
return nil, err
@@ -216,7 +226,7 @@ func (cl *client) ThingShow(id string) (*iotclient.ArduinoThing, error) {
216226
}
217227

218228
// ThingList returns a list of things on Arduino IoT Cloud.
219-
func (cl *client) ThingList(ids []string, device *string, props bool) ([]iotclient.ArduinoThing, error) {
229+
func (cl *client) ThingList(ids []string, device *string, props bool, tags map[string]string) ([]iotclient.ArduinoThing, error) {
220230
opts := &iotclient.ThingsV2ListOpts{}
221231
opts.ShowProperties = optional.NewBool(props)
222232

@@ -228,6 +238,15 @@ func (cl *client) ThingList(ids []string, device *string, props bool) ([]iotclie
228238
opts.DeviceId = optional.NewString(*device)
229239
}
230240

241+
if tags != nil {
242+
t := make([]string, 0, len(tags))
243+
for key, val := range tags {
244+
// Use the 'key:value' format required from the backend
245+
t = append(t, key+":"+val)
246+
}
247+
opts.Tags = optional.NewInterface(t)
248+
}
249+
231250
things, _, err := cl.api.ThingsV2Api.ThingsV2List(cl.ctx, opts)
232251
if err != nil {
233252
err = fmt.Errorf("retrieving things, %w", errorDetail(err))

internal/iot/mocks/Client.go

+14-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)