constructing request data for SOAP endpoint without WSDL - soap

The endpoint URL looks like this (not an actual url)
https://webservices.abcde.com/ThirdParty/PostData.V55.ashx/ProcessRequest
It does not have a WSDL, and in the documentation, there's a sample request XML.
It's huge. I am adding the first couple lines from it below.
<MESSAGE xmlns:agentnet="http://services.abcde.com/entity/agentnet/v2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.mismo.org/residential/2009/schemas/v32">
<ABOUT_VERSIONS>
<ABOUT_VERSION>
<AboutVersionIdentifier>ClientSystem</AboutVersionIdentifier>
<DataVersionIdentifier>1.0</DataVersionIdentifier>
<DataVersionName>ASDFSFD</DataVersionName>
</ABOUT_VERSION>
</ABOUT_VERSIONS>
<DEAL_SETS>
<DEAL_SET>
<DEALS>
<DEAL>
<PARTIES/>
<SERVICES>
<SERVICE>
<SERVICE_PRODUCT>
<SERVICE_PRODUCT_REQUEST>
<EXTENSION>
<OTHER>
<agentnet:AGENTNET_PRODUCT_REQUEST>
<agentnet:AgentNetServiceType>GET_DATA</agentnet:AgentNetServiceType>
<agentnet:AGENTNET_GET_DATA>
<agentnet:GetRequestType>ACCOUNTS</agentnet:GetRequestType>
</agentnet:AGENTNET_GET_DATA>
</agentnet:AGENTNET_PRODUCT_REQUEST>
...
...
...
...
... (the XML is huge)
With other endpoints, I was able to use a SOAP library like savon to generate the XML payload using a small Ruby Hash(dictionary).
I assume that was possible because those were WSDLs?
Would it be possible to generate the payload the same way by passing only some essential data (for example, GET_DATA and ACCOUNTS in the example), or should I manually construct the XML payload strings manually (maybe using some XML library)?
I really want to avoid manually constructing XML payloads since the code will not be readable and will be hard to work with in general. Is there a way to avoid it?

You can definitively create a Savon client without using the WSDL. I personally like this better because I believe it's more performant.
You have to define endpoint and namespacewhen you create your client, like this fictitious example:
require 'savon'
c = Savon.client(endpoint: "http://www.example.com",
namespace: "urn:ns.example.com",
log: true,
log_level: :debug,
pretty_print_xml: true)
r = c.call(:call,
:message => {
:InquiryParam => [
{"crmParam" => 123,
:attributes! => { "crmParam" => { "name" => "AccountNumber" }}},
{"crmParam" => 456,
:attributes! => { "crmParam" => { "name" => "history" }}}
]
}
)

Related

How To Preserve SOAP Headers to use with Mule 4 Web Service Consumer?

