translation from XML to rest-client for POST request - ruby

The is the XML request via POST I have to make in order to receive a response:
<BackgroundCheck userId="username" password="password">
<BackgroundSearchPackage action="submit" type="demo product">
<ReferenceId>some_id_value</ReferenceId>
<PersonalData>
<PersonName>
<GivenName>John</GivenName>
<MiddleName>Q</MiddleName>
<FamilyName>Test</FamilyName>
</PersonName>
<Aliases>
<PersonName>
<GivenName>Jack</GivenName>
<MiddleName>Quigley</MiddleName>
<FamilyName>Example</FamilyName>
</PersonName>
</Aliases>
<DemographicDetail>
<GovernmentId issuingAuthority="SSN">123456789</GovernmentId>
<DateOfBirth>1973-12-25</DateOfBirth>
</DemographicDetail>
<PostalAddress>
<PostalCode>83201</PostalCode>
<Region>UT</Region>
<Municipality>Salt Lake City</Municipality>
<DeliveryAddress>
<AddressLine>1234</AddressLine>
<StreetName>Main Street</StreetName>
</DeliveryAddress>
</PostalAddress>
<EmailAddress>john#test.com</EmailAddress>
<Telephone>801-789-4229</Telephone>
</PersonalData>
</BackgroundCheck>
</BackgroundSearchPackage>
Using the examples on the rest-client github page I came up with the following translation using rest-client:
response = RestClient.post( 'url',
{
:BackgroundCheck => {
:userID => 'username',
:password => 'password',
},
:BackgroundSearchPackage => {
:action => 'submit',
:type => 'demo'
},
:ReferenceID => 'some_id_value',
:PersonalData => {
:PersonalName => {
:GivenName => 'John',
:MiddleName => 'Q',
:FamilyName => 'Test'
},
:Aliases => {
:GivenName => 'Jack',
:MiddleName => 'Quigly',
:FamilyName => 'Example'
}
},
:DemographicDetail => {
:GovernmentId => {
:issuingAuthority => "SSN"
}, ## where do I enter the SSN?
:DateOfBirth => '1972-12-25'
},
:PostalAddress => {
:PostalCode => '83201',
:Region => 'UT',
:Municipality => 'Salt Lake City',
:DeliveryAddress => {
:AddressLine => '1234',
:StreetName => 'Main Street'
}
},
:EmailAddress => 'john#test.com',
:Telephone => '801-789-4229'
})
Its my first time with XML and the rest-client gem.
My question is did I translate the XML correctly in the POST request?
More specifically how do I handle the GovernmentID and referencing the SSN entry?

