File Coverage

blib/lib/WebService/Akeneo/Transport.pm
Criterion Covered Total %
statement 69 98 70.4
branch 12 28 42.8
condition 6 21 28.5
subroutine 11 13 84.6
pod 0 5 0.0
total 98 165 59.3


line stmt bran cond sub pod time code
1             package WebService::Akeneo::Transport;
2             $WebService::Akeneo::Transport::VERSION = '0.001';
3 4     4   125073 use v5.38;
  4         33  
4              
5 4     4   890 use Object::Pad;
  4         15815  
  4         28  
6              
7 4     4   3716 use Mojo::UserAgent;
  4         2426820  
  4         40  
8 4     4   242 use Mojo::URL;
  4         8  
  4         21  
9 4     4   156 use Mojo::JSON qw(encode_json decode_json);
  4         9  
  4         269  
10              
11 4     4   23 use Time::HiRes 'sleep';
  4         7  
  4         60  
12              
13 4     4   956 use WebService::Akeneo::HTTPError;
  4         11  
  4         836  
14              
15             class WebService::Akeneo::Transport 0.001;
16              
17             field $config :param; # WebService::Akeneo::Config
18             field $auth :param; # WebService::Akeneo::Auth
19             field $ua = Mojo::UserAgent->new;
20             field $rate_guard = 1;
21             field $on_request;
22             field $on_response;
23              
24 1     1 0 58 method set_ua ($new) { $ua = $new; $self }
  1         5  
  1         2  
  1         2  
  1         9  
  1         60  
25 1     1 0 15 method on_request ($cb) { $on_request = $cb; $self }
  1         24  
  1         3  
  1         3  
  1         2  
  1         4  
26 0     0 0 0 method on_response ($cb){ $on_response = $cb; $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
27 0 0   0 0 0 method set_rate_guard ($b){ $rate_guard = $b ? 1 : 0; $self }
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
28              
29 1     1 0 14 method request ($method, $path, %opt) {
  1         5  
  1         2  
  1         3  
  1         4  
  1         2  
30 1         7 $auth->refresh_if_needed;
31 1         10 my $url = Mojo::URL->new($config->base_url . $config->api_prefix . $path);
32 1 50       268 $url->query($opt{query}) if $opt{query};
33              
34 1         7 my $headers = { Accept=>'application/json', Authorization => 'Bearer ' . $auth->bearer };
35 1         32 my $tx;
36              
37 1 50       6 if (exists $opt{ndjson}) {
    0          
38 1   50     5 my $records = $opt{ndjson} // [];
39 1 50       5 die 'ndjson must be arrayref' unless ref($records) eq 'ARRAY';
40 1 50       5 die 'ndjson array empty' unless @$records;
41 1         3 my $ndjson = join("", map { encode_json($_)."\n" } @$records);
  2         27  
42 1         12 $headers->{'Content-Type'} = 'application/vnd.akeneo.collection+json';
43 1         7 $tx = $ua->build_tx($method => $url => $headers => $ndjson);
44             }
45             elsif (exists $opt{json}) {
46 0         0 $headers->{'Content-Type'} = 'application/json';
47 0         0 $tx = $ua->build_tx($method => $url => $headers => encode_json($opt{json}));
48             }
49             else {
50 0         0 $tx = $ua->build_tx($method => $url => $headers);
51             }
52              
53 1 50 50     544 $on_request->({ method => $method, url => "$url", headers=>{ %$headers }, body=>($tx->req->body//'') })
54             if $on_request;
55              
56 1         1181 $tx = $ua->start($tx);
57 1         192 my $res = $tx->result;
58              
59 1 50       60 if ($rate_guard) {
60 1         8 my $h = $res->headers;
61 1 50 50     17 if ( ($h->header('X-Rate-Limit-Remaining') // '') eq '0') {
62 0   0     0 my $reset = $h->header('X-Rate-Limit-Reset') // 1;
63 0         0 sleep($reset+0.05)
64             }
65             }
66              
67 1 50 0     25 if ($res->is_error && ($res->code//0) == 401) {
      33        
68 0         0 $auth->refresh_token; $headers->{Authorization} = 'Bearer ' . $auth->bearer;
  0         0  
69 0         0 $tx = $ua->start($ua->build_tx($method => $url => $headers => $tx->req->body));
70 0         0 $res = $tx->result;
71             }
72              
73 1 50       31 if ($res->is_error) {
74 0   0     0 WebService::Akeneo::HTTPError->new(code=>$res->code, message=>$res->message, body=>($res->body//''))->throw
75             }
76              
77 1         21 my $decoded = _decode($res);
78              
79 1 50       62 $on_response->({ code=>$res->code, headers=>{ %{$res->headers->to_hash} }, decoded=>$decoded })
  0         0  
80             if $on_response;
81              
82 1         46 return $decoded;
83             }
84              
85 1     1   3 sub _decode ($res) {
  1         2  
  1         2  
86 1   50     5 my $ct = $res->headers->content_type // '';
87 1   50     28 my $body = $res->body // '';
88              
89 1 50       29 if ($ct =~ m{application/vnd\.akeneo\.collection\+json}i) {
90 0   0     0 my @items = map { eval { decode_json($_) } // { raw => $_ } } grep { length } split /\r?\n/, $body; return \@items;
  0         0  
  0         0  
  0         0  
  0         0  
91             }
92              
93 1 50       18 return $res->json if $ct =~ m{application/json}i;
94              
95 0           return $body;
96             }
97              
98             1;