NAME
Dancer2::Plugin::OpenAPIRoutes - automatic routes creation from Swagger specification file.
SYNOPSIS
use Dancer2;
use Dancer2::Plugin::OpenAPIRoutes;
OpenAPIRoutes(0);
DESCRIPTION
Automatically creates Dancer's routes from Swagger specification file. Extracts request parameters according to given spec. Uploaded files are Dancer2::Core::Request::Upload objects.
Automatically decodes JSON parameters if "Content-Type" is application/json. Automatically encodes answers to application/json if "Accept" header asks for it and returned value is reference. It checks also whether parameter is required or not but doesn't do real validation yet.
Catches thrown exceptions and makes JSON error messages if "Accept" is application/json.
Makes very smart mapping from route to Module::handler_function. For example:
/order:
post:
...
/order/{id}
delete:
...
patch:
...
will be mapped to Order::create(), Order::remove() and Order::update() accordingly.
CONFIGURATION
Schema details will be taken from your Dancer2 application config file, and should be specified as, for example:
plugins:
OpenAPIRoutes:
schema: public/swagger.yaml
namespace: MyApp
default_module: MyApp
- schema
-
Location of the Swagger spec file relative to the project root.
- namespace
-
Starting namespace for generated module name.
- default_module
-
Module name to put root's routes.
You have to call OpenAPIRoutes([$debug_flag, $custom_map])
in your main application module. Optionally you can pass true value as first argument to see how it maps routes to Modules and functions.
SMART MAPPING
This is probably the most crucial feature of this plugin. It automatically makes your application structured according to given spec file. It also makes your application less dependent on Dancer2 framework - you have to think more about application logic and less about framework details. Mapping is complicated but intuitive.
MAPPING RULES
Both the route and its HTTP method are used to compose the mapping.
HTTP METHOD MAPPING
This is starting point of the mapping algorithm. If route has only one method, then route's last part can be used as function name in module which name made of previous route parts.
- POST
-
In RESful terms POST means creation of some resource. That's why usually it maps to
create()
function with one exception: if route ends with /{someId} then it meansupdate()
. - GET
-
This methis is mapped to function
fetch()
. - DELETE
-
This method is mapped to
remove()
. Perl language already hasdelete()
function and it's better not to reuse its name. - PUT
-
In RESful terms PUT means full replacement of some resource. This method is mapped to
replace()
- PATCH
-
In RESful terms PATCH means partial update of some resource. This method is mapped to
update()
- OPTIONS
-
This method is mapped to
choices()
- HEAD
-
This method is mapped to
check()
You don't usually have to define HEAD method because it's done automatically from GET throwing away real answer.
ROUTES MAPPING
Basic idea is very simple: /resource/subresource is mapped to Resource::Subresource module and function name is mapped according HTTP method. Then there're special cases (from OpenAPI example spec):
It would be silly to put these three routes with single method in separate modules Pet::UploadImage
, Pet::FindByTags
and Pet::FindByStatus
. That's why routes with only one method are mapped to theirs "parents" with function name from last route part.
NOTICE: It's important to describe path parameters twice: in route and in parameters method's section. Because they are extracted as regexp captures
and routes with integer parameters should be dispatched first to avoid collision between /pet/{petId}
and /pet/findByTags
type of routes.
INTERFACE
ENVIRONMENT VARIABLES
When you need some variable from PSGI's environment, like REMOTE_USER, then it's really inconvenient to get directly from Dancer2 framework. There's a support to get it automatic using OpenAPI extension keyword x-env-{environment-variable} like x-env-remote-user: user. This keyword should be put in HTTP method section. Directive x-env-remote-user: user will put value of PSGI's environment variable REMOTE_USER
into input hash parameter key user
.
FUNCTION INTERFACE
Mapped route's function is called like this:
($result, $status, $callback) = ${module_name}::$module_func( \%input, $dsl );
Function receives hash reference with extracted parameters according to Swagger spec and Dancer2
DSL
object. This object is rarely needed but sometimes you need to have access to application's object, for example:
$dsl->app->send_file(...);
Most of the time function can return only one result like this:
sub fetch {
my $input = $_[0];
my $pet = schema->get_pet( $input->{petId} );
return $pet;
}
Sometimes you want to change response status:
sub remove {
my $input = $_[0];
my $error = schema->delete_pet( $input->{petId} );
if ($error) {
return ( { error => $error }, 'bad_request' );
}
return ( '', 'no_content' );
}
In some odd cases when you use old Dancer2, then you have to call specific functions directly from route handler using callback:
sub downloadFile {
my $dsl = $_[1];
# ...
return (
undef, undef,
sub {
$dsl->app->send_file(
$filename,
filename => $filename,
content_type =>
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
}
);
}
CUSTOM MAPPING
When you need some customization to your routes mapping, you can do it passing hash reference as second parameter to OpenAPIRoutes([$debug, $castom_map])
. You can change mapping for HTTP method for all paths or only for specific ones like this:
OpenAPIRoutes(1, {"get:/store/order/{orderId}" => "remove"});
(Very naughty joke): Instead of calling "fetch" for this specific path it will call "remove". The whole schema:
OpenAPIRoutes(1, {"$method[:$path]" => "[$function]:[$full::module::name]"});
like this:
OpenAPIRoutes(1, {
"put" => "update",
"post:/store/order" => "create_order",
"post:/store/image" => "upload_image",
# and so on ...
});
AUTHOR
This module was written and is maintained by:
Anton Petrusevich