Skip to content

Refactor template format #30

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ Devices currently present on Arduino IoT Cloud can be retrieved by using this co

Things can be created starting from a template or by cloning another thing.

Create a thing from a thing template. The name parameter is optional. If it is provided then it overrides the name retrieved from the template:
Create a thing from a thing template. Supported template formats are JSON and YAML. The name parameter is optional. If it is provided then it overrides the name retrieved from the template:

`$ iot-cloud-cli thing create --name <thingName> --template <template.json>`
`$ iot-cloud-cli thing create --name <thingName> --template <template.(json|yaml)>`

Create a thing by cloning another thing, here the *name is mandatory*:

Expand All @@ -72,9 +72,9 @@ Delete a thing with the following command:

`$ iot-cloud-cli thing delete --device-id <deviceID>`

Extract a template from an existing thing:
Extract a template from an existing thing. The template can be saved in two formats: json or yaml. The default format is yaml:

`$ iot-cloud-cli thing extract --id <thingID> --outfile <templateFile.json>`
`$ iot-cloud-cli thing extract --id <thingID> --outfile <templateFile> --format <yaml|json>`

Bind a thing to an existing device:

Expand Down
8 changes: 7 additions & 1 deletion cli/thing/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ func initCreateCommand() *cobra.Command {
RunE: runCreateCommand,
}
createCommand.Flags().StringVarP(&createFlags.name, "name", "n", "", "Thing name")
createCommand.Flags().StringVarP(&createFlags.template, "template", "t", "", "File containing a thing template")
createCommand.Flags().StringVarP(
&createFlags.template,
"template",
"t",
"",
"File containing a thing template, JSON and YAML format are supported",
)
createCommand.MarkFlagRequired("template")
return createCommand
}
Expand Down
11 changes: 10 additions & 1 deletion cli/thing/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
var extractFlags struct {
id string
outfile string
format string
}

func initExtractCommand() *cobra.Command {
Expand All @@ -21,6 +22,13 @@ func initExtractCommand() *cobra.Command {
}
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
}
Expand All @@ -29,7 +37,8 @@ func runExtractCommand(cmd *cobra.Command, args []string) error {
fmt.Printf("Extracting template from thing %s\n", extractFlags.id)

params := &thing.ExtractParams{
ID: extractFlags.id,
ID: extractFlags.id,
Format: extractFlags.format,
}
if extractFlags.outfile != "" {
params.Outfile = &extractFlags.outfile
Expand Down
10 changes: 7 additions & 3 deletions command/thing/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
iotclient "github.com/arduino/iot-client-go"
"github.com/arduino/iot-cloud-cli/internal/config"
"github.com/arduino/iot-cloud-cli/internal/iot"
"gopkg.in/yaml.v3"
)

// CreateParams contains the parameters needed to create a new thing.
Expand Down Expand Up @@ -67,9 +68,12 @@ func loadTemplate(file string) (*iotclient.Thing, error) {
}

template := make(map[string]interface{})
err = json.Unmarshal([]byte(templateBytes), &template)
if err != nil {
return nil, fmt.Errorf("%s: %w", "reading template file: template not valid: ", err)

// Extract template trying all the supported formats: json and yaml
if err = json.Unmarshal([]byte(templateBytes), &template); err != nil {
if err = yaml.Unmarshal([]byte(templateBytes), &template); err != nil {
return nil, errors.New("reading template file: template format is not valid")
}
}

// Adapt thing template to thing structure
Expand Down
61 changes: 49 additions & 12 deletions command/thing/extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@ package thing

import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"strings"

iotclient "github.com/arduino/iot-client-go"
"github.com/arduino/iot-cloud-cli/internal/config"
"github.com/arduino/iot-cloud-cli/internal/iot"
"gopkg.in/yaml.v3"
)

// ExtractParams contains the parameters needed to
// extract a thing from Arduino IoT Cloud and save it on local storage.
// Format determines the file format of the template ("json" or "yaml")
// Output indicates the destination path of the extraction.
type ExtractParams struct {
ID string
Format string
Outfile *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")
}

conf, err := config.Retrieve()
if err != nil {
return err
Expand All @@ -42,23 +52,19 @@ func Extract(params *ExtractParams) error {
return err
}

if params.Outfile == nil {
outfile := thing.Name + "-template.json"
params.Outfile = &outfile
}
err = ioutil.WriteFile(*params.Outfile, template, os.FileMode(0644))
err = templateToFile(template, params)
if err != nil {
err = fmt.Errorf("%s: %w", "cannot write outfile: ", err)
return err
}

return nil
}

func templateFromThing(thing *iotclient.ArduinoThing) ([]byte, error) {
func templateFromThing(thing *iotclient.ArduinoThing) (map[string]interface{}, error) {
template := make(map[string]interface{})
template["name"] = thing.Name

// Extract template from thing structure
var props []map[string]interface{}
for _, p := range thing.Properties {
prop := make(map[string]interface{})
Expand All @@ -72,11 +78,42 @@ func templateFromThing(thing *iotclient.ArduinoThing) ([]byte, error) {
}
template["variables"] = props

// Extract json template from thing structure
file, err := json.MarshalIndent(template, "", " ")
return template, nil
}

func templateToFile(template map[string]interface{}, params *ExtractParams) error {
var file []byte
var err error

if params.Format == "json" {
file, err = json.MarshalIndent(template, "", " ")
if err != nil {
return fmt.Errorf("%s: %w", "thing marshal failure: ", err)
}

} else if params.Format == "yaml" {
file, err = yaml.Marshal(template)
if err != nil {
return fmt.Errorf("%s: %w", "thing marshal failure: ", err)
}

} else {
return errors.New("format is not valid: only 'json' and 'yaml' are supported")
}

if params.Outfile == nil {
name, ok := template["name"].(string)
if name == "" || !ok {
return errors.New("thing template does not have a valid name")
}
outfile := name + "." + params.Format
params.Outfile = &outfile
}

err = ioutil.WriteFile(*params.Outfile, file, os.FileMode(0644))
if err != nil {
err = fmt.Errorf("%s: %w", "thing marshal failure: ", err)
return nil, err
return fmt.Errorf("%s: %w", "cannot write outfile: ", err)
}
return file, nil

return nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ require (
google.golang.org/genproto v0.0.0-20210504143626-3b2ad6ccc450 // indirect
google.golang.org/grpc v1.39.0
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
)