| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package IO::Iron::Connector; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | ## no critic (Documentation::RequirePodAtEnd) | 
| 4 |  |  |  |  |  |  | ## no critic (Documentation::RequirePodSections) | 
| 5 |  |  |  |  |  |  | ## no critic (RegularExpressions::RequireExtendedFormatting) | 
| 6 |  |  |  |  |  |  | ## no critic (RegularExpressions::RequireLineBoundaryMatching) | 
| 7 |  |  |  |  |  |  | ## no critic (RegularExpressions::ProhibitEscapedMetacharacters) | 
| 8 |  |  |  |  |  |  |  | 
| 9 | 3 |  |  | 3 |  | 58 | use 5.010_000; | 
|  | 3 |  |  |  |  | 11 |  | 
| 10 | 3 |  |  | 3 |  | 20 | use strict; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 62 |  | 
| 11 | 3 |  |  | 3 |  | 14 | use warnings; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 131 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | # Global creator | 
| 14 | 3 |  |  |  |  | 256 | BEGIN { | 
| 15 | 3 |  |  | 3 |  | 19 | use parent qw( IO::Iron::ConnectorBase ); # Inheritance | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 37 |  | 
| 16 |  |  |  |  |  |  | } | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | # Global destructor | 
| 19 |  |  |  | 3 |  |  | END { | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | # ABSTRACT: REST API Connector, HTTP interface class. | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | our $VERSION = '0.13'; # VERSION: generated by DZP::OurPkgVersion | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  |  | 
| 29 | 3 |  |  | 3 |  | 6 | use Log::Any  qw{$log}; | 
|  | 3 |  |  |  |  | 16 |  | 
|  | 3 |  |  |  |  | 1035 |  | 
| 30 |  |  |  |  |  |  | require JSON::MaybeXS; | 
| 31 | 3 |  |  | 3 |  | 607 | use Data::UUID (); | 
|  | 3 |  |  |  |  | 81 |  | 
|  | 3 |  |  |  |  | 16 |  | 
| 32 | 3 |  |  | 3 |  | 45 | use Hash::Util 0.06 qw{lock_keys lock_keys_plus unlock_keys legal_keys}; | 
|  | 3 |  |  |  |  | 18 |  | 
|  | 3 |  |  |  |  | 211 |  | 
| 33 | 3 |  |  | 3 |  | 21 | use Carp::Assert; | 
|  | 3 |  |  |  |  | 17 |  | 
|  | 3 |  |  |  |  | 359 |  | 
| 34 | 3 |  |  | 3 |  | 7 | use Carp::Assert::More; | 
|  | 3 |  |  |  |  | 462 |  | 
|  | 3 |  |  |  |  | 21 |  | 
| 35 | 3 |  |  | 3 |  | 9 | use Carp; | 
|  | 3 |  |  |  |  | 151 |  | 
|  | 3 |  |  |  |  | 23 |  | 
| 36 | 3 |  |  | 3 |  | 6 | use English '-no_match_vars'; | 
|  | 3 |  |  |  |  | 20 |  | 
|  | 3 |  |  |  |  | 2602 |  | 
| 37 | 3 |  |  | 3 |  | 140393 | use REST::Client (); | 
|  | 3 |  |  |  |  | 99 |  | 
|  | 3 |  |  |  |  | 27 |  | 
| 38 | 3 |  |  | 3 |  | 7 | use URI::Escape qw{uri_escape_utf8}; | 
|  | 3 |  |  |  |  | 181 |  | 
|  | 3 |  |  |  |  | 21 |  | 
| 39 | 3 |  |  | 3 |  | 9 | use Try::Tiny; | 
|  | 3 |  |  |  |  | 160 |  | 
|  | 3 |  |  |  |  | 22 |  | 
| 40 | 3 |  |  | 3 |  | 7 | use Scalar::Util qw{blessed looks_like_number}; | 
|  | 3 |  |  |  |  | 166 |  | 
|  | 3 |  |  |  |  | 511 |  | 
| 41 |  |  |  |  |  |  | use Exception::Class ( | 
| 42 | 3 |  |  |  |  | 1156 | 'IronHTTPCallException' => { | 
| 43 |  |  |  |  |  |  | fields => ['status_code', 'response_message'], | 
| 44 |  |  |  |  |  |  | } | 
| 45 | 3 |  |  | 3 |  | 8534 | ); | 
|  | 3 |  |  |  |  | 28 |  | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | # CONSTANTS | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 3 |  |  | 3 |  | 9 | use Const::Fast; | 
|  | 3 |  |  |  |  | 28 |  | 
|  | 3 |  |  |  |  | 8 |  | 
| 50 |  |  |  |  |  |  | const my $HTTP_CODE_OK_MIN => 200; | 
| 51 |  |  |  |  |  |  | const my $HTTP_CODE_OK_MAX => 299; | 
| 52 |  |  |  |  |  |  | const my $HTTP_CODE_SERVICE_UNAVAILABLE => 503; | 
| 53 |  |  |  |  |  |  | const my $HTTP_CONTENT_TYPE_JSON => q{application/json; charset=utf-8}; | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | sub new { | 
| 57 | 3 |  |  | 3 | 1 | 35 | my ($class) = @_; | 
| 58 | 3 |  |  |  |  | 280 | $log->tracef('Entering new(%s)', $class); | 
| 59 | 3 |  |  |  |  | 7 | my $self = IO::Iron::ConnectorBase->new(); | 
| 60 |  |  |  |  |  |  | # Add more keys to the self hash. | 
| 61 |  |  |  |  |  |  | my @self_keys = ( | 
| 62 |  |  |  |  |  |  | 'client',        # REST client timeout (for REST calls accessing Iron services). | 
| 63 |  |  |  |  |  |  | 'mime_boundary', # The boundary string separating parts in multipart REST messages. | 
| 64 | 3 |  |  |  |  | 13 | legal_keys(%{$self}), | 
|  | 3 |  |  |  |  | 22 |  | 
| 65 |  |  |  |  |  |  | ); | 
| 66 | 3 |  |  |  |  | 13 | unlock_keys(%{$self}); | 
|  | 3 |  |  |  |  | 29 |  | 
| 67 | 3 |  |  |  |  | 6 | bless $self, $class; | 
| 68 | 3 |  |  |  |  | 19 | lock_keys(%{$self}, @self_keys); | 
|  | 3 |  |  |  |  | 137 |  | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # Set up REST client | 
| 71 | 3 |  |  |  |  | 15534 | my $client = REST::Client->new(); | 
| 72 | 3 |  |  |  |  | 796 | $self->{'client'} = $client; | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | # Create MIME multipart message boundary string | 
| 75 | 3 |  |  |  |  | 540 | my $ug                   = Data::UUID->new(); | 
| 76 | 3 |  |  |  |  | 49 | my $uuid1                = $ug->create(); | 
| 77 | 3 |  |  |  |  | 33 | $self->{'mime_boundary'} = 'MIME_BOUNDARY_' . (substr $ug->to_string($uuid1), 1, 20); ## no critic (ValuesAndExpressions::ProhibitMagicNumbers) | 
| 78 |  |  |  |  |  |  |  | 
| 79 | 3 |  |  |  |  | 191 | $log->infof('Iron Connector created with REST::Client as HTTP user agent.'); | 
| 80 | 3 |  |  |  |  | 1821 | $log->tracef('Exiting new: %s', $self); | 
| 81 | 0 |  |  |  |  |  | return $self; | 
| 82 |  |  |  |  |  |  | } | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | # TODO check why previous message (coded content) is in the next message! | 
| 86 |  |  |  |  |  |  | sub perform_iron_action { ## no critic (Subroutines::ProhibitExcessComplexity) | 
| 87 | 0 |  |  | 0 | 1 |  | my ($self, $iron_action, $params) = @_; | 
| 88 | 0 | 0 |  |  |  |  | if(!defined $params) { | 
| 89 | 0 |  |  |  |  |  | $params = {}; | 
| 90 |  |  |  |  |  |  | } | 
| 91 | 0 |  |  |  |  |  | $log->tracef('Entering Connector:perform_iron_action(%s, %s)', $iron_action, $params); | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 0 |  |  |  |  |  | my $href = $iron_action->{'href'}; | 
| 94 | 0 |  |  |  |  |  | my $action_verb = $iron_action->{'action'}; | 
| 95 | 0 |  |  |  |  |  | my $return_type = $iron_action->{'return'}; | 
| 96 | 0 |  |  |  |  |  | my $retry = $iron_action->{'retry'}; | 
| 97 | 0 |  |  |  |  |  | my $require_body = $iron_action->{'require_body'}; | 
| 98 | 0 | 0 |  |  |  |  | my $paged = $iron_action->{'paged'} ? $iron_action->{'paged'} : 0; | 
| 99 | 0 | 0 |  |  |  |  | my $per_page = $iron_action->{'per_page'} ? $iron_action->{'per_page'} : 0; | 
| 100 | 0 |  |  |  |  |  | my $url_params = q{}; | 
| 101 | 0 | 0 | 0 |  |  |  | if(exists $iron_action->{'url_params'} && ref $iron_action->{'url_params'} eq 'HASH') { | 
| 102 | 0 |  |  |  |  |  | foreach (keys %{$iron_action->{'url_params'}}) { | 
|  | 0 |  |  |  |  |  |  | 
| 103 | 0 |  |  |  |  |  | $log->tracef('perform_iron_action(): url_param:%s', $_); | 
| 104 | 0 | 0 |  |  |  |  | if ($params->{'{'.$_.'}'}) { | 
| 105 | 0 |  |  |  |  |  | $url_params .= "$_={$_}&"; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  | } | 
| 108 | 0 |  |  |  |  |  | $url_params = substr $url_params, 0, (length $url_params) - 1; | 
| 109 |  |  |  |  |  |  | } | 
| 110 | 0 | 0 |  |  |  |  | if ($url_params) { | 
| 111 | 0 |  |  |  |  |  | $href .= (q{?} . $url_params); | 
| 112 |  |  |  |  |  |  | } | 
| 113 | 0 |  |  |  |  |  | my $content_type = $iron_action->{'content_type'}; | 
| 114 | 0 |  |  |  |  |  | $params->{'content_type'} = $content_type; | 
| 115 | 0 |  |  |  |  |  | $params->{'return_type'} = $return_type; | 
| 116 | 0 |  |  |  |  |  | $log->tracef('href before value substitution:\'%s\'.', $href); | 
| 117 | 0 |  |  |  |  |  | foreach my $value_key (sort keys %{$params}) { | 
|  | 0 |  |  |  |  |  |  | 
| 118 | 0 |  |  |  |  |  | my $value = $params->{$value_key}; | 
| 119 | 0 |  |  |  |  |  | $log->tracef('Param key:%s; value=%s;', $value_key, $value); | 
| 120 | 0 |  |  |  |  |  | $href =~ s/$value_key/$value/gs; | 
| 121 |  |  |  |  |  |  | }; | 
| 122 | 0 |  |  |  |  |  | $log->tracef('href after value substitution:\'%s\'.', $href); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 0 |  |  |  |  |  | my ($http_status_code, $returned_msg); | 
| 125 | 0 |  |  |  |  |  | my $keep_on_trying = 1; | 
| 126 | 0 |  |  |  |  |  | while($keep_on_trying) { | 
| 127 | 0 |  |  |  |  |  | $keep_on_trying = 0; | 
| 128 |  |  |  |  |  |  | try { | 
| 129 |  |  |  |  |  |  | assert( | 
| 130 |  |  |  |  |  |  | ($require_body == 1 && defined $params->{'body'} && ref $params->{'body'} eq 'HASH') | 
| 131 | 0 |  | 0 | 0 |  |  | || ($require_body == 0 && !defined $params->{'body'}) | 
| 132 |  |  |  |  |  |  | ); | 
| 133 | 0 |  |  |  |  |  | assert_in($action_verb, ['GET','PATCH','PUT','POST','DELETE','OPTIONS','HEAD'], 'action_verb is a valid HTTP verb.'); | 
| 134 | 0 |  |  |  |  |  | assert_nonblank( $params->{'{Protocol}'}, 'params->{Protocol} is defined and not blank.' ); | 
| 135 | 0 |  |  |  |  |  | assert_nonblank( $params->{'{Port}'}, 'params->{Port} is defined and not blank.' ); | 
| 136 | 0 |  |  |  |  |  | assert_nonblank( $params->{'{Host}'}, 'params->{Host} is defined and not blank.' ); | 
| 137 | 0 |  |  |  |  |  | assert_nonblank( $params->{'{Project ID}'}, 'params->{Project ID} is defined and not blank.' ); | 
| 138 | 0 |  |  |  |  |  | assert_nonblank( $params->{'{API Version}'}, 'params->{API Version} is defined and not blank.' ); | 
| 139 | 0 |  |  |  |  |  | assert_nonblank( $params->{'authorization_token'}, 'params->{authorization_token} is defined and not blank.' ); | 
| 140 | 0 |  |  |  |  |  | assert_nonblank( $params->{'http_client_timeout'}, 'params->{http_client_timeout} is defined and not blank.' ); | 
| 141 | 0 | 0 |  |  |  |  | my $url_escape_these_fields = defined $iron_action->{'url_escape'} ? $iron_action->{'url_escape'} : {}; | 
| 142 | 0 |  |  |  |  |  | foreach my $field_name (keys %{$url_escape_these_fields}) { | 
|  | 0 |  |  |  |  |  |  | 
| 143 | 0 | 0 |  |  |  |  | if (defined $params->{$field_name}) { | 
| 144 | 0 |  |  |  |  |  | $params->{$field_name} = uri_escape_utf8($params->{$field_name}); | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  | # | 
| 148 | 0 | 0 |  |  |  |  | if($paged) { | 
| 149 | 0 |  |  |  |  |  | $log->debugf('A paged query.'); | 
| 150 | 0 |  |  |  |  |  | my @returned_msgs; | 
| 151 | 0 |  |  |  |  |  | my ($http_status_code_temp, $returned_msg_temp); | 
| 152 | 0 |  |  |  |  |  | my $page_number = 0; | 
| 153 | 0 |  |  |  |  |  | while(1) { | 
| 154 | 0 |  |  |  |  |  | my $page_href = $href; | 
| 155 | 0 |  |  |  |  |  | $log->debugf('A paged query. Href:\'%s\'', $page_href); | 
| 156 | 0 | 0 |  |  |  |  | $page_href .= ($href =~ /\?/gsx ? q{&} : q{?}) . 'per_page='.$per_page.'&page='.$page_number; | 
| 157 | 0 |  |  |  |  |  | ($http_status_code_temp, $returned_msg_temp) = | 
| 158 |  |  |  |  |  |  | $self->perform_http_action($action_verb, $page_href, $params); | 
| 159 | 0 |  |  |  |  |  | my $return_list = $returned_msg_temp; | 
| 160 | 0 |  |  |  |  |  | my ($return_type_def, $list_hash_key) = (split m/:/s, $return_type); | 
| 161 | 0 | 0 | 0 |  |  |  | $return_list = $returned_msg_temp->{$list_hash_key} | 
| 162 |  |  |  |  |  |  | if $return_type_def eq 'LIST' && defined $list_hash_key; ## no critic (ControlStructures::ProhibitPostfixControls) | 
| 163 | 0 |  |  |  |  |  | push @returned_msgs, @{$return_list}; | 
|  | 0 |  |  |  |  |  |  | 
| 164 | 0 | 0 | 0 |  |  |  | if( scalar @{$return_list} == 0 || @{$return_list} < $per_page ) { | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 165 | 0 |  |  |  |  |  | last; | 
| 166 |  |  |  |  |  |  | } | 
| 167 | 0 |  |  |  |  |  | $page_number++; | 
| 168 |  |  |  |  |  |  | } | 
| 169 | 0 |  |  |  |  |  | $http_status_code = $http_status_code_temp; | 
| 170 | 0 |  |  |  |  |  | $returned_msg = \@returned_msgs; | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  | else { | 
| 173 | 0 |  |  |  |  |  | ($http_status_code, $returned_msg) = $self->perform_http_action($action_verb, $href, $params); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  | catch { | 
| 177 | 0 |  |  | 0 |  |  | $log->debugf('perform_iron_action(): Caught exception:\'%s\'.', $_); | 
| 178 | 0 | 0 | 0 |  |  |  | croak $_ unless blessed $_ && $_->can('rethrow'); ## no critic (ControlStructures::ProhibitPostfixControls) | 
| 179 | 0 | 0 |  |  |  |  | if ( $_->isa('IronHTTPCallException') ) { | 
| 180 | 0 | 0 |  |  |  |  | if( $_->status_code == $HTTP_CODE_SERVICE_UNAVAILABLE ) { | 
| 181 |  |  |  |  |  |  | # 503 Service Unavailable. Clients should implement exponential backoff to retry the request. | 
| 182 | 0 | 0 |  |  |  |  | $keep_on_trying = 1 if ($retry == 1); ## no critic (ControlStructures::ProhibitPostfixControls) | 
| 183 |  |  |  |  |  |  | # TODO Fix this temporary solution for backoff to retry the request. | 
| 184 |  |  |  |  |  |  | } | 
| 185 |  |  |  |  |  |  | else { | 
| 186 | 0 |  |  |  |  |  | $log->debugf('perform_iron_action(): rethrow the exception.', $_); | 
| 187 | 0 |  |  |  |  |  | $_->rethrow; | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | else { | 
| 191 | 0 |  |  |  |  |  | $_->rethrow; | 
| 192 |  |  |  |  |  |  | } | 
| 193 | 0 |  |  |  |  |  | }; | 
| 194 |  |  |  |  |  |  | # Module::Pluggable here? | 
| 195 |  |  |  |  |  |  | } | 
| 196 | 0 |  |  |  |  |  | $log->tracef('Exiting Connector:perform_iron_action(): %s', $returned_msg ); | 
| 197 | 0 |  |  |  |  |  | return $http_status_code, $returned_msg; | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | sub perform_http_action { | 
| 203 | 0 |  |  | 0 | 1 |  | my ($self, $action_verb, $href, $params) = @_; | 
| 204 | 0 |  |  |  |  |  | my $client = $self->{'client'}; | 
| 205 | 0 |  |  |  |  |  | my $json = JSON::MaybeXS->new(utf8 => 1, pretty => 1); | 
| 206 |  |  |  |  |  |  | # TODO assert href is URL | 
| 207 | 0 |  |  |  |  |  | assert_in($action_verb, ['GET','PATCH','PUT','POST','DELETE','OPTIONS','HEAD'], 'action_verb is a valid HTTP verb.'); | 
| 208 | 0 |  |  |  |  |  | assert_exists($params, ['http_client_timeout', 'authorization_token'], 'params contains items http_client_timeout and authorization_token.'); | 
| 209 | 0 |  |  |  |  |  | assert_integer($params->{'http_client_timeout'}, 'params->{\'http_client_timeout\'} is integer.'); | 
| 210 | 0 |  |  |  |  |  | assert_nonblank($params->{'authorization_token'}, 'params->{\'authorization_token\'} is a non-blank string.'); | 
| 211 | 0 |  |  |  |  |  | $log->tracef('Entering Connector:perform_http_action(%s, %s, %s)', $action_verb, $href, $params); | 
| 212 |  |  |  |  |  |  | # | 
| 213 |  |  |  |  |  |  | # HTTP request attributes | 
| 214 | 0 |  |  |  |  |  | my $timeout = $params->{'http_client_timeout'}; | 
| 215 | 0 |  |  |  |  |  | my $request_body; | 
| 216 |  |  |  |  |  |  | # Headers | 
| 217 | 0 | 0 |  |  |  |  | my $content_type = defined($params->{'content_type'}) ? $params->{'content_type'} : $HTTP_CONTENT_TYPE_JSON; | 
| 218 | 0 |  |  |  |  |  | my $authorization = 'OAuth ' . $params->{'authorization_token'}; | 
| 219 |  |  |  |  |  |  | # | 
| 220 | 0 | 0 |  |  |  |  | if($content_type =~ /multipart/is) { | 
| 221 | 0 | 0 |  |  |  |  | my $body_content = $params->{'body'} ? $params->{'body'} : { }; # Else use an empty hash for body. | 
| 222 | 0 |  |  |  |  |  | my $file_as_zip = $params->{'body'}->{'file'}; | 
| 223 | 0 |  |  |  |  |  | delete $params->{'body'}->{'file'}; | 
| 224 | 0 |  |  |  |  |  | my $encoded_body_content = $json->encode($body_content); | 
| 225 | 0 |  |  |  |  |  | my $boundary = $self->{'mime_boundary'}; | 
| 226 | 0 |  |  |  |  |  | $content_type = "multipart/form-data; boundary=$boundary"; | 
| 227 | 0 |  |  |  |  |  | my $file_name = $params->{'body'}->{'file_name'} . '.zip'; | 
| 228 |  |  |  |  |  |  | #$request_body = 'MIME-Version: 1.0' . "\n"; | 
| 229 |  |  |  |  |  |  | #$request_body .= 'Content-Length: ' . $req_content_length . "\n"; | 
| 230 |  |  |  |  |  |  | #$request_body .= 'Content-Type: ' . $req_content_type . "\n"; | 
| 231 | 0 |  |  |  |  |  | $request_body = q{--} . $boundary . "\n"; | 
| 232 | 0 |  |  |  |  |  | $request_body .= 'Content-Disposition: ' . 'form-data; name="data"' . "\n"; | 
| 233 | 0 |  |  |  |  |  | $request_body .= 'Content-Type: ' . 'text/plain; charset=utf-8' . "\n"; | 
| 234 | 0 |  |  |  |  |  | $request_body .= "\n"; | 
| 235 | 0 |  |  |  |  |  | $request_body .= $encoded_body_content . "\n"; | 
| 236 | 0 |  |  |  |  |  | $request_body .= "\n"; | 
| 237 | 0 |  |  |  |  |  | $request_body .= q{--} . $boundary . "\n"; | 
| 238 | 0 |  |  |  |  |  | $request_body .= 'Content-Disposition: ' . 'form-data; name="file"; filename="' . $file_name . q{"} . "\n"; | 
| 239 | 0 |  |  |  |  |  | $request_body .= 'Content-Type: ' . 'application/zip' . "\n"; | 
| 240 | 0 |  |  |  |  |  | $request_body .= 'Content-Transfer-Encoding: base64' . "\n"; | 
| 241 | 0 |  |  |  |  |  | $request_body .= "\n"; | 
| 242 | 0 |  |  |  |  |  | $request_body .= $file_as_zip . "\n"; | 
| 243 | 0 |  |  |  |  |  | $request_body .= q{--} . $boundary . q{--} . "\n"; | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  | else { | 
| 246 | 0 | 0 |  |  |  |  | my $body_content = $params->{'body'} ? $params->{'body'} : { }; # Else use an empty hash for body. | 
| 247 | 0 |  |  |  |  |  | $log->debugf('About to jsonize the body:\'%s\'', $body_content); | 
| 248 | 0 |  |  |  |  |  | foreach (keys %{$body_content}) { | 
|  | 0 |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | # Gimmick to ensure the proper jsonization of numbers | 
| 250 |  |  |  |  |  |  | # Otherwise numbers might end up as strings. | 
| 251 | 0 | 0 |  |  |  |  | $body_content->{$_} += 0 if looks_like_number $body_content->{$_}; ## no critic (ControlStructures::ProhibitPostfixControls) | 
| 252 |  |  |  |  |  |  | } | 
| 253 | 0 |  |  |  |  |  | my $encoded_body_content = $json->encode($body_content); | 
| 254 | 0 |  |  |  |  |  | $log->debugf('Jsonized body:\'%s\'', $encoded_body_content); | 
| 255 | 0 |  |  |  |  |  | $request_body = $encoded_body_content; | 
| 256 |  |  |  |  |  |  | } | 
| 257 | 0 |  |  |  |  |  | $client->setTimeout($timeout); | 
| 258 | 0 |  |  |  |  |  | $log->tracef('client: %s; action=%s; href=%s;', $client, $action_verb, $href); | 
| 259 | 0 |  |  |  |  |  | $log->debugf('REST Request: [verb=%s; href=%s; body=%s; Headers: Content-Type=%s; Authorization=%s]', $action_verb, $href, $request_body, $content_type, $authorization); | 
| 260 | 0 |  |  |  |  |  | $client->request($action_verb, $href, $request_body, | 
| 261 |  |  |  |  |  |  | { | 
| 262 |  |  |  |  |  |  | 'Content-Type' => $content_type, | 
| 263 |  |  |  |  |  |  | 'Authorization' => $authorization, | 
| 264 |  |  |  |  |  |  | }); | 
| 265 |  |  |  |  |  |  | # RETURN: | 
| 266 | 0 |  |  |  |  |  | $log->debugf('Returned HTTP response code:%s', $client->responseCode()); | 
| 267 | 0 |  |  |  |  |  | $log->tracef('Returned HTTP response:%s', $client->responseContent()); | 
| 268 | 0 | 0 | 0 |  |  |  | if( $client->responseCode() >= $HTTP_CODE_OK_MIN && $client->responseCode() <= $HTTP_CODE_OK_MAX ) { | 
| 269 |  |  |  |  |  |  | # 200 OK: Successful GET; 201 Created: Successful POST | 
| 270 | 0 |  |  |  |  |  | $log->tracef('HTTP Response code: %d, %s', $client->responseCode(), 'Successful!'); | 
| 271 | 0 |  |  |  |  |  | my $decoded_body_content; | 
| 272 | 0 | 0 | 0 |  |  |  | if(defined $params->{'return_type'} && $params->{'return_type'} eq 'BINARY') { | 
|  |  | 0 | 0 |  |  |  |  | 
| 273 | 0 |  |  |  |  |  | $log->tracef('Returned HTTP response header Content-Disposition:%s', $client->responseHeader('Content-Disposition')); | 
| 274 | 0 |  |  |  |  |  | my $filename; | 
| 275 | 0 | 0 |  |  |  |  | if($client->responseHeader ('Content-Disposition') =~ /filename=(.+)$/s) { | 
| 276 | 0 | 0 |  |  |  |  | $filename = $1 ? $1 : '[Unknown filename]'; | 
| 277 |  |  |  |  |  |  | } | 
| 278 | 0 |  |  |  |  |  | $decoded_body_content = { 'file' => $client->responseContent(), 'file_name' => $filename }; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  | elsif(defined $params->{'return_type'} && $params->{'return_type'} eq 'PLAIN_TEXT') { | 
| 281 | 0 |  |  |  |  |  | $decoded_body_content = $client->responseContent(); | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  | else { | 
| 284 | 0 |  |  |  |  |  | $decoded_body_content = $json->decode( $client->responseContent() ); | 
| 285 |  |  |  |  |  |  | } | 
| 286 | 0 |  |  |  |  |  | $log->tracef('Exiting Connector:perform_http_action(): %s, %s', $client->responseCode(), $decoded_body_content ); | 
| 287 | 0 |  |  |  |  |  | return $client->responseCode(), $decoded_body_content; | 
| 288 |  |  |  |  |  |  | } | 
| 289 |  |  |  |  |  |  | else { | 
| 290 | 0 |  |  |  |  |  | $log->tracef('HTTP Response code: %d, %s', $client->responseCode(), 'Failure!'); | 
| 291 | 0 |  |  |  |  |  | my $decoded_body_content; | 
| 292 |  |  |  |  |  |  | try { | 
| 293 | 0 |  |  | 0 |  |  | $decoded_body_content = $json->decode( $client->responseContent() ); | 
| 294 | 0 |  |  |  |  |  | }; | 
| 295 | 0 | 0 |  |  |  |  | my $response_message = $decoded_body_content ? $decoded_body_content->{'msg'} : $client->responseContent(); | 
| 296 | 0 |  |  |  |  |  | $log->tracef('Throwing exception in perform_http_action(): status_code=%s, response_message=%s', $client->responseCode(), $response_message ); | 
| 297 | 0 |  |  |  |  |  | IronHTTPCallException->throw( | 
| 298 |  |  |  |  |  |  | status_code => $client->responseCode(), | 
| 299 |  |  |  |  |  |  | response_message => $response_message, | 
| 300 |  |  |  |  |  |  | error => 'IronHTTPCallException: status_code=' . $client->responseCode() | 
| 301 |  |  |  |  |  |  | . ' response_message=' . $response_message, | 
| 302 |  |  |  |  |  |  | ); | 
| 303 |  |  |  |  |  |  | } | 
| 304 |  |  |  |  |  |  | return; # Control does not reach this point. | 
| 305 |  |  |  |  |  |  | } | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | 1; | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | __END__ |