Skip to content

Commit d80c52b

Browse files
committed
add in-memory marshal and unmarshal to complement existing encoder and decoder #1021
1 parent c026faa commit d80c52b

3 files changed

Lines changed: 211 additions & 2 deletions

File tree

request_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import (
2626
)
2727

2828
type AuthSuccess struct {
29-
ID, Message string
29+
ID string `xml:"Id"`
30+
Message string `xml:"Message"`
3031
}
3132

3233
type AuthError struct {

util.go

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"crypto/sha256"
1212
"encoding/binary"
1313
"encoding/hex"
14+
"encoding/json"
15+
"encoding/xml"
1416
"errors"
1517
"fmt"
1618
"io"
@@ -69,9 +71,74 @@ func (l *logger) output(format string, v ...any) {
6971
l.l.Printf(format, v...)
7072
}
7173

74+
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
75+
// In Memory JSON & XML Marshal and Unmarshal using Go package
76+
//_____________________________________________________________
77+
78+
var (
79+
// InMemoryJSONMarshal function performs the JSON marshalling completely in memory.
80+
//
81+
// c := resty.New()
82+
// defer c.Close()
83+
//
84+
// c.AddContentTypeEncoder("application/json", resty.InMemoryJSONMarshal)
85+
InMemoryJSONMarshal = func(w io.Writer, v any) error {
86+
jsonData, err := json.Marshal(v)
87+
if err != nil {
88+
return err
89+
}
90+
_, err = w.Write(jsonData)
91+
return err
92+
}
93+
94+
// InMemoryJSONUnmarshal function performs the JSON unmarshalling completely in memory.
95+
//
96+
// c := resty.New()
97+
// defer c.Close()
98+
//
99+
// c.AddContentTypeDecoder("application/json", resty.InMemoryJSONUnmarshal)
100+
InMemoryJSONUnmarshal = func(r io.Reader, v any) error {
101+
byteData, err := io.ReadAll(r)
102+
if err != nil {
103+
return err
104+
}
105+
return json.Unmarshal(byteData, v)
106+
}
107+
108+
// InMemoryXMLMarshal function performs the XML marshalling completely in memory.
109+
//
110+
// c := resty.New()
111+
// defer c.Close()
112+
//
113+
// c.AddContentTypeEncoder("application/xml", resty.InMemoryXMLMarshal)
114+
InMemoryXMLMarshal = func(w io.Writer, v any) error {
115+
xmlData, err := xml.Marshal(v)
116+
if err != nil {
117+
return err
118+
}
119+
_, err = w.Write(xmlData)
120+
return err
121+
}
122+
123+
// InMemoryJSONUnmarshal function performs the XML unmarshalling completely in memory.
124+
//
125+
// c := resty.New()
126+
// defer c.Close()
127+
//
128+
// c.AddContentTypeDecoder("application/xml", resty.InMemoryXMLUnmarshal)
129+
InMemoryXMLUnmarshal = func(r io.Reader, v any) error {
130+
byteData, err := io.ReadAll(r)
131+
if err != nil {
132+
return err
133+
}
134+
return xml.Unmarshal(byteData, v)
135+
}
136+
)
137+
72138
// credentials type is to hold an username and password information
73139
type credentials struct {
74-
Username, Password string
140+
Username string `json:"username"`
141+
Password string `json:"password"`
75142
}
76143

77144
// Clone method returns clone of c.

util_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ package resty
88
import (
99
"bytes"
1010
"errors"
11+
"fmt"
1112
"io"
13+
"net/http"
1214
"net/url"
1315
"os"
1416
"path/filepath"
@@ -180,6 +182,145 @@ func TestUtil_readMachineID(t *testing.T) {
180182
})
181183
}
182184