First of all, the XML you've provided isn't valid! Your root element starts with BackgroundCheck and ends with BackgroundSearchPackage:
<BackgroundCheck userId="username" password="password">
<BackgroundSearchPackage action="submit" type="demo product">
</BackgroundCheck>
</BackgroundSearchPackage>
In addition, your translation / transformation from XML to Ruby hash is incorrect. If BackgroundCheck is your root element and BackgroundSearchPackage is a child of it, your Ruby hash should look like this (rest-client accepts the string and the symbol notation):
my_xml_hash = {
"BackgroundCheck" => {
"userId"=>"username",
"password"=>"password",
"BackgroundSearchPackage" => {
"action"=>"submit",
"type"=>"demo product",
...
"PersonalData" => { ... },
...
}
}
}
You can access values in a Ruby hash like this:
# string syntax
my_xml_hash['BackgroundCheck']['BackgroundSearchPackage']['PersonalData']['DemographicDetail']['GovernmentId']
=> "123456789"
# symbol syntax
other_xml_hash[:BackgroundCheck][:BackgroundSearchPackage][:PersonalData][:DemographicDetail]['GovernmentId']
=> "123456789"
If I understood you correctly, you want to send XML via a POST request. But if you use the hash syntax, you will not achieve the result, what you probably want, because rest-client will post your data as parameters and not as XML data!
If you need to adjust only GovernmentID and issuingAuthority, I would do it as follows.
require 'rest_client'
# the customized 'GovernmentID'
government_id = '123'
# the customized 'issuingAuthority'
issuing_authority = 'FOO'
xml_template =<<END_OF_XML
<BackgroundCheck userId="username" password="password">
<BackgroundSearchPackage action="submit" type="demo product">
<ReferenceId>some_id_value</ReferenceId>
<PersonalData>
<PersonName>
<GivenName>John</GivenName>
<MiddleName>Q</MiddleName>
<FamilyName>Test</FamilyName>
</PersonName>
<Aliases>
<PersonName>
<GivenName>Jack</GivenName>
<MiddleName>Quigley</MiddleName>
<FamilyName>Example</FamilyName>
</PersonName>
</Aliases>
<DemographicDetail>
<GovernmentId issuingAuthority="#{issuing_authority}">#{government_id}</GovernmentId>
<DateOfBirth>1973-12-25</DateOfBirth>
</DemographicDetail>
<PostalAddress>
<PostalCode>83201</PostalCode>
<Region>UT</Region>
<Municipality>Salt Lake City</Municipality>
<DeliveryAddress>
<AddressLine>1234</AddressLine>
<StreetName>Main Street</StreetName>
</DeliveryAddress>
</PostalAddress>
<EmailAddress>john#test.com</EmailAddress>
<Telephone>801-789-4229</Telephone>
</PersonalData>
</BackgroundSearchPackage>
</BackgroundCheck>
END_OF_XML
# Go to http://requestb.in/ , click on "Create a RequestBin", copy the "Bin URL" and use it for your tests ;-)
response = RestClient.post('http://your.target.tld/your/webservice', xml_template, { content_type: :xml })
puts "Response: #{response.inspect}"
REXML example:
require 'rest_client'
require 'rexml/document'
xml_string =<<END_OF_XML
<BackgroundCheck userId="username" password="password">
<BackgroundSearchPackage action="submit" type="demo product">
<ReferenceId>some_id_value</ReferenceId>
<PersonalData>
<PersonName>
<GivenName>John</GivenName>
<MiddleName>Q</MiddleName>
<FamilyName>Test</FamilyName>
</PersonName>
<Aliases>
<PersonName>
<GivenName>Jack</GivenName>
<MiddleName>Quigley</MiddleName>
<FamilyName>Example</FamilyName>
</PersonName>
</Aliases>
<DemographicDetail>
<GovernmentId issuingAuthority="SSN">123456789</GovernmentId>
<DateOfBirth>1973-12-25</DateOfBirth>
</DemographicDetail>
<PostalAddress>
<PostalCode>83201</PostalCode>
<Region>UT</Region>
<Municipality>Salt Lake City</Municipality>
<DeliveryAddress>
<AddressLine>1234</AddressLine>
<StreetName>Main Street</StreetName>
</DeliveryAddress>
</PostalAddress>
<EmailAddress>john#test.com</EmailAddress>
<Telephone>801-789-4229</Telephone>
</PersonalData>
</BackgroundSearchPackage>
</BackgroundCheck>
END_OF_XML
# Build XML document from string
doc = REXML::Document.new(xml_string)
government_element = REXML::XPath.first(doc, "//GovernmentId")
# Read values:
puts government_element.text
puts government_element.attributes['issuingAuthority']
# OR directly via XPath
puts REXML::XPath.first(doc, "//GovernmentId").text
puts REXML::XPath.first(doc, "//GovernmentId/#issuingAuthority").value
# Write values:
government_element.text = 'my new text value'
government_element.attributes['issuingAuthority'] = 'my new attribute value'
# Go to http://requestb.in/ , click on "Create a RequestBin", copy the "Bin URL" and use it for your tests ;-)
response = RestClient.post('http://your.target.tld/your/webservice', doc.to_s, { content_type: :xml })
puts "Response: #{response.inspect}"
If you need to write complex XML trees, I recommend you to take a look at the following gems:
Nokogiri
LibXml Ruby
XmlSimple
REXML (Ruby built in)
Or use a templating engine like ERB, to simplify it.

Related

Ruby - Webmock: Match URI using regular expression

