diff --git a/README.md b/README.md index 63b2c3a2..8b4d9a63 100644 --- a/README.md +++ b/README.md @@ -154,9 +154,9 @@ Delete a thing with the following command: `$ arduino-cloud-cli thing delete --id ` -Extract a template from an existing thing. The template can be saved in two formats: json or yaml. The default format is yaml: +Extract a template from an existing thing. The template is printed to stdout and its format depends on the global `--format` flag: -`$ arduino-cloud-cli thing extract --id --outfile --format ` +`$ arduino-cloud-cli thing extract --id --format ` Bind a thing to an existing device: @@ -198,9 +198,9 @@ Delete a dashboard with the following command: `$ arduino-cloud-cli dashboard delete --id ` -Extract a template from an existing dashboard. The template can be saved in two formats: json or yaml. The default format is yaml: +Extract a template from an existing dashboard. The template is printed to stdout and its format depends on the global `--format` flag: -`$ arduino-cloud-cli dashboard extract --id --outfile --format ` +`$ arduino-cloud-cli dashboard extract --id --format ` Create a dashboard: dashboards can be created only starting from a template. Supported dashboard template formats are JSON and YAML. The name parameter is optional. If it is provided then it overrides the name retrieved from the template. The `override` flag can be used to override the template `thing_id` placeholder with the actual ID of the thing to be used. diff --git a/cli/dashboard/extract.go b/cli/dashboard/extract.go index f285f3a0..281e4bc0 100644 --- a/cli/dashboard/extract.go +++ b/cli/dashboard/extract.go @@ -25,29 +25,21 @@ import ( "github.com/arduino/arduino-cloud-cli/command/dashboard" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "gopkg.in/yaml.v3" ) var extractFlags struct { - id string - outfile string - format string + id string } func initExtractCommand() *cobra.Command { extractCommand := &cobra.Command{ Use: "extract", Short: "Extract a template from a dashboard", - Long: "Extract a template from a Arduino IoT Cloud dashboard and save it in a file", + Long: "Extract a template from a Arduino IoT Cloud dashboard", Run: runExtractCommand, } extractCommand.Flags().StringVarP(&extractFlags.id, "id", "i", "", "Dashboard ID") - extractCommand.Flags().StringVarP(&extractFlags.outfile, "outfile", "o", "", "Template file destination path") - extractCommand.Flags().StringVar( - &extractFlags.format, - "format", - "yaml", - "Format of template file, can be {json|yaml}. Default is 'yaml'", - ) extractCommand.MarkFlagRequired("id") return extractCommand @@ -57,18 +49,31 @@ func runExtractCommand(cmd *cobra.Command, args []string) { logrus.Infof("Extracting template from dashboard %s", extractFlags.id) params := &dashboard.ExtractParams{ - ID: extractFlags.id, - Format: extractFlags.format, - } - if extractFlags.outfile != "" { - params.Outfile = &extractFlags.outfile + ID: extractFlags.id, } - err := dashboard.Extract(params) + template, err := dashboard.Extract(params) if err != nil { feedback.Errorf("Error during template extraction: %v", err) os.Exit(errorcodes.ErrGeneric) } - logrus.Info("Template successfully extracted") + feedback.PrintResult(extractResult{template}) +} + +type extractResult struct { + template map[string]interface{} +} + +func (r extractResult) Data() interface{} { + return r.template +} + +func (r extractResult) String() string { + t, err := yaml.Marshal(r.template) + if err != nil { + feedback.Errorf("Error during template parsing: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + return string(t) } diff --git a/cli/thing/extract.go b/cli/thing/extract.go index 98efbf7c..9cf5a57e 100644 --- a/cli/thing/extract.go +++ b/cli/thing/extract.go @@ -25,29 +25,21 @@ import ( "github.com/arduino/arduino-cloud-cli/command/thing" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "gopkg.in/yaml.v3" ) var extractFlags struct { - id string - outfile string - format string + id string } func initExtractCommand() *cobra.Command { extractCommand := &cobra.Command{ Use: "extract", Short: "Extract a template from a thing", - Long: "Extract a template from a Arduino IoT Cloud thing and save it in a file", + Long: "Extract a template from a Arduino IoT Cloud thing", Run: runExtractCommand, } extractCommand.Flags().StringVarP(&extractFlags.id, "id", "i", "", "Thing ID") - extractCommand.Flags().StringVarP(&extractFlags.outfile, "outfile", "o", "", "Template file destination path") - extractCommand.Flags().StringVar( - &extractFlags.format, - "format", - "yaml", - "Format of template file, can be {json|yaml}. Default is 'yaml'", - ) extractCommand.MarkFlagRequired("id") return extractCommand @@ -57,18 +49,31 @@ func runExtractCommand(cmd *cobra.Command, args []string) { logrus.Infof("Extracting template from thing %s", extractFlags.id) params := &thing.ExtractParams{ - ID: extractFlags.id, - Format: extractFlags.format, - } - if extractFlags.outfile != "" { - params.Outfile = &extractFlags.outfile + ID: extractFlags.id, } - err := thing.Extract(params) + template, err := thing.Extract(params) if err != nil { feedback.Errorf("Error during template extraction: %v", err) os.Exit(errorcodes.ErrGeneric) } - logrus.Info("Template successfully extracted") + feedback.PrintResult(extractResult{template}) +} + +type extractResult struct { + template map[string]interface{} +} + +func (r extractResult) Data() interface{} { + return r.template +} + +func (r extractResult) String() string { + t, err := yaml.Marshal(r.template) + if err != nil { + feedback.Errorf("Error during template parsing: %v", err) + os.Exit(errorcodes.ErrGeneric) + } + return string(t) } diff --git a/command/dashboard/extract.go b/command/dashboard/extract.go index 672071b6..a02b737b 100644 --- a/command/dashboard/extract.go +++ b/command/dashboard/extract.go @@ -18,64 +18,36 @@ package dashboard import ( - "errors" "fmt" - "strings" "github.com/arduino/arduino-cloud-cli/internal/config" "github.com/arduino/arduino-cloud-cli/internal/iot" "github.com/arduino/arduino-cloud-cli/internal/template" - "github.com/sirupsen/logrus" ) // ExtractParams contains the parameters needed to -// extract a template dashboard from Arduino IoT Cloud and save it on local storage. +// extract a template dashboard from Arduino IoT Cloud. type ExtractParams struct { - ID string - Format string // Format determines the file format of the template ("json" or "yaml") - Outfile *string // Destination path of the extracted template + ID string } // Extract command is used to extract a dashboard template // from a dashboard on Arduino IoT Cloud. -func Extract(params *ExtractParams) error { - params.Format = strings.ToLower(params.Format) - if params.Format != "json" && params.Format != "yaml" { - return errors.New("format is not valid: only 'json' and 'yaml' are supported") - } - +func Extract(params *ExtractParams) (map[string]interface{}, error) { conf, err := config.Retrieve() if err != nil { - return err + return nil, err } iotClient, err := iot.NewClient(conf.Client, conf.Secret) if err != nil { - return err + return nil, err } dashboard, err := iotClient.DashboardShow(params.ID) if err != nil { err = fmt.Errorf("%s: %w", "cannot extract dashboard: ", err) - return err - } - - templ := template.FromDashboard(dashboard) - - if params.Outfile == nil { - name, ok := templ["name"].(string) - if name == "" || !ok { - return errors.New("dashboard template does not have a valid name") - } - name = strings.Join(strings.Fields(name), "") - outfile := name + "-dashboard." + params.Format - params.Outfile = &outfile - } - - logrus.Infof("Extracting template in file: %s", *params.Outfile) - err = template.ToFile(templ, *params.Outfile, params.Format) - if err != nil { - return fmt.Errorf("saving template: %w", err) + return nil, err } - return nil + return template.FromDashboard(dashboard), nil } diff --git a/command/thing/extract.go b/command/thing/extract.go index 23866fe0..fa3fe308 100644 --- a/command/thing/extract.go +++ b/command/thing/extract.go @@ -18,64 +18,36 @@ package thing import ( - "errors" "fmt" - "strings" "github.com/arduino/arduino-cloud-cli/internal/config" "github.com/arduino/arduino-cloud-cli/internal/iot" "github.com/arduino/arduino-cloud-cli/internal/template" - "github.com/sirupsen/logrus" ) // ExtractParams contains the parameters needed to -// extract a template thing from Arduino IoT Cloud and save it on local storage. +// extract a template thing from Arduino IoT Cloud. type ExtractParams struct { - ID string - Format string // Format determines the file format of the template ("json" or "yaml") - Outfile *string // Destination path of the extracted template + ID string } // Extract command is used to extract a thing template // from a thing on Arduino IoT Cloud. -func Extract(params *ExtractParams) error { - params.Format = strings.ToLower(params.Format) - if params.Format != "json" && params.Format != "yaml" { - return errors.New("format is not valid: only 'json' and 'yaml' are supported") - } - +func Extract(params *ExtractParams) (map[string]interface{}, error) { conf, err := config.Retrieve() if err != nil { - return err + return nil, err } iotClient, err := iot.NewClient(conf.Client, conf.Secret) if err != nil { - return err + return nil, err } thing, err := iotClient.ThingShow(params.ID) if err != nil { err = fmt.Errorf("%s: %w", "cannot extract thing: ", err) - return err - } - - templ := template.FromThing(thing) - - if params.Outfile == nil { - name, ok := templ["name"].(string) - if name == "" || !ok { - return errors.New("thing template does not have a valid name") - } - name = strings.Join(strings.Fields(name), "") - outfile := name + "-thing." + params.Format - params.Outfile = &outfile - } - - logrus.Infof("Extracting template in file: %s", *params.Outfile) - err = template.ToFile(templ, *params.Outfile, params.Format) - if err != nil { - return fmt.Errorf("saving template: %w", err) + return nil, err } - return nil + return template.FromThing(thing), nil }