├── .travis.yml ├── AUTHORS ├── Gopkg.lock ├── Gopkg.toml ├── LICENSE ├── README.md ├── contrib └── release.sh ├── main.go ├── soap ├── client.go └── client_test.go ├── wsdl ├── decoder.go ├── decoder_test.go ├── testdata │ ├── golden1.wsdl │ └── golden2.wsdl └── types.go └── wsdlgo ├── encoder.go ├── encoder_test.go ├── package.go ├── package_test.go └── testdata ├── arrayexample.golden ├── arrayexample.wsdl ├── broken.wsdl ├── data.golden ├── data.wsdl ├── data_withkeyword.golden ├── data_withkeyword.wsdl ├── importer-root.wsdl ├── importer-schema.wsdl ├── importer.wsdl ├── localimport-url.wsdl ├── localimport.golden ├── localimport.wsdl ├── localimport.xsd ├── localimport_choice.golden ├── localimport_choice.wsdl ├── localimport_choice.xsd ├── memcache.golden ├── memcache.wsdl ├── soap12wcf.golden ├── soap12wcf.wsdl ├── tpexample1.golden ├── tpexample1.wsdl ├── w3cexample1.golden ├── w3cexample1.wsdl ├── w3cexample2.golden ├── w3cexample2.wsdl ├── w3example1.golden ├── w3example1.wsdl ├── w3example2.golden └── w3example2.wsdl /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: go 4 | 5 | install: 6 | - go get -u github.com/golang/dep/cmd/dep 7 | - go get -u golang.org/x/lint/golint 8 | 9 | before_script: 10 | - dep ensure 11 | - go vet ./... 12 | - golint ./... 13 | 14 | go: 15 | - 1.9 16 | - tip 17 | 18 | script: 19 | - go test -v -race ./... 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # This is the official list of wsdl2go authors for copyright purposes. 2 | # This file is distinct from the CONTRIBUTORS file. 3 | # 4 | # Names should be added to this file as 5 | # Name or Organization 6 | # 7 | # The email address is not required for organizations. 8 | # 9 | # Please keep the list sorted. 10 | 11 | Alexandre Fiori 12 | Matt Spurrier 13 | -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:5193d913046443e59093d66a97a40c51f4a5ea4ceba60f3b3ecf89694de5d16f" 7 | name = "golang.org/x/net" 8 | packages = [ 9 | "html", 10 | "html/atom", 11 | "html/charset", 12 | ] 13 | pruneopts = "UT" 14 | revision = "1a61f4433d8505ec66450dd581f6abb8e83cb4d9" 15 | 16 | [[projects]] 17 | digest = "1:aa4d6967a3237f8367b6bf91503964a77183ecf696f1273e8ad3551bb4412b5f" 18 | name = "golang.org/x/text" 19 | packages = [ 20 | "encoding", 21 | "encoding/charmap", 22 | "encoding/htmlindex", 23 | "encoding/internal", 24 | "encoding/internal/identifier", 25 | "encoding/japanese", 26 | "encoding/korean", 27 | "encoding/simplifiedchinese", 28 | "encoding/traditionalchinese", 29 | "encoding/unicode", 30 | "internal/gen", 31 | "internal/tag", 32 | "internal/utf8internal", 33 | "language", 34 | "runes", 35 | "transform", 36 | "unicode/cldr", 37 | ] 38 | pruneopts = "UT" 39 | revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" 40 | version = "v0.3.0" 41 | 42 | [solve-meta] 43 | analyzer-name = "dep" 44 | analyzer-version = 1 45 | input-imports = ["golang.org/x/net/html/charset"] 46 | solver-name = "gps-cdcl" 47 | solver-version = 1 48 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | branch = "master" 30 | name = "golang.org/x/net" 31 | 32 | [prune] 33 | go-tests = true 34 | unused-packages = true 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 wsdl2go authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * The names of authors or contributors may NOT be used to endorse or 14 | promote products derived from this software without specific prior 15 | written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsdl2go 2 | 3 | [![Build Status](https://travis-ci.org/fiorix/wsdl2go.svg)](https://travis-ci.org/fiorix/wsdl2go) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/fiorix/wsdl2go)](https://goreportcard.com/report/github.com/fiorix/wsdl2go) 5 | [![GoDoc](https://godoc.org/github.com/fiorix/wsdl2go?status.svg)](https://godoc.org/github.com/fiorix/wsdl2go) 6 | 7 | wsdl2go is a command line tool to generate [Go](https://golang.org) code 8 | from [WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language). 9 | 10 | Download: 11 | 12 | ``` 13 | go get github.com/fiorix/wsdl2go 14 | ``` 15 | 16 | ### Usage 17 | 18 | tl;dr 19 | 20 | ``` 21 | wsdl2go < file.wsdl > hello.go 22 | ``` 23 | 24 | wsdl2go is a code generator that consumes WSDL from stdin (or file, or URL) and produces Go on stdout. The generated code contains services and methods described in the WSDL input, in a single output file. It is your responsibility to make it a package, in the sense that you put it in a directory that makes sense for you, and import it in your code later. Note that the generated code depends on the "soap" package that is part of this project. 25 | 26 | WSDL inputs that contain import tags (includes) pointing to other WSDL resources (other files or URLs) may be a source of trouble. The default behavior of wsdl2go is to try and load them, recursively. However, wsdl2go does not support authentication for remote HTTP resources, and cannot fetch resources from HTTPS servers with insecure TLS certificates. In those cases, you have to download the WSDL files yourself using curl or whatever, and process them locally. You might have to tweak their import paths. 27 | 28 | Once the code is generated, wsd2go runs gofmt on it. You must have gofmt in your $PATH, or $GOROOT/bin, or you'll get an error. 29 | 30 | ### Using the generated code 31 | 32 | Here's how to use the generated code: let's say you have a WSDL that defines the "example" service. You generate the code and make it the "example" package somewhere in your $GOPATH. This service provides an Echo method that takes an EchoRequest and returns an EchoReply. 33 | 34 | The process is this: 35 | 36 | - Import the generated code 37 | - Create a soap.Client (and here you can configure SOAP authentication, for example) 38 | - Instantiate the service using your soap.Client 39 | - Call the service methods 40 | 41 | Example: 42 | 43 | ```go 44 | import ( 45 | "/path/to/generated/example" 46 | 47 | "github.com/fiorix/wsdl2go/soap" 48 | ) 49 | 50 | func main() { 51 | cli := soap.Client{ 52 | URL: "http://server", 53 | Namespace: example.Namespace, 54 | } 55 | soapService := example.NewEchoService(&cli) 56 | echoReply, err := soapService.Echo(&example.EchoRequest{Data: "hello world"}) 57 | ... 58 | } 59 | ``` 60 | 61 | The soap.Client supports two forms of authentication: 62 | 63 | - Setting the "Pre" hook to a function that is run on all outbound HTTP requests, which can set HTTP headers and Basic Auth 64 | - Setting the Header attribute to an AuthHeader, to have it as a SOAP header (with username and password) in every request 65 | 66 | Note that only the **Document** style of SOAP is supported. The RPC style is currently not supported. 67 | 68 | ### Status 69 | 70 | Works for my needs, been tested with a few SOAP enterprise systems. Not fully compliant to WSDL or SOAP specs. 71 | 72 | Because of some [limitations](https://github.com/golang/go/issues/14407) of XML namespaces in Go, there's only so much one can do to make things like SOAP work properly. Although, the code generated by wsdl2go might be sufficient for most systems. 73 | 74 | Types supported: 75 | 76 | - [x] byte 77 | - [x] int 78 | - [x] long (int64) 79 | - [x] float (float64) 80 | - [x] double (float64) 81 | - [x] boolean (bool) 82 | - [x] string 83 | - [x] hexBinary ([]byte) 84 | - [x] base64Binary ([]byte) 85 | - [x] date 86 | - [x] time 87 | - [x] dateTime 88 | - [x] simpleType (w/ enum and validation) 89 | - [x] complexType (struct) 90 | - [x] complexContent (slices, embedded structs) 91 | - [x] token (as string) 92 | - [x] any (slice of empty interfaces) 93 | - [x] anyURI (string) 94 | - [x] QName (string) 95 | - [x] union (empty interface w/ comments) 96 | - [x] nonNegativeInteger (uint) 97 | - [ ] faults 98 | - [ ] decimal 99 | - [ ] g{Day,Month,Year}... 100 | - [ ] NOTATION 101 | 102 | Date types are currently defined as strings, need to implement XML Marshaler and Unmarshaler interfaces. The binary ones (hex and base64) are also lacking marshal/unmarshal. 103 | 104 | For simple types that have restrictions defined, such as an enumerated list of possible values, we generate the validation function using reflect to compare values. This and the entire API might change anytime, be warned. 105 | -------------------------------------------------------------------------------- /contrib/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=${VERSION:-`git describe --tags`} 4 | 5 | for OS in linux freebsd windows darwin 6 | do 7 | GOOS=$OS GOARCH=amd64 go build -ldflags "-w -X main.version=${VERSION}" 8 | TARBALL=wsdl2go-$VERSION-$OS-amd64.tar.gz 9 | if [ "$OS" = "windows" ]; then 10 | tar czf $TARBALL wsdl2go.exe 11 | else 12 | tar czf $TARBALL wsdl2go 13 | fi 14 | rm -f wsdl2go wsdl2go.exe 15 | done 16 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/tls" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net/http" 10 | "net/url" 11 | "os" 12 | 13 | "github.com/fiorix/wsdl2go/wsdl" 14 | "github.com/fiorix/wsdl2go/wsdlgo" 15 | ) 16 | 17 | var version = "tip" 18 | 19 | type options struct { 20 | Src string 21 | Dst string 22 | Package string 23 | Namespace string 24 | Insecure bool 25 | ClientCertFile string 26 | ClientKeyFile string 27 | Version bool 28 | } 29 | 30 | func main() { 31 | opts := options{} 32 | 33 | flag.StringVar(&opts.Src, "i", opts.Src, "input file, url, or '-' for stdin") 34 | flag.StringVar(&opts.Dst, "o", opts.Dst, "output file, or '-' for stdout") 35 | flag.StringVar(&opts.Namespace, "n", opts.Namespace, "override namespace") 36 | flag.StringVar(&opts.Package, "p", opts.Package, "package name") 37 | flag.BoolVar(&opts.Insecure, "yolo", opts.Insecure, "accept invalid https certificates") 38 | flag.StringVar(&opts.ClientCertFile, "cert", opts.ClientCertFile, "use client TLS cert file") 39 | flag.StringVar(&opts.ClientKeyFile, "key", opts.ClientKeyFile, "use client TLS key file") 40 | flag.BoolVar(&opts.Version, "version", opts.Version, "show version and exit") 41 | flag.Parse() 42 | if opts.Version { 43 | fmt.Printf("wsdl2go %s\n", version) 44 | return 45 | } 46 | var w io.Writer 47 | switch opts.Dst { 48 | case "", "-": 49 | w = os.Stdout 50 | default: 51 | f, err := os.OpenFile(opts.Dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 52 | if err != nil { 53 | log.Fatal(err) 54 | } 55 | defer f.Close() 56 | w = f 57 | } 58 | 59 | cli := httpClient(opts.Insecure, opts.ClientCertFile, opts.ClientKeyFile) 60 | 61 | err := codegen(w, opts, cli) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | } 66 | 67 | func codegen(w io.Writer, opts options, cli *http.Client) error { 68 | var err error 69 | var f io.ReadCloser 70 | if opts.Src == "" || opts.Src == "-" { 71 | f = os.Stdin 72 | } else if f, err = open(opts.Src, cli); err != nil { 73 | return err 74 | } 75 | d, err := wsdl.Unmarshal(f) 76 | if err != nil { 77 | return err 78 | } 79 | f.Close() 80 | 81 | enc := wsdlgo.NewEncoder(w) 82 | enc.SetClient(cli) 83 | if opts.Package != "" { 84 | enc.SetPackageName(wsdlgo.PackageName(opts.Package)) 85 | } 86 | if opts.Namespace != "" { 87 | enc.SetLocalNamespace(opts.Namespace) 88 | } 89 | 90 | return enc.Encode(d) 91 | } 92 | 93 | func open(name string, cli *http.Client) (io.ReadCloser, error) { 94 | u, err := url.Parse(name) 95 | if err != nil || u.Scheme == "" { 96 | return os.Open(name) 97 | } 98 | resp, err := cli.Get(name) 99 | if err != nil { 100 | return nil, err 101 | } 102 | return resp.Body, err 103 | } 104 | 105 | // httpClient returns http client with default options 106 | func httpClient(insecure bool, clientCertPath, clientKeyPath string) *http.Client { 107 | tlsConfig := &tls.Config{InsecureSkipVerify: insecure} 108 | 109 | if clientCertPath != "" && clientKeyPath != "" { 110 | clientCert, err := tls.LoadX509KeyPair(clientCertPath, clientKeyPath) 111 | if err != nil { 112 | log.Fatalln("Failed to load x509 client key pair:", err) 113 | } 114 | tlsConfig.Certificates = []tls.Certificate{clientCert} 115 | tlsConfig.Renegotiation = tls.RenegotiateFreelyAsClient 116 | } else if clientCertPath == "" && clientKeyPath != "" { 117 | log.Fatalln("Certificate file is required when using key file") 118 | } else if clientCertPath != "" && clientKeyPath == "" { 119 | log.Fatalln("Key file is required when using certificate file") 120 | } 121 | 122 | defaultTransport := http.DefaultTransport.(*http.Transport) 123 | transport := &http.Transport{ 124 | Proxy: defaultTransport.Proxy, 125 | DialContext: defaultTransport.DialContext, 126 | MaxIdleConns: defaultTransport.MaxIdleConns, 127 | IdleConnTimeout: defaultTransport.IdleConnTimeout, 128 | ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout, 129 | TLSHandshakeTimeout: defaultTransport.TLSHandshakeTimeout, 130 | TLSClientConfig: tlsConfig, 131 | } 132 | return &http.Client{Transport: transport} 133 | } 134 | -------------------------------------------------------------------------------- /soap/client.go: -------------------------------------------------------------------------------- 1 | // Package soap provides a SOAP HTTP client. 2 | package soap 3 | 4 | import ( 5 | "bytes" 6 | "context" 7 | "encoding/xml" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "net/http" 12 | "reflect" 13 | 14 | "golang.org/x/net/html/charset" 15 | ) 16 | 17 | // XSINamespace is a link to the XML Schema instance namespace. 18 | const XSINamespace = "http://www.w3.org/2001/XMLSchema-instance" 19 | 20 | var xmlTyperType reflect.Type = reflect.TypeOf((*XMLTyper)(nil)).Elem() 21 | 22 | // A RoundTripper executes a request passing the given req as the SOAP 23 | // envelope body. The HTTP response is then de-serialized onto the resp 24 | // object. Returns error in case an error occurs serializing req, making 25 | // the HTTP request, or de-serializing the response. 26 | type RoundTripper interface { 27 | RoundTrip(req, resp Message) error 28 | RoundTripSoap12(action string, req, resp Message) error 29 | } 30 | 31 | // Message is an opaque type used by the RoundTripper to carry XML 32 | // documents for SOAP. 33 | type Message interface{} 34 | 35 | // Header is an opaque type used as the SOAP Header element in requests. 36 | type Header interface{} 37 | 38 | // AuthHeader is a Header to be encoded as the SOAP Header element in 39 | // requests, to convey credentials for authentication. 40 | type AuthHeader struct { 41 | Namespace string `xml:"xmlns:ns,attr"` 42 | Username string `xml:"ns:username"` 43 | Password string `xml:"ns:password"` 44 | } 45 | 46 | // Client is a SOAP client. 47 | type Client struct { 48 | URL string // URL of the server 49 | UserAgent string // User-Agent header will be added to each request 50 | Namespace string // SOAP Namespace 51 | URNamespace string // Uniform Resource Namespace 52 | ThisNamespace string // SOAP This-Namespace (tns) 53 | ExcludeActionNamespace bool // Include Namespace to SOAP Action header 54 | Envelope string // Optional SOAP Envelope 55 | Header Header // Optional SOAP Header 56 | ContentType string // Optional Content-Type (default text/xml) 57 | Config *http.Client // Optional HTTP client 58 | Pre func(*http.Request) // Optional hook to modify outbound requests 59 | Post func(*http.Response) // Optional hook to snoop inbound responses 60 | Ctx context.Context // Optional variable to allow Context Tracking. 61 | } 62 | 63 | // XMLTyper is an abstract interface for types that can set an XML type. 64 | type XMLTyper interface { 65 | SetXMLType() 66 | } 67 | 68 | func setXMLType(v reflect.Value) { 69 | if !v.IsValid() { 70 | return 71 | } 72 | switch v.Type().Kind() { 73 | case reflect.Interface: 74 | setXMLType(v.Elem()) 75 | case reflect.Ptr: 76 | if v.IsNil() { 77 | break 78 | } 79 | ok := v.Type().Implements(xmlTyperType) 80 | if ok { 81 | v.MethodByName("SetXMLType").Call(nil) 82 | } 83 | setXMLType(v.Elem()) 84 | case reflect.Slice: 85 | for i := 0; i < v.Len(); i++ { 86 | setXMLType(v.Index(i)) 87 | } 88 | case reflect.Struct: 89 | for i := 0; i < v.NumField(); i++ { 90 | if v.Field(i).CanAddr() { 91 | setXMLType(v.Field(i).Addr()) 92 | } else { 93 | setXMLType(v.Field(i)) 94 | } 95 | } 96 | } 97 | } 98 | 99 | func doRoundTrip(c *Client, setHeaders func(*http.Request), in, out Message) error { 100 | setXMLType(reflect.ValueOf(in)) 101 | req := &Envelope{ 102 | EnvelopeAttr: c.Envelope, 103 | URNAttr: c.URNamespace, 104 | NSAttr: c.Namespace, 105 | TNSAttr: c.ThisNamespace, 106 | XSIAttr: XSINamespace, 107 | Header: c.Header, 108 | Body: in, 109 | } 110 | 111 | if req.EnvelopeAttr == "" { 112 | req.EnvelopeAttr = "http://schemas.xmlsoap.org/soap/envelope/" 113 | } 114 | if req.NSAttr == "" { 115 | req.NSAttr = c.URL 116 | } 117 | if req.TNSAttr == "" { 118 | req.TNSAttr = req.NSAttr 119 | } 120 | var b bytes.Buffer 121 | err := xml.NewEncoder(&b).Encode(req) 122 | if err != nil { 123 | return err 124 | } 125 | cli := c.Config 126 | if cli == nil { 127 | cli = http.DefaultClient 128 | } 129 | r, err := http.NewRequest("POST", c.URL, &b) 130 | if err != nil { 131 | return err 132 | } 133 | setHeaders(r) 134 | if c.Pre != nil { 135 | c.Pre(r) 136 | } 137 | 138 | if c.Ctx != nil { 139 | r = r.WithContext(c.Ctx) 140 | } 141 | 142 | resp, err := cli.Do(r) 143 | if err != nil { 144 | return err 145 | } 146 | defer resp.Body.Close() 147 | if c.Post != nil { 148 | c.Post(resp) 149 | } 150 | if resp.StatusCode != http.StatusOK { 151 | // read only the first MiB of the body in error case 152 | limReader := io.LimitReader(resp.Body, 1024*1024) 153 | body, _ := ioutil.ReadAll(limReader) 154 | return &HTTPError{ 155 | StatusCode: resp.StatusCode, 156 | Status: resp.Status, 157 | Msg: string(body), 158 | } 159 | } 160 | 161 | marshalStructure := struct { 162 | XMLName xml.Name `xml:"Envelope"` 163 | Body Message 164 | }{Body: out} 165 | 166 | decoder := xml.NewDecoder(resp.Body) 167 | decoder.CharsetReader = charset.NewReaderLabel 168 | return decoder.Decode(&marshalStructure) 169 | } 170 | 171 | // RoundTrip implements the RoundTripper interface. 172 | func (c *Client) RoundTrip(in, out Message) error { 173 | headerFunc := func(r *http.Request) { 174 | if c.UserAgent != "" { 175 | r.Header.Add("User-Agent", c.UserAgent) 176 | } 177 | var actionName, soapAction string 178 | if in != nil { 179 | soapAction = reflect.TypeOf(in).Elem().Name() 180 | } 181 | ct := c.ContentType 182 | if ct == "" { 183 | ct = "text/xml" 184 | } 185 | r.Header.Set("Content-Type", ct) 186 | if in != nil { 187 | if c.ExcludeActionNamespace { 188 | actionName = soapAction 189 | } else { 190 | actionName = fmt.Sprintf("%s/%s", c.Namespace, soapAction) 191 | } 192 | r.Header.Add("SOAPAction", actionName) 193 | } 194 | } 195 | return doRoundTrip(c, headerFunc, in, out) 196 | } 197 | 198 | // RoundTripWithAction implements the RoundTripper interface for SOAP clients 199 | // that need to set the SOAPAction header. 200 | func (c *Client) RoundTripWithAction(soapAction string, in, out Message) error { 201 | headerFunc := func(r *http.Request) { 202 | if c.UserAgent != "" { 203 | r.Header.Add("User-Agent", c.UserAgent) 204 | } 205 | var actionName string 206 | ct := c.ContentType 207 | if ct == "" { 208 | ct = "text/xml" 209 | } 210 | r.Header.Set("Content-Type", ct) 211 | if in != nil { 212 | if c.ExcludeActionNamespace { 213 | actionName = soapAction 214 | } else { 215 | actionName = fmt.Sprintf("%s/%s", c.Namespace, soapAction) 216 | } 217 | r.Header.Add("SOAPAction", actionName) 218 | } 219 | } 220 | return doRoundTrip(c, headerFunc, in, out) 221 | } 222 | 223 | // RoundTripSoap12 implements the RoundTripper interface for SOAP 1.2. 224 | func (c *Client) RoundTripSoap12(action string, in, out Message) error { 225 | headerFunc := func(r *http.Request) { 226 | r.Header.Add("Content-Type", fmt.Sprintf("application/soap+xml; charset=utf-8; action=\"%s\"", action)) 227 | } 228 | return doRoundTrip(c, headerFunc, in, out) 229 | } 230 | 231 | // HTTPError is detailed soap http error 232 | type HTTPError struct { 233 | StatusCode int 234 | Status string 235 | Msg string 236 | } 237 | 238 | func (e *HTTPError) Error() string { 239 | return fmt.Sprintf("%q: %q", e.Status, e.Msg) 240 | } 241 | 242 | // Envelope is a SOAP envelope. 243 | type Envelope struct { 244 | XMLName xml.Name `xml:"SOAP-ENV:Envelope"` 245 | EnvelopeAttr string `xml:"xmlns:SOAP-ENV,attr"` 246 | NSAttr string `xml:"xmlns:ns,attr"` 247 | TNSAttr string `xml:"xmlns:tns,attr,omitempty"` 248 | URNAttr string `xml:"xmlns:urn,attr,omitempty"` 249 | XSIAttr string `xml:"xmlns:xsi,attr,omitempty"` 250 | Header Message `xml:"SOAP-ENV:Header"` 251 | Body Message `xml:"SOAP-ENV:Body"` 252 | } 253 | -------------------------------------------------------------------------------- /soap/client_test.go: -------------------------------------------------------------------------------- 1 | package soap 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "net/http" 7 | "net/http/httptest" 8 | "reflect" 9 | "testing" 10 | ) 11 | 12 | type StructFieldSetXMLData struct { 13 | TypeAttrXSI, TypeNamespace string 14 | } 15 | 16 | type SetXMLData struct { 17 | TypeAttrXSI, TypeNamespace string 18 | Pointer *StructFieldSetXMLData 19 | Field StructFieldSetXMLData 20 | } 21 | 22 | func (s *SetXMLData) SetXMLType() { 23 | s.TypeAttrXSI = "test" 24 | s.TypeNamespace = "test 1" 25 | } 26 | 27 | func (s *StructFieldSetXMLData) SetXMLType() { 28 | s.TypeAttrXSI = "struct" 29 | s.TypeNamespace = "struct 1" 30 | } 31 | 32 | func TestSetXMLType(t *testing.T) { 33 | type interfaceT interface{} 34 | type testT struct { 35 | A string 36 | B []interfaceT 37 | } 38 | 39 | test := &testT{ 40 | A: "unchanged", 41 | } 42 | list := []*SetXMLData{{ 43 | Pointer: &StructFieldSetXMLData{}, 44 | }, {}} 45 | test.B = make([]interfaceT, len(list)) 46 | for i, el := range list { 47 | test.B[i] = el 48 | } 49 | setXMLType(reflect.ValueOf(test)) 50 | for _, interfaceEl := range test.B { 51 | el, _ := interfaceEl.(*SetXMLData) 52 | if el.TypeAttrXSI != "test" { 53 | t.Fatal("TypeAttrXSI not set") 54 | } 55 | if el.TypeNamespace != "test 1" { 56 | t.Fatal("TypeNamespace not set") 57 | } 58 | if el.Pointer != nil { 59 | if el.Pointer.TypeAttrXSI != "struct" { 60 | t.Fatal("TypeAttrXSI not set") 61 | } 62 | } 63 | if el.Field.TypeAttrXSI != "struct" { 64 | t.Fatal("TypeAttrXSI not set") 65 | } 66 | } 67 | } 68 | 69 | func TestRoundTrip(t *testing.T) { 70 | type msgT struct{ A, B string } 71 | type envT struct{ msgT } 72 | echo := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 73 | if r.Method != "POST" { 74 | http.NotFound(w, r) 75 | return 76 | } 77 | if v := r.Header.Get("X-Test"); v != "true" { 78 | http.NotFound(w, r) 79 | return 80 | } 81 | io.Copy(w, r.Body) 82 | }) 83 | s := httptest.NewServer(echo) 84 | defer s.Close() 85 | pre := func(r *http.Request) { r.Header.Set("X-Test", "true") } 86 | cases := []struct { 87 | C *Client 88 | Action string 89 | In, Out Message 90 | Fail bool 91 | }{ 92 | { 93 | C: &Client{URL: s.URL, Pre: pre}, 94 | Action: "hello", 95 | In: &msgT{A: "hello", B: "world"}, 96 | Out: &envT{}, 97 | }, 98 | { 99 | C: &Client{URL: s.URL, Pre: pre}, 100 | Action: "foo", 101 | In: &msgT{A: "foo", B: "bar"}, 102 | Out: &envT{}, 103 | }, 104 | { 105 | C: &Client{URL: "", Pre: pre}, 106 | Action: "none", 107 | Out: &envT{}, 108 | Fail: true, 109 | }, 110 | } 111 | for i, tc := range cases { 112 | err := tc.C.RoundTrip(tc.In, tc.Out) 113 | if err != nil && !tc.Fail { 114 | t.Errorf("test %d: %v", i, err) 115 | continue 116 | } 117 | if tc.Fail { 118 | continue 119 | } 120 | env, ok := tc.Out.(*envT) 121 | if !ok { 122 | t.Errorf("test %d: response to %#v is not an envelope", i, tc.In) 123 | continue 124 | } 125 | if !reflect.DeepEqual(env.msgT, *tc.In.(*msgT)) { 126 | t.Errorf("test %d: message mismatch\nwant: %#v\nhave: %#v", 127 | i, tc.In, &env.msgT) 128 | continue 129 | } 130 | } 131 | } 132 | 133 | func TestRoundTripWithAction(t *testing.T) { 134 | type msgT struct{ A, B string } 135 | type envT struct{ msgT } 136 | echo := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 137 | if r.Method != "POST" { 138 | http.NotFound(w, r) 139 | return 140 | } 141 | if v := r.Header.Get("X-Test"); v != "true" { 142 | http.NotFound(w, r) 143 | return 144 | } 145 | io.Copy(w, r.Body) 146 | }) 147 | s := httptest.NewServer(echo) 148 | defer s.Close() 149 | pre := func(r *http.Request) { r.Header.Set("X-Test", "true") } 150 | cases := []struct { 151 | C *Client 152 | Action string 153 | In, Out Message 154 | Fail bool 155 | }{ 156 | { 157 | C: &Client{URL: s.URL, Pre: pre}, 158 | Action: "hello", 159 | In: &msgT{A: "hello", B: "world"}, 160 | Out: &envT{}, 161 | }, 162 | { 163 | C: &Client{URL: s.URL, Pre: pre}, 164 | Action: "foo", 165 | In: &msgT{A: "foo", B: "bar"}, 166 | Out: &envT{}, 167 | }, 168 | { 169 | C: &Client{URL: "", Pre: pre}, 170 | Action: "none", 171 | Out: &envT{}, 172 | Fail: true, 173 | }, 174 | } 175 | for i, tc := range cases { 176 | err := tc.C.RoundTripWithAction(tc.Action, tc.In, tc.Out) 177 | if err != nil && !tc.Fail { 178 | t.Errorf("test %d: %v", i, err) 179 | continue 180 | } 181 | if tc.Fail { 182 | continue 183 | } 184 | env, ok := tc.Out.(*envT) 185 | if !ok { 186 | t.Errorf("test %d: response to %#v is not an envelope", i, tc.In) 187 | continue 188 | } 189 | if !reflect.DeepEqual(env.msgT, *tc.In.(*msgT)) { 190 | t.Errorf("test %d: message mismatch\nwant: %#v\nhave: %#v", 191 | i, tc.In, &env.msgT) 192 | continue 193 | } 194 | } 195 | } 196 | 197 | func TestRoundTripSoap12(t *testing.T) { 198 | type msgT struct{ A, B string } 199 | type envT struct{ msgT } 200 | testAction := "http://foo.bar.com" 201 | 202 | echo := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 203 | if r.Method != "POST" { 204 | http.NotFound(w, r) 205 | return 206 | } 207 | expectedContentType := fmt.Sprintf("application/soap+xml; charset=utf-8; action=\"%s\"", testAction) 208 | if r.Header.Get("Content-Type") != expectedContentType { 209 | http.NotFound(w, r) 210 | return 211 | } 212 | if v := r.Header.Get("X-Test"); v != "true" { 213 | http.NotFound(w, r) 214 | return 215 | } 216 | io.Copy(w, r.Body) 217 | }) 218 | s := httptest.NewServer(echo) 219 | defer s.Close() 220 | pre := func(r *http.Request) { r.Header.Set("X-Test", "true") } 221 | cases := []struct { 222 | C *Client 223 | Action string 224 | In, Out Message 225 | Fail bool 226 | }{ 227 | { 228 | C: &Client{URL: s.URL, Pre: pre}, 229 | In: &msgT{A: "hello", B: "world"}, 230 | Out: &envT{}, 231 | }, 232 | { 233 | C: &Client{URL: s.URL, Pre: pre}, 234 | In: &msgT{A: "foo", B: "bar"}, 235 | Out: &envT{}, 236 | }, 237 | { 238 | C: &Client{URL: "", Pre: pre}, 239 | Out: &envT{}, 240 | Fail: true, 241 | }, 242 | } 243 | for i, tc := range cases { 244 | err := tc.C.RoundTripSoap12(testAction, tc.In, tc.Out) 245 | if err != nil && !tc.Fail { 246 | t.Errorf("test %d: %v", i, err) 247 | continue 248 | } 249 | if tc.Fail { 250 | continue 251 | } 252 | env, ok := tc.Out.(*envT) 253 | if !ok { 254 | t.Errorf("test %d: response to %#v is not an envelope", i, tc.In) 255 | continue 256 | } 257 | if !reflect.DeepEqual(env.msgT, *tc.In.(*msgT)) { 258 | t.Errorf("test %d: message mismatch\nwant: %#v\nhave: %#v", 259 | i, tc.In, &env.msgT) 260 | continue 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /wsdl/decoder.go: -------------------------------------------------------------------------------- 1 | // Package wsdl provides Web Services Description Language (WSDL) decoder. 2 | // 3 | // http://www.w3schools.com/xml/xml_wsdl.asp 4 | package wsdl 5 | 6 | import ( 7 | "encoding/xml" 8 | "io" 9 | 10 | "golang.org/x/net/html/charset" 11 | ) 12 | 13 | // Unmarshal unmarshals WSDL documents starting from the tag. 14 | // 15 | // The Definitions object it returns is an unmarshalled version of the 16 | // WSDL XML that can be introspected to generate the Web Services API. 17 | func Unmarshal(r io.Reader) (*Definitions, error) { 18 | var d Definitions 19 | decoder := xml.NewDecoder(r) 20 | decoder.CharsetReader = charset.NewReaderLabel 21 | err := decoder.Decode(&d) 22 | if err != nil { 23 | return nil, err 24 | } 25 | return &d, nil 26 | } 27 | -------------------------------------------------------------------------------- /wsdl/decoder_test.go: -------------------------------------------------------------------------------- 1 | package wsdl 2 | 3 | import ( 4 | "encoding/xml" 5 | "os" 6 | "path/filepath" 7 | "reflect" 8 | "testing" 9 | ) 10 | 11 | func TestUnmarshal(t *testing.T) { 12 | cases := []struct { 13 | F string 14 | E error 15 | }{ 16 | { 17 | F: "golden1.wsdl", 18 | E: nil, 19 | }, { 20 | F: "golden2.wsdl", 21 | E: xml.UnmarshalError("..."), 22 | }, 23 | } 24 | for i, tc := range cases { 25 | f, err := os.Open(filepath.Join("testdata", tc.F)) 26 | if err != nil { 27 | t.Errorf("test %d (%q) failed: %v", i, tc.F, err) 28 | } 29 | defer f.Close() 30 | _, err = Unmarshal(f) 31 | if tc.E == nil { 32 | if err != nil { 33 | t.Errorf("test %d (%q) failed: want %v, have %v", i, tc.F, tc.E, err) 34 | } 35 | continue 36 | } 37 | want := reflect.ValueOf(tc.E).Type().Name() 38 | have := reflect.ValueOf(err).Type().Name() 39 | if want != have { 40 | t.Errorf("test %d (%q) failed: want %q, have %q", i, tc.F, want, have) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /wsdl/testdata/golden1.wsdl: -------------------------------------------------------------------------------- 1 | 14 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /wsdl/testdata/golden2.wsdl: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /wsdl/types.go: -------------------------------------------------------------------------------- 1 | package wsdl 2 | 3 | // TODO: Add all types from the spec. 4 | 5 | import "encoding/xml" 6 | 7 | // Definitions is the root element of a WSDL document. 8 | type Definitions struct { 9 | XMLName xml.Name `xml:"definitions"` 10 | Name string `xml:"name,attr"` 11 | TargetNamespace string `xml:"targetNamespace,attr"` 12 | Namespaces map[string]string `xml:"-"` 13 | SOAPEnv string `xml:"SOAP-ENV,attr"` 14 | SOAPEnc string `xml:"SOAP-ENC,attr"` 15 | Service Service `xml:"service"` 16 | Imports []*Import `xml:"import"` 17 | Schema Schema `xml:"types>schema"` 18 | Messages []*Message `xml:"message"` 19 | PortType PortType `xml:"portType"` // TODO: PortType slice? 20 | Binding Binding `xml:"binding"` 21 | } 22 | 23 | type definitionDup Definitions 24 | 25 | // UnmarshalXML implements the xml.Unmarshaler interface. 26 | func (def *Definitions) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 27 | for _, attr := range start.Attr { 28 | if attr.Name.Space == "xmlns" { 29 | if def.Namespaces == nil { 30 | def.Namespaces = make(map[string]string) 31 | } 32 | def.Namespaces[attr.Name.Local] = attr.Value 33 | } 34 | } 35 | return d.DecodeElement((*definitionDup)(def), &start) 36 | } 37 | 38 | // Service defines a WSDL service and with a location, like an HTTP server. 39 | type Service struct { 40 | Doc string `xml:"documentation"` 41 | Ports []*Port `xml:"port"` 42 | } 43 | 44 | // Port for WSDL service. 45 | type Port struct { 46 | XMLName xml.Name `xml:"port"` 47 | Name string `xml:"name,attr"` 48 | Binding string `xml:"binding,attr"` 49 | Address Address `xml:"address"` 50 | } 51 | 52 | // Address of WSDL service. 53 | type Address struct { 54 | XMLName xml.Name `xml:"address"` 55 | Location string `xml:"location,attr"` 56 | } 57 | 58 | // Schema of WSDL document. 59 | type Schema struct { 60 | XMLName xml.Name `xml:"schema"` 61 | TargetNamespace string `xml:"targetNamespace,attr"` 62 | Namespaces map[string]string `xml:"-"` 63 | Imports []*ImportSchema `xml:"import"` 64 | Includes []*IncludeSchema `xml:"include"` 65 | SimpleTypes []*SimpleType `xml:"simpleType"` 66 | ComplexTypes []*ComplexType `xml:"complexType"` 67 | Elements []*Element `xml:"element"` 68 | } 69 | 70 | // Unmarshaling solution from Matt Harden (http://grokbase.com/t/gg/golang-nuts/14bk21xb7a/go-nuts-extending-encoding-xml-to-capture-unknown-attributes) 71 | // We duplicate the type Schema here so that we can unmarshal into it 72 | // without recursively triggering the *Schema.UnmarshalXML method. 73 | // Other options are to embed tt into Type or declare Type as a synonym for tt. 74 | // The important thing is that tt is only used directly in *Schema.UnmarshalXML or Schema.MarshalXML. 75 | type schemaDup Schema 76 | 77 | // UnmarshalXML implements the xml.Unmarshaler interface. 78 | func (schema *Schema) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 79 | for _, attr := range start.Attr { 80 | if attr.Name.Space == "xmlns" { 81 | if schema.Namespaces == nil { 82 | schema.Namespaces = make(map[string]string) 83 | } 84 | schema.Namespaces[attr.Name.Local] = attr.Value 85 | } 86 | } 87 | return d.DecodeElement((*schemaDup)(schema), &start) 88 | } 89 | 90 | // SimpleType describes a simple type, such as string. 91 | type SimpleType struct { 92 | XMLName xml.Name `xml:"simpleType"` 93 | Name string `xml:"name,attr"` 94 | Union *Union `xml:"union"` 95 | Restriction *Restriction `xml:"restriction"` 96 | TargetNamespace string 97 | } 98 | 99 | // Union is a mix of multiple types in a union. 100 | type Union struct { 101 | XMLName xml.Name `xml:"union"` 102 | MemberTypes string `xml:"memberTypes,attr"` 103 | } 104 | 105 | // Restriction describes the WSDL type of the simple type and 106 | // optionally its allowed values. 107 | type Restriction struct { 108 | XMLName xml.Name `xml:"restriction"` 109 | Base string `xml:"base,attr"` 110 | Enum []*Enum `xml:"enumeration"` 111 | Attributes []*Attribute `xml:"attribute"` 112 | } 113 | 114 | // Enum describes one possible value for a Restriction. 115 | type Enum struct { 116 | XMLName xml.Name `xml:"enumeration"` 117 | Value string `xml:"value,attr"` 118 | } 119 | 120 | // ComplexType describes a complex type, such as a struct. 121 | type ComplexType struct { 122 | XMLName xml.Name `xml:"complexType"` 123 | Name string `xml:"name,attr"` 124 | Abstract bool `xml:"abstract,attr"` 125 | Doc string `xml:"annotation>documentation"` 126 | AllElements []*Element `xml:"all>element"` 127 | ComplexContent *ComplexContent `xml:"complexContent"` 128 | SimpleContent *SimpleContent `xml:"simpleContent"` 129 | Sequence *Sequence `xml:"sequence"` 130 | Choice *Choice `xml:"choice"` 131 | Attributes []*Attribute `xml:"attribute"` 132 | TargetNamespace string 133 | } 134 | 135 | // SimpleContent describes simple content within a complex type. 136 | type SimpleContent struct { 137 | XMLName xml.Name `xml:"simpleContent"` 138 | Extension *Extension `xml:"extension"` 139 | Restriction *Restriction `xml:"restriction"` 140 | } 141 | 142 | // ComplexContent describes complex content within a complex type. Usually 143 | // for extending the complex type with fields from the complex content. 144 | type ComplexContent struct { 145 | XMLName xml.Name `xml:"complexContent"` 146 | Extension *Extension `xml:"extension"` 147 | Restriction *Restriction `xml:"restriction"` 148 | } 149 | 150 | // Extension describes a complex content extension. 151 | type Extension struct { 152 | XMLName xml.Name `xml:"extension"` 153 | Base string `xml:"base,attr"` 154 | Sequence *Sequence `xml:"sequence"` 155 | Choice *Choice `xml:"choice"` 156 | Attributes []*Attribute `xml:"attribute"` 157 | } 158 | 159 | // Sequence describes a list of elements (parameters) of a type. 160 | type Sequence struct { 161 | XMLName xml.Name `xml:"sequence"` 162 | ComplexTypes []*ComplexType `xml:"complexType"` 163 | Elements []*Element `xml:"element"` 164 | Any []*AnyElement `xml:"any"` 165 | Choices []*Choice `xml:"choice"` 166 | } 167 | 168 | // Choice describes a list of elements (parameters) of a type. 169 | type Choice struct { 170 | XMLName xml.Name `xml:"choice"` 171 | ComplexTypes []*ComplexType `xml:"complexType"` 172 | Elements []*Element `xml:"element"` 173 | Any []*AnyElement `xml:"any"` 174 | } 175 | 176 | // Attribute describes an attribute of a given type. 177 | type Attribute struct { 178 | XMLName xml.Name `xml:"attribute"` 179 | Name string `xml:"name,attr"` 180 | Ref string `xml:"ref,attr"` 181 | Type string `xml:"type,attr"` 182 | ArrayType string `xml:"arrayType,attr"` 183 | Min int `xml:"minOccurs,attr"` 184 | Max string `xml:"maxOccurs,attr"` // can be # or unbounded 185 | Nillable bool `xml:"nillable,attr"` 186 | } 187 | 188 | // Element describes an element of a given type. 189 | type Element struct { 190 | XMLName xml.Name `xml:"element"` 191 | Name string `xml:"name,attr"` 192 | Ref string `xml:"ref,attr"` 193 | Type string `xml:"type,attr"` 194 | Min int `xml:"minOccurs,attr"` 195 | Max string `xml:"maxOccurs,attr"` // can be # or unbounded 196 | Nillable bool `xml:"nillable,attr"` 197 | ComplexType *ComplexType `xml:"complexType"` 198 | } 199 | 200 | // AnyElement describes an element of an undefined type. 201 | type AnyElement struct { 202 | XMLName xml.Name `xml:"any"` 203 | Min int `xml:"minOccurs,attr"` 204 | Max string `xml:"maxOccurs,attr"` // can be # or unbounded 205 | } 206 | 207 | // Import points to another WSDL to be imported at root level. 208 | type Import struct { 209 | XMLName xml.Name `xml:"import"` 210 | Namespace string `xml:"namespace,attr"` 211 | Location string `xml:"location,attr"` 212 | } 213 | 214 | // ImportSchema points to another WSDL to be imported at schema level. 215 | type ImportSchema struct { 216 | XMLName xml.Name `xml:"import"` 217 | Namespace string `xml:"namespace,attr"` 218 | Location string `xml:"schemaLocation,attr"` 219 | } 220 | 221 | // IncludeSchema points to another WSDL to be imported at schema level. 222 | type IncludeSchema struct { 223 | XMLName xml.Name `xml:"include"` 224 | Namespace string `xml:"namespace,attr"` 225 | Location string `xml:"schemaLocation,attr"` 226 | } 227 | 228 | // Message describes the data being communicated, such as functions 229 | // and their parameters. 230 | type Message struct { 231 | XMLName xml.Name `xml:"message"` 232 | Name string `xml:"name,attr"` 233 | Parts []*Part `xml:"part"` 234 | } 235 | 236 | // Part describes what Type or Element to use from the PortType. 237 | type Part struct { 238 | XMLName xml.Name `xml:"part"` 239 | Name string `xml:"name,attr"` 240 | Type string `xml:"type,attr,omitempty"` 241 | Element string `xml:"element,attr,omitempty"` // TODO: not sure omitempty 242 | } 243 | 244 | // PortType describes a set of operations. 245 | type PortType struct { 246 | XMLName xml.Name `xml:"portType"` 247 | Name string `xml:"name,attr"` 248 | Operations []*Operation `xml:"operation"` 249 | } 250 | 251 | // Operation describes an operation. 252 | type Operation struct { 253 | XMLName xml.Name `xml:"operation"` 254 | Name string `xml:"name,attr"` 255 | Doc string `xml:"documentation"` 256 | Input *IO `xml:"input"` 257 | Output *IO `xml:"output"` 258 | } 259 | 260 | // IO describes which message is linked to an operation, for input 261 | // or output parameters. 262 | type IO struct { 263 | XMLName xml.Name 264 | Message string `xml:"message,attr"` 265 | } 266 | 267 | // Binding describes SOAP to WSDL binding. 268 | type Binding struct { 269 | XMLName xml.Name `xml:"binding"` 270 | Name string `xml:"name,attr"` 271 | Type string `xml:"type,attr"` 272 | BindingType *BindingType `xml:"binding"` 273 | Operations []*BindingOperation `xml:"operation"` 274 | } 275 | 276 | // BindingType contains additional meta data on how to implement the binding. 277 | type BindingType struct { 278 | Style string `xml:"style,attr"` 279 | Transport string `xml:"transport,attr"` 280 | } 281 | 282 | // BindingOperation describes the requirement for binding SOAP to WSDL 283 | // operations. 284 | type BindingOperation struct { 285 | XMLName xml.Name `xml:"operation"` 286 | Name string `xml:"name,attr"` 287 | Operation SOAP12Operation `xml:"http://schemas.xmlsoap.org/wsdl/soap12/ operation"` 288 | Operation11 SOAP11Operation `xml:"http://schemas.xmlsoap.org/wsdl/soap/ operation"` 289 | Input *BindingIO `xml:"input>body"` 290 | Output *BindingIO `xml:"output>body"` 291 | } 292 | 293 | // SOAP12Operation describes a SOAP 1.2 operation. The soap12 namespace is 294 | // important as the presence of a SOAP12Operation.Action is used to switch 295 | // things over to sending the SOAP 1.2 content type header: 296 | // (application/xml; charset=UTF-8; action='foobar') 297 | type SOAP12Operation struct { 298 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/wsdl/soap12/ operation"` 299 | Action string `xml:"soapAction,attr"` 300 | } 301 | 302 | // SOAP11Operation describes a SOAP 1.1 operation. If it is specified in the wsdl, 303 | // the soapAction will use this value instead of the default value 304 | type SOAP11Operation struct { 305 | XMLName xml.Name `xml:"http://schemas.xmlsoap.org/wsdl/soap/ operation"` 306 | Action string `xml:"soapAction,attr"` 307 | } 308 | 309 | // BindingIO describes the IO binding of SOAP operations. See IO for details. 310 | type BindingIO struct { 311 | Parts string `xml:"parts,attr"` 312 | Use string `xml:"use,attr"` 313 | } 314 | -------------------------------------------------------------------------------- /wsdlgo/encoder.go: -------------------------------------------------------------------------------- 1 | // Package wsdlgo provides an encoder from WSDL to Go code. 2 | package wsdlgo 3 | 4 | // TODO: make it generate code fully compliant with the spec. 5 | // TODO: support all WSDL types. 6 | // TODO: fully support SOAP bindings, faults, and transports. 7 | 8 | import ( 9 | "bufio" 10 | "bytes" 11 | "encoding/xml" 12 | "fmt" 13 | "go/parser" 14 | "go/token" 15 | "io" 16 | "log" 17 | "net/http" 18 | "net/url" 19 | "os" 20 | "os/exec" 21 | "path/filepath" 22 | "regexp" 23 | "sort" 24 | "strconv" 25 | "strings" 26 | "text/template" 27 | 28 | "github.com/fiorix/wsdl2go/wsdl" 29 | "golang.org/x/net/html/charset" 30 | ) 31 | 32 | const fileHeader = "// Code generated by wsdl2go. DO NOT EDIT." 33 | 34 | // An Encoder generates Go code from WSDL definitions. 35 | type Encoder interface { 36 | // Encode generates Go code from d. 37 | Encode(d *wsdl.Definitions) error 38 | 39 | // SetPackageName sets some fmt.Stringer that can produce package name 40 | SetPackageName(packageName fmt.Stringer) 41 | 42 | // SetClient records the given http client that 43 | // is used when fetching remote parts of WSDL 44 | // and WSDL schemas. 45 | SetClient(c *http.Client) 46 | 47 | // SetLocalNamespace allows overriding of the Namespace in XMLName instead 48 | // of the one specified in wsdl 49 | SetLocalNamespace(namespace string) 50 | } 51 | 52 | type goEncoder struct { 53 | // where to write Go code 54 | w io.Writer 55 | 56 | // http client 57 | http *http.Client 58 | 59 | // some mechanism to name package 60 | packageName fmt.Stringer 61 | 62 | // types cache 63 | stypes map[string]*wsdl.SimpleType 64 | ctypes map[string]*wsdl.ComplexType 65 | 66 | // elements cache 67 | elements map[string]*wsdl.Element 68 | 69 | // funcs cache 70 | funcs map[string]*wsdl.Operation 71 | funcnames []string 72 | 73 | // messages cache 74 | messages map[string]*wsdl.Message 75 | 76 | // soap operations cache 77 | soapOps map[string]*wsdl.BindingOperation 78 | 79 | // whether to add supporting types 80 | needsDateType bool 81 | needsTimeType bool 82 | needsDateTimeType bool 83 | needsDurationType bool 84 | needsTag map[string]string 85 | needsStdPkg map[string]bool 86 | needsExtPkg map[string]bool 87 | importedSchemas map[string]bool 88 | usedNamespaces map[string]string 89 | 90 | // localNamespace allows overriding of namespace in XMLName 91 | localNamespace string 92 | } 93 | 94 | // NewEncoder creates and initializes an Encoder that generates code to w. 95 | func NewEncoder(w io.Writer) Encoder { 96 | return &goEncoder{ 97 | w: w, 98 | http: http.DefaultClient, 99 | stypes: make(map[string]*wsdl.SimpleType), 100 | ctypes: make(map[string]*wsdl.ComplexType), 101 | elements: make(map[string]*wsdl.Element), 102 | funcs: make(map[string]*wsdl.Operation), 103 | messages: make(map[string]*wsdl.Message), 104 | soapOps: make(map[string]*wsdl.BindingOperation), 105 | needsTag: make(map[string]string), 106 | needsStdPkg: make(map[string]bool), 107 | needsExtPkg: make(map[string]bool), 108 | importedSchemas: make(map[string]bool), 109 | } 110 | } 111 | 112 | func (ge *goEncoder) SetPackageName(name fmt.Stringer) { 113 | ge.packageName = name 114 | } 115 | 116 | func (ge *goEncoder) SetClient(c *http.Client) { 117 | ge.http = c 118 | } 119 | 120 | func gofmtPath() (string, error) { 121 | goroot := os.Getenv("GOROOT") 122 | if goroot != "" { 123 | return filepath.Join(goroot, "bin", "gofmt"), nil 124 | } 125 | return exec.LookPath("gofmt") 126 | 127 | } 128 | 129 | var numberSequence = regexp.MustCompile(`([a-zA-Z])(\d+)([a-zA-Z]?)`) 130 | var numberReplacement = []byte(`$1 $2 $3`) 131 | 132 | func addWordBoundariesToNumbers(s string) string { 133 | b := []byte(s) 134 | b = numberSequence.ReplaceAll(b, numberReplacement) 135 | return string(b) 136 | } 137 | 138 | func (ge *goEncoder) Encode(d *wsdl.Definitions) error { 139 | if d == nil { 140 | return nil 141 | } 142 | 143 | // default mechanism to set package name 144 | if ge.packageName == nil { 145 | ge.packageName = BindingPackageName(d.Binding) 146 | } 147 | 148 | var b bytes.Buffer 149 | err := ge.encode(&b, d) 150 | if err != nil { 151 | return err 152 | } 153 | if b.Len() == 0 { 154 | return nil 155 | } 156 | var errb bytes.Buffer 157 | input := b.String() 158 | 159 | // try to parse the generated code 160 | fset := token.NewFileSet() 161 | _, err = parser.ParseFile(fset, "", &b, parser.ParseComments) 162 | if err != nil { 163 | var src bytes.Buffer 164 | s := bufio.NewScanner(strings.NewReader(input)) 165 | for line := 1; s.Scan(); line++ { 166 | fmt.Fprintf(&src, "%5d\t%s\n", line, s.Bytes()) 167 | } 168 | return fmt.Errorf("generated bad code: %v\n%s", err, src.String()) 169 | } 170 | 171 | // dat pipe to gofmt 172 | path, err := gofmtPath() 173 | if err != nil { 174 | return fmt.Errorf("cannot find gofmt with err: %v", err) 175 | } 176 | cmd := exec.Cmd{ 177 | Path: path, 178 | Stdin: &b, 179 | Stdout: ge.w, 180 | Stderr: &errb, 181 | } 182 | err = cmd.Run() 183 | if err != nil { 184 | var x bytes.Buffer 185 | fmt.Fprintf(&x, "gofmt: %v\n", err) 186 | if errb.Len() > 0 { 187 | fmt.Fprintf(&x, "gofmt stderr:\n%s\n", errb.String()) 188 | } 189 | fmt.Fprintf(&x, "generated code:\n%s\n", input) 190 | return fmt.Errorf(x.String()) 191 | } 192 | return nil 193 | } 194 | 195 | func (ge *goEncoder) encode(w io.Writer, d *wsdl.Definitions) error { 196 | ge.unionSchemasData(d, &d.Schema) 197 | err := ge.importParts(d) 198 | ge.usedNamespaces = d.Namespaces 199 | if err != nil { 200 | return fmt.Errorf("wsdl import: %v", err) 201 | } 202 | ge.cacheTypes(d) 203 | ge.cacheFuncs(d) 204 | ge.cacheMessages(d) 205 | ge.cacheSOAPOperations(d) 206 | 207 | var b bytes.Buffer 208 | var ff []func(io.Writer, *wsdl.Definitions) error 209 | if len(ge.soapOps) > 0 { 210 | ff = append(ff, 211 | ge.writeInterfaceFuncs, 212 | ge.writeGoTypes, 213 | ge.writePortType, 214 | ge.writeGoFuncs, 215 | ) 216 | } else { 217 | // TODO: probably faulty wsdl? 218 | ff = append(ff, 219 | ge.writeGoFuncs, 220 | ge.writeGoTypes, 221 | ) 222 | } 223 | for _, f := range ff { 224 | err := f(&b, d) 225 | if err != nil { 226 | return err 227 | } 228 | } 229 | 230 | fmt.Fprintf(w, "%s\n\npackage %s\n\nimport (\n", fileHeader, ge.packageName) 231 | for pkg := range ge.needsStdPkg { 232 | fmt.Fprintf(w, "%q\n", pkg) 233 | } 234 | if len(ge.needsStdPkg) > 0 { 235 | fmt.Fprintf(w, "\n") 236 | } 237 | for pkg := range ge.needsExtPkg { 238 | fmt.Fprintf(w, "%q\n", pkg) 239 | } 240 | fmt.Fprintf(w, ")\n\n") 241 | if d.TargetNamespace != "" { 242 | ge.writeComments(w, "Namespace", "") 243 | fmt.Fprintf(w, "var Namespace = %q\n\n", d.TargetNamespace) 244 | } 245 | _, err = io.Copy(w, &b) 246 | return err 247 | } 248 | 249 | func (ge *goEncoder) importParts(d *wsdl.Definitions) error { 250 | err := ge.importRoot(d) 251 | if err != nil { 252 | return err 253 | } 254 | return ge.importSchema(d) 255 | } 256 | 257 | func (ge *goEncoder) importRoot(d *wsdl.Definitions) error { 258 | for _, imp := range d.Imports { 259 | if imp.Location == "" { 260 | continue 261 | } 262 | err := ge.importRemote(imp.Location, &d) 263 | if err != nil { 264 | return err 265 | } 266 | } 267 | return nil 268 | } 269 | 270 | func (ge *goEncoder) importSchema(d *wsdl.Definitions) error { 271 | for _, imp := range d.Schema.Imports { 272 | if imp.Location == "" { 273 | continue 274 | } 275 | schema := &wsdl.Schema{} 276 | err := ge.importRemote(imp.Location, schema) 277 | if err != nil { 278 | return err 279 | } 280 | ge.unionSchemasData(d, schema) 281 | for _, item := range schema.Imports { 282 | schema = &wsdl.Schema{} 283 | err := ge.importRemote(item.Location, schema) 284 | if err != nil { 285 | return err 286 | } 287 | ge.unionSchemasData(d, schema) 288 | } 289 | for _, item := range schema.Includes { 290 | schema = &wsdl.Schema{} 291 | err := ge.importRemote(item.Location, schema) 292 | if err != nil { 293 | return err 294 | } 295 | ge.unionSchemasData(d, schema) 296 | } 297 | } 298 | return nil 299 | } 300 | 301 | func (ge *goEncoder) unionSchemasData(d *wsdl.Definitions, s *wsdl.Schema) { 302 | for ns := range s.Namespaces { 303 | d.Namespaces[ns] = s.Namespaces[ns] 304 | } 305 | for _, ct := range s.ComplexTypes { 306 | ct.TargetNamespace = s.TargetNamespace 307 | } 308 | for _, st := range s.SimpleTypes { 309 | st.TargetNamespace = s.TargetNamespace 310 | } 311 | d.Schema.ComplexTypes = append(d.Schema.ComplexTypes, s.ComplexTypes...) 312 | d.Schema.SimpleTypes = append(d.Schema.SimpleTypes, s.SimpleTypes...) 313 | d.Schema.Elements = append(d.Schema.Elements, s.Elements...) 314 | } 315 | 316 | // download xml from url, decode in v. 317 | func (ge *goEncoder) importRemote(loc string, v interface{}) error { 318 | _, alreadyImported := ge.importedSchemas[loc] 319 | if alreadyImported { 320 | return nil 321 | } 322 | 323 | u, err := url.Parse(loc) 324 | if err != nil { 325 | return err 326 | } 327 | 328 | var r io.Reader 329 | switch u.Scheme { 330 | case "http", "https": 331 | resp, err := ge.http.Get(loc) 332 | if err != nil { 333 | return err 334 | } 335 | ge.importedSchemas[loc] = true 336 | defer resp.Body.Close() 337 | r = resp.Body 338 | default: 339 | file, err := os.Open(u.Path) 340 | if err != nil { 341 | return fmt.Errorf("could not open file raw: %s path: %s escaped: %s : %v", u.RawPath, u.Path, u.EscapedPath(), err) 342 | } 343 | 344 | r = bufio.NewReader(file) 345 | } 346 | decoder := xml.NewDecoder(r) 347 | decoder.CharsetReader = charset.NewReaderLabel 348 | return decoder.Decode(&v) 349 | 350 | } 351 | 352 | func (ge *goEncoder) cacheTypes(d *wsdl.Definitions) { 353 | // operation types are declared as go struct types 354 | for _, v := range d.Schema.Elements { 355 | if v.Type == "" && v.ComplexType != nil { 356 | ct := *v.ComplexType 357 | ct.Name = v.Name 358 | ge.ctypes[v.Name] = &ct 359 | } 360 | } 361 | // simple types map 1:1 to go basic types 362 | for _, v := range d.Schema.SimpleTypes { 363 | ge.stypes[v.Name] = v 364 | } 365 | // complex types are declared as go struct types 366 | for _, v := range d.Schema.ComplexTypes { 367 | ge.ctypes[v.Name] = v 368 | } 369 | // cache elements from schema 370 | ge.cacheElements(d.Schema.Elements) 371 | // cache elements from complex types 372 | for _, ct := range ge.ctypes { 373 | ge.cacheComplexTypeElements(ct) 374 | } 375 | } 376 | 377 | func (ge *goEncoder) cacheChoiceTypeElements(choice *wsdl.Choice) { 378 | if choice != nil { 379 | for _, cct := range choice.ComplexTypes { 380 | ge.cacheComplexTypeElements(cct) 381 | } 382 | ge.cacheElements(choice.Elements) 383 | } 384 | } 385 | 386 | func (ge *goEncoder) cacheComplexTypeElements(ct *wsdl.ComplexType) { 387 | if ct.AllElements != nil { 388 | ge.cacheElements(ct.AllElements) 389 | } 390 | if ct.Sequence != nil { 391 | ge.cacheElements(ct.Sequence.Elements) 392 | } 393 | if ct.Choice != nil { 394 | ge.cacheElements(ct.Choice.Elements) 395 | } 396 | 397 | cc := ct.ComplexContent 398 | if cc != nil { 399 | cce := cc.Extension 400 | if cce != nil && cce.Sequence != nil { 401 | seq := cce.Sequence 402 | for _, cct := range seq.ComplexTypes { 403 | ge.cacheComplexTypeElements(cct) 404 | } 405 | ge.cacheElements(seq.Elements) 406 | 407 | //Add in Choice elements 408 | for _, choice := range seq.Choices { 409 | ge.cacheChoiceTypeElements(choice) 410 | } 411 | } 412 | if cce != nil && cce.Choice != nil { 413 | ge.cacheChoiceTypeElements(cce.Choice) 414 | } 415 | } 416 | } 417 | 418 | func (ge *goEncoder) cacheElements(ct []*wsdl.Element) { 419 | for _, el := range ct { 420 | if el.Name == "" || el.Type == "" { 421 | if el.Ref == "" { 422 | continue 423 | } 424 | el.Name = trimns(el.Ref) 425 | el.Type = el.Name 426 | } 427 | name := trimns(el.Name) 428 | if _, exists := ge.elements[name]; exists { 429 | continue 430 | } 431 | ge.elements[name] = el 432 | ct := el.ComplexType 433 | if ct != nil { 434 | ge.cacheElements(ct.AllElements) 435 | if ct.Sequence != nil { 436 | ge.cacheElements(ct.Sequence.Elements) 437 | } 438 | if ct.Choice != nil { 439 | ge.cacheElements(ct.Choice.Elements) 440 | } 441 | } 442 | } 443 | } 444 | 445 | func (ge *goEncoder) cacheFuncs(d *wsdl.Definitions) { 446 | // operations are declared as boilerplate go functions 447 | for _, v := range d.PortType.Operations { 448 | ge.funcs[v.Name] = v 449 | } 450 | ge.funcnames = make([]string, len(ge.funcs)) 451 | i := 0 452 | for k := range ge.funcs { 453 | ge.funcnames[i] = k 454 | i++ 455 | } 456 | sort.Strings(ge.funcnames) 457 | } 458 | 459 | func (ge *goEncoder) cacheMessages(d *wsdl.Definitions) { 460 | for _, v := range d.Messages { 461 | ge.messages[v.Name] = v 462 | } 463 | } 464 | 465 | func (ge *goEncoder) cacheSOAPOperations(d *wsdl.Definitions) { 466 | for _, v := range d.Binding.Operations { 467 | ge.soapOps[v.Name] = v 468 | } 469 | } 470 | 471 | var interfaceTypeT = template.Must(template.New("interfaceType").Parse(` 472 | // New{{.Name}} creates an initializes a {{.Name}}. 473 | func New{{.Name}}(cli *soap.Client) {{.Name}} { 474 | return &{{.Impl}}{cli} 475 | } 476 | 477 | // {{.Name}} was auto-generated from WSDL 478 | // and defines interface for the remote service. Useful for testing. 479 | type {{.Name}} interface { 480 | {{- range .Funcs }} 481 | {{.Doc}}{{.Name}}({{.Input}}) ({{.Output}}) 482 | {{ end }} 483 | } 484 | `)) 485 | 486 | type interfaceTypeFunc struct{ Doc, Name, Input, Output string } 487 | 488 | // writeInterfaceFuncs writes Go interface definitions from WSDL types to w. 489 | // Functions are written in the same order of the WSDL document. 490 | func (ge *goEncoder) writeInterfaceFuncs(w io.Writer, d *wsdl.Definitions) error { 491 | funcs := make([]*interfaceTypeFunc, len(ge.funcs)) 492 | // Looping over the operations to determine what are the interface 493 | // functions. 494 | i := 0 495 | for _, fn := range ge.funcnames { 496 | op := ge.funcs[fn] 497 | if _, exists := ge.soapOps[op.Name]; !exists { 498 | // TODO: probably faulty wsdl? 499 | continue 500 | } 501 | inParams, err := ge.inputParams(op) 502 | if err != nil { 503 | return err 504 | } 505 | outParams, err := ge.outputParams(op) 506 | if err != nil { 507 | return err 508 | } 509 | in, out := code(inParams), codeParams(outParams) 510 | name := goSymbol(op.Name) 511 | var doc bytes.Buffer 512 | ge.writeComments(&doc, name, op.Doc) 513 | funcs[i] = &interfaceTypeFunc{ 514 | Doc: doc.String(), 515 | Name: name, 516 | Input: strings.Join(in, ","), 517 | Output: strings.Join(out, ","), 518 | } 519 | i++ 520 | } 521 | n := d.PortType.Name 522 | return interfaceTypeT.Execute(w, &struct { 523 | Name string 524 | Impl string // private type that implements the interface 525 | Funcs []*interfaceTypeFunc 526 | }{ 527 | goSymbol(n), 528 | strings.ToLower(n)[:1] + n[1:], 529 | funcs[:i], 530 | }) 531 | } 532 | 533 | var portTypeT = template.Must(template.New("portType").Parse(` 534 | // {{.Name}} implements the {{.Interface}} interface. 535 | type {{.Name}} struct { 536 | cli *soap.Client 537 | } 538 | 539 | `)) 540 | 541 | func (ge *goEncoder) writePortType(w io.Writer, d *wsdl.Definitions) error { 542 | if len(ge.funcs) == 0 { 543 | return nil 544 | } 545 | n := d.PortType.Name 546 | return portTypeT.Execute(w, &struct { 547 | Name string 548 | Interface string 549 | }{ 550 | strings.ToLower(n)[:1] + n[1:], 551 | goSymbol(n), 552 | }) 553 | } 554 | 555 | // writeGoFuncs writes Go function definitions from WSDL types to w. 556 | // Functions are written in the same order of the WSDL document. 557 | func (ge *goEncoder) writeGoFuncs(w io.Writer, d *wsdl.Definitions) error { 558 | if d.Binding.Type != "" { 559 | a, b := trimns(d.Binding.Type), trimns(d.PortType.Name) 560 | if a != b { 561 | return fmt.Errorf( 562 | "binding %q requires port type %q but it's not defined", 563 | d.Binding.Name, d.Binding.Type) 564 | } 565 | } 566 | if len(ge.funcs) == 0 { 567 | return nil 568 | } 569 | for _, fn := range ge.funcnames { 570 | op := ge.funcs[fn] 571 | ge.writeComments(w, op.Name, op.Doc) 572 | inParams, err := ge.inputParams(op) 573 | if err != nil { 574 | return err 575 | } 576 | outParams, err := ge.outputParams(op) 577 | if err != nil { 578 | return err 579 | } 580 | 581 | ok := ge.writeSOAPFunc(w, d, op, inParams, outParams) 582 | if !ok { 583 | in, out := code(inParams), codeParams(outParams) 584 | ret := make([]string, len(out)) 585 | for i, p := range outParams { 586 | ret[i] = ge.wsdl2goDefault(p.dataType) 587 | } 588 | 589 | ge.needsStdPkg["errors"] = true 590 | ge.needsStdPkg["context"] = true 591 | in = append([]string{"ctx context.Context"}, in...) 592 | 593 | fn := ge.fixFuncNameConflicts(goSymbol(op.Name)) 594 | fmt.Fprintf(w, "func %s(%s) (%s) {\nreturn %s\n}\n\n", 595 | fn, 596 | strings.Join(in, ","), 597 | strings.Join(out, ","), 598 | strings.Join(ret, ","), 599 | ) 600 | } 601 | } 602 | return nil 603 | } 604 | 605 | var soapFuncT = template.Must(template.New("soapFunc").Parse( 606 | `func (p *{{.PortType}}) {{.Name}}({{.Input}}) ({{.Output}}) { 607 | α := struct { 608 | {{if .OpInputDataType}} 609 | {{if .RPCStyle}}M{{end}} {{.OpInputDataType}} ` + "`xml:\"{{.OpName}}\"`" + ` 610 | {{end}} 611 | }{ 612 | {{if .OpInputDataType}}{{.OpInputDataType}} { 613 | {{range $index, $element := .InputNames}}{{$element}}, 614 | {{end}} 615 | },{{end}} 616 | } 617 | 618 | γ := struct { 619 | {{if .OpResponseDataType}} 620 | {{if .RPCStyle}}M {{end}}{{.OpResponseDataType}} ` + "`xml:\"{{.OpResponseName}}\"`" + ` 621 | {{end}} 622 | }{} 623 | if err := p.cli.RoundTripWithAction("{{.Name}}", α, &γ); err != nil { 624 | return {{.RetDef}} 625 | } 626 | return {{range $index, $element := .OpOutputNames}}{{index $.OpOutputPrefixes $index}}γ.{{if $.RPCStyle}}M.{{end}}{{$element}}, {{end}}nil 627 | } 628 | `)) 629 | 630 | var soapActionFuncT = template.Must(template.New("soapActionFunc").Parse( 631 | `func (p *{{.PortType}}) {{.Name}}({{.Input}}) ({{.Output}}) { 632 | α := struct { 633 | {{if .OpInputDataType}} 634 | {{if .RPCStyle}}M{{end}} {{.OpInputDataType}} ` + "`xml:\"{{.OpName}}\"`" + ` 635 | {{end}} 636 | }{ 637 | {{if .OpInputDataType}}{{.OpInputDataType}} { 638 | {{range $index, $element := .InputNames}}{{$element}}, 639 | {{end}} 640 | },{{end}} 641 | } 642 | 643 | γ := struct { 644 | {{if .OpResponseDataType}} 645 | {{if .RPCStyle}}M {{end}}{{.OpResponseDataType}} ` + "`xml:\"{{.OpResponseName}}\"`" + ` 646 | {{end}} 647 | }{} 648 | if err := p.cli.{{.RoundTripType}}("{{.Action}}", α, &γ); err != nil { 649 | return {{.RetDef}} 650 | } 651 | return {{range $index, $element := .OpOutputNames}}{{index $.OpOutputPrefixes $index}}γ.{{if $.RPCStyle}}M.{{end}}{{$element}}, {{end}}nil 652 | } 653 | `)) 654 | 655 | func (ge *goEncoder) writeSOAPFunc(w io.Writer, d *wsdl.Definitions, op *wsdl.Operation, in, out []*parameter) bool { 656 | if _, exists := ge.soapOps[op.Name]; !exists { 657 | // TODO: probably faulty wsdl? 658 | return false 659 | } 660 | 661 | // Do we need to wrap into a operation element? 662 | rpcStyle := false 663 | 664 | if d.Binding.BindingType != nil { 665 | rpcStyle = d.Binding.BindingType.Style == "rpc" 666 | } 667 | 668 | ge.needsExtPkg["github.com/fiorix/wsdl2go/soap"] = true 669 | 670 | // inputNames describe the accessors to the input parameter names 671 | inputNames := make([]string, len(in)) 672 | for index, name := range in { 673 | returnVal := maskKeywordUsage(name.code) 674 | 675 | if !strings.HasPrefix(name.dataType, "*") { 676 | returnVal = "&" + returnVal 677 | } 678 | 679 | inputNames[index] = returnVal 680 | } 681 | 682 | // outputDataTypes describe the data types which are returned by the func 683 | outputDataTypes := make([]string, len(out)) 684 | 685 | // retDefaults describes the default return values in case of an error 686 | retDefaults := make([]string, len(out)) 687 | 688 | // operationOutputNames describes the fields which are part of the response we unmarshal 689 | // len-1, because the last parameter is error, which is not part of the xml response we unmarshal 690 | operationOutputNames := make([]string, len(out)-1) 691 | operationOutputPrefixes := make([]string, len(out)-1) 692 | 693 | for index, name := range out { 694 | outputDataTypes[index] = name.dataType 695 | 696 | // operationOutputNames names will only be computed till len-1 697 | if index == len(out)-1 { 698 | continue 699 | } 700 | 701 | operationOutputNames[index] = strings.ToUpper(name.code[:1]) + name.code[1:] 702 | operationOutputPrefixes[index] = "" 703 | retDefaults[index] = "nil" 704 | 705 | // If the output is >not< a pointer, we need to return the value of the response 706 | if !strings.HasPrefix(name.dataType, "*") { 707 | operationOutputPrefixes[index] = "*" 708 | 709 | // Also - only resolve the default for non-pointer returns (otherwise nil suffices) 710 | retDefaults[index] = ge.wsdl2goDefault(name.dataType) 711 | } 712 | } 713 | retDefaults[len(retDefaults)-1] = "err" 714 | 715 | // Check if we need to prefix the op with a namespace 716 | mInput := ge.funcs[op.Name].Input 717 | namespacedOpName := op.Name 718 | 719 | if mInput != nil { 720 | nsSplit := strings.Split(mInput.Message, ":") 721 | if len(nsSplit) > 1 { 722 | namespacedOpName = nsSplit[0] + ":" + namespacedOpName 723 | } 724 | } 725 | 726 | // The response name is always the operation name + "Response" according to specification. 727 | // Note, we also omit the namespace, since this does currently not work reliable with golang 728 | // (See: https://github.com/golang/go/issues/14407) 729 | opResponseName := op.Name + "Response" 730 | 731 | // No-input operations can be inlined into an anonymous struct on rpc, and omitted otherwise 732 | operationInputDataType := "" 733 | 734 | if len(in) > 0 && op.Input != nil { 735 | operationInputDataType = ge.sanitizedOperationsType(ge.messages[trimns(op.Input.Message)].Name) 736 | } else if rpcStyle { 737 | operationInputDataType = "struct{}" 738 | } 739 | 740 | // No-output operations can be inlined into an anonymous struct on rpc, and omitted otherwise 741 | operationOutputDataType := "" 742 | 743 | if len(out) > 0 && op.Output != nil { 744 | operationOutputDataType = ge.sanitizedOperationsType(ge.messages[trimns(op.Output.Message)].Name) 745 | } else if rpcStyle { 746 | operationInputDataType = "struct{}" 747 | } 748 | 749 | soapFunctionName := "RoundTripSoap12" 750 | soapAction := "" 751 | if bindingOp, exists := ge.soapOps[op.Name]; exists { 752 | soapAction = bindingOp.Operation.Action 753 | if soapAction == "" { 754 | soapFunctionName = "RoundTripWithAction" 755 | soapAction = bindingOp.Operation11.Action 756 | } 757 | } 758 | if soapAction != "" { 759 | soapActionFuncT.Execute(w, &struct { 760 | RoundTripType string 761 | Action string 762 | PortType string 763 | Name string 764 | OpName string 765 | OpInputDataType string 766 | InputNames []string 767 | OpResponseName string 768 | OpResponseDataType string 769 | OpOutputNames []string 770 | OpOutputPrefixes []string 771 | Input string 772 | Output string 773 | RetDef string 774 | RPCStyle bool 775 | }{ 776 | soapFunctionName, 777 | soapAction, 778 | strings.ToLower(d.PortType.Name[:1]) + d.PortType.Name[1:], 779 | goSymbol(op.Name), 780 | namespacedOpName, 781 | operationInputDataType, 782 | inputNames, 783 | opResponseName, 784 | operationOutputDataType, 785 | operationOutputNames, 786 | operationOutputPrefixes, 787 | strings.Join(code(in), ","), 788 | strings.Join(outputDataTypes, ","), 789 | strings.Join(retDefaults, ","), 790 | rpcStyle, 791 | }) 792 | return true 793 | } 794 | soapFuncT.Execute(w, &struct { 795 | PortType string 796 | Name string 797 | OpName string 798 | OpInputDataType string 799 | InputNames []string 800 | OpResponseName string 801 | OpResponseDataType string 802 | OpOutputNames []string 803 | OpOutputPrefixes []string 804 | Input string 805 | Output string 806 | RetDef string 807 | RPCStyle bool 808 | }{ 809 | strings.ToLower(d.PortType.Name[:1]) + d.PortType.Name[1:], 810 | goSymbol(op.Name), 811 | namespacedOpName, 812 | operationInputDataType, 813 | inputNames, 814 | opResponseName, 815 | operationOutputDataType, 816 | operationOutputNames, 817 | operationOutputPrefixes, 818 | strings.Join(code(in), ","), 819 | strings.Join(outputDataTypes, ","), 820 | strings.Join(retDefaults, ","), 821 | rpcStyle, 822 | }) 823 | return true 824 | } 825 | 826 | func renameParam(p, name string) string { 827 | v := strings.SplitN(p, " ", 2) 828 | if len(v) != 2 { 829 | return p 830 | } 831 | return name + " " + v[1] 832 | } 833 | 834 | // returns list of function input parameters. 835 | func (ge *goEncoder) inputParams(op *wsdl.Operation) ([]*parameter, error) { 836 | if op.Input == nil { 837 | return []*parameter{}, nil 838 | } 839 | im := trimns(op.Input.Message) 840 | req, ok := ge.messages[im] 841 | if !ok { 842 | return nil, fmt.Errorf("operation %q wants input message %q but it's not defined", op.Name, im) 843 | } 844 | 845 | // TODO: I had to disable this for my use case - do other use cases still work with false? 846 | return ge.genParams(req, false), nil 847 | } 848 | 849 | // returns list of function output parameters plus error. 850 | func (ge *goEncoder) outputParams(op *wsdl.Operation) ([]*parameter, error) { 851 | out := []*parameter{{code: "err", dataType: "error"}} 852 | 853 | if op.Output == nil { 854 | return out, nil 855 | } 856 | om := trimns(op.Output.Message) 857 | resp, ok := ge.messages[om] 858 | if !ok { 859 | return nil, fmt.Errorf("operation %q wants output message %q but it's not defined", op.Name, om) 860 | } 861 | return append(ge.genParams(resp, false), out[0]), nil 862 | } 863 | 864 | var isGoKeyword = map[string]bool{ 865 | "break": true, 866 | "case": true, 867 | "chan": true, 868 | "const": true, 869 | "continue": true, 870 | "default": true, 871 | "else": true, 872 | "defer": true, 873 | "fallthrough": true, 874 | "for": true, 875 | "func": true, 876 | "go": true, 877 | "goto": true, 878 | "if": true, 879 | "import": true, 880 | "interface": true, 881 | "map": true, 882 | "package": true, 883 | "range": true, 884 | "return": true, 885 | "select": true, 886 | "struct": true, 887 | "switch": true, 888 | "type": true, 889 | "var": true, 890 | } 891 | 892 | type parameter struct { 893 | code string 894 | dataType string 895 | xmlToken string 896 | } 897 | 898 | func code(list []*parameter) []string { 899 | code := make([]string, len(list)) 900 | for i, p := range list { 901 | code[i] = maskKeywordUsage(p.code) + " " + p.dataType 902 | } 903 | return code 904 | } 905 | 906 | func codeParams(list []*parameter) []string { 907 | code := make([]string, len(list)) 908 | for i, p := range list { 909 | code[i] = p.dataType 910 | } 911 | return code 912 | } 913 | 914 | func maskKeywordUsage(code string) string { 915 | returnVal := code 916 | 917 | if isGoKeyword[code] { 918 | returnVal = "_" + code 919 | } 920 | 921 | return returnVal 922 | } 923 | 924 | func (ge *goEncoder) genParams(m *wsdl.Message, needsTag bool) []*parameter { 925 | params := make([]*parameter, len(m.Parts)) 926 | for i, param := range m.Parts { 927 | code := param.Name 928 | var t, token, elName string 929 | switch { 930 | case param.Type != "": 931 | t = ge.wsdl2goType(param.Type) 932 | elName = trimns(param.Type) 933 | token = t 934 | case param.Element != "": 935 | elName = trimns(param.Element) 936 | code = goSymbol(param.Element) 937 | if el, ok := ge.elements[elName]; ok { 938 | t = ge.wsdl2goType(trimns(el.Type)) 939 | } else { 940 | t = ge.wsdl2goType(param.Element) 941 | } 942 | token = trimns(param.Element) 943 | } 944 | params[i] = ¶meter{code: code, dataType: t, xmlToken: token} 945 | if needsTag { 946 | ge.needsStdPkg["encoding/xml"] = true 947 | ge.needsTag[strings.TrimPrefix(t, "*")] = elName 948 | } 949 | } 950 | return params 951 | } 952 | 953 | // Fixes conflicts between function and type names. 954 | func (ge *goEncoder) fixFuncNameConflicts(name string) string { 955 | if _, exists := ge.stypes[name]; exists { 956 | name += "Func" 957 | return ge.fixFuncNameConflicts(name) 958 | } 959 | if _, exists := ge.ctypes[name]; exists { 960 | name += "Func" 961 | return ge.fixFuncNameConflicts(name) 962 | } 963 | return name 964 | } 965 | 966 | // Fixes request and response parameters with the same name, in place. 967 | // Each string in the slice consists of Go's "name Type", we only 968 | // compare names. In case of a conflict, we set the response one 969 | // in the form of respName. 970 | func (ge *goEncoder) fixParamConflicts(req, resp []string) { 971 | for _, a := range req { 972 | for j, b := range resp { 973 | x := strings.SplitN(a, " ", 2)[0] 974 | y := strings.SplitN(b, " ", 2) 975 | if len(y) > 1 { 976 | if x == y[0] { 977 | n := goSymbol(y[0]) 978 | resp[j] = "resp" + n + " " + y[1] 979 | } 980 | } 981 | } 982 | } 983 | } 984 | 985 | // Helps to clean up operation names, so we can generate 986 | // nice datatype names which make golang happy. 987 | // E.g. - a soap operation gkstServer_getVersion is sanitized 988 | // to gkstServerGetVersion (remove snake case) 989 | func (ge *goEncoder) sanitizedOperationsType(opName string) string { 990 | return "Operation" + goSymbol(opName) 991 | } 992 | 993 | // Converts types from wsdl type to Go type. 994 | func (ge *goEncoder) wsdl2goType(t string) string { 995 | // TODO: support other types. 996 | v := trimns(t) 997 | if _, exists := ge.stypes[v]; exists { 998 | return goSymbol(v) 999 | } 1000 | switch strings.ToLower(v) { 1001 | case "byte", "unsignedbyte": 1002 | return "byte" 1003 | case "int": 1004 | return "int" 1005 | case "integer": 1006 | return "int64" // todo: replace this with math/big since integer is infinite set 1007 | case "long": 1008 | return "int64" 1009 | case "float", "double", "decimal": 1010 | return "float64" 1011 | case "boolean": 1012 | return "bool" 1013 | case "hexbinary", "base64binary": 1014 | return "[]byte" 1015 | case "string", "anyuri", "token", "nmtoken", "qname", "language", "id": 1016 | return "string" 1017 | case "date": 1018 | ge.needsDateType = true 1019 | return "Date" 1020 | case "time": 1021 | ge.needsTimeType = true 1022 | return "Time" 1023 | case "nonnegativeinteger": 1024 | return "uint" 1025 | case "positiveinteger": 1026 | return "uint64" 1027 | case "normalizedstring": 1028 | return "string" 1029 | case "unsignedint": 1030 | return "uint" 1031 | case "datetime": 1032 | ge.needsDateTimeType = true 1033 | return "DateTime" 1034 | case "duration": 1035 | ge.needsDurationType = true 1036 | return "Duration" 1037 | case "anysequence", "anytype", "anysimpletype": 1038 | return "interface{}" 1039 | default: 1040 | return "*" + goSymbol(v) 1041 | } 1042 | } 1043 | 1044 | // Returns the default Go type for the given wsdl type. 1045 | func (ge *goEncoder) wsdl2goDefault(t string) string { 1046 | v := trimns(t) 1047 | if v != "" && v[0] == '*' { 1048 | v = v[1:] 1049 | } 1050 | switch v { 1051 | case "error": 1052 | return `errors.New("not implemented")` 1053 | case "bool": 1054 | return "false" 1055 | case "uint", "int", "int64", "float64": 1056 | return "0" 1057 | case "string": 1058 | return `""` 1059 | case "[]byte", "interface{}": 1060 | return "nil" 1061 | default: 1062 | return "&" + v + "{}" 1063 | } 1064 | } 1065 | 1066 | func (ge *goEncoder) renameType(old, name string) { 1067 | // TODO: rename Elements that point to this type also? 1068 | ct, exists := ge.ctypes[old] 1069 | if !exists { 1070 | old = trimns(old) 1071 | ct, exists = ge.ctypes[old] 1072 | if !exists { 1073 | return 1074 | } 1075 | name = trimns(name) 1076 | } 1077 | ct.Name = name 1078 | delete(ge.ctypes, old) 1079 | ge.ctypes[name] = ct 1080 | } 1081 | 1082 | // writeGoTypes writes Go types from WSDL types to w. 1083 | // 1084 | // Types are written in this order, alphabetically: date types that we 1085 | // generate, simple types, then complex types. 1086 | func (ge *goEncoder) writeGoTypes(w io.Writer, d *wsdl.Definitions) error { 1087 | var b bytes.Buffer 1088 | for _, name := range ge.sortedSimpleTypes() { 1089 | st := ge.stypes[name] 1090 | stname := goSymbol(st.Name) 1091 | if st.Restriction != nil { 1092 | ge.writeComments(&b, stname, "") 1093 | fmt.Fprintf(&b, "type %s %s\n\n", stname, ge.wsdl2goType(st.Restriction.Base)) 1094 | ge.genValidator(&b, stname, st.Restriction) 1095 | } else if st.Union != nil { 1096 | types := strings.Split(st.Union.MemberTypes, " ") 1097 | ntypes := make([]string, len(types)) 1098 | for i, t := range types { 1099 | t = strings.TrimSpace(t) 1100 | if t == "" { 1101 | continue 1102 | } 1103 | ntypes[i] = ge.wsdl2goType(t) 1104 | } 1105 | doc := stname + " is a union of: " + strings.Join(ntypes, ", ") 1106 | ge.writeComments(&b, stname, doc) 1107 | fmt.Fprintf(&b, "type %s interface{}\n\n", stname) 1108 | } 1109 | } 1110 | var err error 1111 | for _, name := range ge.sortedComplexTypes() { 1112 | ct := ge.ctypes[name] 1113 | err = ge.genGoStruct(&b, d, ct) 1114 | if err != nil { 1115 | return err 1116 | } 1117 | ge.genGoXMLTypeFunction(&b, ct) 1118 | } 1119 | 1120 | // Operation wrappers - mainly used for rpc, not exclusively 1121 | for _, name := range ge.sortedOperations() { 1122 | ct := ge.soapOps[name] 1123 | 1124 | err = ge.genGoOpStruct(&b, d, ct) 1125 | if err != nil { 1126 | return err 1127 | } 1128 | } 1129 | 1130 | ge.genDateTypes(w) // must be called last 1131 | _, err = io.Copy(w, &b) 1132 | return err 1133 | } 1134 | 1135 | func (ge *goEncoder) sortedSimpleTypes() []string { 1136 | keys := make([]string, len(ge.stypes)) 1137 | i := 0 1138 | for k := range ge.stypes { 1139 | keys[i] = k 1140 | i++ 1141 | } 1142 | sort.Strings(keys) 1143 | return keys 1144 | } 1145 | 1146 | func (ge *goEncoder) sortedComplexTypes() []string { 1147 | keys := make([]string, len(ge.ctypes)) 1148 | i := 0 1149 | for k := range ge.ctypes { 1150 | keys[i] = k 1151 | i++ 1152 | } 1153 | sort.Strings(keys) 1154 | return keys 1155 | } 1156 | 1157 | func (ge *goEncoder) sortedOperations() []string { 1158 | keys := make([]string, len(ge.soapOps)) 1159 | i := 0 1160 | for k := range ge.soapOps { 1161 | keys[i] = k 1162 | i++ 1163 | } 1164 | sort.Strings(keys) 1165 | return keys 1166 | } 1167 | 1168 | func (ge *goEncoder) genDateTypes(w io.Writer) { 1169 | cases := []struct { 1170 | needs bool 1171 | name string 1172 | code string 1173 | }{ 1174 | { 1175 | needs: ge.needsDateType, 1176 | name: "Date", 1177 | code: "type Date string\n\n", 1178 | }, 1179 | { 1180 | needs: ge.needsTimeType, 1181 | name: "Time", 1182 | code: "type Time string\n\n", 1183 | }, 1184 | { 1185 | needs: ge.needsDateTimeType, 1186 | name: "DateTime", 1187 | code: "type DateTime string\n\n", 1188 | }, 1189 | { 1190 | needs: ge.needsDurationType, 1191 | name: "Duration", 1192 | code: "type Duration string\n\n", 1193 | }, 1194 | } 1195 | for _, c := range cases { 1196 | if !c.needs { 1197 | continue 1198 | } 1199 | ge.writeComments(w, c.name, c.name+" in WSDL format.") 1200 | io.WriteString(w, c.code) 1201 | } 1202 | } 1203 | 1204 | var validatorT = template.Must(template.New("validator").Parse(` 1205 | // Validate validates {{.TypeName}}. 1206 | func (v {{.TypeName}}) Validate() bool { 1207 | for _, vv := range []{{.Type}} { 1208 | {{range .Args}}{{.}},{{"\n"}}{{end}} 1209 | }{ 1210 | if reflect.DeepEqual(v, vv) { 1211 | return true 1212 | } 1213 | } 1214 | return false 1215 | } 1216 | `)) 1217 | 1218 | func (ge *goEncoder) genValidator(w io.Writer, typeName string, r *wsdl.Restriction) { 1219 | if len(r.Enum) == 0 { 1220 | return 1221 | } 1222 | args := make([]string, len(r.Enum)) 1223 | t := ge.wsdl2goType(r.Base) 1224 | for i, v := range r.Enum { 1225 | if t == "string" { 1226 | args[i] = strconv.Quote(v.Value) 1227 | } else { 1228 | args[i] = v.Value 1229 | } 1230 | } 1231 | ge.needsStdPkg["reflect"] = true 1232 | validatorT.Execute(w, &struct { 1233 | TypeName string 1234 | Type string 1235 | Args []string 1236 | }{ 1237 | typeName, 1238 | t, 1239 | args, 1240 | }) 1241 | } 1242 | 1243 | func (ge *goEncoder) genGoXMLTypeFunction(w io.Writer, ct *wsdl.ComplexType) { 1244 | if ct.ComplexContent == nil || ct.ComplexContent.Extension == nil || ct.TargetNamespace == "" { 1245 | return 1246 | } 1247 | 1248 | ext := ct.ComplexContent.Extension 1249 | if ext.Base != "" && !ct.Abstract { 1250 | ge.writeComments(w, "SetXMLType", "") 1251 | fmt.Fprintf(w, "func (t *%s) SetXMLType() {\n", goSymbol(ct.Name)) 1252 | fmt.Fprintf(w, "if t.OverrideTypeAttrXSI != nil {\n") 1253 | fmt.Fprintf(w, " t.TypeAttrXSI = *t.OverrideTypeAttrXSI\n") 1254 | fmt.Fprintf(w, "} else {\n") 1255 | fmt.Fprintf(w, " t.TypeAttrXSI = \"objtype:%s\"\n", ct.Name) 1256 | fmt.Fprintf(w, "}\n") 1257 | fmt.Fprintf(w, "if t.OverrideTypeNamespace != nil {\n") 1258 | fmt.Fprintf(w, " t.TypeNamespace = *t.OverrideTypeNamespace\n") 1259 | fmt.Fprintf(w, "} else {\n") 1260 | fmt.Fprintf(w, " t.TypeNamespace = \"%s\"\n", ct.TargetNamespace) 1261 | fmt.Fprintf(w, "}\n}\n\n") 1262 | } 1263 | } 1264 | 1265 | // helper function to print out the XMLName 1266 | func (ge *goEncoder) genXMLName(w io.Writer, targetNamespace string, name string) { 1267 | if elName, ok := ge.needsTag[name]; ok { 1268 | if ge.localNamespace == "" { 1269 | fmt.Fprintf(w, "XMLName xml.Name `xml:\"%s %s\" json:\"-\" yaml:\"-\"`\n", 1270 | targetNamespace, elName) 1271 | } else { 1272 | fmt.Fprintf(w, "XMLName xml.Name `xml:\"%s:%s\" json:\"-\" yaml:\"-\"`\n", 1273 | ge.localNamespace, elName) 1274 | } 1275 | } 1276 | } 1277 | 1278 | var invalidGoSymbol = regexp.MustCompile(`[0-9_]*[^0-9a-zA-Z_]+`) 1279 | 1280 | func goSymbol(s string) string { 1281 | v := invalidGoSymbol.ReplaceAllString(trimns(s), " ") 1282 | var name string 1283 | for _, part := range strings.Split(v, " ") { 1284 | name += strings.Title(part) 1285 | } 1286 | return name 1287 | } 1288 | 1289 | func trimns(s string) string { 1290 | n := strings.SplitN(s, ":", 2) 1291 | if len(n) == 2 { 1292 | return n[1] 1293 | } 1294 | return s 1295 | } 1296 | 1297 | func (ge *goEncoder) genGoStruct(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error { 1298 | c := 0 1299 | if len(ct.AllElements) == 0 { 1300 | c++ 1301 | } 1302 | if ct.ComplexContent == nil || ct.ComplexContent.Extension == nil { 1303 | c++ 1304 | } 1305 | if ct.Sequence == nil && ct.Choice == nil { 1306 | c++ 1307 | } else if ct.Sequence != nil && 1308 | (len(ct.Sequence.ComplexTypes) == 0 && len(ct.Sequence.Elements) == 0 && len(ct.Sequence.Choices) == 0) { 1309 | c++ 1310 | } else if ct.Choice != nil && (len(ct.Choice.ComplexTypes) == 0 && len(ct.Choice.Elements) == 0) { 1311 | c++ 1312 | } 1313 | 1314 | name := goSymbol(ct.Name) 1315 | ge.writeComments(w, name, ct.Doc) 1316 | if ct.Abstract { 1317 | fmt.Fprintf(w, "type %s interface{}\n\n", name) 1318 | return nil 1319 | } 1320 | if ct.Sequence != nil && ct.Sequence.Any != nil { 1321 | if len(ct.Sequence.Elements) == 0 { 1322 | fmt.Fprintf(w, "type %s []interface{}\n\n", name) 1323 | return nil 1324 | } 1325 | } 1326 | if ct.Choice != nil && ct.Choice.Any != nil { 1327 | if len(ct.Choice.Elements) == 0 { 1328 | fmt.Fprintf(w, "type %s []interface{}\n\n", name) 1329 | return nil 1330 | } 1331 | } 1332 | if ct.ComplexContent != nil { 1333 | restr := ct.ComplexContent.Restriction 1334 | if restr != nil && len(restr.Attributes) == 1 && restr.Attributes[0].ArrayType != "" { 1335 | fmt.Fprintf(w, "type %s struct {\n", name) 1336 | typ := strings.SplitN(trimns(restr.Attributes[0].ArrayType), "[", 2)[0] 1337 | fmt.Fprintf(w, "Items []%s `xml:\"item,omitempty\" json:\"item,omitempty\" yaml:\"item,omitempty\"`\n", ge.wsdl2goType(typ)) 1338 | fmt.Fprintf(w, "}\n\n") 1339 | return nil 1340 | } 1341 | } 1342 | 1343 | if c > 2 && len(ct.Attributes) == 0 && ct.SimpleContent == nil { 1344 | fmt.Fprintf(w, "type %s struct {\n", name) 1345 | ge.genXMLName(w, d.TargetNamespace, name) 1346 | fmt.Fprintf(w, "}\n\n") 1347 | return nil 1348 | } 1349 | fmt.Fprintf(w, "type %s struct {\n", name) 1350 | ge.genXMLName(w, d.TargetNamespace, name) 1351 | 1352 | err := ge.genStructFields(w, d, ct) 1353 | 1354 | if ct.ComplexContent != nil && ct.ComplexContent.Extension != nil { 1355 | fmt.Fprint(w, "TypeAttrXSI string `xml:\"xsi:type,attr,omitempty\"`\n") 1356 | fmt.Fprint(w, "TypeNamespace string `xml:\"xmlns:objtype,attr,omitempty\"`\n") 1357 | fmt.Fprint(w, "\n") 1358 | fmt.Fprint(w, "OverrideTypeAttrXSI *string `xml:\"-\"`\n") 1359 | fmt.Fprint(w, "OverrideTypeNamespace *string `xml:\"-\"`\n") 1360 | } 1361 | if err != nil { 1362 | return err 1363 | } 1364 | fmt.Fprintf(w, "}\n\n") 1365 | return nil 1366 | } 1367 | 1368 | func (ge *goEncoder) genGoOpStruct(w io.Writer, d *wsdl.Definitions, bo *wsdl.BindingOperation) error { 1369 | name := goSymbol(bo.Name) 1370 | function := ge.funcs[name] 1371 | 1372 | if function.Input == nil { 1373 | log.Printf("function input is nil! %v is %v", name, function) 1374 | } else { 1375 | message := trimns(function.Input.Message) 1376 | inputMessage := ge.messages[message] 1377 | 1378 | // No-Op on operations which don't take arguments 1379 | // (These can be inlined, and don't need to pollute the file) 1380 | if len(inputMessage.Parts) > 0 { 1381 | ge.genOpStructMessage(w, d, name, inputMessage) 1382 | } 1383 | } 1384 | 1385 | if function.Output == nil { 1386 | log.Printf("function output is nil! %v is %v", name, function) 1387 | } else { 1388 | // Output messages are always required 1389 | ge.genOpStructMessage(w, d, name, ge.messages[trimns(ge.funcs[bo.Name].Output.Message)]) 1390 | } 1391 | 1392 | return nil 1393 | } 1394 | 1395 | func (ge *goEncoder) genStructFields(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error { 1396 | err := ge.genComplexContent(w, d, ct) 1397 | if err != nil { 1398 | return err 1399 | } 1400 | 1401 | err = ge.genSimpleContent(w, d, ct) 1402 | if err != nil { 1403 | return err 1404 | } 1405 | 1406 | return ge.genElements(w, ct) 1407 | } 1408 | 1409 | func (ge *goEncoder) genOpStructMessage(w io.Writer, d *wsdl.Definitions, name string, message *wsdl.Message) { 1410 | sanitizedMessageName := ge.sanitizedOperationsType(message.Name) 1411 | 1412 | ge.writeComments(w, sanitizedMessageName, "Operation wrapper for "+name+".") 1413 | ge.writeComments(w, sanitizedMessageName, "") 1414 | fmt.Fprintf(w, "type %s struct {\n", sanitizedMessageName) 1415 | if elName, ok := ge.needsTag[sanitizedMessageName]; ok { 1416 | fmt.Fprintf(w, "XMLName xml.Name `xml:\"%s %s\" json:\"-\" yaml:\"-\"`\n", 1417 | d.TargetNamespace, elName) 1418 | } 1419 | 1420 | for _, part := range message.Parts { 1421 | wsdlType := part.Type 1422 | 1423 | // Probably soap12 1424 | if wsdlType == "" { 1425 | wsdlType = part.Element 1426 | } 1427 | 1428 | partName := part.Name 1429 | if part.Element != "" { 1430 | elName := trimns(part.Element) 1431 | if el, ok := ge.elements[elName]; ok { 1432 | partName = trimns(el.Name) 1433 | } else if el, ok := ge.ctypes[elName]; ok { 1434 | partName = trimns(el.Name) 1435 | } else if el, ok := ge.stypes[elName]; ok { 1436 | partName = trimns(el.Name) 1437 | } 1438 | } 1439 | 1440 | ge.genElementField(w, &wsdl.Element{ 1441 | XMLName: part.XMLName, 1442 | Name: partName, 1443 | Type: wsdlType, 1444 | // TODO: Maybe one could make guesses about nillable? 1445 | }) 1446 | } 1447 | 1448 | fmt.Fprintf(w, "}\n\n") 1449 | } 1450 | 1451 | func (ge *goEncoder) genComplexContent(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error { 1452 | if ct.ComplexContent == nil || ct.ComplexContent.Extension == nil { 1453 | return nil 1454 | } 1455 | ext := ct.ComplexContent.Extension 1456 | if ext.Base != "" { 1457 | base, exists := ge.ctypes[trimns(ext.Base)] 1458 | if exists { 1459 | err := ge.genStructFields(w, d, base) 1460 | if err != nil { 1461 | return err 1462 | } 1463 | } 1464 | } 1465 | 1466 | for _, attr := range ext.Attributes { 1467 | ge.genAttributeField(w, attr) 1468 | } 1469 | 1470 | sequences := make([]*wsdl.Sequence, 0) 1471 | if ext.Sequence != nil { 1472 | sequences = append(sequences, ext.Sequence) 1473 | for _, choice := range ext.Sequence.Choices { 1474 | tmpSeq := &wsdl.Sequence{ 1475 | ComplexTypes: choice.ComplexTypes, 1476 | Elements: choice.Elements, 1477 | Any: choice.Any} 1478 | sequences = append(sequences, tmpSeq) 1479 | } 1480 | } 1481 | if ext.Choice != nil { 1482 | tmpSeq := &wsdl.Sequence{ 1483 | ComplexTypes: ext.Choice.ComplexTypes, 1484 | Elements: ext.Choice.Elements, 1485 | Any: ext.Choice.Any} 1486 | sequences = append(sequences, tmpSeq) 1487 | } 1488 | for _, seq := range sequences { 1489 | for _, v := range seq.ComplexTypes { 1490 | err := ge.genElements(w, v) 1491 | if err != nil { 1492 | return err 1493 | } 1494 | } 1495 | for _, v := range seq.Elements { 1496 | ge.genElementField(w, v) 1497 | } 1498 | 1499 | } 1500 | return nil 1501 | } 1502 | 1503 | func (ge *goEncoder) genSimpleContent(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error { 1504 | if ct.SimpleContent == nil || ct.SimpleContent.Extension == nil { 1505 | return nil 1506 | } 1507 | 1508 | ext := ct.SimpleContent.Extension 1509 | if ext.Base != "" { 1510 | baseComplex, exists := ge.ctypes[trimns(ext.Base)] 1511 | if exists { 1512 | err := ge.genStructFields(w, d, baseComplex) 1513 | if err != nil { 1514 | return err 1515 | } 1516 | } else { 1517 | // otherwise it's a simple type 1518 | ge.genElementField(w, &wsdl.Element{ 1519 | Type: trimns(ext.Base), 1520 | Name: "Content", 1521 | }) 1522 | } 1523 | } 1524 | 1525 | for _, attr := range ext.Attributes { 1526 | ge.genAttributeField(w, attr) 1527 | } 1528 | 1529 | // sequence, choice, etc. are not supported in simpleContent tags. 1530 | return nil 1531 | } 1532 | 1533 | func (ge *goEncoder) genElements(w io.Writer, ct *wsdl.ComplexType) error { 1534 | for _, el := range ct.AllElements { 1535 | ge.genElementField(w, el) 1536 | } 1537 | if ct.Sequence != nil { 1538 | for _, el := range ct.Sequence.Elements { 1539 | ge.genElementField(w, el) 1540 | } 1541 | for _, choice := range ct.Sequence.Choices { 1542 | for _, el := range choice.Elements { 1543 | ge.genElementField(w, el) 1544 | } 1545 | } 1546 | } 1547 | if ct.Choice != nil { 1548 | for _, el := range ct.Choice.Elements { 1549 | ge.genElementField(w, el) 1550 | } 1551 | } 1552 | for _, attr := range ct.Attributes { 1553 | ge.genAttributeField(w, attr) 1554 | } 1555 | return nil 1556 | } 1557 | 1558 | func (ge *goEncoder) genElementField(w io.Writer, el *wsdl.Element) { 1559 | if el.Ref != "" { 1560 | ref := trimns(el.Ref) 1561 | nel, ok := ge.elements[ref] 1562 | if !ok { 1563 | return 1564 | } 1565 | el = nel 1566 | } 1567 | var slicetype string 1568 | if el.Type == "" && el.ComplexType != nil { 1569 | seq := el.ComplexType.Sequence 1570 | if seq == nil && el.ComplexType.Choice != nil { 1571 | seq = &wsdl.Sequence{ 1572 | ComplexTypes: el.ComplexType.Choice.ComplexTypes, 1573 | Elements: el.ComplexType.Choice.Elements, 1574 | Any: el.ComplexType.Choice.Any} 1575 | } 1576 | if seq != nil { 1577 | if len(seq.Elements) == 1 { 1578 | n := el.Name 1579 | seqel := seq.Elements[0] 1580 | el = new(wsdl.Element) 1581 | *el = *seqel 1582 | slicetype = seqel.Name 1583 | el.Name = n 1584 | } else if len(seq.Any) == 1 { 1585 | el = &wsdl.Element{ 1586 | Name: el.Name, 1587 | Type: "anysequence", 1588 | Min: seq.Any[0].Min, 1589 | Max: seq.Any[0].Max, 1590 | } 1591 | slicetype = el.Name 1592 | } 1593 | } 1594 | } 1595 | et := el.Type 1596 | if et == "" { 1597 | et = "string" 1598 | } 1599 | tag := el.Name 1600 | fmt.Fprintf(w, "%s ", goSymbol(el.Name)) 1601 | if el.Max != "" && el.Max != "1" { 1602 | fmt.Fprintf(w, "[]") 1603 | if slicetype != "" { 1604 | tag = el.Name + ">" + slicetype 1605 | } 1606 | } 1607 | typ := ge.wsdl2goType(et) 1608 | if el.Nillable || el.Min == 0 { 1609 | tag += ",omitempty" 1610 | //since we add omitempty tag, we should add pointer to type. 1611 | //thus xmlencoder can differ not-initialized fields from zero-initialized values 1612 | if !strings.HasPrefix(typ, "*") { 1613 | typ = "*" + typ 1614 | } 1615 | } 1616 | fmt.Fprintf(w, "%s `xml:\"%s\" json:\"%s\" yaml:\"%s\"`\n", 1617 | typ, tag, tag, tag) 1618 | } 1619 | 1620 | func (ge *goEncoder) genAttributeField(w io.Writer, attr *wsdl.Attribute) { 1621 | if attr.Name == "" && attr.Ref != "" { 1622 | attr.Name = trimns(attr.Ref) 1623 | } 1624 | if attr.Type == "" { 1625 | attr.Type = "string" 1626 | } 1627 | 1628 | tag := fmt.Sprintf("%s,attr", attr.Name) 1629 | fmt.Fprintf(w, "%s ", goSymbol(attr.Name)) 1630 | typ := ge.wsdl2goType(attr.Type) 1631 | if attr.Nillable || attr.Min == 0 { 1632 | tag += ",omitempty" 1633 | } 1634 | fmt.Fprintf(w, "%s `xml:\"%s\" json:\"%s\" yaml:\"%s\"`\n", 1635 | typ, tag, tag, tag) 1636 | } 1637 | 1638 | // writeComments writes comments to w, capped at ~80 columns. 1639 | func (ge *goEncoder) writeComments(w io.Writer, typeName, comment string) { 1640 | comment = strings.Trim(strings.Replace(comment, "\n", " ", -1), " ") 1641 | if comment == "" { 1642 | comment = goSymbol(typeName) + " was auto-generated from WSDL." 1643 | } 1644 | count, line := 0, "" 1645 | words := strings.Split(comment, " ") 1646 | for _, word := range words { 1647 | if line == "" { 1648 | count, line = 2, "//" 1649 | } 1650 | 1651 | count += len(word) 1652 | if count > 60 { 1653 | fmt.Fprintf(w, "%s %s\n", line, word) 1654 | count, line = 0, "" 1655 | continue 1656 | } 1657 | line = line + " " + word 1658 | count++ 1659 | } 1660 | if line != "" { 1661 | fmt.Fprintf(w, "%s\n", line) 1662 | } 1663 | return 1664 | } 1665 | 1666 | // SetLocalNamespace allows overridding of namespace in XMLName 1667 | func (ge *goEncoder) SetLocalNamespace(s string) { 1668 | ge.localNamespace = s 1669 | } 1670 | -------------------------------------------------------------------------------- /wsdlgo/encoder_test.go: -------------------------------------------------------------------------------- 1 | package wsdlgo 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "net" 9 | "net/http" 10 | "net/http/httptest" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "strings" 15 | "testing" 16 | 17 | "github.com/fiorix/wsdl2go/wsdl" 18 | ) 19 | 20 | func LoadDefinition(t *testing.T, filename string, want error) *wsdl.Definitions { 21 | f, err := os.Open(filepath.Join("testdata", filename)) 22 | if err != nil { 23 | t.Errorf("missing wsdl file %q: %v", filename, err) 24 | } 25 | defer f.Close() 26 | 27 | // replace CURRENT_DIR in wsdl with the current working directory 28 | // - for testing file: schema 29 | ba, _ := ioutil.ReadAll(f) 30 | pwd, _ := os.Getwd() 31 | s := strings.Replace(string(ba), "CURRENT_DIR", pwd, 1) 32 | 33 | d, err := wsdl.Unmarshal(strings.NewReader(s)) 34 | if err != want { 35 | t.Errorf("%q failed: want %v, have %v", filename, want, err) 36 | } 37 | return d 38 | } 39 | 40 | var EncoderCases = []struct { 41 | F string 42 | G string 43 | E error 44 | }{ 45 | {F: "broken.wsdl", E: io.EOF}, 46 | {F: "w3cexample1.wsdl", G: "w3cexample1.golden", E: nil}, 47 | {F: "w3cexample2.wsdl", G: "w3cexample2.golden", E: nil}, 48 | {F: "w3example1.wsdl", G: "w3example1.golden", E: nil}, 49 | {F: "w3example2.wsdl", G: "w3example2.golden", E: nil}, 50 | {F: "soap12wcf.wsdl", G: "soap12wcf.golden", E: nil}, 51 | {F: "memcache.wsdl", G: "memcache.golden", E: nil}, 52 | {F: "importer.wsdl", G: "memcache.golden", E: nil}, 53 | {F: "data.wsdl", G: "data.golden", E: nil}, 54 | {F: "data_withkeyword.wsdl", G: "data_withkeyword.golden", E: nil}, 55 | {F: "localimport.wsdl", G: "localimport.golden", E: nil}, 56 | {F: "localimport-url.wsdl", G: "localimport.golden", E: nil}, 57 | {F: "localimport_choice.wsdl", G: "localimport_choice.golden", E: nil}, 58 | {F: "arrayexample.wsdl", G: "arrayexample.golden", E: nil}, 59 | } 60 | 61 | func NewTestServer(t *testing.T) *httptest.Server { 62 | mux := http.NewServeMux() 63 | fs := http.FileServer(http.Dir("testdata")) 64 | mux.Handle("/", fs) 65 | s := httptest.NewUnstartedServer(mux) 66 | l, err := net.Listen("tcp4", ":9999") 67 | if err != nil { 68 | t.Fatalf("cannot listen on 9999: %v", err) 69 | } 70 | s.Listener = l 71 | s.Start() 72 | return s 73 | } 74 | 75 | func TestEncoder(t *testing.T) { 76 | s := NewTestServer(t) 77 | defer s.Close() 78 | for i, tc := range EncoderCases { 79 | d := LoadDefinition(t, tc.F, tc.E) 80 | var err error 81 | var want []byte 82 | var have bytes.Buffer 83 | err = NewEncoder(&have).Encode(d) 84 | if err != nil { 85 | t.Errorf("test %d, encoding %q: %v", i, tc.F, err) 86 | } 87 | if tc.G == "" { 88 | continue 89 | } 90 | want, err = ioutil.ReadFile(filepath.Join("testdata", tc.G)) 91 | if err != nil { 92 | t.Errorf("test %d: missing golden file %q: %v", i, tc.G, err) 93 | } 94 | if !bytes.Equal(have.Bytes(), want) { 95 | err := Diff("_diff", "go", want, have.Bytes()) 96 | t.Errorf("test %d, %q != %q: %v\ngenerated:\n%s\n", 97 | i, tc.F, tc.G, err, have.Bytes()) 98 | } 99 | } 100 | } 101 | 102 | func Diff(prefix, ext string, a, b []byte) error { 103 | diff, err := exec.LookPath("diff") 104 | if err != nil { 105 | return fmt.Errorf("diff: %v", err) 106 | } 107 | cases := []struct { 108 | File string 109 | Data []byte 110 | }{ 111 | {File: prefix + "-a." + ext, Data: a}, 112 | {File: prefix + "-b." + ext, Data: b}, 113 | } 114 | for _, c := range cases { 115 | defer os.Remove(c.File) 116 | if err = ioutil.WriteFile(c.File, c.Data, 0600); err != nil { 117 | return err 118 | } 119 | } 120 | var stdout, stderr bytes.Buffer 121 | cmd := exec.Cmd{ 122 | Path: diff, 123 | Args: []string{"-u", cases[0].File, cases[1].File}, 124 | Stdout: &stdout, 125 | Stderr: &stderr, 126 | } 127 | err = cmd.Run() 128 | if err != nil { 129 | return fmt.Errorf("%v: %s", err, stdout.String()) 130 | } 131 | return nil 132 | } 133 | -------------------------------------------------------------------------------- /wsdlgo/package.go: -------------------------------------------------------------------------------- 1 | package wsdlgo 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/fiorix/wsdl2go/wsdl" 7 | ) 8 | 9 | const ( 10 | fallbackPackageName = "internal" 11 | ) 12 | 13 | // BindingPackageName formats package name from wsdl binding 14 | type BindingPackageName wsdl.Binding 15 | 16 | func (p BindingPackageName) String() string { 17 | packageName := strings.Replace(strings.ToLower(p.Name), ".", "", -1) 18 | if packageName == "" { 19 | packageName = fallbackPackageName 20 | } 21 | return packageName 22 | } 23 | 24 | // PackageName is just a string with interface 25 | type PackageName string 26 | 27 | func (p PackageName) String() string { 28 | return string(p) 29 | } 30 | -------------------------------------------------------------------------------- /wsdlgo/package_test.go: -------------------------------------------------------------------------------- 1 | package wsdlgo 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/fiorix/wsdl2go/wsdl" 7 | ) 8 | 9 | func TestBindingPackageName_String(t *testing.T) { 10 | tests := []struct { 11 | expected string 12 | binding wsdl.Binding 13 | }{ 14 | {"foo", wsdl.Binding{Name: "foo"}}, 15 | {"dataendpointsoap11binding", wsdl.Binding{Name: "DataEndpointSoap11Binding"}}, 16 | {"somedottedbindingname", wsdl.Binding{Name: "Some.Dotted.Binding.Name"}}, 17 | } 18 | 19 | for _, test := range tests { 20 | namer := BindingPackageName(test.binding) 21 | name := namer.String() 22 | if test.expected != name { 23 | t.Errorf("expected `%s`, actual `%s`", test.expected, name) 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wsdlgo/testdata/arrayexample.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package stockquotesoapbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://example.com/stockquote.wsdl" 11 | 12 | // NewStockQuotePortType creates an initializes a StockQuotePortType. 13 | func NewStockQuotePortType(cli *soap.Client) StockQuotePortType { 14 | return &stockQuotePortType{cli} 15 | } 16 | 17 | // StockQuotePortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type StockQuotePortType interface { 20 | // GetTradePrices was auto-generated from WSDL. 21 | GetTradePrices(String string) (*ArrayOfFloat, error) 22 | } 23 | 24 | // ArrayOfFloat was auto-generated from WSDL. 25 | type ArrayOfFloat struct { 26 | Items []float64 `xml:"item,omitempty" json:"item,omitempty" yaml:"item,omitempty"` 27 | } 28 | 29 | // Operation wrapper for GetTradePrices. 30 | // OperationGetTradePricesInput was auto-generated from WSDL. 31 | type OperationGetTradePricesInput struct { 32 | TickerSymbol *string `xml:"tickerSymbol,omitempty" json:"tickerSymbol,omitempty" yaml:"tickerSymbol,omitempty"` 33 | } 34 | 35 | // Operation wrapper for GetTradePrices. 36 | // OperationGetTradePricesOutput was auto-generated from WSDL. 37 | type OperationGetTradePricesOutput struct { 38 | Result *ArrayOfFloat `xml:"result,omitempty" json:"result,omitempty" yaml:"result,omitempty"` 39 | } 40 | 41 | // stockQuotePortType implements the StockQuotePortType interface. 42 | type stockQuotePortType struct { 43 | cli *soap.Client 44 | } 45 | 46 | // GetTradePrices was auto-generated from WSDL. 47 | func (p *stockQuotePortType) GetTradePrices(String string) (*ArrayOfFloat, error) { 48 | α := struct { 49 | M OperationGetTradePricesInput `xml:"tns:GetTradePrices"` 50 | }{ 51 | OperationGetTradePricesInput{ 52 | &String, 53 | }, 54 | } 55 | 56 | γ := struct { 57 | M OperationGetTradePricesOutput `xml:"GetTradePricesResponse"` 58 | }{} 59 | if err := p.cli.RoundTripWithAction("http://example.com/GetTradePrices", α, &γ); err != nil { 60 | return nil, err 61 | } 62 | return γ.M.Result, nil 63 | } 64 | -------------------------------------------------------------------------------- /wsdlgo/testdata/arrayexample.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 9 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | My first service 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /wsdlgo/testdata/broken.wsdl: -------------------------------------------------------------------------------- 1 | fail-me 2 | -------------------------------------------------------------------------------- /wsdlgo/testdata/data.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package dataendpointhttpbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://pdf.host.com" 11 | 12 | // NewDataEndpointPortType creates an initializes a DataEndpointPortType. 13 | func NewDataEndpointPortType(cli *soap.Client) DataEndpointPortType { 14 | return &dataEndpointPortType{cli} 15 | } 16 | 17 | // DataEndpointPortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type DataEndpointPortType interface { 20 | // GetData was auto-generated from WSDL. 21 | GetData(GetData *GetData) (*GetDataResp, error) 22 | } 23 | 24 | // BaseReq was auto-generated from WSDL. 25 | type BaseReq struct { 26 | ClientIdentification *ClientIdentification `xml:"clientIdentification,omitempty" json:"clientIdentification,omitempty" yaml:"clientIdentification,omitempty"` 27 | TestAttr string `xml:"TestAttr,attr,omitempty" json:"TestAttr,attr,omitempty" yaml:"TestAttr,attr,omitempty"` 28 | } 29 | 30 | // BaseResp was auto-generated from WSDL. 31 | type BaseResp struct { 32 | ErrorDetails *ErrorDetails `xml:"errorDetails,omitempty" json:"errorDetails,omitempty" yaml:"errorDetails,omitempty"` 33 | Success *bool `xml:"success,omitempty" json:"success,omitempty" yaml:"success,omitempty"` 34 | } 35 | 36 | // DataGenerationReq was auto-generated from WSDL. 37 | type DataGenerationReq struct { 38 | ClientIdentification *ClientIdentification `xml:"clientIdentification,omitempty" json:"clientIdentification,omitempty" yaml:"clientIdentification,omitempty"` 39 | TestAttr string `xml:"TestAttr,attr,omitempty" json:"TestAttr,attr,omitempty" yaml:"TestAttr,attr,omitempty"` 40 | CustomerAccountNumber *string `xml:"customerAccountNumber,omitempty" json:"customerAccountNumber,omitempty" yaml:"customerAccountNumber,omitempty"` 41 | PdfGenerationReqType *int `xml:"pdfGenerationReqType,omitempty" json:"pdfGenerationReqType,omitempty" yaml:"pdfGenerationReqType,omitempty"` 42 | WithCreditTranferForm *bool `xml:"withCreditTranferForm,omitempty" json:"withCreditTranferForm,omitempty" yaml:"withCreditTranferForm,omitempty"` 43 | TypeAttrXSI string `xml:"xsi:type,attr,omitempty"` 44 | TypeNamespace string `xml:"xmlns:objtype,attr,omitempty"` 45 | 46 | OverrideTypeAttrXSI *string `xml:"-"` 47 | OverrideTypeNamespace *string `xml:"-"` 48 | } 49 | 50 | // SetXMLType was auto-generated from WSDL. 51 | func (t *DataGenerationReq) SetXMLType() { 52 | if t.OverrideTypeAttrXSI != nil { 53 | t.TypeAttrXSI = *t.OverrideTypeAttrXSI 54 | } else { 55 | t.TypeAttrXSI = "objtype:DataGenerationReq" 56 | } 57 | if t.OverrideTypeNamespace != nil { 58 | t.TypeNamespace = *t.OverrideTypeNamespace 59 | } else { 60 | t.TypeNamespace = "http://pdf.host.com/xsd" 61 | } 62 | } 63 | 64 | // DataGenerationResp was auto-generated from WSDL. 65 | type DataGenerationResp struct { 66 | ErrorDetails *ErrorDetails `xml:"errorDetails,omitempty" json:"errorDetails,omitempty" yaml:"errorDetails,omitempty"` 67 | Success *bool `xml:"success,omitempty" json:"success,omitempty" yaml:"success,omitempty"` 68 | Pdf *[]byte `xml:"pdf,omitempty" json:"pdf,omitempty" yaml:"pdf,omitempty"` 69 | Url *string `xml:"url,omitempty" json:"url,omitempty" yaml:"url,omitempty"` 70 | TypeAttrXSI string `xml:"xsi:type,attr,omitempty"` 71 | TypeNamespace string `xml:"xmlns:objtype,attr,omitempty"` 72 | 73 | OverrideTypeAttrXSI *string `xml:"-"` 74 | OverrideTypeNamespace *string `xml:"-"` 75 | } 76 | 77 | // SetXMLType was auto-generated from WSDL. 78 | func (t *DataGenerationResp) SetXMLType() { 79 | if t.OverrideTypeAttrXSI != nil { 80 | t.TypeAttrXSI = *t.OverrideTypeAttrXSI 81 | } else { 82 | t.TypeAttrXSI = "objtype:DataGenerationResp" 83 | } 84 | if t.OverrideTypeNamespace != nil { 85 | t.TypeNamespace = *t.OverrideTypeNamespace 86 | } else { 87 | t.TypeNamespace = "http://pdf.host.com/xsd" 88 | } 89 | } 90 | 91 | // GetData was auto-generated from WSDL. 92 | type GetData struct { 93 | Request *DataGenerationReq `xml:"request,omitempty" json:"request,omitempty" yaml:"request,omitempty"` 94 | } 95 | 96 | // GetDataResp was auto-generated from WSDL. 97 | type GetDataResp struct { 98 | Return *DataGenerationResp `xml:"return,omitempty" json:"return,omitempty" yaml:"return,omitempty"` 99 | } 100 | 101 | // Operation wrapper for GetData. 102 | // OperationGetDataReq was auto-generated from WSDL. 103 | type OperationGetDataReq struct { 104 | GetData *GetData `xml:"getData,omitempty" json:"getData,omitempty" yaml:"getData,omitempty"` 105 | } 106 | 107 | // Operation wrapper for GetData. 108 | // OperationGetDataResp was auto-generated from WSDL. 109 | type OperationGetDataResp struct { 110 | GetDataResp *GetDataResp `xml:"getDataResp,omitempty" json:"getDataResp,omitempty" yaml:"getDataResp,omitempty"` 111 | } 112 | 113 | // dataEndpointPortType implements the DataEndpointPortType interface. 114 | type dataEndpointPortType struct { 115 | cli *soap.Client 116 | } 117 | 118 | // GetData was auto-generated from WSDL. 119 | func (p *dataEndpointPortType) GetData(GetData *GetData) (*GetDataResp, error) { 120 | α := struct { 121 | OperationGetDataReq `xml:"ns:getData"` 122 | }{ 123 | OperationGetDataReq{ 124 | GetData, 125 | }, 126 | } 127 | 128 | γ := struct { 129 | OperationGetDataResp `xml:"getDataResponse"` 130 | }{} 131 | if err := p.cli.RoundTripWithAction("GetData", α, &γ); err != nil { 132 | return nil, err 133 | } 134 | return γ.GetDataResp, nil 135 | } 136 | -------------------------------------------------------------------------------- /wsdlgo/testdata/data.wsdl: -------------------------------------------------------------------------------- 1 | 2 | DataEndpoint 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /wsdlgo/testdata/data_withkeyword.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package dataendpointhttpbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://pdf.host.com" 11 | 12 | // NewDataEndpointPortType creates an initializes a DataEndpointPortType. 13 | func NewDataEndpointPortType(cli *soap.Client) DataEndpointPortType { 14 | return &dataEndpointPortType{cli} 15 | } 16 | 17 | // DataEndpointPortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type DataEndpointPortType interface { 20 | // GetData was auto-generated from WSDL. 21 | GetData(GetData *GetData) (*GetDataResp, error) 22 | } 23 | 24 | // BaseReq was auto-generated from WSDL. 25 | type BaseReq struct { 26 | ClientIdentification *ClientIdentification `xml:"clientIdentification,omitempty" json:"clientIdentification,omitempty" yaml:"clientIdentification,omitempty"` 27 | TestAttr string `xml:"TestAttr,attr,omitempty" json:"TestAttr,attr,omitempty" yaml:"TestAttr,attr,omitempty"` 28 | } 29 | 30 | // BaseResp was auto-generated from WSDL. 31 | type BaseResp struct { 32 | ErrorDetails *ErrorDetails `xml:"errorDetails,omitempty" json:"errorDetails,omitempty" yaml:"errorDetails,omitempty"` 33 | Success *bool `xml:"success,omitempty" json:"success,omitempty" yaml:"success,omitempty"` 34 | } 35 | 36 | // DataGenerationReq was auto-generated from WSDL. 37 | type DataGenerationReq struct { 38 | ClientIdentification *ClientIdentification `xml:"clientIdentification,omitempty" json:"clientIdentification,omitempty" yaml:"clientIdentification,omitempty"` 39 | TestAttr string `xml:"TestAttr,attr,omitempty" json:"TestAttr,attr,omitempty" yaml:"TestAttr,attr,omitempty"` 40 | CustomerAccountNumber *string `xml:"customerAccountNumber,omitempty" json:"customerAccountNumber,omitempty" yaml:"customerAccountNumber,omitempty"` 41 | PdfGenerationReqType *int `xml:"pdfGenerationReqType,omitempty" json:"pdfGenerationReqType,omitempty" yaml:"pdfGenerationReqType,omitempty"` 42 | WithCreditTranferForm *bool `xml:"withCreditTranferForm,omitempty" json:"withCreditTranferForm,omitempty" yaml:"withCreditTranferForm,omitempty"` 43 | TypeAttrXSI string `xml:"xsi:type,attr,omitempty"` 44 | TypeNamespace string `xml:"xmlns:objtype,attr,omitempty"` 45 | 46 | OverrideTypeAttrXSI *string `xml:"-"` 47 | OverrideTypeNamespace *string `xml:"-"` 48 | } 49 | 50 | // SetXMLType was auto-generated from WSDL. 51 | func (t *DataGenerationReq) SetXMLType() { 52 | if t.OverrideTypeAttrXSI != nil { 53 | t.TypeAttrXSI = *t.OverrideTypeAttrXSI 54 | } else { 55 | t.TypeAttrXSI = "objtype:DataGenerationReq" 56 | } 57 | if t.OverrideTypeNamespace != nil { 58 | t.TypeNamespace = *t.OverrideTypeNamespace 59 | } else { 60 | t.TypeNamespace = "http://pdf.host.com/xsd" 61 | } 62 | } 63 | 64 | // DataGenerationResp was auto-generated from WSDL. 65 | type DataGenerationResp struct { 66 | ErrorDetails *ErrorDetails `xml:"errorDetails,omitempty" json:"errorDetails,omitempty" yaml:"errorDetails,omitempty"` 67 | Success *bool `xml:"success,omitempty" json:"success,omitempty" yaml:"success,omitempty"` 68 | Pdf *[]byte `xml:"pdf,omitempty" json:"pdf,omitempty" yaml:"pdf,omitempty"` 69 | Url *string `xml:"url,omitempty" json:"url,omitempty" yaml:"url,omitempty"` 70 | TypeAttrXSI string `xml:"xsi:type,attr,omitempty"` 71 | TypeNamespace string `xml:"xmlns:objtype,attr,omitempty"` 72 | 73 | OverrideTypeAttrXSI *string `xml:"-"` 74 | OverrideTypeNamespace *string `xml:"-"` 75 | } 76 | 77 | // SetXMLType was auto-generated from WSDL. 78 | func (t *DataGenerationResp) SetXMLType() { 79 | if t.OverrideTypeAttrXSI != nil { 80 | t.TypeAttrXSI = *t.OverrideTypeAttrXSI 81 | } else { 82 | t.TypeAttrXSI = "objtype:DataGenerationResp" 83 | } 84 | if t.OverrideTypeNamespace != nil { 85 | t.TypeNamespace = *t.OverrideTypeNamespace 86 | } else { 87 | t.TypeNamespace = "http://pdf.host.com/xsd" 88 | } 89 | } 90 | 91 | // GetData was auto-generated from WSDL. 92 | type GetData struct { 93 | Request *DataGenerationReq `xml:"request,omitempty" json:"request,omitempty" yaml:"request,omitempty"` 94 | } 95 | 96 | // GetDataResp was auto-generated from WSDL. 97 | type GetDataResp struct { 98 | Return *DataGenerationResp `xml:"return,omitempty" json:"return,omitempty" yaml:"return,omitempty"` 99 | } 100 | 101 | // Operation wrapper for GetData. 102 | // OperationGetDataReq was auto-generated from WSDL. 103 | type OperationGetDataReq struct { 104 | GetData *GetData `xml:"getData,omitempty" json:"getData,omitempty" yaml:"getData,omitempty"` 105 | } 106 | 107 | // Operation wrapper for GetData. 108 | // OperationGetDataResp was auto-generated from WSDL. 109 | type OperationGetDataResp struct { 110 | GetDataResp *GetDataResp `xml:"getDataResp,omitempty" json:"getDataResp,omitempty" yaml:"getDataResp,omitempty"` 111 | } 112 | 113 | // dataEndpointPortType implements the DataEndpointPortType interface. 114 | type dataEndpointPortType struct { 115 | cli *soap.Client 116 | } 117 | 118 | // GetData was auto-generated from WSDL. 119 | func (p *dataEndpointPortType) GetData(GetData *GetData) (*GetDataResp, error) { 120 | α := struct { 121 | OperationGetDataReq `xml:"ns:getData"` 122 | }{ 123 | OperationGetDataReq{ 124 | GetData, 125 | }, 126 | } 127 | 128 | γ := struct { 129 | OperationGetDataResp `xml:"getDataResponse"` 130 | }{} 131 | if err := p.cli.RoundTripWithAction("GetData", α, &γ); err != nil { 132 | return nil, err 133 | } 134 | return γ.GetDataResp, nil 135 | } 136 | -------------------------------------------------------------------------------- /wsdlgo/testdata/data_withkeyword.wsdl: -------------------------------------------------------------------------------- 1 | 2 | DataEndpoint 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /wsdlgo/testdata/importer-root.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /wsdlgo/testdata/importer-schema.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SetRequest carries a key-value pair. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | GetResponse carries value and TTL. 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /wsdlgo/testdata/importer.wsdl: -------------------------------------------------------------------------------- 1 | 6 | xmlns:tns="http://localhost:8080/MemoryService.wsdl" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | WSDL File for HelloService 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport-url.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | My first service 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package stockquotesoapbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://example.com/stockquote.wsdl" 11 | 12 | // NewStockQuotePortType creates an initializes a StockQuotePortType. 13 | func NewStockQuotePortType(cli *soap.Client) StockQuotePortType { 14 | return &stockQuotePortType{cli} 15 | } 16 | 17 | // StockQuotePortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type StockQuotePortType interface { 20 | // GetLastTradePrice was auto-generated from WSDL. 21 | GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) 22 | } 23 | 24 | // TradePrice was auto-generated from WSDL. 25 | type TradePrice struct { 26 | Price *float64 `xml:"price,omitempty" json:"price,omitempty" yaml:"price,omitempty"` 27 | } 28 | 29 | // TradePriceRequest was auto-generated from WSDL. 30 | type TradePriceRequest struct { 31 | TickerSymbol *string `xml:"tickerSymbol,omitempty" json:"tickerSymbol,omitempty" yaml:"tickerSymbol,omitempty"` 32 | } 33 | 34 | // Operation wrapper for GetLastTradePrice. 35 | // OperationGetLastTradePriceInput was auto-generated from WSDL. 36 | type OperationGetLastTradePriceInput struct { 37 | TradePriceRequest *TradePriceRequest `xml:"TradePriceRequest,omitempty" json:"TradePriceRequest,omitempty" yaml:"TradePriceRequest,omitempty"` 38 | } 39 | 40 | // Operation wrapper for GetLastTradePrice. 41 | // OperationGetLastTradePriceOutput was auto-generated from WSDL. 42 | type OperationGetLastTradePriceOutput struct { 43 | TradePrice *TradePrice `xml:"TradePrice,omitempty" json:"TradePrice,omitempty" yaml:"TradePrice,omitempty"` 44 | } 45 | 46 | // stockQuotePortType implements the StockQuotePortType interface. 47 | type stockQuotePortType struct { 48 | cli *soap.Client 49 | } 50 | 51 | // GetLastTradePrice was auto-generated from WSDL. 52 | func (p *stockQuotePortType) GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) { 53 | α := struct { 54 | OperationGetLastTradePriceInput `xml:"tns:GetLastTradePrice"` 55 | }{ 56 | OperationGetLastTradePriceInput{ 57 | TradePriceRequest, 58 | }, 59 | } 60 | 61 | γ := struct { 62 | OperationGetLastTradePriceOutput `xml:"GetLastTradePriceResponse"` 63 | }{} 64 | if err := p.cli.RoundTripWithAction("http://example.com/GetLastTradePrice", α, &γ); err != nil { 65 | return nil, err 66 | } 67 | return γ.TradePrice, nil 68 | } 69 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | My first service 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport_choice.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package stockquotesoapbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://example.com/stockquote.wsdl" 11 | 12 | // NewStockQuotePortType creates an initializes a StockQuotePortType. 13 | func NewStockQuotePortType(cli *soap.Client) StockQuotePortType { 14 | return &stockQuotePortType{cli} 15 | } 16 | 17 | // StockQuotePortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type StockQuotePortType interface { 20 | // GetLastTradePrice was auto-generated from WSDL. 21 | GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) 22 | } 23 | 24 | // TradePrice was auto-generated from WSDL. 25 | type TradePrice struct { 26 | Price *float64 `xml:"price,omitempty" json:"price,omitempty" yaml:"price,omitempty"` 27 | Discountprice *float64 `xml:"discountprice,omitempty" json:"discountprice,omitempty" yaml:"discountprice,omitempty"` 28 | } 29 | 30 | // TradePriceRequest was auto-generated from WSDL. 31 | type TradePriceRequest struct { 32 | TickerSymbol *string `xml:"tickerSymbol,omitempty" json:"tickerSymbol,omitempty" yaml:"tickerSymbol,omitempty"` 33 | } 34 | 35 | // Operation wrapper for GetLastTradePrice. 36 | // OperationGetLastTradePriceInput was auto-generated from WSDL. 37 | type OperationGetLastTradePriceInput struct { 38 | Body *TradePriceRequest `xml:"body,omitempty" json:"body,omitempty" yaml:"body,omitempty"` 39 | } 40 | 41 | // Operation wrapper for GetLastTradePrice. 42 | // OperationGetLastTradePriceOutput was auto-generated from WSDL. 43 | type OperationGetLastTradePriceOutput struct { 44 | TradePrice *TradePrice `xml:"TradePrice,omitempty" json:"TradePrice,omitempty" yaml:"TradePrice,omitempty"` 45 | } 46 | 47 | // stockQuotePortType implements the StockQuotePortType interface. 48 | type stockQuotePortType struct { 49 | cli *soap.Client 50 | } 51 | 52 | // GetLastTradePrice was auto-generated from WSDL. 53 | func (p *stockQuotePortType) GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) { 54 | α := struct { 55 | OperationGetLastTradePriceInput `xml:"tns:GetLastTradePrice"` 56 | }{ 57 | OperationGetLastTradePriceInput{ 58 | TradePriceRequest, 59 | }, 60 | } 61 | 62 | γ := struct { 63 | OperationGetLastTradePriceOutput `xml:"GetLastTradePriceResponse"` 64 | }{} 65 | if err := p.cli.RoundTripWithAction("http://example.com/GetLastTradePrice", α, &γ); err != nil { 66 | return nil, err 67 | } 68 | return γ.TradePrice, nil 69 | } 70 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport_choice.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | My first service 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /wsdlgo/testdata/localimport_choice.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /wsdlgo/testdata/memcache.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package memoryservice 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://localhost:8080/MemoryService.wsdl" 11 | 12 | // NewMemoryServicePortType creates an initializes a MemoryServicePortType. 13 | func NewMemoryServicePortType(cli *soap.Client) MemoryServicePortType { 14 | return &memoryServicePortType{cli} 15 | } 16 | 17 | // MemoryServicePortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type MemoryServicePortType interface { 20 | // Get was auto-generated from WSDL. 21 | Get(key string) (*GetResponse, error) 22 | 23 | // GetMulti was auto-generated from WSDL. 24 | GetMulti(keys *GetMultiRequest) (*GetMultiResponse, error) 25 | 26 | // Set was auto-generated from WSDL. 27 | Set(info *SetRequest) (bool, error) 28 | } 29 | 30 | // Duration in WSDL format. 31 | type Duration string 32 | 33 | // GetMultiResponse was auto-generated from WSDL. 34 | type GetMultiResponse struct { 35 | Values []*GetResponse `xml:"Values,omitempty" json:"Values,omitempty" yaml:"Values,omitempty"` 36 | } 37 | 38 | // GetResponse carries value and TTL. 39 | type GetResponse struct { 40 | Value *string `xml:"Value,omitempty" json:"Value,omitempty" yaml:"Value,omitempty"` 41 | TTL *Duration `xml:"TTL,omitempty" json:"TTL,omitempty" yaml:"TTL,omitempty"` 42 | } 43 | 44 | // SetRequest carries a key-value pair. 45 | type SetRequest struct { 46 | Key string `xml:"Key" json:"Key" yaml:"Key"` 47 | Value string `xml:"Value" json:"Value" yaml:"Value"` 48 | Expiration *Duration `xml:"Expiration,omitempty" json:"Expiration,omitempty" yaml:"Expiration,omitempty"` 49 | } 50 | 51 | // GetMultiRequest was auto-generated from WSDL. 52 | type GetMultiRequest struct { 53 | Keys []string `xml:"Keys" json:"Keys" yaml:"Keys"` 54 | } 55 | 56 | // Operation wrapper for Get. 57 | // OperationGetRequest was auto-generated from WSDL. 58 | type OperationGetRequest struct { 59 | Key *string `xml:"key,omitempty" json:"key,omitempty" yaml:"key,omitempty"` 60 | } 61 | 62 | // Operation wrapper for Get. 63 | // OperationGetResponse was auto-generated from WSDL. 64 | type OperationGetResponse struct { 65 | Resp *GetResponse `xml:"resp,omitempty" json:"resp,omitempty" yaml:"resp,omitempty"` 66 | } 67 | 68 | // Operation wrapper for GetMulti. 69 | // OperationGetMultiRequest was auto-generated from WSDL. 70 | type OperationGetMultiRequest struct { 71 | Keys *GetMultiRequest `xml:"keys,omitempty" json:"keys,omitempty" yaml:"keys,omitempty"` 72 | } 73 | 74 | // Operation wrapper for GetMulti. 75 | // OperationGetMultiResponse was auto-generated from WSDL. 76 | type OperationGetMultiResponse struct { 77 | Values *GetMultiResponse `xml:"values,omitempty" json:"values,omitempty" yaml:"values,omitempty"` 78 | } 79 | 80 | // Operation wrapper for Set. 81 | // OperationSetRequest was auto-generated from WSDL. 82 | type OperationSetRequest struct { 83 | Info *SetRequest `xml:"info,omitempty" json:"info,omitempty" yaml:"info,omitempty"` 84 | } 85 | 86 | // Operation wrapper for Set. 87 | // OperationSetResponse was auto-generated from WSDL. 88 | type OperationSetResponse struct { 89 | Ok *bool `xml:"ok,omitempty" json:"ok,omitempty" yaml:"ok,omitempty"` 90 | } 91 | 92 | // memoryServicePortType implements the MemoryServicePortType interface. 93 | type memoryServicePortType struct { 94 | cli *soap.Client 95 | } 96 | 97 | // Get was auto-generated from WSDL. 98 | func (p *memoryServicePortType) Get(key string) (*GetResponse, error) { 99 | α := struct { 100 | M OperationGetRequest `xml:"tns:Get"` 101 | }{ 102 | OperationGetRequest{ 103 | &key, 104 | }, 105 | } 106 | 107 | γ := struct { 108 | M OperationGetResponse `xml:"GetResponse"` 109 | }{} 110 | if err := p.cli.RoundTripWithAction("Get", α, &γ); err != nil { 111 | return nil, err 112 | } 113 | return γ.M.Resp, nil 114 | } 115 | 116 | // GetMulti was auto-generated from WSDL. 117 | func (p *memoryServicePortType) GetMulti(keys *GetMultiRequest) (*GetMultiResponse, error) { 118 | α := struct { 119 | M OperationGetMultiRequest `xml:"tns:GetMulti"` 120 | }{ 121 | OperationGetMultiRequest{ 122 | keys, 123 | }, 124 | } 125 | 126 | γ := struct { 127 | M OperationGetMultiResponse `xml:"GetMultiResponse"` 128 | }{} 129 | if err := p.cli.RoundTripWithAction("GetMulti", α, &γ); err != nil { 130 | return nil, err 131 | } 132 | return γ.M.Values, nil 133 | } 134 | 135 | // Set was auto-generated from WSDL. 136 | func (p *memoryServicePortType) Set(info *SetRequest) (bool, error) { 137 | α := struct { 138 | M OperationSetRequest `xml:"tns:Set"` 139 | }{ 140 | OperationSetRequest{ 141 | info, 142 | }, 143 | } 144 | 145 | γ := struct { 146 | M OperationSetResponse `xml:"SetResponse"` 147 | }{} 148 | if err := p.cli.RoundTripWithAction("Set", α, &γ); err != nil { 149 | return false, err 150 | } 151 | return *γ.M.Ok, nil 152 | } 153 | -------------------------------------------------------------------------------- /wsdlgo/testdata/memcache.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | xmlns:tns="http://localhost:8080/MemoryService.wsdl" 9 | 10 | 11 | 12 | 13 | 14 | 15 | SetRequest carries a key-value pair. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | GetResponse carries value and TTL. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 97 | 98 | 99 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 113 | 114 | 115 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 129 | 130 | 131 | 135 | 136 | 137 | 138 | 139 | 140 | WSDL File for HelloService 141 | 142 | 143 | 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /wsdlgo/testdata/soap12wcf.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package testsoap12binding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://foo.bar.com/HelloWorld/1.0" 11 | 12 | // NewTest creates an initializes a Test. 13 | func NewTest(cli *soap.Client) Test { 14 | return &test{cli} 15 | } 16 | 17 | // Test was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type Test interface { 20 | // HelloWorld was auto-generated from WSDL. 21 | HelloWorld(HelloRequest string) (string, error) 22 | } 23 | 24 | // Operation wrapper for HelloWorld. 25 | // OperationHelloWorldMessageIn was auto-generated from WSDL. 26 | type OperationHelloWorldMessageIn struct { 27 | HelloRequest *HelloRequest `xml:"HelloRequest,omitempty" json:"HelloRequest,omitempty" yaml:"HelloRequest,omitempty"` 28 | } 29 | 30 | // Operation wrapper for HelloWorld. 31 | // OperationHelloWorldMessageOut was auto-generated from WSDL. 32 | type OperationHelloWorldMessageOut struct { 33 | HelloResponse *HelloResponse `xml:"HelloResponse,omitempty" json:"HelloResponse,omitempty" yaml:"HelloResponse,omitempty"` 34 | } 35 | 36 | // test implements the Test interface. 37 | type test struct { 38 | cli *soap.Client 39 | } 40 | 41 | // HelloWorld was auto-generated from WSDL. 42 | func (p *test) HelloWorld(HelloRequest string) (string, error) { 43 | α := struct { 44 | OperationHelloWorldMessageIn `xml:"tns:HelloWorld"` 45 | }{ 46 | OperationHelloWorldMessageIn{ 47 | &HelloRequest, 48 | }, 49 | } 50 | 51 | γ := struct { 52 | OperationHelloWorldMessageOut `xml:"HelloWorldResponse"` 53 | }{} 54 | if err := p.cli.RoundTripSoap12("http://example.com/Test/HelloWorldRequest", α, &γ); err != nil { 55 | return "", err 56 | } 57 | return *γ.HelloResponse, nil 58 | } 59 | -------------------------------------------------------------------------------- /wsdlgo/testdata/soap12wcf.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | HelloWorld Service 1.0 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /wsdlgo/testdata/tpexample1.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package hello_binding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://www.examples.com/wsdl/HelloService.wsdl" 11 | 12 | // NewHello_PortType creates an initializes a Hello_PortType. 13 | func NewHello_PortType(cli *soap.Client) Hello_PortType { 14 | return &hello_PortType{cli} 15 | } 16 | 17 | // Hello_PortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type Hello_PortType interface { 20 | // SayHello was auto-generated from WSDL. 21 | SayHello(firstName string) (string, error) 22 | } 23 | 24 | // Operation wrapper for SayHello. 25 | // OperationSayHelloRequest was auto-generated from WSDL. 26 | type OperationSayHelloRequest struct { 27 | FirstName *string `xml:"firstName,omitempty" json:"firstName,omitempty" yaml:"firstName,omitempty"` 28 | } 29 | 30 | // Operation wrapper for SayHello. 31 | // OperationSayHelloResponse was auto-generated from WSDL. 32 | type OperationSayHelloResponse struct { 33 | Greeting *string `xml:"greeting,omitempty" json:"greeting,omitempty" yaml:"greeting,omitempty"` 34 | } 35 | 36 | // hello_PortType implements the Hello_PortType interface. 37 | type hello_PortType struct { 38 | cli *soap.Client 39 | } 40 | 41 | // SayHello was auto-generated from WSDL. 42 | func (p *hello_PortType) SayHello(firstName string) (string, error) { 43 | α := struct { 44 | M OperationSayHelloRequest `xml:"tns:sayHello"` 45 | }{ 46 | OperationSayHelloRequest{ 47 | &firstName, 48 | }, 49 | } 50 | 51 | γ := struct { 52 | M OperationSayHelloResponse `xml:"sayHelloResponse"` 53 | }{} 54 | if err := p.cli.RoundTripWithAction("sayHello", α, &γ); err != nil { 55 | return "", err 56 | } 57 | return *γ.M.Greeting, nil 58 | } 59 | -------------------------------------------------------------------------------- /wsdlgo/testdata/tpexample1.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 | 47 | WSDL File for HelloService 48 | 49 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3cexample1.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | ) 9 | 10 | // GetNothing was auto-generated from WSDL. 11 | func GetNothing(ctx context.Context, something string) error { 12 | return errors.New("not implemented") 13 | } 14 | 15 | // GetTerm was auto-generated from WSDL. 16 | func GetTerm(ctx context.Context, term string) (string, error) { 17 | return "", errors.New("not implemented") 18 | } 19 | 20 | // GetVoid was auto-generated from WSDL. 21 | func GetVoid(ctx context.Context, something string) error { 22 | return errors.New("not implemented") 23 | } 24 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3cexample1.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3cexample2.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | "context" 7 | "errors" 8 | ) 9 | 10 | // DoNothing was auto-generated from WSDL. 11 | func DoNothing(ctx context.Context) error { 12 | return errors.New("not implemented") 13 | } 14 | 15 | // SetNothing was auto-generated from WSDL. 16 | func SetNothing(ctx context.Context) error { 17 | return errors.New("not implemented") 18 | } 19 | 20 | // SetTerm was auto-generated from WSDL. 21 | func SetTerm(ctx context.Context, term string, value string) error { 22 | return errors.New("not implemented") 23 | } 24 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3cexample2.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3example1.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package endorsementsearchsoapbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://namespaces.snowboard-info.com" 11 | 12 | // NewGetEndorsingBoarderPortType creates an initializes a GetEndorsingBoarderPortType. 13 | func NewGetEndorsingBoarderPortType(cli *soap.Client) GetEndorsingBoarderPortType { 14 | return &getEndorsingBoarderPortType{cli} 15 | } 16 | 17 | // GetEndorsingBoarderPortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type GetEndorsingBoarderPortType interface { 20 | // GetEndorsingBoarder was auto-generated from WSDL. 21 | GetEndorsingBoarder(GetEndorsingBoarder *GetEndorsingBoarder) (*GetEndorsingBoarderResponse, error) 22 | } 23 | 24 | // GetEndorsingBoarder was auto-generated from WSDL. 25 | type GetEndorsingBoarder struct { 26 | Manufacturer *string `xml:"manufacturer,omitempty" json:"manufacturer,omitempty" yaml:"manufacturer,omitempty"` 27 | Model *string `xml:"model,omitempty" json:"model,omitempty" yaml:"model,omitempty"` 28 | } 29 | 30 | // GetEndorsingBoarderFault was auto-generated from WSDL. 31 | type GetEndorsingBoarderFault struct { 32 | ErrorMessage *string `xml:"errorMessage,omitempty" json:"errorMessage,omitempty" yaml:"errorMessage,omitempty"` 33 | } 34 | 35 | // GetEndorsingBoarderResponse was auto-generated from WSDL. 36 | type GetEndorsingBoarderResponse struct { 37 | EndorsingBoarder *string `xml:"endorsingBoarder,omitempty" json:"endorsingBoarder,omitempty" yaml:"endorsingBoarder,omitempty"` 38 | } 39 | 40 | // Operation wrapper for GetEndorsingBoarder. 41 | // OperationGetEndorsingBoarderRequest was auto-generated from 42 | // WSDL. 43 | type OperationGetEndorsingBoarderRequest struct { 44 | GetEndorsingBoarder *GetEndorsingBoarder `xml:"GetEndorsingBoarder,omitempty" json:"GetEndorsingBoarder,omitempty" yaml:"GetEndorsingBoarder,omitempty"` 45 | } 46 | 47 | // Operation wrapper for GetEndorsingBoarder. 48 | // OperationGetEndorsingBoarderResponse was auto-generated from 49 | // WSDL. 50 | type OperationGetEndorsingBoarderResponse struct { 51 | GetEndorsingBoarderResponse *GetEndorsingBoarderResponse `xml:"GetEndorsingBoarderResponse,omitempty" json:"GetEndorsingBoarderResponse,omitempty" yaml:"GetEndorsingBoarderResponse,omitempty"` 52 | } 53 | 54 | // getEndorsingBoarderPortType implements the GetEndorsingBoarderPortType interface. 55 | type getEndorsingBoarderPortType struct { 56 | cli *soap.Client 57 | } 58 | 59 | // GetEndorsingBoarder was auto-generated from WSDL. 60 | func (p *getEndorsingBoarderPortType) GetEndorsingBoarder(GetEndorsingBoarder *GetEndorsingBoarder) (*GetEndorsingBoarderResponse, error) { 61 | α := struct { 62 | OperationGetEndorsingBoarderRequest `xml:"es:GetEndorsingBoarder"` 63 | }{ 64 | OperationGetEndorsingBoarderRequest{ 65 | GetEndorsingBoarder, 66 | }, 67 | } 68 | 69 | γ := struct { 70 | OperationGetEndorsingBoarderResponse `xml:"GetEndorsingBoarderResponse"` 71 | }{} 72 | if err := p.cli.RoundTripWithAction("http://www.snowboard-info.com/EndorsementSearch", α, &γ); err != nil { 73 | return nil, err 74 | } 75 | return γ.GetEndorsingBoarderResponse, nil 76 | } 77 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3example1.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 92 | 93 | 94 | 96 | 97 | 98 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | snowboarding-info.com Endorsement Service 107 | 108 | 109 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3example2.golden: -------------------------------------------------------------------------------- 1 | // Code generated by wsdl2go. DO NOT EDIT. 2 | 3 | package stockquotesoapbinding 4 | 5 | import ( 6 | "github.com/fiorix/wsdl2go/soap" 7 | ) 8 | 9 | // Namespace was auto-generated from WSDL. 10 | var Namespace = "http://example.com/stockquote.wsdl" 11 | 12 | // NewStockQuotePortType creates an initializes a StockQuotePortType. 13 | func NewStockQuotePortType(cli *soap.Client) StockQuotePortType { 14 | return &stockQuotePortType{cli} 15 | } 16 | 17 | // StockQuotePortType was auto-generated from WSDL 18 | // and defines interface for the remote service. Useful for testing. 19 | type StockQuotePortType interface { 20 | // DestroySession was auto-generated from WSDL. 21 | DestroySession(DestroySessionRequest *DestroySessionRequest) (*DestroySessionResponse, error) 22 | 23 | // GetLastTradePrice was auto-generated from WSDL. 24 | GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) 25 | 26 | // GetSession was auto-generated from WSDL. 27 | GetSession(GetSessionRequest *GetSessionRequest) (*GetSessionResponse, error) 28 | } 29 | 30 | // DestroySessionRequest was auto-generated from WSDL. 31 | type DestroySessionRequest struct { 32 | SessionId *string `xml:"sessionId,omitempty" json:"sessionId,omitempty" yaml:"sessionId,omitempty"` 33 | } 34 | 35 | // DestroySessionResponse was auto-generated from WSDL. 36 | type DestroySessionResponse struct { 37 | } 38 | 39 | // GetSessionRequest was auto-generated from WSDL. 40 | type GetSessionRequest struct { 41 | } 42 | 43 | // GetSessionResponse was auto-generated from WSDL. 44 | type GetSessionResponse struct { 45 | SessionId *string `xml:"sessionId,omitempty" json:"sessionId,omitempty" yaml:"sessionId,omitempty"` 46 | } 47 | 48 | // TradePrice was auto-generated from WSDL. 49 | type TradePrice struct { 50 | Price *float64 `xml:"price,omitempty" json:"price,omitempty" yaml:"price,omitempty"` 51 | } 52 | 53 | // TradePriceRequest was auto-generated from WSDL. 54 | type TradePriceRequest struct { 55 | TickerSymbol *string `xml:"tickerSymbol,omitempty" json:"tickerSymbol,omitempty" yaml:"tickerSymbol,omitempty"` 56 | } 57 | 58 | // Operation wrapper for DestroySession. 59 | // OperationDestroySessionInput was auto-generated from WSDL. 60 | type OperationDestroySessionInput struct { 61 | DestroySessionRequest *DestroySessionRequest `xml:"DestroySessionRequest,omitempty" json:"DestroySessionRequest,omitempty" yaml:"DestroySessionRequest,omitempty"` 62 | } 63 | 64 | // Operation wrapper for DestroySession. 65 | // OperationDestroySessionOutput was auto-generated from WSDL. 66 | type OperationDestroySessionOutput struct { 67 | DestroySessionResponse *DestroySessionResponse `xml:"DestroySessionResponse,omitempty" json:"DestroySessionResponse,omitempty" yaml:"DestroySessionResponse,omitempty"` 68 | } 69 | 70 | // Operation wrapper for GetLastTradePrice. 71 | // OperationGetLastTradePriceInput was auto-generated from WSDL. 72 | type OperationGetLastTradePriceInput struct { 73 | TradePriceRequest *TradePriceRequest `xml:"TradePriceRequest,omitempty" json:"TradePriceRequest,omitempty" yaml:"TradePriceRequest,omitempty"` 74 | } 75 | 76 | // Operation wrapper for GetLastTradePrice. 77 | // OperationGetLastTradePriceOutput was auto-generated from WSDL. 78 | type OperationGetLastTradePriceOutput struct { 79 | TradePrice *TradePrice `xml:"TradePrice,omitempty" json:"TradePrice,omitempty" yaml:"TradePrice,omitempty"` 80 | } 81 | 82 | // Operation wrapper for GetSession. 83 | // OperationGetSessionInput was auto-generated from WSDL. 84 | type OperationGetSessionInput struct { 85 | GetSessionRequest *GetSessionRequest `xml:"GetSessionRequest,omitempty" json:"GetSessionRequest,omitempty" yaml:"GetSessionRequest,omitempty"` 86 | } 87 | 88 | // Operation wrapper for GetSession. 89 | // OperationGetSessionOutput was auto-generated from WSDL. 90 | type OperationGetSessionOutput struct { 91 | GetSessionResponse *GetSessionResponse `xml:"GetSessionResponse,omitempty" json:"GetSessionResponse,omitempty" yaml:"GetSessionResponse,omitempty"` 92 | } 93 | 94 | // stockQuotePortType implements the StockQuotePortType interface. 95 | type stockQuotePortType struct { 96 | cli *soap.Client 97 | } 98 | 99 | // DestroySession was auto-generated from WSDL. 100 | func (p *stockQuotePortType) DestroySession(DestroySessionRequest *DestroySessionRequest) (*DestroySessionResponse, error) { 101 | α := struct { 102 | OperationDestroySessionInput `xml:"tns:DestroySession"` 103 | }{ 104 | OperationDestroySessionInput{ 105 | DestroySessionRequest, 106 | }, 107 | } 108 | 109 | γ := struct { 110 | OperationDestroySessionOutput `xml:"DestroySessionResponse"` 111 | }{} 112 | if err := p.cli.RoundTripWithAction("http://example.com/DestroySession", α, &γ); err != nil { 113 | return nil, err 114 | } 115 | return γ.DestroySessionResponse, nil 116 | } 117 | 118 | // GetLastTradePrice was auto-generated from WSDL. 119 | func (p *stockQuotePortType) GetLastTradePrice(TradePriceRequest *TradePriceRequest) (*TradePrice, error) { 120 | α := struct { 121 | OperationGetLastTradePriceInput `xml:"tns:GetLastTradePrice"` 122 | }{ 123 | OperationGetLastTradePriceInput{ 124 | TradePriceRequest, 125 | }, 126 | } 127 | 128 | γ := struct { 129 | OperationGetLastTradePriceOutput `xml:"GetLastTradePriceResponse"` 130 | }{} 131 | if err := p.cli.RoundTripWithAction("http://example.com/GetLastTradePrice", α, &γ); err != nil { 132 | return nil, err 133 | } 134 | return γ.TradePrice, nil 135 | } 136 | 137 | // GetSession was auto-generated from WSDL. 138 | func (p *stockQuotePortType) GetSession(GetSessionRequest *GetSessionRequest) (*GetSessionResponse, error) { 139 | α := struct { 140 | OperationGetSessionInput `xml:"tns:GetSession"` 141 | }{ 142 | OperationGetSessionInput{ 143 | GetSessionRequest, 144 | }, 145 | } 146 | 147 | γ := struct { 148 | OperationGetSessionOutput `xml:"GetSessionResponse"` 149 | }{} 150 | if err := p.cli.RoundTripWithAction("http://example.com/GetSession", α, &γ); err != nil { 151 | return nil, err 152 | } 153 | return γ.GetSessionResponse, nil 154 | } 155 | -------------------------------------------------------------------------------- /wsdlgo/testdata/w3example2.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | My first service 163 | 164 | 165 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | --------------------------------------------------------------------------------