savon soap attributes - soap

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.

Related

constructing request data for SOAP endpoint without WSDL

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" }}}
]
}
)

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")
}

Call soap web-service in mobilefirst hybrid app

I'm attempting to call SOAP Web-Service in hybrid app. How should I form SOAP message correctly if the back-end service displays the next error in log:
Caused by: com.ibm.websphere.security.WSSecurityException: Exception
org.apache.axis2.AxisFault: CWWSS7509W: The received SOAP request
message is rejected becasue it does not correctly specify SOAP action
and WS-Addressing action while there is at least one PolicySet
attachment at operation level of the
TestServiceService.TestServicePort service. ocurred while running
action:
com.ibm.ws.wssecurity.handler.WSSecurityConsumerHandler$1#9b5addf6 at
com.ibm.ws.security.context.ContextImpl.runWith(ContextImpl.java:394)
at
com.ibm.ws.wssecurity.platform.websphere.auth.WSSContextImpl.runWith(WSSContextImpl.java:65)
... 35 more
This is content of js file in adapter
function getToken(){
var token = WL.Server.getActiveUser().attributes.LtpaToken;
var fulltoken = "LtpaToken2=" + token;
return fulltoken;
}
function callService(){
WL.Logger.warn("INSIDE callService "+getToken());
var path="checkauth/TestServiceService";
var request=
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:q0="http://provider.ws/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<q0:callService />
</soapenv:Body>
</soapenv:Envelope>;
var input = {
method : 'post',
returnedContentType : 'xml',
path : path,
body: {
content: request.toString(),
contentType: 'text/xml; charset=utf-8',
},
headers: {"Cookie": getToken()}
};
var result= WL.Server.invokeHttp(input);
return result;
}
This is SOAP Envelope which was displayed via TCP/IP Monitor:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<s:Security xmlns:s="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:w2="http://www.ibm.com/websphere/appserver/tokentype" soapenv:mustUnderstand="1">
<u:Timestamp>
<u:Created>2015-08-10T13:18:56.644Z</u:Created>
</u:Timestamp>
<s:BinarySecurityToken ValueType="w2:LTPAv2" u:Id="ltpa_20">dt8G5gZ9PpZ/Ea5oXr6EQd8dpmfXKiqeXiShPlpSWntK59hUzyoDNX9TKq1nFLfxUEJyJdjMxoG7EVxw8Q1zhyZdYhTXnsMkNVqScvSsPpX7ln/ad+/WAHqaaFymD8XtVEsjOlezQDarPaUmnKAQRUSrLkRnL5B1MoCclTe129Oojg8o+hACgDKjuvPnvL8jaf45wNiou6Il5ZOayBcoHpNehI7i2hADa4fTKzX/T69OPnsZOyWYrNosdezNd24b61vs85k2YK26rLTp5dkEp8f3mwKZBwOOK4z1wQdiAXJf6kQvzR22SfFitbJA5MStlBcovHAvB5T+J5Ip80/kI5BPa2ogoufd9HZAdKTNII8cHpHBN2Ub/+atzg1L7EhIWuzO1BPI62KoU/hPqAHn3uGCGrbIILesKx0TPvlgmU4Bg54H9prC0I8hgXbO1HLuz4M5DNE5ASFbH0W3LJ/UU7BGXJs6iJmfAfJtQ+ip5ZFHlLItZA+ca2LkVWmyD/xKVxyxHE1uDz8zV/CfV9Km0T+8FTA0Cfi/PIb5KiAagdrmqtw6GuJDbSCsC3sdh21G/cA3Y0p/f+rhDw8m/e17y1cEuq9HOBharwn7ET3wO30V4D4rGoLhd4QsN6X1z89gZmZVaI6J9urpPAEiSndmyQ==</s:BinarySecurityToken>
</s:Security>
<wsa:To>http://X.X.X.X:9082/checkauth/TestServiceService</wsa:To>
<wsa:MessageID>urn:uuid:5d1f8656-5550-40d2-9f39-c58f57279489</wsa:MessageID>
<wsa:Action>http://provider.ws/TestServiceDelegate/callServiceRequest</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ns2:callService xmlns:ns2="http://provider.ws/"/>
</soapenv:Body></soapenv:Envelope>
The body consists of a single line, and this makes the scenario a peculiar one, as well as raises a question is this is meant to work at all.
I can suggest two things:
You can attempt to parse your WSDL file with the SOAPUI application; it should show you how the SOAP envelope is supposed to look like
Use the Service Discovery feature in MobileFirst Studio that can generate the adapter for you with a ready SOAP envelope. Read more how to use this feature, here: http://www-01.ibm.com/support/knowledgecenter/SSHS8R_7.0.0/com.ibm.worklight.dev.doc/dev/c_using_service_discovery_wizard_to_explore_backend-services.html

