Skip to content

Add thing list command #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,18 @@ Create a thing from a template:
Create a thing by cloning another thing:

`$ iot-cloud-cli thing create --name <thingName> --clone-id <thingToCloneID>`


Things can be printed thanks to a list command.

Print a list of available things and their properties by using this command:

`$ iot-cloud-cli thing list --properties`

Print a *filtered* list of available things, print only things belonging to the ids list:

`$ iot-cloud-cli thing list --ids <thingOneID>,<thingTwoID>`

Print only the thing associated to the passed device:

`$ iot-cloud-cli thing list --device-id <deviceID>`
82 changes: 82 additions & 0 deletions cli/thing/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package thing

import (
"fmt"
"strings"

"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/table"
"github.com/arduino/iot-cloud-cli/command/thing"
"github.com/spf13/cobra"
)

var listFlags struct {
ids []string
deviceID string
properties bool
}

func initListCommand() *cobra.Command {
listCommand := &cobra.Command{
Use: "list",
Short: "List things",
Long: "List things on Arduino IoT Cloud",
RunE: runListCommand,
}
// list only the things corresponding to the passed ids
listCommand.Flags().StringSliceVarP(&listFlags.ids, "ids", "i", []string{}, "List of thing IDs to be retrieved")
// list only the thing associated to the passed device id
listCommand.Flags().StringVarP(&listFlags.deviceID, "device-id", "d", "", "ID of Device associated to the thing to be retrieved")
listCommand.Flags().BoolVarP(&listFlags.properties, "properties", "p", false, "Show thing properties")
return listCommand
}

func runListCommand(cmd *cobra.Command, args []string) error {
fmt.Println("Listing things")

params := &thing.ListParams{
IDs: listFlags.ids,
Properties: listFlags.properties,
}
if listFlags.deviceID != "" {
params.DeviceID = &listFlags.deviceID
}

things, err := thing.List(params)
if err != nil {
return err
}

feedback.PrintResult(result{things})
return nil
}

type result struct {
things []thing.ThingInfo
}

func (r result) Data() interface{} {
return r.things
}

func (r result) String() string {
if len(r.things) == 0 {
return "No things found."
}
t := table.New()

h := []interface{}{"Name", "ID", "Device"}
if listFlags.properties {
h = append(h, "Properties")
}
t.SetHeader(h...)

for _, thing := range r.things {
r := []interface{}{thing.Name, thing.ID, thing.DeviceID}
if listFlags.properties {
r = append(r, strings.Join(thing.Properties, ", "))
}
t.AddRow(r...)
}
return t.Render()
}
1 change: 1 addition & 0 deletions cli/thing/thing.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ func NewCommand() *cobra.Command {
}

thingCommand.AddCommand(initCreateCommand())
thingCommand.AddCommand(initListCommand())

return thingCommand
}
61 changes: 61 additions & 0 deletions command/thing/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package thing

import (
"github.com/arduino/iot-cloud-cli/internal/config"
"github.com/arduino/iot-cloud-cli/internal/iot"
)

// ThingInfo contains the main parameters of
// an Arduino IoT Cloud thing.
type ThingInfo struct {
Name string
ID string
DeviceID string
Properties []string
}

// ListParams contains the optional parameters needed
// to filter the things to be listed.
// If IDs is valid, only things belonging to that list are listed.
// If DeviceID is provided, only things associated to that device are listed.
// If Properties is true, properties names are retrieved.
type ListParams struct {
IDs []string
DeviceID *string
Properties bool
}

// List command is used to list
// the things of Arduino IoT Cloud.
func List(params *ListParams) ([]ThingInfo, error) {
conf, err := config.Retrieve()
if err != nil {
return nil, err
}
iotClient, err := iot.NewClient(conf.Client, conf.Secret)
if err != nil {
return nil, err
}

foundThings, err := iotClient.ListThings(params.IDs, params.DeviceID, params.Properties)
if err != nil {
return nil, err
}

var things []ThingInfo
for _, foundThing := range foundThings {
var props []string
for _, p := range foundThing.Properties {
props = append(props, p.Name)
}
th := ThingInfo{
Name: foundThing.Name,
ID: foundThing.Id,
DeviceID: foundThing.DeviceId,
Properties: props,
}
things = append(things, th)
}

return things, nil
}
22 changes: 22 additions & 0 deletions internal/iot/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Client interface {
AddCertificate(id, csr string) (*iotclient.ArduinoCompressedv2, error)
AddThing(thing *iotclient.Thing, force bool) (string, error)
GetThing(id string) (*iotclient.ArduinoThing, error)
ListThings(ids []string, device *string, props bool) ([]iotclient.ArduinoThing, error)
}

type client struct {
Expand Down Expand Up @@ -117,6 +118,27 @@ func (cl *client) GetThing(id string) (*iotclient.ArduinoThing, error) {
return &thing, nil
}

// ListThings returns a list of things on Arduino IoT Cloud.
func (cl *client) ListThings(ids []string, device *string, props bool) ([]iotclient.ArduinoThing, error) {
opts := &iotclient.ThingsV2ListOpts{}
opts.ShowProperties = optional.NewBool(props)

if ids != nil {
opts.Ids = optional.NewInterface(ids)
}

if device != nil {
opts.DeviceId = optional.NewString(*device)
}

things, _, err := cl.api.ThingsV2Api.ThingsV2List(cl.ctx, opts)
if err != nil {
err = fmt.Errorf("retrieving things, %w", err)
return nil, err
}
return things, nil
}

func (cl *client) setup(client, secret string) error {
// Get the access token in exchange of client_id and client_secret
tok, err := token(client, secret)
Expand Down