Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 77 additions & 75 deletions fixtures/epcis/epcisquery.src

Large diffs are not rendered by default.

57 changes: 37 additions & 20 deletions gowsdl.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"io"
"log"
"net"
"net/http"
Expand All @@ -36,18 +36,20 @@ type GoWSDL struct {
wsdl *WSDL
resolvedXSDExternals map[string]bool
currentRecursionLevel uint8
currentNamespace string
currentSchema *XSDSchema
typeResolver *typeResolver
}

// Method setNS sets (and returns) the currently active XML namespace.
func (g *GoWSDL) setNS(ns string) string {
g.currentNamespace = ns
return ns
// setCurrentSchema sets (and returns) the currently active XSD schema.
// This may be consumed from within templates in order to help resolve namespaces.
func (g *GoWSDL) setCurrentSchema(s *XSDSchema) *XSDSchema {
g.currentSchema = s
return s
}

// Method setNS returns the currently active XML namespace.
func (g *GoWSDL) getNS() string {
return g.currentNamespace
// getCurrentSchema returns the currently active XSD schema.
func (g *GoWSDL) getCurrentSchema() *XSDSchema {
return g.currentSchema
}

var cacheDir = filepath.Join(os.TempDir(), "gowsdl-cache")
Expand Down Expand Up @@ -82,10 +84,10 @@ func downloadFile(url string, ignoreTLS bool) ([]byte, error) {

defer resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("Received response code %d", resp.StatusCode)
return nil, fmt.Errorf("received response code %d while fetching %s", resp.StatusCode, url)
}

data, err := ioutil.ReadAll(resp.Body)
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -132,9 +134,10 @@ func (g *GoWSDL) Start() (map[string][]byte, error) {
return nil, err
}

// Process WSDL nodes
for _, schema := range g.wsdl.Types.Schemas {
newTraverser(schema, g.wsdl.Types.Schemas).traverse()
g.typeResolver = newTypeResolver(g.wsdl.Types.Schemas)
err = resolveAttrRefs(g.wsdl.Types.Schemas)
if err != nil {
return nil, fmt.Errorf("failed to resolve attribute references: %w", err)
}

var wg sync.WaitGroup
Expand Down Expand Up @@ -192,7 +195,7 @@ func (g *GoWSDL) Start() (map[string][]byte, error) {
func (g *GoWSDL) fetchFile(loc *Location) (data []byte, err error) {
if loc.f != "" {
log.Println("Reading", "file", loc.f)
data, err = ioutil.ReadFile(loc.f)
data, err = os.ReadFile(loc.f)
} else {
log.Println("Downloading", "file", loc.u.String())
data, err = downloadFile(loc.u.String(), g.ignoreTLS)
Expand Down Expand Up @@ -298,10 +301,12 @@ func (g *GoWSDL) genTypes() ([]byte, error) {
"comment": comment,
"removeNS": removeNS,
"goString": goString,
"findNameByType": g.findNameByType,
"xmlNameForType": g.xmlNameForType,
"removePointerFromType": removePointerFromType,
"setNS": g.setNS,
"getNS": g.getNS,
"setCurrentSchema": g.setCurrentSchema,
"getCurrentSchema": g.getCurrentSchema,
"renderXMLName": renderXMLName,
"renderXMLTag": renderXMLTag,
}

data := new(bytes.Buffer)
Expand Down Expand Up @@ -610,8 +615,8 @@ func (g *GoWSDL) findType(message string) string {
}

// Given a type, check if there's an Element with that type, and return its name.
func (g *GoWSDL) findNameByType(name string) string {
return newTraverser(nil, g.wsdl.Types.Schemas).findNameByType(name)
func (g *GoWSDL) xmlNameForType(typeName string, schema *XSDSchema) xml.Name {
return g.typeResolver.xmlNameForType(typeName, schema)
}

// TODO(c4milo): Add support for namespaces instead of striping them out
Expand Down Expand Up @@ -731,3 +736,15 @@ func comment(text string) string {
}
return ""
}

func renderXMLName(xn xml.Name) string {
result := xn.Local
if xn.Space != "" {
result = fmt.Sprintf("%s %s", xn.Space, xn.Local)
}
return result
}

func renderXMLTag(xn xml.Name) string {
return fmt.Sprintf("`xml:\"%s\"`", renderXMLName(xn))
}
49 changes: 27 additions & 22 deletions gowsdl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"go/parser"
"go/printer"
"go/token"
"io/ioutil"
"log"
"os"
"path/filepath"
Expand Down Expand Up @@ -84,7 +83,7 @@ func TestAttributeRef(t *testing.T) {
Status []struct {
Value string ` + "`" + `xml:",chardata" json:"-,"` + "`" + `

Code string ` + "`" + `xml:"http://www.mnb.hu/webservices/ code,attr,omitempty" json:"code,omitempty"` + "`" + `
Code string ` + "`" + `xml:"code,attr,omitempty" json:"code,omitempty"` + "`" + `
} ` + "`" + `xml:"status,omitempty" json:"status,omitempty"` + "`" + `

ResponseCode string ` + "`" + `xml:"http://www.mnb.hu/webservices/ responseCode,attr,omitempty" json:"responseCode,omitempty"` + "`" + `
Expand Down Expand Up @@ -209,28 +208,34 @@ func TestVboxGeneratesWithoutSyntaxErrors(t *testing.T) {
}

for _, file := range files {
g, err := NewGoWSDL(file, "myservice", false, true)
if err != nil {
t.Error(err)
}

resp, err := g.Start()
if err != nil {
if file == "fixtures/vim.wsdl" {
// This fixture references missing .xsd files from the VMWare SDK, which aren't actually
// vendored. We use it explicitly from test_wsdl.go to test unmarshalling, but not here.
continue
//t.Error(err)
}
t.Run(file, func(t *testing.T) {
g, err := NewGoWSDL(file, "myservice", false, true)
if err != nil {
t.Error(err)
}

data := new(bytes.Buffer)
data.Write(resp["header"])
data.Write(resp["types"])
data.Write(resp["operations"])
data.Write(resp["soap"])
resp, err := g.Start()
if err != nil {
t.Error(err)
}

_, err = format.Source(data.Bytes())
if err != nil {
fmt.Println(string(data.Bytes()))
t.Error(err)
}
data := new(bytes.Buffer)
data.Write(resp["header"])
data.Write(resp["types"])
data.Write(resp["operations"])
data.Write(resp["soap"])

_, err = format.Source(data.Bytes())
if err != nil {
fmt.Println(data.String())
t.Error(err)
}
})
}
}

Expand Down Expand Up @@ -308,15 +313,15 @@ func TestEPCISWSDL(t *testing.T) {
if err != nil {
t.Fatal(err)
}
expectedBytes, err := ioutil.ReadFile("./fixtures/epcis/epcisquery.src")
expectedBytes, err := os.ReadFile("./fixtures/epcis/epcisquery.src")
if err != nil {
t.Fatal(err)
}

actual := string(source)
expected := string(expectedBytes)
if actual != expected {
_ = ioutil.WriteFile("./fixtures/epcis/epcisquery_gen.src", source, 0664)
_ = os.WriteFile("./fixtures/epcis/epcisquery_gen.src", source, 0664)
t.Error("got source ./fixtures/epcis/epcisquery_gen.src but expected ./fixtures/epcis/epcisquery.src")
}
}
Expand Down
67 changes: 67 additions & 0 deletions resolve_attr_refs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package gowsdl

import (
"errors"
"fmt"
"strings"
)

// resolveAttrRefs resolves all attribute references across the provided schemas.
// It modifies the schemas in-place, copying properties from the referenced attributes
// onto the referencing attributes.
func resolveAttrRefs(schemas []*XSDSchema) error {
// First, build an index from (namespace, attrName) -> attrDef
// for all global attrs across all schemas.
attrIndex := make(map[namespacedKey]*XSDAttribute)
for _, s := range schemas {
for _, attr := range s.Attributes {
if attr.Name != "" {
attrIndex[newNamespacedKey(s.TargetNamespace, attr.Name)] = attr
}
}
}

keyFromAttrRef := func(s *XSDSchema, attrRef string) (namespacedKey, error) {
before, after, hadColon := strings.Cut(attrRef, ":")
if !hadColon {
return newNamespacedKey(s.TargetNamespace, before), nil
}
if ns, ok := s.Xmlns[before]; ok {
return newNamespacedKey(ns, after), nil
}
return "", fmt.Errorf("unable to resolve namespace prefix %q in attribute ref %q", before, attrRef)
}

// Next, traverse all attrs with refs and copy over the properties from the referenced attrs.
var currentSchema *XSDSchema
var errs []error
visitor{schemas}.visit(&visitorConfig{
onEnterSchema: func(s *XSDSchema) {
currentSchema = s
},
onEnterAttribute: func(attr *XSDAttribute) {
if attr.Ref != "" {
nsk, err := keyFromAttrRef(currentSchema, attr.Ref)
if err != nil {
errs = append(errs, err)
return
}
refAttr, ok := attrIndex[nsk]
if !ok || refAttr.Ref != "" {
errs = append(errs, fmt.Errorf("unable to resolve attribute ref %q in schema with namespace %q", attr.Ref, currentSchema.TargetNamespace))
return
}
attr.Name = refAttr.Name
attr.Type = refAttr.Type
if attr.Fixed == "" {
attr.Fixed = refAttr.Fixed
}
attr.TargetNamespace = currentSchema.XMLNameForAttribute(refAttr).Space
} else if attr.Type == "" && attr.SimpleType != nil {
attr.Type = attr.SimpleType.Restriction.Base
}
},
})

return errors.Join(errs...)
}
3 changes: 1 addition & 2 deletions soap/MMAEncoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"mime"
"mime/multipart"
"net/textproto"
Expand Down Expand Up @@ -130,7 +129,7 @@ func (d *mmaDecoder) Decode(v interface{}) error {
if contentID == "" {
return errors.New("Invalid multipart content ID")
}
content, err := ioutil.ReadAll(p)
content, err := io.ReadAll(p)
if err != nil {
return err
}
Expand Down
3 changes: 1 addition & 2 deletions soap/MTOMEncoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math/rand"
"mime"
"mime/multipart"
Expand Down Expand Up @@ -253,7 +252,7 @@ func (d *mtomDecoder) Decode(v interface{}) error {
if contentID == "" {
return errors.New("Invalid multipart content ID")
}
content, err := ioutil.ReadAll(p)
content, err := io.ReadAll(p)
if err != nil {
return err
}
Expand Down
5 changes: 2 additions & 3 deletions soap/soap.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"encoding/xml"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"time"
Expand Down Expand Up @@ -503,7 +502,7 @@ func (s *Client) call(ctx context.Context, soapAction string, request, response
defer res.Body.Close()

if res.StatusCode >= 400 && res.StatusCode != 500 {
body, _ := ioutil.ReadAll(res.Body)
body, _ := io.ReadAll(res.Body)
return &HTTPError{
StatusCode: res.StatusCode,
ResponseBody: body,
Expand All @@ -526,7 +525,7 @@ func (s *Client) call(ctx context.Context, soapAction string, request, response
}

var mmaBoundary string
if s.opts.mma{
if s.opts.mma {
mmaBoundary, err = getMmaHeader(res.Header.Get("Content-Type"))
if err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions soap/soap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"context"
"encoding/xml"
"fmt"
"io/ioutil"
"io"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -138,7 +138,7 @@ func TestClient_Attachments_WithAttachmentResponse(t *testing.T) {
for k, v := range r.Header {
w.Header().Set(k, v[0])
}
bodyBuf, _ := ioutil.ReadAll(r.Body)
bodyBuf, _ := io.ReadAll(r.Body)
_, err := w.Write(bodyBuf)
if err != nil {
panic(err)
Expand Down Expand Up @@ -183,7 +183,7 @@ func TestClient_MTOM(t *testing.T) {
for k, v := range r.Header {
w.Header().Set(k, v[0])
}
bodyBuf, _ := ioutil.ReadAll(r.Body)
bodyBuf, _ := io.ReadAll(r.Body)
w.Write(bodyBuf)
}))
defer ts.Close()
Expand Down
Loading