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 )