OvhApi

Official OVH Perl wrapper upon the OVH RESTful API. (c) OVH SAS
git clone https://git.e1e0.net/OvhApi.git
Log | Files | Refs | README | LICENSE

OvhApi.pm (10299B)


      1 package OvhApi;
      2 
      3 use strict;
      4 use warnings;
      5 
      6 our $VERSION = 0.2;
      7 
      8 
      9 use OvhApi::Answer;
     10 
     11 use Carp            qw{ carp croak };
     12 use List::Util      'first';
     13 use LWP::UserAgent  ();
     14 use JSON            ();
     15 use Digest::SHA1    'sha1_hex';
     16 
     17 
     18 
     19 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
     20 # Class constants
     21 
     22 use constant {
     23     OVH_API_EU => 'https://eu.api.ovh.com/1.0',
     24     OVH_API_CA => 'https://ca.api.ovh.com/1.0',
     25 };
     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     my @keys = qw{ applicationKey applicationSecret consumerKey };
     51 
     52     my ($class, %params) = @_;
     53 
     54     if (my @missingParameters = grep { not $params{$_} } qw{ applicationKey applicationSecret })
     55     {
     56         local $" = ', ';
     57         croak "Missing parameter: @missingParameters";
     58     }
     59 
     60     unless ($params{'type'} and grep { $params{'type'} eq $_ } (OVH_API_EU, OVH_API_CA))
     61     {
     62         carp 'Missing or invalid type parameter: defaulting to OVH_API_EU';
     63     }
     64 
     65     my $self = {
     66         _type   => ($params{'type'} or OVH_API_EU),
     67     };
     68 
     69     @$self{@keys} = @params{@keys};
     70 
     71     bless $self, $class;
     72 }
     73 
     74 sub setRequestTimeout
     75 {
     76     my ($class, %params) = @_;
     77 
     78     if ($params{'timeout'} =~ /^\d+$/)
     79     {
     80         $UserAgent->timeout($params{'timeout'});
     81     }
     82     elsif (exists $params{'timeout'})
     83     {
     84         carp "Invalid timeout: $params{'timeout'}";
     85     }
     86     else
     87     {
     88         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     my ($self, %params) = @_;
    103 
    104     my $method = lc $params{'method'};
    105     my $url    = $self->{'_type'} . (substr($params{'path'}, 0, 1) eq '/' ? '' : '/') . $params{'path'};
    106 
    107     my %httpHeaders;
    108 
    109     my $body = '';
    110     my %content;
    111 
    112     if ($method ne 'get' and $method ne 'delete')
    113     {
    114         $body = $Json->encode($params{'body'});
    115 
    116         $httpHeaders{'Content-type'} = 'application/json';
    117         $content{'Content'} = $body;
    118     }
    119 
    120     unless ($params{'noSignature'})
    121     {
    122         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             $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     $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     my ($self, %params) = @_;
    145 
    146     croak 'Missing parameter: accessRules' unless $params{'accessRules'};
    147     croak 'Invalid parameter: accessRules' if ref $params{'accessRules'} ne 'ARRAY';
    148 
    149     my @rules = map {
    150         croak 'Invalid access rule: must be HASH ref' if ref ne 'HASH';
    151 
    152         my %rule = %$_;
    153 
    154         $rule{'method'} = uc $rule{'method'};
    155 
    156         croak 'Access rule must have method and path keys' unless $rule{'method'} and $rule{'path'};
    157         croak 'Invalid access rule method'                 unless first { $_ eq $rule{'method'} } (@accessRuleMethods, 'ALL');
    158 
    159         if ($rule{'method'} eq 'ALL')
    160         {
    161             map { path => $rule{'path'}, method => $_ }, @accessRuleMethods;
    162         }
    163         else
    164         {
    165             \%rule
    166         }
    167     } @{ $params{'accessRules'} };
    168 
    169     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     no strict 'refs';
    176 
    177     for my $method (qw{ get post put delete })
    178     {
    179         *$method = sub { rawCall(@_, 'method', $method ) };
    180     }
    181 }
    182 
    183 # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    184 # Private part
    185 
    186 sub _timeDelta
    187 {
    188     my ($self, %params) = @_;
    189 
    190     unless (defined $self->{'_timeDelta'})
    191     {
    192         if (my $ServerTimeResponse = $self->get(path => 'auth/time', noSignature => 1))
    193         {
    194             $self->{'_timeDelta'} = ($ServerTimeResponse->content - time);
    195         }
    196         else
    197         {
    198             return 0;
    199         }
    200     }
    201 
    202     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>.
    356 
    357 =cut
    358