From 01df93b5103caac01cf4ddd58520f5ad6b39beef Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 6 May 2024 09:01:03 +0200 Subject: [PATCH 01/20] Report conflicted OTA requests (#151) * Print conflicted OTA in report * Refactored print error --- command/ota/upload.go | 21 +++++++++++++++++++-- internal/iot/client.go | 11 ++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/command/ota/upload.go b/command/ota/upload.go index fcb32267..a41cf462 100644 --- a/command/ota/upload.go +++ b/command/ota/upload.go @@ -19,6 +19,7 @@ package ota import ( "context" + "errors" "fmt" "os" "path/filepath" @@ -101,9 +102,18 @@ func Upload(ctx context.Context, params *UploadParams, cred *config.Credentials) expiration = otaDeferredExpirationMins } + var conflictedOta *otaapi.Ota err = iotClient.DeviceOTA(ctx, params.DeviceID, file, expiration) if err != nil { - return err + if errors.Is(err, iot.ErrOtaAlreadyInProgress) { + conflictedOta = &otaapi.Ota{ + DeviceID: params.DeviceID, + Status: "Skipped", + ErrorReason: "OTA already in progress", + } + } else { + return err + } } // Try to get ota-id from API otaID, err := otapi.GetOtaLastStatusByDeviceID(params.DeviceID) @@ -111,7 +121,14 @@ func Upload(ctx context.Context, params *UploadParams, cred *config.Credentials) return err } if otaID != nil && len(otaID.Ota) > 0 { - feedback.PrintResult(otaID.Ota[0]) + if conflictedOta != nil { + toPrint := otaapi.OtaStatusList{ + Ota: []otaapi.Ota{*conflictedOta, otaID.Ota[0]}, + } + feedback.PrintResult(toPrint) + } else { + feedback.PrintResult(otaID.Ota[0]) + } } return nil diff --git a/internal/iot/client.go b/internal/iot/client.go index 55170caa..1ea50544 100644 --- a/internal/iot/client.go +++ b/internal/iot/client.go @@ -28,6 +28,8 @@ import ( "golang.org/x/oauth2" ) +var ErrOtaAlreadyInProgress = fmt.Errorf("ota already in progress") + // Client can perform actions on Arduino IoT Cloud. type Client struct { api *iotclient.APIClient @@ -196,9 +198,12 @@ func (cl *Client) DeviceOTA(ctx context.Context, id string, file *os.File, expir Async: optional.NewBool(true), } resp, err := cl.api.DevicesV2OtaApi.DevicesV2OtaUpload(ctx, id, file, opt) - if err != nil && resp.StatusCode != 409 { // 409 (Conflict) is the status code for an already existing OTA for the same SHA/device, so ignoring it. - err = fmt.Errorf("uploading device ota: %w", errorDetail(err)) - return err + if err != nil { + // 409 (Conflict) is the status code for an already existing OTA in progress for the same device. Handling it in a different way. + if resp.StatusCode == 409 { + return ErrOtaAlreadyInProgress + } + return fmt.Errorf("uploading device ota: %w", errorDetail(err)) } return nil } From 4022ef6bef41c9bbca0fe6bb59878672de5148fb Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Fri, 10 May 2024 14:26:05 +0200 Subject: [PATCH 02/20] Adding more test coverage for mass upload use case (#152) * Fix variable overload * Adding test for ota file generation * Added test for no header generation * Code refactoring * Fix test --- command/ota/massupload.go | 44 ++++++++++++++++++++++----------- command/ota/massupload_test.go | 29 ++++++++++++++++++++++ command/ota/testdata/cloud.bin | Bin 0 -> 92112 bytes 3 files changed, 59 insertions(+), 14 deletions(-) create mode 100755 command/ota/testdata/cloud.bin diff --git a/command/ota/massupload.go b/command/ota/massupload.go index 0d81ae2e..150fe596 100644 --- a/command/ota/massupload.go +++ b/command/ota/massupload.go @@ -21,6 +21,7 @@ import ( "context" "errors" "fmt" + "github.com/sirupsen/logrus" "os" "path/filepath" @@ -54,6 +55,27 @@ type Result struct { OtaStatus otaapi.Ota } +func buildOtaFile(params *MassUploadParams) (string, string, error) { + var otaFile string + var otaDir string + var err error + if params.DoNotApplyHeader { + otaFile = params.File + } else { + otaDir, err = os.MkdirTemp("", "") + if err != nil { + return "", "", fmt.Errorf("%s: %w", "cannot create temporary folder", err) + } + otaFile = filepath.Join(otaDir, "temp.ota") + + err = Generate(params.File, otaFile, params.FQBN) + if err != nil { + return "", "", fmt.Errorf("%s: %w", "cannot generate .ota file", err) + } + } + return otaFile, otaDir, nil +} + // MassUpload command is used to mass upload a firmware OTA, // on devices of Arduino IoT Cloud. func MassUpload(ctx context.Context, params *MassUploadParams, cred *config.Credentials) ([]Result, error) { @@ -64,6 +86,7 @@ func MassUpload(ctx context.Context, params *MassUploadParams, cred *config.Cred } // Generate .ota file + logrus.Infoln("Uploading binary", params.File) _, err := os.Stat(params.File) if err != nil { return nil, fmt.Errorf("file %s does not exists: %w", params.File, err) @@ -78,21 +101,12 @@ func MassUpload(ctx context.Context, params *MassUploadParams, cred *config.Cred } // Generate .ota file - var otaFile string - if params.DoNotApplyHeader { - otaFile = params.File - } else { - otaDir, err := os.MkdirTemp("", "") - if err != nil { - return nil, fmt.Errorf("%s: %w", "cannot create temporary folder", err) - } - otaFile := filepath.Join(otaDir, "temp.ota") + otaFile, otaDir, err := buildOtaFile(params) + if err != nil { + return nil, err + } + if otaDir != "" { defer os.RemoveAll(otaDir) - - err = Generate(params.File, otaFile, params.FQBN) - if err != nil { - return nil, fmt.Errorf("%s: %w", "cannot generate .ota file", err) - } } iotClient, err := iot.NewClient(cred) @@ -196,6 +210,7 @@ func run(ctx context.Context, uploader otaUploader, otapi otaStatusGetter, ids [ for _, id := range ids { file, err := os.Open(otaFile) if err != nil { + logrus.Error("cannot open ota file:", otaFile) r := Result{ID: id, Err: fmt.Errorf("cannot open ota file")} results = append(results, r) continue @@ -205,6 +220,7 @@ func run(ctx context.Context, uploader otaUploader, otapi otaStatusGetter, ids [ } close(jobs) + logrus.Infoln("Uploading firmware to devices...") for i := 0; i < numConcurrentUploads; i++ { go func() { for job := range jobs { diff --git a/command/ota/massupload_test.go b/command/ota/massupload_test.go index bc2856da..476fa7c6 100644 --- a/command/ota/massupload_test.go +++ b/command/ota/massupload_test.go @@ -10,9 +10,11 @@ import ( otaapi "github.com/arduino/arduino-cloud-cli/internal/ota-api" iotclient "github.com/arduino/iot-client-go" "github.com/gofrs/uuid" + "github.com/stretchr/testify/assert" ) const testFilename = "testdata/empty.bin" +const cloudFirmwareFilename = "testdata/cloud.bin" type deviceUploaderTest struct { deviceOTA func(ctx context.Context, id string, file *os.File, expireMins int) error @@ -119,3 +121,30 @@ func TestValidateDevices(t *testing.T) { t.Errorf("expected 2 invalid devices, but found %d: %v", len(i), i) } } + +func TestValidateBuildOtaFile(t *testing.T) { + + file, tmp, err := buildOtaFile(&MassUploadParams{ + File: cloudFirmwareFilename, + DoNotApplyHeader: false, + FQBN: "arduino:samd:nano_33_iot", + }) + assert.Nil(t, err) + assert.NotNil(t, file) + assert.True(t, strings.HasSuffix(file, "temp.ota")) + assert.NotEmpty(t, tmp) + defer os.RemoveAll(tmp) +} + +func TestValidateBuildOtaFile_whenNoHeaderIsRequested(t *testing.T) { + + file, tmp, err := buildOtaFile(&MassUploadParams{ + File: cloudFirmwareFilename, + DoNotApplyHeader: true, + FQBN: "arduino:samd:nano_33_iot", + }) + assert.Nil(t, err) + assert.NotNil(t, file) + assert.Equal(t, cloudFirmwareFilename, file) + assert.Empty(t, tmp) +} diff --git a/command/ota/testdata/cloud.bin b/command/ota/testdata/cloud.bin new file mode 100755 index 0000000000000000000000000000000000000000..0bf2398affb51fafc93d4692d8770d6b7e00e182 GIT binary patch literal 92112 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%0AEaTl5wGMpz64FzW=zHbU)Qg_Q3X)`apY1 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 literal 0 HcmV?d00001 From e7a449561083fe7c784ff5bf1a451bdaa87c26b9 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Fri, 17 May 2024 09:09:44 +0200 Subject: [PATCH 03/20] OTA v2: support for cancel of an in-progess/pending ota (#153) * Introducing ota cancel command * Handling conflict --- cli/ota/cancel.go | 64 ++++++++++++++++++++++++++++++++++++++ cli/ota/ota.go | 1 + cli/ota/status.go | 4 +-- command/ota/cancel.go | 36 +++++++++++++++++++++ internal/ota-api/client.go | 39 ++++++++++++++++++++++- 5 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 cli/ota/cancel.go create mode 100644 command/ota/cancel.go diff --git a/cli/ota/cancel.go b/cli/ota/cancel.go new file mode 100644 index 00000000..6b1a4c53 --- /dev/null +++ b/cli/ota/cancel.go @@ -0,0 +1,64 @@ +// 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 ota + +import ( + "fmt" + "os" + + "github.com/arduino/arduino-cli/cli/errorcodes" + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-cloud-cli/command/ota" + "github.com/arduino/arduino-cloud-cli/config" + "github.com/spf13/cobra" +) + +type cancelFlags struct { + otaID string +} + +func initOtaCancelCommand() *cobra.Command { + flags := &cancelFlags{} + uploadCommand := &cobra.Command{ + Use: "cancel", + Short: "OTA cancel", + Long: "Cancel OTA by OTA ID", + Run: func(cmd *cobra.Command, args []string) { + if err := runOtaCancelCommand(flags); err != nil { + feedback.Errorf("Error during ota cancel: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + }, + } + uploadCommand.Flags().StringVarP(&flags.otaID, "ota-id", "o", "", "OTA ID") + + return uploadCommand +} + +func runOtaCancelCommand(flags *cancelFlags) error { + if flags.otaID == "" { + return fmt.Errorf("required flag \"ota-id\" not set") + } + + cred, err := config.RetrieveCredentials() + if err != nil { + return fmt.Errorf("retrieving credentials: %w", err) + } + + return ota.CancelOta(flags.otaID, cred) +} diff --git a/cli/ota/ota.go b/cli/ota/ota.go index b7a683a8..db4dbc01 100644 --- a/cli/ota/ota.go +++ b/cli/ota/ota.go @@ -33,6 +33,7 @@ func NewCommand() *cobra.Command { otaCommand.AddCommand(initOtaStatusCommand()) otaCommand.AddCommand(initEncodeBinaryCommand()) otaCommand.AddCommand(initDecodeHeaderCommand()) + otaCommand.AddCommand(initOtaCancelCommand()) return otaCommand } diff --git a/cli/ota/status.go b/cli/ota/status.go index ebcce353..89d67081 100644 --- a/cli/ota/status.go +++ b/cli/ota/status.go @@ -43,7 +43,7 @@ func initOtaStatusCommand() *cobra.Command { Short: "OTA status", Long: "Get OTA status by OTA or device ID", Run: func(cmd *cobra.Command, args []string) { - if err := runOtaStatusCommand(flags); err != nil { + if err := runPrintOtaStatusCommand(flags); err != nil { feedback.Errorf("Error during ota get status: %v", err) os.Exit(errorcodes.ErrGeneric) } @@ -58,7 +58,7 @@ func initOtaStatusCommand() *cobra.Command { return uploadCommand } -func runOtaStatusCommand(flags *statusFlags) error { +func runPrintOtaStatusCommand(flags *statusFlags) error { if flags.otaID == "" && flags.deviceId == "" && flags.otaIDs == "" { return fmt.Errorf("required flag(s) \"ota-id\" or \"device-id\" or \"ota-ids\" not set") } diff --git a/command/ota/cancel.go b/command/ota/cancel.go new file mode 100644 index 00000000..1c12feda --- /dev/null +++ b/command/ota/cancel.go @@ -0,0 +1,36 @@ +package ota + +import ( + "fmt" + + "github.com/arduino/arduino-cli/cli/feedback" + "github.com/arduino/arduino-cloud-cli/config" + otaapi "github.com/arduino/arduino-cloud-cli/internal/ota-api" +) + +func CancelOta(otaid string, cred *config.Credentials) error { + + if feedback.GetFormat() == feedback.JSONMini { + return fmt.Errorf("jsonmini format is not supported for this command") + } + + otapi := otaapi.NewClient(cred) + + if otaid != "" { + _, err := otapi.CancelOta(otaid) + if err != nil { + return err + } + // No error, get current status + res, err := otapi.GetOtaStatusByOtaID(otaid, 1, otaapi.OrderDesc) + if err != nil { + return err + } + if res != nil { + feedback.PrintResult(res.Ota) + } + return nil + } + + return nil +} diff --git a/internal/ota-api/client.go b/internal/ota-api/client.go index dc647406..1c4bd52f 100644 --- a/internal/ota-api/client.go +++ b/internal/ota-api/client.go @@ -38,6 +38,7 @@ const ( ) var ErrAlreadyInProgress = fmt.Errorf("already in progress") +var ErrAlreadyCancelled = fmt.Errorf("already cancelled") type OtaApiClient struct { client *http.Client @@ -58,7 +59,11 @@ func NewClient(credentials *config.Credentials) *OtaApiClient { } func (c *OtaApiClient) performGetRequest(endpoint, token string) (*http.Response, error) { - req, err := http.NewRequest("GET", endpoint, nil) + return c.performRequest(endpoint, "GET", token) +} + +func (c *OtaApiClient) performRequest(endpoint, method, token string) (*http.Response, error) { + req, err := http.NewRequest(method, endpoint, nil) if err != nil { return nil, err } @@ -205,3 +210,35 @@ func (c *OtaApiClient) GetOtaStatusByDeviceID(deviceID string, limit int, order return nil, err } + +func (c *OtaApiClient) CancelOta(otaid string) (bool, error) { + + if otaid == "" { + return false, fmt.Errorf("invalid ota-id: empty") + } + + userRequestToken, err := c.src.Token() + if err != nil { + if strings.Contains(err.Error(), "401") { + return false, errors.New("wrong credentials") + } + return false, fmt.Errorf("cannot retrieve a valid token: %w", err) + } + + endpoint := c.host + "/ota/v1/ota/" + otaid + "/cancel" + res, err := c.performRequest(endpoint, "PUT", userRequestToken.AccessToken) + if err != nil { + return false, err + } + defer res.Body.Close() + + if res.StatusCode == 200 { + return true, nil + } else if res.StatusCode == 404 || res.StatusCode == 400 { + return false, fmt.Errorf("ota-id %s not found", otaid) + } else if res.StatusCode == 409 { + return false, ErrAlreadyCancelled + } + + return false, err +} From 5aebb3d314736d88a7a63a9a453c6fb0d510b82b Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Fri, 21 Jun 2024 12:45:08 +0200 Subject: [PATCH 04/20] [WIRE-419] Update iot-client to latest generated artifact (#155) --- .../go/github.com/antihax/optional.dep.yml | 19 -- .../v2.dep.yml} | 6 +- .../github.com/golang/protobuf/jsonpb.dep.yml | 5 +- .../github.com/golang/protobuf/proto.dep.yml | 5 +- .../github.com/golang/protobuf/ptypes.dep.yml | 5 +- .../golang/protobuf/ptypes/any.dep.yml | 7 +- .../golang/protobuf/ptypes/duration.dep.yml | 7 +- .../golang/protobuf/ptypes/timestamp.dep.yml | 7 +- .licenses/go/golang.org/x/oauth2.dep.yml | 2 +- .../x/oauth2/clientcredentials.dep.yml | 4 +- .../go/golang.org/x/oauth2/internal.dep.yml | 4 +- .../protobuf/encoding/protojson.dep.yml | 6 +- .../protobuf/encoding/prototext.dep.yml | 6 +- .../protobuf/encoding/protowire.dep.yml | 6 +- .../protobuf/internal/descfmt.dep.yml | 6 +- .../protobuf/internal/descopts.dep.yml | 6 +- .../protobuf/internal/detrand.dep.yml | 6 +- .../protobuf/internal/editiondefaults.dep.yml | 63 +++++++ .../protobuf/internal/editionssupport.dep.yml | 62 ++++++ .../protobuf/internal/encoding/defval.dep.yml | 6 +- .../protobuf/internal/encoding/json.dep.yml | 8 +- .../internal/encoding/messageset.dep.yml | 6 +- .../protobuf/internal/encoding/tag.dep.yml | 6 +- .../protobuf/internal/encoding/text.dep.yml | 6 +- .../protobuf/internal/errors.dep.yml | 6 +- .../protobuf/internal/filedesc.dep.yml | 6 +- .../protobuf/internal/filetype.dep.yml | 6 +- .../protobuf/internal/flags.dep.yml | 6 +- .../protobuf/internal/genid.dep.yml | 6 +- .../protobuf/internal/impl.dep.yml | 8 +- .../protobuf/internal/order.dep.yml | 6 +- .../protobuf/internal/pragma.dep.yml | 6 +- .../protobuf/internal/set.dep.yml | 6 +- .../protobuf/internal/strs.dep.yml | 6 +- .../protobuf/internal/version.dep.yml | 6 +- .../google.golang.org/protobuf/proto.dep.yml | 6 +- .../protobuf/reflect/protodesc.dep.yml | 6 +- .../protobuf/reflect/protoreflect.dep.yml | 6 +- .../protobuf/reflect/protoregistry.dep.yml | 6 +- .../protobuf/runtime/protoiface.dep.yml | 6 +- .../protobuf/runtime/protoimpl.dep.yml | 6 +- .../protobuf/types/descriptorpb.dep.yml | 8 +- .../protobuf/types/gofeaturespb.dep.yml | 62 ++++++ .../protobuf/types/known/anypb.dep.yml | 6 +- .../protobuf/types/known/durationpb.dep.yml | 6 +- .../protobuf/types/known/timestamppb.dep.yml | 6 +- .../protobuf/types/known/wrapperspb.dep.yml | 8 +- cli/device/list.go | 10 +- command/dashboard/create.go | 4 +- command/dashboard/dashboard.go | 6 +- command/device/create.go | 9 +- command/device/creategeneric.go | 4 +- command/device/createlora.go | 4 +- command/device/device.go | 6 +- command/device/provision.go | 4 +- command/ota/generate.go | 7 + command/ota/massupload.go | 9 +- command/ota/massupload_test.go | 8 +- command/ota/upload.go | 2 +- command/thing/bind.go | 11 +- command/thing/clone.go | 39 +--- command/thing/create.go | 4 +- command/thing/thing.go | 4 +- go.mod | 16 +- go.sum | 39 ++-- internal/iot/client.go | 176 +++++++++++------- internal/iot/error.go | 4 +- internal/iot/token.go | 6 +- internal/template/dashboard.go | 4 +- internal/template/extract.go | 4 +- internal/template/load.go | 10 +- internal/template/load_test.go | 64 ++++--- 72 files changed, 559 insertions(+), 359 deletions(-) delete mode 100644 .licenses/go/github.com/antihax/optional.dep.yml rename .licenses/go/github.com/arduino/{iot-client-go.dep.yml => iot-client-go/v2.dep.yml} (85%) create mode 100644 .licenses/go/google.golang.org/protobuf/internal/editiondefaults.dep.yml create mode 100644 .licenses/go/google.golang.org/protobuf/internal/editionssupport.dep.yml create mode 100644 .licenses/go/google.golang.org/protobuf/types/gofeaturespb.dep.yml diff --git a/.licenses/go/github.com/antihax/optional.dep.yml b/.licenses/go/github.com/antihax/optional.dep.yml deleted file mode 100644 index fd41710c..00000000 --- a/.licenses/go/github.com/antihax/optional.dep.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: github.com/antihax/optional -version: v1.0.0 -type: go -summary: -homepage: https://pkg.go.dev/github.com/antihax/optional -license: mit -licenses: -- sources: LICENSE - text: | - The MIT License (MIT) - Copyright (c) 2016 Adam Hintz - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -notices: [] diff --git a/.licenses/go/github.com/arduino/iot-client-go.dep.yml b/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml similarity index 85% rename from .licenses/go/github.com/arduino/iot-client-go.dep.yml rename to .licenses/go/github.com/arduino/iot-client-go/v2.dep.yml index ae869df4..ed76f3c6 100644 --- a/.licenses/go/github.com/arduino/iot-client-go.dep.yml +++ b/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml @@ -1,9 +1,9 @@ --- -name: github.com/arduino/iot-client-go -version: v1.4.4 +name: github.com/arduino/iot-client-go/v2 +version: v2.0.2 type: go summary: -homepage: https://pkg.go.dev/github.com/arduino/iot-client-go +homepage: https://pkg.go.dev/github.com/arduino/iot-client-go/v2 license: apache-2.0 licenses: - sources: LICENSE diff --git a/.licenses/go/github.com/golang/protobuf/jsonpb.dep.yml b/.licenses/go/github.com/golang/protobuf/jsonpb.dep.yml index f31bd044..9bbd6dcb 100644 --- a/.licenses/go/github.com/golang/protobuf/jsonpb.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/jsonpb.dep.yml @@ -1,13 +1,13 @@ --- name: github.com/golang/protobuf/jsonpb -version: v1.5.3 +version: v1.5.4 type: go summary: Package jsonpb provides functionality to marshal and unmarshal between a protocol buffer message and JSON. homepage: https://pkg.go.dev/github.com/golang/protobuf/jsonpb license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -38,4 +38,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/github.com/golang/protobuf/proto.dep.yml b/.licenses/go/github.com/golang/protobuf/proto.dep.yml index cec24b9a..e8fc9149 100644 --- a/.licenses/go/github.com/golang/protobuf/proto.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/proto.dep.yml @@ -1,12 +1,12 @@ --- name: github.com/golang/protobuf/proto -version: v1.5.3 +version: v1.5.4 type: go summary: Package proto provides functionality for handling protocol buffer messages. homepage: https://pkg.go.dev/github.com/golang/protobuf/proto license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -37,4 +37,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/github.com/golang/protobuf/ptypes.dep.yml b/.licenses/go/github.com/golang/protobuf/ptypes.dep.yml index 61048e01..774b077c 100644 --- a/.licenses/go/github.com/golang/protobuf/ptypes.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/ptypes.dep.yml @@ -1,12 +1,12 @@ --- name: github.com/golang/protobuf/ptypes -version: v1.5.3 +version: v1.5.4 type: go summary: Package ptypes provides functionality for interacting with well-known types. homepage: https://pkg.go.dev/github.com/golang/protobuf/ptypes license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -37,4 +37,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/github.com/golang/protobuf/ptypes/any.dep.yml b/.licenses/go/github.com/golang/protobuf/ptypes/any.dep.yml index e964341f..9bb63a9b 100644 --- a/.licenses/go/github.com/golang/protobuf/ptypes/any.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/ptypes/any.dep.yml @@ -1,12 +1,12 @@ --- name: github.com/golang/protobuf/ptypes/any -version: v1.5.3 +version: v1.5.4 type: go -summary: +summary: homepage: https://pkg.go.dev/github.com/golang/protobuf/ptypes/any license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -37,4 +37,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/github.com/golang/protobuf/ptypes/duration.dep.yml b/.licenses/go/github.com/golang/protobuf/ptypes/duration.dep.yml index a2afe63a..b443330f 100644 --- a/.licenses/go/github.com/golang/protobuf/ptypes/duration.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/ptypes/duration.dep.yml @@ -1,12 +1,12 @@ --- name: github.com/golang/protobuf/ptypes/duration -version: v1.5.3 +version: v1.5.4 type: go -summary: +summary: homepage: https://pkg.go.dev/github.com/golang/protobuf/ptypes/duration license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -37,4 +37,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/github.com/golang/protobuf/ptypes/timestamp.dep.yml b/.licenses/go/github.com/golang/protobuf/ptypes/timestamp.dep.yml index 0eaf03df..f7c119e7 100644 --- a/.licenses/go/github.com/golang/protobuf/ptypes/timestamp.dep.yml +++ b/.licenses/go/github.com/golang/protobuf/ptypes/timestamp.dep.yml @@ -1,12 +1,12 @@ --- name: github.com/golang/protobuf/ptypes/timestamp -version: v1.5.3 +version: v1.5.4 type: go -summary: +summary: homepage: https://pkg.go.dev/github.com/golang/protobuf/ptypes/timestamp license: bsd-3-clause licenses: -- sources: protobuf@v1.5.3/LICENSE +- sources: protobuf@v1.5.4/LICENSE text: |+ Copyright 2010 The Go Authors. All rights reserved. @@ -37,4 +37,3 @@ licenses: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. notices: [] -... diff --git a/.licenses/go/golang.org/x/oauth2.dep.yml b/.licenses/go/golang.org/x/oauth2.dep.yml index fb3a4ba5..7be49c5b 100644 --- a/.licenses/go/golang.org/x/oauth2.dep.yml +++ b/.licenses/go/golang.org/x/oauth2.dep.yml @@ -1,6 +1,6 @@ --- name: golang.org/x/oauth2 -version: v0.14.0 +version: v0.21.0 type: go summary: Package oauth2 provides support for making OAuth2 authorized and authenticated HTTP requests, as specified in RFC 6749. diff --git a/.licenses/go/golang.org/x/oauth2/clientcredentials.dep.yml b/.licenses/go/golang.org/x/oauth2/clientcredentials.dep.yml index 15c89b78..f5cc2b56 100644 --- a/.licenses/go/golang.org/x/oauth2/clientcredentials.dep.yml +++ b/.licenses/go/golang.org/x/oauth2/clientcredentials.dep.yml @@ -1,13 +1,13 @@ --- name: golang.org/x/oauth2/clientcredentials -version: v0.14.0 +version: v0.21.0 type: go summary: Package clientcredentials implements the OAuth2.0 "client credentials" token flow, also known as the "two-legged OAuth 2.0". homepage: https://pkg.go.dev/golang.org/x/oauth2/clientcredentials license: bsd-3-clause licenses: -- sources: oauth2@v0.14.0/LICENSE +- sources: oauth2@v0.21.0/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. diff --git a/.licenses/go/golang.org/x/oauth2/internal.dep.yml b/.licenses/go/golang.org/x/oauth2/internal.dep.yml index 06346c35..4d1daff8 100644 --- a/.licenses/go/golang.org/x/oauth2/internal.dep.yml +++ b/.licenses/go/golang.org/x/oauth2/internal.dep.yml @@ -1,12 +1,12 @@ --- name: golang.org/x/oauth2/internal -version: v0.14.0 +version: v0.21.0 type: go summary: Package internal contains support packages for oauth2 package. homepage: https://pkg.go.dev/golang.org/x/oauth2/internal license: bsd-3-clause licenses: -- sources: oauth2@v0.14.0/LICENSE +- sources: oauth2@v0.21.0/LICENSE text: | Copyright (c) 2009 The Go Authors. All rights reserved. diff --git a/.licenses/go/google.golang.org/protobuf/encoding/protojson.dep.yml b/.licenses/go/google.golang.org/protobuf/encoding/protojson.dep.yml index 86cc6294..a4a3cd8a 100644 --- a/.licenses/go/google.golang.org/protobuf/encoding/protojson.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/encoding/protojson.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/encoding/protojson -version: v1.32.0 +version: v1.34.2 type: go summary: Package protojson marshals and unmarshals protocol buffer messages as JSON format. homepage: https://pkg.go.dev/google.golang.org/protobuf/encoding/protojson license: other licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/encoding/prototext.dep.yml b/.licenses/go/google.golang.org/protobuf/encoding/prototext.dep.yml index b291d739..3f009bc9 100644 --- a/.licenses/go/google.golang.org/protobuf/encoding/prototext.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/encoding/prototext.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/encoding/prototext -version: v1.32.0 +version: v1.34.2 type: go summary: Package prototext marshals and unmarshals protocol buffer messages as the textproto format. homepage: https://pkg.go.dev/google.golang.org/protobuf/encoding/prototext license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/encoding/protowire.dep.yml b/.licenses/go/google.golang.org/protobuf/encoding/protowire.dep.yml index 893bb78d..efd306fa 100644 --- a/.licenses/go/google.golang.org/protobuf/encoding/protowire.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/encoding/protowire.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/encoding/protowire -version: v1.32.0 +version: v1.34.2 type: go summary: Package protowire parses and formats the raw wire encoding. homepage: https://pkg.go.dev/google.golang.org/protobuf/encoding/protowire license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/descfmt.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/descfmt.dep.yml index b50a32e1..d062bbc7 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/descfmt.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/descfmt.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/descfmt -version: v1.32.0 +version: v1.34.2 type: go summary: Package descfmt provides functionality to format descriptors. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/descfmt license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/descopts.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/descopts.dep.yml index d05b2994..0cfeb15b 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/descopts.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/descopts.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/descopts -version: v1.32.0 +version: v1.34.2 type: go summary: Package descopts contains the nil pointers to concrete descriptor options. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/descopts license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/detrand.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/detrand.dep.yml index 8a8e4027..eb5cf0f4 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/detrand.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/detrand.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/detrand -version: v1.32.0 +version: v1.34.2 type: go summary: Package detrand provides deterministically random functionality. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/detrand license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/editiondefaults.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/editiondefaults.dep.yml new file mode 100644 index 00000000..d7fe6050 --- /dev/null +++ b/.licenses/go/google.golang.org/protobuf/internal/editiondefaults.dep.yml @@ -0,0 +1,63 @@ +--- +name: google.golang.org/protobuf/internal/editiondefaults +version: v1.34.2 +type: go +summary: Package editiondefaults contains the binary representation of the editions + defaults. +homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/editiondefaults +license: bsd-3-clause +licenses: +- sources: protobuf@v1.34.2/LICENSE + text: | + Copyright (c) 2018 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- sources: protobuf@v1.34.2/PATENTS + text: | + Additional IP Rights Grant (Patents) + + "This implementation" means the copyrightable works distributed by + Google as part of the Go project. + + Google hereby grants to You a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this section) + patent license to make, have made, use, offer to sell, sell, import, + transfer and otherwise run, modify and propagate the contents of this + implementation of Go, where such license applies only to those patent + claims, both currently owned or controlled by Google and acquired in + the future, licensable by Google that are necessarily infringed by this + implementation of Go. This grant does not include claims that would be + infringed only as a consequence of further modification of this + implementation. If you or your agent or exclusive licensee institute or + order or agree to the institution of patent litigation against any + entity (including a cross-claim or counterclaim in a lawsuit) alleging + that this implementation of Go or any code incorporated within this + implementation of Go constitutes direct or contributory patent + infringement, or inducement of patent infringement, then any patent + rights granted to you under this License for this implementation of Go + shall terminate as of the date such litigation is filed. +notices: [] diff --git a/.licenses/go/google.golang.org/protobuf/internal/editionssupport.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/editionssupport.dep.yml new file mode 100644 index 00000000..dcd28b8d --- /dev/null +++ b/.licenses/go/google.golang.org/protobuf/internal/editionssupport.dep.yml @@ -0,0 +1,62 @@ +--- +name: google.golang.org/protobuf/internal/editionssupport +version: v1.34.2 +type: go +summary: Package editionssupport defines constants for editions that are supported. +homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/editionssupport +license: bsd-3-clause +licenses: +- sources: protobuf@v1.34.2/LICENSE + text: | + Copyright (c) 2018 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- sources: protobuf@v1.34.2/PATENTS + text: | + Additional IP Rights Grant (Patents) + + "This implementation" means the copyrightable works distributed by + Google as part of the Go project. + + Google hereby grants to You a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this section) + patent license to make, have made, use, offer to sell, sell, import, + transfer and otherwise run, modify and propagate the contents of this + implementation of Go, where such license applies only to those patent + claims, both currently owned or controlled by Google and acquired in + the future, licensable by Google that are necessarily infringed by this + implementation of Go. This grant does not include claims that would be + infringed only as a consequence of further modification of this + implementation. If you or your agent or exclusive licensee institute or + order or agree to the institution of patent litigation against any + entity (including a cross-claim or counterclaim in a lawsuit) alleging + that this implementation of Go or any code incorporated within this + implementation of Go constitutes direct or contributory patent + infringement, or inducement of patent infringement, then any patent + rights granted to you under this License for this implementation of Go + shall terminate as of the date such litigation is filed. +notices: [] diff --git a/.licenses/go/google.golang.org/protobuf/internal/encoding/defval.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/encoding/defval.dep.yml index 3f5fa0aa..f8c57aec 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/encoding/defval.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/encoding/defval.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/encoding/defval -version: v1.32.0 +version: v1.34.2 type: go summary: Package defval marshals and unmarshals textual forms of default values. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/encoding/defval license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/encoding/json.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/encoding/json.dep.yml index 9b24c44a..aebd0e4d 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/encoding/json.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/encoding/json.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/encoding/json -version: v1.32.0 +version: v1.34.2 type: go -summary: +summary: homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/encoding/json license: other licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/encoding/messageset.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/encoding/messageset.dep.yml index 8a167b8a..de1ead0f 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/encoding/messageset.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/encoding/messageset.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/encoding/messageset -version: v1.32.0 +version: v1.34.2 type: go summary: Package messageset encodes and decodes the obsolete MessageSet wire format. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/encoding/messageset license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/encoding/tag.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/encoding/tag.dep.yml index 4285175c..cec632cd 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/encoding/tag.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/encoding/tag.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/internal/encoding/tag -version: v1.32.0 +version: v1.34.2 type: go summary: Package tag marshals and unmarshals the legacy struct tags as generated by historical versions of protoc-gen-go. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/encoding/tag license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/encoding/text.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/encoding/text.dep.yml index 1f1e6102..a5a5fe70 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/encoding/text.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/encoding/text.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/encoding/text -version: v1.32.0 +version: v1.34.2 type: go summary: Package text implements the text format for protocol buffers. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/encoding/text license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/errors.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/errors.dep.yml index 6aa38301..f8fac1a1 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/errors.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/errors.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/errors -version: v1.32.0 +version: v1.34.2 type: go summary: Package errors implements functions to manipulate errors. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/errors license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/filedesc.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/filedesc.dep.yml index d5a13a20..7bd85fc8 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/filedesc.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/filedesc.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/filedesc -version: v1.32.0 +version: v1.34.2 type: go summary: Package filedesc provides functionality for constructing descriptors. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/filedesc license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/filetype.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/filetype.dep.yml index 5ff68c1f..121eef33 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/filetype.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/filetype.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/internal/filetype -version: v1.32.0 +version: v1.34.2 type: go summary: Package filetype provides functionality for wrapping descriptors with Go type information. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/filetype license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/flags.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/flags.dep.yml index 4b9ed00f..07486178 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/flags.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/flags.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/flags -version: v1.32.0 +version: v1.34.2 type: go summary: Package flags provides a set of flags controlled by build tags. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/flags license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/genid.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/genid.dep.yml index 6c5e5e9b..41bc8146 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/genid.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/genid.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/internal/genid -version: v1.32.0 +version: v1.34.2 type: go summary: Package genid contains constants for declarations in descriptor.proto and the well-known types. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/genid license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/impl.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/impl.dep.yml index f6134647..b00e8269 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/impl.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/impl.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/impl -version: v1.32.0 +version: v1.34.2 type: go -summary: +summary: homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/impl license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/order.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/order.dep.yml index 82fc654d..f3a12858 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/order.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/order.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/order -version: v1.32.0 +version: v1.34.2 type: go summary: Package order provides ordered access to messages and maps. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/order license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/pragma.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/pragma.dep.yml index a433d6da..258bb73b 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/pragma.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/pragma.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/internal/pragma -version: v1.32.0 +version: v1.34.2 type: go summary: Package pragma provides types that can be embedded into a struct to statically enforce or prevent certain language properties. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/pragma license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/set.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/set.dep.yml index d6e02e54..795d843b 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/set.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/set.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/set -version: v1.32.0 +version: v1.34.2 type: go summary: Package set provides simple set data structures for uint64s. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/set license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/strs.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/strs.dep.yml index d8737a57..aa357fc0 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/strs.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/strs.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/strs -version: v1.32.0 +version: v1.34.2 type: go summary: Package strs provides string manipulation functionality specific to protobuf. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/strs license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/internal/version.dep.yml b/.licenses/go/google.golang.org/protobuf/internal/version.dep.yml index af54a63a..664f1909 100644 --- a/.licenses/go/google.golang.org/protobuf/internal/version.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/internal/version.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/internal/version -version: v1.32.0 +version: v1.34.2 type: go summary: Package version records versioning information about this module. homepage: https://pkg.go.dev/google.golang.org/protobuf/internal/version license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/proto.dep.yml b/.licenses/go/google.golang.org/protobuf/proto.dep.yml index b62bb6ab..4b37df11 100644 --- a/.licenses/go/google.golang.org/protobuf/proto.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/proto.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/proto -version: v1.32.0 +version: v1.34.2 type: go summary: Package proto provides functions operating on protocol buffer messages. homepage: https://pkg.go.dev/google.golang.org/protobuf/proto license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/reflect/protodesc.dep.yml b/.licenses/go/google.golang.org/protobuf/reflect/protodesc.dep.yml index 254023d3..eb89fd65 100644 --- a/.licenses/go/google.golang.org/protobuf/reflect/protodesc.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/reflect/protodesc.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/reflect/protodesc -version: v1.32.0 +version: v1.34.2 type: go summary: Package protodesc provides functionality for converting FileDescriptorProto messages to/from [protoreflect.FileDescriptor] values. homepage: https://pkg.go.dev/google.golang.org/protobuf/reflect/protodesc license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/reflect/protoreflect.dep.yml b/.licenses/go/google.golang.org/protobuf/reflect/protoreflect.dep.yml index 0f835606..104d1f33 100644 --- a/.licenses/go/google.golang.org/protobuf/reflect/protoreflect.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/reflect/protoreflect.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/reflect/protoreflect -version: v1.32.0 +version: v1.34.2 type: go summary: Package protoreflect provides interfaces to dynamically manipulate messages. homepage: https://pkg.go.dev/google.golang.org/protobuf/reflect/protoreflect license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/reflect/protoregistry.dep.yml b/.licenses/go/google.golang.org/protobuf/reflect/protoregistry.dep.yml index d65478d4..861ea2a3 100644 --- a/.licenses/go/google.golang.org/protobuf/reflect/protoregistry.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/reflect/protoregistry.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/reflect/protoregistry -version: v1.32.0 +version: v1.34.2 type: go summary: Package protoregistry provides data structures to register and lookup protobuf descriptor types. homepage: https://pkg.go.dev/google.golang.org/protobuf/reflect/protoregistry license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/runtime/protoiface.dep.yml b/.licenses/go/google.golang.org/protobuf/runtime/protoiface.dep.yml index 1d019473..0d2f7a83 100644 --- a/.licenses/go/google.golang.org/protobuf/runtime/protoiface.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/runtime/protoiface.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/runtime/protoiface -version: v1.32.0 +version: v1.34.2 type: go summary: Package protoiface contains types referenced or implemented by messages. homepage: https://pkg.go.dev/google.golang.org/protobuf/runtime/protoiface license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/runtime/protoimpl.dep.yml b/.licenses/go/google.golang.org/protobuf/runtime/protoimpl.dep.yml index ab4d7329..9574d512 100644 --- a/.licenses/go/google.golang.org/protobuf/runtime/protoimpl.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/runtime/protoimpl.dep.yml @@ -1,13 +1,13 @@ --- name: google.golang.org/protobuf/runtime/protoimpl -version: v1.32.0 +version: v1.34.2 type: go summary: Package protoimpl contains the default implementation for messages generated by protoc-gen-go. homepage: https://pkg.go.dev/google.golang.org/protobuf/runtime/protoimpl license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -36,7 +36,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/types/descriptorpb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/descriptorpb.dep.yml index e28d7d6e..56d8aeb4 100644 --- a/.licenses/go/google.golang.org/protobuf/types/descriptorpb.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/types/descriptorpb.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/types/descriptorpb -version: v1.32.0 +version: v1.34.2 type: go -summary: +summary: homepage: https://pkg.go.dev/google.golang.org/protobuf/types/descriptorpb license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/types/gofeaturespb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/gofeaturespb.dep.yml new file mode 100644 index 00000000..e4dceaf4 --- /dev/null +++ b/.licenses/go/google.golang.org/protobuf/types/gofeaturespb.dep.yml @@ -0,0 +1,62 @@ +--- +name: google.golang.org/protobuf/types/gofeaturespb +version: v1.34.2 +type: go +summary: +homepage: https://pkg.go.dev/google.golang.org/protobuf/types/gofeaturespb +license: bsd-3-clause +licenses: +- sources: protobuf@v1.34.2/LICENSE + text: | + Copyright (c) 2018 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- sources: protobuf@v1.34.2/PATENTS + text: | + Additional IP Rights Grant (Patents) + + "This implementation" means the copyrightable works distributed by + Google as part of the Go project. + + Google hereby grants to You a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this section) + patent license to make, have made, use, offer to sell, sell, import, + transfer and otherwise run, modify and propagate the contents of this + implementation of Go, where such license applies only to those patent + claims, both currently owned or controlled by Google and acquired in + the future, licensable by Google that are necessarily infringed by this + implementation of Go. This grant does not include claims that would be + infringed only as a consequence of further modification of this + implementation. If you or your agent or exclusive licensee institute or + order or agree to the institution of patent litigation against any + entity (including a cross-claim or counterclaim in a lawsuit) alleging + that this implementation of Go or any code incorporated within this + implementation of Go constitutes direct or contributory patent + infringement, or inducement of patent infringement, then any patent + rights granted to you under this License for this implementation of Go + shall terminate as of the date such litigation is filed. +notices: [] diff --git a/.licenses/go/google.golang.org/protobuf/types/known/anypb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/known/anypb.dep.yml index 96a2e4d6..b9c5ae4b 100644 --- a/.licenses/go/google.golang.org/protobuf/types/known/anypb.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/types/known/anypb.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/types/known/anypb -version: v1.32.0 +version: v1.34.2 type: go summary: Package anypb contains generated types for google/protobuf/any.proto. homepage: https://pkg.go.dev/google.golang.org/protobuf/types/known/anypb license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/types/known/durationpb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/known/durationpb.dep.yml index 94ba0200..03140396 100644 --- a/.licenses/go/google.golang.org/protobuf/types/known/durationpb.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/types/known/durationpb.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/types/known/durationpb -version: v1.32.0 +version: v1.34.2 type: go summary: Package durationpb contains generated types for google/protobuf/duration.proto. homepage: https://pkg.go.dev/google.golang.org/protobuf/types/known/durationpb license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/types/known/timestamppb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/known/timestamppb.dep.yml index d006164a..c896d5ec 100644 --- a/.licenses/go/google.golang.org/protobuf/types/known/timestamppb.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/types/known/timestamppb.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/types/known/timestamppb -version: v1.32.0 +version: v1.34.2 type: go summary: Package timestamppb contains generated types for google/protobuf/timestamp.proto. homepage: https://pkg.go.dev/google.golang.org/protobuf/types/known/timestamppb license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/.licenses/go/google.golang.org/protobuf/types/known/wrapperspb.dep.yml b/.licenses/go/google.golang.org/protobuf/types/known/wrapperspb.dep.yml index 8709d6b6..8177dc8d 100644 --- a/.licenses/go/google.golang.org/protobuf/types/known/wrapperspb.dep.yml +++ b/.licenses/go/google.golang.org/protobuf/types/known/wrapperspb.dep.yml @@ -1,12 +1,12 @@ --- name: google.golang.org/protobuf/types/known/wrapperspb -version: v1.32.0 +version: v1.34.2 type: go -summary: +summary: homepage: https://pkg.go.dev/google.golang.org/protobuf/types/known/wrapperspb license: bsd-3-clause licenses: -- sources: protobuf@v1.32.0/LICENSE +- sources: protobuf@v1.34.2/LICENSE text: | Copyright (c) 2018 The Go Authors. All rights reserved. @@ -35,7 +35,7 @@ licenses: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -- sources: protobuf@v1.32.0/PATENTS +- sources: protobuf@v1.34.2/PATENTS text: | Additional IP Rights Grant (Patents) diff --git a/cli/device/list.go b/cli/device/list.go index a853dfc7..d7799a0d 100644 --- a/cli/device/list.go +++ b/cli/device/list.go @@ -92,7 +92,7 @@ func (r listResult) String() string { return "No devices found." } t := table.New() - t.SetHeader("Name", "ID", "Board", "FQBN", "SerialNumber", "Tags") + t.SetHeader("Name", "ID", "Board", "FQBN", "SerialNumber", "Status", "Tags") for _, device := range r.devices { t.AddRow( device.Name, @@ -100,8 +100,16 @@ func (r listResult) String() string { device.Board, device.FQBN, device.Serial, + dereferenceString(device.Status), strings.Join(device.Tags, ","), ) } return t.Render() } + +func dereferenceString(s *string) string { + if s == nil { + return "" + } + return *s +} diff --git a/command/dashboard/create.go b/command/dashboard/create.go index 0aab64cb..2fc78620 100644 --- a/command/dashboard/create.go +++ b/command/dashboard/create.go @@ -47,10 +47,10 @@ func Create(ctx context.Context, params *CreateParams, cred *config.Credentials) // Name passed as parameter has priority over name from template if params.Name != nil { - dashboard.Name = *params.Name + dashboard.Name = params.Name } // If name is not specified in the template, it should be passed as parameter - if dashboard.Name == "" { + if dashboard.Name == nil || *dashboard.Name == "" { return nil, errors.New("dashboard name not specified") } diff --git a/command/dashboard/dashboard.go b/command/dashboard/dashboard.go index b1da267b..431fb6b4 100644 --- a/command/dashboard/dashboard.go +++ b/command/dashboard/dashboard.go @@ -18,7 +18,7 @@ package dashboard import ( - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) // DashboardInfo contains the most interesting @@ -33,7 +33,9 @@ type DashboardInfo struct { func getDashboardInfo(dashboard *iotclient.ArduinoDashboardv2) *DashboardInfo { var widgets []string for _, w := range dashboard.Widgets { - widgets = append(widgets, w.Name) + if w.Name != nil { + widgets = append(widgets, *w.Name) + } } info := &DashboardInfo{ Name: dashboard.Name, diff --git a/command/device/create.go b/command/device/create.go index 33d5cd30..cabc9996 100644 --- a/command/device/create.go +++ b/command/device/create.go @@ -100,7 +100,14 @@ func Create(ctx context.Context, params *CreateParams, cred *config.Credentials) ID: dev.Id, Board: dev.Type, Serial: dev.Serial, - FQBN: dev.Fqbn, + FQBN: dereferenceString(dev.Fqbn), } return devInfo, nil } + +func dereferenceString(s *string) string { + if s == nil { + return "" + } + return *s +} diff --git a/command/device/creategeneric.go b/command/device/creategeneric.go index f0ebbe0b..fa34dcea 100644 --- a/command/device/creategeneric.go +++ b/command/device/creategeneric.go @@ -75,9 +75,9 @@ func CreateGeneric(ctx context.Context, params *CreateGenericParams, cred *confi ID: dev.Id, Board: dev.Type, Serial: dev.Serial, - FQBN: dev.Fqbn, + FQBN: dereferenceString(dev.Fqbn), }, - Password: pass.SuggestedPassword, + Password: dereferenceString(pass.SuggestedPassword), } return devInfo, nil } diff --git a/command/device/createlora.go b/command/device/createlora.go index 821d5018..06542bad 100644 --- a/command/device/createlora.go +++ b/command/device/createlora.go @@ -26,7 +26,7 @@ import ( "github.com/arduino/arduino-cloud-cli/arduino/cli" "github.com/arduino/arduino-cloud-cli/config" "github.com/arduino/arduino-cloud-cli/internal/iot" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "github.com/sirupsen/logrus" "go.bug.st/serial" ) @@ -181,7 +181,7 @@ func getDeviceLoraInfo(ctx context.Context, iotClient *iot.Client, loraDev *iotc ID: dev.Id, Board: dev.Type, Serial: dev.Serial, - FQBN: dev.Fqbn, + FQBN: dereferenceString(dev.Fqbn), }, AppEUI: loraDev.AppEui, AppKey: loraDev.AppKey, diff --git a/command/device/device.go b/command/device/device.go index f965e016..832f1116 100644 --- a/command/device/device.go +++ b/command/device/device.go @@ -19,7 +19,7 @@ package device import ( "github.com/arduino/arduino-cloud-cli/command/tag" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) // DeviceInfo contains the most interesting @@ -31,6 +31,7 @@ type DeviceInfo struct { Serial string `json:"serial_number"` FQBN string `json:"fqbn"` Tags []string `json:"tags,omitempty"` + Status *string `json:"status,omitempty"` } func getDeviceInfo(device *iotclient.ArduinoDevicev2) (*DeviceInfo, error) { @@ -45,8 +46,9 @@ func getDeviceInfo(device *iotclient.ArduinoDevicev2) (*DeviceInfo, error) { ID: device.Id, Board: device.Type, Serial: device.Serial, - FQBN: device.Fqbn, + FQBN: dereferenceString(device.Fqbn), Tags: tags, + Status: device.DeviceStatus, } return dev, nil } diff --git a/command/device/provision.go b/command/device/provision.go index 58781a33..2515cb3a 100644 --- a/command/device/provision.go +++ b/command/device/provision.go @@ -29,7 +29,7 @@ import ( "github.com/arduino/arduino-cloud-cli/internal/binary" "github.com/arduino/arduino-cloud-cli/internal/serial" "github.com/arduino/go-paths-helper" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "github.com/sirupsen/logrus" ) @@ -185,7 +185,7 @@ func (p provision) configBoard(ctx context.Context) error { } logrus.Info("Sending certificate authority key") - b, err = hex.DecodeString(cert.AuthorityKeyIdentifier) + b, err = hex.DecodeString(dereferenceString(cert.AuthorityKeyIdentifier)) if err != nil { return fmt.Errorf("decoding certificate authority key id: %w", err) } diff --git a/command/ota/generate.go b/command/ota/generate.go index 2c01051c..50663222 100644 --- a/command/ota/generate.go +++ b/command/ota/generate.go @@ -68,3 +68,10 @@ func Generate(binFile string, outFile string, fqbn string) error { return nil } + +func dereferenceString(s *string) string { + if s == nil { + return "" + } + return *s +} diff --git a/command/ota/massupload.go b/command/ota/massupload.go index 150fe596..1fbffa38 100644 --- a/command/ota/massupload.go +++ b/command/ota/massupload.go @@ -21,16 +21,17 @@ import ( "context" "errors" "fmt" - "github.com/sirupsen/logrus" "os" "path/filepath" + "github.com/sirupsen/logrus" + "github.com/arduino/arduino-cloud-cli/config" "github.com/arduino/arduino-cloud-cli/internal/iot" "github.com/arduino/arduino-cloud-cli/internal/ota" otaapi "github.com/arduino/arduino-cloud-cli/internal/ota-api" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) const ( @@ -179,8 +180,8 @@ func validateDevices(ctx context.Context, lister deviceLister, ids []string, fqb continue } // Device FQBN doesn't match the passed one - if found.Fqbn != fqbn { - inv := Result{ID: id, Err: fmt.Errorf("has FQBN '%s' instead of '%s'", found.Fqbn, fqbn)} + if dereferenceString(found.Fqbn) != fqbn { + inv := Result{ID: id, Err: fmt.Errorf("has FQBN '%s' instead of '%s'", dereferenceString(found.Fqbn), fqbn)} invalid = append(invalid, inv) continue } diff --git a/command/ota/massupload_test.go b/command/ota/massupload_test.go index 476fa7c6..981b18e6 100644 --- a/command/ota/massupload_test.go +++ b/command/ota/massupload_test.go @@ -8,7 +8,7 @@ import ( "testing" otaapi "github.com/arduino/arduino-cloud-cli/internal/ota-api" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "github.com/gofrs/uuid" "github.com/stretchr/testify/assert" ) @@ -96,9 +96,9 @@ func TestValidateDevices(t *testing.T) { mockDeviceList := deviceListerTest{ list: []iotclient.ArduinoDevicev2{ - {Id: idCorrect1, Fqbn: correctFQBN}, - {Id: idCorrect2, Fqbn: correctFQBN}, - {Id: idNotValid, Fqbn: wrongFQBN}, + {Id: idCorrect1, Fqbn: &correctFQBN}, + {Id: idCorrect2, Fqbn: &correctFQBN}, + {Id: idNotValid, Fqbn: &wrongFQBN}, }, } diff --git a/command/ota/upload.go b/command/ota/upload.go index a41cf462..f9e7424f 100644 --- a/command/ota/upload.go +++ b/command/ota/upload.go @@ -85,7 +85,7 @@ func Upload(ctx context.Context, params *UploadParams, cred *config.Credentials) otaFile = filepath.Join(otaDir, "temp.ota") defer os.RemoveAll(otaDir) - err = Generate(params.File, otaFile, dev.Fqbn) + err = Generate(params.File, otaFile, dereferenceString(dev.Fqbn)) if err != nil { return fmt.Errorf("%s: %w", "cannot generate .ota file", err) } diff --git a/command/thing/bind.go b/command/thing/bind.go index c21f0961..c6ee8983 100644 --- a/command/thing/bind.go +++ b/command/thing/bind.go @@ -23,7 +23,7 @@ import ( "github.com/arduino/arduino-cloud-cli/config" "github.com/arduino/arduino-cloud-cli/internal/iot" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) // BindParams contains the parameters needed to @@ -42,7 +42,7 @@ func Bind(ctx context.Context, params *BindParams, cred *config.Credentials) err } thing := &iotclient.ThingUpdate{ - DeviceId: params.DeviceID, + DeviceId: ¶ms.DeviceID, } err = iotClient.ThingUpdate(ctx, params.ID, thing, true) @@ -52,3 +52,10 @@ func Bind(ctx context.Context, params *BindParams, cred *config.Credentials) err return nil } + +func dereferenceString(s *string) string { + if s == nil { + return "" + } + return *s +} diff --git a/command/thing/clone.go b/command/thing/clone.go index b22e6ae9..e1fec588 100644 --- a/command/thing/clone.go +++ b/command/thing/clone.go @@ -23,7 +23,6 @@ import ( "github.com/arduino/arduino-cloud-cli/config" "github.com/arduino/arduino-cloud-cli/internal/iot" - iotclient "github.com/arduino/iot-client-go" ) // CloneParams contains the parameters needed to clone a thing. @@ -39,16 +38,9 @@ func Clone(ctx context.Context, params *CloneParams, cred *config.Credentials) ( return nil, err } - thing, err := retrieve(ctx, iotClient, params.CloneID) + newThing, err := iotClient.ThingClone(ctx, params.CloneID, params.Name) if err != nil { - return nil, err - } - - thing.Name = params.Name - force := true - newThing, err := iotClient.ThingCreate(ctx, thing, force) - if err != nil { - return nil, err + return nil, fmt.Errorf("cloning thing %s: %w", params.CloneID, err) } t, err := getThingInfo(newThing) @@ -57,30 +49,3 @@ func Clone(ctx context.Context, params *CloneParams, cred *config.Credentials) ( } return t, nil } - -type thingFetcher interface { - ThingShow(ctx context.Context, id string) (*iotclient.ArduinoThing, error) -} - -func retrieve(ctx context.Context, fetcher thingFetcher, thingID string) (*iotclient.ThingCreate, error) { - clone, err := fetcher.ThingShow(ctx, thingID) - if err != nil { - return nil, fmt.Errorf("%s: %w", "retrieving the thing to be cloned", err) - } - - thing := &iotclient.ThingCreate{} - - // Copy variables - for _, p := range clone.Properties { - thing.Properties = append(thing.Properties, iotclient.Property{ - Name: p.Name, - Permission: p.Permission, - UpdateParameter: p.UpdateParameter, - UpdateStrategy: p.UpdateStrategy, - Type: p.Type, - VariableName: p.VariableName, - }) - } - - return thing, nil -} diff --git a/command/thing/create.go b/command/thing/create.go index a0e447be..849d34ef 100644 --- a/command/thing/create.go +++ b/command/thing/create.go @@ -47,10 +47,10 @@ func Create(ctx context.Context, params *CreateParams, cred *config.Credentials) // Name passed as parameter has priority over name from template if params.Name != nil { - thing.Name = *params.Name + thing.Name = params.Name } // If name is not specified in the template, it should be passed as parameter - if thing.Name == "" { + if dereferenceString(thing.Name) == "" { return nil, errors.New("thing name not specified") } diff --git a/command/thing/thing.go b/command/thing/thing.go index 16bc7817..8b10defb 100644 --- a/command/thing/thing.go +++ b/command/thing/thing.go @@ -19,7 +19,7 @@ package thing import ( "github.com/arduino/arduino-cloud-cli/command/tag" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) // ThingInfo contains the main parameters of @@ -47,7 +47,7 @@ func getThingInfo(thing *iotclient.ArduinoThing) (*ThingInfo, error) { info := &ThingInfo{ Name: thing.Name, ID: thing.Id, - DeviceID: thing.DeviceId, + DeviceID: dereferenceString(thing.DeviceId), Variables: vars, Tags: tags, } diff --git a/go.mod b/go.mod index 6718765a..f4e84138 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,23 @@ module github.com/arduino/arduino-cloud-cli go 1.19 require ( - github.com/antihax/optional v1.0.0 github.com/arduino/arduino-cli v0.0.0-20221116144942-76251df9241a github.com/arduino/go-paths-helper v1.7.0 github.com/arduino/go-win32-utils v1.0.0 - github.com/arduino/iot-client-go v1.4.4 + github.com/arduino/iot-client-go/v2 v2.0.2 github.com/gofrs/uuid v4.2.0+incompatible github.com/google/go-cmp v0.6.0 github.com/howeyc/crc16 v0.0.0-20171223171357-2b2a61e366a6 + github.com/icza/bitio v1.1.0 github.com/manifoldco/promptui v0.9.0 github.com/sirupsen/logrus v1.9.0 github.com/spf13/cobra v1.3.0 github.com/spf13/viper v1.10.1 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.9.0 go.bug.st/cleanup v1.0.0 go.bug.st/serial v1.3.3 golang.org/x/crypto v0.18.0 - golang.org/x/oauth2 v0.14.0 + golang.org/x/oauth2 v0.21.0 google.golang.org/grpc v1.61.0 gopkg.in/yaml.v3 v3.0.1 gotest.tools v2.2.0+incompatible @@ -40,10 +40,9 @@ require ( github.com/fluxio/iohelpers v0.0.0-20160419043813-3a4dd67a94d2 // indirect github.com/fluxio/multierror v0.0.0-20160419044231-9c68d39025e5 // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/h2non/filetype v1.1.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect - github.com/icza/bitio v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -71,7 +70,7 @@ require ( github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/src-d/gcfg v1.4.0 // indirect - github.com/stretchr/objx v0.4.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect go.bug.st/downloader/v2 v2.1.1 // indirect @@ -82,9 +81,8 @@ require ( golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect - google.golang.org/protobuf v1.32.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.66.2 // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/src-d/go-git.v4 v4.13.1 // indirect diff --git a/go.sum b/go.sum index 9759e937..64155a38 100644 --- a/go.sum +++ b/go.sum @@ -61,7 +61,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/arduino/arduino-cli v0.0.0-20221116144942-76251df9241a h1:jbGxP4VJXFXJGYJgA9p5kcJweLNOcaz5tXTgFjaEiRE= github.com/arduino/arduino-cli v0.0.0-20221116144942-76251df9241a/go.mod h1:pIrqM9m4MCRyMUhguneLOkPxvNydFfup8qZqgjIFcTg= @@ -75,8 +74,8 @@ github.com/arduino/go-properties-orderedmap v1.7.1 h1:HQ9Pn/mk3+XyfrE39EEvaZwJkr github.com/arduino/go-properties-orderedmap v1.7.1/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= github.com/arduino/go-win32-utils v1.0.0 h1:/cXB86sOJxOsCHP7sQmXGLkdValwJt56mIwOHYxgQjQ= github.com/arduino/go-win32-utils v1.0.0/go.mod h1:0jqM7doGEAs6DaJCxxhLBUDS5OawrqF48HqXkcEie/Q= -github.com/arduino/iot-client-go v1.4.4 h1:FICCXD5uCZ0scGG6RioOlpZamDqgSD1l/fQlFwKR284= -github.com/arduino/iot-client-go v1.4.4/go.mod h1:gYvpMt7Qw+OSScTLyIlCnpbvy9y96ey/2zhB4w6FoK0= +github.com/arduino/iot-client-go/v2 v2.0.2 h1:5Zh8ZtZ+O8WBlvXvbA8Sla18Fvaw+QdQsz0X7DK4XLU= +github.com/arduino/iot-client-go/v2 v2.0.2/go.mod h1:kwX4B2AVEWl5ug94QbQ087xbvLFa9Co//jhbM/Z2eSQ= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= @@ -199,8 +198,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -287,6 +286,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/icza/bitio v1.1.0 h1:ysX4vtldjdi3Ygai5m1cWy4oLkhWTAi+SyO6HC8L9T0= github.com/icza/bitio v1.1.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A= +github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= @@ -447,17 +447,16 @@ github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jW github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -469,7 +468,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.bug.st/cleanup v1.0.0 h1:XVj1HZxkBXeq3gMT7ijWUpHyIC1j8XAoNSyQ06CskgA= go.bug.st/cleanup v1.0.0/go.mod h1:EqVmTg2IBk4znLbPD28xne3abjsJftMdqqJEjhn70bk= go.bug.st/downloader/v2 v2.1.1 h1:nyqbUizo3E2IxCCm4YFac4FtSqqFpqWP+Aae5GCMuw4= @@ -507,7 +505,6 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= @@ -547,7 +544,6 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -593,7 +589,6 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -613,8 +608,8 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -626,7 +621,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -696,13 +690,10 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -713,7 +704,6 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -775,7 +765,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -821,8 +810,6 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -931,8 +918,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/iot/client.go b/internal/iot/client.go index 1ea50544..4025c4c5 100644 --- a/internal/iot/client.go +++ b/internal/iot/client.go @@ -22,9 +22,8 @@ import ( "fmt" "os" - "github.com/antihax/optional" "github.com/arduino/arduino-cloud-cli/config" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "golang.org/x/oauth2" ) @@ -48,6 +47,10 @@ func NewClient(cred *config.Credentials) (*Client, error) { return cl, nil } +func toStringPointer(s string) *string { + return &s +} + // DeviceCreate allows to create a new device on Arduino IoT Cloud. // It returns the newly created device, and an error. func (cl *Client) DeviceCreate(ctx context.Context, fqbn, name, serial, dType string, cType *string) (*iotclient.ArduinoDevicev2, error) { @@ -57,22 +60,24 @@ func (cl *Client) DeviceCreate(ctx context.Context, fqbn, name, serial, dType st } payload := iotclient.CreateDevicesV2Payload{ - Fqbn: fqbn, - Name: name, - Serial: serial, + Fqbn: toStringPointer(fqbn), + Name: toStringPointer(name), + Serial: toStringPointer(serial), Type: dType, } if cType != nil { - payload.ConnectionType = *cType + payload.ConnectionType = cType } - dev, _, err := cl.api.DevicesV2Api.DevicesV2Create(ctx, payload, nil) + req := cl.api.DevicesV2Api.DevicesV2Create(ctx) + req = req.CreateDevicesV2Payload(payload) + dev, _, err := cl.api.DevicesV2Api.DevicesV2CreateExecute(req) if err != nil { err = fmt.Errorf("creating device, %w", errorDetail(err)) return nil, err } - return &dev, nil + return dev, nil } // DeviceLoraCreate allows to create a new LoRa device on Arduino IoT Cloud. @@ -88,17 +93,19 @@ func (cl *Client) DeviceLoraCreate(ctx context.Context, name, serial, devType, e Eui: eui, FrequencyPlan: freq, Name: name, - Serial: serial, + Serial: toStringPointer(serial), Type: devType, UserId: "me", } - dev, _, err := cl.api.LoraDevicesV1Api.LoraDevicesV1Create(ctx, payload, nil) + req := cl.api.LoraDevicesV1Api.LoraDevicesV1Create(ctx) + req = req.CreateLoraDevicesV1Payload(payload) + dev, _, err := cl.api.LoraDevicesV1Api.LoraDevicesV1CreateExecute(req) if err != nil { err = fmt.Errorf("creating lora device: %w", errorDetail(err)) return nil, err } - return &dev, nil + return dev, nil } // DevicePassSet sets the device password to the one suggested by Arduino IoT Cloud. @@ -110,21 +117,23 @@ func (cl *Client) DevicePassSet(ctx context.Context, id string) (*iotclient.Ardu } // Fetch suggested password - opts := &iotclient.DevicesV2PassGetOpts{SuggestedPassword: optional.NewBool(true)} - pass, _, err := cl.api.DevicesV2PassApi.DevicesV2PassGet(ctx, id, opts) + req := cl.api.DevicesV2PassApi.DevicesV2PassGet(ctx, id) + req = req.SuggestedPassword(true) + pass, _, err := cl.api.DevicesV2PassApi.DevicesV2PassGetExecute(req) if err != nil { err = fmt.Errorf("fetching device suggested password: %w", errorDetail(err)) return nil, err } // Set password to the suggested one - p := iotclient.Devicev2Pass{Password: pass.SuggestedPassword} - pass, _, err = cl.api.DevicesV2PassApi.DevicesV2PassSet(ctx, id, p) + reqSet := cl.api.DevicesV2PassApi.DevicesV2PassSet(ctx, id) + reqSet = reqSet.Devicev2Pass(iotclient.Devicev2Pass{Password: pass.SuggestedPassword}) + pass, _, err = cl.api.DevicesV2PassApi.DevicesV2PassSetExecute(reqSet) if err != nil { err = fmt.Errorf("setting device password: %w", errorDetail(err)) return nil, err } - return &pass, nil + return pass, nil } // DeviceDelete deletes the device corresponding to the passed ID @@ -135,7 +144,8 @@ func (cl *Client) DeviceDelete(ctx context.Context, id string) error { return err } - _, err = cl.api.DevicesV2Api.DevicesV2Delete(ctx, id, nil) + req := cl.api.DevicesV2Api.DevicesV2Delete(ctx, id) + _, err = cl.api.DevicesV2Api.DevicesV2DeleteExecute(req) if err != nil { err = fmt.Errorf("deleting device: %w", errorDetail(err)) return err @@ -151,17 +161,16 @@ func (cl *Client) DeviceList(ctx context.Context, tags map[string]string) ([]iot return nil, err } - opts := &iotclient.DevicesV2ListOpts{} + req := cl.api.DevicesV2Api.DevicesV2List(ctx) if tags != nil { t := make([]string, 0, len(tags)) for key, val := range tags { // Use the 'key:value' format required from the backend t = append(t, key+":"+val) } - opts.Tags = optional.NewInterface(t) + req = req.Tags(t) } - - devices, _, err := cl.api.DevicesV2Api.DevicesV2List(ctx, opts) + devices, _, err := cl.api.DevicesV2Api.DevicesV2ListExecute(req) if err != nil { err = fmt.Errorf("listing devices: %w", errorDetail(err)) return nil, err @@ -177,12 +186,13 @@ func (cl *Client) DeviceShow(ctx context.Context, id string) (*iotclient.Arduino return nil, err } - dev, _, err := cl.api.DevicesV2Api.DevicesV2Show(ctx, id, nil) + req := cl.api.DevicesV2Api.DevicesV2Show(ctx, id) + dev, _, err := cl.api.DevicesV2Api.DevicesV2ShowExecute(req) if err != nil { err = fmt.Errorf("retrieving device, %w", errorDetail(err)) return nil, err } - return &dev, nil + return dev, nil } // DeviceOTA performs an OTA upload request to Arduino IoT Cloud, passing @@ -193,14 +203,14 @@ func (cl *Client) DeviceOTA(ctx context.Context, id string, file *os.File, expir return err } - opt := &iotclient.DevicesV2OtaUploadOpts{ - ExpireInMins: optional.NewInt32(int32(expireMins)), - Async: optional.NewBool(true), - } - resp, err := cl.api.DevicesV2OtaApi.DevicesV2OtaUpload(ctx, id, file, opt) + req := cl.api.DevicesV2OtaApi.DevicesV2OtaUpload(ctx, id) + req = req.ExpireInMins(int32(expireMins)) + req = req.Async(true) + req = req.OtaFile(file) + _, resp, err := cl.api.DevicesV2OtaApi.DevicesV2OtaUploadExecute(req) if err != nil { // 409 (Conflict) is the status code for an already existing OTA in progress for the same device. Handling it in a different way. - if resp.StatusCode == 409 { + if resp != nil && resp.StatusCode == 409 { return ErrOtaAlreadyInProgress } return fmt.Errorf("uploading device ota: %w", errorDetail(err)) @@ -216,8 +226,9 @@ func (cl *Client) DeviceTagsCreate(ctx context.Context, id string, tags map[stri } for key, val := range tags { - t := iotclient.Tag{Key: key, Value: val} - _, err := cl.api.DevicesV2TagsApi.DevicesV2TagsUpsert(ctx, id, t) + req := cl.api.DevicesV2TagsApi.DevicesV2TagsUpsert(ctx, id) + req = req.Tag(iotclient.Tag{Key: key, Value: val}) + _, err := cl.api.DevicesV2TagsApi.DevicesV2TagsUpsertExecute(req) if err != nil { err = fmt.Errorf("cannot create tag %s: %w", key, errorDetail(err)) return err @@ -235,7 +246,8 @@ func (cl *Client) DeviceTagsDelete(ctx context.Context, id string, keys []string } for _, key := range keys { - _, err := cl.api.DevicesV2TagsApi.DevicesV2TagsDelete(ctx, id, key) + req := cl.api.DevicesV2TagsApi.DevicesV2TagsDelete(ctx, id, key) + _, err := cl.api.DevicesV2TagsApi.DevicesV2TagsDeleteExecute(req) if err != nil { err = fmt.Errorf("cannot delete tag %s: %w", key, errorDetail(err)) return err @@ -252,7 +264,8 @@ func (cl *Client) LoraFrequencyPlansList(ctx context.Context) ([]iotclient.Ardui return nil, err } - freqs, _, err := cl.api.LoraFreqPlanV1Api.LoraFreqPlanV1List(ctx) + req := cl.api.LoraFreqPlanV1Api.LoraFreqPlanV1List(ctx) + freqs, _, err := cl.api.LoraFreqPlanV1Api.LoraFreqPlanV1ListExecute(req) if err != nil { err = fmt.Errorf("listing lora frequency plans: %w", errorDetail(err)) return nil, err @@ -269,12 +282,14 @@ func (cl *Client) CertificateCreate(ctx context.Context, id, csr string) (*iotcl } cert := iotclient.CreateDevicesV2CertsPayload{ - Ca: "Arduino", + Ca: toStringPointer("Arduino"), Csr: csr, Enabled: true, } - newCert, _, err := cl.api.DevicesV2CertsApi.DevicesV2CertsCreate(ctx, id, cert) + req := cl.api.DevicesV2CertsApi.DevicesV2CertsCreate(ctx, id) + req = req.CreateDevicesV2CertsPayload(cert) + newCert, _, err := cl.api.DevicesV2CertsApi.DevicesV2CertsCreateExecute(req) if err != nil { err = fmt.Errorf("creating certificate, %w", errorDetail(err)) return nil, err @@ -290,12 +305,14 @@ func (cl *Client) ThingCreate(ctx context.Context, thing *iotclient.ThingCreate, return nil, err } - opt := &iotclient.ThingsV2CreateOpts{Force: optional.NewBool(force)} - newThing, _, err := cl.api.ThingsV2Api.ThingsV2Create(ctx, *thing, opt) + req := cl.api.ThingsV2Api.ThingsV2Create(ctx) + req = req.ThingCreate(*thing) + req = req.Force(force) + newThing, _, err := cl.api.ThingsV2Api.ThingsV2CreateExecute(req) if err != nil { return nil, fmt.Errorf("%s: %w", "adding new thing", errorDetail(err)) } - return &newThing, nil + return newThing, nil } // ThingUpdate updates a thing on Arduino IoT Cloud. @@ -305,8 +322,10 @@ func (cl *Client) ThingUpdate(ctx context.Context, id string, thing *iotclient.T return err } - opt := &iotclient.ThingsV2UpdateOpts{Force: optional.NewBool(force)} - _, _, err = cl.api.ThingsV2Api.ThingsV2Update(ctx, id, *thing, opt) + req := cl.api.ThingsV2Api.ThingsV2Update(ctx, id) + req = req.Force(force) + req = req.ThingUpdate(*thing) + _, _, err = cl.api.ThingsV2Api.ThingsV2UpdateExecute(req) if err != nil { return fmt.Errorf("%s: %v", "updating thing", errorDetail(err)) } @@ -320,7 +339,8 @@ func (cl *Client) ThingDelete(ctx context.Context, id string) error { return err } - _, err = cl.api.ThingsV2Api.ThingsV2Delete(ctx, id, nil) + req := cl.api.ThingsV2Api.ThingsV2Delete(ctx, id) + _, err = cl.api.ThingsV2Api.ThingsV2DeleteExecute(req) if err != nil { err = fmt.Errorf("deleting thing: %w", errorDetail(err)) return err @@ -328,20 +348,36 @@ func (cl *Client) ThingDelete(ctx context.Context, id string) error { return nil } -// ThingShow allows to retrieve a specific thing, given its id, -// from Arduino IoT Cloud. +// ThingShow allows to retrieve a specific thing, given its id from Arduino IoT Cloud. func (cl *Client) ThingShow(ctx context.Context, id string) (*iotclient.ArduinoThing, error) { ctx, err := ctxWithToken(ctx, cl.token) if err != nil { return nil, err } - thing, _, err := cl.api.ThingsV2Api.ThingsV2Show(ctx, id, nil) + req := cl.api.ThingsV2Api.ThingsV2Show(ctx, id) + thing, _, err := cl.api.ThingsV2Api.ThingsV2ShowExecute(req) + if err != nil { + return nil, fmt.Errorf("retrieving thing, %w", errorDetail(err)) + } + return thing, nil +} + +// ThingClone allows to clone a specific thing, given its id from Arduino IoT Cloud. +func (cl *Client) ThingClone(ctx context.Context, id, newName string) (*iotclient.ArduinoThing, error) { + ctx, err := ctxWithToken(ctx, cl.token) if err != nil { - err = fmt.Errorf("retrieving thing, %w", errorDetail(err)) return nil, err } - return &thing, nil + + req := cl.api.ThingsV2Api.ThingsV2Clone(ctx, id) + includeTags := true + req = req.ThingClone(iotclient.ThingClone{Name: newName, IncludeTags: &includeTags}) + thing, _, err := cl.api.ThingsV2Api.ThingsV2CloneExecute(req) + if err != nil { + return nil, fmt.Errorf("cloning thing thing, %w", errorDetail(err)) + } + return thing, nil } // ThingList returns a list of things on Arduino IoT Cloud. @@ -351,15 +387,14 @@ func (cl *Client) ThingList(ctx context.Context, ids []string, device *string, p return nil, err } - opts := &iotclient.ThingsV2ListOpts{} - opts.ShowProperties = optional.NewBool(props) - + req := cl.api.ThingsV2Api.ThingsV2List(ctx) + req = req.ShowProperties(props) if ids != nil { - opts.Ids = optional.NewInterface(ids) + req = req.Ids(ids) } if device != nil { - opts.DeviceId = optional.NewString(*device) + req = req.DeviceId(*device) } if tags != nil { @@ -368,10 +403,9 @@ func (cl *Client) ThingList(ctx context.Context, ids []string, device *string, p // Use the 'key:value' format required from the backend t = append(t, key+":"+val) } - opts.Tags = optional.NewInterface(t) + req = req.Tags(t) } - - things, _, err := cl.api.ThingsV2Api.ThingsV2List(ctx, opts) + things, _, err := cl.api.ThingsV2Api.ThingsV2ListExecute(req) if err != nil { err = fmt.Errorf("retrieving things, %w", errorDetail(err)) return nil, err @@ -387,8 +421,9 @@ func (cl *Client) ThingTagsCreate(ctx context.Context, id string, tags map[strin } for key, val := range tags { - t := iotclient.Tag{Key: key, Value: val} - _, err := cl.api.ThingsV2TagsApi.ThingsV2TagsUpsert(ctx, id, t) + req := cl.api.ThingsV2TagsApi.ThingsV2TagsUpsert(ctx, id) + req = req.Tag(iotclient.Tag{Key: key, Value: val}) + _, err := cl.api.ThingsV2TagsApi.ThingsV2TagsUpsertExecute(req) if err != nil { err = fmt.Errorf("cannot create tag %s: %w", key, errorDetail(err)) return err @@ -406,7 +441,8 @@ func (cl *Client) ThingTagsDelete(ctx context.Context, id string, keys []string) } for _, key := range keys { - _, err := cl.api.ThingsV2TagsApi.ThingsV2TagsDelete(ctx, id, key) + req := cl.api.ThingsV2TagsApi.ThingsV2TagsDelete(ctx, id, key) + _, err := cl.api.ThingsV2TagsApi.ThingsV2TagsDeleteExecute(req) if err != nil { err = fmt.Errorf("cannot delete tag %s: %w", key, errorDetail(err)) return err @@ -422,11 +458,13 @@ func (cl *Client) DashboardCreate(ctx context.Context, dashboard *iotclient.Dash return nil, err } - newDashboard, _, err := cl.api.DashboardsV2Api.DashboardsV2Create(ctx, *dashboard, nil) + req := cl.api.DashboardsV2Api.DashboardsV2Create(ctx) + req = req.Dashboardv2(*dashboard) + newDashboard, _, err := cl.api.DashboardsV2Api.DashboardsV2CreateExecute(req) if err != nil { return nil, fmt.Errorf("%s: %w", "adding new dashboard", errorDetail(err)) } - return &newDashboard, nil + return newDashboard, nil } // DashboardShow allows to retrieve a specific dashboard, given its id, @@ -437,12 +475,13 @@ func (cl *Client) DashboardShow(ctx context.Context, id string) (*iotclient.Ardu return nil, err } - dashboard, _, err := cl.api.DashboardsV2Api.DashboardsV2Show(ctx, id, nil) + req := cl.api.DashboardsV2Api.DashboardsV2Show(ctx, id) + dashboard, _, err := cl.api.DashboardsV2Api.DashboardsV2ShowExecute(req) if err != nil { err = fmt.Errorf("retrieving dashboard, %w", errorDetail(err)) return nil, err } - return &dashboard, nil + return dashboard, nil } // DashboardList returns a list of dashboards on Arduino IoT Cloud. @@ -452,7 +491,8 @@ func (cl *Client) DashboardList(ctx context.Context) ([]iotclient.ArduinoDashboa return nil, err } - dashboards, _, err := cl.api.DashboardsV2Api.DashboardsV2List(ctx, nil) + req := cl.api.DashboardsV2Api.DashboardsV2List(ctx) + dashboards, _, err := cl.api.DashboardsV2Api.DashboardsV2ListExecute(req) if err != nil { err = fmt.Errorf("listing dashboards: %w", errorDetail(err)) return nil, err @@ -467,7 +507,8 @@ func (cl *Client) DashboardDelete(ctx context.Context, id string) error { return err } - _, err = cl.api.DashboardsV2Api.DashboardsV2Delete(ctx, id, nil) + req := cl.api.DashboardsV2Api.DashboardsV2Delete(ctx, id) + _, err = cl.api.DashboardsV2Api.DashboardsV2DeleteExecute(req) if err != nil { err = fmt.Errorf("deleting dashboard: %w", errorDetail(err)) return err @@ -483,9 +524,14 @@ func (cl *Client) setup(client, secret, organization string) error { config := iotclient.NewConfiguration() if organization != "" { - config.DefaultHeader = map[string]string{"X-Organization": organization} + config.AddDefaultHeader("X-Organization", organization) + } + config.Servers = iotclient.ServerConfigurations{ + { + URL: fmt.Sprintf("%s/iot", baseURL), + Description: "IoT API endpoint", + }, } - config.BasePath = baseURL + "/iot" cl.api = iotclient.NewAPIClient(config) return nil diff --git a/internal/iot/error.go b/internal/iot/error.go index 00c205de..e26de626 100644 --- a/internal/iot/error.go +++ b/internal/iot/error.go @@ -21,7 +21,7 @@ import ( "encoding/json" "fmt" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) // errorDetail takes a generic iot-client-go error @@ -34,7 +34,7 @@ func errorDetail(err error) error { modErr, ok := apiErr.Model().(iotclient.ModelError) if ok { - return fmt.Errorf("%w: %s", err, modErr.Detail) + return fmt.Errorf("%w: %s", err, modErr.GetDetail()) } body := make(map[string]interface{}) diff --git a/internal/iot/token.go b/internal/iot/token.go index 717ef342..ebca7536 100644 --- a/internal/iot/token.go +++ b/internal/iot/token.go @@ -25,7 +25,7 @@ import ( "os" "strings" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "golang.org/x/oauth2" cc "golang.org/x/oauth2/clientcredentials" ) @@ -58,12 +58,12 @@ func NewUserTokenSource(client, secret, baseURL string) oauth2.TokenSource { func ctxWithToken(ctx context.Context, src oauth2.TokenSource) (context.Context, error) { // Retrieve a valid token from the src. - tok, err := src.Token() + _, err := src.Token() if err != nil { if strings.Contains(err.Error(), "401") { return nil, errors.New("wrong credentials") } return nil, fmt.Errorf("cannot retrieve a valid token: %w", err) } - return context.WithValue(ctx, iotclient.ContextAccessToken, tok.AccessToken), nil + return context.WithValue(ctx, iotclient.ContextOAuth2, src), nil } diff --git a/internal/template/dashboard.go b/internal/template/dashboard.go index aeace762..eae04243 100644 --- a/internal/template/dashboard.go +++ b/internal/template/dashboard.go @@ -22,7 +22,7 @@ import ( "encoding/json" "fmt" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" ) type dashboardTemplate struct { @@ -76,7 +76,7 @@ func getVariableID(ctx context.Context, thingID string, variableName string, fet } for _, v := range thing.Properties { - if v.VariableName == variableName { + if v.VariableName != nil && *v.VariableName == variableName { return v.Id, nil } } diff --git a/internal/template/extract.go b/internal/template/extract.go index 843a37cc..ecbe3163 100644 --- a/internal/template/extract.go +++ b/internal/template/extract.go @@ -24,7 +24,7 @@ import ( "io/ioutil" "os" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "gopkg.in/yaml.v3" ) @@ -67,7 +67,7 @@ func FromDashboard(dashboard *iotclient.ArduinoDashboardv2) map[string]interface widget["x"] = w.X widget["y"] = w.Y - if w.WidthMobile != 0 && w.HeightMobile != 0 { + if w.WidthMobile != nil && w.HeightMobile != nil && *w.WidthMobile != 0 && *w.HeightMobile != 0 { widget["width_mobile"] = w.WidthMobile widget["height_mobile"] = w.HeightMobile widget["x_mobile"] = w.XMobile diff --git a/internal/template/load.go b/internal/template/load.go index eca76933..6086c25d 100644 --- a/internal/template/load.go +++ b/internal/template/load.go @@ -22,10 +22,10 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "os" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "github.com/gofrs/uuid" "gopkg.in/yaml.v3" ) @@ -41,7 +41,7 @@ func loadTemplate(file string, template interface{}) error { } defer templateFile.Close() - templateBytes, err := ioutil.ReadAll(templateFile) + templateBytes, err := io.ReadAll(templateFile) if err != nil { return err } @@ -111,9 +111,13 @@ func LoadDashboard(ctx context.Context, file string, override map[string]string, // Set the correct variable id, given the thing id and the variable name for j, variable := range widget.Variables { // Check if thing name should be overridden + precheck := variable.ThingID if id, ok := override[variable.ThingID]; ok { variable.ThingID = id } + if variable.ThingID == "" || variable.ThingID == precheck { + return nil, fmt.Errorf("no override provided for thing %s", precheck) + } variable.VariableID, err = getVariableID(ctx, variable.ThingID, variable.VariableName, thinger) if err != nil { return nil, err diff --git a/internal/template/load_test.go b/internal/template/load_test.go index 1d7cecc6..705dc0d1 100644 --- a/internal/template/load_test.go +++ b/internal/template/load_test.go @@ -21,7 +21,7 @@ import ( "context" "testing" - iotclient "github.com/arduino/iot-client-go" + iotclient "github.com/arduino/iot-client-go/v2" "github.com/gofrs/uuid" "github.com/google/go-cmp/cmp" ) @@ -33,10 +33,19 @@ const ( relayID = "relay-id" blinkSpeedID = "blink_speed-id" - thingOverriddenID = "thing-overridden-id" - switchyOverriddenID = "switchy-overridden-id" + thingOverriddenID = "thing-overridden-id" + thingRemoteControlOverriddenID = "remote-controlled-lights-overridden-id" + switchyOverriddenID = "switchy-overridden-id" ) +func toStringPointer(s string) *string { + return &s +} + +func toInt64Pointer(i int64) *int64 { + return &i +} + var ( dashboardTemplateTest = map[string]interface{}{ "id": "home-security-alarm-dashboard", @@ -55,53 +64,53 @@ var ( } dashboardDetailed = &iotclient.Dashboardv2{ - Name: "dashboard", + Name: toStringPointer("dashboard"), Widgets: []iotclient.Widget{ - {Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4, - X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true}, + {Name: toStringPointer("Switch-name"), Height: 1, HeightMobile: toInt64Pointer(2), Width: 3, WidthMobile: toInt64Pointer(4), + X: 5, XMobile: toInt64Pointer(6), Y: 7, YMobile: toInt64Pointer(8), Options: map[string]interface{}{"showLabels": true}, Type: "Switch", }, }, } dashboardNoOptions = &iotclient.Dashboardv2{ - Name: "dashboard-no-options", + Name: toStringPointer("dashboard-no-options"), Widgets: []iotclient.Widget{ - {Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4, - X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{}, + {Name: toStringPointer("Switch-name"), Height: 1, HeightMobile: toInt64Pointer(2), Width: 3, WidthMobile: toInt64Pointer(4), + X: 5, XMobile: toInt64Pointer(6), Y: 7, YMobile: toInt64Pointer(8), Options: map[string]interface{}{}, Type: "Switch", }, }, } dashboardWithVariable = &iotclient.Dashboardv2{ - Name: "dashboard-with-variable", + Name: toStringPointer("dashboard-with-variable"), Widgets: []iotclient.Widget{ - {Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4, - X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true}, Type: "Switch", + {Name: toStringPointer("Switch-name"), Height: 1, HeightMobile: toInt64Pointer(2), Width: 3, WidthMobile: toInt64Pointer(4), + X: 5, XMobile: toInt64Pointer(6), Y: 7, YMobile: toInt64Pointer(8), Options: map[string]interface{}{"showLabels": true}, Type: "Switch", Variables: []string{switchyID}, }, }, } dashboardVariableOverride = &iotclient.Dashboardv2{ - Name: "dashboard-with-variable", + Name: toStringPointer("dashboard-with-variable"), Widgets: []iotclient.Widget{ - {Name: "Switch-name", Height: 1, HeightMobile: 2, Width: 3, WidthMobile: 4, - X: 5, XMobile: 6, Y: 7, YMobile: 8, Options: map[string]interface{}{"showLabels": true}, Type: "Switch", + {Name: toStringPointer("Switch-name"), Height: 1, HeightMobile: toInt64Pointer(2), Width: 3, WidthMobile: toInt64Pointer(4), + X: 5, XMobile: toInt64Pointer(6), Y: 7, YMobile: toInt64Pointer(8), Options: map[string]interface{}{"showLabels": true}, Type: "Switch", Variables: []string{switchyOverriddenID}, }, }, } dashboardTwoWidgets = &iotclient.Dashboardv2{ - Name: "dashboard-two-widgets", + Name: toStringPointer("dashboard-two-widgets"), Widgets: []iotclient.Widget{ - {Name: "blink_speed", Height: 7, Width: 8, + {Name: toStringPointer("blink_speed"), Height: 7, Width: 8, X: 7, Y: 5, Options: map[string]interface{}{"min": float64(0), "max": float64(5000)}, Type: "Slider", Variables: []string{blinkSpeedID}, }, - {Name: "relay_2", Height: 5, Width: 5, + {Name: toStringPointer("relay_2"), Height: 5, Width: 5, X: 5, Y: 0, Options: map[string]interface{}{"showLabels": true}, Type: "Switch", Variables: []string{relayID}, }, @@ -150,15 +159,15 @@ func (t *thingShowTest) ThingShow(ctx context.Context, thingID string) (*iotclie if thingID == thingOverriddenID { return &iotclient.ArduinoThing{ Properties: []iotclient.ArduinoProperty{ - {Id: switchyOverriddenID, VariableName: "switchy"}, + {Id: switchyOverriddenID, VariableName: toStringPointer("switchy")}, }, }, nil } return &iotclient.ArduinoThing{ Properties: []iotclient.ArduinoProperty{ - {Id: switchyID, VariableName: "switchy"}, - {Id: relayID, VariableName: "relay_2"}, - {Id: blinkSpeedID, VariableName: "blink_speed"}, + {Id: switchyID, VariableName: toStringPointer("switchy")}, + {Id: relayID, VariableName: toStringPointer("relay_2")}, + {Id: blinkSpeedID, VariableName: toStringPointer("blink_speed")}, }, }, nil } @@ -177,39 +186,34 @@ func TestLoadDashboard(t *testing.T) { override: nil, want: dashboardDetailed, }, - { name: "dashboard with wrong options to be filtered out", file: "testdata/dashboard-wrong-options.yaml", override: nil, want: dashboardDetailed, }, - { name: "dashboard without options, should have a not nil map", file: "testdata/dashboard-no-options.yaml", override: nil, want: dashboardNoOptions, }, - { name: "dashboard with variable, mocked variable id is concatenation of thing_id and variable_id", file: "testdata/dashboard-with-variable.yaml", - override: nil, - want: dashboardWithVariable, + override: map[string]string{"thing": thingOverriddenID}, + want: dashboardVariableOverride, }, - { name: "dashboard with variable, thing is overridden", file: "testdata/dashboard-with-variable.yaml", override: map[string]string{"thing": thingOverriddenID}, want: dashboardVariableOverride, }, - { name: "dashboard with two widgets", file: "testdata/dashboard-two-widgets.yaml", - override: nil, + override: map[string]string{"remote-controlled-lights": thingRemoteControlOverriddenID}, want: dashboardTwoWidgets, }, } From a17c93479a47ffbac79856dee76a5bdf02ba0d83 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Wed, 3 Jul 2024 16:51:24 +0200 Subject: [PATCH 05/20] Support for ota download progress (#156) --- cli/ota/status.go | 21 ++++++------ command/ota/status.go | 5 +-- internal/ota-api/dto.go | 62 +++++++++++++++++++++++++++++++++--- internal/ota-api/dto_test.go | 19 +++++++++++ 4 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 internal/ota-api/dto_test.go diff --git a/cli/ota/status.go b/cli/ota/status.go index 89d67081..4c3ce90e 100644 --- a/cli/ota/status.go +++ b/cli/ota/status.go @@ -38,28 +38,29 @@ type statusFlags struct { func initOtaStatusCommand() *cobra.Command { flags := &statusFlags{} - uploadCommand := &cobra.Command{ + statusCommand := &cobra.Command{ Use: "status", Short: "OTA status", Long: "Get OTA status by OTA or device ID", Run: func(cmd *cobra.Command, args []string) { - if err := runPrintOtaStatusCommand(flags); err != nil { - feedback.Errorf("Error during ota get status: %v", err) + if err := runPrintOtaStatusCommand(flags, cmd); err != nil { + feedback.Errorf("\nError during ota get status: %v", err) os.Exit(errorcodes.ErrGeneric) } }, } - uploadCommand.Flags().StringVarP(&flags.otaID, "ota-id", "o", "", "OTA ID") - uploadCommand.Flags().StringVarP(&flags.otaIDs, "ota-ids", "", "", "OTA IDs (comma separated)") - uploadCommand.Flags().StringVarP(&flags.deviceId, "device-id", "d", "", "Device ID") - uploadCommand.Flags().Int16VarP(&flags.limit, "limit", "l", 10, "Output limit (default: 10)") - uploadCommand.Flags().StringVarP(&flags.sort, "sort", "s", "desc", "Sorting (default: desc)") + statusCommand.Flags().StringVarP(&flags.otaID, "ota-id", "o", "", "OTA ID") + statusCommand.Flags().StringVarP(&flags.otaIDs, "ota-ids", "", "", "OTA IDs (comma separated)") + statusCommand.Flags().StringVarP(&flags.deviceId, "device-id", "d", "", "Device ID") + statusCommand.Flags().Int16VarP(&flags.limit, "limit", "l", 10, "Output limit (default: 10)") + statusCommand.Flags().StringVarP(&flags.sort, "sort", "s", "desc", "Sorting (default: desc)") - return uploadCommand + return statusCommand } -func runPrintOtaStatusCommand(flags *statusFlags) error { +func runPrintOtaStatusCommand(flags *statusFlags, command *cobra.Command) error { if flags.otaID == "" && flags.deviceId == "" && flags.otaIDs == "" { + command.Help() return fmt.Errorf("required flag(s) \"ota-id\" or \"device-id\" or \"ota-ids\" not set") } diff --git a/command/ota/status.go b/command/ota/status.go index 6913209d..f13ec5dd 100644 --- a/command/ota/status.go +++ b/command/ota/status.go @@ -27,8 +27,9 @@ func PrintOtaStatus(otaid, otaids, device string, cred *config.Credentials, limi res, err := otapi.GetOtaStatusByOtaID(otaid, limit, order) if err == nil && res != nil { feedback.PrintResult(otaapi.OtaStatusDetail{ - Ota: res.Ota, - Details: res.States, + FirmwareSize: res.FirmwareSize, + Ota: res.Ota, + Details: res.States, }) } else if err != nil { return err diff --git a/internal/ota-api/dto.go b/internal/ota-api/dto.go index eaf09424..04184ed8 100644 --- a/internal/ota-api/dto.go +++ b/internal/ota-api/dto.go @@ -18,6 +18,7 @@ package otaapi import ( + "strconv" "strings" "time" @@ -26,10 +27,13 @@ import ( "github.com/arduino/arduino-cli/table" ) +const progressBarMultiplier = 2 + type ( OtaStatusResponse struct { - Ota Ota `json:"ota"` - States []State `json:"states,omitempty"` + FirmwareSize *int64 `json:"firmware_size,omitempty"` + Ota Ota `json:"ota"` + States []State `json:"states,omitempty"` } OtaStatusList struct { @@ -53,8 +57,9 @@ type ( } OtaStatusDetail struct { - Ota Ota `json:"ota"` - Details []State `json:"details,omitempty"` + FirmwareSize *int64 `json:"firmware_size,omitempty"` + Ota Ota `json:"ota"` + Details []State `json:"details,omitempty"` } ) @@ -154,8 +159,13 @@ func (r OtaStatusDetail) String() string { if len(r.Details) > 0 { t = table.New() t.SetHeader("Time", "Status", "Detail") + fwSize := int64(0) + if r.FirmwareSize != nil { + fwSize = *r.FirmwareSize + } for _, s := range r.Details { - t.AddRow(formatHumanReadableTs(s.Timestamp), upperCaseFirst(s.State), s.StateData) + stateData := formatStateData(s.State, s.StateData, fwSize, hasReachedFlashState(r.Details)) + t.AddRow(formatHumanReadableTs(s.Timestamp), upperCaseFirst(s.State), stateData) } output += "\nDetails:\n" + t.Render() } @@ -163,6 +173,48 @@ func (r OtaStatusDetail) String() string { return output } +func hasReachedFlashState(states []State) bool { + for _, s := range states { + if s.State == "flash" || s.State == "reboot" { + return true + } + } + return false +} + +func formatStateData(state, data string, firmware_size int64, hasReceivedFlashState bool) string { + if data == "" || data == "Unknown" { + return "" + } + if state == "fetch" { + // This is the state 'fetch' of OTA progress. This contains a number that represents the number of bytes fetched + actualDownloadedData, err := strconv.Atoi(data) + if err != nil || actualDownloadedData <= 0 || firmware_size <= 0 { // Sanitize and avoid division by zero + return data + } + if hasReceivedFlashState { + return buildSimpleProgressBar(float64(100)) + } + percentage := (float64(actualDownloadedData) / float64(firmware_size)) * 100 + return buildSimpleProgressBar(percentage) + } + return data +} + +func buildSimpleProgressBar(progress float64) string { + progressInt := int(progress) / 10 + progressInt = progressInt * progressBarMultiplier + maxProgress := 10 * progressBarMultiplier + var bar strings.Builder + bar.WriteString("[") + bar.WriteString(strings.Repeat("=", progressInt)) + bar.WriteString(strings.Repeat(" ", maxProgress-progressInt)) + bar.WriteString("] ") + bar.WriteString(strconv.FormatFloat(progress, 'f', 2, 64)) + bar.WriteString("%") + return bar.String() +} + func upperCaseFirst(s string) string { if len(s) > 0 { s = strings.ReplaceAll(s, "_", " ") diff --git a/internal/ota-api/dto_test.go b/internal/ota-api/dto_test.go new file mode 100644 index 00000000..bb91225f --- /dev/null +++ b/internal/ota-api/dto_test.go @@ -0,0 +1,19 @@ +package otaapi + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestProgressBar_notCompletePct(t *testing.T) { + firmwareSize := int64(25665 * 2) + bar := formatStateData("fetch", "25665", firmwareSize, false) + assert.Equal(t, "[========== ] 50.00%", bar) +} + +func TestProgressBar_ifFlashState_goTo100Pct(t *testing.T) { + firmwareSize := int64(25665 * 2) + bar := formatStateData("fetch", "25665", firmwareSize, true) // If in flash status, go to 100% + assert.Equal(t, "[====================] 100.00%", bar) +} From 5ca90dfab283a9cf033389ad1c6d4e555ffaab5f Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Mon, 29 Jul 2024 09:28:05 +0200 Subject: [PATCH 06/20] [WIRE-422] Custom templates support (#157) --- .../arduino/iot-client-go/v2.dep.yml | 2 +- README.md | 49 ++- cli/cli.go | 2 + cli/device/device.go | 1 + cli/device/show.go | 102 +++++ cli/template/apply.go | 96 +++++ cli/template/export.go | 64 +++ cli/template/import.go | 62 +++ cli/template/list.go | 53 +++ cli/template/template.go | 37 ++ command/device/device.go | 36 +- command/device/show.go | 66 +++ command/ota/cancel.go | 17 + command/ota/massupload_test.go | 17 + command/ota/status.go | 17 + .../template_ok-rp-with-binaries.tino | Bin 0 -> 253458 bytes command/template/apply.go | 237 +++++++++++ command/template/apply_test.go | 15 + command/template/export.go | 46 ++ command/template/import.go | 40 ++ command/template/list.go | 42 ++ go.mod | 2 +- go.sum | 2 + internal/iot/client.go | 44 ++ internal/storage-api/client.go | 394 ++++++++++++++++++ internal/storage-api/dto.go | 344 +++++++++++++++ 26 files changed, 1769 insertions(+), 18 deletions(-) create mode 100644 cli/device/show.go create mode 100644 cli/template/apply.go create mode 100644 cli/template/export.go create mode 100644 cli/template/import.go create mode 100644 cli/template/list.go create mode 100644 cli/template/template.go create mode 100644 command/device/show.go create mode 100644 command/template/.testdata/template_ok-rp-with-binaries.tino create mode 100644 command/template/apply.go create mode 100644 command/template/apply_test.go create mode 100644 command/template/export.go create mode 100644 command/template/import.go create mode 100644 command/template/list.go create mode 100644 internal/storage-api/client.go create mode 100644 internal/storage-api/dto.go diff --git a/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml b/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml index ed76f3c6..43ef0bb2 100644 --- a/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml +++ b/.licenses/go/github.com/arduino/iot-client-go/v2.dep.yml @@ -1,6 +1,6 @@ --- name: github.com/arduino/iot-client-go/v2 -version: v2.0.2 +version: v2.0.3 type: go summary: homepage: https://pkg.go.dev/github.com/arduino/iot-client-go/v2 diff --git a/README.md b/README.md index 1554809d..f78f4058 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ Here are the FQBNs of the Arduino boards that can be provisioned with this comma * `arduino:samd:mkrnb1500` * `arduino:mbed_opta:opta` * `arduino:mbed_giga:giga` +* `arduino:esp32:nano_nora` +* `arduino:renesas_uno:unor4wifi` If the device supports more than one connectivity type (Eg: WiFi and Ethernet) the --connection flag can be used to set the desired connectivity @@ -327,10 +329,17 @@ Note that the binary file (`.bin`) should be compiled using an arduino core that arduino-cloud-cli ota upload --device-id --file ``` -The default OTA upload should complete in 10 minutes. Use `--deferred` flag to extend this time to one week (see an example sketch [here](https://github.com/arduino-libraries/ArduinoIoTCloud/blob/ab0af75a5666f875929029ac6df59e04789269c5/examples/ArduinoIoTCloud-DeferredOTA/ArduinoIoTCloud-DeferredOTA.ino)): +This schedule a new OTA. Its ID is printed as output. +It is possible to check status for scheduled/executed OTAs using status command. ```bash -arduino-cloud-cli ota upload --device-id --file --deferred +arduino-cloud-cli ota status --ota-id +``` + +or by device + +```bash +arduino-cloud-cli ota status --device-id ``` ### Mass upload @@ -383,3 +392,39 @@ Create a dashboard: dashboards can be created only starting from a template. Sup ```bash arduino-cloud-cli dashboard create --name --template --override =,= ``` + +## Custom templates + +### List custom templates + +Use following command to list available custom templates + +```bash +arduino-cloud-cli template list +``` + +### Export custom template + +Given list command, it is possible to get custom template ID. Given its ID, use following command to export it as '.tino' archive: + +```bash +arduino-cloud-cli template export -t +``` + +it is possible to specify output directory with '-d' flag + +### Import custom template + +To import a custom template, use command: + +```bash +arduino-cloud-cli template import -f