App-Dochazka-REST

 view release on metacpan or  search on metacpan

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN


    # first pass
    return 1 if $pass == 1;

    # second pass
    my $conn = $self->context->{'dbix_conn'};
    return $CELL->status_crit( "DOCHAZKA_NO_DBIX_CONNECTOR" ) unless ref( $conn ) and $conn->can( 'dbh' );
    my $dbh = $conn->dbh;
    my $noof_connections;
    my $status;
    try {
        $conn->run( fixup => sub { 
            ( $noof_connections ) = $_->selectrow_array( 
                $site->SQL_NOOF_CONNECTIONS,
                undef,
            );
        } );
        $log->notice( "Current number of DBI connections is $noof_connections" ); 
        my $dbstatus = conn_status( $conn );
        $status = $CELL->status_ok( 
            'DOCHAZKA_DBSTATUS', 
            args => [ $dbstatus ],
            payload => { 
                'conn_status' => $dbstatus,
                'dbmsname' => $dbh->get_info(17),
                'dbmsver' => $dbh->get_info(18),
                'username' => $dbh->{Username},
                'noof_connections' => ( $noof_connections += 0 ),
            } 
        );
    } catch {
        $status = $CELL->status_err( 'DOCHAZKA_DBI_ERR', args => [ $_ ] );
    };

    return $status;
}


=head3 handler_docu

=cut

sub handler_docu {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_docu, pass number $pass" );

    # first pass
    return 1 if $pass == 1;

    # '/docu/...' resources only

    # the resource to be documented should be in the request body - if not, return 400
    my $docu_resource = $self->context->{'request_entity'};
    if ( $docu_resource ) {
        $log->debug( "handler_docu: request body is ->$docu_resource<-" );
    } else {
        $self->mrest_declare_status( 'code' => 400, 'explanation' => 'Missing request entity' );
        return $fail;
    }

    # the resource should be defined - if not, return 404
    my $def = $resources->{$docu_resource};
    $log->debug( "handler_docu: resource definition is " . Dumper( $def ) );
    if ( ref( $def ) ne 'HASH' ) {
        $self->mrest_declare_status( 'code' => 404, 
            'explanation' => "Could not find resource definition for $docu_resource" 
        );
        return $fail;
    }

    # all green - assemble the requested documentation
    my $method = $self->context->{'method'};
    my $resource_name = $self->context->{'resource_name'};
    my $pl = {
        'resource' => $docu_resource,
    };
    my $docs = $def->{'documentation'} || <<"EOH";
=pod

The definition of resource $docu_resource lacks a 'documentation' property 
EOH
    # if they want POD, give them POD; if they want HTML, give them HTML, etc.
    if ( $resource_name eq 'docu/pod' ) {
        $pl->{'format'} = 'POD';
        $pl->{'documentation'} = $docs;
    } elsif ( $resource_name eq 'docu/html' ) {
        $pl->{'format'} = 'HTML';
        $pl->{'documentation'} = pod_to_html( $docs );
    } else {
        # fall back to plain text
        $pl->{'format'} = 'text';
        $pl->{'documentation'} = pod_to_text( $docs );
    }
    return $CELL->status_ok( 'DISPATCH_ONLINE_DOCUMENTATION', payload => $pl );
}


=head3 handler_echo

Echo request body back in the response

=cut

sub handler_echo {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_echo, pass number $pass" );
    
    # first pass
    return 1 if $pass == 1;

    # second pass
    return $CELL->status_ok( "ECHO_REQUEST_ENTITY", payload =>
       $self->context->{'request_entity'} );
}


=head3 handler_forbidden

Handler for 'forbidden' resource.

=cut

sub handler_forbidden {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_forbidden, pass number $pass" );

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

    # second pass
    my $context = $self->context;
    return $fail unless shared_employee_acl_part2( $self );
    return shared_update_employee( 
        $self,
        $context->{'current_obj'}, 
        $context->{'request_entity'} 
    );
}


=head3 handler_delete_employee_eid

