Skip to content

Commit

Permalink
Merge pull request #26 from Viriathus1/xml-support
Browse files Browse the repository at this point in the history
XML support
  • Loading branch information
hugo-andrade authored Nov 17, 2024
2 parents 0aa6e93 + 46e0e59 commit ee7ded2
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Fast Shot is a robust, feature-rich, and highly configurable HTTP client for Go.
* Advanced retry mechanism with customizable backoff strategies
* Client-side load balancing for improved reliability
* JSON request and response support
* XML request and response support
* Timeout and redirect control
* Proxy support
* Extensible and customizable for specific needs
Expand Down
1 change: 1 addition & 0 deletions constant/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const (
ErrMsgCreateRequest = "failed to create request"
ErrMsgEmptyBaseURL = "empty base URL"
ErrMsgMarshalJSON = "failed to marshal JSON"
ErrMsgMarshalXML = "failed to marshal XML"
ErrMsgParseProxyURL = "failed to parse proxy URL"
ErrMsgParseQueryString = "failed to parse query string"
ErrMsgParseURL = "failed to parse URL"
Expand Down
2 changes: 2 additions & 0 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,8 @@ type BodyWrapper interface {
io.ReadCloser
ReadAsJSON(obj interface{}) error
WriteAsJSON(obj interface{}) error
ReadAsXML(obj interface{}) error
WriteAsXML(obj interface{}) error
ReadAsString() (string, error)
WriteAsString(body string) error
Set(body io.Reader) error
Expand Down
10 changes: 10 additions & 0 deletions mock/wrapper_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ func (m *BodyWrapper) WriteAsJSON(obj interface{}) error {
return args.Error(0)
}

func (m *BodyWrapper) ReadAsXML(obj interface{}) error {
args := m.Called(obj)
return args.Error(0)
}

func (m *BodyWrapper) WriteAsXML(obj interface{}) error {
args := m.Called(obj)
return args.Error(0)
}

func (m *BodyWrapper) ReadAsString() (string, error) {
args := m.Called()
return args.String(0), args.Error(1)
Expand Down
9 changes: 9 additions & 0 deletions request_builder_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,12 @@ func (b *RequestBodyBuilder) AsJSON(obj interface{}) *RequestBuilder {
}
return b.parentBuilder
}

// AsXML sets the body as XML.
func (b *RequestBodyBuilder) AsXML(obj interface{}) *RequestBuilder {
err := b.requestConfig.Body().WriteAsXML(obj)
if err != nil {
b.requestConfig.Validations().Add(errors.Join(errors.New(constant.ErrMsgMarshalXML), err))
}
return b.parentBuilder
}
26 changes: 26 additions & 0 deletions request_builder_body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@ func TestRequestBodyBuilder(t *testing.T) {
},
expectedError: errors.Join(errors.New(constant.ErrMsgMarshalJSON), mockedErr),
},
{
name: "AsXML success",
setup: func(rb *RequestBodyBuilder) {
mockBody := new(mock.BodyWrapper)
mockBody.On("WriteAsXML", tmock.Anything).Return(nil)
rb.requestConfig.body = mockBody
},
method: func(rb *RequestBodyBuilder) *RequestBuilder {
body := `<example><Key>value</Key></example>`
return rb.AsXML(&body)
},
expectedError: nil,
},
{
name: "AsXML failure",
setup: func(rb *RequestBodyBuilder) {
mockBody := new(mock.BodyWrapper)
mockBody.On("WriteAsXML", tmock.Anything).Return(mockedErr)
rb.requestConfig.body = mockBody
},
method: func(rb *RequestBodyBuilder) *RequestBuilder {
body := `<example><Key>value</Key></example>`
return rb.AsXML(&body)
},
expectedError: errors.Join(errors.New(constant.ErrMsgMarshalXML), mockedErr),
},
}

