From 09a5df8a639f2d3c5a045a8a91dbc2ad44da6ce5 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 9 Nov 2021 17:27:29 +0100 Subject: [PATCH 1/7] Show thing tags in list command --- cli/thing/list.go | 3 ++- command/thing/clone.go | 6 +++++- command/thing/create.go | 7 ++++++- command/thing/list.go | 7 ++++++- command/thing/thing.go | 23 ++++++++++++++++++++--- command/thing/thing_test.go | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 command/thing/thing_test.go diff --git a/cli/thing/list.go b/cli/thing/list.go index ad149c60..c0122c59 100644 --- a/cli/thing/list.go +++ b/cli/thing/list.go @@ -93,7 +93,7 @@ func (r result) String() string { } t := table.New() - h := []interface{}{"Name", "ID", "Device"} + h := []interface{}{"Name", "ID", "Device", "Tags"} if listFlags.variables { h = append(h, "Variables") } @@ -101,6 +101,7 @@ func (r result) String() string { for _, thing := range r.things { r := []interface{}{thing.Name, thing.ID, thing.DeviceID} + r = append(r, strings.Join(thing.Tags, ", ")) if listFlags.variables { r = append(r, strings.Join(thing.Variables, ", ")) } diff --git a/command/thing/clone.go b/command/thing/clone.go index d0a8c0a1..514268e8 100644 --- a/command/thing/clone.go +++ b/command/thing/clone.go @@ -54,7 +54,11 @@ func Clone(params *CloneParams) (*ThingInfo, error) { return nil, err } - return getThingInfo(newThing), nil + t, err := getThingInfo(newThing) + if err != nil { + return nil, fmt.Errorf("parsing thing %s from cloud: %w", newThing.Id, err) + } + return t, nil } func retrieve(client iot.Client, thingID string) (*iotclient.Thing, error) { diff --git a/command/thing/create.go b/command/thing/create.go index 4be2bac7..68b867fa 100644 --- a/command/thing/create.go +++ b/command/thing/create.go @@ -19,6 +19,7 @@ package thing import ( "errors" + "fmt" "github.com/arduino/arduino-cloud-cli/internal/config" "github.com/arduino/arduino-cloud-cli/internal/iot" @@ -62,5 +63,9 @@ func Create(params *CreateParams) (*ThingInfo, error) { return nil, err } - return getThingInfo(newThing), nil + t, err := getThingInfo(newThing) + if err != nil { + return nil, fmt.Errorf("getting the new thing %s from cloud: %w", newThing.Id, err) + } + return t, nil } diff --git a/command/thing/list.go b/command/thing/list.go index e0d647a7..f5808a3c 100644 --- a/command/thing/list.go +++ b/command/thing/list.go @@ -18,6 +18,8 @@ package thing import ( + "fmt" + "github.com/arduino/arduino-cloud-cli/internal/config" "github.com/arduino/arduino-cloud-cli/internal/iot" ) @@ -50,7 +52,10 @@ func List(params *ListParams) ([]ThingInfo, error) { var things []ThingInfo for _, foundThing := range foundThings { - info := getThingInfo(&foundThing) + info, err := getThingInfo(&foundThing) + if err != nil { + return nil, fmt.Errorf("getting thing %s from cloud: %w", foundThing.Id, err) + } things = append(things, *info) } diff --git a/command/thing/thing.go b/command/thing/thing.go index c5753de2..1f161ae9 100644 --- a/command/thing/thing.go +++ b/command/thing/thing.go @@ -17,7 +17,11 @@ package thing -import iotclient "github.com/arduino/iot-client-go" +import ( + "fmt" + + iotclient "github.com/arduino/iot-client-go" +) // ThingInfo contains the main parameters of // an Arduino IoT Cloud thing. @@ -26,18 +30,31 @@ type ThingInfo struct { ID string `json:"id"` DeviceID string `json:"device-id"` Variables []string `json:"variables"` + Tags []string `json:"tags,omitempty"` } -func getThingInfo(thing *iotclient.ArduinoThing) *ThingInfo { +func getThingInfo(thing *iotclient.ArduinoThing) (*ThingInfo, error) { + // Retrieve thing variables var vars []string for _, p := range thing.Properties { vars = append(vars, p.Name) } + // Retrieve thing tags + var tags []string + for key, value := range thing.Tags { + if valStr, ok := value.(string); ok { + tags = append(tags, key+": "+valStr) + } else { + return nil, fmt.Errorf("value of tag `%s` should be of type `string` but is of type `%T`", key, value) + } + } + info := &ThingInfo{ Name: thing.Name, ID: thing.Id, DeviceID: thing.DeviceId, Variables: vars, + Tags: tags, } - return info + return info, nil } diff --git a/command/thing/thing_test.go b/command/thing/thing_test.go new file mode 100644 index 00000000..2e68a074 --- /dev/null +++ b/command/thing/thing_test.go @@ -0,0 +1,33 @@ +package thing + +import ( + "fmt" + "testing" + + iotclient "github.com/arduino/iot-client-go" +) + +func TestGetThingInfo(t *testing.T) { + thingTagsValid := &iotclient.ArduinoThing{Tags: map[string]interface{}{ + "location": "rome", + "room": "101", + }} + thingTagsNotValid := &iotclient.ArduinoThing{Tags: map[string]interface{}{ + "location": "rome", + "room": 101, + }} + + thing, err := getThingInfo(thingTagsValid) + if err != nil { + t.Error("unexpected error") + } + if len(thing.Tags) != 2 { + fmt.Println(len(thing.Tags)) + t.Error("expected two tags") + } + + _, err = getThingInfo(thingTagsNotValid) + if err == nil { + t.Error("an error was expected because tags are not valid") + } +} From 904df0fd854e063e14de45b3565eddb323c9d5ef Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 9 Nov 2021 18:16:49 +0100 Subject: [PATCH 2/7] Move tags extraction into proper function --- command/tag/tag.go | 19 +++++++++++++++++++ command/thing/thing.go | 13 ++++--------- command/thing/thing_test.go | 33 --------------------------------- 3 files changed, 23 insertions(+), 42 deletions(-) create mode 100644 command/tag/tag.go delete mode 100644 command/thing/thing_test.go diff --git a/command/tag/tag.go b/command/tag/tag.go new file mode 100644 index 00000000..4a7421f8 --- /dev/null +++ b/command/tag/tag.go @@ -0,0 +1,19 @@ +package tag + +import "fmt" + +type Tags map[string]interface{} + +// Info transforms tags into user-readable strings +// An error is returned if a tag value is not a string +func (t Tags) Info() ([]string, error) { + var str []string + for key, value := range t { + if valStr, ok := value.(string); ok { + str = append(str, key+": "+valStr) + } else { + return nil, fmt.Errorf("value of tag `%s` should be of type `string` but is of type `%T`", key, value) + } + } + return str, nil +} diff --git a/command/thing/thing.go b/command/thing/thing.go index 1f161ae9..bd82c92a 100644 --- a/command/thing/thing.go +++ b/command/thing/thing.go @@ -18,8 +18,7 @@ package thing import ( - "fmt" - + "github.com/arduino/arduino-cloud-cli/command/tag" iotclient "github.com/arduino/iot-client-go" ) @@ -40,13 +39,9 @@ func getThingInfo(thing *iotclient.ArduinoThing) (*ThingInfo, error) { vars = append(vars, p.Name) } // Retrieve thing tags - var tags []string - for key, value := range thing.Tags { - if valStr, ok := value.(string); ok { - tags = append(tags, key+": "+valStr) - } else { - return nil, fmt.Errorf("value of tag `%s` should be of type `string` but is of type `%T`", key, value) - } + tags, err := tag.Tags(thing.Tags).Info() + if err != nil { + return nil, err } info := &ThingInfo{ diff --git a/command/thing/thing_test.go b/command/thing/thing_test.go deleted file mode 100644 index 2e68a074..00000000 --- a/command/thing/thing_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package thing - -import ( - "fmt" - "testing" - - iotclient "github.com/arduino/iot-client-go" -) - -func TestGetThingInfo(t *testing.T) { - thingTagsValid := &iotclient.ArduinoThing{Tags: map[string]interface{}{ - "location": "rome", - "room": "101", - }} - thingTagsNotValid := &iotclient.ArduinoThing{Tags: map[string]interface{}{ - "location": "rome", - "room": 101, - }} - - thing, err := getThingInfo(thingTagsValid) - if err != nil { - t.Error("unexpected error") - } - if len(thing.Tags) != 2 { - fmt.Println(len(thing.Tags)) - t.Error("expected two tags") - } - - _, err = getThingInfo(thingTagsNotValid) - if err == nil { - t.Error("an error was expected because tags are not valid") - } -} From a1aeeadd765f4fe648b683054458ebef59c5814c Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 9 Nov 2021 18:27:04 +0100 Subject: [PATCH 3/7] Show device tags in list command --- cli/device/list.go | 4 +++- command/device/device.go | 52 ++++++++++++++++++++++++++++++++++++++++ command/device/list.go | 23 +++++------------- 3 files changed, 61 insertions(+), 18 deletions(-) create mode 100644 command/device/device.go diff --git a/cli/device/list.go b/cli/device/list.go index 9e7a1036..a7583bf6 100644 --- a/cli/device/list.go +++ b/cli/device/list.go @@ -19,6 +19,7 @@ package device import ( "os" + "strings" "github.com/arduino/arduino-cli/cli/errorcodes" "github.com/arduino/arduino-cli/cli/feedback" @@ -75,7 +76,7 @@ func (r listResult) String() string { return "No devices found." } t := table.New() - t.SetHeader("Name", "ID", "Board", "FQBN", "SerialNumber") + t.SetHeader("Name", "ID", "Board", "FQBN", "SerialNumber", "Tags") for _, device := range r.devices { t.AddRow( device.Name, @@ -83,6 +84,7 @@ func (r listResult) String() string { device.Board, device.FQBN, device.Serial, + strings.Join(device.Tags, ", "), ) } return t.Render() diff --git a/command/device/device.go b/command/device/device.go new file mode 100644 index 00000000..4deda93f --- /dev/null +++ b/command/device/device.go @@ -0,0 +1,52 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package device + +import ( + "github.com/arduino/arduino-cloud-cli/command/tag" + iotclient "github.com/arduino/iot-client-go" +) + +// DeviceInfo contains the most interesting +// parameters of an Arduino IoT Cloud device. +type DeviceInfo struct { + Name string `json:"name"` + ID string `json:"id"` + Board string `json:"board"` + Serial string `json:"serial-number"` + FQBN string `json:"fqbn"` + Tags []string `json:"tags,omitempty"` +} + +func getDeviceInfo(device *iotclient.ArduinoDevicev2) (*DeviceInfo, error) { + // Retrieve device tags + tags, err := tag.Tags(device.Tags).Info() + if err != nil { + return nil, err + } + + dev := &DeviceInfo{ + Name: device.Name, + ID: device.Id, + Board: device.Type, + Serial: device.Serial, + FQBN: device.Fqbn, + Tags: tags, + } + return dev, nil +} diff --git a/command/device/list.go b/command/device/list.go index cd707521..8889962d 100644 --- a/command/device/list.go +++ b/command/device/list.go @@ -18,20 +18,12 @@ package device import ( + "fmt" + "github.com/arduino/arduino-cloud-cli/internal/config" "github.com/arduino/arduino-cloud-cli/internal/iot" ) -// DeviceInfo contains the most interesting -// parameters of an Arduino IoT Cloud device. -type DeviceInfo struct { - Name string `json:"name"` - ID string `json:"id"` - Board string `json:"board"` - Serial string `json:"serial-number"` - FQBN string `json:"fqbn"` -} - // ListParams contains the optional parameters needed // to filter the devices to be listed. type ListParams struct { @@ -57,14 +49,11 @@ func List(params *ListParams) ([]DeviceInfo, error) { var devices []DeviceInfo for _, foundDev := range foundDevices { - dev := DeviceInfo{ - Name: foundDev.Name, - ID: foundDev.Id, - Board: foundDev.Type, - Serial: foundDev.Serial, - FQBN: foundDev.Fqbn, + dev, err := getDeviceInfo(&foundDev) + if err != nil { + return nil, fmt.Errorf("getting device %s from cloud: %w", foundDev.Id, err) } - devices = append(devices, dev) + devices = append(devices, *dev) } return devices, nil From 4ff8dd256c7ee9ad5ce532b0a29de90f4ea72a34 Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Mon, 15 Nov 2021 19:08:29 +0100 Subject: [PATCH 4/7] remove tags type introducing an exported function --- command/device/device.go | 2 +- command/tag/tag.go | 8 +++----- command/thing/thing.go | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/command/device/device.go b/command/device/device.go index 4deda93f..20678449 100644 --- a/command/device/device.go +++ b/command/device/device.go @@ -35,7 +35,7 @@ type DeviceInfo struct { func getDeviceInfo(device *iotclient.ArduinoDevicev2) (*DeviceInfo, error) { // Retrieve device tags - tags, err := tag.Tags(device.Tags).Info() + tags, err := tag.TagsInfo(device.Tags) if err != nil { return nil, err } diff --git a/command/tag/tag.go b/command/tag/tag.go index 4a7421f8..1c6d3983 100644 --- a/command/tag/tag.go +++ b/command/tag/tag.go @@ -2,13 +2,11 @@ package tag import "fmt" -type Tags map[string]interface{} - -// Info transforms tags into user-readable strings +// TagsInfo transforms tags into user-readable strings // An error is returned if a tag value is not a string -func (t Tags) Info() ([]string, error) { +func TagsInfo(tags map[string]interface{}) ([]string, error) { var str []string - for key, value := range t { + for key, value := range tags { if valStr, ok := value.(string); ok { str = append(str, key+": "+valStr) } else { diff --git a/command/thing/thing.go b/command/thing/thing.go index bd82c92a..df7be97b 100644 --- a/command/thing/thing.go +++ b/command/thing/thing.go @@ -39,7 +39,7 @@ func getThingInfo(thing *iotclient.ArduinoThing) (*ThingInfo, error) { vars = append(vars, p.Name) } // Retrieve thing tags - tags, err := tag.Tags(thing.Tags).Info() + tags, err := tag.TagsInfo(thing.Tags) if err != nil { return nil, err } From 5136228060986a07e9e3cee6a76b9f7a30691d0b Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Mon, 15 Nov 2021 19:31:47 +0100 Subject: [PATCH 5/7] Keep the happy path on the same level of indentation Co-authored-by: Giuseppe Lumia --- command/tag/tag.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/command/tag/tag.go b/command/tag/tag.go index 1c6d3983..343b5744 100644 --- a/command/tag/tag.go +++ b/command/tag/tag.go @@ -7,11 +7,11 @@ import "fmt" func TagsInfo(tags map[string]interface{}) ([]string, error) { var str []string for key, value := range tags { - if valStr, ok := value.(string); ok { - str = append(str, key+": "+valStr) - } else { + valStr, ok := value.(string) + if !ok { return nil, fmt.Errorf("value of tag `%s` should be of type `string` but is of type `%T`", key, value) } + str = append(str, key+": "+valStr) } return str, nil } From 0727673d2134afadead83ab1e30b3acabc159e8e Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Mon, 15 Nov 2021 19:42:31 +0100 Subject: [PATCH 6/7] Improvements from review --- command/device/list.go | 2 +- command/tag/tag.go | 17 +++++++++++++++++ command/thing/create.go | 2 +- command/thing/list.go | 2 +- command/thing/thing.go | 4 ++-- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/command/device/list.go b/command/device/list.go index 8889962d..9ace969e 100644 --- a/command/device/list.go +++ b/command/device/list.go @@ -51,7 +51,7 @@ func List(params *ListParams) ([]DeviceInfo, error) { for _, foundDev := range foundDevices { dev, err := getDeviceInfo(&foundDev) if err != nil { - return nil, fmt.Errorf("getting device %s from cloud: %w", foundDev.Id, err) + return nil, fmt.Errorf("parsing device %s from cloud: %w", foundDev.Id, err) } devices = append(devices, *dev) } diff --git a/command/tag/tag.go b/command/tag/tag.go index 343b5744..e219ec90 100644 --- a/command/tag/tag.go +++ b/command/tag/tag.go @@ -1,3 +1,20 @@ +// This file is part of arduino-cloud-cli. +// +// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/) +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + package tag import "fmt" diff --git a/command/thing/create.go b/command/thing/create.go index 68b867fa..a9572146 100644 --- a/command/thing/create.go +++ b/command/thing/create.go @@ -65,7 +65,7 @@ func Create(params *CreateParams) (*ThingInfo, error) { t, err := getThingInfo(newThing) if err != nil { - return nil, fmt.Errorf("getting the new thing %s from cloud: %w", newThing.Id, err) + return nil, fmt.Errorf("parsing the new thing %s from cloud: %w", newThing.Id, err) } return t, nil } diff --git a/command/thing/list.go b/command/thing/list.go index f5808a3c..9fc4a95b 100644 --- a/command/thing/list.go +++ b/command/thing/list.go @@ -54,7 +54,7 @@ func List(params *ListParams) ([]ThingInfo, error) { for _, foundThing := range foundThings { info, err := getThingInfo(&foundThing) if err != nil { - return nil, fmt.Errorf("getting thing %s from cloud: %w", foundThing.Id, err) + return nil, fmt.Errorf("parsing thing %s from cloud: %w", foundThing.Id, err) } things = append(things, *info) } diff --git a/command/thing/thing.go b/command/thing/thing.go index df7be97b..edbc9f6b 100644 --- a/command/thing/thing.go +++ b/command/thing/thing.go @@ -33,12 +33,12 @@ type ThingInfo struct { } func getThingInfo(thing *iotclient.ArduinoThing) (*ThingInfo, error) { - // Retrieve thing variables + // Process thing variables var vars []string for _, p := range thing.Properties { vars = append(vars, p.Name) } - // Retrieve thing tags + // Process thing tags tags, err := tag.TagsInfo(thing.Tags) if err != nil { return nil, err From cb495d32a4d249566ad2a44c78c12d44b007d0aa Mon Sep 17 00:00:00 2001 From: Paolo Calao Date: Tue, 16 Nov 2021 09:56:02 +0100 Subject: [PATCH 7/7] Change tags info format --- cli/device/list.go | 2 +- cli/thing/list.go | 2 +- command/tag/tag.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/device/list.go b/cli/device/list.go index a7583bf6..718f17f3 100644 --- a/cli/device/list.go +++ b/cli/device/list.go @@ -84,7 +84,7 @@ func (r listResult) String() string { device.Board, device.FQBN, device.Serial, - strings.Join(device.Tags, ", "), + strings.Join(device.Tags, ","), ) } return t.Render() diff --git a/cli/thing/list.go b/cli/thing/list.go index c0122c59..e81e3612 100644 --- a/cli/thing/list.go +++ b/cli/thing/list.go @@ -101,7 +101,7 @@ func (r result) String() string { for _, thing := range r.things { r := []interface{}{thing.Name, thing.ID, thing.DeviceID} - r = append(r, strings.Join(thing.Tags, ", ")) + r = append(r, strings.Join(thing.Tags, ",")) if listFlags.variables { r = append(r, strings.Join(thing.Variables, ", ")) } diff --git a/command/tag/tag.go b/command/tag/tag.go index e219ec90..319bb6b1 100644 --- a/command/tag/tag.go +++ b/command/tag/tag.go @@ -28,7 +28,7 @@ func TagsInfo(tags map[string]interface{}) ([]string, error) { if !ok { return nil, fmt.Errorf("value of tag `%s` should be of type `string` but is of type `%T`", key, value) } - str = append(str, key+": "+valStr) + str = append(str, key+"="+valStr) } return str, nil }