How to send arbitrary payload with Mojo::UserAgent? - perl

I'm trying to use Mojo::UserAgent to access the eBay API.
One of the options is to use API requests with an XML payload, but I have had no success doing it with Mojo::UserAgent. I didn't find any options for the $ua->post method.
I also tried
my $tx = $ua->build_tx(POST => $ebay_api_url => $headers);
$tx->req->body($xml_body);
my $res = $ua->start($tx)->res->json;
with no success. The XML body is not set for the request.
What do I need to do to achieve the desired result?
I know about the possibility of using JSON requests, but that is a reserve plan.

Try to post your $xml_body like so:
my $tx = $ua->post($ebay_api_url => form => $xml_body);

You likely want (2nd example in post doc):
my $tx = $ua->post($ebay_api_url => {Accept => '*/*'} => $xml_body);

I faced the similar issue like yours but later I realized that the problem was with the xml data. Please ensure that you do not have any trailing or leading white spaces in your $xml_body. This works:
my $tx = $ua->post($ebay_api_url => $headers => $xml_body);
Mojolicious and Mojo::UserAgent is awesome and light.

Related

perl jira rest api port number

my $jira = JIRA::REST->new({
url => 'https://something.com:8443',
username => 'username',
password => 'password',
session => 1,
});
The above code doesn't work and fails with below error probably due to port number at the end
Can't connect to something.com:8443 (Bad file descriptor)
is there a way/variable to mention the port number?
You need to add the setting ssl_verify_none => 1 into the hash used in your constructor.
The underlying LWP code allows you to not verify the certs of systems you connect to (which is not recommended for production), or it also allows you to specify a Certificate Authority (CA) cert that can be used to verify certs of systems you connect to. It looks like JIRA::REST has only supported the first option.
You might be better off just using the underlying LWP code, like this:
use LWP::UserAgent;
my $ua = LWP::UserAgent->new(
ssl_opts => { verify_hostname => 1, SSL_ca_file => 'myCA.cer' },
protocols_allowed => ['https'],
);
my $req = HTTP::Request->new(
GET => 'https://something.com:8443/rest/api/latest/issue/ABC-123',
);
$req->authorization_basic('username','password');
my $res = $ua->request($req);
It looks like JIRA::REST is just providing the raw JSON response to you anyway, so it's not really saving you all that much processing.
The main advantage of REST::Client is that it saves some stuff for you to add to each request implicitly. There's nothing particularly REST-y or helpful beyond sending a request and giving its response back to you.
The JIRA::Client has a few advantages, though, since it knows how to get a session cookie and properly attach files, and, more importantly, deal with paginated results. But often, when I'm doing things with Jira, I want more power.
Back in LWP's heyday, it was very frustrating to track transactions: a request-response pair. You could, but you had to manage it yourself. And, there weren't hooks in the process, so you had to create everything every time.
Then, LWP tried to work around some SSL issues (verifying host names, etc) and also split out LWP::Protocol::https. That's not a big deal when you understand that, but even though I do, it's something I have to remember every time I want to use LWP for something. There are reasons for everything that happened, but that doesn't make it any less annoying. That leads to the sort of work jimtut showed in his answer. Every time. But, it's a small speed bump on the way to insecurity.
I like Mojolicious much more because it represents complete transactions but also has hooks (well, events) that allow you to fiddle with things automatically while the process is chugging along.
Here's an example from Mojo Web Clients that shows me creating a user-agent for each service and setting some stuff for each transaction. I can adjust the request any way that I please before it does its work (and this is mostly what that REST::Client and JIRA::Client are doing for you):
my $travis_ua = Mojo::UserAgent->new();
$travis_ua->on( start => sub {
my( $ua, $tx ) = #_;
$tx->req->headers->authorization(
"token $ENV{TRAVIS_API_KEY}" );
$tx->req->headers->accept(
'application/vnd.travis-ci.2.1+json' );
} );
my $appveyor_ua = Mojo::UserAgent->new();
$appveyor_ua->on( start => sub {
my( $ua, $tx ) = #_;
$tx->req->headers->authorization(
"Bearer $ENV{APPVEYOR_API_KEY}" );
} );
In the Basic auth case, it's just a different value in that header:
use Mojo::Util qw(b64_encode);
my $jira_ua = Mojo::UserAgent->new();
$jira_ua->on( start => sub {
my( $ua, $tx ) = #_;
$tx->req->headers->authorization(
'Basic ' . b64_encode( join ':', $username, $password ) );
} );
Now, when I use those user-agents, the auth stuff is automatically added:
my $tx = $travis_ua->get( $url );
And, that $tx gives me access to the request and the response, so I don't need REST::Client to handle that for me either.
Since Mojolicious is handling all of this in one convenient package, I don't have to wrangle different objects. As such, there's not much left over that REST::Client can do for me.

Perl modules for web automation

My situation: Want to create a perl script to automate web login page. I need also to be able to pass in (POST) components such as HTTP Headers, i.e X-forwarded-for, username, password, csrf tokens and the likes. I know of Mechanize, but which other modules can I use to do the mentioned? Can LWP do it?
Library recommendations are off-topic, so I'll focus the question about LWP.
Yes, LWP::UserAgent (and its subclass WWW::Mechanize) can be used to send arbitrary headers.
$ua->request takes a request object with custom headers. You can create this object and perform the request as follows:
use HTTP::Request::Common qw( GET );
my $request = GET($url,
HeaderName => 'HeaderValue',
HeaderName => 'HeaderValue',
);
my $response = $ua->request($request);
LWP:UserAgent provides a shorthand for this.
my $response = $ua->get($url,
HeaderName => 'HeaderValue',
HeaderName => 'HeaderValue',
);

