Group
Extension

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


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.