Skip to content

Commit 9541639

Browse files
committed
Second batch of XML-to-JSON conversion (#8)
1 parent 5a9e04b commit 9541639

35 files changed

Lines changed: 3717 additions & 2183 deletions

upcloud/account.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
package upcloud
22

3+
import "encoding/json"
4+
35
// Account represents an account
46
type Account struct {
57
Credits float64 `xml:"credits"`
68
UserName string `xml:"username"`
79
}
10+
11+
// UnmarshalJSON is a custom unmarshaller that deals with
12+
// deeply embedded values.
13+
func (s *Account) UnmarshalJSON(b []byte) error {
14+
type localAccount Account
15+
16+
v := struct {
17+
Account localAccount `json:"account"`
18+
}{}
19+
err := json.Unmarshal(b, &v)
20+
if err != nil {
21+
return err
22+
}
23+
24+
(*s) = Account(v.Account)
25+
26+
return nil
27+
}

upcloud/account_test.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package upcloud
22

33
import (
4-
"encoding/xml"
4+
"encoding/json"
55
"testing"
66

77
"github.com/stretchr/testify/assert"
88
)
99

1010
// TestUnmarshalAccount tests that Account objects unmarshal correctly
1111
func TestUnmarshalAccount(t *testing.T) {
12-
originalXML := `<?xml version="1.0" encoding="utf-8"?>
13-
<account>
14-
<credits>22465.536</credits>
15-
<username>foobar</username>
16-
</account>`
12+
originalJSON := `
13+
{
14+
"account": {
15+
"credits": 9972.2324,
16+
"username": "username"
17+
}
18+
}
19+
`
1720

1821
account := Account{}
19-
err := xml.Unmarshal([]byte(originalXML), &account)
20-
assert.Nil(t, err)
21-
assert.Equal(t, 22465.536, account.Credits)
22-
assert.Equal(t, "foobar", account.UserName)
22+
err := json.Unmarshal([]byte(originalJSON), &account)
23+
assert.NoError(t, err)
24+
assert.Equal(t, 9972.2324, account.Credits)
25+
assert.Equal(t, "username", account.UserName)
2326
}

upcloud/client/client.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func (c *Client) PerformPutRequest(url string, requestBody []byte) ([]byte, erro
132132
bodyReader = bytes.NewBuffer(requestBody)
133133
}
134134

135-
request, err := http.NewRequest("PUT", url, bodyReader)
135+
request, err := http.NewRequest(http.MethodPut, url, bodyReader)
136136

137137
if err != nil {
138138
return nil, err
@@ -141,6 +141,23 @@ func (c *Client) PerformPutRequest(url string, requestBody []byte) ([]byte, erro
141141
return c.performRequest(request)
142142
}
143143

144+
// PerformJSONPutRequest performs a PUT request to the specified URL and returns the response body and eventual errors
145+
func (c *Client) PerformJSONPutRequest(url string, requestBody []byte) ([]byte, error) {
146+
var bodyReader io.Reader
147+
148+
if requestBody != nil {
149+
bodyReader = bytes.NewBuffer(requestBody)
150+
}
151+
152+
request, err := http.NewRequest(http.MethodPut, url, bodyReader)
153+
154+
if err != nil {
155+
return nil, err
156+
}
157+
158+
return c.performJSONRequest(request)
159+
}
160+
144161
// PerformDeleteRequest performs a DELETE request to the specified URL and returns the response body and eventual errors
145162
func (c *Client) PerformDeleteRequest(url string) error {
146163
request, err := http.NewRequest(http.MethodDelete, url, nil)

upcloud/request/server.go

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ func (r *GetServerDetailsRequest) RequestURL() string {
3333
return fmt.Sprintf("/server/%s", r.UUID)
3434
}
3535

36+
// CreateServerIPAddressSlice is a slice of strings
37+
// It exists to allow for a custom JSON marshaller.
3638
type CreateServerIPAddressSlice []CreateServerIPAddress
3739

40+
// MarshalJSON is a custom marshaller that deals with
41+
// deeply embedded values.
3842
func (s CreateServerIPAddressSlice) MarshalJSON() ([]byte, error) {
3943
v := struct {
4044
IPAddress []CreateServerIPAddress `json:"ip_address"`
@@ -44,8 +48,12 @@ func (s CreateServerIPAddressSlice) MarshalJSON() ([]byte, error) {
4448
return json.Marshal(v)
4549
}
4650

51+
// CreateServerStorageDeviceSlice is a slice of strings
52+
// It exists to allow for a custom JSON marshaller.
4753
type CreateServerStorageDeviceSlice []upcloud.CreateServerStorageDevice
4854

55+
// MarshalJSON is a custom marshaller that deals with
56+
// deeply embedded values.
4957
func (s CreateServerStorageDeviceSlice) MarshalJSON() ([]byte, error) {
5058
v := struct {
5159
StorageDevice []upcloud.CreateServerStorageDevice `json:"storage_device"`
@@ -81,6 +89,8 @@ type CreateServerRequest struct {
8189
Zone string `xml:"zone" json:"zone"`
8290
}
8391

92+
// MarshalJSON is a custom marshaller that deals with
93+
// deeply embedded values.
8494
func (r CreateServerRequest) MarshalJSON() ([]byte, error) {
8595
type localCreateServerRequest CreateServerRequest
8696
v := struct {
@@ -96,8 +106,12 @@ func (r *CreateServerRequest) RequestURL() string {
96106
return "/server"
97107
}
98108

109+
// SSHKeySlice is a slice of strings
110+
// It exists to allow for a custom JSON unmarshaller.
99111
type SSHKeySlice []string
100112

113+
// UnmarshalJSON is a custom unmarshaller that deals with
114+
// deeply embedded values.
101115
func (s *SSHKeySlice) UnmarshalJSON(b []byte) error {
102116
v := struct {
103117
SSHKey []string `json:"ssh_key"`
@@ -112,6 +126,8 @@ func (s *SSHKeySlice) UnmarshalJSON(b []byte) error {
112126
return nil
113127
}
114128

129+
// MarshalJSON is a custom marshaller that deals with
130+
// deeply embedded values.
115131
func (s SSHKeySlice) MarshalJSON() ([]byte, error) {
116132
v := struct {
117133
SSHKey []string `json:"ssh_key"`
@@ -158,19 +174,32 @@ func (r *StartServerRequest) RequestURL() string {
158174

159175
// StopServerRequest represents a request to stop a server
160176
type StopServerRequest struct {
161-
XMLName xml.Name `xml:"stop_server"`
177+
XMLName xml.Name `xml:"stop_server" json:"-"`
162178

163-
UUID string `xml:"-"`
179+
UUID string `xml:"-" json:"-"`
164180

165-
StopType string `xml:"stop_type,omitempty"`
166-
Timeout time.Duration `xml:"timeout,omitempty"`
181+
StopType string `xml:"stop_type,omitempty" json:"stop_type,omitempty"`
182+
Timeout time.Duration `xml:"timeout,omitempty" json:"timeout,omitempty,string"`
167183
}
168184

169185
// RequestURL implements the Request interface
170186
func (r *StopServerRequest) RequestURL() string {
171187
return fmt.Sprintf("/server/%s/stop", r.UUID)
172188
}
173189

190+
// MarshalJSON is a custom marshaller that deals with
191+
// deeply embedded values.
192+
func (r StopServerRequest) MarshalJSON() ([]byte, error) {
193+
type localStopServerRequest StopServerRequest
194+
v := struct {
195+
StopServerRequest localStopServerRequest `json:"stop_server"`
196+
}{}
197+
v.StopServerRequest = localStopServerRequest(r)
198+
v.StopServerRequest.Timeout = v.StopServerRequest.Timeout / 1e9
199+
200+
return json.Marshal(&v)
201+
}
202+
174203
// MarshalXML implements a custom marshaller for StopServerRequest which converts the timeout to seconds
175204
func (r *StopServerRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
176205
type Alias StopServerRequest
@@ -186,20 +215,33 @@ func (r *StopServerRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) e
186215

187216
// RestartServerRequest represents a request to restart a server
188217
type RestartServerRequest struct {
189-
XMLName xml.Name `xml:"restart_server"`
218+
XMLName xml.Name `xml:"restart_server" json:"-"`
190219

191-
UUID string `xml:"-"`
220+
UUID string `xml:"-" json:"-"`
192221

193-
StopType string `xml:"stop_type,omitempty"`
194-
Timeout time.Duration `xml:"timeout,omitempty"`
195-
TimeoutAction string `xml:"timeout_action,omitempty"`
222+
StopType string `xml:"stop_type,omitempty" json:"stop_type,omitempty"`
223+
Timeout time.Duration `xml:"timeout,omitempty" json:"timeout,omitempty,string"`
224+
TimeoutAction string `xml:"timeout_action,omitempty" json:"timeout_action,omitempty"`
196225
}
197226

198227
// RequestURL implements the Request interface
199228
func (r *RestartServerRequest) RequestURL() string {
200229
return fmt.Sprintf("/server/%s/restart", r.UUID)
201230
}
202231

232+
// MarshalJSON is a custom marshaller that deals with
233+
// deeply embedded values.
234+
func (r RestartServerRequest) MarshalJSON() ([]byte, error) {
235+
type localRestartServerRequest RestartServerRequest
236+
v := struct {
237+
RestartServerRequest localRestartServerRequest `json:"restart_server"`
238+
}{}
239+
v.RestartServerRequest = localRestartServerRequest(r)
240+
v.RestartServerRequest.Timeout = v.RestartServerRequest.Timeout / 1e9
241+
242+
return json.Marshal(&v)
243+
}
244+
203245
// MarshalXML implements a custom marshaller for RestartServerRequest which converts the timeout to seconds
204246
func (r *RestartServerRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
205247
type Alias RestartServerRequest
@@ -215,26 +257,36 @@ func (r *RestartServerRequest) MarshalXML(e *xml.Encoder, start xml.StartElement
215257

216258
// ModifyServerRequest represents a request to modify a server
217259
type ModifyServerRequest struct {
218-
XMLName xml.Name `xml:"server"`
260+
XMLName xml.Name `xml:"server" json:"-"`
219261

220-
UUID string `xml:"-"`
262+
UUID string `xml:"-" json:"-"`
221263

222-
AvoidHost string `xml:"avoid_host,omitempty"`
223-
BootOrder string `xml:"boot_order,omitempty"`
224-
// TODO: Investigate correct type and format
225-
CoreNumber string `xml:"core_number,omitempty"`
264+
AvoidHost string `xml:"avoid_host,omitempty" json:"avoid_host,omitempty"`
265+
BootOrder string `xml:"boot_order,omitempty" json:"boot_order,omitempty"`
266+
CoreNumber int `xml:"core_number,omitempty" json:"core_number,omitempty,string"`
226267
// TODO: Convert to boolean
227-
Firewall string `xml:"firewall,omitempty"`
228-
Hostname string `xml:"hostname,omitempty"`
229-
// TODO: Investigate correct type and format
230-
MemoryAmount string `xml:"memory_amount,omitempty"`
231-
Plan string `xml:"plan,omitempty"`
232-
TimeZone string `xml:"timezone,omitempty"`
233-
Title string `xml:"title,omitempty"`
234-
VideoModel string `xml:"video_model,omitempty"`
268+
Firewall string `xml:"firewall,omitempty" json:"firewall,omitempty"`
269+
Hostname string `xml:"hostname,omitempty" json:"hostname,omitempty"`
270+
MemoryAmount int `xml:"memory_amount,omitempty" json:"memory_amount,omitempty,string"`
271+
Plan string `xml:"plan,omitempty" json:"plan,omitempty"`
272+
TimeZone string `xml:"timezone,omitempty" json:"timezone,omitempty"`
273+
Title string `xml:"title,omitempty" json:"title,omitempty"`
274+
VideoModel string `xml:"video_model,omitempty" json:"video_model,omitempty"`
235275
// TODO: Convert to boolean
236-
VNC string `xml:"vnc,omitempty"`
237-
VNCPassword string `xml:"vnc_password,omitempty"`
276+
VNC string `xml:"vnc,omitempty" json:"vnc,omitempty"`
277+
VNCPassword string `xml:"vnc_password,omitempty" json:"vnc_password,omitempty"`
278+
}
279+
280+
// MarshalJSON is a custom marshaller that deals with
281+
// deeply embedded values.
282+
func (r ModifyServerRequest) MarshalJSON() ([]byte, error) {
283+
type localModifyServerRequest ModifyServerRequest
284+
v := struct {
285+
ModifyServerRequest localModifyServerRequest `json:"server"`
286+
}{}
287+
v.ModifyServerRequest = localModifyServerRequest(r)
288+
289+
return json.Marshal(&v)
238290
}
239291

240292
// RequestURL implements the Request interface

upcloud/request/server_test.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package request
22

33
import (
44
"encoding/json"
5-
"encoding/xml"
65
"testing"
76
"time"
87

@@ -124,14 +123,21 @@ func TestStartServerRequest(t *testing.T) {
124123
func TestStopServerRequest(t *testing.T) {
125124
request := StopServerRequest{
126125
UUID: "foo",
127-
StopType: ServerStopTypeHard,
126+
StopType: ServerStopTypeSoft,
128127
Timeout: time.Minute * 5,
129128
}
130129

131-
expectedXML := "<stop_server><timeout>300</timeout><stop_type>hard</stop_type></stop_server>"
132-
actualXML, err := xml.Marshal(&request)
130+
expectedJSON := `
131+
{
132+
"stop_server": {
133+
"stop_type": "soft",
134+
"timeout": "300"
135+
}
136+
}
137+
`
138+
actualJSON, err := json.MarshalIndent(&request, "", " ")
133139
assert.Nil(t, err)
134-
assert.Equal(t, expectedXML, string(actualXML))
140+
assert.JSONEq(t, expectedJSON, string(actualJSON))
135141
assert.Equal(t, "/server/foo/stop", request.RequestURL())
136142
}
137143

@@ -144,24 +150,44 @@ func TestRestartServerRequest(t *testing.T) {
144150
TimeoutAction: RestartTimeoutActionDestroy,
145151
}
146152

147-
expectedXML := "<restart_server><timeout>300</timeout><stop_type>soft</stop_type><timeout_action>destroy</timeout_action></restart_server>"
148-
actualXML, err := xml.Marshal(&request)
153+
expectedJSON := `
154+
{
155+
"restart_server": {
156+
"stop_type": "soft",
157+
"timeout": "300",
158+
"timeout_action": "destroy"
159+
}
160+
}
161+
`
162+
actualJSON, err := json.Marshal(&request)
149163
assert.Nil(t, err)
150-
assert.Equal(t, expectedXML, string(actualXML))
164+
assert.JSONEq(t, expectedJSON, string(actualJSON))
151165
assert.Equal(t, "/server/foo/restart", request.RequestURL())
152166
}
153167

154168
// TestModifyServerRequest tests that ModifyServerRequest objects behave correctly
155169
func TestModifyServerRequest(t *testing.T) {
156170
request := ModifyServerRequest{
157-
UUID: "foo",
158-
Title: "Modified server",
171+
UUID: "foo",
172+
Title: "Modified server",
173+
CoreNumber: 8,
174+
MemoryAmount: 16384,
175+
Plan: "custom",
159176
}
160177

161-
expectedXML := "<server><title>Modified server</title></server>"
162-
actualXML, err := xml.Marshal(&request)
178+
expectedJSON := `
179+
{
180+
"server" : {
181+
"title": "Modified server",
182+
"core_number": "8",
183+
"memory_amount": "16384",
184+
"plan" : "custom"
185+
}
186+
}
187+
`
188+
actualJSON, err := json.Marshal(&request)
163189
assert.Nil(t, err)
164-
assert.Equal(t, expectedXML, string(actualXML))
190+
assert.JSONEq(t, expectedJSON, string(actualJSON))
165191
assert.Equal(t, "/server/foo", request.RequestURL())
166192
}
167193

0 commit comments

Comments
 (0)