File Coverage

blib/lib/WWW/ORCID/API.pm
Criterion Covered Total %
statement 39 112 34.8
branch 7 44 15.9
condition 3 12 25.0
subroutine 9 23 39.1
pod 0 7 0.0
total 58 198 29.2


line stmt bran cond sub pod time code
1             package WWW::ORCID::API;
2              
3 4     4   89578 use strict;
  4         12  
  4         134  
4 4     4   27 use warnings;
  4         10  
  4         226  
5              
6             our $VERSION = 0.0401;
7              
8 4     4   1313 use Class::Load qw(try_load_class);
  4         49715  
  4         312  
9 4     4   837 use JSON qw(decode_json);
  4         17845  
  4         34  
10 4     4   1888 use Sub::Quote qw(quote_sub);
  4         18770  
  4         314  
11 4     4   36 use Carp;
  4         11  
  4         258  
12 4     4   586 use Moo::Role;
  4         17439  
  4         41  
13 4     4   2522 use namespace::clean;
  4         6956  
  4         35  
14              
15             with 'WWW::ORCID::Base';
16              
17             requires 'ops';
18             requires '_build_api_url';
19              
20             has sandbox => (is => 'ro',);
21             has client_id => (is => 'ro', required => 1);
22             has client_secret => (is => 'ro', required => 1);
23             has api_url => (is => 'lazy',);
24             has oauth_url => (is => 'lazy');
25             has read_public_token => (is => 'lazy');
26             has transport => (is => 'lazy',);
27             has last_error => (
28             is => 'rwp',
29             init_arg => undef,
30             clearer => '_clear_last_error',
31             trigger => 1
32             );
33             has _t => (is => 'lazy',);
34              
35             sub _build_oauth_url {
36 0 0   0   0 $_[0]->sandbox
37             ? 'https://sandbox.orcid.org/oauth'
38             : 'https://orcid.org/oauth';
39             }
40              
41             sub _build_read_public_token {
42 0     0   0 $_[0]->access_token(
43             grant_type => 'client_credentials',
44             scope => '/read-public'
45             );
46             }
47              
48             sub access_token {
49 0     0 0 0 my $self = shift;
50 0         0 $self->_clear_last_error;
51 0 0       0 my $opts = ref $_[0] ? $_[0] : {@_};
52 0         0 $opts->{client_id} = $self->client_id;
53 0         0 $opts->{client_secret} = $self->client_secret;
54 0         0 my $url = join('/', $self->oauth_url, 'token');
55 0         0 my $headers = {'Accept' => 'application/json'};
56 0         0 my $res = $self->_t->post_form($url, $opts, $headers);
57              
58 0 0       0 if ($res->[0] eq '200') {
59 0         0 return decode_json($res->[2]);
60             }
61 0         0 $self->_set_last_error($res);
62 0         0 return;
63             }
64              
65             sub authorize_url {
66 0     0 0 0 my $self = shift;
67 0 0       0 my $opts = ref $_[0] ? $_[0] : {@_};
68 0         0 $opts->{client_id} = $self->client_id;
69 0         0 $self->_param_url(join('/', $self->oauth_url, 'authorize'), $opts);
70             }
71              
72             sub record_url {
73 0     0 0 0 my ($self, $orcid) = @_;
74 0 0       0 $self->sandbox
75             ? "http://sandbox.orcid.org/$orcid"
76             : "http://orcid.org/$orcid";
77             }
78              
79             sub _build_transport {
80 0     0   0 'LWP';
81             }
82              
83             sub _build__t {
84 0     0   0 my ($self) = @_;
85 0         0 my $transport = $self->transport;
86 0         0 my $transport_class = "WWW::ORCID::Transport::${transport}";
87 0 0       0 try_load_class($transport_class)
88             or croak("Could not load $transport_class: $!");
89 0         0 $transport_class->new;
90             }
91              
92             sub _trigger_last_error {
93 0     0   0 my ($self, $res) = @_;
94 0 0       0 $self->log->errorf("%s", $res) if $self->log->is_error;
95             }
96              
97             sub _url {
98 0     0   0 my ($host, $path, $opts) = @_;
99 0 0       0 $path = join('/', @$path) if ref $path;
100 0         0 $path =~ s|_summary$|/summary|;
101 0         0 $path =~ s|_|-|g;
102 0 0       0 if (defined(my $orcid = $opts->{orcid})) {
103 0         0 $path = "$orcid/$path";
104             }
105 0 0       0 if (defined(my $put_code = $opts->{put_code})) {
106 0 0       0 $put_code = join(',', @$put_code) if ref $put_code;
107 0         0 $path = "$path/$put_code";
108             }
109 0         0 join('/', $host, $path);
110             }
111              
112             sub _headers {
113 0     0   0 my ($opts, $add_accept, $add_content_type) = @_;
114 0         0 my $token = $opts->{token};
115 0 0       0 $token = $token->{access_token} if ref $token;
116 0         0 my $headers = {'Authorization' => "Bearer $token",};
117 0 0       0 if ($add_accept) {
118 0         0 $headers->{'Accept'} = 'application/vnd.orcid+json';
119             }
120 0 0       0 if ($add_content_type) {
121 0         0 $headers->{'Content-Type'} = 'application/vnd.orcid+json';
122             }
123 0         0 $headers;
124             }
125              
126             sub _clean {
127 0     0   0 my ($opts) = @_;
128 0         0 delete $opts->{$_} for qw(orcid token put_code);
129 0         0 $opts;
130             }
131              
132             sub client {
133 0     0 0 0 my $self = shift;
134 0         0 $self->_clear_last_error;
135 0 0       0 my $opts = ref $_[0] ? $_[0] : {@_};
136 0   0     0 $opts->{token} ||= $self->read_public_token;
137 0         0 my $url = join('/', $self->api_url, 'client', $self->client_id);
138 0         0 my $res = $self->_t->get($url, undef, _headers($opts, 1, 0));
139 0 0       0 if ($res->[0] eq '200') {
140 0         0 return decode_json($res->[2]);
141             }
142 0         0 $self->_set_last_error($res);
143 0         0 return;
144             }
145              
146             sub search {
147 0     0 0 0 shift->get('search', @_);
148             }
149              
150             sub get {
151 0     0 0 0 my $self = shift;
152 0         0 $self->_clear_last_error;
153 0         0 my $path = shift;
154 0 0       0 my $opts = ref $_[0] ? $_[0] : {@_};
155 0   0     0 $opts->{token} ||= $self->read_public_token;
156 0         0 my $url = _url($self->api_url, $path, $opts);
157 0         0 my $headers = _headers($opts, 1, 0);
158 0         0 my $res = $self->_t->get($url, _clean($opts), $headers);
159              
160 0 0       0 if ($res->[0] eq '200') {
161 0         0 return decode_json($res->[2]);
162             }
163 0         0 $self->_set_last_error($res);
164 0         0 return;
165             }
166              
167             sub install_helper_methods {
168 2     2 0 9 my $class = $_[0];
169 2         11 my $ops = $class->ops;
170 2         33 for my $op (sort keys %$ops) {
171 51         1557 my $spec = $ops->{$op};
172 51         96 my $sym = $op;
173 51         183 $sym =~ s|[-/]|_|g;
174              
175 51 50 66     204 if ($spec->{get} || $spec->{get_pc} || $spec->{get_pc_bulk}) {
      33        
176 51         241 quote_sub("${class}::${sym}", qq|shift->get('${op}', \@_)|);
177             }
178              
179 51 100       6959 if ($spec->{add}) {
180 11         44 quote_sub("${class}::add_${sym}", qq|shift->add('${op}', \@_)|);
181             }
182              
183 51 100       1435 if ($spec->{update}) {
184 11         49 quote_sub("${class}::update_${sym}",
185             qq|shift->update('${op}', \@_)|);
186             }
187              
188 51 100       1592 if ($spec->{delete}) {
189 12         76 quote_sub("${class}::delete_${sym}",
190             qq|shift->delete('${op}', \@_)|);
191             }
192             }
193              
194 2         70 1;
195             }
196              
197             1;