File Coverage

blib/lib/WebService/TypePad/Request.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             WebService::TypePad::Request - TypePad API Batch Request
5              
6             =head1 SYNOPSIS
7              
8             use WebService::TypePad;
9             my $typepad = WebService::TypePad->new();
10             my $request = $typepad->new_request();
11             $request->add_task('user', $typepad->user('melody')->load_task());
12             $request->add_task('memberships', $typepad->user('melody')->load_memberships_task());
13             my $result = $request->run();
14             my $user = $result->{user};
15             my $memberships = $result->{memberships};
16              
17             =head1 DESCRIPTION
18              
19             This class provides the mechanism to make batch requests to the TypePad API.
20              
21             It is not to be instantiated directly; instead, use the C method on
22             L.
23              
24             =cut
25              
26             package WebService::TypePad::Request;
27              
28 1     1   6 use strict;
  1         2  
  1         107  
29 1     1   6 use warnings;
  1         1  
  1         22  
30 1     1   5 use Carp;
  1         2  
  1         54  
31 1     1   1118 use LWP::UserAgent;
  1         50450  
  1         35  
32 1     1   13 use HTTP::Request;
  1         2  
  1         22  
33 1     1   511 use HTTP::Request::Multi;
  0            
  0            
34             use WebService::TypePad::Util::JSON;
35             use Net::OAuth;
36              
37             BEGIN {
38             eval { require Math::Random::MT };
39             unless ($@) {
40             Math::Random::MT->import(qw(srand rand));
41             }
42             }
43              
44             sub new_for_api {
45             my ($class, $api) = @_;
46              
47             my $self = bless {}, $class;
48             $self->{api} = $api;
49             $self->{tasks} = {};
50             return $self;
51             }
52              
53             sub add_task {
54             my ($self, $task_name, $task) = @_;
55              
56             croak "This request object already contains a task named $task_name" if defined($self->{tasks}{$task_name});
57             $self->{tasks}{$task_name} = $task;
58             1;
59             }
60              
61             sub run {
62             my ($self) = @_;
63              
64             my $ua = LWP::UserAgent->new();
65             $ua->agent('WebService::TypePad/'.$WebService::TypePad::VERSION);
66              
67             my $requests = {};
68             my $results = {};
69              
70             my $num_tasks = scalar(keys(%{$self->{tasks}}));
71              
72             return {} if $num_tasks == 0;
73              
74             if ($num_tasks == 1) {
75              
76             my ($key) = keys(%{$self->{tasks}});
77             my $task = $self->{tasks}{$key};
78              
79             my $req = $self->_task_to_http_request($task);
80             $self->_add_auth_to_request($req);
81             my $res = $ua->request($req);
82              
83             return { $key => $self->_http_response_to_result($res, $task) };
84              
85             }
86             else {
87              
88             foreach my $task_name (keys %{$self->{tasks}}) {
89             my $task = $self->{tasks}{$task_name};
90             $requests->{$task_name} = $self->_task_to_http_request($task);
91             }
92              
93             my $req = HTTP::Request::Multi->create_request($self->{api}->url()."batch-processor", $requests);
94             $self->_add_auth_to_request($req);
95             my $res = $ua->request($req);
96              
97             if ($res->is_success) {
98              
99             my %responses = HTTP::Request::Multi->parse_response($res);
100              
101             print STDERR Data::Dumper::Dumper(\%responses);
102              
103             foreach my $task_name (keys %{$self->{tasks}}) {
104             my $task = $self->{tasks}{$task_name};
105              
106             my $res = $responses{$task_name} || croak "Server did not return a response for task $task_name";
107              
108             $results->{$task_name} = $self->_http_response_to_result($res, $task);
109             }
110              
111             }
112             else {
113             croak "Batch request failed: ".$res->status_line;
114             }
115              
116             return $results;
117              
118             }
119              
120             }
121              
122             sub _task_to_http_request {
123             my ($self, $task) = @_;
124              
125             # For now, we assume all requests are unauthed. Later will need to add
126             # an additional field to Task to determine whether the request should
127             # be authed and perhaps whether it should auth as the group or the user.
128              
129             my $url = $self->{api}->backend_url() . join('/', @{$task->path_chunks}) . '.json';
130             my $method = $task->method;
131              
132             my $req = HTTP::Request->new($method => $url);
133              
134             if (my $headers = $task->headers) {
135             $headers->scan(sub {
136             my ($name, $value) = @_;
137             $req->headers->push_header($name => $value);
138             });
139             }
140              
141             if (my $body = $task->body) {
142             if (ref $body) {
143             my $json_body = json_encode($body);
144             $req->header('Content-Type' => 'application/json');
145             $req->header('Content-Length' => length($json_body));
146             $req->content($json_body);
147             }
148             else {
149             $req->header('Content-Length' => length($body));
150             $req->content($body);
151             }
152             }
153              
154             return $req;
155              
156             }
157              
158             sub _http_response_to_result {
159             my ($self, $res, $task) = @_;
160              
161             if ($res->is_success) {
162             my $body;
163              
164             if ($res->content_type eq 'application/json') {
165             $body = json_decode($res->content);
166             }
167             else {
168             $body = $res->content;
169             }
170              
171             if (my $handler = $task->result_handler) {
172             $body = $handler->($body);
173             }
174              
175             return $body;
176             }
177             else {
178             # Return the HTTP::Response object so the caller can extract the
179             # status, etc.
180             return $res;
181             }
182              
183             }
184              
185             sub _add_auth_to_request {
186             my ($self, $req) = @_;
187              
188             return unless $self->{api}->authenticated;
189              
190             my $url = $req->uri;
191             my $method = $req->method;
192              
193             my $bare_url = $url->clone;
194             $bare_url->query('');
195             my $extra_args = $url->query_form;
196              
197             my $request = Net::OAuth->request('protected resource')->new(
198             $self->{api}->oauth_parameters,
199             request_url => $url->canonical."",
200             request_method => $method,
201             extra_params => $extra_args,
202             signature_method => 'HMAC-SHA1',
203             timestamp => time(),
204             nonce => $self->_nonce(),
205             );
206              
207             $request->sign();
208              
209             print STDERR "OAuth request is ".Data::Dumper::Dumper($request);
210              
211             my $auth_header = $request->to_authorization_header();
212              
213             $req->header('Authorization' => $auth_header);
214             print STDERR "The auth header is ".$req->as_string."\n";
215             }
216              
217             sub _nonce {
218             return int(rand(2**32));
219             }
220              
221             1;