File Coverage

lib/OVH/OvhApi.pm
Criterion Covered Total %
statement 30 83 36.1
branch 0 30 0.0
condition 0 11 0.0
subroutine 10 19 52.6
pod 3 4 75.0
total 43 147 29.2


line stmt bran cond sub pod time code
1              
2             use strict;
3 36     36   211 use warnings;
  36         54  
  36         910  
4 36     36   149  
  36         66  
  36         1204  
5             our $VERSION = 0.47;
6              
7              
8             use OVH::OvhApi::Answer;
9 36     36   12555  
  36         89  
  36         1145  
10             use Carp qw{ carp croak };
11 36     36   220 use List::Util 'first';
  36         70  
  36         1801  
12 36     36   182 use LWP::UserAgent ();
  36         60  
  36         1847  
13 36     36   21176 use JSON ();
  36         1441458  
  36         902  
14 36     36   329 use Digest::SHA1 'sha1_hex';
  36         85  
  36         581  
15 36     36   16535  
  36         21520  
  36         2333  
16              
17              
18             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
19             # Class constants
20              
21             use constant {
22             OVH_API_EU => 'https://eu.api.ovh.com/1.0',
23 36         27814 OVH_API_CA => 'https://ca.api.ovh.com/1.0',
24             };
25 36     36   229  
  36         81  
26             # End - Class constants
27             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
28              
29              
30              
31             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
32             # Class variables
33              
34             my $UserAgent = LWP::UserAgent->new(timeout => 10);
35             my $Json = JSON->new->allow_nonref;
36              
37             my @accessRuleMethods = qw{ GET POST PUT DELETE };
38              
39             # End - Class variables
40             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
41              
42              
43              
44             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
45             # Class methods
46              
47             {
48             my @keys = qw{ applicationKey applicationSecret consumerKey };
49              
50 0     0 0   my ($class, %params) = @_;
51              
52 0           if (my @missingParameters = grep { not $params{$_} } qw{ applicationKey applicationSecret })
53             {
54 0 0         local $" = ', ';
  0            
55             croak "Missing parameter: @missingParameters";
56 0           }
57 0            
58             unless ($params{'type'} and grep { $params{'type'} eq $_ } (OVH_API_EU, OVH_API_CA))
59             {
60 0 0 0       carp 'Missing or invalid type parameter: defaulting to OVH_API_EU';
  0            
61             }
62 0            
63             my $self = {
64             _type => ($params{'type'} or OVH_API_EU),
65             };
66 0   0        
67             @$self{@keys} = @params{@keys};
68              
69 0           bless $self, $class;
70             }
71 0            
72             {
73             my ($class, %params) = @_;
74              
75             if ($params{'timeout'} =~ /^\d+$/)
76 0     0 1   {
77             $UserAgent->timeout($params{'timeout'});
78 0 0         }
    0          
79             elsif (exists $params{'timeout'})
80 0           {
81             carp "Invalid timeout: $params{'timeout'}";
82             }
83             else
84 0           {
85             carp 'Missing parameter: timeout';
86             }
87             }
88 0            
89             # End - Class methods
90             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
91              
92              
93              
94             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
95             # Instance methods
96              
97             {
98             my ($self, %params) = @_;
99              
100             my $method = lc $params{'method'};
101             my $url = $self->{'_type'} . (substr($params{'path'}, 0, 1) eq '/' ? '' : '/') . $params{'path'};
102 0     0 1    
103             my %httpHeaders;
104 0            
105 0 0         my $body = '';
106             my %content;
107 0            
108             if ($method ne 'get' and $method ne 'delete')
109 0           {
110 0           $body = $Json->encode($params{'body'});
111              
112 0 0 0       $httpHeaders{'Content-type'} = 'application/json';
113             $content{'Content'} = $body;
114 0           }
115              
116 0           unless ($params{'noSignature'})
117 0           {
118             my $now = $self->_timeDelta + time;
119              
120 0 0         $httpHeaders{'X-Ovh-Consumer'} = $self->{'consumerKey'},
121             $httpHeaders{'X-Ovh-Timestamp'} = $now,
122 0           $httpHeaders{'X-Ovh-Signature'} = '$1$' . sha1_hex(join('+', (
123             # Full signature is '$1$' followed by the hex digest of the SHA1 of all these data joined by a + sign
124             $self->{'applicationSecret'}, # Application secret
125             $self->{'consumerKey'}, # Consumer key
126             uc $method, # HTTP method (uppercased)
127             $url, # Full URL
128             $body, # Full body
129 0           $now, # Curent OVH server time
130             )));
131             }
132              
133             $httpHeaders{'X-Ovh-Application'} = $self->{'applicationKey'},
134              
135             return OVH::OvhApi::Answer->new(response => $UserAgent->$method($url, %httpHeaders, %content));
136             }
137 0            
138             {
139             my ($self, %params) = @_;
140              
141             croak 'Missing parameter: accessRules' unless $params{'accessRules'};
142             croak 'Invalid parameter: accessRules' if ref $params{'accessRules'} ne 'ARRAY';
143              
144 0     0 1   my @rules = map {
145             croak 'Invalid access rule: must be HASH ref' if ref ne 'HASH';
146 0 0          
147 0 0         my %rule = %$_;
148              
149             $rule{'method'} = uc $rule{'method'};
150 0 0          
151             croak 'Access rule must have method and path keys' unless $rule{'method'} and $rule{'path'};
152 0           croak 'Invalid access rule method' unless first { $_ eq $rule{'method'} } (@accessRuleMethods, 'ALL');
153              
154 0           if ($rule{'method'} eq 'ALL')
155             {
156 0 0 0       map { path => $rule{'path'}, method => $_ }, @accessRuleMethods;
157 0 0   0     }
  0            
158             else
159 0 0         {
160             \%rule
161 0           }
162             } @{ $params{'accessRules'} };
163              
164             return $self->post(path => '/auth/credential/', noSignature => 1, body => { accessRules => \@rules });
165 0           }
166              
167 0           # Generation of helper subs: simple wrappers to rawCall
  0            
168             # Generate: get(), post(), put(), delete()
169 0           {
170             no strict 'refs';
171              
172             for my $method (qw{ get post put delete })
173             {
174             *$method = sub { rawCall(@_, 'method', $method ) };
175 36     36   276 }
  36         88  
  36         6417  
176             }
177              
178             # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
179 0     0     # Private part
        0      
        0      
180              
181             {
182             my ($self, %params) = @_;
183              
184             unless (defined $self->{'_timeDelta'})
185             {
186             if (my $ServerTimeResponse = $self->get(path => 'auth/time', noSignature => 1))
187             {
188 0     0     $self->{'_timeDelta'} = ($ServerTimeResponse->content - time);
189             }
190 0 0         else
191             {
192 0 0         return 0;
193             }
194 0           }
195              
196             return $self->{'_timeDelta'};
197             }
198 0            
199             # End - Instance methods
200             # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
201              
202 0            
203             return 42;
204              
205              
206              
207             =head1 NAME
208              
209             OvhApi - Official OVH Perl wrapper upon the OVH RESTful API.
210              
211             =head1 SYNOPSIS
212              
213             use OVH::OvhApi;
214              
215             my $Api = OVH::OvhApi->new(type => OVH::OvhApi::OVH_API_EU, applicationKey => $AK, applicationSecret => $AS, consumerKey => $CK);
216             my $Answer = $Api->get(path => '/me');
217              
218             =head1 DESCRIPTION
219              
220             This module is an official Perl wrapper that OVH provides in order to offer a simple way to use its RESTful API.
221             C<OvhApi> handles the authentication layer, and uses C<LWP::UserAgent> in order to run requests.
222              
223             Answer are returned as instances of L<OvhApi::Answer|OvhApi::Answer>.
224              
225             =head1 CLASS METHODS
226              
227             =head2 Constructor
228              
229             There is only one constructor: C<new>.
230              
231             Its parameters are:
232              
233             Parameter Mandatory Default Usage
234             ------------ ------------ ---------- --------
235             type Carp if missing OVH_API_EU() Determine if you'll use european or canadian OVH API (possible values are OVH_API_EU and OVH_API_CA)
236             timeout No 10 Set the timeout LWP::UserAgent will use
237             applicationKey Yes - Your application key
238             applicationSecret Yes - Your application secret
239             consumerKey Yes, unless for a credential request - Your consumer key
240              
241             =head2 OVH_API_EU
242              
243             L<Constant|constant> that points to the root URL of OVH european API.
244              
245             =head2 OVH_API_CA
246              
247             L<Constant|constant> that points to the root URL of OVH canadian API.
248              
249             =head2 setRequestTimeout
250              
251             This method changes the timeout C<LWP::UserAgent> uses. You can set that in L<new|/Constructor> instead.
252              
253             Its parameters are:
254              
255             Parameter Mandatory
256             ------------ ------------
257             timeout Yes
258              
259             =head1 INSTANCE METHODS
260              
261             =head2 rawCall
262              
263             This is the main method of that wrapper. This method will take care of the signature, of the JSON conversion of your data, and of the effective run of the query.
264              
265             Its parameters are:
266              
267             Parameter Mandatory Default Usage
268             ------------ ------------ ---------- --------
269             path Yes - The API URL you want to request
270             method Yes - The HTTP method of the request (GET, POST, PUT, DELETE)
271             body No '' The body to send in the query. Will be ignore on a GET
272             noSignature No false If set to a true value, no signature will be send
273              
274             =head2 get
275              
276             Helper method that wraps a call to:
277              
278             rawCall(method => 'get");
279              
280             All parameters are forwarded to L<rawCall|/rawCall>.
281              
282             =head2 post
283              
284             Helper method that wraps a call to:
285              
286             rawCall(method => 'post');
287              
288             All parameters are forwarded to L<rawCall|/rawCall>.
289              
290             =head2 put
291              
292             Helper method that wraps a call to:
293              
294             rawCall(method => 'put');
295              
296             All parameters are forwarded to L<rawCall|/rawCall>.
297              
298             =head2 delete
299              
300             Helper method that wraps a call to:
301              
302             rawCall(method => 'delete');
303              
304             All parameters are forwarded to L<rawCall|/rawCall>.
305              
306             =head2 requestCredentials
307              
308             This method will request a Consumer Key to the API. That credential will need to be validated with the link returned in the answer.
309              
310             Its parameters are:
311              
312             Parameter Mandatory
313             ------------ ------------
314             accessRules Yes
315              
316             The C<accessRules> parameter is an ARRAY of HASHes. Each hash contains these keys:
317              
318             =over
319              
320             =item * method: an HTTP method among GET, POST, PUT and DELETE. ALL is a special values that includes all the methods;
321              
322             =item * path: a string that represents the URLs the credential will have access to. C<*> can be used as a wildcard. C</*> will allow all URLs, for example.
323              
324             =back
325              
326             =head3 Example
327              
328             my $Api = OVH::OvhApi->new(type => OvhApi::OVH_API_EU, applicationKey => $AK, applicationSecret => $AS, consumerKey => $CK);
329             my $Answer = $Api->requestCredentials(accessRules => [ { method => 'ALL', path => '/*' }]);
330              
331             if ($Answer)
332             {
333             my ($consumerKey, $validationUrl) = @{ $Answer->content}{qw{ consumerKey validationUrl }};
334              
335             # $consumerKey contains the newly created Consumer Key
336             # $validationUrl contains a link to OVH website in order to login an OVH account and link it to the credential
337             }
338              
339             =head1 SEE ALSO
340              
341             The guts of module are using: C<LWP::UserAgent>, C<JSON>, C<Digest::SHA1>.
342              
343             =head1 COPYRIGHT
344              
345             Copyright (c) 2013, OVH SAS.
346             All rights reserved.
347              
348             This library is distributed under the terms of C<license.txt>.
349              
350             =cut
351