Unable to obtain cookie-based access to Iframe within a HTML page

I am trying to automate sms sending through Way2sms in Perl LWP. I am able to login successfully. Then I click the Send SMS link in the browser to go the sms-sending page. From there, based upon the page url and the url of the iframe within which the sms fields are located, I try to construct the absolute URL of the page to which the form should be posted, and post it with the correct parameters (you can see it all in the image). However, the sms isn't being sent. Can anybody tell me what am I doing wrong here? (There is a similar module in CPAN which accomplishes this through Mechanize, I am trying a different approach)
use LWP::UserAgent;
use HTTP::Cookies;
my $cookie_jar = HTTP::Cookies->new(
file => "cookies.txt",
autosave => 1,
);
my $ua = LWP::UserAgent->new(
agent =>
'Mozilla/5.0 (X11; Linux x86_64; rv:14.0) Gecko/20100101 Firefox/14.0.1',
cookie_jar => $cookie_jar,
);
my $response = $ua->post(
'http://site2.way2sms.com/contentt/bar/Login1.action',
{
username => $mob,
password => $pass,
}
);
if ( $response->is_redirect ) {
$response = $ua->get( $response->header('Location') );
print 5 if $response->decoded_content =~ /Kaustav Mukherjee/i; #prints it, showing that the login is successful
}
my $smsresp = $ua->post("http://site5.way2sms.com/jsp/quicksms.action",[MobNo=>$mob,textArea=>'Hello World']);
if ( $smsresp->is_redirect ) {
$smsresp = $ua->post($smsresp->header('Location'),[MobNo=>$mob,textArea=>'Hello World']);
}
Here is an indirect answer that may help you out:
If you have to use this website, try using something like CasperJS. There may be some JavaScript magic that needs to happen.
Maybe you could use something like Google Voice or some other smarter service to automate send text messages. Looks like someone on CPAN already has a sweet Google::Voice module for this.

XML::Compile::SOAP and a fault call

I'm having a problem trying to deal with a SOAP fault and I can't figure out if its on my end or the other end.
I'm using a WSDL file and I have the following code:
use XML::Compile::WSDL11;
use XML::Compile::SOAP11;
use XML::Compile::Transport::SOAPHTTP;
my $wsdlXml = XML::LibXML->new->parse_file("pathtowsdl");
my $wsdl = XML::Compile::WSDL11->new($wsdlXml);
my $call = $wsdl->compileClient('Add');
my %param = ('param1' => 'xxx',
'param2' => 'xxxx',
'param3' => 'xxxx',
'Name' => {
'first' => 'xxx',
'last' => 'xxx',
});
my ($response, $trace) = $call->(\%param);
If I call that with the correct params then everything works as you would think, $response if a hash ref that has the results I'm looking for.
If however I cause a fault, then I get nothing back. $response has nothing in it (per Data::Dumper).
The thing is, $trace DOES have the soap envelope in there. The soap envelope is there in $trace on both a success and a failure/fault but only on a success is there something in $response, on the failure the "faultType" and "faultData" that is in the envelope should be in $response.
Am I doing something wrong? From everything I've read it sounds like I shouldn't have to do anything different and in the case of a fault we should have something like $response->{'fault'}, yet like I said, $response is empty.
Any ideas of what I should be looking for?
TIA!
Probably not really an answer for your question, but I cannot post just comments yet, so here is an idea: try to use soapUI (they have a free version too that is enough for your goal) or similar software and test your SOAP service with it to be sure that the issue is in your code and not on the SOAP service side.

Perl: Programatically set POST param using REST::Client module

I've built a REST Server and now I want to rapidly test it from a Perl Client, using REST::Client module.
It works fine if I perform GET Request (explicitly setting parameters in the URL) but I can't figure out how to set those params in POST Requests.
This is how my code looks like:
#!/usr/bin/perl
use strict;
use warnings;
use REST::Client;
my $client = REST::Client->new();
my $request_url = 'http://myHost:6633/my_operation';
$client->POST($request_url);
print $client->responseContent();
I've tried with something similar to:
$client->addHeader ('my_param' , 'my value');
But it's clearly wrong since I don't want to set an HTTP predefined Header but a request parameter.
Thank you!
It quite straight forward. However, you need to know what kind of content the server expects. That will typically either be XML or JSON.
F.ex. this works with a server that can understand the JSON in the second parameter, if you tell it what it is in the header in the third parameter.
$client->POST('http://localhost:3000/user/0/', '{ "name": "phluks" }', { "Content-type" => 'application/json'});
The REST module accepts a body content parameter, but I found to make it work with a string of parameters, you need to set a proper content type.
So the following code works for me:
$params = $client->buildQuery([username => $args{username},
password => $args{password}]);
$ret = $client->POST('api/rest/0.001/login', substr($params, 1),
{'Content-type' => 'application/x-www-form-urlencoded'});
I've not used the REST module, but looking at the POST function, it accepts a body content parameter, try creating a string of the parameters and send that within the function
$client->POST($request_url, "my_param=my+value");
print $client->responseContent();