Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lib/WebAPI/DBIC/Resource/GenericSetInvoke.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package WebAPI::DBIC::Resource::GenericSetInvoke;

=head1 NAME

WebAPI::DBIC::Resource::GenericSetInvoke - a set of roles to implement a resource for making method calls on a DBIC item

=cut

use Moo;
use namespace::clean;

extends 'WebAPI::DBIC::Resource::GenericCore';
with 'WebAPI::DBIC::Resource::Role::Set',
'WebAPI::DBIC::Resource::Role::SetInvoke',
;

1;
84 changes: 84 additions & 0 deletions lib/WebAPI/DBIC/Resource/Role/SetInvoke.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package WebAPI::DBIC::Resource::Role::SetInvoke;

=head1 NAME

WebAPI::DBIC::Resource::Role::SetInvoke - methods for resources representing method calls on item resources

=cut

use Scalar::Util qw(blessed);

use Moo::Role;


requires 'decode_json';
requires 'encode_json';
requires 'render_item_as_plain_hash';
requires 'throwable';
requires 'set';

has method => (
is => 'ro',
required => 1,
);

sub post_is_create { return 0 }

around 'allowed_methods' => sub {
return [ qw(POST) ];
};


sub process_post {
my $self = shift;

# Here's we're calling a method on the set as a simple generic behaviour.
# This is very limited because, for example, the method has no knowledge
# that it's being called inside a web service, thus no way to do redirects
# or provide HTTP specific rich-exceptions.
# If anything more sophisticated is required then it should be implemented
# as a specific resource class for the method (or perhaps a role if there's
# a set of methods that require similar behaviour).

# The POST body content provides a data structure containing the method arguments
# { args => [ (@_) ] }
$self->throwable->throw_bad_request(415, errors => "Request content-type not application/json")
unless $self->request->header('Content-Type') =~ 'application/.*?json';
my $invoke_body_data = $self->decode_json($self->request->content);
$self->throwable->throw_bad_request(400, errors => "Request content not a JSON hash")
unless ref $invoke_body_data eq 'HASH';

my @method_args;
if (my $args = delete $invoke_body_data->{args}) {
$self->throwable->throw_bad_request(400, errors => "The args must be an array")
if ref $args ne 'ARRAY';
@method_args = @$args;
}
$self->throwable->throw_bad_request(400, errors => "Unknown attributes: @{[ keys %$invoke_body_data ]}")
if keys %$invoke_body_data;

my $method_name = $self->method;
# the method is expected to throw an exception on error.
my $result_raw = $self->set->$method_name(@method_args);

my $result_rendered;
# return a DBIC resultset as array of hashes of ALL records (no paging)
if (blessed($result_raw) && $result_raw->isa('DBIx::Class::ResultSet')) {
$result_rendered = [ map { $self->render_item_as_plain_hash($_) } $result_raw->all ];
}
# return a DBIC result row as a hash
elsif (blessed($result_raw) && $result_raw->isa('DBIx::Class::Row')) {
$result_rendered = $self->render_item_as_plain_hash($result_raw);
}
# return anything else as raw JSON wrapped in a hash
else {
# we shouldn't get an object here, but if we do then we
# stringify it here to avoid exposing the guts
$result_rendered = { result => (blessed $result_raw) ? "$result_raw" : $result_raw };
}

$self->response->body( $self->encode_json($result_rendered) );
return 200;
}

1;