File Coverage

blib/lib/Mojar/Google/Analytics.pm
Criterion Covered Total %
statement 31 61 50.8
branch 3 20 15.0
condition 1 18 5.5
subroutine 9 13 69.2
pod 4 4 100.0
total 48 116 41.3


line stmt bran cond sub pod time code
1             package Mojar::Google::Analytics;
2 3     3   56792 use Mojo::Base -base;
  3         10  
  3         26  
3              
4             our $VERSION = 1.112;
5              
6 3     3   713 use 5.014; # For MIME::Base64::encode_base64url
  3         25  
7 3     3   22 use Carp 'croak';
  3         9  
  3         249  
8 3     3   4243 use IO::Socket::SSL 1.75;
  3         258814  
  3         24  
9 3     3   1851 use Mojar::Auth::Jwt;
  3         19  
  3         40  
10 3     3   1826 use Mojar::Google::Analytics::Request;
  3         11  
  3         30  
11 3     3   1437 use Mojar::Google::Analytics::Response;
  3         11  
  3         32  
12 3     3   1506 use Mojo::UserAgent;
  3         283696  
  3         34  
13              
14             # Attributes
15              
16             # Analytics request
17             has api_url => 'https://www.googleapis.com/analytics/v3/data/ga';
18             has ua => sub {
19             Mojo::UserAgent->new->max_redirects(2)->inactivity_timeout(shift->timeout)
20             };
21             has 'profile_id';
22             has timeout => 60;
23              
24             sub req {
25 3     3 1 4840 my $self = shift;
26 3 100       21 return $self->{req} unless @_;
27 1 50       4 if (@_ == 1) {
28 0         0 $self->{req} = $_[0];
29             }
30             else {
31 1   33     18 $self->{req} //= Mojar::Google::Analytics::Request->new;
32 1         5 %{$self->{req}} = ( %{$self->{req}},
  1         4  
33             ids => $self->{profile_id},
34             @_
35 1         11 );
36             }
37 1         3 return $self;
38             }
39              
40             has res => sub { Mojar::Google::Analytics::Response->new };
41              
42             # Authentication token
43             has 'auth_user';
44             has grant_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer';
45             has 'private_key';
46             has jwt => sub {
47             my $self = shift;
48             my %param = map +($_ => $self->$_), 'private_key';
49             $param{iss} = $self->auth_user;
50             Mojar::Auth::Jwt->new(
51             iss => $self->auth_user,
52             private_key => $self->private_key
53             )
54             };
55             has validity_margin => 10; # Too close to expiry (seconds)
56             has token => sub { $_[0]->_request_token };
57              
58             # Public methods
59              
60             sub fetch {
61 0     0 1   my ($self) = @_;
62 0 0         croak 'Failed to see a built request' unless my $req = $self->req;
63              
64             # Validate params
65 0 0         $self->renew_token unless $self->has_valid_token;
66 0   0       defined $self->$_ or croak "Missing required field ($_)" for qw(token);
67 0           $req->access_token($self->token);
68             defined $req->$_ or croak "Missing required field ($_)"
69 0   0       for qw(access_token ids);
70              
71 0           my $res = Mojar::Google::Analytics::Response->new;
72 0           my $tx = $self->ua->get(
73             $self->api_url .'?'. $req->params,
74             { 'User-Agent' => __PACKAGE__, Authorization => 'Bearer '. $self->token }
75             );
76 0 0 0       return $res->parse($tx->res) ? $self->res($res)->res : $self->res($res) && undef;
77             }
78              
79             sub has_valid_token {
80 0     0 1   my ($self) = @_;
81 0 0         return undef unless my $token = $self->token;
82 0 0         return undef unless my $jwt = $self->jwt;
83 0 0         return undef unless time < $jwt->exp - $self->validity_margin;
84             # Currently not too late
85 0           return 1;
86             }
87              
88             sub renew_token {
89 0     0 1   my ($self) = @_;
90             # Delete anything not reusable
91 0           delete $self->{token};
92 0           $self->jwt->reset;
93             # Build a new one
94 0           return $self->token;
95             }
96              
97             # Private methods
98              
99             sub _request_token {
100 0     0     my $self = shift;
101 0           my $jwt = $self->jwt;
102 0           my $res = $self->ua->post($jwt->aud,
103             { 'User-Agent' => 'MojarGA' }, form => {
104             grant_type => $self->grant_type,
105             assertion => $jwt->encode
106             })->res;
107 0 0         if ($res->is_success) {
108 0           my $j = $res->json;
109 0 0 0       return undef unless ref $j eq 'HASH' and $j->{expires_in};
110 0           return $j->{access_token};
111             }
112             else {
113 0           my $ga_res = Mojar::Google::Analytics::Response->new;
114 0           $ga_res->parse($res);
115 0   0       my $code = $ga_res->code || 'Connection';
116 0   0       croak sprintf '%s error: %s',
117             $ga_res->code || 'Connection', $ga_res->message;
118             }
119             }
120              
121             1;
122             __END__