Documentation
¶
Overview ¶
Package soap is what the API gateway knows about SOAP, in one place: the WSDL import that turns a service description into the operation model the gateway already serves, and the envelope and fault handling one of those operations needs on the wire.
A WSDL describes the same three things an OpenAPI document does — a set of named operations, the shape each one takes, and the address they are sent to — in a different vocabulary. Parse reads the document into that shared shape; OpenAPI renders it as a document the gateway's existing loader, discovery, persona route rules and metrics read without modification. The SOAP-specific facts a caller cannot derive from the rendered operation (the envelope version, the action header, the body element and its namespace) travel with it as the x-soap extension, which is what lets the invoke path build an envelope from a plain object.
The subset is document/literal, SOAP 1.1 and 1.2, which is the overwhelming majority of what is still deployed. RPC and encoded bindings are refused by name rather than imported wrongly: their body element is assembled from the operation and part names rather than declared by a schema, so an importer that treated them as document/literal would emit a request shape that does not exist.
References inside the document are resolved by the local part of their QName. The decoder resolves every element name through the document's namespace declarations and then drops the declarations (internal/xmltree), so a prefix in an attribute VALUE — which is what `message`, `type` and `element` carry — has nothing left to resolve against. Within one WSDL that costs nothing, because every such reference names a definition in the same document; a reference that does not resolve locally is an error naming what was missing, never a silent mismatch.
Index ¶
Constants ¶
const ( Version11 = "1.1" Version12 = "1.2" // MediaType11 is the Content-Type a SOAP 1.1 request carries. SOAP // 1.2 replaced it with its own type, and an upstream of either version // rejects the other's. MediaType11 = "text/xml" // MediaType12 is the SOAP 1.2 Content-Type. MediaType12 = "application/soap+xml" )
SOAP versions the importer emits, as they appear in x-soap.version and in the media type each one requires.
const ExtensionKey = "x-soap"
ExtensionKey is the OpenAPI extension the importer writes onto every operation it renders, and the key the gateway reads to recognize one.
It carries the four facts a SOAP call needs that a rendered REST operation has nowhere to put: which envelope version to write, which action header the binding requires, which address the operation is actually sent to, and which element the body carries in each direction.
Variables ¶
var ErrNotAnObject = errors.New("soap: a SOAP operation's body must be an object of the operation's fields, or a string containing the whole envelope")
ErrNotAnObject is returned when a caller sends a body the encoder cannot place into an envelope. A caller holding XML already has a way through — a string body is sent verbatim — so the refusal names it.
var ErrNotWSDL = errors.New("soap: document root is not a WSDL 1.1 <definitions> element")
ErrNotWSDL is returned when the document's root is not a WSDL 1.1 definitions element. It is separate from a parse failure so the admin handler can tell an operator who pasted an OpenAPI document into a wsdl spec entry apart from one who pasted a broken WSDL.
Functions ¶
func Encode ¶
Encode builds the request document for one SOAP operation from a body the caller sent as an object.
The caller supplies the operation's fields and nothing else: the envelope, the body wrapper, the operation's own element and its namespace are all known from the extension the import wrote, and assembling them is the point of the exercise. schema is the operation's request schema, which says which fields are attributes rather than child elements and what order the children go in; a nil schema still encodes, with every field an element in sorted order, because a connection may carry an operation whose schema the WSDL left open.
func Render ¶
Render converts a parsed Service into the OpenAPI document the catalog stores and the gateway serves.
Every operation of a SOAP service is a POST to one address, which OpenAPI cannot express: a path item holds at most one operation per method. The document therefore keys each operation under the address followed by the operation name, which is unique by construction because a portType's operation names are. That key is what api_discover lists as the operation's path and what a path-shaped persona rule matches; the address the request is actually sent to travels in x-soap.path, and the gateway substitutes it when it builds the URL. The alternative — one path item carrying one operation for the whole service — would reduce a hundred-operation ERP surface to a single undiscoverable endpoint, which is the state this import exists to end.
Types ¶
type Element ¶
type Element struct {
// Name is the element's local name.
Name string
// Namespace is the element's namespace URI.
Namespace string
// Qualified reports whether the element's DESCENDANTS carry the
// namespace too, which is the schema's elementFormDefault. The element
// itself is global and always carries it.
Qualified bool
// Schema is the element's content as a JSON Schema object. It is nil
// when the element declares no content.
Schema *Schema
}
Element is one message's body element: the name and namespace it is written with, and the shape of its content as a JSON Schema the caller fills in.
type Extension ¶
type Extension struct {
// Version is Version11 or Version12.
Version string `json:"version"`
// Action is the binding's soapAction. It is always written, including
// when empty: an empty action is sent as an empty header, which is not
// the same as sending no header at all.
Action string `json:"action"`
// Path is the address every operation of the service is sent to. The
// document's path key is synthetic (see Render), so this is the only
// place the wire path exists.
Path string `json:"path"`
// Input is the element the request body carries.
Input ExtensionElement `json:"input"`
// Output is the element a successful response carries. Its Name is
// empty for a one-way operation.
Output *ExtensionElement `json:"output,omitempty"`
}
Extension is the x-soap value on one rendered operation.
func ExtensionFrom ¶
ExtensionFrom reads the x-soap extension off a loaded OpenAPI operation, reporting whether the operation is a SOAP one at all.
It is how every caller outside this package recognizes a SOAP operation: the gateway holds a parsed document and no memory of where the spec came from, and the presence of this extension is what says an envelope has to be built rather than the body sent as-is.
type ExtensionElement ¶
type ExtensionElement struct {
Element string `json:"element"`
Namespace string `json:"namespace,omitempty"`
// Qualified reports whether the element's descendants carry the
// namespace, which is the schema's elementFormDefault.
Qualified bool `json:"qualified,omitempty"`
}
ExtensionElement names one body element.
type Fault ¶
type Fault struct {
// Code is the fault code: the 1.1 faultcode, or the 1.2 Code/Value.
Code string
// Reason is the human-readable text: the 1.1 faultstring, or the 1.2
// Reason/Text.
Reason string
// Detail is the fault's application-specific detail as text, empty when
// the fault carries none. It is not parsed further: its content is
// defined by the upstream's own schema, and the decoded response body
// carries the whole tree for a caller that needs it.
Detail string
}
Fault is a soap:Fault read off a response.
The two versions spell it differently — SOAP 1.1 carries faultcode and faultstring as unqualified children, SOAP 1.2 carries Code/Value and Reason/Text — and a caller should not have to know which one answered. Both are read into the same two fields, named for the 1.1 spelling because that is the one the gateway's output has always used for an upstream's own error text.
func FaultFromTree ¶
FaultFromTree reports the soap:Fault in an already-decoded response.
It takes a decoded tree rather than a document because that is what the gateway holds: a SOAP operation's response is read as XML on the way out of the decoder, and parsing it a second time to ask one more question of it would be waste.
The document is matched on local names, so the prefix the sender chose — soap, soapenv, S, env — never has to be guessed at.
type Operation ¶
type Operation struct {
// Name is the wsdl:operation name. It becomes the operationId, which is
// what api_discover lists and what a persona route rule is written
// against.
Name string
// Documentation is the operation's wsdl:documentation, empty when absent.
Documentation string
// SOAPAction is the binding's soapAction. An empty action is legal and
// is sent as an empty header value, which is not the same as omitting
// the header: several stacks dispatch on its presence.
SOAPAction string
// Input is the element the request body carries.
Input Element
// Output is the element a successful response body carries. Its Name is
// empty for a one-way operation, which declares no output message.
Output Element
}
Operation is one WSDL operation resolved into what a call needs: the name it is addressed by, the action header the binding requires, and the element the body carries in each direction.
type Schema ¶
type Schema struct {
Type string `json:"type,omitempty"`
Format string `json:"format,omitempty"`
Description string `json:"description,omitempty"`
Properties map[string]*Schema `json:"properties,omitempty"`
Required []string `json:"required,omitempty"`
Items *Schema `json:"items,omitempty"`
Enum []string `json:"enum,omitempty"`
Nullable bool `json:"nullable,omitempty"`
// Order is the sequence the upstream requires this object's child
// elements in. An xsd:sequence is ordered and a JSON object is not, so
// without it the encoder would emit the properties in whatever order a
// map yields and the upstream would reject a body whose every value was
// correct. Attributes are absent from it: their position is the start
// tag, not the sequence.
Order []string `json:"x-soap-order,omitempty"`
// Attribute marks a property the envelope writer emits as an XML
// attribute rather than a child element. It is an extension because
// JSON Schema has no notion of the distinction and the encoder needs
// it: a property written in the wrong position is silently ignored by
// the upstream.
Attribute bool `json:"x-soap-attribute,omitempty"`
}
Schema is the subset of JSON Schema the importer emits. It is rendered into the OpenAPI document as-is, so the field names are the JSON Schema ones.
It is a type of its own rather than kin-openapi's: the importer's output is validated by the same ParseSpec every other spec goes through, and building the document as data keeps that validation an honest check rather than a round-trip of the library's own structures.
type Service ¶
type Service struct {
// Name is the wsdl:service name, used as the rendered document's title.
Name string
// Documentation is the service's wsdl:documentation, empty when absent.
Documentation string
// TargetNamespace is the document's targetNamespace. It is the default
// namespace of the body elements and is what an envelope is built with.
TargetNamespace string
// Address is the soap:address location of the port that was imported,
// as written. Only its path reaches the rendered document; the host is
// dropped because a catalog spec is shared across connections and each
// connection supplies its own base_url.
Address string
// Path is Address's path, always starting with "/".
Path string
// SOAPVersion is Version11 or Version12, taken from the binding.
SOAPVersion string
// Operations are the port type's operations in document order.
Operations []Operation
}
Service is one imported WSDL: the service the document describes and the operations it exposes, already resolved down to what an HTTP caller needs.
func Import ¶
Import parses a WSDL document and renders it in one step, which is what every caller outside this package wants.