Packages

Production-ready SOAP library for Elixir featuring SOAP 1.1/1.2 support, dynamic WSDL parsing, interactive web forms, MTOM attachments, Phoenix integration, and multi-protocol API generation

Current section

Files

Jump to
lather README.md
Raw

README.md

# Lather 🧼
[![Hex.pm](https://img.shields.io/hexpm/v/lather.svg)](https://hex.pm/packages/lather)
[![Documentation](https://img.shields.io/badge/docs-hexdocs-blue.svg)](https://hexdocs.pm/lather)
[![License](https://img.shields.io/hexpm/l/lather.svg)](https://github.com/markcotner/lather/blob/main/LICENSE)
**A comprehensive SOAP library for Elixir** that provides both client and server capabilities with modern web interfaces. Lather can work with any SOAP service without requiring service-specific implementations, using dynamic WSDL analysis and runtime operation building.
## ✨ Key Features
- 🌐 **Universal SOAP Client**: Works with any SOAP service using WSDL analysis
- πŸ–₯️ **Complete SOAP Server**: Build SOAP services with a clean DSL
- πŸ”„ **Dynamic Operations**: Automatically discovers and builds requests for any SOAP operation
- πŸ›‘οΈ **Enterprise Security**: WS-Security, Basic Auth, SSL/TLS support
- ⚑ **High Performance**: Built on Finch with connection pooling and async support
- πŸ”§ **Phoenix Integration**: Seamless integration with Phoenix applications
- πŸ“ **Type Safety**: Dynamic type mapping and validation with struct generation
- 🚨 **Robust Error Handling**: Structured error types with SOAP fault parsing
## 🌟 Enhanced Features (v1.0.0)
### Multi-Protocol SOAP & REST Support
Lather v1.0.0 features a **three-layer API architecture** that serves multiple protocol types from a single service:
```
β”Œβ”€ SOAP 1.1 (Top - Maximum Compatibility) β”‚ Legacy systems, .NET Framework
β”œβ”€ SOAP 1.2 (Middle - Enhanced Features) β”‚ Modern SOAP, better error handling
└─ REST/JSON (Bottom - Modern Applications) β”‚ Web apps, mobile, JavaScript
```
### Interactive Web Interface
Professional HTML5 testing interface similar to .NET Web Services:
- πŸ“ **Interactive Forms**: Test operations directly in your browser
- 🌐 **Multi-Protocol Examples**: See SOAP 1.1, SOAP 1.2, and JSON formats
- πŸ“± **Responsive Design**: Works on desktop and mobile
- πŸŒ™ **Dark Mode Support**: Automatically respects browser dark mode preference
- ⚑ **Real-time Validation**: Type-aware parameter validation
### Enhanced WSDL Generation
Generate comprehensive WSDL documents with multiple protocol bindings:
```elixir
# Standard WSDL (SOAP 1.1 only)
wsdl = Lather.Server.WsdlGenerator.generate(service_info, base_url)
# Enhanced WSDL (multi-protocol)
enhanced_wsdl = Lather.Server.EnhancedWSDLGenerator.generate(service_info, base_url)
# Interactive web forms
forms = Lather.Server.FormGenerator.generate_service_overview(service_info, base_url)
```
### Flexible URL Structure
- `GET /service` β†’ Interactive service overview with testing forms
- `GET /service?wsdl` β†’ Standard WSDL download
- `GET /service?wsdl&enhanced=true` β†’ Multi-protocol WSDL
- `GET /service?op=OperationName` β†’ Interactive operation testing form
- `POST /service` β†’ SOAP 1.1 endpoint (maximum compatibility)
- `POST /service/v1.2` β†’ SOAP 1.2 endpoint (enhanced features)
- `POST /service/api` β†’ JSON/REST endpoint (modern applications)
## πŸš€ Quick Start
### Installation
Add `lather` to your `mix.exs` dependencies:
```elixir
def deps do
[
{:lather, "~> 1.0"},
# Optional: for JSON/REST endpoints in enhanced features
{:jason, "~> 1.4"}
]
end
```
### SOAP Client
Connect to any SOAP service and start making calls:
```elixir
# Create a dynamic client from any WSDL
{:ok, client} = Lather.DynamicClient.new("http://example.com/service?wsdl")
# Call any operation defined in the WSDL
{:ok, response} = Lather.DynamicClient.call(client, "GetUser", %{
"userId" => "12345"
})
# With authentication
{:ok, client} = Lather.DynamicClient.new(wsdl_url, [
basic_auth: {"username", "password"},
timeout: 30_000
])
```
### SOAP Server
Define SOAP services with a clean, macro-based DSL:
```elixir
defmodule MyApp.UserService do
use Lather.Server
@service_name "UserService"
@target_namespace "http://myapp.com/user"
defoperation get_user,
input: [user_id: :string],
output: [user: %{name: :string, email: :string}] do
user = MyApp.Users.get!(user_id)
{:ok, %{user: %{name: user.name, email: user.email}}}
end
end
# Add to your Phoenix router
pipe_through :api
post "/soap/user", Lather.Server.Plug, service: MyApp.UserService
```
### Enhanced Multi-Protocol Server
```elixir
# Define a service that supports SOAP 1.1, SOAP 1.2, and JSON/REST
defmodule MyApp.UserService do
use Lather.Server
@namespace "http://myapp.com/users"
@service_name "UserManagementService"
soap_operation "GetUser" do
description "Retrieve user information by ID"
input do
parameter "userId", :string, required: true, description: "User identifier"
parameter "includeProfile", :boolean, required: false, description: "Include full profile"
end
output do
parameter "user", "tns:User", description: "User information"
end
soap_action "#{@namespace}/GetUser"
end
def get_user(%{"userId" => user_id} = params) do
include_profile = Map.get(params, "includeProfile", false)
# Your business logic here
{:ok, %{"user" => %{"id" => user_id, "name" => "John Doe"}}}
end
end
# Phoenix router with enhanced features
scope "/api/users" do
pipe_through :api
# Multi-protocol endpoints
match :*, "/", Lather.Server.EnhancedPlug, service: MyApp.UserService
match :*, "/*path", Lather.Server.EnhancedPlug, service: MyApp.UserService
end
# Generate enhanced WSDL with multiple protocols
service_info = MyApp.UserService.__service_info__()
enhanced_wsdl = Lather.Server.EnhancedWSDLGenerator.generate(service_info, "https://myapp.com/api/users")
# Generate interactive web forms
overview_page = Lather.Server.FormGenerator.generate_service_overview(service_info, "https://myapp.com/api/users")
```
### Access Multiple Protocol Endpoints
Your service automatically exposes multiple endpoints:
```bash
# Interactive web interface
curl -X GET "https://myapp.com/api/users"
# Standard WSDL (SOAP 1.1)
curl -X GET "https://myapp.com/api/users?wsdl"
# Enhanced WSDL (multi-protocol)
curl -X GET "https://myapp.com/api/users?wsdl&enhanced=true"
# Interactive operation form
curl -X GET "https://myapp.com/api/users?op=GetUser"
# SOAP 1.1 request
curl -X POST "https://myapp.com/api/users" \
-H "Content-Type: text/xml; charset=utf-8" \
-H "SOAPAction: http://myapp.com/users/GetUser" \
-d '<soap:Envelope>...</soap:Envelope>'
# SOAP 1.2 request
curl -X POST "https://myapp.com/api/users/v1.2" \
-H "Content-Type: application/soap+xml; charset=utf-8; action=\"http://myapp.com/users/GetUser\"" \
-d '<soap:Envelope>...</soap:Envelope>'
# JSON/REST request
curl -X POST "https://myapp.com/api/users/api" \
-H "Content-Type: application/json" \
-d '{"operation": "GetUser", "parameters": {"userId": "123"}}'
```
## πŸ“š Interactive Learning with Livebooks
Lather includes comprehensive interactive documentation via **Livebooks** that you can run directly in your development environment. These tutorials provide hands-on experience with real SOAP services and practical examples.
### Available Livebooks
#### 🌱 **Getting Started** (`livebooks/getting_started.livemd`)
Perfect introduction to Lather with step-by-step examples:
- Creating your first SOAP client
- Making basic SOAP calls
- Handling responses and errors
- Authentication basics
#### 🌀️ **Weather Service Example** (`livebooks/weather_service_example.livemd`)
Real-world example using the National Weather Service API:
- Working with document/encoded SOAP services
- Complex parameter handling
- Response parsing and data extraction
- Error handling with external services
#### 🌍 **Country Info Service Example** (`livebooks/country_info_service_example.livemd`)
Demonstrates document/literal SOAP style:
- Different SOAP encoding styles
- Namespace handling
- Complex data structures
- Service discovery
#### πŸ–₯️ **SOAP Server Development** (`livebooks/soap_server_development.livemd`)
Complete server development tutorial:
- Building SOAP services with Lather.Server
- Multi-protocol endpoint configuration
- Interactive web interfaces
- Testing your services
#### πŸ”§ **Advanced Types** (`livebooks/advanced_types.livemd`)
Master complex data structures:
- Working with complex types
- Arrays and nested objects
- Type validation and conversion
- Custom type mappings
#### 🏒 **Enterprise Integration** (`livebooks/enterprise_integration.livemd`)
Production-ready patterns and practices:
- WS-Security implementation
- SSL/TLS configuration
- Performance optimization
- Monitoring and logging
#### πŸ› **Debugging & Troubleshooting** (`livebooks/debugging_troubleshooting.livemd`)
Essential debugging techniques:
- SOAP message inspection
- Common error patterns
- Network troubleshooting
- Performance analysis
### Running Livebooks
To use the interactive tutorials:
```bash
# Install Livebook if you haven't already
mix escript.install hex livebook
# Navigate to your project directory
cd your_project
# Start Livebook
livebook server
# Open any of the tutorial files from the livebooks/ directory
```
Or run individual livebooks directly:
```bash
# Run a specific livebook
livebook server livebooks/getting_started.livemd
```
The livebooks are self-contained and include all necessary dependencies. They're perfect for:
- **Learning**: Step-by-step tutorials with explanations
- **Testing**: Try different SOAP services interactively
- **Development**: Use as templates for your own implementations
- **Troubleshooting**: Debug issues with real examples
## πŸ—οΈ Architecture
```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Application β”‚ β”‚ Lather.Server β”‚ β”‚ Phoenix/Plug β”‚
β”‚ β”‚ β”‚ β”‚ β”‚ β”‚
β”‚ DynamicClient │◄──►│ Service DSL │◄──►│ HTTP Integrationβ”‚
β”‚ WSDL Analysis β”‚ β”‚ WSDL Generation β”‚ β”‚ Request Routing β”‚
β”‚ Type Mapping β”‚ β”‚ Operation Dispatchβ”‚ β”‚ Middleware β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚ β”‚ β”‚
β–Ό β–Ό β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Core Infrastructure β”‚
β”‚ β”‚
β”‚ HTTP Transport β”‚ XML Processing β”‚ Error Handling β”‚ Auth β”‚
β”‚ (Finch) β”‚ (SweetXML) β”‚ (Structured) β”‚ (WS-*) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```
## πŸ”§ Advanced Usage
### Enterprise Authentication
```elixir
# WS-Security with UsernameToken
username_token = Lather.Auth.WSSecurity.username_token("user", "pass", :digest)
security_header = Lather.Auth.WSSecurity.security_header(username_token)
{:ok, client} = Lather.DynamicClient.new(wsdl_url,
soap_headers: [security_header],
ssl_options: [verify: :verify_peer]
)
```
### Complex Data Structures
```elixir
{:ok, response} = Lather.DynamicClient.call(client, "CreateOrder", %{
"order" => %{
"customer" => %{
"name" => "John Doe",
"email" => "john@example.com"
},
"items" => [
%{"sku" => "ITEM001", "quantity" => 2},
%{"sku" => "ITEM002", "quantity" => 1}
],
"shipping" => %{
"method" => "express",
"address" => %{
"street" => "123 Main St",
"city" => "Portland",
"state" => "OR",
"zip" => "97201"
}
}
}
})
```
### Error Handling
```elixir
case Lather.DynamicClient.call(client, "Operation", params) do
{:ok, response} ->
handle_success(response)
{:error, %{type: :soap_fault} = fault} ->
Logger.error("SOAP Fault: #{fault.fault_string}")
handle_soap_fault(fault)
{:error, %{type: :http_error} = error} ->
Logger.error("HTTP Error #{error.status}")
handle_http_error(error)
{:error, %{type: :transport_error} = error} ->
if Lather.Error.recoverable?(error) do
schedule_retry()
else
handle_fatal_error(error)
end
end
```
## πŸ§ͺ Testing
```bash
# Run all tests (excludes external API tests by default)
mix test
# Run with external API tests (hits real SOAP services - use sparingly!)
mix test --include external_api
# Run with coverage
mix test --cover
# Run specific test files
mix test test/lather/xml/parser_test.exs
```
### External API Tests
By default, tests that call external SOAP services are excluded to avoid:
- Overloading public APIs
- Network-dependent test failures
- Slow test runs
External API tests validate the library against real-world services like:
- National Weather Service (document/encoded style)
- Country Info Service (document/literal style)
**Use external API tests responsibly:**
- Only when making significant SOAP-related changes
- Before releases
- When investigating service-specific issues
```bash
# Enable external API tests (be considerate!)
mix test --include external_api
```
## πŸ”§ Configuration
```elixir
# config/config.exs
config :lather,
default_timeout: 30_000,
ssl_verify: :verify_peer,
finch_pools: %{
default: [size: 25, count: 1]
}
# Configure Finch for optimal performance
config :lather, :finch,
pools: %{
"https://api.example.com" => [
size: 25,
protocols: [:http2, :http1]
]
}
```
## 🀝 Contributing
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Add tests for your changes
4. Ensure all tests pass (`mix test`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
## πŸ“„ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## πŸ™ Acknowledgments
- Built with [Finch](https://hex.pm/packages/finch) for HTTP transport
- XML parsing powered by [SweetXml](https://hex.pm/packages/sweet_xml)
- Inspired by the SOAP libraries of other ecosystems
## πŸ“ž Support
- πŸ“– [Documentation](https://hexdocs.pm/lather)
- πŸ› [Issues](https://github.com/markcotner/lather/issues)
- πŸ’¬ [Discussions](https://github.com/markcotner/lather/discussions)
## πŸ—ΊοΈ Roadmap
### v1.1.0 (Next Release)
- [ ] MTOM/XOP binary attachments
- [ ] Enhanced WS-Security features (XML Signature, Encryption)
- [ ] Performance optimizations and benchmarking
- [ ] Additional server examples and templates
### v1.2.0
- [ ] WS-Addressing and WS-ReliableMessaging
- [ ] OpenAPI 3.0 SOAP extension support
- [ ] GraphQL-style query interface for SOAP
- [ ] Advanced caching strategies
### Future Releases
- [ ] Service mesh integration patterns
- [ ] gRPC interoperability layer
- [ ] Kubernetes-native deployment tools
- [ ] Advanced monitoring and observability
---
**Lather v1.0.1** - Making SOAP integration in Elixir as smooth as possible! 🧼✨
*Released January 2025 with complete SOAP 1.2 support and multi-protocol capabilities.*