Skip to content

Commit 18dfa27

Browse files
committed
Thing create uses updated iot-client-go
Given that iot-client-go has been updated and now supports the creation of things with a slice of properties, it's been here employed to create a thing. Issue: response's error details, coming from arduino iot cloud, are masked by iot-client-go
1 parent 5365a3d commit 18dfa27

File tree

4 files changed

+59
-85
lines changed

4 files changed

+59
-85
lines changed

command/thing/create.go

+33-12
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import (
66
"io/ioutil"
77
"os"
88

9+
"errors"
10+
11+
iotclient "github.com/arduino/iot-client-go"
912
"github.com/arduino/iot-cloud-cli/internal/config"
1013
"github.com/arduino/iot-cloud-cli/internal/iot"
1114
)
@@ -37,7 +40,7 @@ func Create(params *CreateParams) (string, error) {
3740
return "", err
3841
}
3942

40-
var thing map[string]interface{}
43+
var thing *iotclient.Thing
4144

4245
if params.Clone != "" {
4346
thing, err = cloneThing(iotClient, params.Clone)
@@ -50,12 +53,15 @@ func Create(params *CreateParams) (string, error) {
5053
if err != nil {
5154
return "", err
5255
}
56+
57+
} else {
58+
return "", errors.New("provide either a thing(ID) to clone (--clone) or a thing template file (--template)")
5359
}
5460

55-
thing["name"] = params.Name
61+
thing.Name = params.Name
5662
force := true
5763
if params.Device != "" {
58-
thing["device_id"] = params.Device
64+
thing.DeviceId = params.Device
5965
}
6066
thingID, err := iotClient.AddThing(thing, force)
6167
if err != nil {
@@ -65,24 +71,39 @@ func Create(params *CreateParams) (string, error) {
6571
return thingID, nil
6672
}
6773

68-
func cloneThing(client iot.Client, thingID string) (map[string]interface{}, error) {
74+
func cloneThing(client iot.Client, thingID string) (*iotclient.Thing, error) {
6975
clone, err := client.GetThing(thingID)
7076
if err != nil {
7177
return nil, fmt.Errorf("%s: %w", "retrieving the thing to be cloned", err)
7278
}
7379

74-
thing := make(map[string]interface{})
80+
thing := &iotclient.Thing{}
81+
82+
// Copy device id
7583
if clone.DeviceId != "" {
76-
thing["device_id"] = clone.DeviceId
84+
thing.DeviceId = clone.DeviceId
7785
}
78-
if clone.Properties != nil {
79-
thing["properties"] = clone.Properties
86+
87+
// Copy properties
88+
for _, p := range clone.Properties {
89+
thing.Properties = append(thing.Properties, iotclient.Property{
90+
Name: p.Name,
91+
MinValue: p.MinValue,
92+
MaxValue: p.MaxValue,
93+
Permission: p.Permission,
94+
UpdateParameter: p.UpdateParameter,
95+
UpdateStrategy: p.UpdateStrategy,
96+
Type: p.Type,
97+
VariableName: p.VariableName,
98+
Persist: p.Persist,
99+
Tag: p.Tag,
100+
})
80101
}
81102

82103
return thing, nil
83104
}
84105

85-
func loadTemplate(file string) (map[string]interface{}, error) {
106+
func loadTemplate(file string) (*iotclient.Thing, error) {
86107
templateFile, err := os.Open(file)
87108
if err != nil {
88109
return nil, err
@@ -94,11 +115,11 @@ func loadTemplate(file string) (map[string]interface{}, error) {
94115
return nil, err
95116
}
96117

97-
var template map[string]interface{}
98-
err = json.Unmarshal([]byte(templateBytes), &template)
118+
thing := &iotclient.Thing{}
119+
err = json.Unmarshal([]byte(templateBytes), thing)
99120
if err != nil {
100121
return nil, fmt.Errorf("%s: %w", "reading template file: template not valid: ", err)
101122
}
102123

103-
return template, nil
124+
return thing, nil
104125
}

go.mod

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@ go 1.15
55
require (
66
github.com/arduino/arduino-cli v0.0.0-20210607095659-16f41352eac3
77
github.com/arduino/go-paths-helper v1.6.0
8-
github.com/arduino/iot-client-go v1.3.3
8+
github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1
99
github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6
1010
github.com/sirupsen/logrus v1.4.2
1111
github.com/spf13/cobra v1.1.3
1212
github.com/spf13/viper v1.7.0
1313
github.com/stretchr/testify v1.6.1
1414
go.bug.st/serial v1.3.0
15-
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 // indirect
16-
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
15+
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d // indirect
16+
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
1717
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 // indirect
18+
google.golang.org/appengine v1.6.7 // indirect
1819
google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450 // indirect
1920
google.golang.org/grpc v1.39.0
21+
google.golang.org/protobuf v1.27.1 // indirect
2022
)

go.sum

+10-8
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4l
5959
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b/go.mod h1:uwGy5PpN4lqW97FiLnbcx+xx8jly5YuPMJWfVwwjJiQ=
6060
github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b h1:3PjgYG5gVPA7cipp7vIR2lF96KkEJIFBJ+ANnuv6J20=
6161
github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b/go.mod h1:iIPnclBMYm1g32Q5kXoqng4jLhMStReIP7ZxaoUC2y8=
62-
github.com/arduino/iot-client-go v1.3.3 h1:W+92osS+WcdVpePdPmj/BtupM+xV6DOJlI0HGpKrTX4=
63-
github.com/arduino/iot-client-go v1.3.3/go.mod h1:gYvpMt7Qw+OSScTLyIlCnpbvy9y96ey/2zhB4w6FoK0=
62+
github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1 h1:tgVUBPbqkyd3KHTs+gweP5t9KAnkLbAsAMrHvu9jZSg=
63+
github.com/arduino/iot-client-go v1.3.4-0.20210824101852-4a44149473c1/go.mod h1:gYvpMt7Qw+OSScTLyIlCnpbvy9y96ey/2zhB4w6FoK0=
6464
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
6565
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
6666
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -494,15 +494,15 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
494494
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
495495
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
496496
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
497-
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125 h1:Ugb8sMTWuWRC3+sz5WeN/4kejDx9BvIwnPUiJBjJE+8=
498-
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
497+
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d h1:LO7XpTYMwTqxjLcGWPijK3vRXg1aWdlNOVOHRq45d7c=
498+
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
499499
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
500500
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
501501
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
502502
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
503503
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
504-
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 h1:3B43BWw0xEBsLZ/NO1VALz6fppU3481pik+2Ksv45z8=
505-
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
504+
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
505+
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
506506
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
507507
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
508508
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -638,8 +638,9 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
638638
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
639639
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
640640
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
641-
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
642641
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
642+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
643+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
643644
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
644645
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
645646
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -701,8 +702,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
701702
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
702703
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
703704
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
704-
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
705705
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
706+
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
707+
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
706708
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
707709
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
708710
gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

internal/iot/client.go

+11-62
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package iot
22

33
import (
4-
"bytes"
54
"context"
65
"encoding/json"
7-
"errors"
86
"fmt"
9-
"net/http"
10-
"strconv"
117

128
iotclient "github.com/arduino/iot-client-go"
139
)
@@ -18,7 +14,7 @@ type Client interface {
1814
DeleteDevice(id string) error
1915
ListDevices() ([]iotclient.ArduinoDevicev2, error)
2016
AddCertificate(id, csr string) (*iotclient.ArduinoCompressedv2, error)
21-
AddThing(thing interface{}, force bool) (string, error)
17+
AddThing(thing *iotclient.Thing, force bool) (string, error)
2218
GetThing(id string) (*iotclient.ArduinoThing, error)
2319
}
2420

@@ -96,68 +92,21 @@ func (cl *client) AddCertificate(id, csr string) (*iotclient.ArduinoCompressedv2
9692
return &newCert.Compressed, nil
9793
}
9894

99-
func (cl *client) AddThing(thing interface{}, force bool) (string, error) {
100-
// Request
101-
url := "https://api2.arduino.cc/iot/v2/things"
102-
bodyBuf := &bytes.Buffer{}
103-
err := json.NewEncoder(bodyBuf).Encode(thing)
95+
// AddThing adds a new thing on Arduino IoT Cloud.
96+
func (cl *client) AddThing(thing *iotclient.Thing, force bool) (string, error) {
97+
opt := &iotclient.ThingsV2CreateOpts{Force: optional.NewBool(force)}
98+
newThing, resp, err := cl.api.ThingsV2Api.ThingsV2Create(cl.ctx, *thing, opt)
10499
if err != nil {
105-
return "", err
106-
}
107-
if bodyBuf.Len() == 0 {
108-
err = errors.New("invalid body type")
109-
return "", err
110-
}
111-
112-
req, err := http.NewRequest(http.MethodPut, url, bodyBuf)
113-
if err != nil {
114-
return "", err
115-
}
116-
req.Header.Set("Content-Type", "application/json")
117-
118-
var bearer = "Bearer " + cl.ctx.Value(iotclient.ContextAccessToken).(string)
119-
req.Header.Add("Authorization", bearer)
120-
121-
q := req.URL.Query()
122-
q.Add("force", strconv.FormatBool(force))
123-
req.URL.RawQuery = q.Encode()
124-
125-
client := &http.Client{}
126-
resp, err := client.Do(req)
127-
if err != nil {
128-
err = fmt.Errorf("%s: %w", "adding new thing", err)
129-
return "", err
130-
}
131-
132-
// Response
133-
if resp.StatusCode != 201 {
134-
// Get response error detail
135100
var respObj map[string]interface{}
136-
err = json.NewDecoder(resp.Body).Decode(&respObj)
137-
if err != nil {
138-
return "", fmt.Errorf("%s: %s: %s", "cannot get response body", err, resp.Status)
139-
}
140-
return "", fmt.Errorf("%s: %s", "adding new thing", respObj["detail"].(string))
101+
json.NewDecoder(resp.Body).Decode(&respObj)
102+
resp.Body.Close()
103+
return "", fmt.Errorf("%s: %s: %v", "adding new thing", err, respObj)
141104
}
142-
143-
if resp.Body == nil {
144-
return "", errors.New("response body is empty")
145-
}
146-
defer resp.Body.Close()
147-
148-
var newThing map[string]interface{}
149-
err = json.NewDecoder(resp.Body).Decode(&newThing)
150-
if err != nil {
151-
err = fmt.Errorf("%s: %w", "cannot parse body response", err)
152-
return "", err
153-
}
154-
newID, ok := newThing["id"].(string)
155-
if !ok {
156-
return "", errors.New("adding new thing: new thing created: returned id is not available")
157-
}
158-
return newID, nil
105+
return newThing.Id, nil
159106
}
160107

108+
// GetThing allows to retrieve a specific thing, given its id,
109+
// from Arduino IoT Cloud.
161110
func (cl *client) GetThing(id string) (*iotclient.ArduinoThing, error) {
162111
thing, _, err := cl.api.ThingsV2Api.ThingsV2Show(cl.ctx, id, nil)
163112
if err != nil {

0 commit comments

Comments
 (0)