Handler for 'DELETE employee/eid/:eid' resource.

=cut

sub handler_delete_employee_eid {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_delete_employee_eid" ); 

    # first pass
    if ( $pass == 1 ) {
        return $self->handler_get_employee_eid( $pass );
    }

    # second pass
    my $context = $self->context;
    return $context->{'stashed_employee_object'}->delete( $context );
}


=head3 handler_get_employee_eid

Handler for 'GET employee/eid/:eid'

=cut

sub handler_get_employee_eid {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_eid" ); 
    return shared_get_employee( $self, $pass, 'EID', $self->context->{'mapping'}->{'eid'} );
}


=head3 _ldap_sync_pass1

=cut

sub _ldap_sync_pass1 {
    my ( $self, $emp ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::_ldap_sync_pass1" ); 

    my $status = $emp->ldap_sync();
    $log->debug( "ldap_sync status: " . Dumper( $status ) );
    if ( $status->not_ok ) {
        if ( $status->code eq 'DOCHAZKA_LDAP_SYSTEM_USER_NOSYNC' ) {
            # system user - 403
            $status->{'http_code'} = 403;
        } else {
            $status->{'http_code'} = 404;
        }
        $self->mrest_declare_status( $status );
        return 0;
    }
    $self->context->{'stashed_employee_object'} = $emp;
    return 1;
}


=head3 handler_get_employee_ldap

Handler for 'GET employee/nick/:nick/ldap' resource.

=cut

sub handler_get_employee_ldap {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_ldap" ); 

    my $context = $self->context;
    my $nick = $context->{'mapping'}->{'nick'};

    if ( $pass == 1 ) {
        my $emp = App::Dochazka::REST::Model::Employee->spawn(
            'nick' => $nick,
            'sync' => 1,
        );
        return $self->_ldap_sync_pass1( $emp );
    }

    return $CELL->status_ok( 'DOCHAZKA_LDAP_LOOKUP', payload => $context->{'stashed_employee_object'} );
}


=head3 handler_put_employee_ldap

Handler for 'PUT employee/nick/:nick/ldap' resource.

=cut

sub handler_put_employee_ldap {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_put_employee_ldap" ); 

    my $context = $self->context;
    $log->debug( "mapping " . Dumper( $context->{'mapping'} ) );
    my $nick = $context->{'mapping'}->{'nick'};
    my $status;

    # first pass
    if ( $pass == 1 ) {
        # determine if this is an insert or an update
        my $emp = shared_first_pass_lookup( $self, 'nick', $nick );
        $self->nullify_declared_status;
        return 0 unless shared_employee_acl_part1( $self, $emp );  # additional ACL checks
        if ( $emp ) {
            $context->{'put_employee_func'} = 'update_employee';
        } else {
            $context->{'put_employee_func'} = 'insert_employee';
            $emp = App::Dochazka::REST::Model::Employee->spawn( 'nick' => $nick );

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

    # first pass
    if ( $pass == 1 ) {
        return $self->handler_get_employee_nick( $pass );
    }

    # second pass
    my $context = $self->context;
    return $context->{'stashed_employee_object'}->delete( $context );
}


=head3 handler_get_employee_nick

Handler for 'GET employee/nick/:nick'

=cut

sub handler_get_employee_nick {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_nick" ); 
    return shared_get_employee( $self, $pass, 'nick', $self->context->{'mapping'}->{'nick'} );
}


=head3 handler_get_employee_sec_id

Handler for 'GET employee/sec_id/:sec_id'

=cut

sub handler_get_employee_sec_id {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_sec_id" ); 
    return shared_get_employee( $self, $pass, 'sec_id', $self->context->{'mapping'}->{'sec_id'} );
}


=head3 handler_get_employee_search_nick

Handler for 'GET employee/search/nick/:key'

=cut

sub handler_get_employee_search_nick {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_get_employee_search_nick" ); 

    # first pass
    return 1 if $pass == 1;

    # second pass
    my $key = $self->context->{'mapping'}->{'key'};
    $key = "%$key%" unless $key =~ m/%/;
    my $status = $CELL->status_ok;
    $status = load_multiple(
        conn => $self->context->{'dbix_conn'},
        class => 'App::Dochazka::REST::Model::Employee',
        sql => $site->SQL_EMPLOYEE_SELECT_MULTIPLE_BY_NICK,
        keys => [ $key ],
    );
    # check for 404
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        $self->mrest_declare_status( code => 404,
            explanation => "DISPATCH_SEARCH_EMPTY",
            args => [ 'employee', "nick LIKE $key" ],
        );
        return $fail;
    }
    return $status if $status->not_ok;

    # found some employee objects
    foreach my $emp ( @{ $status->payload } ) {
        $emp = $emp->TO_JSON;
    }
    return $status;
}


=head2 Genreport handlers

=head3 handler_genreport

Handler for the 'POST genreport' resource.

=cut

sub handler_genreport {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_genreport" ); 

    # first pass
    return 1 if $pass == 1;

    # second pass
    # - check that entity is kosher
    my $status = shared_entity_check( $self, 'path' );
    return $status unless $status->ok;
    my $context = $self->context;
    my $entity = $context->{'request_entity'};

    # - get path and look it up
    my $path = $entity->{'path'};
    my $comp = shared_first_pass_lookup( $self, 'path', $path );
    return $fail unless $path;
    delete $entity->{'path'};

    # - if there is a 'parameters' property, check that it is a hashref
    my $parameters;
    if ( $entity->{'parameters'} ) {
        $log->debug( "Vetting parameters: " . Dumper $entity->{'parameters'} ) ;
        if ( ref( $entity->{'parameters'} ) ne 'HASH' ) {
            $self->mrest_declare_status( 
                code => 400, 
                explanation => 'parameters must be given as key:value pairs'
            );
            return $fail;
        }
        # - convert $parameters hashref into $parameters arrayref for validation
        my $count = 0;
        foreach my $key ( keys %{ $entity->{'parameters'} } ) {
            $parameters->[$count] = $key;
            $count += 1;
            $parameters->[$count] = $entity->{'parameters'}->{$key};

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

        'nick' => $context->{'current'}->{'nick'},
    );

    if ( defined $context->{'mapping'}->{'tsrange'} ) {
        $ARGS{'tsrange'} = $context->{'mapping'}->{'tsrange'};
    }
    
    if ( $context->{'components'}->[0] eq 'priv' ) {
        return get_privhistory( $context, %ARGS );
    } elsif ( $context->{'components'}->[0] eq 'schedule' ) {
        return get_schedhistory( $context, %ARGS );
    }
}


=head3 handler_history_get_single

Handler method for GET requests on the '/{priv,schedule}/history/eid/..' and
'/{priv,schedule}/history/nick/..' resources (potentially returning
a single record).

=cut

sub handler_history_get_single {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_single" ); 

    my ( $context, $method, $mapping, undef, $ts, $key, $value ) = shared_history_init( $self->context );

    # first pass
    if ( $pass == 1 ) {
        my $emp = shared_first_pass_lookup( $self, $key, $value );
        return 0 unless $emp;
        $self->context->{'stashed_employee_obj'} = $emp;
        return 1;
    }

    # second pass
    my $prop = $context->{'components'}->[0];
    my $emp = $self->context->{'stashed_employee_obj'};
    my $status;
    if ( $prop eq 'priv' ) {
        $status = App::Dochazka::REST::Model::Privhistory->load_by_eid(
            $context->{'dbix_conn'},
            $emp->eid,
            $ts
        );
    } elsif ( $prop eq 'schedule' ) {
        $status = App::Dochazka::REST::Model::Schedhistory->load_by_eid(
            $context->{'dbix_conn'},
            $emp->eid,
            $ts
        );
    } else {
        die "BGUDFUUFF! Improper prop ->$prop<- seen!";
    }
    # - process return value
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        my $tsmsg = ( $ts ) ? $ts : 'now';
        $self->mrest_declare_status(
            code => 404,
            explanation => "No $prop history for $key $value as of $tsmsg",
        );
        return $fail;
    } elsif ( $status->not_ok ) {
        $self->mrest_declare_status(
            code => 500,
            explanation => $status->text,
        );
        return $fail;
    }
    return $status;
}


=head3 handler_history_get_multiple

Handler method for GET requests on the '/{priv,schedule}/history/eid/..' and
'/{priv,schedule}/history/nick/..' resources (all potentially returning
multiple records).

=cut

sub handler_history_get_multiple {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_history_get_multiple" ); 

    my ( $context, $method, $mapping, $tsrange, undef, $key, $value ) = shared_history_init( $self->context );

    # first pass
    if ( $pass == 1 ) {
        my $emp = shared_first_pass_lookup( $self, $key, $value );
        return 0 unless $emp;
        $self->context->{'stashed_employee_obj'} = $emp;
        return 1;
    }

    # second pass
    my ( $class, $prop, undef ) = shared_get_class_prop_id( $context );
    my $emp = $self->context->{'stashed_employee_obj'};
    my $status = App::Dochazka::REST::Model::Shared::get_history( 
        $prop,
        $context->{'dbix_conn'},
        eid => $emp->eid,
        nick => $emp->nick, 
        tsrange => $tsrange, 
    );
    # - process return value
    if ( $status->level eq 'NOTICE' and $status->code eq 'DISPATCH_NO_RECORDS_FOUND' ) {
        $self->mrest_declare_status( code => 404, explanation => "No history for $key $value $tsrange" );
        return $fail;
    } elsif ( $status->not_ok ) {
        $self->mrest_declare_status( code => 500, explanation => $status->text );
        return $fail;
    }
    return $status;
}


=head3 handler_history_post

Handler method for POST requests on the '/{priv,schedule}/history/eid/..' and
'/{priv,schedule}/history/nick/..' resources.

=cut

sub handler_history_post {
    my ( $self, $pass ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::handler_history_post" ); 

    my ( $context, undef, undef, undef, undef, $key, $value ) = shared_history_init( $self->context );

    # first pass
    if ( $pass == 1 ) {
        # get employee object from key+value
        my $emp = shared_first_pass_lookup( $self, $key, $value );
        return 0 unless $emp;
        $self->context->{'stashed_employee_obj'} = $emp;
        $self->context->{'post_is_create'} = 1;
        return 1;
    }

    # second pass
    my ( $class, $prop, $id ) = shared_get_class_prop_id( $context );
    my $emp = $context->{'stashed_employee_obj'};

    my $entity = $context->{'request_entity'};
    if ( $prop eq 'sid' ) {
        # we might have scode instead of sid in the entity
        if ( $entity->{'scode'} and not $entity->{'sid'} ) {
            my $sched = shared_first_pass_lookup( $self, 'scode', $entity->{'scode'} );
            if ( $sched ) {
                $entity->{'sid'} = $sched->sid;
            } else {
                $self->mrest_declare_status(
                    explanation => 'Schedule code ' . $entity->{'scode'} . ' not found',
                    permanent => 1,
                );
                return $fail;
            }
        }
    }

    # - check entity for presence of certain properties
    my $status = shared_entity_check( $self, $prop, 'effective' );
    return $status unless $status->ok;

    # - run the insert operation
    my $ho;
    try {

lib/App/Dochazka/REST/Dispatch.pm  view on Meta::CPAN

        %$entity,
    );
    if ($mode eq 'Fillup') {
        $fillup->act_obj( $act );
    }
    if ( ! defined( $fillup ) or ref( $fillup ) ne 'App::Dochazka::REST::Fillup' ) {
        $self->mrest_declare_status( 
            code => 500, 
            explanation => "No Fillup object" 
        );
        return $fail;
    }
    if ( ! $fillup->constructor_status or
         ! $fillup->constructor_status->isa( 'App::CELL::Status' ) )
    {
        $self->mrest_declare_status( 
            code => 500, 
            explanation => "No constructor_status in Fillup object" 
        );
        return $fail;
    }
    $log->debug( "Fillup object created; constructor status is " . Dumper( $fillup->constructor_status ) );
    if ( $fillup->constructor_status->not_ok ) {
        my $status = $fillup->constructor_status;
        $status->{'http_code'} = ( $status->code eq 'DOCHAZKA_DBI_ERR' )
            ? 500 
            : 400;
        $self->mrest_declare_status( $status );
        return $fail;
    }
    
    my $status = $fillup->commit;
    if ( $status->not_ok ) {
        $self->mrest_declare_status( code => 500, explanation => $status->text );
        return $fail;
    }
    return $status;
}

# helper function to extract employee spec from request entity
# takes request entity hash and returns either undef on failure
# or Employee object on success
sub _extract_employee_spec {
    my ( $self, $entity ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::_extract_employee_spec " .
                 "with entity " . Dumper( $entity ) );
    my ( $key, $value );
    # the key can be one and only one of the following: 
    # eid, nick, sec_id (in that order; additional keys are ignored)
    if ( $entity->{eid} ) {
        $key = 'eid';
        $value = $entity->{eid};
    } elsif ( $entity->{nick} ) {
        $key = 'nick';
        $value = $entity->{nick};
    } elsif ( $entity->{sec_id} ) {
        $key = 'sec_id';
        $value = $entity->{sec_id};
    } else {
        $self->mrest_declare_status(
            code => 404,
            explanation => "DISPATCH_EMPLOYEE_CANNOT_BE_DETERMINED"
        );
        return;
    }
    map { delete $entity->{$_} } ( 'eid', 'nick', 'sec_id' );
    my $emp = shared_first_pass_lookup( $self, $key, $value );
    return unless $emp->isa( 'App::Dochazka::REST::Model::Employee' );
    return $emp;
}

# helper function to extract activity spec from request entity
# takes request entity hash and returns either undef on failure
# or Activity object on success
sub _extract_activity_spec {
    my ( $self, $entity ) = @_;
    $log->debug( "Entering " . __PACKAGE__ . "::_extract_activity_spec " .
                 "with entity " . Dumper( $entity ) );
    my ( $key, $value );
    # the key can be one and only one of the following: 
    # aid, code, or nothing (in which case code defaults to "WORK")
    if ( $entity->{aid} ) {
        $key = 'aid';
        $value = $entity->{aid};
    } elsif ( $entity->{code} ) {
        $key = 'code';
        $value = $entity->{code};
    } else {
        $key = 'code';
        $value = 'WORK';
    }
    map { delete $entity->{$_} } ( 'aid', 'code' );
    my $act = shared_first_pass_lookup( $self, $key, $value );
    return unless $act->isa( 'App::Dochazka::REST::Model::Activity' );
    return $act;
}

# helper function to extract date_list or tsrange from request entity
sub _extract_date_list_or_tsrange {
    my ( $self, $entity ) = @_;
    $log->debug( "Entering " . __PACKAGE__ .  "::_extract_date_list_or_tsrange " .
                 "with entity " . Dumper( $entity ) );

    my $date_list = $entity->{date_list};
    my $tsrange = $entity->{tsrange};
    my $dlts;
    
    if ( ( $date_list and $tsrange ) or
         ( ! $date_list and ! $tsrange ) ) {
        $self->mrest_declare_status( code => 400, explanation => "DISPATCH_DATE_LIST_OR_TSRANGE" );
        return;
    }

    if ( $entity->{date_list} ) {
        $dlts = { 'date_list' => $entity->{date_list} };
    } elsif ( $entity->{tsrange} ) {
        $dlts = { 'tsrange' => $entity->{tsrange} };
    } else {
        die "ASSERT AGCJDK!!!!!!DEE";
    }



( run in 1.641 second using v1.01-cache-2.11-cpan-39bf76dae61 )