I'm working with rspec and webmock and I'm looking into stubbing request. I do have a problem when I try to use regex to match the URI.
Everything was working fine when I used the stub below, without matching a specific URI (/.*/)
it "returns nil and stores an error when the response code is not OK" do
stub_request(:get, /.*/).
with(
:headers => insertion_api.send(:default_headers, false).merge('User-Agent'=>'Ruby'),
:body => {}
).
to_return(
:status => Insertion.internal_server_error.to_i,
:body => "{\"message\": \"failure\"}",
:headers => { 'Cookie' => [session_token] }
)
expect(insertion_api.get_iou(uid)).to be_nil
expect(insertion_api.error).to eq("An internal server error occurred")
end
Since I want to be more specific in my test to improve readability, if I try to match a this specific URI:
/insertion_order/012awQQd?fields=name,type&depth=4
using the stub below:
it "returns nil and stores an error when the response code is not OK" do
stub_request(:get, %r{insertion_order/\w+\?fields\=[\w,]+\&depth\=[0-9]}).
with(
:headers => insertion_api.send(:default_headers, false).merge('User-Agent'=>'Ruby'),
:body => {}
).
to_return(
:status => Insertion.internal_server_error.to_i,
:body => "{\"message\": \"failure\"}",
:headers => { 'Cookie' => [session_token] }
)
expect(insertion_api.get_iou(uid)).to be_nil
expect(insertion_api.error).to eq("An internal server error occurred")
end
running the test I've got:
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: GET https://mocktocapture.com/mgmt/insertion_order/0C12345678 with body '{}' with headers {'Accept'=>'application/vnd.xxx.mgmt+json; version=2.0', 'Cookie'=>'y0Urv3ryLon6s3cur1tYT0k3ng0zeh3r3', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:get, "https://mocktocapture.com/mgmt/insertion_order_units/0C12345678").
with(:body => "{}",
:headers => {'Accept'=>'application/vnd.dataxu.mgmt+json; version=2.0', 'Cookie'=>'y0Urv3ryLon6s3cur1tYT0k3ng0zeh3r3', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
registered request stubs:
stub_request(:get, "/insertion_order\/\w+\?fields\=[\w,]+\&depth\=[0-9]/").
with(:body => {},
:headers => {'Accept'=>'application/vnd.xxx.mgmt+json; version=2.0', 'Cookie'=>'y0Urv3ryLon6s3cur1tYT0k3ng0zeh3r3', 'User-Agent'=>'Ruby'})
The regex I've used is correct, but I don't understand why I've got this error message.
The request you got is :
https://mocktocapture.com/mgmt/insertion_order/0C12345678
You have given the regexp :
%r{insertion_order/\w+\?fields\=[\w,]+\&depth\=[0-9]}
In the regexp you have specified with the "\?" that it is mandatory that the request should contain "?" (or a query) after "insertion_order/\w+". In the request you got there aren't any query parameters. That's why it isn't matching the request.
One way you can fix that is to make the part that comes after "insertion_order/\w+" in the regexp optional. I would do it like this :
%r{insertion_order/\w+(\?fields\=[\w,]+\&depth\=[0-9])?}

Passing Hash as Instance Variable in Get Method In Sinatra

I'm creating a metro application for a project. I'm attempting to pass a hash containing the options that a user can select as the key and the url it will redirect to as the value. When I do, sinatra reports:
no implicit conversion of Array into String
Which is strange, because I do not have an array so far in my application. I have tried commenting out the embedded ruby in the index.erb file, but that didn't help. The application is as follows:
require "sinatra"
require "sinatra/reloader"
red = {
"Woodley Park" => '/woodley_park',
"Dupont Circle" => '/dupont_circle',
"Farragut North" => '/farragut_north',
"Metro Center" => '/metro_center',
"Union Station" => '/union_station'
}
turquoise = {
"Crystal City" => '/crystal_city',
"Metro Center" => '/metro_center',
"Shaw-Howard" => '/shaw_howard',
"Beltwater" => '/beltwater'
}
orange = {
"Farragut West" => '/farragut_west',
"McPherson" => '/mcpherson',
"Metro Center" => '/metro_center',
"Federal Triangle" => '/federal_triangle',
"Smithsonsian" => '/smithsonsian',
"L'efant Plaza" => '/lefant_planza'
}
lines = {
"Red" => "/red_line",
"Turquoise" => "/turquoise_line",
"Orange" => "/orange_line"
}
get '/' do
#header = "Welcome to the D.C. Metro! Which line will you be starting at?"
#options = lines
end
And the index.erb looks like this. Again, commenting out the relevant lines did not change the results. Thanks in advance!
<h2><%= #header %></h2>
<% #options.each do |name, link| %>
<a href=<%= link %>><%= name %></a>
<% end %>

How to convert the data to the yml file

I have a Ruby file, "one.rb":
require 'yaml'
e = { "names"=>{"first_name" => "shaik", "last_name" => "farooq"} }
puts e.to_yaml
When I run this it gets executed successfully in the console and outputs:
---
names:
first_name: shaik
last_name: farooq
I want to store the executed data in a file with a "yml" extension. How can I do this from the above file (test.rb)?
It's really simple:
require 'yaml'
e = { "names"=>{"first_name" => "shaik", "last_name" => "farooq"} }
File.write('test.yaml', e.to_yaml)
After running that, a file called "test.yaml" will exist in the current directory that contains:
---
names:
first_name: shaik
last_name: farooq
You can reload that data easily also:
new_e = YAML.load_file('test.yaml')
# => {"names"=>{"first_name"=>"shaik", "last_name"=>"farooq"}}
You can write the yaml to file with:
require 'yaml'
e = { "names"=>{"first_name" => "shaik", "last_name" => "farooq"} }
File.open('your_file_name.yml', 'w') { |f| f.write(e.to_yaml) }

Does OpenStruct has support for nesting ? If Yes, How can I construct XML using Nokogiri? If not, Can this be done using Nokogiri and JSon?

The Earlier solution provided by #Jamie seems to be working fine for only few scenario's. The solution is
require 'nokogiri'
require 'ostruct'
xml = <<-'XML'
<Catalog>
<Interface></Interface>
<Dialog></Dialog>
<Manifest></Manifest>
</Catalog>
XML
collection = [
OpenStruct.new(book: '1984', pen: 'George Orwell'),
OpenStruct.new(book: 'Thinking, Fash and Slow', pen: 'Daniel Kahneman')
]
doc = Nokogiri::XML(xml)
catalog = doc.root
node_set = Nokogiri::XML::NodeSet.new(doc)
collection.each do |object|
book = Nokogiri::XML::Node.new('Book', doc)
book_author = Nokogiri::XML::Node.new('Book_Author', doc)
book.content = object.book
book_author.content = object.pen
node_set << book
node_set << book_author
end
catalog.first_element_child.before(node_set)
puts doc.to_xml
While I have the below XML to be constructed using Nokogiri. I just wanted to know, if this can't be done using OpenStruct. Could this be done using Json? If Yes, Can you please suggest me how to construct this using Nokogiri and Json.
Below is this XML, I want to construct...
<CatalogOrder>
<CatalogStoreNumber>657758</CatalogStoreNumber>
<CatalogStoreId>CTH6536</CatalogStoreId>
<CatalogStoreLocation>UnitedStates</CatalogStoreLocation>
<CatalogOwnerId>TYCT11190</CatalogOwnerId>
<CatalogOwner>McGrawHill Pub.</CatalogOwner>
<CatalogList>
<CatalogProductInfo>
<ProductType>Book</ProductType>
<ProductName>The Client</ProductName>
<ProductId>CRSUS113246A</ProductId>
<ProductCategory>Crime And Suspense</ProductCategory>
<ProductSubCategory>Vintage Books</ProductSubCategory>
<ProductPrice>45.50 USD</ProductPrice>
<ProductAuthor>
<AuthorFirstName>John Grisham</AuthorFirstName>
<AuthorMiddleName>Willbur</AuthorMiddleName>
<AuthorContact>19876648981</AuthorContact>
</ProductAuthor>
</CatalogProductInfo>
<CatalogProductInfo>
<ProductType>Pen</ProductType>
<ProductName>Reynolds</ProductName>
<ProductId>PRREY546647</ProductId>
<ProductCategory>Misc. Stationary</ProductCategory>
<ProductSubCategory>Stationary Items</ProductSubCategory>
<ProductPrice>3.00 USD</ProductPrice>
<ProductAuthor>
<AuthorFirstName>Ryan Reynolds</AuthorFirstName>
<AuthorMiddleName>William</AuthorMiddleName>
<AuthorContact>16577589898</AuthorContact>
</ProductAuthor>
</CatalogProductInfo>
<CatalogListType>ProductCollection</CatalogListType>
<CatalogListSource>Web</CatalogListSource>
</CatalogList>
<CatalogVerificationLog>
<CatalogVerificationStatus>Verified</CatalogVerificationStatus>
<CatalogVerificationDateTime>2012-03-12T13:00:15+5:30</CatalogVerificationDateTime>
<CatalogVerificationId>64774A</CatalogVerificationId>
<CatalogVerificationRequestedBy>User_121</CatalogVerificationRequestedBy>
</CatalogVerificationLog>
</CatalogOrder>
I have data that is organized in OpenStruct format below...
require 'ostruct'
require 'nokogiri'
collection = [
OpenStruct.new(:catalogStoreNumber => '657758',
:catalogStoreId => 'CTH6536',
:catalogStoreLocation => 'UnitedStates',
:catalogOwnerId => 'TYCT11190',
:catalogOwner => 'McGrawHill Pub.',
:catalogList => OpenStruct.new(
:catalogProductInfo => OpenStruct.new(
:productType => 'Book',
:productName => 'The Client',
:productId => 'CRSUS113246A',
:productCategory => 'Crime And Suspense',
:productSubcategory => 'Vintage Books',
:productPrice => '45.50 USD',
:productAuthor => OpenStruct.new(
:authorFirstName =>'John Grisham',
:authorMiddleName=> 'Willburt',
:authorContact => '19876648981')),
:catalogProductInfo => OpenStruct.new(
:productType => 'Pen',
:productName => 'Reynolds',
:productId => 'PRREY546647',
:productCategory => 'Misc. Stationary',
:productSubcategory => 'Stationery Items',
:productPrice => '3.00 USD',
:productManufacturer => 'Reynolds Inc.',
:productAuthor => OpenStruct.new(
:authorFirstName => 'Ryan Reynolds',
:authorMiddleName => 'William',
:authorContact => '16577589898')),
:catalogListType => 'ProductCollection',
:catalogListSource => 'Web'
),
:catalogVerificaitionLog => OpenStruct.new(
:catalogVerificationStatus => 'Verified',
:catalogVerificationDateTime => '2012-03-12T13:00:15+5:30',
:catalogVerificationId => '64774A',
:catalogVerificationRequestedBy => 'user_121')
)]
I am not sure whether it is possible with OpenStruct, as it lacks Nesting capabilities. Is there any other Way to use JSon to achieve this without any outstanding limitations ? Please correct my earlier code.
To see this earlier question, How to Add child Nodes in NodeSet using Nokogiri
Click [here] (http://stackoverflow.com/questions/10300095/how-to-add-child-nodes-in-nodeset-using-nokogiri)
I see below issues with OpenStruct...
i want to access the "productType" of first "catalogProductInfo" and i said "collection.catalogList.catalogProductInfo.productType.content" and I get the below error
"undefined method `productType' for # (NoMethodError)
I got stuck here and want to get out if this situation. If data can't be organized using OpenStruct. Can this be done using JSon. ? Also suggest me the way to do it Nokogiri and JSon. Help me out.

Parse WSDL file with SOAP4R

Is there any example of WSDL Parser using SOAP4R? I'm trying to list all operations of WSDL file but I can't figure it out :( Can you post me some tutorial?
Thx
Maybe that isn't answer you want, but I recommend you switch to Savon. For example, your task looks like this snippet (this example taken from github's savon page):
require "savon"
# create a client for your SOAP service
client = Savon::Client.new("http://service.example.com?wsdl")
client.wsdl.soap_actions
# => [:create_user, :get_user, :get_all_users]
# execute a SOAP request to call the "getUser" action
response = client.request(:get_user) do
soap.body = { :id => 1 }
end
response.body
# => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }

Resources