I have used an Existing WSDL to create an Experience API where I am performing several steps before either:
Passing the SOAP Request Message as-is to the Original Backend System using Mule4 Web Service Consumer or
Calling another Backend System (RESTful API) and transforming the Response to match the Expected SOAP Response Message
Sample Message
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="urn:Acme/PublicService/V1" xmlns:ns0="urn:/Acme/BasicDataPublicService/V1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header>
<a:Action s:mustUnderstand="1" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">urn:Acme/PublicService/V1/PublicService/SetCustomer</a:Action>
<a:MessageID xmlns:a="http://www.w3.org/2005/08/addressing">urn:uuid:4afe0693-adea-4ede-bec9-10b694708d85</a:MessageID>
<a:ReplyTo xmlns:a="http://www.w3.org/2005/08/addressing">
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<VsDebuggerCausalityData xmlns="http://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink">uIDPo8KxyyGpakdIj8o84JOeAMsAAAAAQBkt3vfAK0C4dDgn3rAKx/iXgqYosnhKv/OHgph9cXoACQAA</VsDebuggerCausalityData>
<a:To s:mustUnderstand="1" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">http://316.820.517.311:36990/PublicInterface/Service</a:To>
<AuthorizationToken xmlns="urn:Acme/Authorization/V1" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<CultureName>uk-UK</CultureName>
<OnBehalfOf i:nil="true"></OnBehalfOf>
<Password>****</Password>
<UserName>sa-ACME</UserName>
</AuthorizationToken>
</env:Header>
<env:Body>
<ns:SetCustomer>Muli-Tier Message</ns:SetCustomer>
</env:Body>
</env:Envelope>
According to Mulesoft KB; this requires additional Transformation steps in the beginning of the flow. Passing the SOAP Body is not an issue; the SOAP Header is a pickle, however. Especially since the KB document is hard-coding the values; while in my case these need to be dynamic (i.e. coming from the Original SOAP Request message).
I tried mapping the Header Parameters to a Variable as described but I cannot seem to get to it.
Option 1 Mapping the Header Elements to Children Attributes of a Variable, results in the Variable Storing Null
%dw 2.0
output application/xml writeDeclaration=false, writeNilOnNull=true
ns ns0 http://www.w3.org/2005/08/addressing
ns s http://www.w3.org/2003/05/soap-envelope
---
headers: {
ns0#Action #(s#mustUnderstands: payload.headers.Action.#mustUnderstands): payload.headers.Action as String default null,
ns0#MessageID: payload.headers.MessageID as String default null,
ns0#ReplyTo: {
ns0#Address: payload.headers.ReplyTo.Address as String default null
},
VsDebuggerCausalityData: payload.headers.VsDebuggerCausalityData as String default null,
ns0#To #(s#mustUnderstands: payload.headers.To.#mustUnderstands): payload.headers.To as String default null,
AuthorizationToken: {
CultureName: payload.headers.AuthorizationToken.CultureName as String default null,
OnBehalfOf: payload.headers.AuthorizationToken.OnBehalfOf as String default null,
Password: payload.headers.AuthorizationToken.Password as String default null,
UserName: payload.headers.AuthorizationToken.UserName as String default null
}
}
Option 2 Mapping payload.headers to a Variable, results in extra tags; and losing the XML Tag attributes
%dw 2.0
output application/xml writeDeclaration=false, writeNilOnNull=true
ns ns0 http://www.w3.org/2005/08/addressing
ns s http://www.w3.org/2003/05/soap-envelope
---
headers: payload.headers
It seems there is an issue with the WebService Consumer connector when generating the SOAP Envelope Header.
Instead of using the WebService Consumer Connector, try using an HTTP Request Connector passing the payload as-is.
I will come back and add more details on the answer, but here is how to solve that issue:
%dw 2.0
output application/xml writeDeclaration=false, writeNilOnNull=true
---
headers: (payload.headers.headers mapObject (value, key) -> {
(value)
})

Perl: Connecting to Walmart API to load items Unsupported Media type error

