File Coverage

blib/lib/App/Dochazka/REST/Dispatch.pm
Criterion Covered Total %
statement 93 1219 7.6
branch 0 554 0.0
condition 0 92 0.0
subroutine 31 135 22.9
pod 88 91 96.7
total 212 2091 10.1


line stmt bran cond sub pod time code
1             # *************************************************************************
2             # Copyright (c) 2014-2017, SUSE LLC
3             #
4             # All rights reserved.
5             #
6             # Redistribution and use in source and binary forms, with or without
7             # modification, are permitted provided that the following conditions are met:
8             #
9             # 1. Redistributions of source code must retain the above copyright notice,
10             # this list of conditions and the following disclaimer.
11             #
12             # 2. Redistributions in binary form must reproduce the above copyright
13             # notice, this list of conditions and the following disclaimer in the
14             # documentation and/or other materials provided with the distribution.
15             #
16             # 3. Neither the name of SUSE LLC nor the names of its contributors may be
17             # used to endorse or promote products derived from this software without
18             # specific prior written permission.
19             #
20             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30             # POSSIBILITY OF SUCH DAMAGE.
31             # *************************************************************************
32              
33             # ------------------------
34             # Top-level resources
35             # ------------------------
36              
37              
38             use strict;
39 41     41   576 use warnings;
  41         98  
  41         1128  
40 41     41   256  
  41         83  
  41         1212  
41             use App::CELL qw( $CELL $log $core $meta $site );
42 41     41   200 use App::Dochazka::Common qw( $today init_timepiece );
  41         76  
  41         4592  
43 41     41   257 use App::Dochazka::REST;
  41         88  
  41         3431  
44 41     41   268 use App::Dochazka::REST::ACL qw(
  41         171  
  41         1623  
45 41         2632 check_acl_context
46             acl_check_is_me
47             acl_check_is_my_report
48             );
49 41     41   14259 use App::Dochazka::REST::ConnBank qw( $dbix_conn conn_status );
  41         102  
50 41     41   304 use App::Dochazka::REST::Fillup;
  41         78  
  41         3273  
51 41     41   19479 use App::Dochazka::REST::LDAP qw( ldap_exists );
  41         107  
  41         1486  
52 41     41   307 use App::Dochazka::REST::Model::Activity;
  41         87  
  41         1812  
53 41     41   224 use App::Dochazka::REST::Model::Component qw( get_all_components );
  41         84  
  41         1185  
54 41     41   16730 use App::Dochazka::REST::Model::Employee qw(
  41         147  
  41         2535  
55 41         2285 list_employees_by_priv
56             noof_employees_by_priv
57             );
58 41     41   282 use App::Dochazka::REST::Model::Interval qw(
  41         100  
59 41         1871 delete_intervals_by_eid_and_tsrange
60             fetch_intervals_by_eid_and_tsrange
61             generate_interval_summary
62             );
63 41     41   246 use App::Dochazka::REST::Model::Lock qw(
  41         92  
64 41         1420 fetch_locks_by_eid_and_tsrange
65             );
66 41     41   300 use App::Dochazka::REST::Model::Privhistory qw( get_privhistory );
  41         82  
67 41     41   16244 use App::Dochazka::REST::Model::Schedhistory qw( get_schedhistory );
  41         114  
  41         2171  
68 41     41   13639 use App::Dochazka::REST::Model::Schedintvls;
  41         119  
  41         4106  
69 41     41   15027 use App::Dochazka::REST::Model::Schedule qw( get_all_schedules );
  41         133  
  41         3137  
70 41     41   14009 use App::Dochazka::REST::Model::Shared qw(
  41         2489  
  41         12677  
71 41         9694 canonicalize_date
72             canonicalize_tsrange
73             load_multiple
74             priv_by_eid
75             schedule_by_eid
76             select_set_of_single_scalar_rows
77             split_tsrange
78             timestamp_delta_plus
79             );
80 41     41   1994 use App::Dochazka::REST::ResourceDefs;
  41         1750  
81 41     41   29540 use App::Dochazka::REST::Shared qw( :ALL ); # all the shared_* functions
  41         307  
  41         2316  
82 41     41   20241 use App::Dochazka::REST::Holiday qw(
  41         133  
  41         7728  
83 41         1875 holidays_and_weekends
84             holidays_in_daterange
85             );
86 41     41   371 use Data::Dumper;
  41         104  
87 41     41   239 use File::Path qw( mkpath rmtree );
  41         115  
  41         1376  
88 41     41   231 use Module::Runtime qw( use_module );
  41         102  
  41         1764  
89 41     41   253 use Params::Validate qw( :all );
  41         93  
  41         332  
90 41     41   1863 use Try::Tiny;
  41         111  
  41         5825  
91 41     41   324 use Web::MREST::InitRouter qw( $router $resources );
  41         96  
  41         1857  
92 41     41   259 use Web::MREST::Util qw( pod_to_html pod_to_text );
  41         186  
  41         3904  
93 41     41   18304  
  41         231136  
  41         2298  
94             use parent 'App::Dochazka::REST::Auth';
95 41     41   310  
  41         95  
  41         342  
96              
97              
98              
99             =head1 NAME
100              
101             App::Dochazka::REST::Dispatch - Implementation of top-level resources
102              
103              
104              
105              
106             =head1 DESCRIPTION
107              
108             This module contains the C<init_router> method as well as all the resource
109             handlers referred to in the resource definitions.
110              
111              
112              
113             =head1 PACKAGE VARIABLES
114              
115             This module uses some package variables, which are essentially constants, to do
116             its work.
117              
118             =cut
119              
120             my $fail = $CELL->status_not_ok;
121             my %iue_dispatch = (
122             'insert_employee' => \&shared_insert_employee,
123             'update_employee' => \&shared_update_employee,
124             );
125              
126              
127              
128             =head1 FUNCTIONS
129              
130             =cut
131              
132             =head2 init
133              
134             This function is called by C<bin/mrest>.
135              
136             =cut
137              
138             $log->debug("Entering " . __PACKAGE__. "::init");
139             App::Dochazka::REST::ConnBank::init_singleton();
140 39     39 1 288  
141 39         3393 my $status = App::Dochazka::REST::reset_mason_dir();
142             return $status unless $status->ok;
143 39         758 my $comp_root = $status->payload;
144 0 0          
145 0           # get Mason components from database and write them to filesystem
146             $status = get_all_components( $dbix_conn );
147             if ( $status->ok and $status->code eq 'DISPATCH_RECORDS_FOUND' ) {
148 0           foreach my $comp ( @{ $status->payload } ) {
149 0 0 0       $comp->create_file;
150 0           }
  0            
151 0           }
152             }
153              
154              
155             =head2 init_router
156              
157             The "router" (i.e., L<Path::Router> instance) is initialized when the first
158             request comes in, as a first step before any processing of the request takes
159             place.
160              
161             This happens when L<Web::MREST::Resource> calls the C<init_router> method.
162              
163             L<App::Dochazka::REST> implements its own C<init_router> method, overriding the
164             default one in L<Web::MREST::InitRouter>.
165              
166             =cut
167              
168             $log->debug("Entering " . __PACKAGE__. "::init_router");
169             return if ref( $router ) and $router->can( 'match' );
170             $router = Path::Router->new;
171 0     0 1   App::Dochazka::REST::ResourceDefs::load();
172 0 0 0       }
173 0            
174 0            
175             =head2 Top-level handlers
176              
177             These are largely (but not entirely) copy-pasted from L<Web::MREST::Dispatch>.
178              
179              
180             =head3 handler_bugreport
181              
182             Handler for the C<bugreport> resource.
183              
184             =cut
185              
186             my ( $self, $pass ) = @_;
187             $log->debug( "Entering " . __PACKAGE__ . "::handler_bugreport, pass number $pass" );
188              
189             # first pass
190 0     0 1   return 1 if $pass == 1;
191 0            
192             # second pass
193             return $CELL->status_ok( 'DISPATCH_BUGREPORT',
194 0 0         payload => { report_bugs_to => $site->DOCHAZKA_REPORT_BUGS_TO },
195             );
196             }
197 0            
198              
199             =head3 handler_configinfo
200              
201             Handler for the C<configinfo> resource.
202              
203             =cut
204              
205             my ( $self, $pass ) = @_;
206             $log->debug( "Entering " . __PACKAGE__ . "::handler_configinfo, pass number $pass" );
207              
208             # first pass
209             return 1 if $pass == 1;
210 0     0 1    
211 0           # second pass
212             return $CELL->status_ok( 'DISPATCH_CONFIGINFO',
213             payload => $meta->CELL_META_SITEDIR_LIST,
214 0 0         );
215             }
216              
217 0            
218             =head3 handler_dbstatus
219              
220             Handler for the C<dbstatus> resource.
221              
222             =cut
223              
224             my ( $self, $pass ) = @_;
225             $log->debug( "Entering " . __PACKAGE__ . "::_get_dbstatus" );
226             $log->debug( "DBIx::Connector object: " . ref( $self->context->{'dbix_conn'} ) );
227              
228             # first pass
229             return 1 if $pass == 1;
230 0     0 1    
231 0           # second pass
232 0           my $conn = $self->context->{'dbix_conn'};
233             return $CELL->status_crit( "DOCHAZKA_NO_DBIX_CONNECTOR" ) unless ref( $conn ) and $conn->can( 'dbh' );
234             my $dbh = $conn->dbh;
235 0 0         my $noof_connections;
236             my $status;
237             try {
238 0           $conn->run( fixup => sub {
239 0 0 0       ( $noof_connections ) = $_->selectrow_array(
240 0           $site->SQL_NOOF_CONNECTIONS,
241 0           undef,
242             );
243             } );
244             $log->notice( "Current number of DBI connections is $noof_connections" );
245 0           my $dbstatus = conn_status( $conn );
246             $status = $CELL->status_ok(
247             'DOCHAZKA_DBSTATUS',
248             args => [ $dbstatus ],
249 0     0     payload => {
250 0           'conn_status' => $dbstatus,
251 0           'dbmsname' => $dbh->get_info(17),
252             'dbmsver' => $dbh->get_info(18),
253             'username' => $dbh->{Username},
254             'noof_connections' => ( $noof_connections += 0 ),
255             }
256             );
257             } catch {
258             $status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] );
259             };
260 0            
261             return $status;
262             }
263              
264 0     0      
265 0           =head3 handler_docu
266              
267 0           =cut
268              
269             my ( $self, $pass ) = @_;
270             $log->debug( "Entering " . __PACKAGE__ . "::handler_docu, pass number $pass" );
271              
272             # first pass
273             return 1 if $pass == 1;
274              
275             # '/docu/...' resources only
276 0     0 1    
277 0           # the resource to be documented should be in the request body - if not, return 400
278             my $docu_resource = $self->context->{'request_entity'};
279             if ( $docu_resource ) {
280 0 0         $log->debug( "handler_docu: request body is ->$docu_resource<-" );
281             } else {
282             $self->mrest_declare_status( 'code' => 400, 'explanation' => 'Missing request entity' );
283             return $fail;
284             }
285 0            
286 0 0         # the resource should be defined - if not, return 404
287 0           my $def = $resources->{$docu_resource};
288             $log->debug( "handler_docu: resource definition is " . Dumper( $def ) );
289 0           if ( ref( $def ) ne 'HASH' ) {
290 0           $self->mrest_declare_status( 'code' => 404,
291             'explanation' => "Could not find resource definition for $docu_resource"
292             );
293             return $fail;
294 0           }
295 0            
296 0 0         # all green - assemble the requested documentation
297 0           my $method = $self->context->{'method'};
298             my $resource_name = $self->context->{'resource_name'};
299             my $pl = {
300 0           'resource' => $docu_resource,
301             };
302             my $docs = $def->{'documentation'} || <<"EOH";
303             =pod
304 0            
305 0           The definition of resource $docu_resource lacks a 'documentation' property
306 0           EOH
307             # if they want POD, give them POD; if they want HTML, give them HTML, etc.
308             if ( $resource_name eq 'docu/pod' ) {
309 0   0       $pl->{'format'} = 'POD';
310             $pl->{'documentation'} = $docs;
311             } elsif ( $resource_name eq 'docu/html' ) {
312             $pl->{'format'} = 'HTML';
313             $pl->{'documentation'} = pod_to_html( $docs );
314             } else {
315 0 0         # fall back to plain text
    0          
316 0           $pl->{'format'} = 'text';
317 0           $pl->{'documentation'} = pod_to_text( $docs );
318             }
319 0           return $CELL->status_ok( 'DISPATCH_ONLINE_DOCUMENTATION', payload => $pl );
320 0           }
321              
322              
323 0           =head3 handler_echo
324 0            
325             Echo request body back in the response
326 0            
327             =cut
328              
329             my ( $self, $pass ) = @_;
330             $log->debug( "Entering " . __PACKAGE__ . "::handler_echo, pass number $pass" );
331            
332             # first pass
333             return 1 if $pass == 1;
334              
335             # second pass
336             return $CELL->status_ok( "ECHO_REQUEST_ENTITY", payload =>
337 0     0 1   $self->context->{'request_entity'} );
338 0           }
339              
340              
341 0 0         =head3 handler_forbidden
342              
343             Handler for 'forbidden' resource.
344              
345 0           =cut
346              
347             my ( $self, $pass ) = @_;
348             $log->debug( "Entering " . __PACKAGE__ . "::handler_forbidden, pass number $pass" );
349            
350             # first pass
351             return 1 if $pass == 1;
352              
353             # second pass
354             $self->mrest_declare_status( explanation => 'Resource forbidden by definition', permanent => 1 );
355             return $fail;
356 0     0 1   }
357 0            
358              
359             =head3 handler_holiday_tsrange
360 0 0          
361             Handler for 'holiday/:tsrange' resource.
362              
363 0           =cut
364 0            
365             my ( $self, $pass ) = @_;
366             $log->debug( "Entering " . __PACKAGE__ . "::handler_holiday_tsrange, pass number $pass" );
367            
368             # first pass
369             if ( $pass == 1 ) {
370             my $status = split_tsrange(
371             $self->context->{'dbix_conn'},
372             $self->context->{'mapping'}->{'tsrange'},
373             );
374             if ( $status->not_ok ) {
375 0     0 1   $status->{'http_code'} = ( $status->code eq 'DOCHAZKA_DBI_ERR' )
376 0           ? 500
377             : 400;
378             $self->mrest_declare_status( $status );
379 0 0         return 0;
380             }
381             my $datereg = qr/(\d+-\d+-\d+)/;
382 0           my ( $begin ) = $status->payload->[0] =~ $datereg;
383             my ( $end ) = $status->payload->[1] =~ $datereg;
384 0 0         if ( ! defined( $begin ) or ! defined( $end ) ) {
385 0 0         $self->mrest_declare_status(
386             level => 'ERR',
387             code => 400,
388 0           explanation => 'DISPATCH_UNBOUNDED_TSRANGE',
389 0           );
390             return 0;
391 0           }
392 0           $self->context->{'stashed_daterange'} = {
393 0           "begin" => $begin,
394 0 0 0       "end" => $end,
395 0           };
396             }
397              
398             # second pass
399             return $CELL->status_ok( 'DOCHAZKA_HOLIDAYS_AND_WEEKENDS_IN_TSRANGE',
400 0           tsrange => $self->context->{'mapping'}->{'tsrange'},
401             payload => holidays_and_weekends( %{ $self->context->{'stashed_daterange'} } )
402 0           );
403             }
404              
405              
406             =head3 handler_param
407              
408             Handler for 'param/:type/:param' resource.
409              
410             =cut
411 0            
  0            
412             my ( $self, $pass ) = @_;
413             $log->debug( "Entering " . __PACKAGE__ . "::handler_param, pass number $pass" );
414              
415             # get parameters
416             my $method = $self->context->{'method'};
417             my $mapping = $self->context->{'mapping'};
418             my ( $type, $param );
419             if ( $mapping ) {
420             $type = $self->context->{'mapping'}->{'type'};
421             $param = $self->context->{'mapping'}->{'param'};
422             } else {
423 0     0 1   die "AAAHAHAHAAHAAHAAAAAAAA! no mapping?? in handler_param_get";
424 0           }
425             my $resource_name = $self->context->{'resource_name'};
426              
427 0           my ( $bool, $param_obj );
428 0           if ( $type eq 'meta' ) {
429 0           $param_obj = $meta;
430 0 0         } elsif ( $type eq 'core' ) {
431 0           $param_obj = $core;
432 0           } elsif ( $type eq 'site' ) {
433             $param_obj = $site;
434 0           }
435             if ( ! $param_obj) {
436 0           $self->mrest_declare_status( code => '500', explanation => 'IMPROPER TYPE' );
437             return 0;
438 0           }
439 0 0          
    0          
    0          
440 0           # first pass
441             if ( $pass == 1 ) {
442 0           $bool = $param_obj->exists( $param );
443             $bool = $bool ? 1 : 0;
444 0           $self->context->{'stash'}->{'param_value'} = $param_obj->get( $param ) if $bool;
445             return $bool;
446 0 0         }
447 0            
448 0           # second pass
449             if ( $type ne 'meta' and $method =~ m/^(PUT)|(DELETE)$/ ) {
450             $self->mrest_declare_status( code => 400, explanation =>
451             'PUT and DELETE can be used with meta parameters only' );
452 0 0         return $fail;
453 0           }
454 0 0         if ( $method eq 'GET' ) {
455 0 0         return $CELL->status_ok( 'MREST_PARAMETER_VALUE', payload => {
456 0           $param => $self->context->{'stash'}->{'param_value'},
457             } );
458             } elsif ( $method eq 'PUT' ) {
459             $log->debug( "Request entity: " . Dumper( $self->context->{'request_entity'} ) );
460 0 0 0       return $param_obj->set( $param, $self->context->{'request_entity'} );
461 0           } elsif ( $method eq 'DELETE' ) {
462             delete $param_obj->{$param};
463 0           return $CELL->status_ok( 'MREST_PARAMETER_DELETED', payload => {
464             'type' => $type,
465 0 0         'param' => $param,
    0          
    0          
466             } );
467 0           }
468             }
469              
470 0            
471 0           =head3 handler_noop
472              
473 0           Generalized handler for resources that don't do anything.
474 0            
475             =cut
476              
477             my ( $self, $pass ) = @_;
478             $log->debug( "Entering " . __PACKAGE__ . "::handler_noop" );
479              
480             # first pass
481             return 1 if $pass == 1;
482              
483             # second pass
484             my $method = $self->context->{'method'};
485             my $resource_name = $self->context->{'resource_name'};
486             my $def = $resources->{$resource_name};
487             my $pl = {
488             'resource_name' => $resource_name,
489 0     0 1   'description' => $def->{$method}->{'description'},
490 0           'parent' => $def->{'parent'},
491             'children' => $def->{'children'},
492             };
493 0 0         return $CELL->status_ok( 'DISPATCH_NOOP',
494             payload => $pl
495             );
496 0           }
497 0            
498 0            
499             =head3
500              
501             Handler for the C<session> resource.
502              
503 0           =cut
504              
505 0           my ( $self, $pass ) = @_;
506             $log->debug( "Entering " . __PACKAGE__ . "::handler_session" );
507              
508             # first pass
509             return 1 if $pass == 1;
510              
511             # second pass
512             my $session = $self->request->{'env'}->{'psgix.session'};
513             return $CELL->status_ok( 'DISPATCH_SESSION_DATA', payload => {
514             session => $session,
515             } );
516             }
517              
518 0     0 0    
519 0           =head3
520              
521             Handler for the C<session/terminate> resource.
522 0 0          
523             =cut
524              
525 0           my ( $self, $pass ) = @_;
526 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_session_terminate" );
527              
528             # first pass
529             return 1 if $pass == 1;
530              
531             # second pass
532             $self->request->{'env'}->{'psgix.session'} = {};
533             return $CELL->status_ok( 'DOCHAZKA_SESSION_TERMINATED' );
534             }
535              
536              
537             =head3 handler_version
538              
539 0     0 0   Handler for the C<version> resource.
540 0            
541             =cut
542              
543 0 0         my ( $self, $pass ) = @_;
544             $log->debug( "Entering " . __PACKAGE__ . "::handler_version, pass number $pass" );
545              
546 0           # first pass
547 0           return 1 if $pass == 1;
548              
549             # second pass
550             my $param = $site->MREST_APPLICATION_MODULE;
551             my $version = use_module( $param )->version;
552             my $payload = ( $version )
553             ? {
554             'application' => $param,
555             'version' => $version,
556             }
557             : "BUBBA did not find nothin";
558 0     0 1    
559 0           return $CELL->status_ok( 'DISPATCH_VERSION', payload => $payload );
560             }
561              
562 0 0          
563             =head2 Activity handlers
564              
565 0            
566 0           =head3 handler_get_activity_all
567 0 0          
568             Handler for 'GET activity/all'
569              
570             =cut
571              
572             my ( $self, $pass ) = @_;
573             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_activity_all" );
574 0            
575             # first pass
576             return 1 if $pass == 1 ;
577              
578             # second pass
579             return App::Dochazka::REST::Model::Activity::get_all_activities(
580             $self->context->{'dbix_conn'},
581             disabled => 0
582             );
583             }
584              
585              
586             =head3 handler_get_activity_all_disabled
587              
588 0     0 1   Handler for 'GET activity/all/disabled'
589 0            
590             =cut
591              
592 0 0         my ( $self, $pass ) = @_;
593             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_activity_all_disabled" );
594              
595             # first pass
596 0           return 1 if $pass == 1 ;
597              
598             # second pass
599             return App::Dochazka::REST::Model::Activity::get_all_activities(
600             $self->context->{'dbix_conn'},
601             disabled => 1
602             );
603             }
604              
605              
606             =head3 handler_post_activity_aid
607              
608             Handler for 'POST activity/aid' resource.
609 0     0 1    
610 0           =cut
611              
612             my ( $self, $pass ) = @_;
613 0 0         $log->debug( "Entering " . __PACKAGE__ . "::handler_post_activity_aid" );
614              
615             # first pass
616             return 1 if $pass == 1;
617 0            
618             # second pass
619             # - check that entity is kosher
620             my $status = shared_entity_check( $self, 'aid' );
621             return $status unless $status->ok;
622             my $context = $self->context;
623             my $entity = $context->{'request_entity'};
624              
625             # - get aid and look it up
626             my $aid = $entity->{'aid'};
627             my $act = shared_first_pass_lookup( $self, 'AID', $aid );
628             return $fail unless $act;
629              
630 0     0 1   # - perform the update
631 0           return shared_update_activity( $self, $act, $entity );
632             }
633              
634 0 0          
635             =head3 handler_post_activity_code
636              
637             Handler for 'POST activity/code' resource. This is a little more complicated
638 0           because it can be either create or modify.
639 0 0          
640 0           =cut
641 0            
642             my ( $self, $pass ) = @_;
643             $log->debug( "Entering " . __PACKAGE__ . "::handler_post_activity_code" );
644 0            
645 0           # first pass
646 0 0         return 1 if $pass == 1;
647              
648             # second pass
649 0           # - check that entity is kosher
650             my $status = shared_entity_check( $self, 'code' );
651             return $status unless $status->ok;
652             my $context = $self->context;
653             my $entity = $context->{'request_entity'};
654              
655             # - create or modify?
656             my $code = $entity->{'code'};
657             my $act = shared_first_pass_lookup( $self, 'code', $code );
658             $self->nullify_declared_status;
659              
660             # - perform the insert/update
661 0     0 1   if ( $act ) {
662 0           return shared_update_activity( $self, $act, $entity );
663             } else {
664             return shared_insert_activity( $self, $code, $entity );
665 0 0         }
666             }
667              
668              
669 0           =head3 handler_activity_aid
670 0 0          
671 0           Handler for the 'activity/aid/:aid' resource.
672 0            
673             =cut
674              
675 0           my ( $self, $pass ) = @_;
676 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_activity_aid" );
677 0            
678             my $context = $self->context;
679            
680 0 0         # first pass
681 0           if ( $pass == 1 ) {
682             my $act = shared_first_pass_lookup( $self, 'AID', $context->{'mapping'}->{'aid'} );
683 0           return 0 unless $act;
684             $context->{'stashed_activity_object'} = $act;
685             return 1;
686             }
687              
688             # second pass
689             if ( $context->{'method'} eq 'GET' ) {
690             return $CELL->status_ok( 'DISPATCH_ACTIVITY_FOUND',
691             payload => $context->{'stashed_activity_object'}
692             );
693             } elsif ( $context->{'method'} eq 'PUT' ) {
694             return shared_update_activity(
695 0     0 1   $self,
696 0           $context->{'stashed_activity_object'},
697             $context->{'request_entity'}
698 0           );
699             } elsif ( $context->{'method'} eq 'DELETE' ) {
700             return $context->{'stashed_activity_object'}->delete( $context );
701 0 0         }
702 0           return $CELL->status_crit("Aaaaaaaaaaahhh! Swallowed by the abyss" );
703 0 0         }
704 0            
705 0            
706             =head3 handler_get_activity_code
707              
708             Handler for the 'GET activity/code/:code' resource.
709 0 0          
    0          
    0          
710             =cut
711 0            
712             my ( $self, $pass ) = @_;
713             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_activity_code" );
714              
715             my $context = $self->context;
716            
717 0           # first pass
718             if ( $pass == 1 ) {
719             my $act = shared_first_pass_lookup( $self, 'code', $context->{'mapping'}->{'code'} );
720 0           return 0 unless $act;
721             $context->{'stashed_activity_object'} = $act;
722 0           return 1;
723             }
724              
725             # second pass
726             return $CELL->status_ok( 'DISPATCH_ACTIVITY_FOUND',
727             payload => $context->{'stashed_activity_object'}
728             );
729             }
730              
731              
732             =head3 handler_delete_activity_code
733 0     0 1    
734 0           Handler for the 'DELETE activity/code/:code' resource.
735              
736 0           =cut
737              
738             my ( $self, $pass ) = @_;
739 0 0         $log->debug( "Entering " . __PACKAGE__ . "::handler_delete_activity_code" );
740 0            
741 0 0         my $context = $self->context;
742 0          
743 0           # first pass
744             if ( $pass == 1 ) {
745             my $act = shared_first_pass_lookup( $self, 'code', $context->{'mapping'}->{'code'} );
746             return 0 unless $act;
747             $context->{'stashed_activity_object'} = $act;
748 0           return 1;
749             }
750              
751             # second pass
752             return $context->{'stashed_activity_object'}->delete( $context );
753             }
754              
755              
756             =head3 handler_put_activity_code
757              
758             Handler for the 'PUT activity/code/:code' resource.
759              
760 0     0 1   =cut
761 0            
762             my ( $self, $pass ) = @_;
763 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_put_activity_code" );
764              
765             my $context = $self->context;
766 0 0        
767 0           # first pass
768 0 0         return 1 if ( $pass == 1 );
769 0            
770 0           # second pass
771              
772             # - create or modify?
773             my $code = $context->{'mapping'}->{'code'};
774 0           my $entity = $context->{'request_entity'};
775             if ( ! defined($entity) ) {
776             $self->mrest_declare_status( 'code' => 400, 'explanation' => 'Missing request entity' );
777             return $fail;
778             }
779             my $act = shared_first_pass_lookup( $self, 'code', $code );
780             $self->nullify_declared_status;
781              
782             # - perform insert/update operation
783             if ( $act ) {
784             return shared_update_activity( $self, $act, $entity );
785 0     0 1   } else {
786 0           return shared_insert_activity( $self, $code, $entity );
787             }
788 0           }
789              
790              
791 0 0          
792             =head2 Component handlers
793              
794              
795             =head3 handler_get_component_all
796 0            
797 0           Handler for 'GET component/all'
798 0 0          
799 0           =cut
800 0            
801             my ( $self, $pass ) = @_;
802 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_get_component_all" );
803 0            
804             # first pass
805             return 1 if $pass == 1 ;
806 0 0          
807 0           # second pass
808             return App::Dochazka::REST::Model::Component::get_all_components(
809 0           $self->context->{'dbix_conn'},
810             );
811             }
812              
813              
814             =head3 handler_post_component_cid
815              
816             Handler for 'POST component/cid' resource.
817              
818             =cut
819              
820             my ( $self, $pass ) = @_;
821             $log->debug( "Entering " . __PACKAGE__ . "::handler_post_component_cid" );
822              
823             # first pass
824             return 1 if $pass == 1;
825 0     0 1    
826 0           # second pass
827             # - check that entity is kosher
828             my $status = shared_entity_check( $self, 'cid' );
829 0 0         return $status unless $status->ok;
830             my $context = $self->context;
831             my $entity = $context->{'request_entity'};
832              
833 0           # - get cid and look it up
834             my $cid = $entity->{'cid'};
835             my $comp = shared_first_pass_lookup( $self, 'CID', $cid );
836             return $fail unless $cid;
837              
838             # - perform the update
839             return shared_update_component( $self, $comp, $entity );
840             }
841              
842              
843             =head3 handler_post_component_path
844              
845 0     0 1   Handler for 'POST component/path' resource. This is a little more complicated
846 0           because it can be either create or modify.
847              
848             =cut
849 0 0          
850             my ( $self, $pass ) = @_;
851             $log->debug( "Entering " . __PACKAGE__ . "::handler_post_component_path" );
852              
853 0           # first pass
854 0 0         return 1 if $pass == 1;
855 0            
856 0           # second pass
857             # - check that entity is kosher
858             my $status = shared_entity_check( $self, 'path' );
859 0           return $status unless $status->ok;
860 0           my $context = $self->context;
861 0 0         my $entity = $context->{'request_entity'};
862              
863             # - create or modify?
864 0           my $path = $entity->{'path'};
865             my $comp = shared_first_pass_lookup( $self, 'path', $path );
866             $self->nullify_declared_status;
867              
868             # - perform the insert/update
869             if ( $comp ) {
870             return shared_update_component( $self, $comp, $entity );
871             } else {
872             my $status = shared_entity_check( $self, 'path', 'source', 'acl' );
873             return $status unless $status->ok;
874             return shared_insert_component( $self, $path, $entity );
875             }
876 0     0 1   }
877 0            
878              
879             =head3 handler_component_cid
880 0 0          
881             Handler for the 'component/cid/:cid' resource.
882              
883             =cut
884 0            
885 0 0         my ( $self, $pass ) = @_;
886 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_component_cid" );
887 0            
888             my $context = $self->context;
889              
890 0           # first pass
891 0           if ( $pass == 1 ) {
892 0           my $comp = shared_first_pass_lookup( $self, 'cid', $context->{'mapping'}->{'cid'} );
893             return 0 unless $comp;
894             $context->{'stashed_component_object'} = $comp;
895 0 0         return 1;
896 0           }
897              
898 0           # second pass
899 0 0         if ( $context->{'method'} eq 'GET' ) {
900 0           return $CELL->status_ok( 'DISPATCH_COMPONENT_FOUND',
901             payload => $context->{'stashed_component_object'}
902             );
903             } elsif ( $context->{'method'} eq 'PUT' ) {
904             return shared_update_component(
905             $self,
906             $context->{'stashed_component_object'},
907             $context->{'request_entity'}
908             );
909             } elsif ( $context->{'method'} eq 'DELETE' ) {
910             return $context->{'stashed_component_object'}->delete( $context );
911             }
912 0     0 1   return $CELL->status_crit("Aaaabllaaaaaaahhh Component! Swallowed by the abyss" );
913 0           }
914              
915 0            
916              
917             =head2 Employee handlers
918 0 0          
919 0            
920 0 0         =head3 handler_get_employee_count
921 0            
922 0           Handler for 'GET employee/count/?:priv' resource.
923              
924             =cut
925              
926 0 0         my ( $self, $pass ) = @_;
    0          
    0          
927             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_count" );
928 0            
929             # first pass
930             return 1 if $pass == 1;
931              
932             # second pass
933             my $result;
934 0           if ( my $priv = $self->context->{'mapping'}->{'priv'} ) {
935             $result = noof_employees_by_priv( $self->context->{'dbix_conn'}, lc $priv );
936             } else {
937 0           $result = noof_employees_by_priv( $self->context->{'dbix_conn'}, 'total' );
938             }
939 0           return $result;
940             }
941              
942              
943             =head3 handler_get_employee_list
944              
945             Handler for 'GET employee/list/?:priv' resource.
946              
947             =cut
948              
949             my ( $self, $pass ) = @_;
950             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_list" );
951              
952             # first pass
953             return 1 if $pass == 1;
954 0     0 1    
955 0           # second pass
956             my $result;
957             if ( my $priv = $self->context->{'mapping'}->{'priv'} ) {
958 0 0         $result = list_employees_by_priv( $self->context->{'dbix_conn'}, lc $priv );
959             } else {
960             $result = list_employees_by_priv( $self->context->{'dbix_conn'}, 'all' );
961 0           }
962 0 0         return $result;
963 0           }
964              
965 0            
966             =head3 handler_get_employee_team
967 0            
968             Handler for 'GET employee/team' resource.
969              
970             =cut
971              
972             my ( $self, $pass ) = @_;
973             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_team" );
974              
975             # first pass
976             return 1 if $pass == 1;
977              
978 0     0 1   # second pass
979 0           my $employee_obj = $self->context->{'current_obj'};
980             my $dbix_conn = $self->context->{'dbix_conn'};
981             my $status = $employee_obj->team_nicks( $dbix_conn );
982 0 0          
983             return $status;
984             }
985 0            
986 0 0          
987 0           =head3 handler_whoami
988              
989 0           Handler for GET requests on the 'whoami', 'employee/current', and
990             'employee/self' resources (which are all synonyms).
991 0            
992             =cut
993              
994             my ( $self, $pass ) = @_;
995             $log->debug( "Entering " . __PACKAGE__ . "::handler_whoami" );
996              
997             # first pass
998             return 1 if $pass == 1;
999              
1000             # second pass
1001             my $context = $self->context;
1002 0     0 1   my $current_emp = $context->{'current'};
1003 0           delete $current_emp->{'passhash'};
1004             delete $current_emp->{'salt'};
1005             delete $current_emp->{'remark'} unless $context->{'current_priv'} eq 'admin';
1006 0 0         $CELL->status_ok( 'DISPATCH_EMPLOYEE_SELF', args =>
1007             [ $current_emp->{'nick'} ], payload => $current_emp );
1008             }
1009 0            
1010 0            
1011 0           =head3 _handler_get_employee_full_pass2
1012              
1013 0           =cut
1014              
1015             my ( $self ) = @_;
1016             my ( $emp, $has_reports, $status );
1017             my $context = $self->context;
1018             my $conn = $context->{'dbix_conn'};
1019             my @ARGS = ( $conn, $context->{'stashed_employee_object'}->{'eid'} );
1020             # need a real Employee object
1021             $status = App::Dochazka::REST::Model::Employee->load_by_eid( @ARGS );
1022             if ( $status->ok ) {
1023             $emp = $status->payload;
1024             } else {
1025 0     0 1   die "YYURIEFFE has_reports returned not-OK status " . Dumper( $status );
1026 0           }
1027             my $current_priv = priv_by_eid( @ARGS );
1028             my $current_sched = schedule_by_eid( @ARGS );
1029 0 0         $status = $emp->has_reports( $conn );
1030             if ( $status->ok ) {
1031             $has_reports = $status->payload;
1032 0           } else {
1033 0           die "YYURIEFFF has_reports returned not-OK status " . Dumper( $status );
1034 0           }
1035 0           my %history;
1036 0 0         foreach my $prop ( 'priv', 'schedule' ) {
1037             if ( $prop eq 'priv' ) {
1038 0           $status = App::Dochazka::REST::Model::Privhistory->load_by_eid( @ARGS );
1039             } elsif ( $prop eq 'schedule' ) {
1040             $status = App::Dochazka::REST::Model::Schedhistory->load_by_eid( @ARGS );
1041             } else {
1042             die "DEFDXXEGUGaloblast!";
1043             }
1044             $history{$prop} = $status->payload;
1045             }
1046             $CELL->status_ok(
1047 0     0     'DISPATCH_EMPLOYEE_PROFILE_FULL',
1048 0           args => [ $emp->{'nick'}, $current_priv ],
1049 0           payload => {
1050 0           'emp' => $emp,
1051 0           'has_reports' => $has_reports,
1052             'priv' => $current_priv,
1053 0           'privhistory' => $history{'priv'},
1054 0 0         'schedule' => $current_sched,
1055 0           'schedhistory' => $history{'schedule'},
1056             }
1057 0           );
1058             }
1059 0            
1060 0           =head3 handler_get_employee_self_full
1061 0            
1062 0 0         Handler for GET requests on 'employee/self/full'
1063 0            
1064             =cut
1065 0            
1066             my ( $self, $pass ) = @_;
1067 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_self_full" );
1068 0            
1069 0 0         my $context = $self->context;
    0          
1070 0            
1071             # first pass
1072 0           if ( $pass == 1 ) {
1073             $context->{'stashed_employee_object'} = $context->{'current'};
1074 0           return 1;
1075             }
1076 0            
1077             # second pass
1078             return $self->_handler_get_employee_full_pass2();
1079             }
1080              
1081              
1082             =head3 handler_get_employee_eid_full
1083              
1084             Handler for GET requests on 'employee/eid/:eid/full'
1085              
1086             =cut
1087 0            
1088             my ( $self, $pass ) = @_;
1089             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_eid_full" );
1090              
1091             my $context = $self->context;
1092              
1093             # first pass
1094             if ( $pass == 1 ) {
1095             return shared_get_employee_pass1( $self, $pass, 'EID', $self->context->{'mapping'}->{'eid'} );
1096             }
1097              
1098             # second pass
1099 0     0 1   return $self->_handler_get_employee_full_pass2();
1100 0           }
1101              
1102 0            
1103             =head3 handler_get_employee_nick_full
1104              
1105 0 0         Handler for GET requests on 'employee/nick/:nick/full'
1106 0            
1107 0           =cut
1108              
1109             my ( $self, $pass ) = @_;
1110             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_nick_full" );
1111 0            
1112             my $context = $self->context;
1113              
1114             # first pass
1115             if ( $pass == 1 ) {
1116             return shared_get_employee_pass1( $self, $pass, 'nick', $self->context->{'mapping'}->{'nick'} );
1117             }
1118              
1119             # second pass
1120             return $self->_handler_get_employee_full_pass2();
1121             }
1122 0     0 1    
1123 0            
1124             =head3 handler_put_employee_eid
1125 0            
1126             Handler for 'PUT employee/eid/:eid' - can only be update.
1127              
1128 0 0         =cut
1129 0            
1130             my ( $self, $pass ) = @_;
1131             $log->debug( "Entering " . __PACKAGE__ . "::handler_put_employee_eid" );
1132              
1133 0           my $context = $self->context;
1134              
1135             # first pass
1136             if ( $pass == 1 ) {
1137             # determine if this is an insert or an update
1138             my $emp = shared_first_pass_lookup( $self, 'EID', $self->context->{'mapping'}->{'eid'} );
1139             return 0 unless $emp;
1140             return 0 unless shared_employee_acl_part1( $self, $emp ); # additional ACL checks
1141             $context->{'stashed_employee_object'} = $emp;
1142             return 1;
1143             }
1144 0     0 1    
1145 0           # second pass
1146             return $fail unless shared_employee_acl_part2( $self );
1147 0           return shared_update_employee(
1148             $self,
1149             $context->{'stashed_employee_object'},
1150 0 0         $context->{'request_entity'}
1151 0           );
1152             }
1153              
1154              
1155 0           =head3 handler_post_employee_eid
1156              
1157             Handler for 'POST employee/eid' - can only be update.
1158              
1159             =cut
1160              
1161             my ( $self, $pass ) = @_;
1162             $log->debug( "Entering " . __PACKAGE__ . "::handler_post_employee_eid" );
1163              
1164             my $context = $self->context;
1165              
1166 0     0 1   # first pass
1167 0           return 1 if $pass == 1;
1168              
1169 0           # second pass
1170             my ( $eid, $emp );
1171             if ( $eid = $context->{'request_entity'}->{'eid'} ) {
1172 0 0         $emp = shared_first_pass_lookup( $self, 'EID', $eid );
1173             return $fail unless $emp;
1174 0           return $fail unless shared_employee_acl_part1( $self, $emp ); # additional ACL checks
1175 0 0         return $fail unless shared_employee_acl_part2( $self );
1176 0 0         } else {
1177 0           $self->mrest_declare_status( code => 400,
1178 0           explanation => 'DISPATCH_PROP_MISSING_IN_ENTITY', args => [ 'eid' ],
1179             );
1180             return $fail;
1181             }
1182 0 0         return shared_update_employee(
1183             $self,
1184             $emp,
1185             $context->{'request_entity'}
1186 0           );
1187             }
1188              
1189              
1190             =head3 handler_put_employee_nick
1191              
1192             Handler for 'PUT employee/nick/:nick' - a little complicated because it can
1193             be insert or update, depending on whether or not the employee exists.
1194              
1195             =cut
1196              
1197             my ( $self, $pass ) = @_;
1198 0     0 1   $log->debug( "Entering " . __PACKAGE__ . "::handler_put_employee_nick" );
1199 0            
1200             my $context = $self->context;
1201 0            
1202             # first pass
1203             if ( $pass == 1 ) {
1204 0 0         # determine if this is an insert or an update
1205             my $emp = shared_first_pass_lookup( $self, 'nick', $self->context->{'mapping'}->{'nick'} );
1206             if ( $emp ) {
1207 0           $context->{'put_employee_func'} = 'update_employee';
1208 0 0         } else {
1209 0           $context->{'put_employee_func'} = 'insert_employee';
1210 0 0         }
1211 0 0         return 0 unless shared_employee_acl_part1( $self, $emp ); # additional ACL checks
1212 0 0         $context->{'stashed_employee_object'} = $emp;
1213             $self->nullify_declared_status;
1214 0           return 1;
1215             }
1216              
1217 0           # second pass
1218             my $func = $context->{'put_employee_func'};
1219             $log->debug( "PUT employee function is $func - " );
1220             if ( $func eq 'update_employee' ) {
1221             return $fail unless shared_employee_acl_part2( $self );
1222 0           } elsif ( $func eq 'insert_employee' ) {
1223             $context->{'request_entity'}->{'nick'} = $context->{'mapping'}->{'nick'};
1224             } else {
1225             die "AAAAAAAAAAAAGAGGGGGGGGAAAHAHAAHHHH!";
1226             }
1227             return $iue_dispatch{$func}->(
1228             $self,
1229             $context->{'stashed_employee_object'},
1230             $context->{'request_entity'}
1231             );
1232             }
1233              
1234              
1235 0     0 1   =head3 handler_post_employee_nick
1236 0            
1237             Handler for 'POST employee/nick' - a little complicated because it can
1238 0           be insert or update, depending on whether or not the employee exists.
1239              
1240             =cut
1241 0 0          
1242             my ( $self, $pass ) = @_;
1243 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_post_employee_nick" );
1244 0 0          
1245 0           my $context = $self->context;
1246              
1247 0           # first pass
1248             return 1 if $pass == 1;
1249 0 0          
1250 0           # second pass
1251 0           my ( $nick, $emp, $func );
1252 0           if ( $nick = $context->{'request_entity'}->{'nick'} ) {
1253             $emp = shared_first_pass_lookup( $self, 'nick', $nick );
1254             $func = $emp ? 'update_employee' : 'insert_employee';
1255             return $fail unless shared_employee_acl_part1( $self, $emp ); # additional ACL checks
1256 0           $self->nullify_declared_status;
1257 0           } else {
1258 0 0         $self->mrest_declare_status( code => 400,
    0          
1259 0 0         explanation => 'DISPATCH_PROP_MISSING_IN_ENTITY', args => [ 'nick' ],
1260             );
1261 0           return $fail;
1262             }
1263 0           if ( $func eq 'update_employee' ) {
1264             delete $context->{'request_entity'}->{'nick'};
1265             return $fail unless shared_employee_acl_part2( $self );
1266             } elsif ( $func eq 'insert_employee' ) {
1267             $log->info( "Ready to insert new employee $nick" );
1268 0           } else {
1269             die "AAAAAAAAAAAAGAGGGGGGGGAAAHAHAAHHHH!";
1270             }
1271             die "AAGAGAGAGGGGGGGGG self is undef" unless defined $self;
1272              
1273             return $iue_dispatch{$func}->(
1274             $self,
1275             $emp,
1276             $context->{'request_entity'}
1277             );
1278              
1279             }
1280              
1281 0     0 1    
1282 0           =head3 handler_post_employee_self
1283              
1284 0           Handler for 'POST employee/{current,self}' resources. The request entity
1285             is supposed to contain a list of key:value pairs where the keys are properties
1286             of the employee object and the values are new values for those properties, e.g.:
1287 0 0          
1288             {
1289             "fullname" : "Bubba Jones",
1290 0           "nick" : "bubba"
1291 0 0         }
1292 0            
1293 0 0         Note that it should be possible to set a property to null:
1294 0 0          
1295 0           { "fullname" : null }
1296              
1297 0           The JSON will be converted into a Perl hashref, of course, and that will
1298             be handed off to the DBI for insertion into placeholders in an UPDATE statement.
1299              
1300 0           =cut
1301              
1302 0 0         my ( $self, $pass ) = @_;
    0          
1303 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_post_employee_self (pass $pass)" );
1304 0 0          
1305             # first pass
1306 0           return 1 if $pass == 1;
1307            
1308 0           # second pass
1309             my $context = $self->context;
1310 0 0         return $fail unless shared_employee_acl_part2( $self );
1311             return shared_update_employee(
1312             $self,
1313             $context->{'current_obj'},
1314             $context->{'request_entity'}
1315 0           );
1316             }
1317              
1318              
1319             =head3 handler_delete_employee_eid
1320              
1321             Handler for 'DELETE employee/eid/:eid' resource.
1322              
1323             =cut
1324              
1325             my ( $self, $pass ) = @_;
1326             $log->debug( "Entering " . __PACKAGE__ . "::handler_delete_employee_eid" );
1327              
1328             # first pass
1329             if ( $pass == 1 ) {
1330             return $self->handler_get_employee_eid( $pass );
1331             }
1332              
1333             # second pass
1334             my $context = $self->context;
1335             return $context->{'stashed_employee_object'}->delete( $context );
1336             }
1337              
1338              
1339             =head3 handler_get_employee_eid
1340              
1341             Handler for 'GET employee/eid/:eid'
1342 0     0 1    
1343 0           =cut
1344              
1345             my ( $self, $pass ) = @_;
1346 0 0         $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_eid" );
1347             return shared_get_employee( $self, $pass, 'EID', $self->context->{'mapping'}->{'eid'} );
1348             }
1349 0            
1350 0 0          
1351             =head3 _ldap_sync_pass1
1352              
1353             =cut
1354 0            
1355             my ( $self, $emp ) = @_;
1356             $log->debug( "Entering " . __PACKAGE__ . "::_ldap_sync_pass1" );
1357              
1358             my $status = $emp->ldap_sync();
1359             $log->debug( "ldap_sync status: " . Dumper( $status ) );
1360             if ( $status->not_ok ) {
1361             if ( $status->code eq 'DOCHAZKA_LDAP_SYSTEM_USER_NOSYNC' ) {
1362             # system user - 403
1363             $status->{'http_code'} = 403;
1364             } else {
1365             $status->{'http_code'} = 404;
1366 0     0 1   }
1367 0           $self->mrest_declare_status( $status );
1368             return 0;
1369             }
1370 0 0         $self->context->{'stashed_employee_object'} = $emp;
1371 0           return 1;
1372             }
1373              
1374              
1375 0           =head3 handler_get_employee_ldap
1376 0            
1377             Handler for 'GET employee/nick/:nick/ldap' resource.
1378              
1379             =cut
1380              
1381             my ( $self, $pass ) = @_;
1382             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_ldap" );
1383              
1384             my $context = $self->context;
1385             my $nick = $context->{'mapping'}->{'nick'};
1386              
1387 0     0 1   if ( $pass == 1 ) {
1388 0           my $emp = App::Dochazka::REST::Model::Employee->spawn(
1389 0           'nick' => $nick,
1390             'sync' => 1,
1391             );
1392             return $self->_ldap_sync_pass1( $emp );
1393             }
1394              
1395             return $CELL->status_ok( 'DOCHAZKA_LDAP_LOOKUP', payload => $context->{'stashed_employee_object'} );
1396             }
1397              
1398 0     0      
1399 0           =head3 handler_put_employee_ldap
1400              
1401 0           Handler for 'PUT employee/nick/:nick/ldap' resource.
1402 0            
1403 0 0         =cut
1404 0 0          
1405             my ( $self, $pass ) = @_;
1406 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_put_employee_ldap" );
1407              
1408 0           my $context = $self->context;
1409             $log->debug( "mapping " . Dumper( $context->{'mapping'} ) );
1410 0           my $nick = $context->{'mapping'}->{'nick'};
1411 0           my $status;
1412              
1413 0           # first pass
1414 0           if ( $pass == 1 ) {
1415             # determine if this is an insert or an update
1416             my $emp = shared_first_pass_lookup( $self, 'nick', $nick );
1417             $self->nullify_declared_status;
1418             return 0 unless shared_employee_acl_part1( $self, $emp ); # additional ACL checks
1419             if ( $emp ) {
1420             $context->{'put_employee_func'} = 'update_employee';
1421             } else {
1422             $context->{'put_employee_func'} = 'insert_employee';
1423             $emp = App::Dochazka::REST::Model::Employee->spawn( 'nick' => $nick );
1424             }
1425 0     0 1   $emp->sync( 1 );
1426 0           return $self->_ldap_sync_pass1( $emp );
1427             }
1428 0            
1429 0           # second pass
1430              
1431 0 0         my $emp = $context->{'stashed_employee_object'};
1432 0           my $func = $context->{'put_employee_func'};
1433             if ( $func eq 'update_employee' ) {
1434             $log->debug( "Updating employee from LDAP" );
1435             $status = $emp->update( $context );
1436 0           } elsif ( $func eq 'insert_employee' ) {
1437             $log->debug( "Inserting new employee from LDAP" );
1438             $status = $emp->insert( $context );
1439 0           } else {
1440             die "AAAAAAAAAAAAGAGGGGGGGGAAAHAHAAHHHH!";
1441             }
1442              
1443             return $status;
1444             }
1445              
1446              
1447             =head3 handler_get_employee_minimal
1448              
1449             Handler for 'GET employee/eid/:eid/minimal' resource.
1450 0     0 1   Handler for 'GET employee/nick/:nick/minimal' resource.
1451 0           Handler for 'GET employee/sec_id/:sec_id/minimal' resource.
1452              
1453 0           =cut
1454 0            
1455 0           my ( $self, $pass ) = @_;
1456 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_minimal" );
1457              
1458             if ( $pass == 1 ) {
1459 0 0          
1460             # determine key and value
1461 0           my $resource_name = $self->context->{'resource_name'};
1462 0           my ( $key, $value );
1463 0 0         if ( $resource_name eq 'employee/eid/:eid/minimal' ) {
1464 0 0         $key = 'EID';
1465 0           $value = $self->context->{'mapping'}->{'eid'};
1466             } elsif ( $resource_name eq 'employee/nick/:nick/minimal' ) {
1467 0           $key = 'nick';
1468 0           $value = $self->context->{'mapping'}->{'nick'};
1469             } elsif ( $resource_name eq 'employee/sec_id/:sec_id/minimal' ) {
1470 0           $key = 'sec_id';
1471 0           $value = $self->context->{'mapping'}->{'sec_id'};
1472             }
1473              
1474             # ACL check
1475             my $priv = $self->context->{'current_priv'};
1476 0           if ( $priv eq 'passerby' ) {
1477 0           if ( ! acl_check_is_me( $self, ( lc $key ) => $value ) ) {
1478 0 0         $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
    0          
1479 0           return 0;
1480 0           }
1481             }
1482 0            
1483 0           # populate $emp
1484             my $emp = shared_first_pass_lookup( $self, $key, $value );
1485 0           return 0 unless $emp;
1486              
1487             # populate stashed value
1488 0           my $min_fields = $site->DOCHAZKA_EMPLOYEE_MINIMAL_FIELDS;
1489             die "AGACHCH! Problem with DOCHAZKA_EMPLOYEE_MINIMAL_FIELDS site param, which is set to " .
1490             Dumper( $min_fields ) . " with the following sitedirs loaded: " .
1491             Dumper( $meta->CELL_META_SITEDIR_LIST ) unless ref( $min_fields) eq 'ARRAY';
1492             foreach my $prop ( @{ $site->DOCHAZKA_EMPLOYEE_MINIMAL_FIELDS } ) {
1493             $self->context->{'stashed_value'}->{ $prop } = $emp->get( $prop );
1494             }
1495              
1496             return 1;
1497             }
1498              
1499             return $CELL->status_ok( 'DOCHAZKA_EMPLOYEE_MINIMAL', payload => $self->context->{'stashed_value'} );
1500             }
1501 0     0 1    
1502 0            
1503             =head3 handler_get_employee_eid_team
1504 0 0          
1505             Handler for 'GET employee/eid/:eid/team'
1506              
1507 0           =cut
1508 0            
1509 0 0         my ( $self, $pass ) = @_;
    0          
    0          
1510 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_eid_team" );
1511 0            
1512             if ( $pass == 1 ) {
1513 0           return $self->handler_get_employee_eid( $pass );
1514 0           }
1515              
1516 0           my $context = $self->context;
1517 0           my $dbix_conn = $context->{'dbix_conn'};
1518             return $context->{'stashed_employee_object'}->team_nicks( $dbix_conn );
1519             }
1520              
1521 0            
1522 0 0         =head3 handler_get_employee_nick_team
1523 0 0          
1524 0           Handler for 'GET employee/nick/:nick/team'
1525 0            
1526             =cut
1527              
1528             my ( $self, $pass ) = @_;
1529             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_nick_team" );
1530 0            
1531 0 0         if ( $pass == 1 ) {
1532             return $self->handler_get_employee_nick( $pass );
1533             }
1534 0            
1535 0 0         my $context = $self->context;
1536             my $dbix_conn = $context->{'dbix_conn'};
1537             return $context->{'stashed_employee_object'}->team_nicks( $dbix_conn );
1538 0           }
  0            
1539 0            
1540              
1541             =head3 handler_delete_employee_nick
1542 0            
1543             Handler for 'DELETE employee/nick/:nick'
1544              
1545 0           =cut
1546              
1547             my ( $self, $pass ) = @_;
1548             $log->debug( "Entering " . __PACKAGE__ . "::handler_delete_employee_nick" );
1549              
1550             # first pass
1551             if ( $pass == 1 ) {
1552             return $self->handler_get_employee_nick( $pass );
1553             }
1554              
1555             # second pass
1556 0     0 1   my $context = $self->context;
1557 0           return $context->{'stashed_employee_object'}->delete( $context );
1558             }
1559 0 0          
1560 0            
1561             =head3 handler_get_employee_nick
1562              
1563 0           Handler for 'GET employee/nick/:nick'
1564 0            
1565 0           =cut
1566              
1567             my ( $self, $pass ) = @_;
1568             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_nick" );
1569             return shared_get_employee( $self, $pass, 'nick', $self->context->{'mapping'}->{'nick'} );
1570             }
1571              
1572              
1573             =head3 handler_get_employee_sec_id
1574              
1575             Handler for 'GET employee/sec_id/:sec_id'
1576 0     0 1    
1577 0           =cut
1578              
1579 0 0         my ( $self, $pass ) = @_;
1580 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_sec_id" );
1581             return shared_get_employee( $self, $pass, 'sec_id', $self->context->{'mapping'}->{'sec_id'} );
1582             }
1583 0            
1584 0            
1585 0           =head3 handler_get_employee_search_nick
1586              
1587             Handler for 'GET employee/search/nick/:key'
1588              
1589             =cut
1590              
1591             my ( $self, $pass ) = @_;
1592             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_search_nick" );
1593              
1594             # first pass
1595             return 1 if $pass == 1;
1596 0     0 1    
1597 0           # second pass
1598             my $key = $self->context->{'mapping'}->{'key'};
1599             $key = "%$key%" unless $key =~ m/%/;
1600 0 0         my $status = $CELL->status_ok;
1601 0           $status = load_multiple(
1602             conn => $self->context->{'dbix_conn'},
1603             class => 'App::Dochazka::REST::Model::Employee',
1604             sql => $site->SQL_EMPLOYEE_SELECT_MULTIPLE_BY_NICK,
1605 0           keys => [ $key ],
1606 0           );
1607             # check for 404
1608             if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
1609             $self->mrest_declare_status( code => 404,
1610             explanation => "DISPATCH_SEARCH_EMPTY",
1611             args => [ 'employee', "nick LIKE $key" ],
1612             );
1613             return $fail;
1614             }
1615             return $status if $status->not_ok;
1616              
1617 0     0 1   # found some employee objects
1618 0           foreach my $emp ( @{ $status->payload } ) {
1619 0           $emp = $emp->TO_JSON;
1620             }
1621             return $status;
1622             }
1623              
1624              
1625             =head2 Genreport handlers
1626              
1627             =head3 handler_genreport
1628              
1629             Handler for the 'POST genreport' resource.
1630 0     0 1    
1631 0           =cut
1632 0            
1633             my ( $self, $pass ) = @_;
1634             $log->debug( "Entering " . __PACKAGE__ . "::handler_genreport" );
1635              
1636             # first pass
1637             return 1 if $pass == 1;
1638              
1639             # second pass
1640             # - check that entity is kosher
1641             my $status = shared_entity_check( $self, 'path' );
1642             return $status unless $status->ok;
1643 0     0 1   my $context = $self->context;
1644 0           my $entity = $context->{'request_entity'};
1645              
1646             # - get path and look it up
1647 0 0         my $path = $entity->{'path'};
1648             my $comp = shared_first_pass_lookup( $self, 'path', $path );
1649             return $fail unless $path;
1650 0           delete $entity->{'path'};
1651 0 0          
1652 0           # - if there is a 'parameters' property, check that it is a hashref
1653             my $parameters;
1654 0           if ( $entity->{'parameters'} ) {
1655             $log->debug( "Vetting parameters: " . Dumper $entity->{'parameters'} ) ;
1656             if ( ref( $entity->{'parameters'} ) ne 'HASH' ) {
1657             $self->mrest_declare_status(
1658             code => 400,
1659             explanation => 'parameters must be given as key:value pairs'
1660 0 0 0       );
1661 0           return $fail;
1662             }
1663             # - convert $parameters hashref into $parameters arrayref for validation
1664             my $count = 0;
1665 0           foreach my $key ( keys %{ $entity->{'parameters'} } ) {
1666             $parameters->[$count] = $key;
1667 0 0         $count += 1;
1668             $parameters->[$count] = $entity->{'parameters'}->{$key};
1669             $count += 1;
1670 0           }
  0            
1671 0           }
1672              
1673 0           # - if there is a validations property, convert it into a hashref
1674             # and check the parameters against it
1675             if ( $comp->{validations} ) {
1676             my $validations = eval $comp->{validations};
1677             $log->debug( "Validations before eval: " . Dumper $comp->{validations} );
1678             $log->debug( "Validations after eval: " . Dumper $validations );
1679             die "AGAAKH! validations is not a HASHREF: $validations" unless
1680             ref( $validations ) eq 'HASH';
1681             $parameters = {} if not defined $parameters;
1682             $log->debug( "About to validate parameters: " . Dumper $parameters );
1683             my $success = 1;
1684             validate_with(
1685             params => $parameters,
1686 0     0 1   spec => $validations,
1687 0           on_fail => sub {
1688             my $errmsg = shift;
1689             $self->mrest_declare_status( code => 400, explanation => $errmsg );
1690 0 0         $success = 0;
1691             },
1692             );
1693             return $fail unless $success;
1694 0           } elsif ( $parameters ) {
1695 0 0         $log->WARN( "Parameters were given to component, but component has no validations!" );
1696 0           }
1697 0            
1698             # - generate report
1699             $parameters = [] if not defined $parameters;
1700 0           return $CELL->status_ok(
1701 0           'DISPATCH_GENERATED_REPORT',
1702 0 0         payload => $comp->generate( my %paramhash = @$parameters )
1703 0           );
1704             }
1705              
1706 0            
1707 0 0         =head2 History handlers
1708 0            
1709 0 0          
1710 0           =head3 handler_history_self
1711              
1712             Handler method for the '{priv,schedule}/history/self/?:tsrange' resource.
1713              
1714 0           =cut
1715              
1716             my ( $self, $pass ) = @_;
1717 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_history_self" );
1718 0            
  0            
1719 0           # first pass
1720 0           return 1 if $pass == 1;
1721 0            
1722 0           # second pass
1723             my $context = $self->context;
1724             my %ARGS = (
1725             'eid' => $context->{'current'}->{'eid'},
1726             'nick' => $context->{'current'}->{'nick'},
1727             );
1728 0 0          
    0          
1729 0           if ( defined $context->{'mapping'}->{'tsrange'} ) {
1730 0           $ARGS{'tsrange'} = $context->{'mapping'}->{'tsrange'};
1731 0           }
1732 0 0        
1733             if ( $context->{'components'}->[0] eq 'priv' ) {
1734 0 0         return get_privhistory( $context, %ARGS );
1735 0           } elsif ( $context->{'components'}->[0] eq 'schedule' ) {
1736 0           return get_schedhistory( $context, %ARGS );
1737             }
1738             }
1739              
1740              
1741 0     0     =head3 handler_history_get_single
1742 0            
1743 0           Handler method for GET requests on the '/{priv,schedule}/history/eid/..' and
1744             '/{priv,schedule}/history/nick/..' resources (potentially returning
1745 0           a single record).
1746 0 0          
1747             =cut
1748 0            
1749             my ( $self, $pass ) = @_;
1750             $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_single" );
1751              
1752 0 0         my ( $context, $method, $mapping, undef, $ts, $key, $value ) = shared_history_init( $self->context );
1753 0            
1754             # first pass
1755             if ( $pass == 1 ) {
1756             my $emp = shared_first_pass_lookup( $self, $key, $value );
1757             return 0 unless $emp;
1758             $self->context->{'stashed_employee_obj'} = $emp;
1759             return 1;
1760             }
1761              
1762             # second pass
1763             my $prop = $context->{'components'}->[0];
1764             my $emp = $self->context->{'stashed_employee_obj'};
1765             my $status;
1766             if ( $prop eq 'priv' ) {
1767             $status = App::Dochazka::REST::Model::Privhistory->load_by_eid(
1768             $context->{'dbix_conn'},
1769             $emp->eid,
1770 0     0 1   $ts
1771 0           );
1772             } elsif ( $prop eq 'schedule' ) {
1773             $status = App::Dochazka::REST::Model::Schedhistory->load_by_eid(
1774 0 0         $context->{'dbix_conn'},
1775             $emp->eid,
1776             $ts
1777 0           );
1778             } else {
1779             die "BGUDFUUFF! Improper prop ->$prop<- seen!";
1780 0           }
1781             # - process return value
1782             if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
1783 0 0         my $tsmsg = ( $ts ) ? $ts : 'now';
1784 0           $self->mrest_declare_status(
1785             code => 404,
1786             explanation => "No $prop history for $key $value as of $tsmsg",
1787 0 0         );
    0          
1788 0           return $fail;
1789             } elsif ( $status->not_ok ) {
1790 0           $self->mrest_declare_status(
1791             code => 500,
1792             explanation => $status->text,
1793             );
1794             return $fail;
1795             }
1796             return $status;
1797             }
1798              
1799              
1800             =head3 handler_history_get_multiple
1801              
1802             Handler method for GET requests on the '/{priv,schedule}/history/eid/..' and
1803             '/{priv,schedule}/history/nick/..' resources (all potentially returning
1804 0     0 1   multiple records).
1805 0            
1806             =cut
1807 0            
1808             my ( $self, $pass ) = @_;
1809             $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_multiple" );
1810 0 0          
1811 0           my ( $context, $method, $mapping, $tsrange, undef, $key, $value ) = shared_history_init( $self->context );
1812 0 0          
1813 0           # first pass
1814 0           if ( $pass == 1 ) {
1815             my $emp = shared_first_pass_lookup( $self, $key, $value );
1816             return 0 unless $emp;
1817             $self->context->{'stashed_employee_obj'} = $emp;
1818 0           return 1;
1819 0           }
1820 0            
1821 0 0         # second pass
    0          
1822             my ( $class, $prop, undef ) = shared_get_class_prop_id( $context );
1823 0           my $emp = $self->context->{'stashed_employee_obj'};
1824             my $status = App::Dochazka::REST::Model::Shared::get_history(
1825             $prop,
1826             $context->{'dbix_conn'},
1827             eid => $emp->eid,
1828             nick => $emp->nick,
1829 0           tsrange => $tsrange,
1830             );
1831             # - process return value
1832             if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
1833             $self->mrest_declare_status( code => 404, explanation => "No history for $key $value $tsrange" );
1834 0           return $fail;
1835             } elsif ( $status->not_ok ) {
1836             $self->mrest_declare_status( code => 500, explanation => $status->text );
1837 0 0 0       return $fail;
    0          
1838 0 0         }
1839 0           return $status;
1840             }
1841              
1842              
1843 0           =head3 handler_history_post
1844              
1845 0           Handler method for POST requests on the '/{priv,schedule}/history/eid/..' and
1846             '/{priv,schedule}/history/nick/..' resources.
1847              
1848             =cut
1849 0            
1850             my ( $self, $pass ) = @_;
1851 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_history_post" );
1852              
1853             my ( $context, undef, undef, undef, undef, $key, $value ) = shared_history_init( $self->context );
1854              
1855             # first pass
1856             if ( $pass == 1 ) {
1857             # get employee object from key+value
1858             my $emp = shared_first_pass_lookup( $self, $key, $value );
1859             return 0 unless $emp;
1860             $self->context->{'stashed_employee_obj'} = $emp;
1861             $self->context->{'post_is_create'} = 1;
1862             return 1;
1863             }
1864 0     0 1    
1865 0           # second pass
1866             my ( $class, $prop, $id ) = shared_get_class_prop_id( $context );
1867 0           my $emp = $context->{'stashed_employee_obj'};
1868              
1869             my $entity = $context->{'request_entity'};
1870 0 0         if ( $prop eq 'sid' ) {
1871 0           # we might have scode instead of sid in the entity
1872 0 0         if ( $entity->{'scode'} and not $entity->{'sid'} ) {
1873 0           my $sched = shared_first_pass_lookup( $self, 'scode', $entity->{'scode'} );
1874 0           if ( $sched ) {
1875             $entity->{'sid'} = $sched->sid;
1876             } else {
1877             $self->mrest_declare_status(
1878 0           explanation => 'Schedule code ' . $entity->{'scode'} . ' not found',
1879 0           permanent => 1,
1880             );
1881             return $fail;
1882 0           }
1883             }
1884             }
1885              
1886             # - check entity for presence of certain properties
1887             my $status = shared_entity_check( $self, $prop, 'effective' );
1888 0 0 0       return $status unless $status->ok;
    0          
1889 0            
1890 0           # - run the insert operation
1891             my $ho;
1892 0           try {
1893 0           $ho = $class->spawn(
1894             eid => $emp->eid,
1895 0           effective => $entity->{'effective'},
1896             $prop => $entity->{$prop},
1897             remark => $entity->{'remark'},
1898             );
1899             } catch {
1900             $log->crit($_);
1901             return $CELL->status_crit("DISPATCH_HISTORY_COULD_NOT_SPAWN", args => [ $_ ] );
1902             };
1903             $status = $ho->insert( $context );
1904             if ( $status->not_ok ) {
1905             $self->context->{'create_path'} = $status->level;
1906             if ( $status->code eq 'DOCHAZKA_MALFORMED_400' ) {
1907 0     0 1   return $self->mrest_declare_status(
1908 0           code => 400,
1909             explanation => "Check syntax of your request entity"
1910 0           );
1911             }
1912             return $self->mrest_declare_status(
1913 0 0         code => 500,
1914             explanation => $status->code,
1915 0           args => $status->args
1916 0 0         );
1917 0           }
1918 0           $self->context->{'create_path'} = '.../history/phid/' . ( $status->payload->{$id} || 'UNDEF' );
1919 0           return $status;
1920             }
1921              
1922              
1923 0           =head3 handler_history_get_phid
1924 0            
1925             Handler for 'GET priv/history/phid/:phid'
1926 0            
1927 0 0         =cut
1928              
1929 0 0 0       my ( $self, $pass ) = @_;
1930 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_phid" );
1931 0 0          
1932 0           # first pass
1933             if ( $pass == 1 ) {
1934             my $p_obj = shared_first_pass_lookup( $self, 'PHID', $self->context->{'mapping'}->{'phid'} );
1935 0           return 0 unless $p_obj;
1936             $self->context->{'stashed_history_object'} = $p_obj;
1937             return 1;
1938 0           }
1939              
1940             # second pass
1941             return $CELL->status_ok(
1942             'DISPATCH_HISTORY_RECORD_FOUND',
1943             payload => $self->context->{'stashed_history_object'},
1944 0           );
1945 0 0         }
1946              
1947              
1948 0           =head3 handler_history_post_phid
1949              
1950             Handler for 'POST priv/history/phid/:phid'
1951              
1952             =cut
1953              
1954 0     0     my ( $self, $pass ) = @_;
1955             $log->debug( "Entering " . __PACKAGE__ . "::handler_history_post_phid" );
1956              
1957 0     0     # first pass
1958 0           if ( $pass == 1 ) {
1959 0           my $p_obj = shared_first_pass_lookup( $self, 'PHID', $self->context->{'mapping'}->{'phid'} );
1960 0           return 0 unless $p_obj;
1961 0 0         $self->context->{'stashed_history_object'} = $p_obj;
1962 0           return 1;
1963 0 0         }
1964 0            
1965             # second pass
1966             return shared_update_history(
1967             $self,
1968             $self->context->{'stashed_history_object'},
1969 0           $self->context->{'request_entity'}
1970             );
1971             }
1972              
1973              
1974             =head3 handler_history_delete_phid
1975 0   0        
1976 0           =cut
1977              
1978             my ( $self, $pass ) = @_;
1979             $log->debug( "Entering " . __PACKAGE__ . "::handler_history_delete_phid" );
1980             return $self->handler_history_get_phid(1) if $pass == 1;
1981             return $self->context->{'stashed_history_object'}->delete( $self->context );
1982             }
1983              
1984              
1985             =head3 handler_history_get_shid
1986              
1987 0     0 1   Handler for 'GET schedule/history/shid/:shid'
1988 0            
1989             =cut
1990              
1991 0 0         my ( $self, $pass ) = @_;
1992 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_shid" );
1993 0 0          
1994 0           # first pass
1995 0           if ( $pass == 1 ) {
1996             my $s_obj = shared_first_pass_lookup( $self, 'SHID', $self->context->{'mapping'}->{'shid'} );
1997             return 0 unless $s_obj;
1998             $self->context->{'stashed_history_object'} = $s_obj;
1999             return 1;
2000             }
2001 0            
2002             # second pass
2003             return $CELL->status_ok(
2004             'DISPATCH_HISTORY_RECORD_FOUND',
2005             payload => $self->context->{'stashed_history_object'},
2006             );
2007             }
2008              
2009              
2010             =head3 handler_history_post_shid
2011              
2012             Handler for 'POST priv/history/shid/:shid'
2013 0     0 1    
2014 0           =cut
2015              
2016             my ( $self, $pass ) = @_;
2017 0 0         $log->debug( "Entering " . __PACKAGE__ . "::handler_history_post_shid" );
2018 0            
2019 0 0         # first pass
2020 0           if ( $pass == 1 ) {
2021 0           my $p_obj = shared_first_pass_lookup( $self, 'SHID', $self->context->{'mapping'}->{'shid'} );
2022             return 0 unless $p_obj;
2023             $self->context->{'stashed_history_object'} = $p_obj;
2024             return 1;
2025             }
2026              
2027             # second pass
2028 0           return shared_update_history(
2029             $self,
2030             $self->context->{'stashed_history_object'},
2031             $self->context->{'request_entity'}
2032             );
2033             }
2034              
2035              
2036             =head3 handler_history_delete_shid
2037              
2038 0     0 1   =cut
2039 0            
2040 0 0         my ( $self, $pass ) = @_;
2041 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_history_delete_shid" );
2042             return $self->handler_history_get_shid(1) if $pass == 1;
2043             return $self->context->{'stashed_history_object'}->delete( $self->context );
2044             }
2045              
2046              
2047             =head2 Interval handlers
2048              
2049              
2050             =head3 handler_interval_eid
2051              
2052 0     0 1   Handler for
2053 0            
2054             GET interval/eid/:eid/:tsrange
2055             DELETE interval/eid/:eid/:tsrange
2056 0 0          
2057 0           #FIXME: implement a configurable limit on the tsrange
2058 0 0          
2059 0           =cut
2060 0            
2061             my ( $self, $pass ) = @_;
2062             $log->debug( "Entering " . __PACKAGE__ . "::handler_interval_eid " );
2063              
2064             return $self->_handler_intlock( 'Interval', 'eid', $pass );
2065             }
2066 0            
2067              
2068             =head3 handler_get_lock_eid
2069              
2070             Handler for 'GET lock/eid/:eid/:tsrange'
2071              
2072             #FIXME: implement a configurable limit on the tsrange
2073              
2074             =cut
2075              
2076             my ( $self, $pass ) = @_;
2077             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_lock_eid " );
2078 0     0 1    
2079 0           return $self->_handler_intlock( 'Lock', 'eid', $pass );
2080             }
2081              
2082 0 0          
2083 0           =head3 handler_interval_nick
2084 0 0          
2085 0           Handler for
2086 0            
2087             GET interval/nick/:nick/:tsrange
2088             DELETE interval/nick/:nick/:tsrange
2089              
2090             #FIXME: implement a configurable limit on the tsrange
2091              
2092             =cut
2093 0            
2094             my ( $self, $pass ) = @_;
2095             $log->debug( "Entering " . __PACKAGE__ . "::handler_interval_nick " );
2096              
2097             return $self->_handler_intlock( 'Interval', 'nick', $pass );
2098             }
2099              
2100              
2101             =head3 handler_get_lock_nick
2102              
2103 0     0 1   Handler for 'GET lock/nick/:nick/:tsrange'
2104 0            
2105 0 0         #FIXME: implement a configurable limit on the tsrange
2106 0            
2107             =cut
2108              
2109             my ( $self, $pass ) = @_;
2110             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_lock_nick " );
2111              
2112             return $self->_handler_intlock( 'Lock', 'nick', $pass );
2113             }
2114              
2115              
2116             =head3 handler_interval_self
2117              
2118             Handler for
2119              
2120             GET interval/self/:tsrange
2121             DELETE interval/self/:tsrange
2122              
2123             #FIXME: implement a configurable limit on the tsrange
2124              
2125 0     0 1   =cut
2126 0            
2127             my ( $self, $pass ) = @_;
2128 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_interval_self " );
2129              
2130             return $self->_handler_intlock( 'Interval', 'self', $pass );
2131             }
2132              
2133              
2134             =head3 handler_get_lock_self
2135              
2136             Handler for 'GET lock/self/:tsrange'
2137              
2138             #FIXME: implement a configurable limit on the tsrange
2139              
2140             =cut
2141 0     0 1    
2142 0           my ( $self, $pass ) = @_;
2143             $log->debug( "Entering " . __PACKAGE__ . "::handler_get_lock_self " );
2144 0            
2145             return $self->_handler_intlock( 'Lock', 'self', $pass );
2146             }
2147              
2148              
2149             my ( $self, $intlock, $key, $pass ) = @_;
2150              
2151             my $context = $self->context;
2152              
2153             # first pass
2154             if ( $pass == 1 ) {
2155              
2156             # determine the employee
2157             my $value;
2158             if ( $key eq 'self' ) {
2159             $key = 'eid';
2160 0     0 1   $value = $context->{'current'}->{'eid'};
2161 0           } else {
2162             $value = $context->{'mapping'}->{ $key };
2163 0           }
2164             if (
2165             ! acl_check_is_me( $self, $key => $value ) and
2166             ! acl_check_is_my_report( $self, $key => $value )
2167             )
2168             {
2169             $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2170             return 0;
2171             }
2172             my $emp = shared_first_pass_lookup( $self, $key, $value );
2173             return 0 unless $emp;
2174              
2175             # determine the tsrange
2176 0     0 1   my $status = _tsrange_from_context( $context );
2177 0           return $status unless $status->ok;
2178             my $tsr = $status->payload;
2179 0            
2180             my @ARGS = (
2181             $context->{'dbix_conn'},
2182             $emp->eid,
2183             $tsr,
2184             );
2185             my $method = $self->context->{'method'};
2186             my $resource = $self->context->{'resource_name'};
2187             $log->debug( "_handler_intlock: resource is $resource" );
2188             if ( $method eq 'GET' and $intlock eq 'Interval' ) {
2189             $status = fetch_intervals_by_eid_and_tsrange( @ARGS );
2190             } elsif ( $method eq 'GET' and $intlock eq 'Lock' ) {
2191             $status = fetch_locks_by_eid_and_tsrange( @ARGS );
2192             } elsif ( $method eq 'GET' and $intlock eq 'Summary' ) {
2193             $status = generate_interval_summary( @ARGS );
2194             if ( $status->level eq 'ERR' and
2195 0     0 1   $status->code eq 'DISPATCH_SUMMARY_ILLEGAL_TSRANGE' ) {
2196 0           $self->mrest_declare_status( 'code' => 400,
2197             'explanation' => $status->text );
2198 0           return 0;
2199             }
2200             } elsif ( $method eq 'DELETE' and $intlock eq 'Interval' ) {
2201             $status = delete_intervals_by_eid_and_tsrange( @ARGS );
2202             } else {
2203             die "AGACHCH!! Horrible, horrible: " . ( $intlock || "undef" );
2204             }
2205             if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
2206             $self->mrest_declare_status( explanation => 'DISPATCH_NOTHING_IN_TSRANGE',
2207             args => [ 'attendance intervals', $tsr ]
2208             );
2209             return 0;
2210             }
2211 0     0 1   $context->{'stashed_attendance_status'} = $status;
2212 0           return 1;
2213             }
2214 0            
2215             # second pass
2216             return $context->{'stashed_attendance_status'};
2217             }
2218              
2219 0     0      
2220             =head3 handler_interval_new
2221 0            
2222             Handler for 'POST interval/new'
2223              
2224 0 0         =cut
2225              
2226             my ( $self, $pass ) = @_;
2227 0           $log->debug( "Entering " . __PACKAGE__. "::handler_interval_new" );
2228 0 0          
2229 0           my $context = $self->context;
2230 0            
2231             # first pass
2232 0           if ( $pass == 1 ) {
2233             $context->{'post_is_create'} = 1;
2234 0 0 0       return 1;
2235             }
2236            
2237             # second pass
2238             my $status = shared_entity_check( $self, 'aid', 'intvl' );
2239 0           return $fail if $status->not_ok;
2240 0            
2241             if ( check_acl_context( $context )->not_ok ) {
2242 0           $self->mrest_declare_status( code => 403, explanation => 'DISPATCH_KEEP_TO_YOURSELF' );
2243 0 0         return $fail;
2244             }
2245              
2246 0           return shared_insert_interval( $self );
2247 0 0         }
2248 0            
2249              
2250             =head3 handler_post_interval_iid
2251 0            
2252             Handler for 'POST interval/iid'.
2253              
2254             =cut
2255 0            
2256 0           my ( $self, $pass ) = @_;
2257 0           $log->debug( "Entering " . __PACKAGE__. "::handler_post_interval_iid" );
2258 0 0 0        
    0 0        
    0 0        
    0 0        
2259 0           my $context = $self->context;
2260              
2261 0           # first pass
2262             return 1 if $pass == 1;
2263 0            
2264 0 0 0       # second pass
2265             # - get IID
2266 0           my $status = shared_entity_check( $self, 'iid' );
2267             return $fail unless $status->ok;
2268 0           my $iid = $context->{'request_entity'}->{'iid'};
2269              
2270             # - is there an interval with this IID?
2271 0           my $int = shared_first_pass_lookup( $self, 'IID', $iid );
2272             return $fail unless $int;
2273 0   0        
2274             # - additional ACL check
2275 0 0 0       if ( ! acl_check_is_me( $self, 'eid' => $int->eid ) ) {
2276 0           $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2277             return $fail;
2278             }
2279 0            
2280             # - perform the operation
2281 0           return shared_update_intlock( $self, $int, $context->{'request_entity'} );
2282 0           }
2283              
2284              
2285             =head3 handler_get_interval_iid
2286 0            
2287             Handler for 'GET interval/iid/:iid' resource.
2288              
2289             =cut
2290              
2291             my ( $self, $pass ) = @_;
2292             $log->debug( "Entering " . __PACKAGE__. "::handler_get_interval_iid" );
2293              
2294             my $context = $self->context;
2295              
2296             # first pass
2297 0     0 1   if ( $pass == 1 ) {
2298 0            
2299             # - get IID
2300 0           my $iid = $self->context->{'mapping'}->{'iid'};
2301             return 0 unless $iid;
2302              
2303 0 0         # - is there an interval with this IID?
2304 0           my $int = shared_first_pass_lookup( $self, 'IID', $iid );
2305 0           return 0 unless $int;
2306              
2307             # - additional ACL check
2308             if (
2309 0           ! acl_check_is_me( $self, 'eid' => $int->eid ) and
2310 0 0         ! acl_check_is_my_report( $self, 'eid' => $int->eid )
2311             )
2312 0 0         {
2313 0           $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2314 0           return 0;
2315             }
2316              
2317 0           $context->{'stashed_interval_object'} = $int;
2318             return 1;
2319             }
2320              
2321             # second pass
2322             my $int = $context->{'stashed_interval_object'};
2323             my $method = $context->{'method'};
2324             if ( $method eq 'GET' ) {
2325             return $CELL->status_ok( 'DISPATCH_INTERVAL_FOUND', payload => $int );
2326             }
2327             die "AAGAGAGGGGGGGGGGHHGHGHKD! method is " . ( $method || "undef" );
2328 0     0 1   }
2329 0            
2330              
2331 0           =head3 handler_interval_iid
2332              
2333             Handler for 'interval/iid/:iid' resource.
2334 0 0          
2335             =cut
2336              
2337             my ( $self, $pass ) = @_;
2338 0           $log->debug( "Entering " . __PACKAGE__. "::handler_interval_iid" );
2339 0 0          
2340 0           my $context = $self->context;
2341              
2342             # first pass
2343 0           if ( $pass == 1 ) {
2344 0 0          
2345             # - get IID
2346             my $iid = $self->context->{'mapping'}->{'iid'};
2347 0 0         return 0 unless $iid;
2348 0            
2349 0           # - is there an interval with this IID?
2350             my $int = shared_first_pass_lookup( $self, 'IID', $iid );
2351             return 0 unless $int;
2352              
2353 0           # - additional ACL check
2354             if ( ! acl_check_is_me( $self, 'eid' => $int->eid ) ) {
2355             $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2356             return 0;
2357             }
2358              
2359             $context->{'stashed_interval_object'} = $int;
2360             return 1;
2361             }
2362              
2363             # second pass
2364 0     0 1   my $int = $context->{'stashed_interval_object'};
2365 0           my $method = $context->{'method'};
2366             if ( $method =~ m/^(PUT)|(POST)$/ ) {
2367 0           return shared_update_intlock( $self, $int, $context->{'request_entity'} );
2368             } elsif ( $method eq 'DELETE' ) {
2369             return $int->delete( $context );
2370 0 0         }
2371             die "AAGAGAGGGGGGGGGGHHGHGHKD! method is " . ( $method || "undef" );
2372             }
2373 0            
2374 0 0          
2375             =head3 handler_get_interval_summary
2376              
2377 0           Handler for "GET interval/summary/eid/:eid/:tsrange"
2378 0 0          
2379             =cut
2380              
2381 0 0 0       my ( $self, $pass ) = @_;
2382             $log->debug("Reached " . __PACKAGE__ . "::handler_get_interval_summary" );
2383              
2384             my $context = $self->context;
2385              
2386 0           # first pass
2387 0           if ( $pass == 1 ) {
2388             my $rv = $self->_handler_intlock( 'Summary', 'eid', $pass );
2389             return 0 unless $rv;
2390 0           }
2391 0            
2392             return $context->{'stashed_attendance_status'};
2393             }
2394              
2395 0            
2396 0            
2397 0 0         =head2 Lock handlers
2398 0            
2399              
2400 0   0       =head3 handler_lock_new
2401              
2402             Handler for 'POST lock/new'
2403              
2404             =cut
2405              
2406             my ( $self, $pass ) = @_;
2407             $log->debug( "Entering " . __PACKAGE__. "::handler_lock_new" );
2408              
2409             my $context = $self->context;
2410              
2411 0     0 1   # first pass
2412 0           if ( $pass == 1 ) {
2413             $context->{'post_is_create'} = 1;
2414 0           return 1;
2415             }
2416            
2417 0 0         # second pass
2418             my $status = shared_entity_check( $self, 'intvl' );
2419             return $fail if $status->not_ok;
2420 0            
2421 0 0         if ( check_acl_context( $context )->not_ok ) {
2422             $self->mrest_declare_status( code => 403, explanation => 'DISPATCH_KEEP_TO_YOURSELF' );
2423             return $fail;
2424 0           }
2425 0 0          
2426             return shared_insert_lock( $self );
2427             }
2428 0 0          
2429 0            
2430 0           =head3 handler_post_lock_lid
2431              
2432             Handler for 'POST lock/lid'.
2433 0            
2434 0           =cut
2435              
2436             my ( $self, $pass ) = @_;
2437             $log->debug( "Entering " . __PACKAGE__. "::handler_post_lock_lid" );
2438 0            
2439 0           my $context = $self->context;
2440 0 0          
    0          
2441 0           # first pass
2442             return 1 if $pass == 1;
2443 0            
2444             # second pass
2445 0   0       # - get LID
2446             my $status = shared_entity_check( $self, 'lid' );
2447             return $fail unless $status->ok;
2448             my $lid = $context->{'request_entity'}->{'lid'};
2449              
2450             # - is there a lock with this LID?
2451             my $lock = shared_first_pass_lookup( $self, 'LID', $lid );
2452             return $fail unless $lock;
2453              
2454             # - additional ACL check
2455             if ( ! acl_check_is_me( $self, 'eid' => $lock->eid ) ) {
2456 0     0 1   $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2457 0           return $fail;
2458             }
2459 0            
2460             # - perform the operation
2461             return shared_update_intlock( $self, $lock, $context->{'request_entity'} );
2462 0 0         }
2463 0            
2464 0 0          
2465             =head3 handler_get_lock_lid
2466              
2467 0           Handler for 'GET lock/lid/:lid' resource.
2468              
2469             =cut
2470              
2471             my ( $self, $pass ) = @_;
2472             $log->debug( "Entering " . __PACKAGE__. "::handler_get_lock_lid" );
2473              
2474             my $context = $self->context;
2475              
2476             # first pass
2477             if ( $pass == 1 ) {
2478              
2479             # - get LID
2480             my $lid = $self->context->{'mapping'}->{'lid'};
2481             return 0 unless $lid;
2482 0     0 1    
2483 0           # - is there a lock with this LID?
2484             my $lock = shared_first_pass_lookup( $self, 'LID', $lid );
2485 0           return 0 unless $lock;
2486              
2487             # - additional ACL check
2488 0 0         if (
2489 0           ! acl_check_is_me( $self, 'eid' => $lock->eid ) and
2490 0           ! acl_check_is_my_report( $self, 'eid' => $lock->eid )
2491             )
2492             {
2493             $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2494 0           return 0;
2495 0 0         }
2496              
2497 0 0         $context->{'stashed_lock_object'} = $lock;
2498 0           return 1;
2499 0           }
2500              
2501             # second pass
2502 0           my $lock = $context->{'stashed_lock_object'};
2503             my $method = $context->{'method'};
2504             if ( $method eq 'GET' ) {
2505             return $CELL->status_ok( 'DISPATCH_LOCK_FOUND', payload => $lock );
2506             }
2507             die "AAGAGAGGGGGGGGGGHHGHGHKD! method is " . ( $method || "undef" );
2508             }
2509              
2510              
2511             =head3 handler_lock_lid
2512              
2513 0     0 1   Handler for 'lock/lid/:lid' resource.
2514 0            
2515             =cut
2516 0            
2517             my ( $self, $pass ) = @_;
2518             $log->debug( "Entering " . __PACKAGE__. "::handler_lock_lid" );
2519 0 0          
2520             my $context = $self->context;
2521              
2522             # first pass
2523 0           if ( $pass == 1 ) {
2524 0 0          
2525 0           # - get LID
2526             my $lid = $self->context->{'mapping'}->{'lid'};
2527             return 0 unless $lid;
2528 0            
2529 0 0         # - is there a lock with this LID?
2530             my $lock = shared_first_pass_lookup( $self, 'LID', $lid );
2531             return 0 unless $lock;
2532 0 0          
2533 0           # - additional ACL check
2534 0           if (
2535             ! acl_check_is_me( $self, 'eid' => $lock->eid )
2536             )
2537             {
2538 0           $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2539             return 0;
2540             }
2541              
2542             $context->{'stashed_lock_object'} = $lock;
2543             return 1;
2544             }
2545              
2546             # second pass
2547             my $lock = $context->{'stashed_lock_object'};
2548             my $method = $context->{'method'};
2549 0     0 1   if ( $method =~ m/^(PUT)|(POST)$/ ) {
2550 0           return shared_update_intlock( $self, $lock, $context->{'request_entity'} );
2551             } elsif ( $method eq 'DELETE' ) {
2552 0           return $lock->delete( $context );
2553             }
2554             die "AAGAGAGGGGGGGGGGHHGHGHKD! method is " . ( $method || "undef" );
2555 0 0         }
2556              
2557              
2558 0           =head2 Priv handlers
2559 0 0          
2560             =head3 handler_priv_get_eid
2561              
2562 0           =cut
2563 0 0          
2564             my ( $self, $pass ) = @_;
2565             $log->debug( "Entering " . __PACKAGE__ . ":handler_priv_get_eid" );
2566 0 0 0       my $eid = $self->context->{'mapping'}->{'eid'};
2567             return shared_get_privsched( $self, 'priv', $pass, 'EID', $eid );
2568             }
2569              
2570              
2571 0           =head3 handler_priv_get_nick
2572 0            
2573             =cut
2574              
2575 0           my ( $self, $pass ) = @_;
2576 0           $log->debug( "Entering " . __PACKAGE__ . ":handler_priv_get_nick" );
2577             my $nick = $self->context->{'mapping'}->{'nick'};
2578             return shared_get_privsched( $self, 'priv', $pass, 'nick', $nick );
2579             }
2580 0            
2581 0            
2582 0 0         =head3 handler_priv_get_self
2583 0            
2584             =cut
2585 0   0        
2586             my ( $self, $pass ) = @_;
2587             $log->debug( "Entering " . __PACKAGE__ . ":handler_priv_get_self" );
2588             return shared_get_privsched( $self, 'priv', $pass, 'EID', $self->context->{'current'}->{'eid'} );
2589             }
2590              
2591              
2592              
2593             =head2 Schedule handlers
2594              
2595             =head3 schedule_all
2596 0     0 1    
2597 0           Works for both 'GET schedule/all' and 'GET schedule/all/disabled'
2598              
2599 0           =cut
2600              
2601             my ( $self, $pass ) = @_;
2602 0 0         $log->debug( "Entering " . __PACKAGE__ . "::handler_schedule_all" );
2603              
2604             # first pass
2605 0           if ( $pass == 1 ) {
2606 0 0         my $disabled = grep( /disabled/, @{ $self->context->{'components'} } );
2607             my $status = get_all_schedules( conn => $self->context->{'dbix_conn'}, disabled => $disabled );
2608             if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
2609 0           my $explanation = ( $disabled )
2610 0 0         ? 'DISPATCH_NO_SCHEDULES'
2611             : 'DISPATCH_NO_ACTIVE_SCHEDULES';
2612             $self->mrest_declare_status( explanation => $explanation );
2613 0 0         return 0;
2614             }
2615             $self->context->{'stashed_all_schedules_status'} = $status;
2616             }
2617 0            
2618 0           # second pass
2619             return $self->context->{'stashed_all_schedules_status'};
2620             }
2621 0            
2622 0            
2623             =head3 handler_get_schedule_eid
2624              
2625             =cut
2626 0            
2627 0           my ( $self, $pass ) = @_;
2628 0 0         $log->debug( "Entering " . __PACKAGE__ . ":handler_get_schedule_eid" );
    0          
2629 0           return shared_get_privsched( $self, 'schedule', $pass, 'EID', $self->context->{'mapping'}->{'eid'} );
2630             }
2631 0            
2632              
2633 0   0       =head3 handler_interval_fillup
2634              
2635             Handler for "POST interval/fillup"
2636              
2637             =cut
2638              
2639             my ( $self, $pass ) = @_;
2640             $log->debug( "Entering " . __PACKAGE__ . ":handler_interval_fillup" );
2641             return $self->_handler_interval_fillup_scheduled( 'Fillup', $pass );
2642             }
2643              
2644 0     0 1    
2645 0           =head3 handler_interval_scheduled
2646 0            
2647 0           Handler for "POST interval/scheduled"
2648              
2649             =cut
2650              
2651             my ( $self, $pass ) = @_;
2652             $log->debug( "Entering " . __PACKAGE__ . ":handler_interval_scheduled" );
2653             return $self->_handler_interval_fillup_scheduled( 'Scheduled', $pass );
2654             }
2655              
2656 0     0 1    
2657 0           my ( $self, $mode, $pass ) = @_;
2658 0           $log->debug( "Entering " . __PACKAGE__ . "::_handler_interval_fillup_scheduled" );
2659 0            
2660             my $context = $self->context;
2661             my $method = $context->{'method'};
2662             my $path = $context->{'path'};
2663             my $entity = $context->{'request_entity'};
2664             my $supervisor_ok = 0;
2665             my ( $acl_check, $act, $clobber, $dry_run, $emp );
2666              
2667             # first pass
2668 0     0 1   return 1 if $self->_first_pass_always_exists( $pass );
2669 0            
2670 0           # second pass
2671             $log->debug( "_handler_interval_fillup_scheduled(): Commencing pass #2, mode is $mode, entity is " . Dumper( $entity ) );
2672              
2673             # extract employee from request entity
2674             $emp = $self->_extract_employee_spec( $entity );
2675             return $fail unless ref( $emp ) eq 'App::Dochazka::REST::Model::Employee';
2676              
2677             if ($mode eq 'Fillup') {
2678             # extract activity from request entity
2679             $act = $self->_extract_activity_spec( $entity );
2680             return $fail unless ref( $act ) eq 'App::Dochazka::REST::Model::Activity';
2681             }
2682              
2683             # either tsrange or date_list, but not both
2684 0     0 0   my $tsdl = $self->_extract_date_list_or_tsrange( $entity );
2685 0           return $fail unless ref( $tsdl ) eq 'HASH';
2686              
2687             # clobber based on the resource ("scheduled" or "fillup")
2688 0 0         if ( $mode eq 'Fillup' ) {
2689 0           $clobber = 0;
  0            
2690 0           $dry_run = {};
2691 0 0 0       } elsif ( $mode eq 'Scheduled' ) {
2692 0 0         $clobber = 1;
2693             delete $entity->{'clobber'};
2694             $dry_run = { 'dry_run' => '1' };
2695 0           delete $entity->{'dry_run'};
2696 0           $supervisor_ok = 1;
2697             } else {
2698 0           die "ASSERTfillupscheduled";
2699             }
2700              
2701             # ACL check
2702 0           if ( $supervisor_ok ) {
2703             $acl_check = acl_check_is_me( $self, 'eid' => $emp->eid ) ||
2704             acl_check_is_my_report( $self, 'eid' => $emp->eid );
2705             } else {
2706             $acl_check = acl_check_is_me( $self, 'eid' => $emp->eid );
2707             }
2708             if ( ! $acl_check ) {
2709             $self->mrest_declare_status( code => 403, explanation => "DISPATCH_KEEP_TO_YOURSELF" );
2710             return;
2711 0     0 1   }
2712 0            
2713 0           # create Fillup object
2714             my $fillup = App::Dochazka::REST::Fillup->new(
2715             context => $context,
2716             emp_obj => $emp,
2717             clobber => $clobber,
2718             %$dry_run,
2719             %$tsdl,
2720             %$entity,
2721             );
2722             if ($mode eq 'Fillup') {
2723             $fillup->act_obj( $act );
2724 0     0 1   }
2725 0           if ( ! defined( $fillup ) or ref( $fillup ) ne 'App::Dochazka::REST::Fillup' ) {
2726 0           $self->mrest_declare_status(
2727             code => 500,
2728             explanation => "No Fillup object"
2729             );
2730             return $fail;
2731             }
2732             if ( ! $fillup->constructor_status or
2733             ! $fillup->constructor_status->isa( 'App::CELL::Status' ) )
2734             {
2735             $self->mrest_declare_status(
2736             code => 500,
2737 0     0 1   explanation => "No constructor_status in Fillup object"
2738 0           );
2739 0           return $fail;
2740             }
2741             $log->debug( "Fillup object created; constructor status is " . Dumper( $fillup->constructor_status ) );
2742             if ( $fillup->constructor_status->not_ok ) {
2743             my $status = $fillup->constructor_status;
2744 0     0     $status->{'http_code'} = ( $status->code eq 'DOCHAZKA_DBI_ERR' )
2745 0           ? 500
2746             : 400;
2747 0           $self->mrest_declare_status( $status );
2748 0           return $fail;
2749 0           }
2750 0          
2751 0           my $status = $fillup->commit;
2752 0           if ( $status->not_ok ) {
2753             $self->mrest_declare_status( code => 500, explanation => $status->text );
2754             return $fail;
2755 0 0         }
2756             return $status;
2757             }
2758 0            
2759             # helper function to extract employee spec from request entity
2760             # takes request entity hash and returns either undef on failure
2761 0           # or Employee object on success
2762 0 0         my ( $self, $entity ) = @_;
2763             $log->debug( "Entering " . __PACKAGE__ . "::_extract_employee_spec " .
2764 0 0         "with entity " . Dumper( $entity ) );
2765             my ( $key, $value );
2766 0           # the key can be one and only one of the following:
2767 0 0         # eid, nick, sec_id (in that order; additional keys are ignored)
2768             if ( $entity->{eid} ) {
2769             $key = 'eid';
2770             $value = $entity->{eid};
2771 0           } elsif ( $entity->{nick} ) {
2772 0 0         $key = 'nick';
2773             $value = $entity->{nick};
2774             } elsif ( $entity->{sec_id} ) {
2775 0 0         $key = 'sec_id';
    0          
2776 0           $value = $entity->{sec_id};
2777 0           } else {
2778             $self->mrest_declare_status(
2779 0           code => 404,
2780 0           explanation => "DISPATCH_EMPLOYEE_CANNOT_BE_DETERMINED"
2781 0           );
2782 0           return;
2783 0           }
2784             map { delete $entity->{$_} } ( 'eid', 'nick', 'sec_id' );
2785 0           my $emp = shared_first_pass_lookup( $self, $key, $value );
2786             return unless $emp->isa( 'App::Dochazka::REST::Model::Employee' );
2787             return $emp;
2788             }
2789 0 0          
2790 0   0       # helper function to extract activity spec from request entity
2791             # takes request entity hash and returns either undef on failure
2792             # or Activity object on success
2793 0           my ( $self, $entity ) = @_;
2794             $log->debug( "Entering " . __PACKAGE__ . "::_extract_activity_spec " .
2795 0 0         "with entity " . Dumper( $entity ) );
2796 0           my ( $key, $value );
2797 0           # the key can be one and only one of the following:
2798             # aid, code, or nothing (in which case code defaults to "WORK")
2799             if ( $entity->{aid} ) {
2800             $key = 'aid';
2801 0           $value = $entity->{aid};
2802             } elsif ( $entity->{code} ) {
2803             $key = 'code';
2804             $value = $entity->{code};
2805             } else {
2806             $key = 'code';
2807             $value = 'WORK';
2808             }
2809 0 0         map { delete $entity->{$_} } ( 'aid', 'code' );
2810 0           my $act = shared_first_pass_lookup( $self, $key, $value );
2811             return unless $act->isa( 'App::Dochazka::REST::Model::Activity' );
2812 0 0 0       return $act;
2813 0           }
2814              
2815             # helper function to extract date_list or tsrange from request entity
2816             my ( $self, $entity ) = @_;
2817 0           $log->debug( "Entering " . __PACKAGE__ . "::_extract_date_list_or_tsrange " .
2818             "with entity " . Dumper( $entity ) );
2819 0 0 0        
2820             my $date_list = $entity->{date_list};
2821             my $tsrange = $entity->{tsrange};
2822 0           my $dlts;
2823            
2824             if ( ( $date_list and $tsrange ) or
2825             ( ! $date_list and ! $tsrange ) ) {
2826 0           $self->mrest_declare_status( code => 400, explanation => "DISPATCH_DATE_LIST_OR_TSRANGE" );
2827             return;
2828 0           }
2829 0 0          
2830 0           if ( $entity->{date_list} ) {
2831 0 0         $dlts = { 'date_list' => $entity->{date_list} };
2832             } elsif ( $entity->{tsrange} ) {
2833             $dlts = { 'tsrange' => $entity->{tsrange} };
2834 0           } else {
2835 0           die "ASSERT AGCJDK!!!!!!DEE";
2836             }
2837              
2838 0           $log->debug( "_extract_date_list_or_tsrange returning " . Dumper $dlts );
2839 0 0         return $dlts;
2840 0           }
2841 0            
2842              
2843 0           =head3 handler_get_schedule_nick
2844              
2845             =cut
2846              
2847             my ( $self, $pass ) = @_;
2848             $log->debug( "Entering " . __PACKAGE__ . ":handler_get_schedule_nick" );
2849             return shared_get_privsched( $self, 'schedule', $pass, 'nick', $self->context->{'mapping'}->{'nick'} );
2850 0     0     }
2851 0            
2852              
2853 0           =head3 handler_get_schedule_self
2854              
2855             =cut
2856 0 0          
    0          
    0          
2857 0           my ( $self, $pass ) = @_;
2858 0           $log->debug( "Entering " . __PACKAGE__ . ":handler_get_schedule_self" );
2859             return shared_get_privsched( $self, 'schedule', $pass, 'EID', $self->context->{'current'}->{'eid'} );
2860 0           }
2861 0            
2862              
2863 0           =head3 handler_schedule_new
2864 0            
2865             Handler for the 'schedule/new' resource.
2866 0            
2867             =cut
2868              
2869             my ( $self, $pass ) = @_;
2870 0           $log->debug( "Entering " . __PACKAGE__ . "::handler_schedule_new" );
2871              
2872 0           my $context = $self->context;
  0            
2873 0            
2874 0 0         # first pass
2875 0           if ( $pass == 1 ) {
2876             $context->{'post_is_create'} = 1;
2877             return 1;
2878             }
2879              
2880             # second pass
2881             my ( $status, $code );
2882 0     0      
2883 0           $status = shared_entity_check( $self, 'schedule' );
2884             return $fail if $status->not_ok;
2885 0           if ( ref( $context->{'request_entity'}->{'schedule'} ) ne "ARRAY" ) {
2886             $self->mrest_declare_status( code => 400, explanation => 'Check schedule syntax' );
2887             return $fail;
2888 0 0         }
    0          
2889 0            
2890 0           # first, spawn a Schedintvls object
2891             my $intvls = App::Dochazka::REST::Model::Schedintvls->spawn;
2892 0           $log->debug( "Spawned Schedintvls object " . Dumper( $intvls ) );
2893 0            
2894             # note that a SSID has been assigned
2895 0           my $ssid = $intvls->ssid;
2896 0           $log->debug("Spawned Schedintvls object with SSID $ssid");
2897              
2898 0           # assume that these are the intervals
  0            
2899 0           $intvls->{'intvls'} = $context->{'request_entity'}->{'schedule'};
2900 0 0         #
2901 0           # insert the intervals
2902             $status = $intvls->insert( $context->{'dbix_conn'} ); # schedintvls is not audited
2903             if ( $status->not_ok ) {
2904             $self->mrest_declare_status( code => 500, explanation => $status->text );
2905             return $fail;
2906 0     0     }
2907 0           $log->info( "schedule/new: Scratch intervals inserted" );
2908              
2909             #
2910 0           # convert the intervals to get the 'schedule' property
2911 0           $status = $intvls->load( $context->{'dbix_conn'} );
2912 0           if ( $status->not_ok ) {
2913             $intvls->delete( $context->{'dbix_conn'} );
2914 0 0 0       $self->mrest_declare_status( code => 400, explanation => $status->text );
      0        
      0        
2915             return $fail;
2916 0           }
2917 0           $log->info( "schedule/new: Scratch intervals converted" );
2918              
2919             #
2920 0 0         # spawn Schedule object
    0          
2921 0           my @ARGS = ( 'schedule' => $intvls->json );
2922             if ( my $scode = $context->{'request_entity'}->{'scode'} ) {
2923 0           push @ARGS, ( 'scode' => $scode );
2924             }
2925 0           my $sched = App::Dochazka::REST::Model::Schedule->spawn( @ARGS );
2926             #
2927             # insert schedule object to get SID
2928 0           $status = $sched->insert( $context );
2929 0           if ( $status->ok ) {
2930             if ( $status->code eq 'DOCHAZKA_SCHEDULE_EXISTS' ) {
2931             $self->context->{'create_path'} = '.../schedule/shid/' . $sched->sid;
2932             $code = 'DISPATCH_SCHEDULE_EXISTS';
2933             $log->info( "POST schedule/new: Returning existing schedule, unchanged" );
2934             $sched = $status->payload;
2935             } elsif ( $status->code eq 'DOCHAZKA_SCHEDULE_UPDATE_OK' ) {
2936             $self->context->{'create_path'} = '.../schedule/shid/' . $sched->sid;
2937             $code = 'DISPATCH_SCHEDULE_UPDATE_OK';
2938 0     0 1   $log->info( "POST schedule/new: Existing schedule updated" );
2939 0           } elsif ( $status->code eq 'DOCHAZKA_SCHEDULE_INSERT_OK' ) {
2940 0           $self->context->{'create_path'} = '.../schedule/shid/' . $sched->sid;
2941             $code = 'DISPATCH_SCHEDULE_INSERT_OK';
2942             $log->info( "POST schedule/new: New schedule inserted" );
2943             } else {
2944             die "AGGHGHG! could not handle App::Dochazka::REST::Model::Schedule->insert status: "
2945             . Dumper( $status );
2946             }
2947             } else {
2948             $self->mrest_declare_status( code => 500, explanation =>
2949 0     0 1   "schedule/new: Model/Schedule.pm->insert failed: " . $status->text );
2950 0           $intvls->delete( $context->{'dbix_conn'} );
2951 0           return $fail;
2952             }
2953             #
2954             # delete the schedintvls object
2955             $status = $intvls->delete( $context->{'dbix_conn'} ); # schedintvls is not audited
2956             if ( $status->not_ok ) {
2957             $self->mrest_declare_status( code => 500, explanation => "Could not delete schedintvls: " . $status->text );
2958             return $fail;
2959             }
2960             $log->info( "schedule/new: scratch intervals deleted" );
2961             #
2962 0     0 1   # success
2963 0           return $CELL->status_ok( $code, payload => $sched->TO_JSON );
2964             }
2965 0            
2966              
2967             =head3 handler_get_schedule_sid
2968 0 0          
2969 0           Handler for '/schedule/sid/:sid'
2970 0            
2971             =cut
2972              
2973             my ( $self, $pass ) = @_;
2974 0           $log->debug( "Entering " . __PACKAGE__ . ":handler_get_schedule_sid" );
2975              
2976 0           # first pass
2977 0 0         if ( $pass == 1 ) {
2978 0 0         my $sched = shared_first_pass_lookup( $self, 'SID', $self->context->{'mapping'}->{'sid'} );
2979 0           return 0 unless $sched;
2980 0           $self->context->{'stashed_schedule_object'} = $sched;
2981             return 1;
2982             }
2983            
2984 0           # second pass
2985 0           return $CELL->status_ok(
2986             'DISPATCH_SCHEDULE_FOUND',
2987             payload => $self->context->{'stashed_schedule_object'},
2988 0           );
2989 0           }
2990              
2991              
2992 0           =head3 handler_put_schedule_sid
2993              
2994             Handler for 'PUT schedule/sid/:sid'
2995 0            
2996 0 0         =cut
2997 0            
2998 0           my ( $self, $pass ) = @_;
2999             $log->debug( "Entering " . __PACKAGE__ . ":handler_put_schedule_sid" );
3000 0            
3001             my $context = $self->context;
3002             my $sid = $context->{'mapping'}->{'sid'};
3003              
3004 0           # first pass
3005 0 0         if ( $pass == 1 ) {
3006 0           my $sched = shared_first_pass_lookup( $self, 'SID', $sid );
3007 0           return 0 unless $sched;
3008 0           $context->{'stashed_schedule_object'} = $sched;
3009             return 1;
3010 0           }
3011              
3012             # run the update operation
3013             return shared_update_schedule(
3014 0           $self,
3015 0 0         $context->{'stashed_schedule_object'},
3016 0           $context->{'request_entity'}
3017             );
3018 0           }
3019              
3020              
3021 0           =head3 handler_delete_schedule_sid
3022 0 0          
3023 0 0         Handler for '/schedule/sid/:sid'
    0          
    0          
3024 0            
3025 0           =cut
3026 0            
3027 0           my ( $self, $pass ) = @_;
3028             $log->debug( "Entering " . __PACKAGE__ . ":handler_delete_schedule_sid" );
3029 0            
3030 0           my $context = $self->context;
3031 0            
3032             # first pass
3033 0           if ( $pass == 1 ) {
3034 0           return $self->handler_get_schedule_sid( $pass );
3035 0           }
3036              
3037 0           # second pass
3038             return $context->{'stashed_schedule_object'}->delete( $context );
3039             }
3040              
3041 0            
3042             =head3 handler_get_schedule_scode
3043 0            
3044 0           Handler for '/schedule/scode/:scode'
3045              
3046             =cut
3047              
3048 0           my ( $self, $pass ) = @_;
3049 0 0         $log->debug( "Entering " . __PACKAGE__ . ":handler_get_schedule_scode" );
3050 0            
3051 0           # first pass
3052             if ( $pass == 1 ) {
3053 0           my $sched = shared_first_pass_lookup( $self, 'scode', $self->context->{'mapping'}->{'scode'} );
3054             return 0 unless $sched;
3055             $self->context->{'stashed_schedule_object'} = $sched;
3056 0           return 1;
3057             }
3058            
3059             # second pass
3060             return $CELL->status_ok(
3061             'DISPATCH_SCHEDULE_FOUND',
3062             payload => $self->context->{'stashed_schedule_object'},
3063             );
3064             }
3065              
3066              
3067 0     0 1   =head3 handler_put_schedule_scode
3068 0            
3069             Handler for 'PUT schedule/scode/:scode'
3070              
3071 0 0         =cut
3072 0            
3073 0 0         my ( $self, $pass ) = @_;
3074 0           $log->debug( "Entering " . __PACKAGE__ . ":handler_put_schedule_scode" );
3075 0            
3076             my $context = $self->context;
3077             my $scode = $context->{'mapping'}->{'scode'};
3078              
3079             # first pass
3080             if ( $pass == 1 ) {
3081 0           my $sched = shared_first_pass_lookup( $self, 'scode', $scode );
3082             return 0 unless $sched;
3083             $context->{'stashed_schedule_object'} = $sched;
3084             return 1;
3085             }
3086              
3087             # run the update operation
3088             return shared_update_schedule(
3089             $self,
3090             $context->{'stashed_schedule_object'},
3091             $context->{'request_entity'}
3092             );
3093 0     0 1   }
3094 0            
3095              
3096 0           =head3 handler_delete_schedule_scode
3097 0            
3098             Handler for '/schedule/scode/:scode'
3099              
3100 0 0         =cut
3101 0            
3102 0 0         my ( $self, $pass ) = @_;
3103 0           $log->debug( "Entering " . __PACKAGE__ . ":handler_delete_schedule_scode" );
3104 0            
3105             my $context = $self->context;
3106              
3107             # first pass
3108             if ( $pass == 1 ) {
3109             return $self->handler_get_schedule_scode( $pass );
3110             }
3111 0            
3112             # second pass
3113             return $context->{'stashed_schedule_object'}->delete( $context );
3114             }
3115              
3116              
3117             =head2 Helper functions
3118              
3119             =head3 _first_pass_always_exists
3120              
3121             Boilerplate code for use in handlers of resources that always exist
3122              
3123 0     0 1   =cut
3124 0            
3125             my ( $self, $pass ) = @_;
3126 0            
3127             if ( $pass == 1 ) {
3128             $log->debug( "Resource handler first pass, resource always exists" );
3129 0 0         return 1;
3130 0           }
3131             return 0;
3132             }
3133              
3134 0           =head3 _tsrange_from_context
3135              
3136             Given a mapping containing either a C<tsrange> property or, alternatively,
3137             a pair of properties C<ts> and C<psqlint> (i.e. a timestamp and a PostgreSQL
3138             interval), return a status object that, if the delta add operation is
3139             successful, will contain a proper timestamp.
3140              
3141             =cut
3142              
3143             my $context = shift;
3144             my $mapping = $context->{'mapping'};
3145 0     0 1   my ( $status, $tsr );
3146 0           $tsr = $mapping->{'tsrange'} if $mapping->{'tsrange'};
3147             if ( $mapping->{'ts'} and $mapping->{'psqlint'} ) {
3148             $status = timestamp_delta_plus(
3149 0 0         $context->{'dbix_conn'},
3150 0           $mapping->{'ts'},
3151 0 0         $mapping->{'psqlint'}
3152 0           );
3153 0           return $status unless $status->ok;
3154             $tsr = "[ " . $mapping->{ts} . ", " . $status->payload . " )";
3155             }
3156             return $CELL->status_ok( 'SUCCESS', payload => $tsr );
3157             }
3158              
3159 0           1;