Skip to content

Commit

Permalink
Preliminary SOAP support
Browse files Browse the repository at this point in the history
  • Loading branch information
fiorix committed Mar 21, 2016
1 parent e196ca9 commit 5d80156
Show file tree
Hide file tree
Showing 16 changed files with 498 additions and 100 deletions.
11 changes: 11 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This is the official list of wsdl2go authors for copyright purposes.
# This file is distinct from the CONTRIBUTORS file.
#
# Names should be added to this file as
# Name or Organization <email address>
#
# The email address is not required for organizations.
#
# Please keep the list sorted.

Alexandre Fiori <[email protected]>
27 changes: 27 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright 2016 wsdl2go authors. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* The names of authors or contributors may NOT be used to endorse or
promote products derived from this software without specific prior
written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
64 changes: 50 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,65 @@
wsdl2go is a command line tool to generate [Go](https://golang.org) code
from [WSDL](https://en.wikipedia.org/wiki/Web_Services_Description_Language).

Usage:
Download:

```
go get github.com/fiorix/wsdl2go
```

### Usage

Make sure you have gofmt under $GOROOT/bin, otherwise it'll fail.

```
wsdl2go < file.wsdl > hello.go
```

Use -i for remote URLs.
Use -i for files or URLs, and -o to specify an output file. WSDL
files that contain import tags are only processed after these
resources are downloaded. It tries automatically but might fail
due to authentication or bad SSL certificates. You can force it
anyway. YOLO.

Here's how to use the generated code: Let's say you generate the
Go code for the hello service, which provides an Echo method that
takes an EchoRequest and returns an EchoReply. To use it, you have
to create a SOAP client, then call the generated function Echo.

Once the code is generated, wsdl2go invokes gofmt from $GOROOT/bin/gofmt
and will fail have if you don't have it installed.
Example:

```
import (
"/path/to/hello"
"github.com/fiorix/wsdl2go/soap"
)
func main() {
cli := &soap.Client{
URL: "http://server",
Namespace: hello.Namespace,
}
req := &hello.EchoRequest{Data: "echo"}
reply, err := hello.Echo(cli, req)
...
}
```

Only the **Document** style of SOAP is supported. If you're looking
for the RPC one, take another bite of your taco and move on. Soz.

### Status

Not fully compliant with SOAP or WSDL. Works for my needs and has been
tested with a few SOAP enterprise systems.
Works for my needs, been tested with a few SOAP enterprise systems.
Not fully compliant to WSDL or SOAP specs.

There are limitations related to XML namespaces in Go, which might impact
how this program works. Details: https://github.com/golang/go/issues/14407.
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.

WSDL types supported:
Types supported:

- [x] int
- [x] long (int64)
Expand All @@ -40,10 +79,7 @@ WSDL types supported:
Date types are currently defined as strings, need to implement XML
Marshaler and Unmarshaler interfaces.

The Go code generator (package wsdlgo) is capable of importing remote
parts of the WSDL via HTTP. You can configure its http.Client to support
authentication and self-signed certificates.

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.
to compare values. This and the entire API might change anytime,
be warned.
101 changes: 101 additions & 0 deletions soap/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Package soap provides a SOAP HTTP client.
package soap

import (
"bytes"
"encoding/xml"
"io/ioutil"
"log"
"net/http"
)

// A RoundTripper executes a request passing the given req as the SOAP
// envelope body. The HTTP response is then de-serialized onto the resp
// object. Returns error in case an error occurs serializing req, making
// the HTTP request, or de-serializing the response.
type RoundTripper interface {
RoundTrip(req, resp Message) error
}

// Message is an opaque type used by the RoundTripper to carry XML
// documents for SOAP.
type Message interface{}

// Header is an opaque type used as the SOAP Header element in requests.
type Header interface{}

// AuthHeader is a Header to be encoded as the SOAP Header element in
// requests, to convey credentials for authentication.
type AuthHeader struct {
Namespace string `xml:"xmlns:ns,attr"`
Username string `xml:"ns:username"`
Password string `xml:"ns:password"`
}

// Client is a SOAP client.
type Client struct {
URL string // URL of the server
Namespace string // SOAP Namespace
Envelope string // Optional SOAP Envelope
Header Header // Optional SOAP Header
ContentType string // Optional Content-Type (default text/xml)
Config *http.Client // Optional HTTP client
}

// RoundTrip implements the RoundTripper interface.
func (c *Client) RoundTrip(in, out Message) error {
req := &Envelope{
EnvelopeAttr: c.Envelope,
NSAttr: c.Namespace,
Header: c.Header,
Body: Body{Message: in},
}
if req.EnvelopeAttr == "" {
req.EnvelopeAttr = "http://schemas.xmlsoap.org/soap/envelope/"
}
if req.NSAttr == "" {
req.NSAttr = c.URL
}
var b bytes.Buffer
err := xml.NewEncoder(&b).Encode(req)
if err != nil {
return err
}
ct := c.ContentType
if ct == "" {
ct = "text/xml"
}
cli := c.Config
if cli == nil {
cli = http.DefaultClient
}
resp, err := cli.Post(c.URL, ct, &b)
if err != nil {
return err
}
defer resp.Body.Close()
if false {
// to be removed
z, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
log.Printf("%s", z)
}
return xml.NewDecoder(resp.Body).Decode(out)
}

// Envelope is a SOAP envelope.
type Envelope struct {
XMLName xml.Name `xml:"SOAP-ENV:Envelope"`
EnvelopeAttr string `xml:"xmlns:SOAP-ENV,attr"`
NSAttr string `xml:"xmlns:ns,attr"`
Header Message `xml:"SOAP-ENV:Header"`
Body Body
}

// Body is the body of a SOAP envelope.
type Body struct {
XMLName xml.Name `xml:"SOAP-ENV:Body"`
Message Message
}
6 changes: 3 additions & 3 deletions wsdl/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ func TestUnmarshal(t *testing.T) {
for i, tc := range cases {
f, err := os.Open(filepath.Join("testdata", tc.F))
if err != nil {
t.Errorf("test %d failed: %v", i, err)
t.Errorf("test %d (%q) failed: %v", i, tc.F, err)
}
defer f.Close()
_, err = Unmarshal(f)
if tc.E == nil {
if err != nil {
t.Errorf("test %d failed: want %v, have %v", i, tc.E, err)
t.Errorf("test %d (%q) failed: want %v, have %v", i, tc.F, tc.E, err)
}
continue
}
want := reflect.ValueOf(tc.E).Type().Name()
have := reflect.ValueOf(err).Type().Name()
if want != have {
t.Errorf("test %d failed: want %q, have %q", i, want, have)
t.Errorf("test %d (%q) failed: want %q, have %q", i, tc.F, want, have)
}
}
}
32 changes: 27 additions & 5 deletions wsdl/testdata/golden1.wsdl
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
<definitions>
<definitions name="Test"
targetNamespace="http://localhost:9999"
xmlns:tns="http://localhost:9999"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="http://localhost:9999"
xmlns:SOAP="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:MIME="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:DIME="http://schemas.xmlsoap.org/ws/2002/04/dime/wsdl/"
xmlns:WSDL="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

<schema targetNamespace="http://localhost:9999"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ns="http://localhost:9999"
xmlns="http://www.w3.org/2001/XMLSchema">

</schema>

<message name="getTermRequest">
<part name="term" type="xs:string"/>
<part name="term" type="xsd:string"/>
</message>

<message name="getTermResponse">
<part name="value" type="xs:string"/>
<part name="value" type="xsd:string"/>
</message>

<portType name="glossaryTerms">
<operation name="getTerm">
<input message="getTermRequest"/>
<output message="getTermResponse"/>
<input message="tns:getTermRequest"/>
<output message="tns:getTermResponse"/>
</operation>
</portType>

Expand Down
15 changes: 7 additions & 8 deletions wsdl/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ type Definitions struct {
XMLName xml.Name `xml:"definitions"`
Name string `xml:"name,attr"`
TargetNamespace string `xml:"targetNamespace,attr"`
SOAPEnv string `xml:"SOAP-ENV,attr"`
SOAPEnc string `xml:"SOAP-ENC,attr"`
Service Service `xml:"service"`
Imports []*Import `xml:"import"`
Schema Schema `xml:"types>schema"`
Expand Down Expand Up @@ -39,14 +41,11 @@ type Address struct {

// Schema of WSDL document.
type Schema struct {
XMLName xml.Name `xml:"schema"`
TargetNamespace string `xml:"targetNamespace,attr"`
ElementFormDefault string `xml:"elementFormDefault,attr"`
AttributeFormDefault string `xml:"attributeFormDefault,attr"`
Imports []*ImportSchema `xml:"import"`
SimpleTypes []*SimpleType `xml:"simpleType"`
ComplexTypes []*ComplexType `xml:"complexType"`
Elements []*Element `xml:"element"`
XMLName xml.Name `xml:"schema"`
Imports []*ImportSchema `xml:"import"`
SimpleTypes []*SimpleType `xml:"simpleType"`
ComplexTypes []*ComplexType `xml:"complexType"`
Elements []*Element `xml:"element"`
}

// SimpleType describes a simple type, such as string.
Expand Down
Loading

0 comments on commit 5d80156

Please sign in to comment.