We are trying using the marketplace.walmartapis.com api to bulk list items and it is returning unsupported media type for xml item feed
We are using Perl and are able to perform other commands to update inventory and pricing, check orders, lookup feeds and skus etc. The token SHOULD be good, as we ran two calls back to back using the same token and the first call (item inventory) worked as expected before trying to send the xml file. It is only when we try to send the xml batch file that we run into the problem.
We assume the issue is with the header/authentication and have tried many different combinations. But obviously we are missing something.
What is being sent:
POST https://marketplace.walmartapis.com/v3/feeds?feedType=item HTTP/1.1
Headers:
Authorization: Basic MDc4......
Content-Length: 2277
Accept: application/xml
Content-Type: multipart/formdata
Host: marketplace.walmartapis.com
WM_QOS.CORRELATION_ID: TB123456V32
WM_SEC.ACCESS_TOKEN: eyJra...
WM_SVC.NAME: Walmart Marketplace
Boundary: 1234ran4321dom5678boundary
formdata payload:
--1234ran4321dom5678boundary
Content-Disposition: formdata; name="xml"
Content-length: 2151
<?xml version="1.0" encoding="UTF-8"?>
<MPItemFeed xmlns="http://walmart.com/">
<MPItemFeedHeader>
<version>3.1</version>
<requestBatchId>......</requestBatchId>
<feedDate>2019-02-18T19:45:17</feedDate>
<mart>WALMART_US</mart>
</MPItemFeedHeader>
<MPItem>
<processMode>CREATE</processMode>
<sku>....</sku>
<productIdentifiers>
<productIdentifier>
<productIdType>UPC</productIdType>
<productId>..........</productId>
</productIdentifier>
</productIdentifiers>
<MPProduct>
<productName>.....................</productName>
<category>
<SportAndRecreation>
<SportAndRecreationOther>
<shortDescription>........................</shortDescription>
<keyFeatures>
<keyFeaturesValue>I...............</keyFeaturesValue>
</keyFeatures>
<brand>............</brand>
<manufacturer>.............</manufacturer>
<manufacturerPartNumber>.............</manufacturerPartNumber>
<modelNumber>..</modelNumber>
<mainImageUrl>............</mainImageUrl>
<count>1</count>
<isProp65WarningRequired>No</isProp65WarningRequired>
<sportsLeague>
<sportsLeagueValue>....</sportsLeagueValue>
</sportsLeague>
<keywords>................</keywords>
<isMemorabilia>......</isMemorabilia>
<isCollectible>...........</isCollectible>
</SportAndRecreationOther>
</SportAndRecreation>
</category>
</MPProduct>
</MPOffer>
<price>21.95</price>
<StartDate>2019-02-23T19:45:17</StartDate>
<EndDate>2019-04-19T19:45:17</EndDate>
<ShippingWeight>
<measure>2</measure>
<unit>lb</unit>
</ShippingWeight>
<ProductTaxCode>2038345</ProductTaxCode>
</MPOffer>
</MPItem>
</MPItemFeed>
--1234ran4321dom5678boundary--
back from Walmart.pm
$HASH = {
"error" => {
"category" => "DATA",
"causes" => {},
"code" => "UNSUPPORTED_MEDIA_TYPE.GMP_GATEWAY_API",
"errorIdentifiers" => {},
"info" => "Unsupported Media Type.",
"severity" => "ERROR"
},
"xmlns:ns2" => "http://walmart.com/"
};
Any pointers in the right direction would be appreciated.
Thanks for everyone that looked. The problem seems to be solved
It was one minor typo
multipart/formdata needed to be multipart/form-data
With that change now we are getting a feed id that shows up in the Seller area

How to get http request header info from the server side with spray RestAPI

I am new to Scala and Spray. I have written a simple REST API according to the instructions given in this blog post.
http://www.smartjava.org/content/first-steps-rest-spray-and-scala
And all are working as expected.
Now I want to modify the program to print the HTTP headers like Encoding, Language, remote-address, etc.. I would like to print all the header information (purpose is to log these information)
But I could not find a proper documentation or examples. Could anyone please help me to get this done.
If you need to extract a specific header:
optionalHeaderValueByName("Encoding") { encodingHeader =>
println(encodingHeader)
complete("hello")
}
alternatively you can access the raw request object and directly extractive the headers. Here's a custom directive that logs all the headers:
def logHeaders(): Directive0 = extract(_.request.headers).map(println)
Usage
logHeaders() {
complete("hello")
}
Here's how I got it working.
Directive:
def logHeaders(innerRoute: Route): (RequestContext => Unit) = extract(_.request.headers) { headers =>
headers.foreach(h => logger.info("header: {} = {}", h.name, h.value))
innerRoute
}
Usage:
logHeaders() {
complete("hello")
}

How do I use Play2 Iteratees to consume streaming HTTP with different event names?