Need help recreating a SOAP request in groovy-wslite

I had issues getting the most basic of actions working, but finally figured it out. You can see what I did here
With that working I'm moving onto the subsequent action and once again running into issues. Here is what the XML SOAP Request looks like which works (generated in PHP)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:ws.company.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="ws.company.com">
<SOAP-ENV:Header>
<ns2:SessionHeader>
<ns2:sessionId>theLoginSessionID</ns2:sessionId>
</ns2:SessionHeader>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:triggerCampaignMessage>
<ns1:campaign>
<ns1:folderName>theFolder</ns1:folderName>
<ns1:objectName>theObject</ns1:objectName>
</ns1:campaign>
<ns1:recipientData>
<ns1:recipient>
<ns1:listName>
<ns1:folderName>theFolder</ns1:folderName>
<ns1:objectName>theObject</ns1:objectName>
</ns1:listName>
<ns1:emailAddress>person#company.com</ns1:emailAddress>
</ns1:recipient>
<ns1:optionalData>
<ns1:name>order_number</ns1:name>
<ns1:value>231</ns1:value>
</ns1:optionalData>
</ns1:recipientData>
</ns1:triggerCampaignMessage>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The code I am trying to use is this
#Grab(group='com.github.groovy-wslite', module='groovy-wslite', version='0.8.0')
import wslite.soap.*
def client = new SOAPClient('https://company/services/WebService')
def response = client.send(SOAPAction:'https://company/services/WebService/') {
body {
login('xmlns':'urn:ws.company.com') {[
username("username"),
password("password")
]}
}
}
theSession = response.envelope
try {
response = client.send(SOAPAction:'https://company/services/WebService/') {
header {
SessionHeader('xmlns':'urn:ws.company.com') {
sessionId(theSession)
}
}
body {
triggerCampaignMessage('xmlns':'urn:ws.company.com') {[
campaign {[
folderName("theFolder"),
objectName("theObject")
]},
recipientData {[
recipient {[
listName {[
folderName("theFolder"),
objectName("theObject")
]},
emailAddress("person#company.com")
]},
optionalData {[
name("order_number"),
value("1234567890")
]}
]}
]}
}
}
} catch (SOAPFaultException sfe) {
println "fault string :" + sfe.message // faultcode/faultstring for 1.1 or Code/Reason for 1.2
println "envelope :" + sfe.text // prints SOAP Envelope
println "status code :" + sfe.httpResponse.statusCode
println sfe.fault.detail.text()
}
I'm pretty sure that the first send to the login is working since it's the code that works all on it's own and the error message I am receiving is
Unexpected subelement {urn:ws.company.com}emailAddress
So I'm guessing I'm okay up to that point, but can't figure it out. Based on the XML that works can anyone help me get my groovy code to work?
EDIT
Here's the response envelope from the login call
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<loginResponse xmlns="urn:ws.company.com">
<result>
<sessionId>string here</sessionId>
</result>
</loginResponse>
</soapenv:Body>
</soapenv:Envelope>
The problem may be with theSession = response.envelope which will set theSession to the entire SOAP response from the first call to login. My guess is that you really need to grab a token value from an element within the login response message, for example:
theSession = response.loginResponse.result.sessionId.text()
If you can provide a sample of the result from the call to login I can try to provide a more exact answer.