XML::Compile::SOAP and a fault call - perl

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.

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.

How to send arbitrary payload with Mojo::UserAgent?

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.

Testing a JSON PUT request in mojolicious

I am creating test cases for my app developed using Mojolicious framework. I am testing the routes for appropriate responses for the REST calls that are made. I use JSON as a means of communication between the server and client. To test GET, POST and DELETE requests I use the following functions
GET : get_ok()POST: post_json_ok() DELETE: delete_ok()
but for PUT I am not able to use put_ok(). And example of my usage of put_ok() would be my $t = Test::Mojo->new;$t->put_ok('this/is/url/to/resource'=>{ "element" => "modified value"})->status_is(204)
I get a bad request status every time(400) and so the test fails. I use Test::Mojo for testing my application.
Additional information:
$t->put_ok('http://localhost:3000/application/instances/system/test'=>{"model" => "testing put"})->status_is(204);
This is the exact test for PUT request that is attempted. model is a property of resource test that is being attempted to be changed to testing put and the expected status response is 204.
The mapping to the controller is done by
$r->route('/application/instances/system/:id, id => qr/[A-Za-z0-9 ]+/ -> via('put')
->to(controller => 'system', action => 'update_sys');
This route calls the update_sys method in the system controller and that is how the resource is located.
Actually, after discussions with SRI on IRC, he pointed me to an example almost identical you your needs in the documentation.
# Test custom transaction
my $tx = $t->ua->build_json_tx('/user/99' => {name => 'sri'});
$tx->req->method('PUT');
$t->tx($t->ua->start($tx))
->status_is(200)
->json_is('/message' => 'User has been replaced.');
So this should work for you!
Postscript:
You might be interested to know that this discussion has brought some progress: the next release of Mojolicious (version 3.66) will now have a cleaner syntax for this purpose, the new request_ok method. The above example can then be rewritten as
my $tx = $t->ua->build_json_tx('/user/99' => {name => 'sri'});
$tx->req->method('PUT');
$t->request_ok($tx)
->status_is(200)
->json_is('/message' => 'User has been replaced.');
Can you share the definition of your route for 'this/is/url/to/resource' ? the server is returning 400, so it appears Mojo::Controller in your app does not understand what you are sending ...

Understanding this object creation statement

I'm not too familiar with Perl, but I am using it for a simple script I am going to write. This script will interface with Qualys so while looking up information about the Qualys API I found this statement while looking through their sample code. I have put it on Pastebin.com (here) so you don't have to download it to view it. If for some reason you do want to download it yourself, here is a link to the page where I got it for those that want to be able to download the source (it's the "Get Map" one).
Anyways, here is the statement (line 261) that has me a little confused:
$request = new HTTP::Request GET => $url;
I'm confused about the new and GET => $url parts of the statement.
I think I mostly understand what's going on with the new part of the statement, but if someone could explain how the HTTP::Request works with creating a new LWP::UserAgent that would help clarify this line (I looked at LWP::UserAgent on CPAN, but the "KEY/DEFAULT" table they have under the new subroutine explanation made little sense to me).
I really have no idea what is happening in the GET => $url part of the statement. My guess is that it is assigning a value in either HTTP::Request or LWP::UserAgent but I can't find any information to back up that idea.
The given line is equivalent to
$request = HTTP::Request->new(GET => $url);
which could also be written as
$request = HTTP::Request->new('GET', $url);
The example used the indirect method syntax.
The connection between HTTP::Request and LWP::UserAgent is sketched in the CPAN documentation as followes:
require HTTP::Request;
$request = HTTP::Request->new(GET => 'http://www.example.com/');
$ua = LWP::UserAgent->new;
$response = $ua->request($request);
So The HTTP:.Request->new(...) creates a new request which can be executed by a user agent

Perl SOAP::Lite and Service Description to Request Object

I'm using Perl's SOAP::Lite to access a remote web service defined by a WSDL. That means I have:
use strict;
use Data::Dumper;
use SOAP::Lite +trace => 'debug';
my $service = SOAP::Lite->service('http://path/wsdl');
Ok so far. Problem is that I need access to the HTTP::Request object to send along custom HTTP request headers (and I'm not talking about authentication headers). It looks like I can access the request object after doing a successful call:
my $result = $service->getClient('parameters');
print Dumper($service->transport->http_request);
That'll give me the correct HTTP::Request object:
$VAR1 = bless( {
'_content' => '',
'_uri' => undef,
'_headers' => bless( {}, 'HTTP::Headers' ),
'_method' => undef
}, 'HTTP::Request' );
If I try to access the request object before doing an autoDispatch (the $service->getClient part), the transport object is empty and I have no way of modifying the request. It seems like everything would work fine if I were going the SOAP::Lite->proxy way -- but that defeats the helpfulness of having a pre-defined service definition.
Any ideas how I'm suppose to access the request object from a service definition without first having to make a call? Chicken and egg problem really...
Thanks!
What I'm trying to accomplish is populating the transport before doing a service call.
And you do exactly that by adding the appropriate handler, because the transport is not empty
Add a handler to the transport, see LWP::Debug for example, see LWP::UserAgent for documentation, or perlmonks.org/?node_id=904166 for example