I want a functional way of consuming server-sent events (SSE) over HTTP (or streaming HTTP as some call it). Through examples (Scala: Receiving Server-Sent-Events) I've found that Play2 Iteratees work well with its WS client when the event name is set to "message." Here is what the "message" stream looks like:
GET http://streaming.server.com/temperature
event: message
data: {"room":"room1","temp":71,"time":"2015-05-06T00:23:10.203+02:00"}
event: message
data: {"room":"room1","temp":70,"time":"2015-05-06T00:31:18.873+02:00"}
...
And here's what my web client looks like:
import com.ning.http.client.AsyncHttpClientConfig.Builder
import play.api.libs.iteratee.Iteratee
import play.api.libs.iteratee.Execution.Implicits.defaultExecutionContext
import play.api.libs.ws.ning.NingWSClient
object Client extends App {
val client = new NingWSClient(new Builder().build())
def print = Iteratee.foreach { chunk: Array[Byte] => println(new String(chunk)) }
client.url("http://streaming.server.com/temperature").get(_ => print)
}
with some output that it printed to my console:
$ sbt run
[info] Running Client
data: {"room":"room1","temp": 70, "time":"2015-05-06T00:31:14.193+02:00"}
data: {"room":"room1","temp": 70, "time":"2015-05-06T00:31:18.873+02:00"}
...
But when I set "event" to some other value than "message" the Iteratee immediately returns the Done signal just after reading the first value and then stops the stream. The spec I'm required to satisfy uses "event":"put". Below is an example of what the "put" stream looks like:
GET http://streaming.server.com/temperature
event: put
data: {"room":"room1","temp":71,"time":"2015-05-06T00:39:14.281+02:00"}
event: put
data: {"room":"room1","temp":70,"time":"2015-05-06T00:39:18.778+02:00"}
...
I discovered this when I added an onComplete() handler at the end and matched on a Success case like so:
client.url("http://streaming.server.com/temperature").get(_ => print).onComplete {
case Success(s) => println(s)
case Failure(s) => println(f.getMessage)
}
This code now prints:
$ sbt run
[info] Running Client
data: {"room":"room1","temp": 71, "time":"2015-05-06T00:39:14.281+02:00"}
Done((),Empty)
So far, I've only had success with the Jersey library for Java whose semantics is very similar to the EventSource JavaScript client; however it doesn't compose and appears to only support single-threaded consumption of SSEs. I would much rather use the Play2 WS+Iteratee libraries. How can I achieve this?

savon soap attributes

Am trying to query netsuite api for currencies. The following soap request works for me in SOAP UI client. But i am having a hard time trying to get the same working with ruby's savon gem version 0.9.7.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:messages_2012_2.platform.webservices.netsuite.com" xmlns:urn1="urn:core_2012_2.platform.webservices.netsuite.com">
<soapenv:Header>
<urn:passport>
<urn1:email>xxx#abc.com</urn1:email>
<urn1:password>xxx</urn1:password>
<urn1:account>xxx</urn1:account>
</urn:passport>
</soapenv:Header>
<soapenv:Body>
<urn:getAll>
<urn:record recordType="currency"/>
</urn:getAll>
</soapenv:Body>
</soapenv:Envelope>
Basically i am not able to set the attribute on the urn:record element. The following is not working:
response = client.request :urn, :get_all do
soap.body = { "urn:record" => { :attributes! => { "recordType" => "currency" } } }
end
Please advise.
As explained on http://savonrb.com the key in the attributes! hash has to match the XML tag. You want to write something like this:
response = client.request :urn, :get_all do
soap.body = {'urn:record'=>'',
:attributes!=>{'urn:record'=>{'recordType'=>'currency'}}
}
end
Please let us know whether this solves it for you.
Double-check the raw soap request. :get_all may need to be "getAll" to have savon take you literally; it may be changing it to GetAll
In new versioin of savon you can place :attributes in the local context for the operation tag:
#interaction_client.call(:retrieve_interaction, message: message_hash, :attributes => { 'attachmentInfo' => include_attachments.to_s })
In this case, the attachmentInfo attribute will be placed into the main operation tag linked with operation, in this example this would be the ns:RetrieveInteractionRequest tag.
Please note that the syntax does not contains the exclamation mark.