Lather API Documentation
View SourceThis document provides detailed API reference for the Lather SOAP library.
Modules Overview
Core Modules:
Lather- Main module and entry point for the libraryLather.Application- OTP application supervision and startupLather.Client- Low-level SOAP client for custom implementationsLather.DynamicClient- High-level client for any SOAP serviceLather.Error- Comprehensive error handling and SOAP fault parsing
Server Modules:
Lather.Server- SOAP server implementation and configurationLather.Server.DSL- Domain-specific language for defining SOAP servicesLather.Server.Plug- Plug integration for hosting SOAP endpointsLather.Server.EnhancedPlug- Advanced Plug with additional featuresLather.Server.WSDLGenerator- Generates WSDL documents from service definitionsLather.Server.EnhancedWSDLGenerator- Extended WSDL generation with complex typesLather.Server.FormGenerator- Generates HTML forms for testing SOAP operationsLather.Server.Handler- Request handling and operation dispatchingLather.Server.RequestParser- Parses incoming SOAP requestsLather.Server.ResponseBuilder- Builds SOAP responses from handler results
SOAP Processing:
Lather.Soap.Envelope- SOAP envelope construction and manipulationLather.Soap.Body- SOAP body element handlingLather.Soap.Header- SOAP header element handlingLather.Operation.Builder- Dynamic SOAP request buildingLather.Wsdl.Analyzer- WSDL parsing and analysis utilities
HTTP & Transport:
Lather.Http.Transport- HTTP transport layer for SOAP requestsLather.Http.Pool- Connection pool management via Finch
Authentication:
Lather.Auth.Basic- Basic HTTP authenticationLather.Auth.WSSecurity- WS-Security authentication with username tokens
XML Processing:
Lather.Xml.Builder- XML document construction from Elixir dataLather.Xml.Parser- XML document parsing to Elixir structures
Types:
Lather.Types.Mapper- Type conversion between XML and ElixirLather.Types.Generator- Dynamic struct generation from WSDL types
MTOM/Attachments:
Lather.Mtom.Attachment- Binary attachment handling for SOAP messagesLather.Mtom.Builder- Builds MTOM-encoded multipart messagesLather.Mtom.Mime- MIME type handling and multipart parsing
Lather.Application
OTP application supervisor for Lather. Starts and manages the Finch HTTP client pool used for SOAP requests.
Supervision Tree
The application starts a supervision tree with the following children:
Finch- HTTP client pool (namedLather.Finch)
The supervisor uses a :one_for_one strategy, meaning if the Finch pool crashes, only it will be restarted.
Automatic Startup
Lather is configured as an OTP application, so the supervision tree starts automatically when your application starts. No manual intervention is required.
Manual Startup
If you need to start Lather manually (e.g., in a script or test):
{:ok, _pid} = Application.ensure_all_started(:lather)Lather.DynamicClient
The main interface for working with SOAP services dynamically.
Functions
new/2
Creates a new dynamic client from a WSDL URL.
@spec new(String.t(), keyword()) :: {:ok, t()} | {:error, term()}Parameters:
wsdl_url- URL to the WSDL documentoptions- Client configuration options
Options:
:basic_auth- Basic authentication{username, password}:ssl_options- SSL/TLS configuration:timeout- Request timeout in milliseconds:headers- Additional HTTP headers:namespace_aware- Enable namespace-aware parsing
Example:
{:ok, client} = Lather.DynamicClient.new(
"https://example.com/service?wsdl",
basic_auth: {"user", "pass"},
timeout: 30_000
)call/4
Calls a SOAP operation with the given parameters.
@spec call(t(), String.t(), map(), keyword()) :: {:ok, map()} | {:error, term()}Parameters:
client- The dynamic client instanceoperation_name- Name of the operation to callparameters- Map of operation parametersoptions- Call-specific options
Options:
:soap_action- Override SOAPAction header:validate- Enable/disable parameter validation (default: true):timeout- Override timeout for this call:headers- Additional headers for this request
Example:
{:ok, response} = Lather.DynamicClient.call(
client,
"GetUser",
%{"userId" => "12345"},
timeout: 60_000
)list_operations/1
Lists all available operations from the WSDL.
@spec list_operations(t()) :: [String.t()]Example:
operations = Lather.DynamicClient.list_operations(client)
# => ["GetUser", "CreateUser", "UpdateUser", "DeleteUser"]get_operation_info/2
Gets detailed information about a specific operation.
@spec get_operation_info(t(), String.t()) :: {:ok, map()} | {:error, term()}Example:
{:ok, info} = Lather.DynamicClient.get_operation_info(client, "GetUser")
# => %{
# name: "GetUser",
# input_parts: [%{name: "userId", type: "string", required: true}],
# output_parts: [%{name: "user", type: "User"}],
# soap_action: "http://example.com/GetUser"
# }validate_parameters/3
Validates parameters against operation requirements.
@spec validate_parameters(t(), String.t(), map()) :: :ok | {:error, term()}Example:
case Lather.DynamicClient.validate_parameters(client, "GetUser", %{"userId" => "123"}) do
:ok ->
# Parameters are valid
{:error, error} ->
# Handle validation error
endLather.Client
Low-level SOAP client for custom implementations.
Functions
new/2
Creates a new SOAP client.
@spec new(String.t(), keyword()) :: t()post/3
Sends a SOAP request to the endpoint.
@spec post(t(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}Lather.Wsdl.Analyzer
WSDL parsing and analysis utilities.
Functions
analyze/2
Analyzes a WSDL document and extracts service information.
@spec analyze(String.t(), keyword()) :: {:ok, map()} | {:error, term()}Returns a map with:
:operations- List of available operations:types- Complex type definitions:bindings- SOAP binding information:services- Service endpoints:namespaces- Namespace declarations
extract_operations/1
Extracts operation definitions from parsed WSDL.
@spec extract_operations(map()) :: [map()]parse_complex_type/1
Parses complex type definitions.
@spec parse_complex_type(map()) :: map()Lather.Operation.Builder
Dynamic SOAP request building.
Functions
build_request/3
Builds a SOAP request for any operation.
@spec build_request(map(), map(), keyword()) :: {:ok, String.t()} | {:error, term()}validate_parameters/2
Validates operation parameters.
@spec validate_parameters(map(), map()) :: :ok | {:error, term()}parse_response/3
Parses SOAP response into Elixir data structures.
@spec parse_response(map(), map(), keyword()) :: {:ok, map()} | {:error, term()}Lather.Soap.Envelope
SOAP envelope construction utilities.
Functions
build/3
Builds a complete SOAP envelope.
@spec build(map(), String.t(), keyword()) :: String.t()Parameters:
body- SOAP body contentnamespace- Target namespaceoptions- Envelope options
Options:
:soap_version- SOAP version (:soap11or:soap12):headers- SOAP headers to include:prefix- Namespace prefix
wrap_body/2
Wraps content in a SOAP body.
@spec wrap_body(map(), keyword()) :: map()Lather.Soap.Body
SOAP body utilities for creating and managing SOAP body content, including parameter serialization and response parsing.
Functions
create/3
Creates a SOAP body element for the given operation and parameters.
@spec create(atom() | String.t(), map(), keyword()) :: map()Parameters:
operation- Operation name (atom or string)params- Operation parameters (map)options- Body options
Options:
:namespace- Target namespace for the operation:namespace_prefix- Prefix for the target namespace
Example:
Lather.Soap.Body.create(:get_user, %{id: 123}, namespace: "http://example.com")
# => %{
# "get_user" => %{
# "@xmlns" => "http://example.com",
# "id" => 123
# }
# }serialize_params/1
Serializes Elixir data structures to XML-compatible format.
@spec serialize_params(any()) :: any()Handles various Elixir types including maps, lists, atoms, booleans, DateTime, Date, Time, and strings, converting them to XML-safe representations.
validate_params/2
Validates parameters against expected types and constraints.
@spec validate_params(map(), map()) :: :ok | {:error, [String.t()]}Parameters:
params- Parameters to validateschema- Validation schema (map)
Schema Format:
%{
"id" => [:required, :integer],
"name" => [:required, :string, {:max_length, 50}],
"email" => [:optional, :string, :email]
}Lather.Soap.Header
SOAP header utilities for creating and managing SOAP headers, including authentication headers and custom header elements.
Functions
username_token/3
Creates a WS-Security UsernameToken header.
@spec username_token(String.t(), String.t(), keyword()) :: map()Parameters:
username- Username for authenticationpassword- Password for authenticationoptions- Header options
Options:
:password_type-:textor:digest(default::text):include_nonce- Whether to include a nonce (default:truefor digest):include_created- Whether to include timestamp (default:true)
Example:
Lather.Soap.Header.username_token("user", "pass")
# => %{
# "wsse:Security" => %{
# "@xmlns:wsse" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
# "wsse:UsernameToken" => %{...}
# }
# }timestamp/1
Creates a WS-Security timestamp header.
@spec timestamp(keyword()) :: map()Options:
:ttl- Time to live in seconds (default: 300)
username_token_with_timestamp/3
Creates a combined WS-Security header with both UsernameToken and Timestamp.
@spec username_token_with_timestamp(String.t(), String.t(), keyword()) :: map()Parameters:
username- Username for authenticationpassword- Password for authenticationoptions- Combined options for both UsernameToken and Timestamp
session/2
Creates a session header for maintaining session state.
@spec session(String.t(), keyword()) :: map()Parameters:
session_id- The session IDoptions- Additional options
Options:
:header_name- Custom header name (default: "SessionId"):namespace- Custom namespace
Example:
Lather.Soap.Header.session("session_12345")
# => %{"SessionId" => "session_12345"}custom/3
Creates a custom header element.
@spec custom(String.t(), map() | String.t(), map()) :: map()Parameters:
name- Header element namecontent- Header content (map or string)attributes- Element attributes
Example:
Lather.Soap.Header.custom("MyHeader", %{"value" => "test"}, %{"xmlns" => "http://example.com"})
# => %{"MyHeader" => %{"@xmlns" => "http://example.com", "value" => "test"}}merge_headers/1
Merges multiple header elements into a single header map.
@spec merge_headers([map()]) :: map()Example:
header1 = Lather.Soap.Header.session("session_123")
header2 = Lather.Soap.Header.custom("MyApp", "v1.0")
Lather.Soap.Header.merge_headers([header1, header2])
# => %{"SessionId" => "session_123", "MyApp" => "v1.0"}Lather.Http.Transport
HTTP transport layer for SOAP requests.
Functions
post/3
Sends an HTTP POST request.
@spec post(String.t(), String.t(), keyword()) :: {:ok, map()} | {:error, term()}Parameters:
url- Request URLbody- Request bodyoptions- HTTP options
Options:
:timeout- Request timeout:headers- HTTP headers:soap_action- SOAPAction header:ssl_options- SSL configuration:basic_auth- Basic authentication
validate_url/1
Validates a URL for SOAP requests.
@spec validate_url(String.t()) :: :ok | {:error, :invalid_url}ssl_options/1
Creates SSL options for secure connections.
@spec ssl_options(keyword()) :: keyword()Lather.Http.Pool
Connection pool configuration for HTTP transport. Provides configuration and utilities for managing Finch connection pools optimized for SOAP requests.
Functions
default_config/0
Returns the default pool configuration for SOAP clients.
@spec default_config() :: keyword()Optimized for typical SOAP usage patterns with reasonable defaults for connection pooling, timeouts, and SSL settings.
Default Configuration:
[
pool_timeout: 5_000,
pool_max_idle_time: 30_000,
http2_max_concurrent_streams: 1000,
transport_opts: [
verify: :verify_peer,
customize_hostname_check: [
match_fun: :public_key.pkix_verify_hostname_match_fun(:https)
]
]
]config_for_endpoint/2
Creates a pool configuration for a specific endpoint.
@spec config_for_endpoint(String.t(), keyword()) :: keyword()Parameters:
endpoint- The SOAP endpoint URLoverrides- Configuration overrides (optional)
Allows customization of pool settings per endpoint, useful for services with different performance characteristics.
Example:
config = Lather.Http.Pool.config_for_endpoint(
"https://api.example.com/soap",
pool_timeout: 10_000
)validate_config/1
Validates pool configuration options.
@spec validate_config(keyword()) :: :ok | {:error, String.t()}Lather.Xml.Builder
XML document construction.
Functions
build/1
Builds an XML document from Elixir data structures.
@spec build(map()) :: {:ok, String.t()} | {:error, any()}Example:
xml = Lather.Xml.Builder.build(%{
"GetUser" => %{
"@xmlns" => "http://example.com",
"userId" => "12345"
}
})
# => "<GetUser xmlns=\"http://example.com\"><userId>12345</userId></GetUser>"escape/1
Escapes XML content.
@spec escape(String.t()) :: String.t()Lather.Xml.Parser
XML document parsing.
Functions
parse/2
Parses XML content into Elixir data structures.
@spec parse(String.t(), keyword()) :: {:ok, map()} | {:error, term()}Options:
:namespace_aware- Enable namespace handling:custom_parsers- Custom type parsers
extract_namespaces/1
Extracts namespace declarations from XML.
@spec extract_namespaces(String.t()) :: map()Lather.Types.Mapper
Type conversion between XML and Elixir.
Functions
xml_to_elixir/3
Converts XML data to Elixir types.
@spec xml_to_elixir(map(), map(), keyword()) :: {:ok, term()} | {:error, term()}elixir_to_xml/3
Converts Elixir data to XML representation.
@spec elixir_to_xml(term(), map(), keyword()) :: {:ok, map()} | {:error, term()}validate_type/3
Validates data against type definitions.
@spec validate_type(term(), map(), keyword()) :: :ok | {:error, term()}Lather.Types.Generator
Dynamic struct generation from WSDL types.
Functions
generate_structs/2
Generates Elixir struct modules from WSDL types.
@spec generate_structs(map(), String.t()) :: {:ok, [module()]} | {:error, term()}create_struct_instance/3
Creates a struct instance with type validation.
@spec create_struct_instance(module(), map(), keyword()) :: {:ok, struct()} | {:error, term()}Lather.Auth.Basic
Basic HTTP authentication.
Functions
header/2
Creates a Basic authentication header.
@spec header(String.t(), String.t()) :: {String.t(), String.t()}Lather.Auth.WSSecurity
WS-Security authentication.
Functions
username_token/3
Creates a WS-Security username token.
@spec username_token(String.t(), String.t(), keyword()) :: map()Parameters:
username- The username for authenticationpassword- The password for authenticationoptions- Keyword options for token configuration
Options:
:password_type- Password type (:textor:digest, default::text):nonce- Custom nonce value (auto-generated if not provided):created- Custom created timestamp (auto-generated if not provided)
Example:
# Username token with password digest
username_token = Lather.Auth.WSSecurity.username_token("user", "pass", password_type: :digest)MTOM Modules
MTOM (Message Transmission Optimization Mechanism) modules enable efficient binary data transmission in SOAP messages using XOP (XML-binary Optimized Packaging).
Lather.Mtom.Attachment
Defines the structure for binary attachments in MTOM messages and provides utilities for creating, validating, and managing attachments.
Types
@type t :: %Lather.Mtom.Attachment{
id: String.t(),
content_type: String.t(),
content_transfer_encoding: String.t(),
data: binary(),
content_id: String.t(),
size: non_neg_integer()
}Functions
new/3
Creates a new attachment from binary data and content type.
@spec new(binary(), String.t(), keyword()) :: t()Parameters:
data- Binary data for the attachmentcontent_type- MIME content type (e.g., "application/pdf")options- Additional options
Options:
:content_id- Custom Content-ID (auto-generated if not provided):content_transfer_encoding- Transfer encoding (default: "binary"):validate- Whether to validate the attachment (default: true)
Example:
attachment = Lather.Mtom.Attachment.new(pdf_data, "application/pdf")
attachment = Lather.Mtom.Attachment.new(image_data, "image/jpeg",
content_id: "custom-id-123"
)from_file/2
Creates an attachment from a file path.
@spec from_file(String.t(), keyword()) :: {:ok, t()} | {:error, term()}Example:
{:ok, attachment} = Lather.Mtom.Attachment.from_file("document.pdf")
{:ok, attachment} = Lather.Mtom.Attachment.from_file("image.jpg", content_type: "image/jpeg")validate/1
Validates an attachment structure and content.
@spec validate(t()) :: :ok | {:error, atom()}Example:
:ok = Lather.Mtom.Attachment.validate(attachment)
{:error, :attachment_too_large} = Lather.Mtom.Attachment.validate(huge_attachment)content_id_header/1
Generates a Content-ID header value for the attachment.
@spec content_id_header(t()) :: String.t()cid_reference/1
Generates a CID reference for XOP includes.
@spec cid_reference(t()) :: String.t()Example:
cid_ref = Lather.Mtom.Attachment.cid_reference(attachment)
# "cid:attachment123@lather.soap"xop_include/1
Creates an XOP Include element for the attachment.
@spec xop_include(t()) :: map()is_attachment?/1
Checks if a parameter value represents an attachment.
@spec is_attachment?(any()) :: boolean()Example:
Lather.Mtom.Attachment.is_attachment?({:attachment, data, "application/pdf"}) # true
Lather.Mtom.Attachment.is_attachment?("regular string") # falsefrom_tuple/1
Converts an attachment tuple to an Attachment struct.
@spec from_tuple(tuple()) :: {:ok, t()} | {:error, term()}Example:
{:ok, attachment} = Lather.Mtom.Attachment.from_tuple({:attachment, data, "application/pdf"})
{:ok, attachment} = Lather.Mtom.Attachment.from_tuple({:attachment, data, "image/jpeg", [content_id: "img1"]})Lather.Mtom.Builder
Constructs MTOM multipart SOAP messages by extracting binary attachments from parameters, replacing them with XOP Include references, and packaging everything into a multipart/related MIME message.
Functions
build_mtom_message/3
Builds a complete MTOM message with SOAP envelope and binary attachments.
@spec build_mtom_message(atom() | String.t(), map(), keyword()) ::
{:ok, {String.t(), binary()}} | {:error, term()}Parameters:
operation- SOAP operation name (atom or string)parameters- Parameters map potentially containing attachment tuplesoptions- SOAP envelope building options
Options:
:namespace- Target namespace for the operation:headers- SOAP headers to include:version- SOAP version (:v1_1or:v1_2):boundary- Custom MIME boundary (auto-generated if not provided):enable_mtom- Force MTOM even without attachments (default: auto-detect)
Example:
params = %{
"document" => {:attachment, pdf_data, "application/pdf"},
"metadata" => %{"title" => "Report"}
}
{:ok, {content_type, body}} = Lather.Mtom.Builder.build_mtom_message(
:UploadDocument,
params,
namespace: "http://example.com/upload"
)process_parameters/1
Processes parameters to extract attachments and replace with XOP includes.
@spec process_parameters(map()) :: {:ok, {map(), [Attachment.t()]}} | {:error, term()}Example:
params = %{"file" => {:attachment, data, "application/pdf"}}
{:ok, {new_params, [attachment]}} = Lather.Mtom.Builder.process_parameters(params)
# new_params contains XOP Include reference instead of binary datahas_attachments?/1
Checks if parameters contain any attachment tuples.
@spec has_attachments?(map()) :: boolean()Example:
Lather.Mtom.Builder.has_attachments?(%{"file" => {:attachment, data, "pdf"}}) # true
Lather.Mtom.Builder.has_attachments?(%{"name" => "John"}) # falsevalidate_attachments/1
Validates that all attachment tuples in parameters are properly formatted.
@spec validate_attachments(map()) :: :ok | {:error, term()}estimate_message_size/2
Estimates the total size of a message including all attachments.
@spec estimate_message_size(map(), non_neg_integer()) :: non_neg_integer()Lather.Mtom.Mime
Provides functions for building and parsing multipart/related MIME messages used in MTOM.
Functions
generate_boundary/0
Generates a unique boundary string for multipart messages.
@spec generate_boundary() :: String.t()Example:
boundary = Lather.Mtom.Mime.generate_boundary()
# "uuid:a1b2c3d4-e5f6-7890-abcd-ef1234567890"build_multipart_message/3
Builds a complete multipart/related MIME message with SOAP envelope and attachments.
@spec build_multipart_message(binary(), [Attachment.t()], keyword()) ::
{String.t(), binary()}Parameters:
soap_envelope- The SOAP envelope XML as binaryattachments- List of Attachment structsoptions- Additional options
Options:
:boundary- Custom boundary (auto-generated if not provided):soap_content_type- SOAP part content type (default: "application/xop+xml"):soap_charset- SOAP part charset (default: "UTF-8")
Example:
{content_type, body} = Lather.Mtom.Mime.build_multipart_message(soap_xml, attachments)parse_multipart_message/3
Parses a multipart/related MIME message.
@spec parse_multipart_message(String.t(), binary(), keyword()) ::
{:ok, {binary(), [map()]}} | {:error, term()}Example:
{:ok, {soap_xml, attachments}} = Lather.Mtom.Mime.parse_multipart_message(content_type, body)extract_boundary/1
Extracts the boundary parameter from a Content-Type header.
@spec extract_boundary(String.t()) :: {:ok, String.t()} | {:error, atom()}Example:
{:ok, boundary} = Lather.Mtom.Mime.extract_boundary("multipart/related; boundary=\"uuid:123\"")parse_headers/1
Parses MIME headers from a header section.
@spec parse_headers(binary()) :: map()Example:
headers = Lather.Mtom.Mime.parse_headers("Content-Type: application/pdf\r\nContent-ID: <att1>")
# %{"content-type" => "application/pdf", "content-id" => "<att1>"}build_content_type_header/3
Builds a Content-Type header for multipart/related messages.
@spec build_content_type_header(String.t(), String.t(), String.t()) :: String.t()validate_content_type/1
Validates a multipart/related Content-Type header.
@spec validate_content_type(String.t()) :: :ok | {:error, atom()}Server Modules
Lather provides a complete SOAP server implementation for building web services in Elixir.
Lather.Server
The main module for creating SOAP service modules. Use use Lather.Server to define a SOAP service.
Macros
using/1
Sets up a module as a SOAP service with automatic WSDL generation.
defmodule MyApp.UserService do
use Lather.Server, namespace: "http://example.com/users", service_name: "UserService"
# Define operations using @soap_operation attribute or DSL macros
endFunctions
soap_fault/3
Creates a SOAP fault response.
@spec soap_fault(String.t(), String.t(), term() | nil) :: {:soap_fault, map()}Example:
soap_fault("Client", "User not found", %{user_id: "123"})validate_required_params/2
Validates that required operation parameters are present.
@spec validate_required_params(map(), map()) :: :ok | {:error, String.t()}validate_param_types/2
Validates parameter types according to operation definition.
@spec validate_param_types(map(), map()) :: :ok | {:error, String.t()}format_response/2
Formats operation response according to SOAP conventions.
@spec format_response(term(), map()) :: {:ok, map()} | {:soap_fault, map()}Lather.Server.DSL
Domain Specific Language for defining SOAP operations and types with a declarative syntax.
Macros
soap_operation/2
Defines a SOAP operation with metadata for WSDL generation.
soap_operation "GetUser" do
description "Retrieves a user by ID"
input do
parameter "userId", :string, required: true, description: "User identifier"
parameter "includeDetails", :boolean, required: false, default: false
end
output do
parameter "user", "User", description: "User information"
end
soap_action "http://example.com/GetUser"
end
def get_user(params) do
# Implementation
endsoap_type/2
Defines a complex type for use in operations.
soap_type "User" do
type_description "User information"
element "id", :string, required: true
element "name", :string, required: true
element "email", :string, required: false
element "created_at", :dateTime, required: true
endsoap_auth/1
Defines authentication requirements for operations.
soap_auth do
basic_auth realm: "SOAP Service"
# or
ws_security required: true
# or
custom_auth handler: MyApp.CustomAuth
endinput/1, output/1
Defines input and output parameter blocks within an operation.
parameter/3
Defines a parameter within an input or output block.
parameter "userId", :string, required: true, description: "User ID", min_occurs: 1, max_occurs: 1element/3
Defines an element within a complex type.
element "name", :string, required: true, description: "User name"description/1, type_description/1
Sets descriptions for operations and types.
basic_auth/1, ws_security/1, custom_auth/1
Authentication configuration macros for use within soap_auth blocks.
Lather.Server.Plug
Plug implementation for SOAP server endpoints. Requires the :plug dependency.
Usage
# In Phoenix router
scope "/soap" do
pipe_through :api
post "/users", Lather.Server.Plug, service: MyApp.UserService
end
# As standalone Plug
plug Lather.Server.Plug, service: MyApp.UserServiceOptions
:service- The SOAP service module (required):path- Base path for WSDL generation (default:"/"):auth_handler- Custom authentication handler module:validate_params- Enable parameter validation (default:true):generate_wsdl- Enable WSDL generation endpoint (default:true)
Functions
init/1
Initializes the Plug with options.
@spec init(keyword()) :: map()call/2
Handles incoming HTTP requests (GET for WSDL, POST for SOAP operations).
@spec call(Plug.Conn.t(), map()) :: Plug.Conn.t()Lather.Server.EnhancedPlug
Enhanced Plug implementation with web form interface and multi-protocol support.
Features
- Interactive web forms for testing operations
- SOAP 1.1, SOAP 1.2, and JSON protocol support
- Enhanced WSDL generation with multi-protocol bindings
- Service overview with complete operation documentation
URL Patterns
GET /service- Service overview with operations listGET /service?wsdl- Standard WSDL downloadGET /service?wsdl&enhanced=true- Multi-protocol WSDLGET /service?op=OperationName- Interactive operation formPOST /service- SOAP 1.1 endpointPOST /service/v1.2- SOAP 1.2 endpointPOST /service/api- JSON/REST endpoint
Usage
# In Phoenix router
scope "/soap" do
pipe_through :api
match :*, "/users", Lather.Server.EnhancedPlug, service: MyApp.UserService
match :*, "/users/*path", Lather.Server.EnhancedPlug, service: MyApp.UserService
endOptions
:service- The SOAP service module (required):base_path- Base path for service (default:"/soap"):enable_forms- Enable web form interface (default:true):enable_json- Enable JSON endpoints (default:true):auth_handler- Custom authentication handler:validate_params- Enable parameter validation (default:true)
Functions
init/1
@spec init(keyword()) :: map()call/2
@spec call(Plug.Conn.t(), map()) :: Plug.Conn.t()Lather.Server.Handler
Generic HTTP handler for SOAP server endpoints without requiring Plug. Works with any HTTP server.
Usage
# In Phoenix controller
defmodule MyAppWeb.SOAPController do
use MyAppWeb, :controller
def handle_soap(conn, _params) do
case Lather.Server.Handler.handle_request(
conn.method,
conn.request_path,
conn.req_headers,
conn.assigns.raw_body,
MyApp.UserService
) do
{:ok, status, headers, body} ->
conn |> put_status(status) |> text(body)
{:error, status, headers, body} ->
conn |> put_status(status) |> text(body)
end
end
endFunctions
handle_request/6
Handles a SOAP HTTP request.
@spec handle_request(String.t(), String.t(), [{String.t(), String.t()}], String.t(), module(), keyword()) ::
{:ok, integer(), [{String.t(), String.t()}], String.t()} |
{:error, integer(), [{String.t(), String.t()}], String.t()}Parameters:
method- HTTP method ("GET"or"POST")path- Request pathheaders- Request headersbody- Request bodyservice- SOAP service moduleopts- Options (:validate_params,:generate_wsdl,:base_url)
Lather.Server.RequestParser
Parses incoming SOAP requests and extracts operation details and parameters.
Functions
parse/1
Parses a SOAP request XML and extracts the operation name and parameters.
@spec parse(String.t()) :: {:ok, %{operation: String.t(), params: map()}} | {:error, {:parse_error, String.t()}}Example:
{:ok, %{operation: "GetUser", params: %{"userId" => "123"}}} =
Lather.Server.RequestParser.parse(soap_xml)Lather.Server.ResponseBuilder
Builds SOAP response XML from operation results.
Functions
build_response/2
Builds a SOAP response envelope containing the operation result.
@spec build_response(term(), map()) :: String.t()Example:
xml = Lather.Server.ResponseBuilder.build_response(
%{"user" => %{"id" => "123", "name" => "John"}},
%{name: "GetUser"}
)build_fault/1
Builds a SOAP fault response.
@spec build_fault(map() | nil) :: String.t()Example:
xml = Lather.Server.ResponseBuilder.build_fault(%{
fault_code: "Client",
fault_string: "User not found",
detail: %{user_id: "123"}
})Lather.Server.WSDLGenerator
Generates WSDL files from SOAP service definitions.
Functions
generate/2
Generates a complete WSDL document for a SOAP service.
@spec generate(map(), String.t()) :: String.t()Parameters:
service_info- Service metadata from__soap_service__/0base_url- Base URL for the service endpoint
Example:
service_info = MyApp.UserService.__soap_service__()
wsdl = Lather.Server.WSDLGenerator.generate(service_info, "http://example.com/soap")Lather.Server.EnhancedWSDLGenerator
Enhanced WSDL generator with multi-protocol support (SOAP 1.1, SOAP 1.2, HTTP/REST).
Functions
generate/3
Generates a comprehensive multi-protocol WSDL document.
@spec generate(map(), String.t(), keyword()) :: String.t()Options:
:protocols- List of protocols to include (default:[:soap_1_1, :soap_1_2, :http]):base_path- Base path for REST endpoints (default:"/api"):include_json- Include JSON content type support (default:true)
Example:
service_info = MyApp.UserService.__soap_service__()
wsdl = Lather.Server.EnhancedWSDLGenerator.generate(
service_info,
"http://example.com",
protocols: [:soap_1_1, :soap_1_2]
)Lather.Server.FormGenerator
Generates HTML forms and documentation pages for SOAP operations, similar to .NET Web Services.
Functions
generate_operation_page/4
Generates a complete HTML page for an operation with testing forms and protocol examples.
@spec generate_operation_page(map(), map(), String.t(), keyword()) :: String.t()Parameters:
service_info- Service metadataoperation- Operation metadatabase_url- Base URL for the serviceoptions- Additional options
generate_service_overview/3
Generates a service overview page with all operations listed.
@spec generate_service_overview(map(), String.t(), keyword()) :: String.t()Example:
service_info = MyApp.UserService.__soap_service__()
html = Lather.Server.FormGenerator.generate_service_overview(
service_info,
"http://example.com/soap"
)Complete Server Example
defmodule MyApp.CalculatorService do
use Lather.Server,
namespace: "http://example.com/calculator",
service_name: "Calculator"
# Define a complex type
soap_type "CalculationResult" do
type_description "Result of a calculation"
element "value", :decimal, required: true
element "operation", :string, required: true
element "timestamp", :dateTime, required: true
end
# Define an operation
soap_operation "Add" do
description "Adds two numbers"
input do
parameter "a", :decimal, required: true, description: "First number"
parameter "b", :decimal, required: true, description: "Second number"
end
output do
parameter "result", "CalculationResult"
end
soap_action "http://example.com/calculator/Add"
end
def add(%{"a" => a, "b" => b}) do
result = Decimal.add(Decimal.new(a), Decimal.new(b))
{:ok, %{
"result" => %{
"value" => Decimal.to_string(result),
"operation" => "add",
"timestamp" => DateTime.utc_now() |> DateTime.to_iso8601()
}
}}
end
end
# Mount in Phoenix router
scope "/soap" do
pipe_through :api
match :*, "/calculator", Lather.Server.EnhancedPlug, service: MyApp.CalculatorService
match :*, "/calculator/*path", Lather.Server.EnhancedPlug, service: MyApp.CalculatorService
endLather.Error
Comprehensive error handling.
Types
soap_fault
SOAP fault information.
@type soap_fault :: %{
fault_code: String.t(),
fault_string: String.t(),
fault_actor: String.t() | nil,
detail: map() | nil
}transport_error
Transport layer errors.
@type transport_error :: %{
type: :transport_error,
reason: atom() | String.t(),
details: map()
}http_error
HTTP-level errors.
@type http_error :: %{
type: :http_error,
status: integer(),
body: String.t(),
headers: [{String.t(), String.t()}]
}validation_error
Parameter validation errors.
@type validation_error :: %{
type: :validation_error,
field: String.t(),
reason: atom(),
details: map()
}Functions
parse_soap_fault/2
Parses SOAP fault from response.
@spec parse_soap_fault(String.t(), keyword()) :: {:ok, soap_fault()} | {:error, term()}transport_error/2
Creates a transport error.
@spec transport_error(term(), map()) :: transport_error()http_error/3
Creates an HTTP error.
@spec http_error(integer(), String.t(), [{String.t(), String.t()}]) :: http_error()validation_error/3
Creates a validation error.
@spec validation_error(String.t(), atom(), map()) :: validation_error()format_error/2
Formats errors for display.
@spec format_error(term(), keyword()) :: String.t()recoverable?/1
Checks if an error is recoverable.
@spec recoverable?(term()) :: boolean()extract_debug_context/1
Extracts debugging information from errors.
@spec extract_debug_context(term()) :: map()Configuration
Application Configuration
# config/config.exs
config :lather,
# Default timeout for all requests
default_timeout: 30_000,
# SSL verification mode
ssl_verify: :verify_peer,
# Connection pool settings
finch_pools: %{
default: [size: 25, count: 1]
},
# WSDL caching
cache_wsdl: true,
cache_ttl: 3600,
# Telemetry events
telemetry_enabled: trueRuntime Configuration
# Override configuration at runtime
Application.put_env(:lather, :default_timeout, 60_000)Telemetry Events
Lather emits telemetry events for monitoring:
[:lather, :request, :start]- SOAP request started[:lather, :request, :stop]- SOAP request completed[:lather, :request, :error]- SOAP request failed[:lather, :wsdl, :parse, :start]- WSDL parsing started[:lather, :wsdl, :parse, :stop]- WSDL parsing completed
Telemetry Example
:telemetry.attach_many(
"lather-handler",
[
[:lather, :request, :start],
[:lather, :request, :stop],
[:lather, :request, :error]
],
&MyApp.Telemetry.handle_event/4,
nil
)Error Codes
| Code | Type | Description |
|---|---|---|
operation_not_found | validation | Operation not defined in WSDL |
missing_required_parameter | validation | Required parameter not provided |
invalid_parameter_type | validation | Parameter type mismatch |
unsupported_encoding | validation | Unsupported SOAP encoding |
invalid_soap_response | validation | Malformed SOAP response |
transport_error | transport | Network/connection error |
http_error | http | HTTP status error |
wsdl_error | wsdl | WSDL parsing error |
Best Practices
- Reuse Clients: Create clients once and reuse them across requests
- Handle Errors: Always handle different error types appropriately
- Set Timeouts: Configure appropriate timeouts for your use case
- Use SSL: Always use HTTPS in production environments
- Cache WSDL: Enable WSDL caching for better performance
- Monitor Operations: Use telemetry for monitoring and debugging
- Validate Parameters: Use built-in validation to catch errors early
- Connection Pooling: Configure Finch pools for optimal performance