Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9097dd3

Browse files
Marta CarboneMarta Carbone
Marta Carbone
authored and
Marta Carbone
committed
Add the server version to arduino-app-cli version.
1 parent a6c084e commit 9097dd3

File tree

2 files changed

+207
-9
lines changed

2 files changed

+207
-9
lines changed

‎cmd/arduino-app-cli/version/version.go‎

Lines changed: 107 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,132 @@
1616
package version
1717

1818
import (
19+
"encoding/json"
1920
"fmt"
21+
"io"
22+
"net"
23+
"net/http"
24+
"net/url"
25+
"time"
2026

2127
"github.com/spf13/cobra"
2228

2329
"github.com/arduino/arduino-app-cli/cmd/feedback"
30+
"github.com/arduino/arduino-app-cli/cmd/i18n"
2431
)
2532

26-
func NewVersionCmd(version string) *cobra.Command {
33+
// The actual listening address for the daemon
34+
// is defined in the installation package
35+
const (
36+
DefaultHostname = "localhost"
37+
DefaultPort = "8800"
38+
)
39+
40+
func NewVersionCmd(clientVersion string) *cobra.Command {
2741
cmd := &cobra.Command{
2842
Use: "version",
29-
Short: "Print the version number of Arduino App CLI",
43+
Short: "Print the client and server version numbers for the Arduino App CLI.",
3044
Run: func(cmd *cobra.Command, args []string) {
31-
feedback.PrintResult(versionResult{
32-
AppName: "Arduino App CLI",
33-
Version: version,
34-
})
45+
host, _ := cmd.Flags().GetString("host")
46+
47+
versionHandler(clientVersion, host)
3548
},
3649
}
50+
cmd.Flags().String("host", fmt.Sprintf("%s:%s", DefaultHostname, DefaultPort),
51+
"The daemon network address [host]:[port]")
3752
return cmd
3853
}
3954

40-
type versionResult struct {
41-
AppName string `json:"appName"`
55+
func versionHandler(clientVersion string, host string) {
56+
httpClient := http.Client{
57+
Timeout: time.Second,
58+
}
59+
result := doVersionHandler(httpClient, clientVersion, host)
60+
feedback.PrintResult(result)
61+
}
62+
63+
func doVersionHandler(httpClient http.Client, clientVersion string, host string) versionResult {
64+
url, err := getValidOrDefaultUrl(host)
65+
if err != nil {
66+
feedback.Fatal(i18n.Tr("Error: invalid host:port format"), feedback.ErrBadArgument)
67+
}
68+
69+
serverVersion, err := getServerVersion(httpClient, url)
70+
if err != nil {
71+
serverVersion = fmt.Sprintf("n/a (cannot connect to the server %s://%s)", url.Scheme, url.Host)
72+
}
73+
74+
return versionResult{
75+
ClientVersion: clientVersion,
76+
ServerVersion: serverVersion,
77+
}
78+
}
79+
80+
func getValidOrDefaultUrl(hostPort string) (url.URL, error) {
81+
host := DefaultHostname
82+
port := DefaultPort
83+
84+
if hostPort != "" {
85+
h, p, err := net.SplitHostPort(hostPort)
86+
if err != nil {
87+
return url.URL{}, err
88+
}
89+
if h != "" {
90+
host = h
91+
}
92+
if p != "" {
93+
port = p
94+
}
95+
96+
}
97+
98+
hostAndPort := net.JoinHostPort(host, port)
99+
100+
u := url.URL{
101+
Scheme: "http",
102+
Host: hostAndPort,
103+
Path: "/v1/version",
104+
}
105+
106+
return u, nil
107+
}
108+
109+
func getServerVersion(httpClient http.Client, url url.URL) (string, error) {
110+
resp, err := httpClient.Get(url.String())
111+
if err != nil {
112+
return "", err
113+
}
114+
defer resp.Body.Close()
115+
116+
if resp.StatusCode != http.StatusOK {
117+
return "", fmt.Errorf("request failed with status %d", resp.StatusCode)
118+
}
119+
120+
body, err := io.ReadAll(resp.Body)
121+
if err != nil {
122+
return "", err
123+
}
124+
125+
var serverResponse serverVersionResponse
126+
if err := json.Unmarshal(body, &serverResponse); err != nil {
127+
return "", err
128+
}
129+
130+
return serverResponse.Version, nil
131+
}
132+
133+
type serverVersionResponse struct {
42134
Version string `json:"version"`
43135
}
44136

137+
type versionResult struct {
138+
ClientVersion string `json:"version"`
139+
ServerVersion string `json:"serverVersion"`
140+
}
141+
45142
func (r versionResult) String() string {
46-
return fmt.Sprintf("%s v%s", r.AppName, r.Version)
143+
return fmt.Sprintf("client: %s\nserver: %s",
144+
r.ClientVersion, r.ServerVersion)
47145
}
48146

49147
func (r versionResult) Data() interface{} {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package version
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"strings"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
func TestServerVersion(t *testing.T) {
15+
clientVersion := "5.1-dev"
16+
17+
testCases := []struct {
18+
name string
19+
serverStub Tripper
20+
expectedResult versionResult
21+
host string
22+
}{
23+
{
24+
name: "return the server version when the server is up",
25+
serverStub: successServer,
26+
expectedResult: versionResult{
27+
ClientVersion: "5.1-dev",
28+
ServerVersion: "3.0",
29+
},
30+
host: "",
31+
},
32+
{
33+
name: "return error if default server is not listening",
34+
serverStub: failureServer,
35+
expectedResult: versionResult{
36+
ClientVersion: "5.1-dev",
37+
ServerVersion: fmt.Sprintf("n/a (cannot connect to the server http://%s:%s)", DefaultHostname, DefaultPort),
38+
},
39+
host: "",
40+
},
41+
{
42+
name: "return error if provided server is not listening",
43+
serverStub: failureServer,
44+
expectedResult: versionResult{
45+
ClientVersion: "5.1-dev",
46+
ServerVersion: "n/a (cannot connect to the server http://unreacheable:123)",
47+
},
48+
host: "unreacheable:123",
49+
},
50+
{
51+
name: "return error for server resopnse 500 Internal Server Error",
52+
serverStub: failureInternalServerError,
53+
expectedResult: versionResult{
54+
ClientVersion: "5.1-dev",
55+
ServerVersion: "n/a (cannot connect to the server http://unreacheable:123)",
56+
},
57+
host: "unreacheable:123",
58+
},
59+
}
60+
for _, tc := range testCases {
61+
t.Run(tc.name, func(t *testing.T) {
62+
// arrange
63+
httpClient := http.Client{}
64+
httpClient.Transport = tc.serverStub
65+
66+
// act
67+
result := doVersionHandler(httpClient, clientVersion, tc.host)
68+
69+
// assert
70+
require.Equal(t, tc.expectedResult, result)
71+
})
72+
}
73+
}
74+
75+
// Leverage the http.Client's RoundTripper
76+
// to return a canned response and bypass network calls.
77+
type Tripper func(*http.Request) (*http.Response, error)
78+
79+
func (t Tripper) RoundTrip(request *http.Request) (*http.Response, error) {
80+
return t(request)
81+
}
82+
83+
var successServer = Tripper(func(*http.Request) (*http.Response, error) {
84+
body := io.NopCloser(strings.NewReader(`{"version":"3.0"}`))
85+
return &http.Response{
86+
StatusCode: http.StatusOK,
87+
Body: body,
88+
}, nil
89+
})
90+
91+
var failureServer = Tripper(func(*http.Request) (*http.Response, error) {
92+
return nil, errors.New("connetion refused")
93+
})
94+
95+
var failureInternalServerError = Tripper(func(*http.Request) (*http.Response, error) {
96+
return &http.Response{
97+
StatusCode: http.StatusInternalServerError,
98+
Body: io.NopCloser(strings.NewReader("")),
99+
}, nil
100+
})

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /