make Stemweb listener for result notification (#27) Relies on tla/stemmatology@79c81b...
[scpubgit/stemmaweb.git] / lib / stemmaweb / Controller / Stemweb.pm
1 package stemmaweb::Controller::Stemweb;
2 use Moose;
3 use namespace::autoclean;
4 use JSON qw/ from_json /;
5 use Safe::Isa;
6 use TryCatch;
7
8 BEGIN { extends 'Catalyst::Controller' }
9
10 =head1 NAME
11
12 stemmaweb::Controller::Stemweb - Client listener for Stemweb results
13
14 =head1 DESCRIPTION
15
16 This is a client listener for the Stemweb API as implemented by the protocol defined at
17 L<https://docs.google.com/document/d/1aNYGAo1v1WPDZi6LXZ30FJSMJwF8RQPYbOkKqHdCZEc/pub>.
18
19 =head1 METHODS
20
21 =head2 result
22
23  POST stemweb/result
24  Content-Type: application/json
25  (On success):
26  { job_id: <ID number>
27    status: 0
28    format: <format>
29    result: <data> }
30  (On failure):
31  { jobid: <ID number>
32    status: >1
33    result: <error message> }
34    
35 Used by the Stemweb server to notify us that one or more stemma graphs
36 has been calculated in response to an earlier request.
37
38 =cut
39
40 sub result :Local :Args(0) {
41         my( $self, $c ) = @_;
42         if( $c->request->method eq 'POST' ) {
43                 # TODO: Verify the sender!
44                 my $answer;
45                 if( ref( $c->request->body ) eq 'File::Temp' ) {
46                         # Read in the file and parse that.
47                         open( POSTDATA, $c->request->body ) or die "Failed to open post data file";
48                         binmode( POSTDATA, ':utf8' );
49                         # JSON should be all one line
50                         my $pdata = <POSTDATA>;
51                         chomp $pdata;
52                         close POSTDATA;
53                         $answer = from_json( $pdata );
54                 } else {
55                         $answer = from_json( $c->request->body );
56                 }
57                 # Find a tradition with the defined Stemweb job ID.
58                 # TODO: Maybe get Stemweb to pass back the tradition ID...
59                 my $m = $c->model('Directory');
60                 my @traditions;
61                 $m->scan( sub{ push( @traditions, $_[0] )
62                                                 if $_[0]->$_isa('Text::Tradition')
63                                                 && $_[0]->has_stemweb_jobid 
64                                                 && $_[0]->stemweb_jobid eq $answer->{job_id}; 
65                                         } );
66                 if( @traditions == 1 ) {
67                         my $tradition = shift @traditions;
68                         if( $answer->{status} == 0 ) {
69                                 try {
70                                         $tradition->record_stemweb_result( $answer );
71                                         $m->save( $tradition );
72                                 } catch( Text::Tradition::Error $e ) {
73                                         return _json_error( $c, 500, $e->message );
74                                 } catch {
75                                         return _json_error( $c, 500, $@ );
76                                 }
77                                 # If we got here, success!
78                                 $c->stash->{'result'} = { 'status' => 'success' };
79                                 $c->forward('View::JSON');
80                         } else {
81                                 return _json_error( $c, 500,
82                                         "Stemweb failure not handled: " . $answer->{result} );
83                         }
84                 } elsif( @traditions ) {
85                         return _json_error( $c, 500, 
86                                 "Multiple traditions with Stemweb job ID " . $answer->{job_id} . "!" );
87                 } else {
88                         return _json_error( $c, 400, 
89                                 "No tradition found with Stemweb job ID " . $answer->{job_id} );
90                 }
91         } else {
92                 return _json_error( $c, 403, 'Please use POST!' );
93         }
94 }
95
96 # Helper to throw a JSON exception
97 sub _json_error {
98         my( $c, $code, $errmsg ) = @_;
99         $c->response->status( $code );
100         $c->stash->{'result'} = { 'error' => $errmsg };
101         $c->forward('View::JSON');
102         return 0;
103 }
104
105 1;