File Coverage

blib/lib/POE/Component/Metabase/Client/Submit.pm
Criterion Covered Total %
statement 33 152 21.7
branch 0 58 0.0
condition 0 14 0.0
subroutine 11 23 47.8
pod 1 1 100.0
total 45 248 18.1


line stmt bran cond sub pod time code
1             package POE::Component::Metabase::Client::Submit;
2              
3 1     1   1093 use strict;
  1         2  
  1         39  
4 1     1   5 use warnings;
  1         1  
  1         29  
5 1     1   5 use Carp ();
  1         10  
  1         17  
6 1     1   4201 use HTTP::Status qw[:constants];
  1         23264  
  1         595  
7 1     1   15121 use HTTP::Request::Common ();
  1         85154  
  1         36  
8 1     1   11 use JSON ();
  1         2  
  1         25  
9 1     1   9816 use POE qw[Component::Client::HTTP Component::Client::Keepalive];
  1         55603  
  1         6  
10 1     1   471801 use URI;
  1         2  
  1         29  
11 1     1   5 use vars qw[$VERSION];
  1         2  
  1         87  
12              
13             $VERSION = '0.12';
14              
15             my @valid_args;
16             BEGIN {
17 1     1   4 @valid_args = qw(profile secret uri fact event session http_alias context resolver);
18              
19 1         3 for my $arg (@valid_args) {
20 1     1   7 no strict 'refs';
  1         1  
  1         65  
21 0     0     *$arg = sub { $_[0]->{$arg}; }
22 9         3132 }
23             }
24              
25             sub submit {
26 0     0 1   my ($class,%opts) = @_;
27 0           $opts{lc $_} = delete $opts{$_} for keys %opts;
28 0           my $options = delete $opts{options};
29 0           my $args = $class->__validate_args(
30             [ %opts ],
31             {
32 0           ( map { $_ => 0 } @valid_args ),
33 0           ( map { $_ => 1 } qw(profile secret uri event fact) )
34             } # hehe
35             );
36              
37 0           my $self = bless $args, $class;
38              
39 0 0         Carp::confess( "'profile' argument for $class must be a Metabase::User::Profile" )
40             unless $self->profile->isa('Metabase::User::Profile');
41 0 0         Carp::confess( "'secret' argument for $class must be a Metabase::User::secret" )
42             unless $self->secret->isa('Metabase::User::Secret');
43 0 0         Carp::confess( "'secret' argument for $class must be a Metabase::Fact" )
44             unless $self->secret->isa('Metabase::Fact');
45              
46 0 0         $self->{session_id} = POE::Session->create(
47             object_states => [
48             $self => [ qw(_start _dispatch _submit _response _register _guid_exists _http_req) ],
49             ],
50             heap => $self,
51             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
52             )->ID();
53 0           return $self;
54             }
55              
56             sub _start {
57 0     0     my ($kernel,$session,$sender,$self) = @_[KERNEL,SESSION,SENDER,OBJECT];
58 0           $self->{session_id} = $session->ID();
59 0 0 0       if ( $kernel == $sender and !$self->{session} ) {
60 0           Carp::confess "Not called from another POE session and 'session' wasn't set\n";
61             }
62 0           my $sender_id;
63 0 0         if ( $self->{session} ) {
64 0 0         if ( my $ref = $kernel->alias_resolve( $self->{session} ) ) {
65 0           $sender_id = $ref->ID();
66             }
67             else {
68 0           Carp::confess "Could not resolve 'session' to a valid POE session\n";
69             }
70             }
71             else {
72 0           $sender_id = $sender->ID();
73             }
74 0           $kernel->refcount_increment( $sender_id, __PACKAGE__ );
75 0           $kernel->detach_myself;
76 0           $self->{sender_id} = $sender_id;
77 0 0         if ( $self->{http_alias} ) {
78 0           my $http_ref = $kernel->alias_resolve( $self->{http_alias} );
79 0 0         $self->{http_id} = $http_ref->ID() if $http_ref;
80             }
81 0 0         unless ( $self->{http_id} ) {
82 0           $self->{http_id} = 'metabaseclient' . $$ . $self->{session_id};
83 0           my $ka;
84 0 0         if ( $self->resolver ) {
85 0           $ka = POE::Component::Client::Keepalive->new(
86             resolver => $self->resolver,
87             );
88             }
89             POE::Component::Client::HTTP->spawn(
90 0 0         Alias => $self->{http_id},
91             FollowRedirects => 2,
92             Timeout => 120,
93             Agent => 'Mozilla/5.0 (X11; U; Linux i686; en-US; '
94             . 'rv:1.1) Gecko/20020913 Debian/1.1-1',
95             ( defined $ka ? ( ConnectionManager => $ka ) : () ),
96             );
97 0           $self->{my_httpc} = 1;
98             }
99 0           $kernel->yield( '_submit' );
100 0           return;
101             }
102              
103             sub _submit {
104 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
105 0           my $fact = $self->fact;
106 0           my $path = sprintf 'submit/%s', $fact->type;
107              
108 0 0         $fact->set_creator($self->profile->resource)
109             unless $fact->creator;
110              
111 0           my $req_uri = $self->_abs_uri($path);
112              
113 0           my $req = HTTP::Request::Common::POST(
114             $req_uri,
115             Content_Type => 'application/json',
116             Accept => 'application/json',
117             Content => JSON->new->encode($fact->as_struct),
118             );
119 0           $req->authorization_basic($self->profile->resource->guid, $self->secret->content);
120 0           $kernel->yield( '_http_req', $req, 'submit' );
121 0           return;
122             }
123              
124             sub _guid_exists {
125 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
126 0           my $path = sprintf 'guid/%s', $self->profile->guid;
127 0           my $req_uri = $self->_abs_uri($path);
128 0           my $req = HTTP::Request::Common::HEAD( $req_uri );
129 0           $kernel->yield( '_http_req', $req, 'guid' );
130 0           return;
131             }
132              
133             sub _register {
134 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
135 0           my $req_uri = $self->_abs_uri('register');
136              
137 0           for my $type ( qw/profile secret/ ) {
138 0 0         $self->$type->set_creator( $self->$type->resource )
139             unless $self->$type->creator;
140             }
141              
142 0           my $req = HTTP::Request::Common::POST(
143             $req_uri,
144             Content_Type => 'application/json',
145             Accept => 'application/json',
146             Content => JSON->new->encode([
147             $self->profile->as_struct, $self->secret->as_struct
148             ]),
149             );
150              
151 0           $kernel->yield( '_http_req', $req, 'register' );
152 0           return;
153             }
154              
155             sub _http_req {
156 0     0     my ($self,$req,$id) = @_[OBJECT,ARG0,ARG1];
157 0           $poe_kernel->post(
158             $self->{http_id},
159             'request',
160             '_response',
161             $req,
162             $id,
163             );
164 0           return;
165             }
166              
167             sub _response {
168 0     0     my ($kernel,$self,$request_packet,$response_packet) = @_[KERNEL,OBJECT,ARG0,ARG1];
169 0           my $tag = $request_packet->[1];
170 0           my $res = $response_packet->[0];
171             # and punt an event back to the requesting session
172 0 0 0       if ( $tag eq 'submit' and $res->code == HTTP_UNAUTHORIZED ) {
173 0           $kernel->yield( '_guid_exists' );
174 0           return;
175             }
176 0 0         if ( $tag eq 'guid' ) {
177 0 0         if ( $res->is_success ) {
178 0           $self->{_error} = 'authentication failed';
179 0           $self->{content} = $res->content;
180 0           $kernel->yield( '_dispatch' );
181 0           return;
182             }
183 0           $kernel->yield( '_register' );
184 0           return;
185             }
186 0 0         if ( $tag eq 'register' ) {
187 0 0         unless ( $res->is_success ) {
188 0           $self->{_error} = 'registration failed';
189 0           $self->{content} = $res->content;
190 0           $kernel->yield( '_dispatch' );
191 0           return;
192             }
193 0           $kernel->yield( '_submit' );
194 0           return;
195             }
196 0 0         unless ( $res->is_success ) {
197 0           $self->{_error} = "fact submission failed";
198             }
199             else {
200 0           $self->{success} = 1;
201             }
202 0           $self->{content} = $res->content;
203 0           $kernel->yield( '_dispatch' );
204 0           return;
205             }
206              
207             sub _dispatch {
208 0     0     my ($kernel,$self) = @_[KERNEL,OBJECT];
209 0 0         $kernel->post( $self->{http_id}, 'shutdown' )
210             if $self->{my_httpc};
211 0           my $ref = {};
212 0           for ( qw(_error success context content) ) {
213 0 0         $ref->{$_} = $self->{$_} if $self->{$_};
214             }
215 0 0         $ref->{error} = delete $ref->{_error} if $ref->{_error};
216 0           $kernel->post( $self->{sender_id}, $self->event, $ref );
217 0           $kernel->refcount_decrement( $self->{sender_id}, __PACKAGE__ );
218 0           return;
219             }
220              
221             #--------------------------------------------------------------------------#
222             # private methods
223             #--------------------------------------------------------------------------#
224              
225             # Stolen from ::Fact.
226             # XXX: Should refactor this into something in Fact, which we can then rely on.
227             # -- rjbs, 2009-03-30
228             sub __validate_args {
229 0     0     my ($self, $args, $spec) = @_;
230 0 0 0       my $hash = (@$args == 1 and ref $args->[0]) ? { %{ $args->[0] } }
  0 0          
231             : (@$args == 0) ? { }
232             : { @$args };
233              
234 0           my @errors;
235              
236 0           for my $key (keys %$hash) {
237 0 0         push @errors, qq{unknown argument "$key" when constructing $self}
238             unless exists $spec->{ $key };
239             }
240              
241 0           for my $key (grep { $spec->{ $_ } } keys %$spec) {
  0            
242 0 0         push @errors, qq{missing required argument "$key" when constructing $self}
243             unless defined $hash->{ $key };
244             }
245              
246 0 0         Carp::confess(join qq{\n}, @errors) if @errors;
247              
248 0           return $hash;
249             }
250              
251             sub _abs_uri {
252 0     0     my ($self, $str) = @_;
253 0           my $req_uri = URI->new($str)->abs($self->uri);
254             }
255              
256             sub _error {
257 0     0     my ($self, $res, $prefix) = @_;
258 0   0       $prefix ||= "unrecognized error";
259 0 0 0       if ( ref($res) && $res->header('Content-Type') eq 'application/json') {
260 0           my $entity = JSON->new->decode($res->content);
261 0           return "$prefix\: $entity->{error}";
262             }
263             else {
264 0           return "$prefix\: " . $res->message;
265             }
266             }
267              
268             'Submit this';
269             __END__