166 lines
6.2 KiB
Rust
166 lines
6.2 KiB
Rust
use std::collections::BTreeMap;
|
|
|
|
use roxmltree::Document;
|
|
|
|
use crate::{SoapAdapterError, SoapOperationSummary, SoapPortSummary, SoapServiceSummary};
|
|
|
|
#[derive(Clone, Debug)]
|
|
struct BindingSummary {
|
|
soap_version: String,
|
|
operations: Vec<SoapOperationSummary>,
|
|
}
|
|
|
|
pub fn inspect_wsdl(payload: &[u8]) -> Result<Vec<SoapServiceSummary>, SoapAdapterError> {
|
|
let text = std::str::from_utf8(payload).map_err(|_| SoapAdapterError::EmptyBody)?;
|
|
let document = Document::parse(text)?;
|
|
let bindings = collect_bindings(&document);
|
|
|
|
let services = document
|
|
.descendants()
|
|
.filter(|node| node.is_element() && node.tag_name().name() == "service")
|
|
.map(|service| {
|
|
let service_name = service.attribute("name").unwrap_or_default().to_owned();
|
|
let ports = service
|
|
.children()
|
|
.filter(|node| node.is_element() && node.tag_name().name() == "port")
|
|
.map(|port| {
|
|
let port_name = port.attribute("name").unwrap_or_default().to_owned();
|
|
let binding_name = local_name(port.attribute("binding").unwrap_or_default());
|
|
let endpoint = port
|
|
.descendants()
|
|
.find(|node| node.is_element() && node.tag_name().name() == "address")
|
|
.and_then(|node| node.attribute("location"))
|
|
.map(ToOwned::to_owned);
|
|
let binding = bindings
|
|
.get(&binding_name)
|
|
.cloned()
|
|
.unwrap_or(BindingSummary {
|
|
soap_version: "soap_11".to_owned(),
|
|
operations: Vec::new(),
|
|
});
|
|
|
|
SoapPortSummary {
|
|
port_name,
|
|
binding_name,
|
|
endpoint,
|
|
soap_version: binding.soap_version,
|
|
operations: binding.operations,
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
SoapServiceSummary {
|
|
service_name,
|
|
ports,
|
|
}
|
|
})
|
|
.collect();
|
|
|
|
Ok(services)
|
|
}
|
|
|
|
fn collect_bindings(document: &Document<'_>) -> BTreeMap<String, BindingSummary> {
|
|
document
|
|
.descendants()
|
|
.filter(|node| node.is_element() && node.tag_name().name() == "binding")
|
|
.map(|binding| {
|
|
let name = binding.attribute("name").unwrap_or_default().to_owned();
|
|
let soap_binding = binding
|
|
.children()
|
|
.find(|node| node.is_element() && node.tag_name().name() == "binding");
|
|
let soap_namespace = soap_binding
|
|
.and_then(|node| node.tag_name().namespace())
|
|
.unwrap_or("http://schemas.xmlsoap.org/wsdl/soap/");
|
|
let soap_version = if soap_namespace.contains("soap12") {
|
|
"soap_12"
|
|
} else {
|
|
"soap_11"
|
|
}
|
|
.to_owned();
|
|
let binding_style = soap_binding
|
|
.and_then(|node| node.attribute("style"))
|
|
.map(|value| match value {
|
|
"rpc" => "rpc_literal",
|
|
_ => "document_literal",
|
|
})
|
|
.unwrap_or("document_literal")
|
|
.to_owned();
|
|
let operations = binding
|
|
.children()
|
|
.filter(|node| node.is_element() && node.tag_name().name() == "operation")
|
|
.map(|operation| SoapOperationSummary {
|
|
operation_name: operation.attribute("name").unwrap_or_default().to_owned(),
|
|
soap_action: operation
|
|
.children()
|
|
.find(|node| node.is_element() && node.tag_name().name() == "operation")
|
|
.and_then(|node| node.attribute("soapAction"))
|
|
.map(ToOwned::to_owned),
|
|
binding_style: operation
|
|
.children()
|
|
.find(|node| node.is_element() && node.tag_name().name() == "operation")
|
|
.and_then(|node| node.attribute("style"))
|
|
.map(|value| match value {
|
|
"rpc" => "rpc_literal",
|
|
_ => "document_literal",
|
|
})
|
|
.unwrap_or(binding_style.as_str())
|
|
.to_owned(),
|
|
})
|
|
.collect();
|
|
|
|
(
|
|
name,
|
|
BindingSummary {
|
|
soap_version,
|
|
operations,
|
|
},
|
|
)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
fn local_name(value: &str) -> String {
|
|
value.rsplit(':').next().unwrap_or(value).to_owned()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::inspect_wsdl;
|
|
|
|
#[test]
|
|
fn parses_services_ports_and_operations_from_wsdl() {
|
|
let services = inspect_wsdl(
|
|
br#"<?xml version="1.0"?>
|
|
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
|
|
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
|
|
xmlns:tns="urn:crm"
|
|
targetNamespace="urn:crm">
|
|
<binding name="LeadBinding" type="tns:LeadPortType">
|
|
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
|
|
<operation name="CreateLead">
|
|
<soap:operation soapAction="urn:createLead"/>
|
|
</operation>
|
|
</binding>
|
|
<service name="LeadService">
|
|
<port name="LeadPort" binding="tns:LeadBinding">
|
|
<soap:address location="https://soap.example.com/lead"/>
|
|
</port>
|
|
</service>
|
|
</definitions>"#,
|
|
)
|
|
.unwrap();
|
|
|
|
assert_eq!(services[0].service_name, "LeadService");
|
|
assert_eq!(services[0].ports[0].port_name, "LeadPort");
|
|
assert_eq!(services[0].ports[0].soap_version, "soap_11");
|
|
assert_eq!(
|
|
services[0].ports[0].operations[0].operation_name,
|
|
"CreateLead"
|
|
);
|
|
assert_eq!(
|
|
services[0].ports[0].operations[0].soap_action.as_deref(),
|
|
Some("urn:createLead")
|
|
);
|
|
}
|
|
}
|