-
Notifications
You must be signed in to change notification settings - Fork 65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for SOAP #4618
Add support for SOAP #4618
Comments
Following meeting notes from release planning meeting. Support for consuming SOAP services in Ballerina
|
Sample SOAP message. <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://www.example.com/webservice">
<soapenv:Header>
<!-- Optional SOAP header elements go here -->
</soapenv:Header>
<soapenv:Body>
<web:HelloRequest>
<web:Name>John Doe</web:Name>
</web:HelloRequest>
</soapenv:Body>
</soapenv:Envelope> |
Sample MTOM message.
|
Sample WSDL. <?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://www.example.com/weather">
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.com/weather">
<xs:element name="City" type="xs:string"/>
<xs:element name="WeatherForecast" type="xs:string"/>
</xs:schema>
</wsdl:types>
<wsdl:message name="GetWeatherRequest">
<wsdl:part name="City" element="tns:City"/>
</wsdl:message>
<wsdl:message name="GetWeatherResponse">
<wsdl:part name="WeatherForecast" element="tns:WeatherForecast"/>
</wsdl:message>
<wsdl:portType name="WeatherServicePortType">
<wsdl:operation name="GetWeather">
<wsdl:input message="tns:GetWeatherRequest"/>
<wsdl:output message="tns:GetWeatherResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="WeatherServiceBinding" type="tns:WeatherServicePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="GetWeather">
<soap:operation soapAction="http://www.example.com/weather/GetWeather"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="WeatherService">
<wsdl:port name="WeatherServicePort" binding="tns:WeatherServiceBinding">
<soap:address location="http://www.example.com/weather"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
|
Above SOAP payload converted to JSON. {
"soapenv:Envelope":{
"soapenv:Header":{
},
"soapenv:Body":{
"web:HelloRequest":{
"web:Name":"John Doe",
"@xmlns:web":"http://www.example.com/webservice"
}
},
"@xmlns:soapenv":"http://schemas.xmlsoap.org/soap/envelope/",
"@xmlns:web":"http://www.example.com/webservice"
}
} |
A possible client. public client class SoapClient {
public function init(string url, http:ClientConfiguration config) {}
remote function sendReceive(xml|mime:Entity[] soap) returns xml|mime:Entity[]|error {}
remote function sendOnly(xml|mime:Entity[] soap) returns error? {}
remote function sendReceiveEnve(SoapEnv soapenv, boolean simpleJsonMapping = true) returns SoapEnv|error {}
remote function sendOnlyEnve(SoapEnv soapenv, boolean simpleJsonMapping = true) returns error? {}
}
public type SoapEnv record {|
xml|record {} header?;
xml|record {} body;
|};
// Is for the MTOM suff
public type Attachment record {|
string name;
byte[]|stream<byte[]>|string content;
|}; |
Just like we have VSCode command to go from JSON to record, we need one to go from XML to record. |
We can use |
|
We are providing two levels of functionality here. The low-level api ( |
I think we can start with the below two clients. public client class BasicClient {
public function init(string url, http:ClientConfiguration config) {}
remote function sendReceive(xml|mime:Entity[] soap) returns xml|mime:Entity[]|error {}
remote function sendOnly(xml|mime:Entity[] soap) returns error? {}
} public client class AdvancedClient {
public function init(string url, http:ClientConfiguration config) {}
remote function sendReceive(SoapEnv soapenv, boolean simpleJsonMapping = true) returns SoapEnv|error {}
remote function sendOnly(SoapEnv soapenv, boolean simpleJsonMapping = true) returns error? {}
}
public type SoapEnv record {|
xml|record {} header?;
xml|record {} body;
|};
// Is for the MTOM suff
public type Attachment record {|
string name;
byte[]|stream<byte[]>|string content;
|}; |
Had a quick chat with @shafreenAnfar on this API. We agreed to go ahead with the Here are some of the things that we discussed:
|
Given the behaviour of the |
SOAP 1.1 & 1.2 clients offer the capability to configure and apply one or multiple security policies during the initiation of a SOAP client connection. These security policies can be applied as follows: SOAP 1.1 Client: UsernameToken and TranportBinding Policyimport ballerina/crypto;
import ballerina/mime;
import ballerina/soap:soap11;
import ballerina/soap:wssec;
public function main() returns error? {
soap11:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
soapSecurity: [
{
username: "username",
password: "password",
passwordType: wssec:TEXT
},
TRANSPORT_BINDING
]
});
xml envelope = xml `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
} SOAP 1.2 Client: Symmetric Binding Policyimport ballerina/crypto;
import ballerina/mime;
import ballerina/soap:soap12;
import ballerina/soap:wssec;
public function main() returns error? {
crypto:KeyStore keyStore = {
path: KEY_STORE_PATH,
password: KEY_PASSWORD
};
crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, KEY_ALIAS, KEY_PASSWORD);
crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, KEY_ALIAS);
soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
soapSecurity: {
signatureAlgorithm: wssec:RSA_SHA256,
encryptionAlgorithm: wssec:RSA_ECB,
bindingKey: privateKey,
verificationKey: publicKey,
x509Token: X509_PUBLIC_CERT_PATH_2
};
});
xml envelope = xml `<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
} |
We decided to use two security configurations for the SOAP client:
import ballerina/crypto;
import ballerina/mime;
import ballerina/soap:soap12;
import ballerina/soap:common;
public function main() returns error? {
crypto:KeyStore clientKeyStore = {
path: X509_KEY_STORE_PATH_2,
password: KEY_PASSWORD
};
crypto:PrivateKey clientPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(clientKeyStore, KEY_ALIAS, KEY_PASSWORD);
crypto:PublicKey clientPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(clientKeyStore, KEY_ALIAS);
crypto:PublicKey serverPublicKey = ...//
soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
inboundSecurity: {
signatureKey: clientPrivateKey,
signatureAlgorithm: wssec:RSA_SHA256,
encryptionKey: serverPublicKey,
encryptionAlgorithm: wssec:RSA_ECB
},
outboundSecurity: {
verificationKey: serverPublicKey,
signatureAlgorithm: wssec:RSA_SHA256,
decryptionKey: clientPrivateKey,
decryptionAlgorithm: wssec:RSA_ECB
}
});
xml envelope = xml `<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
} |
Following our discussion, we've decided to remove the import ballerina/crypto;
import ballerina/mime;
import ballerina/soap;
import ballerina/soap:soap12;
public function main() returns error? {
crypto:KeyStore clientKeyStore = {
path: KEY_STORE_PATH,
password: KEY_PASSWORD
};
crypto:PrivateKey clientPrivateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(clientKeyStore, KEY_ALIAS, KEY_PASSWORD);
crypto:PublicKey clientPublicKey = check crypto:decodeRsaPublicKeyFromTrustStore(clientKeyStore, KEY_ALIAS);
crypto:PublicKey serverPublicKey = ...//
soap12:Client soapClient = check new ("http://www.dneonline.com/calculator.asmx?WSDL",
{
inboundSecurity: {
signatureAlgorithm: soap:RSA_SHA256,
encryptionAlgorithm: soap:RSA_ECB,
signatureKey: clientPrivateKey,
encryptionKey: serverPublicKey,
},
outboundSecurity: {
verificationKey: serverPublicKey,
signatureAlgorithm: soap:RSA_SHA256,
decryptionKey: clientPrivateKey,
decryptionAlgorithm: soap:RSA_ECB
}
});
xml body = xml `<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Body>
<quer:Add xmlns:quer="http://tempuri.org/">
<quer:intA>2</quer:intA>
<quer:intB>3</quer:intB>
</quer:Add>
</soap:Body>
</soap:Envelope>`;
xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
} |
In the previous SOAP APIs, users had to work with a union type of xml|mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add");
if response is xml {
//…
} else {
//…
} However, with the new update, this additional condition is no longer necessary. Users can now directly infer the response type, whether it's xml response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); OR mime:Entity[] response = check soapClient->sendReceive(envelope, "http://tempuri.org/Add"); |
Description:
Need to improve the soap module to facilitate new requirements.
The text was updated successfully, but these errors were encountered: