File Coverage

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