Skip to content

Commit

Permalink
Merge pull request #64 from RealImage/enhance/unmarhsal-error
Browse files Browse the repository at this point in the history
Addresses issue - [Differentiate fault and response unmarshal error]
  • Loading branch information
tiaguinho authored Jul 6, 2020
2 parents 9ad5306 + fefe68c commit c64bd2e
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 4 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
module github.com/tiaguinho/gosoap

require (
github.com/google/go-cmp v0.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
golang.org/x/text v0.3.2 // indirect
gotest.tools v2.2.0+incompatible
)

go 1.13
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
Expand All @@ -7,3 +11,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
37 changes: 33 additions & 4 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,45 @@ type Response struct {
Payload []byte
}

// FaultError implements error interface
type FaultError struct {
fault *Fault
}

func (e FaultError) Error() string {
if e.fault != nil {
return e.fault.String()
}

return ""
}

// IsFault returns whether the given error is a fault error or not.
//
// IsFault will return false when the error could not be typecasted to FaultError, because
// every fault error should have it's dynamic type as FaultError.
func IsFault(err error) bool {
if _, ok := err.(FaultError); !ok {
return false
}

return true
}

// Unmarshal get the body and unmarshal into the interface
func (r *Response) Unmarshal(v interface{}) error {
if len(r.Body) == 0 {
return fmt.Errorf("Body is empty")
}

var f Fault
xml.Unmarshal(r.Body, &f)
if f.Code != "" {
return fmt.Errorf("[%s]: %s", f.Code, f.Description)
var fault Fault
err := xml.Unmarshal(r.Body, &fault)
if err != nil {
return fmt.Errorf("error unmarshalling the body to Fault: %v", err.Error())
}

if fault.Code != "" {
return FaultError{fault: &fault}
}

return xml.Unmarshal(r.Body, v)
Expand Down
128 changes: 128 additions & 0 deletions response_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package gosoap

import (
"encoding/xml"
"fmt"
"testing"

"gotest.tools/assert"
)

func TestUnmarshal(t *testing.T) {
var testCases = []struct {
description string
response *Response
decodeStruct interface{}
isFaultError bool
}{
{
description: "case: fault error",
response: &Response{
Body: []byte(`
<soap:Fault>
<faultcode>soap:Server</faultcode>
<faultstring>Qube.Mama.SoapException: The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
The remote server returned an error: (550) File unavailable (e.g., file not found, no access).
</faultstring>
<detail>
</detail>
</soap:Fault>
`),
},
decodeStruct: &struct{}{},
isFaultError: true,
},
{
description: "case: unmarshal error",
response: &Response{
Body: []byte(`
<GetJobsByIdsResponse
xmlns="http://webservices.qubecinema.com/XP/Usher/2009-09-29/">
<GetJobsByIdsResult>
<JobInfo>
<ID>9e7d58d9-6f62-43e3-b189-5b1b58eea629</ID>
<Status>Completed</Status>
<Progress>0</Progress>
<VerificationProgress>0</VerificationProgress>
<EstimatedCompletionTime>0</EstimatedCompletionTime>
</JobInfo>
</GetJobsByIdsResult>
</GetJobsByIdsResponse>
`),
},
decodeStruct: &struct {
XMLName xml.Name `xml:"GetJobsByIsResponse"`
GetJobsByIDsResult string
}{},
isFaultError: false,
},
{
description: "case: nil error",
response: &Response{
Body: []byte(`
<GetJobsByIdsResponse
xmlns="http://webservices.qubecinema.com/XP/Usher/2009-09-29/">
<GetJobsByIdsResult>
<JobInfo>
<ID>9e7d58d9-6f62-43e3-b189-5b1b58eea629</ID>
<Status>Completed</Status>
<Progress>0</Progress>
<VerificationProgress>0</VerificationProgress>
<EstimatedCompletionTime>0</EstimatedCompletionTime>
</JobInfo>
</GetJobsByIdsResult>
</GetJobsByIdsResponse>
`),
},
decodeStruct: &struct {
XMLName xml.Name `xml:"GetJobsByIdsResponse"`
GetJobsByIDsResult string
}{},
isFaultError: false,
},
}

for _, testCase := range testCases {
t.Logf("running %v test case", testCase.description)

err := testCase.response.Unmarshal(testCase.decodeStruct)
assert.Equal(t, testCase.isFaultError, IsFault(err))
}
}

func TestIsFault(t *testing.T) {
var testCases = []struct {
description string
err error
expectedIsFaultError bool
}{
{
description: "case: fault error",
err: FaultError{
fault: &Fault{
Code: "SOAP-ENV:Client",
},
},
expectedIsFaultError: true,
},
{
description: "case: unmarshal error",
err: fmt.Errorf("unmarshall err: .."),
expectedIsFaultError: false,
},
{
description: "case: nil error",
err: nil,
expectedIsFaultError: false,
},
}

for _, testCase := range testCases {
t.Logf("running %v test case", testCase.description)

isFaultErr := IsFault(testCase.err)
assert.Equal(t, testCase.expectedIsFaultError, isFaultErr)
}
}
6 changes: 6 additions & 0 deletions wsdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package gosoap

import (
"encoding/xml"
"fmt"
"io"
"net/http"
"net/url"
Expand Down Expand Up @@ -211,8 +212,13 @@ func (wsdl *wsdlDefinitions) GetSoapActionFromWsdlOperation(operation string) st
}

// Fault response
// Fault implements Stringer interface
type Fault struct {
Code string `xml:"faultcode"`
Description string `xml:"faultstring"`
Detail string `xml:"detail"`
}

func (f *Fault) String() string {
return fmt.Sprintf("[%s]: %s", f.Code, f.Description)
}
26 changes: 26 additions & 0 deletions wsdl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"runtime"
"strings"
"testing"

"gotest.tools/assert"
)

func Test_getWsdlBody(t *testing.T) {
Expand Down Expand Up @@ -66,3 +68,27 @@ func Test_getWsdlBody(t *testing.T) {
})
}
}

func TestFaultString(t *testing.T) {
var testCases = []struct {
description string
fault *Fault
expectedFaultStr string
}{
{
description: "success case: fault string",
fault: &Fault{
Code: "soap:SERVER",
Description: "soap exception",
},
expectedFaultStr: "[soap:SERVER]: soap exception",
},
}

for _, testCase := range testCases {
t.Logf("running %v testCase", testCase.description)

faultStr := testCase.fault.String()
assert.Equal(t, testCase.expectedFaultStr, faultStr)
}
}

0 comments on commit c64bd2e

Please sign in to comment.