185+
func TestInMemoryJSONMarshalUnmarshal(t *testing.T) {
186+
t.Run("json encoder", func(t *testing.T) {
187+
user := &credentials{Username: "testuser", Password: "testpass"}
188+
buf := acquireBuffer()
189+
defer releaseBuffer(buf)
190+
err := InMemoryJSONMarshal(buf, user)
191+
assertNil(t, err)
192+
assertEqual(t, `{"username":"testuser","password":"testpass"}`, buf.String())
193+
})
194+
195+
t.Run("json encoder error", func(t *testing.T) {
196+
obj := &brokenMarshalJSON{}
197+
buf := acquireBuffer()
198+
defer releaseBuffer(buf)
199+
err := InMemoryJSONMarshal(buf, obj)
200+
assertNotNil(t, err)
201+
assertEqual(t, true, strings.Contains(err.Error(), "b0rk3d"))
202+
})
203+
204+
t.Run("json decoder", func(t *testing.T) {
205+
byteData := []byte(`{"username":"testuser","password":"testpass"}`)
206+
cred := &credentials{}
207+
err := InMemoryJSONUnmarshal(bytes.NewReader(byteData), cred)
208+
assertNil(t, err)
209+
assertEqual(t, "testuser", cred.Username)
210+
assertEqual(t, "testpass", cred.Password)
211+
})
212+
213+
t.Run("json decoder read error", func(t *testing.T) {
214+
cred := &credentials{}
215+
err := InMemoryJSONUnmarshal(&brokenReadCloser{}, cred)
216+
assertNotNil(t, err)
217+
assertEqual(t, err.Error(), "read error")
218+
})
219+
220+
t.Run("json decoder error", func(t *testing.T) {
221+
byteData := []byte(`"username":"testuser","password":"testpass"}`)
222+
cred := &credentials{}
223+
err := InMemoryJSONUnmarshal(bytes.NewReader(byteData), cred)
224+
assertNotNil(t, err)
225+
assertEqual(t, true, strings.Contains(err.Error(), "invalid character ':' after top-level value"))
226+
})
227+
}
228+
229+
func TestInMemoryXMLMarshalUnmarshal(t *testing.T) {
230+
t.Run("xml encoder", func(t *testing.T) {
231+
user := &credentials{Username: "testuser", Password: "testpass"}
232+
buf := acquireBuffer()
233+
defer releaseBuffer(buf)
234+
err := InMemoryXMLMarshal(buf, user)
235+
assertNil(t, err)
236+
assertEqual(t, `<credentials><Username>testuser</Username><Password>testpass</Password></credentials>`, buf.String())
237+
})
238+
239+
t.Run("xml encoder error", func(t *testing.T) {
240+
obj := &brokenMarshalXML{}
241+
buf := acquireBuffer()
242+
defer releaseBuffer(buf)
243+
err := InMemoryXMLMarshal(buf, obj)
244+
assertNotNil(t, err)
245+
assertEqual(t, err.Error(), "b0rk3d")
246+
})
247+
248+
t.Run("xml decoder", func(t *testing.T) {
249+
byteData := []byte(`<?xml version="1.0" encoding="UTF-8"?><credentials><Username>testuser</Username><Password>testpass</Password></credentials>`)
250+
cred := &credentials{}
251+
err := InMemoryXMLUnmarshal(bytes.NewReader(byteData), cred)
252+
assertNil(t, err)
253+
assertEqual(t, "testuser", cred.Username)
254+
assertEqual(t, "testpass", cred.Password)
255+
})
256+
257+
t.Run("xml decoder read error", func(t *testing.T) {
258+
cred := &credentials{}
259+
err := InMemoryXMLUnmarshal(&brokenReadCloser{}, cred)
260+
assertNotNil(t, err)
261+
assertEqual(t, err.Error(), "read error")
262+
})
263+
264+
t.Run("xml decoder error", func(t *testing.T) {
265+
byteData := []byte(`<?xml version="1.0" encoding="UTF-8"?><Username>testuser</Username><Password>testpass</Password></credentials>`)
266+
cred := &credentials{}
267+
err := InMemoryJSONUnmarshal(bytes.NewReader(byteData), cred)
268+
fmt.Println(err)
269+
assertNotNil(t, err)
270+
assertEqual(t, err.Error(), "invalid character '<' looking for beginning of value")
271+
})
272+
}
273+
274+
func TestInMemoryJSONPost(t *testing.T) {
275+
ts := createPostServer(t)
276+
defer ts.Close()
277+
278+
user := &credentials{Username: "testuser", Password: "testpass"}
279+
assertEqual(t, "Username: **********, Password: **********", user.String())
280+
281+
c := dcnl().
282+
AddContentTypeEncoder(jsonContentType, InMemoryJSONMarshal).
283+
AddContentTypeDecoder(jsonContentType, InMemoryJSONUnmarshal)
284+
285+
r := c.R().
286+
SetHeader(hdrContentTypeKey, jsonContentType).
287+
SetBody(user).
288+
SetResult(&AuthSuccess{})
289+
290+
resp, err := r.Post(ts.URL + "/login")
291+
authResp := resp.Result().(*AuthSuccess)
292+
293+
assertError(t, err)
294+
assertEqual(t, http.StatusOK, resp.StatusCode())
295+
assertEqual(t, int64(50), resp.Size())
296+
assertEqual(t, authResp.ID, "success")
297+
assertEqual(t, authResp.Message, "login successful")
298+
}
299+
300+
func TestInMemoryXMLPost(t *testing.T) {
301+
ts := createPostServer(t)
302+
defer ts.Close()
303+
304+
xmlContentType := "application/xml"
305+
c := dcnl().
306+
AddContentTypeEncoder(xmlContentType, InMemoryXMLMarshal).
307+
AddContentTypeDecoder(xmlContentType, InMemoryXMLUnmarshal)
308+
309+
resp, err := c.R().
310+
SetHeader(hdrContentTypeKey, xmlContentType).
311+
SetBody(credentials{Username: "testuser", Password: "testpass"}).
312+
SetResult(&AuthSuccess{}).
313+
Post(ts.URL + "/login")
314+
315+
authResp := resp.Result().(*AuthSuccess)
316+
317+
assertError(t, err)
318+
assertEqual(t, http.StatusOK, resp.StatusCode())
319+
assertEqual(t, int64(116), resp.Size())
320+
assertEqual(t, authResp.ID, "success")
321+
assertEqual(t, authResp.Message, "login successful")
322+
}
323+
183324
// This test methods exist for test coverage purpose
184325
// to validate the getter and setter
185326
func TestUtilMiscTestCoverage(t *testing.T) {

0 commit comments

Comments
 (0)