App-Manoc/lib/App/Manoc/ControllerBase/APIv1.pm
package App::Manoc::ControllerBase::APIv1;
#ABSTRACT: Base class for API controllers
use Moose;
our $VERSION = '2.99.4'; ##TRIAL VERSION
use namespace::autoclean;
BEGIN { extends 'Catalyst::Controller'; }
use App::Manoc::Utils::Validate;
has use_json_boolean => (
is => 'ro',
isa => 'Bool',
default => 0,
);
sub begin : Private {
my ( $self, $c ) = @_;
$c->stash( is_api => 1 );
}
sub api_setup : Chained('/') PathPart('api/v1') CaptureArgs(0) {
my ( $self, $c ) = @_;
# require HTTP Authentication
$c->user_exists or
$c->authenticate( {}, 'agent' );
}
sub deserialize : Chained('api_setup') CaptureArgs(0) PathPart('') {
my ( $self, $c ) = @_;
if ( $c->req->body_data && scalar( keys %{ $c->req->body_data } ) ) {
$c->debug and $c->log->debug('Deserializing body data for API input');
$c->stash( api_request_data => $c->req->body_data );
}
else {
$c->debug and $c->log->debug('No body data for API input');
}
}
sub end : Private {
my ( $self, $c ) = @_;
my $expose_stash = 'json_data';
# don't change the http status code if already set elsewhere
unless ( $c->res->status && $c->res->status != 200 ) {
if ( $c->stash->{api_field_errors} ) {
$c->res->status(422);
}
elsif ( $c->stash->{api_error_message} ) {
$c->res->status(400);
}
else {
$c->res->status(200);
}
}
if ( $c->res->status == 200 ) {
$c->log->debug("Building API response status=200");
my $data = $c->stash->{api_response_data} || $c->stash->{json_data};
$c->stash->{$expose_stash} = $data;
}
elsif ( $c->res->status == 401 ) {
my $data = {};
$data->{message} = "Permission denied";
$c->stash->{$expose_stash} = $data;
}
elsif ( $c->res->status == 404 ) {
my $data = {};
$data->{message} = "Object not found";
$c->stash->{$expose_stash} = $data;
}
else {
# build the response data using error message
$c->log->debug("Building error response");
my $data = {};
my $field_errors = $c->stash->{api_field_errors};
$field_errors and
$data->{errors} = $field_errors;
my $error_message = $c->stash->{api_error_message} ||
'Error processing request';
$data->{message} = $error_message;
$c->stash->{$expose_stash} = $data;
}
$c->forward('View::JSON');
}
sub validate : Private {
my ( $self, $c ) = @_;
my $data = $c->stash->{api_request_data};
my $rules = $c->stash->{api_validate};
my $result = App::Manoc::Utils::Validate::validate( $data, $rules );
if ( !$result->{valid} ) {
$c->stash( api_field_errors => $result->{errors} );
return 0;
}
return 1;
}
__PACKAGE__->meta->make_immutable;
1;
# Local Variables:
# mode: cperl
# indent-tabs-mode: nil
# cperl-indent-level: 4
# cperl-indent-parens-as-block: t
# End:
__END__
=pod
=head1 NAME
App::Manoc::ControllerBase::APIv1 - Base class for API controllers
=head1 VERSION
version 2.99.4
=head1 SYNOPSIS
package App::Manoc::APIv1::FooApi;
BEGIN { extends 'App::Manoc::Controller::APIv1' }
sub foo_base : Chained('deserialize') PathPart('foo') CaptureArgs(0) {
my ( $self, $c ) = @_;
$c->stash( resultset => $c->model('ManocDB::Foo') );
}
sub foo_post : Chained('foo_base') PathPart('') POST {
my ( $self, $c ) = @_;
$c->stash(
api_validate => {
type => 'hash',
items => {
foo_name => {
type => 'scalar',
required => 1,
},
bar_list => {
type => 'array',
required => 1,
},
},
}
);
=head1 DESCRIPTION
This class should be used for implementing API controllers which manage entry point in api/v1.
It disables CRSF and requires HTTP authentication.
Data can be validated using L<App::Manoc::Utils::Validate> via
C<validate> method.
Responses are generated using C<api_response_data> stash element.
Error messages are be stored in C<api_message> or C<api_field_errors>.
=head1 ACTIONS
=head2 api_setup
Path api/v1. Require HTTP Authentication
=head1 METHODS
=head2 begin
Set is_api in stash
=head2 deserialize
Chained to base, stores request body data in C<api_request_data> in stash.
=head2 end
Genereates http status code preparare data (API results or validation
errors) for JSON serializer.
=head2 validate
Read data from C<api_request_data> stash value and validates with
L<App::Manoc::Utils::Validate> using rules in C<api_validate> stash
value.
=head1 AUTHORS
=over 4
=item *
Gabriele Mambrini <gmambro@cpan.org>
=item *
Enrico Liguori
=back
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Gabriele Mambrini.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut