File Coverage

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