for _, tt := range tests {
Expand Down
6 changes: 6 additions & 0 deletions response_fluent_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,9 @@ func (b *ResponseFluentBody) AsJSON(v interface{}) error {

return b.body.ReadAsJSON(v)
}

func (b *ResponseFluentBody) AsXML(v interface{}) error {
defer b.Close()

return b.body.ReadAsXML(v)
}
47 changes: 47 additions & 0 deletions response_fluent_body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,53 @@ func TestResponseFluentBody(t *testing.T) {
expected: map[string]string(nil),
expectedError: errors.New("json error"),
},
{
name: "AsXML success",
setup: func(m *mock.BodyWrapper) {
m.On("ReadAsXML", tmock.Anything).Run(func(args tmock.Arguments) {
arg := args.Get(0).(*struct {
Key string `xml:"Key"`
})
*arg = struct {
Key string `xml:"Key"`
}{
Key: "value",
}
}).Return(nil).Once()
m.On("Close").Return(nil).Once()
},
method: func(rb *ResponseFluentBody) (interface{}, error) {
result := struct {
Key string `xml:"Key"`
}{}
err := rb.AsXML(&result)
return result, err
},
expected: struct {
Key string `xml:"Key"`
}{
Key: "value",
},
expectedError: nil,
},
{
name: "AsXML error",
setup: func(m *mock.BodyWrapper) {
m.On("ReadAsXML", tmock.Anything).Return(errors.New("xml error")).Once()
m.On("Close").Return(nil).Once()
},
method: func(rb *ResponseFluentBody) (interface{}, error) {
result := struct {
Key string `xml:"Key"`
}{}
err := rb.AsXML(&result)
return result, err
},
expected: struct {
Key string `xml:"Key"`
}{},
expectedError: errors.New("xml error"),
},
}

for _, tt := range tests {
Expand Down
32 changes: 32 additions & 0 deletions wrapper_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package fastshot
import (
"bytes"
"encoding/json"
"encoding/xml"
"io"
"strings"
"sync"
Expand Down Expand Up @@ -50,6 +51,19 @@ func (w *BufferedBody) WriteAsJSON(obj interface{}) error {
return json.NewEncoder(w.buffer).Encode(obj)
}

func (w *BufferedBody) ReadAsXML(obj interface{}) error {
w.mutex.RLock()
defer w.mutex.RUnlock()
return xml.NewDecoder(bytes.NewReader(w.buffer.Bytes())).Decode(obj)
}

func (w *BufferedBody) WriteAsXML(obj interface{}) error {
w.mutex.Lock()
defer w.mutex.Unlock()
w.buffer.Reset()
return xml.NewEncoder(w.buffer).Encode(obj)
}

func (w *BufferedBody) ReadAsString() (string, error) {
w.mutex.RLock()
defer w.mutex.RUnlock()
Expand Down Expand Up @@ -113,6 +127,24 @@ func (w *UnbufferedBody) WriteAsJSON(obj interface{}) error {
return nil
}

func (w *UnbufferedBody) ReadAsXML(obj interface{}) error {
w.mutex.RLock()
defer w.mutex.RUnlock()
return xml.NewDecoder(w.reader).Decode(obj)
}

func (w *UnbufferedBody) WriteAsXML(obj interface{}) error {
w.mutex.Lock()
defer w.mutex.Unlock()
var buf bytes.Buffer
err := xml.NewEncoder(&buf).Encode(obj)
if err != nil {
return err
}
w.reader = io.NopCloser(&buf)
return nil
}

func (w *UnbufferedBody) ReadAsString() (string, error) {
w.mutex.RLock()
defer w.mutex.RUnlock()
Expand Down
50 changes: 50 additions & 0 deletions wrapper_body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,56 @@ func TestWrapperBody_Buffered(t *testing.T) {
expected: nil,
expectedError: nil,
},
{
name: "ReadAsXML success",
setup: func(b *BufferedBody) {
b.buffer.WriteString(`<example><Key>value</Key></example>`)
},
method: func(b *BufferedBody) (interface{}, error) {
result := struct {
Key string `xml:"Key"`
}{}
err := b.ReadAsXML(&result)
return result, err
},
expected: struct {
Key string `xml:"Key"`
}{
Key: "value",
},
expectedError: nil,
},
{
name: "ReadAsXML error",
setup: func(b *BufferedBody) {
b.buffer.WriteString(`<>invalid xml`)
},
method: func(b *BufferedBody) (interface{}, error) {
result := struct {
Key string `xml:"Key"`
}{}
err := b.ReadAsXML(&result)
return nil, err
},
expected: nil,
expectedError: errors.New("XML syntax error on line 1: expected element name after <"),
},
{
name: "WriteAsXML success",
setup: func(b *BufferedBody) {
// No setup needed
},
method: func(b *BufferedBody) (interface{}, error) {
result := struct {
Key string `xml:"Key"`
}{
Key: "value",
}
return nil, b.WriteAsXML(&result)
},
expected: nil,
expectedError: nil,
},
{
name: "ReadAsString success",
setup: func(b *BufferedBody) {
Expand Down

0 comments on commit ee7ded2

Please sign in to comment.