Skip to content

Rewrite API using Mojo #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
13 changes: 3 additions & 10 deletions Makefile.PL
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,11 @@ WriteMakefile(
: ()),
PL_FILES => {},
PREREQ_PM => {
'Dancer' => 1.3072,
'Exporter' => 0,
'HTTP::Async' => 0,
'HTTP::Request::Common' => 0,
'LWP::UserAgent' => 0,
'Moose' => 0,
'Module::Refresh' => 0,
'Mojolicious' => 0,
'Test::More' => 0,
'Time::HiRes' => 0,
'YAML' => 0,

},

},
dist => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
clean => { FILES => 'ModelSeedApi-*' },
);
7 changes: 7 additions & 0 deletions app.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env perl
use strict;
use warnings;

use lib 'lib';
use Mojolicious::Commands;
Mojolicious::Commands->start_app("ModelSEED::API");
7 changes: 0 additions & 7 deletions bin/app.pl

This file was deleted.

29 changes: 0 additions & 29 deletions config.yml

This file was deleted.

167 changes: 167 additions & 0 deletions lib/ModelSEED/API.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package ModelSEED::API;
use Mojo::Base 'Mojolicious';
our $VERSION = 0;
use ModelSEED::Auth::Factory;
use ModelSEED::Store;
use IO::String;
use Pod::Text;


sub startup {
my $self = shift;
$self->plugin(PODRenderer => { no_perldoc => 1 });
my $Auth = ModelSEED::Auth::Factory->new->from_config;
my $Store = ModelSEED::Store->new(auth => $Auth);
$self->helper( Store => sub { $Store } );
my $UuidRegex = qr/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/;
my $TopLevelTypes = [qw(
biochemistry
mapping
model
fbaformulation
gapfillingformulation
annotation
)];
my $r = $self->routes;
# Root path returns POD for this file,
# format pod and send it as html
$r->get('/' => sub {
my $self = shift;
my $buffer;
my $p = Pod::Simple::HTML->new;
$p->output_string($buffer);
$p->parse_file(__FILE__);
$self->render(text => $buffer, format => 'html');
});

$r->add_shortcut(resource => sub {
my ($r, $name, $methods) = @_;
my $resource = $r->route("/")->to("$name#");
$methods = { map { $_ => 1 } @$methods };
if ($methods->{post}) {
$resource->post->to('#create')->name("create_$name");
}
if ($methods->{get}) {
$resource->get->to('#show')->name("remove_$name");
}
if ($methods->{put}) {
$resource->put->to("#update")->name("remove_$name");
}
return $resource;
});

# All remaining routes are under $VERSION
$r = $r->under("/$VERSION");

# Type listing route
$r->get("/" => sub {
my $self = shift;
my $base = $self->req->url->base;
my $data = {
map { $_ => $base . "/$VERSION/" . $_ }
@$TopLevelTypes
};
return $self->render(json => $data);
});

# Typed routes
my $typed = $r->under("/:type" => [ type => $TopLevelTypes ]);
$typed->get("/")->to("Aliases#list");

# Typed with :uuid
$typed->get("/:uuid" => [ uuid => $UuidRegex ])
->resource("object", [qw(get put)]);

# Typed with :owner :alias
$typed->under("/#owner/#alias")
->resource("object", [qw(get put)]);

# Incomplete :owner , no alias
$typed->get("/:owner")->to("Aliases#list");
}
1;
__DATA__

=head1 ModelSEED::API

REST API for the ModelSEED

=head2 Routes

/0/
/0/:type
/0/:type/:uuid
/0/:type/:uname
/0/:type/:uname/:alias

Each route begins with the API version number,
which is currently C<0>.

=over 4

=item C</0/>

=over 4

=item GET

Return a JSON hash where keys are the types and each value is the
URL of the collection resource for that type.

=back

=item C</0/:type/>

=over 4

=item GET

Return a JSON list of URLs that are resources available of that type.

=back

=item C</0/:type/:uuid>

=over 4

=item GET

Return an object of type C<:type> matched by its UUID C<:uuid>.

=item PUT

If this object does not exist in the database, inserts the object
and returns 201. Otherwise fails, 403 Forbidden.

=back

=item C</0/:type/:uname>

=over 4

=item GET

Return a JSON list of URLs that are resources of the available type
C<:type> that are owned by the user with username C<:uname>.

=back

=item C</0/:type/:uname/:alias>

=over 4

=item GET

Get an object of type C<:type> owned by user with username
C<:uname> with alias C<:alias>.

=item PUT

Inserts the object and returns 201. Otherwise fails, 403 Forbidden.

=back

=back

=cut

25 changes: 25 additions & 0 deletions lib/ModelSEED/API/Aliases.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ModelSEED::API::Aliases;
use Mojo::Base 'Mojolicious::Controller';

sub list {
my $self = shift;
my $query = { type => $self->param("type") };
$query->{owner} = $self->param("owner") if defined $self->param("owner");
my $aliases = $self->Store->get_aliases($query);
my $base = $self->req->url->base;
foreach my $als (@$aliases) {
my ($t, $o, $a) = ($als->{type}, $als->{owner}, $als->{alias});
$als = "$base/$t/$o/$a";
}
return $self->render(json => $aliases);
}

sub get_config {

}

sub put_config {

}

1;
68 changes: 68 additions & 0 deletions lib/ModelSEED/API/Object.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ModelSEED::API::Object;
use Mojo::Base 'Mojolicious::Controller';

# GET
sub show {
my $self = shift;
Mojo::IOLoop->stream($self->tx->connection)->timeout(60);
my $ref = $self->_getBaseObjectRef();
return $self->render(json => { 'e' => 'Not Found' }, 404) unless defined $ref;
my $url = $self->req->url->to_string;
my $path = $url;
$path =~ s/.*${ref}\/*//;
my $data = $self->Store->get_data($ref);
if ( $path ne "" && defined $data) {
return $self->render(json => { ref => $ref , path => $path, url => $url });
} elsif(defined $data) {
return $self->render(json => $data);
} else {
return $self->render(json => { 'M' => "Not Found" }, 404);
}
}

# POST
sub create {
my $self = shift;
return $self->render(text => "Too large file", 500)
if $self->req->is_limit_exceeded;
my $type = $self->param("type");
my $ref = $self->_getBaseObjectRef();
my $data = $self->req->json;
my $obj = $self->Store->create($type, $data);
my $rtv = $self->Store->save_object($ref, $obj);
if ($rtv) {
$self->render(json => { M => "Created", S => 201}, status => 201);
} else {
$self->render(json => { M => "Forbidden"}, status => 403);
}
}

# PUT - update or create new
sub update {
my $self = shift;
return $self->render(text => "Too large file", 500)
if $self->req->is_limit_exceeded;
my $type = $self->param("type");
my $ref = $self->_getBaseObjectRef();
my $data = $self->req->json;
my $obj = $self->Store->create($type, $data);
my $rtv = $self->Store->save_object($ref, $obj);
if ($rtv) {
$self->render(json => { M => "Created", S => 201}, status => 201);
} else {
$self->render(json => { M => "Forbidden"}, status => 403);
}
}

sub _getBaseObjectRef {
my $self = shift;
my $type = $self->param("type");
my $uuid = $self->param("uuid");
my $owner = $self->param("owner");
my $alias = $self->param("alias");
return "$type/$uuid" if defined $uuid;
return "$type/$owner/$alias" if defined $owner && defined $alias;
return;
}

1;
Loading