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