Skip to content

Commit 5db1b69

Browse files
committed
Inception
1 parent e7a4495 commit 5db1b69

File tree

4 files changed

+254
-0
lines changed

4 files changed

+254
-0
lines changed

cli/template/import.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package template
19+
20+
import (
21+
"os"
22+
23+
"github.com/arduino/arduino-cli/cli/errorcodes"
24+
"github.com/arduino/arduino-cli/cli/feedback"
25+
"github.com/spf13/cobra"
26+
)
27+
28+
type importFlags struct {
29+
templateFile string
30+
}
31+
32+
func initTemplateImportCommand() *cobra.Command {
33+
flags := &importFlags{}
34+
uploadCommand := &cobra.Command{
35+
Use: "import",
36+
Short: "Import template",
37+
Long: "Import a template from a file",
38+
Run: func(cmd *cobra.Command, args []string) {
39+
if err := runTemplateImportCommand(flags); err != nil {
40+
feedback.Errorf("Error during ota get status: %v", err)
41+
os.Exit(errorcodes.ErrGeneric)
42+
}
43+
},
44+
}
45+
46+
uploadCommand.Flags().StringVarP(&flags.templateFile, "file", "f", "", "Template file to import")
47+
48+
return uploadCommand
49+
}
50+
51+
func runTemplateImportCommand(flags *importFlags) error {
52+
return nil
53+
}

cli/template/template.go

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package template
19+
20+
import (
21+
"github.com/spf13/cobra"
22+
)
23+
24+
func NewCommand() *cobra.Command {
25+
templateCommand := &cobra.Command{
26+
Use: "template",
27+
Short: "Custom templates",
28+
Long: "Commands to manage custom templates lifecycle, like import, export and apply.",
29+
}
30+
31+
templateCommand.AddCommand(initTemplateImportCommand())
32+
33+
return templateCommand
34+
}

internal/storage-api/client.go

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package storageapi
19+
20+
import (
21+
"bytes"
22+
"encoding/json"
23+
"errors"
24+
"fmt"
25+
"io"
26+
"mime/multipart"
27+
"net/http"
28+
"os"
29+
"strings"
30+
31+
"github.com/arduino/arduino-cloud-cli/config"
32+
"github.com/arduino/arduino-cloud-cli/internal/iot"
33+
"golang.org/x/oauth2"
34+
)
35+
36+
type StorageApiClient struct {
37+
client *http.Client
38+
host string
39+
src oauth2.TokenSource
40+
organization string
41+
}
42+
43+
func NewClient(credentials *config.Credentials) *StorageApiClient {
44+
host := iot.GetArduinoAPIBaseURL()
45+
tokenSource := iot.NewUserTokenSource(credentials.Client, credentials.Secret, host)
46+
return &StorageApiClient{
47+
client: &http.Client{},
48+
src: tokenSource,
49+
host: host,
50+
organization: credentials.Organization,
51+
}
52+
}
53+
54+
func (c *StorageApiClient) performMultipartRequest(endpoint, method, token, filename, multipartFieldName string) (*http.Response, error) {
55+
var buffer bytes.Buffer
56+
writer := multipart.NewWriter(&buffer)
57+
58+
// Open the file
59+
file, err := os.Open(filename)
60+
if err != nil {
61+
return nil, err
62+
}
63+
defer file.Close()
64+
65+
// Create a form file
66+
formFile, err := writer.CreateFormFile(multipartFieldName, file.Name())
67+
if err != nil {
68+
return nil, err
69+
}
70+
71+
// Copy the file data to the form file
72+
_, err = io.Copy(formFile, file)
73+
if err != nil {
74+
return nil, err
75+
}
76+
// Close the multipart writer
77+
err = writer.Close()
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
req, err := http.NewRequest(method, endpoint, &buffer)
83+
if err != nil {
84+
return nil, err
85+
}
86+
req.Header.Add("Authorization", "Bearer "+token)
87+
if c.organization != "" {
88+
req.Header.Add("X-Organization", c.organization)
89+
}
90+
req.Header.Set("Content-Type", writer.FormDataContentType())
91+
res, err := c.client.Do(req)
92+
if err != nil {
93+
return nil, err
94+
}
95+
return res, nil
96+
}
97+
98+
func (c *StorageApiClient) ImportCustomTemplate(templateFile string) (*ImportCustomTemplateResponse, error) {
99+
100+
if templateFile == "" {
101+
return nil, fmt.Errorf("invalid template: no file provided")
102+
}
103+
104+
userRequestToken, err := c.src.Token()
105+
if err != nil {
106+
if strings.Contains(err.Error(), "401") {
107+
return nil, errors.New("wrong credentials")
108+
}
109+
return nil, fmt.Errorf("cannot retrieve a valid token: %w", err)
110+
}
111+
112+
endpoint := c.host + "/storage/template/archive/v1/"
113+
res, err := c.performMultipartRequest(endpoint, "POST", userRequestToken.AccessToken, templateFile, "template")
114+
if err != nil {
115+
return nil, err
116+
}
117+
defer res.Body.Close()
118+
bodyb, err := io.ReadAll(res.Body)
119+
120+
if res.StatusCode == 200 {
121+
var importTemplResponse ImportCustomTemplateResponse
122+
if err == nil && bodyb != nil {
123+
err = json.Unmarshal(bodyb, &importTemplResponse)
124+
if err != nil {
125+
return nil, err
126+
}
127+
}
128+
129+
return &importTemplResponse, nil
130+
} else if res.StatusCode == 400 {
131+
return nil, fmt.Errorf("bad request: %s", string(bodyb))
132+
} else if res.StatusCode == 409 {
133+
return nil, fmt.Errorf("template already exists: %s", string(bodyb))
134+
} else if res.StatusCode == 401 {
135+
return nil, errors.New("unauthorized request")
136+
} else if res.StatusCode == 403 {
137+
return nil, errors.New("forbidden request")
138+
} else if res.StatusCode == 500 {
139+
return nil, errors.New("internal server error")
140+
}
141+
142+
return nil, err
143+
}

internal/storage-api/dto.go

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// This file is part of arduino-cloud-cli.
2+
//
3+
// Copyright (C) 2021 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This program is free software: you can redistribute it and/or modify
6+
// it under the terms of the GNU Affero General Public License as published
7+
// by the Free Software Foundation, either version 3 of the License, or
8+
// (at your option) any later version.
9+
//
10+
// This program is distributed in the hope that it will be useful,
11+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
// GNU Affero General Public License for more details.
14+
//
15+
// You should have received a copy of the GNU Affero General Public License
16+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
17+
18+
package storageapi
19+
20+
type (
21+
ImportCustomTemplateResponse struct {
22+
TemplateID string `json:"template_id"`
23+
}
24+
)

0 commit comments

Comments
 (0)