khrt / raisin Goto Github PK
View Code? Open in Web Editor NEWRaisin - a REST API micro framework for Perl ๐ซ ๐ช
Raisin - a REST API micro framework for Perl ๐ซ ๐ช
I've tried to build deb package for Ubuntu Server 14.04 LTS and ran into problem: pairs
function available only in List::Util โฅ 1.29. I can't upgrade List::Util, because it is a part of perl-base package in Ubuntu.
pairs
used only in one place at Raisin/Routes.pm and can be replaced with splice
(since temporary array created in any case). I've built patched libraisin-perl and seems like it works for me. May be pairs simplicity is not worth packaging problems on major stable distribution?
The API described by https://petstore.swagger.io/v2/swagger.json (https://petstore.swagger.io)
This is a first step to cover Raisin::Plugin::Swagger
plugin with tests later on.
We ran into this problem today. Seems that using route_param
inside another route_param
loses the params for the parent route_param
.
Here is a test case demonstrating the problem:
subtest 'params nested route_param' => sub {
resource api => sub {
params requires => { name => 'id', type => undef };
route_param id => sub {
params(
requires => { name => 'start', type => undef },
optional => { name => 'count', type => undef },
);
get sub { param };
params(
requires => { name => 'sub_id', type => undef },
);
route_param sub_id => sub {
get sub { param };
};
}
};
my $app = Raisin::API->app;
my $e = $app->routes->routes->[1];
my %params = map { $_->name => $_ } @{ $e->params };
ok $params{id}, 'id';
is $params{id}->named, 1, 'named';
is $params{id}->required, 1, 'required';
ok $params{sub_id}, 'sub_id';
is $params{sub_id}->named, 1, 'named';
is $params{sub_id}->required, 1, 'required';
};
What actually ends up in %params
is only the sub_id
param. The id
param is lost.
When I use MooseX::Types as parameters in Raisin, I'l get an exception as soon as params_requires gets executed with one of these. Int and Str is enough - not to mention subtypes.
Can't locate object method "display_name" via package "Moose::Meta::TypeConstraint" at /usr/share/perl5/MooseX/Types/TypeDecorator.pm line 219 MooseX::Types::TypeDecorator::_try_delegate('MooseX::Types::TypeDecorator=HASH(0x2f83338)', 'display_name') called at /usr/share/perl5/MooseX/Types/TypeDecorator.pm line 184 MooseX::Types::TypeDecorator::AUTOLOAD('MooseX::Types::TypeDecorator=HASH(0x2f83338)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 351 Raisin::Plugin::Swagger::_param_type_object('Raisin::Param=HASH(0x2ecee70)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 253 Raisin::Plugin::Swagger::_parameters_object('GET', 'ARRAY(0x1c37870)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 213 Raisin::Plugin::Swagger::_operation_object('Raisin::Routes::Endpoint=HASH(0x2ececf0)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 177 Raisin::Plugin::Swagger::_paths_object('ARRAY(0x2063970)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 94 Raisin::Plugin::Swagger::_spec_20('Raisin::Plugin::Swagger=HASH(0x2ec91c8)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 23 Raisin::Plugin::Swagger::__ANON__('Raisin=HASH(0x2063a00)') called at /home/cbeckert/Raisin/lib/Raisin.pm line 155 Raisin::psgi('Raisin=HASH(0x2063a00)', 'HASH(0x2ff9ec8)') called at /home/cbeckert/Raisin/lib/Raisin.pm line 93 Raisin::__ANON__('HASH(0x2ff9ec8)') called at /home/cbeckert/perl5/lib/perl5/Plack/Middleware/CrossOrigin.pm line 114 Plack::Middleware::CrossOrigin::call('Plack::Middleware::CrossOrigin=HASH(0x2f8f988)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x2ff9ec8)') called at /home/cbeckert/Raisin/lib/Raisin/Middleware/Formatter.pm line 46 Raisin::Middleware::Formatter::call('Raisin::Middleware::Formatter=HASH(0x2fdf760)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Middleware/Lint.pm line 24 Plack::Middleware::Lint::call('Plack::Middleware::Lint=HASH(0x16880a0)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Middleware::StackTrace::try {...} at /usr/share/perl5/Try/Tiny.pm line 79 eval {...} at /usr/share/perl5/Try/Tiny.pm line 72 Plack::Middleware::StackTrace::call('Plack::Middleware::StackTrace=HASH(0x20636b8)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Middleware/AccessLog.pm line 24 Plack::Middleware::AccessLog::call('Plack::Middleware::AccessLog=HASH(0x2eaeac0)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Middleware/ContentLength.pm line 10 Plack::Middleware::ContentLength::call('Plack::Middleware::ContentLength=HASH(0x2fe0048)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x2ff9ec8)') called at /usr/share/perl5/Plack/Util.pm line 142 eval {...} at /usr/share/perl5/Plack/Util.pm line 142 Plack::Util::run_app('CODE(0x2fe0000)', 'HASH(0x2ff9ec8)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 170 HTTP::Server::PSGI::handle_connection('HTTP::Server::PSGI=HASH(0x2fdfeb0)', 'HASH(0x2ff9ec8)', 'IO::Socket::INET=GLOB(0x2fe0168)', 'CODE(0x2fe0000)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 129 HTTP::Server::PSGI::accept_loop('HTTP::Server::PSGI=HASH(0x2fdfeb0)', 'CODE(0x17cb6d0)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 55 HTTP::Server::PSGI::run('HTTP::Server::PSGI=HASH(0x2fdfeb0)', 'CODE(0x17cb6d0)') called at /usr/share/perl5/Plack/Handler/HTTP/Server/PSGI.pm line 14 Plack::Handler::HTTP::Server::PSGI::run('Plack::Handler::Standalone=HASH(0x2fe0258)', 'CODE(0x17cb6d0)') called at /usr/share/perl5/Plack/Loader.pm line 84 Plack::Loader::run('Plack::Loader=HASH(0x1479398)', 'Plack::Handler::Standalone=HASH(0x2fe0258)') called at /usr/share/perl5/Plack/Runner.pm line 277 Plack::Runner::run('Plack::Runner=HASH(0x1203150)') called at /usr/bin/plackup line 10
Using Moose::Util::TypeConstraints doesnt help either. A simple Setup such as:
subtype 'bla' ,as 'Str';
...
and then
params requires('mac', type => 'bla');
produces:
Can't locate object method "name" via package "bla" (perhaps you forgot to load "bla"?) at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 382 Raisin::Plugin::Swagger::_param_type('bla') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 242 Raisin::Plugin::Swagger::_parameters_object('GET', 'ARRAY(0x28aa768)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 213 Raisin::Plugin::Swagger::_operation_object('Raisin::Routes::Endpoint=HASH(0x27fc9b0)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 177 Raisin::Plugin::Swagger::_paths_object('ARRAY(0x2cce088)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 94 Raisin::Plugin::Swagger::_spec_20('Raisin::Plugin::Swagger=HASH(0x3a7b600)') called at /home/cbeckert/Raisin/lib/Raisin/Plugin/Swagger.pm line 23 Raisin::Plugin::Swagger::__ANON__('Raisin=HASH(0x2cce118)') called at /home/cbeckert/Raisin/lib/Raisin.pm line 155 Raisin::psgi('Raisin=HASH(0x2cce118)', 'HASH(0x3ae7560)') called at /home/cbeckert/Raisin/lib/Raisin.pm line 93 Raisin::__ANON__('HASH(0x3ae7560)') called at /home/cbeckert/perl5/lib/perl5/Plack/Middleware/CrossOrigin.pm line 114 Plack::Middleware::CrossOrigin::call('Plack::Middleware::CrossOrigin=HASH(0x3b0fd80)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x3ae7560)') called at /home/cbeckert/Raisin/lib/Raisin/Middleware/Formatter.pm line 46 Raisin::Middleware::Formatter::call('Raisin::Middleware::Formatter=HASH(0x3b4f9b0)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Middleware/Lint.pm line 24 Plack::Middleware::Lint::call('Plack::Middleware::Lint=HASH(0x219cf30)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Middleware::StackTrace::try {...} at /usr/share/perl5/Try/Tiny.pm line 79 eval {...} at /usr/share/perl5/Try/Tiny.pm line 72 Plack::Middleware::StackTrace::call('Plack::Middleware::StackTrace=HASH(0x251f3b8)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Middleware/AccessLog.pm line 24 Plack::Middleware::AccessLog::call('Plack::Middleware::AccessLog=HASH(0x38e8658)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Middleware/ContentLength.pm line 10 Plack::Middleware::ContentLength::call('Plack::Middleware::ContentLength=HASH(0x3b50fb8)', 'HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Component.pm line 50 Plack::Component::__ANON__('HASH(0x3ae7560)') called at /usr/share/perl5/Plack/Util.pm line 142 eval {...} at /usr/share/perl5/Plack/Util.pm line 142 Plack::Util::run_app('CODE(0x3b50f58)', 'HASH(0x3ae7560)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 170 HTTP::Server::PSGI::handle_connection('HTTP::Server::PSGI=HASH(0x3b50dd8)', 'HASH(0x3ae7560)', 'IO::Socket::INET=GLOB(0x3b0a330)', 'CODE(0x3b50f58)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 129 HTTP::Server::PSGI::accept_loop('HTTP::Server::PSGI=HASH(0x3b50dd8)', 'CODE(0x38e88c8)') called at /usr/share/perl5/HTTP/Server/PSGI.pm line 55 HTTP::Server::PSGI::run('HTTP::Server::PSGI=HASH(0x3b50dd8)', 'CODE(0x38e88c8)') called at /usr/share/perl5/Plack/Handler/HTTP/Server/PSGI.pm line 14 Plack::Handler::HTTP::Server::PSGI::run('Plack::Handler::Standalone=HASH(0x219cbe8)', 'CODE(0x38e88c8)') called at /usr/share/perl5/Plack/Loader/Restarter.pm line 32 Plack::Loader::Restarter::_fork_and_start('Plack::Loader::Restarter=HASH(0x1cf23a0)', 'Plack::Handler::Standalone=HASH(0x219cbe8)') called at /usr/share/perl5/Plack/Loader/Restarter.pm line 61 Plack::Loader::Restarter::run('Plack::Loader::Restarter=HASH(0x1cf23a0)', 'Plack::Handler::Standalone=HASH(0x219cbe8)') called at /usr/share/perl5/Plack/Runner.pm line 277 Plack::Runner::run('Plack::Runner=HASH(0x1ce9278)') called at /usr/bin/plackup line 10 127.0.0.1 - - [18/Oct/2018:17:25:30 +0200] "GET /swagger HTTP/1.1" 500 23459 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0"
I would find useful to add a new hook to be called in case of failed validation, to allow for modification of headers, content body before a standard 400 Bad Request response is returned.
It is clear to me how to add such a hook, but not how to get validation errors that now are logged on STDERR.
The CPAN version of Raisin, app->log() is broken, it was fixed on this commit: 8c9c140
Could you please release to CPAN a new version?
It appears that if you have an optional param that has a type constraint (such as Enum
from Types::Standard
, or enum()
from Moose::Util::TypeConstraints
, then you will not get an Invalid Parameters
response.
If the param is instead changed to requires
, then you get the expected Invalid Parameters
response.
For example
params(
optional('foo', type => Enum[qw(bar baz)])
)
get sub {
...
}
If called with foo=invalid
, then a 200 OK response is received.
The problem is in Raisin::Request::prepare_params()
I have a patch with a test case that I will submit as a PR shortly.
I'm working on an OS X 10.9.5 system, Chrome or Safari, Swagger UI from the demo on the swagger site and using perl-5.18.4 installed via perlbrew and have installed raisin like this:
cpanm -l ~/tmp/raisin-experiment Raisin Log::Dispatch
I can increment and fetch a users bump count from the command line, e.g. using httpie
$ http PUT localhost:5000/api/users/1/bump
HTTP/1.0 200 OK
Content-Length: 15
Content-Type: application/yaml
Date: Tue, 10 Mar 2015 16:40:27 GMT
Server: HTTP::Server::PSGI
X-Framework: Raisin 0.58
---
success: 1
$ http localhost:5000/api/users/1/bump
HTTP/1.0 200 OK
Content-Length: 12
Content-Type: application/yaml
Date: Tue, 10 Mar 2015 16:40:28 GMT
Server: HTTP::Server::PSGI
X-Framework: Raisin 0.58
---
data: 1
The log output of that interaction looks like this:
127.0.0.1 - - [10/Mar/2015:09:40:27 -0700] "PUT /api/users/1/bump HTTP/1.1" 200 15 "-" "HTTPie/0.9.1"
127.0.0.1 - - [10/Mar/2015:09:40:28 -0700] "GET /api/users/1/bump HTTP/1.1" 200 12 "-" "HTTPie/0.9.1"
If, on the other hand, I try to use the API from a swagger sandbox I see the following log message:
127.0.0.1 - - [10/Mar/2015:09:35:58 -0700] "OPTIONS /api/users/1/bump HTTP/1.1" 403 9 "http://petstore.swagger.io/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36"
(note, the method is OPTIONS, not PUT) and the user never gets bumped (a print statement in UseCase::User::bump never gets called and http
as above shows that the users bump count is still null.
This seems to be related to CORS and swagger ui issue 44.
If I try to call options myself, I get a 404 (not a 403 as above).
$ http localhost:5000/api/users/1
HTTP/1.0 200 OK
Content-Length: 90
Content-Type: application/yaml
Date: Tue, 10 Mar 2015 16:59:09 GMT
Server: HTTP::Server::PSGI
X-Framework: Raisin 0.58
---
data:
bumped: 1
email: [email protected]
name: Darth Wader
password: empire
$ http OPTIONS localhost:5000/api/users/1/bump
HTTP/1.0 404 Not Found
Content-Length: 13
Content-Type: text/plain
Date: Tue, 10 Mar 2015 16:59:15 GMT
Server: HTTP::Server::PSGI
X-Framework: Raisin 0.58
Nothing found
Adding a print statement to Raisin::Plugin::Swagger::build suggests that the add_middleware calls is being executed.
Any suggestions?
g.
The documentation for the ::Logger class presents a contradiction.
The synopsis says:
my $logger = Raisin::Logger->new;
$logger->log(info => 'Hello, world!');
But 'info' isn't an option, according to the log() method entry:
METHODS
log
Accept's two parameters: level and message.
We would like an endpoint to to take a list of objects as an input, ex:
{
entries: [
{ id: 1, title: 'Entry 1'},
{ id: 2, title: 'Entry 2'}
]
}
Naive approach, where we don't validate each object in list would be like this
params(
requires(
entries => (
type => ArrayRef[HashRef],
desc => 'Entries',
)
),
);
This generates following bit in swagger:
{
"parameters": [
{
"required": true,
"description": "Entries",
"items": {
"$ref": "#/definitions/Entries-MD5Hash"
},
"in": "body",
"type": "array",
"name": "entries"
}
]
}
But there is no definition of Entries-MD5Hash
in definitions
section in swagger, which makes the swagger.json out of spec.
Ideally, we would be able to define the structure of the HashRef too as part of the definition.
Any suggestions how to deal with this?
Thanks!
Another undeclared dependency:
...
Can't locate JSON.pm in @INC (you may need to install the JSON module) (@INC contains: ... .) at t/behaviour/app-deserialize.t line 7.
BEGIN failed--compilation aborted at t/behaviour/app-deserialize.t line 7.
t/behaviour/app-deserialize.t .........
Dubious, test returned 2 (wstat 512, 0x200)
No subtests run
... (etc) ...
If you don't have 'use Raisin::Types' this in your RaisinApp (as in example). It will stuff up.
eg
$ raisin --routes --params api.pl
GET //user
Can't locate object method "name" via package "Raisin::Types::Integer" (perhaps you forgot to load "Raisin::Types::Integer"?) at .../bin/raisin line 62..
Hello, I'm wondering if an API call like this could be done and how...
GET http://example/api/:id1/:id2/:id3
The goal is to receive the three route_param id1, id2 and id3 in the get sub {} and then process them as needed...
Thanks.
I found the documentation for the logging features confusing. And when i looked in examples/ as well as the synopsis pod they are not used.
Could you please add some logging examples in to the examples/ scripts as well as into the synopsis pod of Raisin.pm ? thanks.
Using Raisin and triggering an error in a route gives Undefined subroutine &Raisin::Plugin::Logger::Dumper called at /usr/local/share/perl/5.24.1/Raisin/Plugin/Logger.pm line 39
Maybe you should add use Data::Dumper to the file, which fixes the problem for me.
Using the samples at Github I set up a rather simple server:
use HTTP::Status qw(:constants);
use List::Util qw(max);
use Raisin::API;
use Types::Standard qw(HashRef Any Int Str);
plugin 'Logger', fallback => 1;
app->log(debug => 'Starting Raisin...');
middleware 'CrossOrigin',
origins => '*',
methods => [qw/DELETE GET HEAD OPTIONS PATCH POST PUT/],
headers => [qw/accept authorization content-type api_key_token/];
plugin 'Swagger';
swagger_setup(
title => 'A simple test',
description => 'Roundtrip Raisin - Swagger - Perl Client',
);
desc 'Users API';
resource foo => sub {
summary 'foo';
params(
requires('foo' , type=> Str ),
);
post sub {
my $params = shift;
app->log(debug => params->{foo});
res->status(HTTP_CREATED);
{ success => 1 }
}
};
run;
Which results in the following Swagger-File:
{
"security" : [],
"schemes" : [
"http"
],
"host" : "localhost:5000",
"info" : {
"description" : "Roundtrip Raisin - Swagger - Perl Client",
"version" : "0.0.1",
"title" : "A simple test"
},
"securityDefinitions" : {},
"basePath" : "/",
"definitions" : {},
"swagger" : "2.0",
"consumes" : [
"application/x-yaml",
"application/json"
],
"tags" : [
{
"name" : "foo",
"description" : "Users API"
}
],
"paths" : {
"/foo" : {
"post" : {
"consumes" : [
"application/x-yaml",
"application/json"
],
"tags" : [
"foo"
],
"produces" : [
"application/x-yaml",
"application/json"
],
"summary" : "foo",
"responses" : {
"default" : {
"description" : "Unexpected error"
}
},
"operationId" : "post_foo",
"description" : "",
"parameters" : [
{
"in" : "formData",
"description" : "",
"name" : "foo",
"type" : "string",
"required" : true
}
]
}
}
},
"produces" : [
"application/x-yaml",
"application/json"
]
}
Pasting this into the Swagger editor results in the following error message:
Semantic error at paths./foo.post
Operations with Parameters of "in: formData" must include "application/x-www-form-urlencoded" or "multipart/form-data" in their "consumes" property
... and the suggested App call:
curl -X POST "http://localhost:5000/foo" -H "accept: application/json" -H "Content-Type: application/json" -d "foo=bar"
which is of course not valid as we'd expect something JSON we could pick up from POST, such as
curl -X POST "http://localhost:5000/foo" -H "accept: application/json" -H "Content-Type: application/json" -d '{"foo":"bar"}'
The code generated with codegen doen't work either as the server expects formData. The documentation suggests parameter settings as I put in my sample.
Using the full code listing from the Github synposis (user management sample) doesn't result in usable code either.
What's wrong here? An issue with the software or with the documentation?
I just upgraded from 0.26 and now basePath doesn't get rid of the "api-docs/" anymore and stays empty (see line 114 in APIDocs.pm).
This breaks SwaggerUI-compatible doc system (like wordnik) and all routs are now just appended after "api-docs/".
basePath should be (like it used to be) the request url minus the "api-docs/"
I edited line 114 in APIDocs.pm and hardcoded the $base_path but that works only because I have only 1 Raisin API running on my perl.
Please fix asap.
By default jquery send this accept header:
application/json, text/javascript, */*; q=0.01
Raisin return "Invalid accept header" error, because it expects only one media type
Raisin::API sub import calls export_to_level which expects 3 parameters:
MyPackage->export_to_level(
$where_to_export, $package, @what_to_export
);
However, Raisin::API only passes two parameters. The end result of this is that the first argument passed to "use Raisin::API" is ignored.
This might not be a bug, there doesn't seem to be a way to access the json serializer to configure it (for example, to enable convert_blessed()
Hello,
The function Raisin::Util::detect_serializer does not support this value of Content-Type:
application/json; charset=UTF-8
Hey,
I'm looking into using Raisin for implementing a nice API into a legacy system, using OAuth2 bearer tokens. I couldn't find any Raisin-based solution that does something similar, aside from Rack middleware that I include and then manually check the environment at the top of every handler (ugly!).
I'd like to make a proof of concept where I intercept a HTTP request, call a custom handler to check that a bearer token exists and is valid, and then proceed if that is the case.
I'm trying to do this as a plugin. However, I've hit a few walls, most notably that I can register a plugin but I can't actually get any information about the route handler I'm calling it before and I can't register a hook to intercept calls.
Working from a high-level concept of how I'd like it to work inspired by Wine Bouncer:
plugin 'OAuth2';
oauth2_setup(
validate_bearer_token => sub {
my ($token, $scopes) = @_;
# ...
}
);
summary 'Get user';
oauth2 'users:read', 'admin';
params(
required('userid', type => Int, desc => 'User ID'),
);
get sub {
};
I'd like to mark up this route with users:read
and admin
as a list of required token scopes (any one will do). I tried hacking this into the $SETTINGS
used by the Swagger plugin, but looks like the ones that aren't known are not actually propagated or stored anywhere.
Next up, I'd like to hook into the request chain, and have my handler fire at before_validation
, for instance. However, hooks don't have any context about the route or request, and neither can they stop processing. Bummer! I was thinking to do something like:
before_validation sub {
my ($self, $route, $request, $response) = @_;
if ($route->metadata_or_whatever->{oauth2}) {
# more checks here ...
return (403, 'Not permitted to access this resource');
}
};
from the plugin code iself, and just call into the user bearer validator (seen above) to check the token.
Is the idea behind hooks/plugins to be able to accommodate something like this or am I looking at the wrong feature?
I noticed a difference in the Swagger file genererated when I switch from Yaml to Json format and vice versa. Both seem to be incomplete in the way that they lack the definition of the Album entity causing the Swagger output description to be incomplete ("null")
{
"artist": "string",
"name": "string",
"hash": "string",
"id": 0,
"albums": [
null
]
}
YAML:
basePath: /
consumes: &1
- application/x-yaml
- application/json
definitions:
MusicApp::Entity::Artist-302668EB84:
properties:
albums:
items:
$ref: '#/definitions/MusicApp::Entity::Album-AFC2856845'
type: array
artist:
description: Artist name
type: string
hash:
description: ID*10
type: string
id:
description: ID
format: int32
type: integer
name:
description: Artist name /shown only if it equals to Nirvana/
type: string
required:
- id
- name
- artist
- hash
- albums
type: object
host: localhost:5004
info:
title: API
version: 0.0.1
paths:
/albums:
get:
consumes: *1
description: ''
operationId: get_albums
produces: *1
responses:
default:
description: Unexpected error
summary: List
tags:
- albums
'/albums/{id}':
get:
consumes: *1
description: ''
operationId: get_albums__id
parameters:
- description: ''
format: int32
in: path
name: id
required: &2 !!perl/scalar:JSON::PP::Boolean 1
type: integer
produces: *1
responses:
default:
description: Unexpected error
summary: ''
tags:
- albums
/artists:
get:
consumes: *1
description: ''
operationId: get_artists
produces: *1
responses:
200:
description: List
schema:
$ref: '#/definitions/MusicApp::Entity::Artist-302668EB84'
default:
description: Unexpected error
summary: List
tags:
- artists
'/artists/{id}':
get:
consumes: *1
description: ''
operationId: get_artists__id
parameters:
- description: ''
format: int32
in: path
name: id
required: *2
type: integer
produces: *1
responses:
200:
description: ''
schema:
$ref: '#/definitions/MusicApp::Entity::Artist-302668EB84'
default:
description: Unexpected error
summary: ''
tags:
- artists
produces: *1
schemes:
- http
security: []
securityDefinitions: {}
swagger: 2.0
tags:
- description: Artist API
name: artists
- description: Albums API
name: albums
The first complain is about the line 60:
required: &2 !!perl/scalar:JSON::PP::Boolean 1
which is: unknown tag !tag:yaml.org,2002:perl/scalar:JSON::PP::Boolean
We have a definition of the Entity usicApp::Entity::Artist-302668EB84 which references the definition for the nested object Albums which links here
#/definitions/MusicApp::Entity::Album-AFC2856845'
but the corresponding definition for Album doesn't exist.
Json
The Json Representation of the same application causes an additional complains about
Semantic error at definitions.MusicApp::Entity::Artist-302668EB84.properties.albums.items.$ref
$refs must reference a valid location in the document
Jump to line 24
due to the quoting of the object names. (probably comes from the fact that Json requires quoting)
The definition for the Album entity is missing here too.
This works in 0.86 but not in 0.87
resource 'etd' => sub {
summary 'Get an estimate';
desc 'Estimated Time of Delivery';
route_param 'locale' => sub {
route_param 'postcode' => sub {
# query param style
before_validation \&fix_material;
params(
requires( 'locale', type => Locale, desc => 'Locale (AU/NZ)' ),
requires( 'postcode', type => PostCode, desc => 'Post Code' ),
requires(
'material',
type => ArrayRef [MaterialCode],
desc => 'Material Code(s)'
),
optional(
'orderdate',
type => Str,
desc => 'Order Date',
default => 'now'
)
);
summary
'GET one or more products via material code as query parameter';
get \&guess_etd;
# url path style
params(
requires( 'locale', type => Locale, desc => 'Locale (AU/NZ)' ),
requires( 'postcode', type => PostCode, desc => 'Post Code' ),
requires(
'material',
type => MaterialCode,
desc => 'Material Code'
),
optional(
'orderdate',
type => Str,
desc => 'Order Date',
default => 'now'
)
);
route_param 'material' => sub {
summary 'GET a single product via material code in url';
get \&guess_etd;
}; # route_param 'material'
}; # route_param 'postcode'
}; # route_param 'locale'
}; # resource 'etd'
The api is as follows
/etd///
or
/etd///?material=&material=
Locale, PostCode, ad MaterialCode are custom constraints.
What doesnt work in 0.87 is the route_param version. The &guess_etd is called BUT the $_[0] is empty rather than containing the params as it does with the material in query query version
the fix_material sub massages the material= so that its always an arrayref even if there is just one value (which is an annoying bug of itself, as a single value will give a scalar and the type will fail)
t/unit/api.t fails if Test::Exception is not installed:
Can't locate Test/Exception.pm in @INC (you may need to install the Test::Exception module) (@INC contains: /home/cpansand/.cpan/build/2019062010/Raisin-0.83-2/blib/lib /home/cpansand/.cpan/build/2019062010/Raisin-0.83-2/blib/arch /home/cpansand/.cpan/build/2019062010/Type-Tiny-1.004004-1/blib/arch /home/cpansand/.cpan/build/2019062010/Type-Tiny-1.004004-1/blib/lib /home/cpansand/.cpan/build/2019062010/Exporter-Tiny-1.002001-0/blib/arch /home/cpansand/.cpan/build/2019062010/Exporter-Tiny-1.002001-0/blib/lib /home/cpansand/.cpan/build/2019062010/Plack-Middleware-CrossOrigin-0.014-1/blib/arch /home/cpansand/.cpan/build/2019062010/Plack-Middleware-CrossOrigin-0.014-1/blib/lib /home/cpansand/.cpan/build/2019062010/Plack-1.0047-1/blib/arch /home/cpansand/.cpan/build/2019062010/Plack-1.0047-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-TCP-2.19-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-TCP-2.19-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-Headers-Fast-0.22-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-Headers-Fast-0.22-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-Entity-Parser-0.21-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-Entity-Parser-0.21-1/blib/lib /home/cpansand/.cpan/build/2019062010/WWW-Form-UrlEncoded-0.26-1/blib/arch /home/cpansand/.cpan/build/2019062010/WWW-Form-UrlEncoded-0.26-1/blib/lib /home/cpansand/.cpan/build/2019062010/Stream-Buffered-0.03-1/blib/arch /home/cpansand/.cpan/build/2019062010/Stream-Buffered-0.03-1/blib/lib /home/cpansand/.cpan/build/2019062010/JSON-MaybeXS-1.004000-1/blib/arch /home/cpansand/.cpan/build/2019062010/JSON-MaybeXS-1.004000-1/blib/lib /home/cpansand/.cpan/build/2019062010/Cpanel-JSON-XS-4.12-1/blib/arch /home/cpansand/.cpan/build/2019062010/Cpanel-JSON-XS-4.12-1/blib/lib /home/cpansand/.cpan/build/2019062010/Hash-MultiValue-0.16-1/blib/arch /home/cpansand/.cpan/build/2019062010/Hash-MultiValue-0.16-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-MultiPartParser-0.02-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-MultiPartParser-0.02-1/blib/lib /home/cpansand/.cpan/build/2019062010/Filesys-Notify-Simple-0.13-1/blib/arch /home/cpansand/.cpan/build/2019062010/Filesys-Notify-Simple-0.13-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-SharedFork-0.35-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-SharedFork-0.35-1/blib/lib /home/cpansand/.cpan/build/2019062010/File-ShareDir-1.116-1/blib/arch /home/cpansand/.cpan/build/2019062010/File-ShareDir-1.116-1/blib/lib /home/cpansand/.cpan/build/2019062010/Class-Inspector-1.34-1/blib/arch /home/cpansand/.cpan/build/2019062010/Class-Inspector-1.34-1/blib/lib /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-AsHTML-0.15-1/blib/arch /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-AsHTML-0.15-1/blib/lib /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-2.04-1/blib/arch /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-2.04-1/blib/lib /home/cpansand/.cpan/build/2019062010/Cookie-Baker-0.11-1/blib/arch /home/cpansand/.cpan/build/2019062010/Cookie-Baker-0.11-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-Time-0.08-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-Time-0.08-1/blib/lib /home/cpansand/.cpan/build/2019062010/Apache-LogFormat-Compiler-0.35-1/blib/arch /home/cpansand/.cpan/build/2019062010/Apache-LogFormat-Compiler-0.35-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-Requires-0.10-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-Requires-0.10-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-MockTime-0.17-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-MockTime-0.17-1/blib/lib /home/cpansand/.cpan/build/2019062010/POSIX-strftime-Compiler-0.42-1/blib/arch /home/cpansand/.cpan/build/2019062010/POSIX-strftime-Compiler-0.42-1/blib/lib /home/cpansand/.cpan/build/2019062010/File-ShareDir-Install-0.13-1/blib/arch /home/cpansand/.cpan/build/2019062010/File-ShareDir-Install-0.13-1/blib/lib /home/cpansand/.cpan/build/2019062010/JSON-4.02-1/blib/arch /home/cpansand/.cpan/build/2019062010/JSON-4.02-1/blib/lib /home/cpansand/.cpan/build/2019062010/Type-Tiny-1.004004-1/blib/arch /home/cpansand/.cpan/build/2019062010/Type-Tiny-1.004004-1/blib/lib /home/cpansand/.cpan/build/2019062010/Exporter-Tiny-1.002001-0/blib/arch /home/cpansand/.cpan/build/2019062010/Exporter-Tiny-1.002001-0/blib/lib /home/cpansand/.cpan/build/2019062010/Plack-Middleware-CrossOrigin-0.014-1/blib/arch /home/cpansand/.cpan/build/2019062010/Plack-Middleware-CrossOrigin-0.014-1/blib/lib /home/cpansand/.cpan/build/2019062010/Plack-1.0047-1/blib/arch /home/cpansand/.cpan/build/2019062010/Plack-1.0047-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-TCP-2.19-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-TCP-2.19-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-Headers-Fast-0.22-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-Headers-Fast-0.22-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-Entity-Parser-0.21-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-Entity-Parser-0.21-1/blib/lib /home/cpansand/.cpan/build/2019062010/WWW-Form-UrlEncoded-0.26-1/blib/arch /home/cpansand/.cpan/build/2019062010/WWW-Form-UrlEncoded-0.26-1/blib/lib /home/cpansand/.cpan/build/2019062010/Stream-Buffered-0.03-1/blib/arch /home/cpansand/.cpan/build/2019062010/Stream-Buffered-0.03-1/blib/lib /home/cpansand/.cpan/build/2019062010/JSON-MaybeXS-1.004000-1/blib/arch /home/cpansand/.cpan/build/2019062010/JSON-MaybeXS-1.004000-1/blib/lib /home/cpansand/.cpan/build/2019062010/Cpanel-JSON-XS-4.12-1/blib/arch /home/cpansand/.cpan/build/2019062010/Cpanel-JSON-XS-4.12-1/blib/lib /home/cpansand/.cpan/build/2019062010/Hash-MultiValue-0.16-1/blib/arch /home/cpansand/.cpan/build/2019062010/Hash-MultiValue-0.16-1/blib/lib /home/cpansand/.cpan/build/2019062010/HTTP-MultiPartParser-0.02-1/blib/arch /home/cpansand/.cpan/build/2019062010/HTTP-MultiPartParser-0.02-1/blib/lib /home/cpansand/.cpan/build/2019062010/Filesys-Notify-Simple-0.13-1/blib/arch /home/cpansand/.cpan/build/2019062010/Filesys-Notify-Simple-0.13-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-SharedFork-0.35-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-SharedFork-0.35-1/blib/lib /home/cpansand/.cpan/build/2019062010/File-ShareDir-1.116-1/blib/arch /home/cpansand/.cpan/build/2019062010/File-ShareDir-1.116-1/blib/lib /home/cpansand/.cpan/build/2019062010/Class-Inspector-1.34-1/blib/arch /home/cpansand/.cpan/build/2019062010/Class-Inspector-1.34-1/blib/lib /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-AsHTML-0.15-1/blib/arch /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-AsHTML-0.15-1/blib/lib /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-2.04-1/blib/arch /home/cpansand/.cpan/build/2019062010/Devel-StackTrace-2.04-1/blib/lib /home/cpansand/.cpan/build/2019062010/Cookie-Baker-0.11-1/blib/arch /home/cpansand/.cpan/build/2019062010/Cookie-Baker-0.11-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-Time-0.08-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-Time-0.08-1/blib/lib /home/cpansand/.cpan/build/2019062010/Apache-LogFormat-Compiler-0.35-1/blib/arch /home/cpansand/.cpan/build/2019062010/Apache-LogFormat-Compiler-0.35-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-Requires-0.10-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-Requires-0.10-1/blib/lib /home/cpansand/.cpan/build/2019062010/Test-MockTime-0.17-1/blib/arch /home/cpansand/.cpan/build/2019062010/Test-MockTime-0.17-1/blib/lib /home/cpansand/.cpan/build/2019062010/POSIX-strftime-Compiler-0.42-1/blib/arch /home/cpansand/.cpan/build/2019062010/POSIX-strftime-Compiler-0.42-1/blib/lib /home/cpansand/.cpan/build/2019062010/File-ShareDir-Install-0.13-1/blib/arch /home/cpansand/.cpan/build/2019062010/File-ShareDir-Install-0.13-1/blib/lib /home/cpansand/.cpan/build/2019062010/JSON-4.02-1/blib/arch /home/cpansand/.cpan/build/2019062010/JSON-4.02-1/blib/lib /opt/perl-5.28.2t/lib/site_perl/5.28.2/x86_64-linux-thread-multi /opt/perl-5.28.2t/lib/site_perl/5.28.2 /opt/perl-5.28.2t/lib/5.28.2/x86_64-linux-thread-multi /opt/perl-5.28.2t/lib/5.28.2 .) at t/unit/api.t line 6.
BEGIN failed--compilation aborted at t/unit/api.t line 6.
t/unit/api.t ..........................
Dubious, test returned 2 (wstat 512, 0x200)
No subtests run
Ist there an easier way to return Perl objects (blessed or Moose) than to
Returning blessed objects to YAML seems to work out of the box. JSON support for plain hashes is also implemented in Raisin.
But perhaps I'm missing the direct path.
Cool project!
I noticed when generating swagger I get an 'Unexpected Error'. How can I document the expected response in Raisin?
{
"get": {
"consumes": [
"application/x-yaml",
"application/json"
],
"operationId": "get_users__id",
"responses": {
"default": {
"description": "Unexpected error"
}
},
"tags": [
"users"
],
"produces": [
"application/x-yaml",
"application/json"
],
"summary": "Show user",
"description": "",
"parameters": [
{
"type": "integer",
"in": "path",
"required": true,
"description": "User ID",
"format": "int32",
"name": "id"
}
]
},
Thanks!
Using Boolean params with JSON does not work because the validation fails. JSON returns JSON::true/JSON::false for true/false values, so these may not be recognized by the validator.
Idea for solution:
Convert JSON::true/JSON::false values in Bool type parameters to 1/0 before validating them.
Is there a way to trigger Type::Tiny to coerce the values?
Potentially like
params( requires('postcode', type => PostCode, desc => 'Post Code', **coerce => 1** ), requires('prodcode', type => ProdCode, desc => 'Product Code'), optional('orderdate',type => Str, desc => 'Order Date', default => 'now') );
Sort of like how you might go
has lines => (
is => "ro",
isa => ArrayRef->plus_coercions(LinesFromStr),
coerce => 1,
);
See also http://search.cpan.org/~tobyink/Type-Tiny-1.000006/lib/Type/Tiny/Manual/Coercions.pod
Just have tried to build Raisin 0.70 on debian jessie and have got error:
$ perl -Iblib/lib t/unit/middleware/formatter.t
...
not ok 2 - content: yaml, default_format: yaml
# Failed test 'content: yaml, default_format: yaml'
# at t/unit/middleware/formatter.t line 96.
# got: undef
# expected: 'yaml'
# unsupported media type: application/xml
...
Dumping env have revealed cause:
'_content' => 'YAML Error: Stream does not end with newline character
# Code: YAML_PARSE_ERR_NO_FINAL_NEWLINE
# Line: 0
# Document: 0
Looks like YAML not satisfied with 'key: val' , and wants "\n" at the end.
Thanks for this great and simple descriptive API!
I am thinking that the following code:
Lines 25 to 37 in 7e460ee
will filter out also defined null values in JSON body, which makes it difficult to define optional params that can be null, meaning a typical PUT or PATCH request to "unset" a value. Say, you have a DB column that needs to be NULLed.
Of course, you still get the "undef"s from req->raisin_parameters, so it can easily be circumvented, but I am still unsure why "undef"s should be removed from declared_params and params?
Is line 34 actually neccessary, is my point.
The "description" of an API should preferably be editable. Now it's just hardcoded to say "Operations about " (see line 101 in APIDocs.pm).
Many thanks in advance for making this configurable per in a future release.
The current docs & examples use different notation for resource:
resource somerecource => sub {
and
resource => somerecource => sub {
Apparently only the last is correct as it doesn't give the following parsing-errors when running raisin --routes
:
Use of uninitialized value $params{"method"} in uc at /Library/Perl/5.12/Raisin/Routes.pm line 34.
Method and path are required at /Library/Perl/5.12/Raisin.pm line 53.
Is there a canonical way for Raisin apps to read in their config file?
Many other frameworks provide this type of support, making life easier (or annoying you, if you dont like they way they do it)
Hello,
I see no documentation about security object definition, but I see there are some commented lines on the code:
my %spec = (
swagger => '2.0',
info => _info_object($app),
host => $req->env->{HTTP_HOST},
basePath => $base_path,
schemes => [$req->scheme],
consumes => \@content_types,
produces => \@content_types,
paths => _paths_object($routes), #R
definitions => _definitions_object($routes),
#parameters => undef,
#responses => undef,
#securityDefinitions => undef,
#security => undef,
tags => _tags_object($self->app),
#externalDocs => '', # TODO
);
So is there some plans adding support or thoughts about security definitions on OpenAPI spec?
Personally I'll be needing api_key, but trying to figure it out how to implement it with Raisin :) Code examples on authentication also are very welcome.
Loving this simplicity of Raisin ๐ Little of coding and you have working REST api
Best regards, Jarkko
If the result of a DBIx::Class query given to present
returns no rows, the generated JSON is undef
, rather than an empty array. Can the behaviour be changed to make it return []
, so as to make the behaviour consistent?
Many thanks,
Dave
Basically, I only want JSON. Route extensions should return 404, and a non-JSON Accept: header should return 406. I don't see any way in docs or code to arrange this.
In 5.10, split in scalar context still split to @, which was removed in 5.12, this affects, at least, Raisin/Middleware/Formatter.pm@65, which throws the deprecation warning when run on 5.10/5.10.1
Use of implicit split to @ is deprecated at /lib/perl5/Raisin/Middleware/Formatter.pm line 65.
Hello,
I am trying to use Raisin for an API which returns a single row of data, read by calling DBIx::Class's ->find()
. I am having trouble making the generated OpenAPI / Swagger schema agree with the generated JSON.
I have two Raisin::Entity classes. One represents the row from DBIx::Class. The other represents the JSON data presented by an endpoint. It refers to the first via expose 'data', using => ...
. When the Swagger schema is generated, it declared the data
is an array, when it is in fact an object represented by a hash.
Is there a way to make Raisin generate a schema matching the returned data?
Many thanks,
Dave
When an object/element/whatever doesnt exist, whats the proper way of returning a 404?
For example, with:
curl localhost:5000/user/:id
If there is no user with that ID, i would like to return a 404 - which i believe is the most correct behaviour for rest
If you do
namespace '/user' => sub {
you get '//user' as the route
raisin --routes api.pl
GET //user
GET //user/all
POST //user
GET //user/{id}
PS the example also has a mistake:
sub {
get sub {
my $params = shift;
%USERS{ $params->{id} };
};
};
should be $USERS
Raisin automatically creates tags for route parameters and subsequent resources. Unfortunately the swagger file comes out with an empty description - or null which causes an error in the Swagger editor:
This code:
use strict;
use warnings;
use Carp;
use HTTP::Status qw(:constants);
use Data::Dumper;
use Raisin::API;
use Types::Standard qw(HashRef Any Int Str);
plugin 'Logger', fallback => 1;
app->log(debug => 'Start');
api_format 'json';
middleware 'CrossOrigin',
origins => q(*),
methods => [qw/DELETE GET HEAD OPTIONS PATCH POST PUT/],
headers => [qw/accept authorization content-type api_key_token/];
plugin 'Swagger';
swagger_setup(
title => 'Test Raisin Api',
description => 'TestRaisin',
contact => {
name => 'Conrad Beckert',
url => 'http://beccon.de',
email => '[email protected]',
},
license => {
name => 'Testmich - darf alles',
url => 'http://www.testmich.de',
},
);
desc 'Users API';
resource cm => sub {
summary 'cm sub';
params requires('mac', type => Str);
route_param mac => sub {
desc 'route param desc';
summary 'route param summary';
resource 'foo' => sub {
desc "foo desc";
get sub {
desc "foo get desc";
my $params = shift;
my $mac = $params->{mac};
res->status(HTTP_OK);
return { hallo => $mac };
};
}
};
};
run;
__END__
produces:
`tags:
Which the Swagger Editor marks as error.
Hi,
I get the feeling that when serving json-data, Raisin encodes the outgoing data twice: once when serializing it via encode_json (Plugin/Format/JSON.pm) which as you know encodes it already into utf8 (see JSON docs), and again in Response.pm where you do $self->body(encode 'UTF-8', $body);
(line 61).
This results in any non-latin1 characters like the letter รซ (e-with-umlaut) to be output by Raisin as รยซ
which is typical of a double-encoding of the letter as you can see from the UTF-8 encoding debugging chart here:
http://www.i18nqa.com/debug/utf8-debug.html
I tried commenting out line 61 in Response.pm but that leaves the response body with no content.
Am I doing something wrong or is Raisin, in fact, wrongly encoding twice into utf8?
Many thanks in advance!
Chris.
I've noticed with the music-app example, the response is contained within an object with key "data". But, when interpreting the Swagger Schema ( visually and with swagger-ui tool ), there is no hint that the top-level object with key "data" should ever exist. See below image for swagger-ui schema showing the mismatch in expected schema response data structure and actual server response data structure. Do you have any ideas on how to make schema-expected and actual responses match when using Entities? I have some rough ideas on how to solve this with a change to Raisin source, but could potentially introduce breaking-changes for others. Here is one idea:
Replace Line 57 in Raisin::Middleware::Formatter with:
if ( defined $r->body->{data} and ref $r->body->{data} ) { $r->body($s->serialize($r->body->{data})); } else { $r->body($s->serialize($r->body)); }
When seeing bug #22 I also saw a message like these in the logs:
Param `%s` didn't pass constraint `%s` with value "%s"
This should not come in the log, the placeholders should be replaced by the real values.
Having a problem with parameters -- if I declare a parameter as ArrayRef[Str], the parser will only see the parameter if there is more than one of them. If I declare a paramter as Str, the parser will not see the parameter unless there is only one.
use strict;
use Data::Dumper;
use warnings;
use FindBin;
use lib ( "$FindBin::Bin/../lib", ".", "$FindBin::Bin/../../Raisin/lib" );
use utf8;
use Raisin::API;
use Types::Standard qw(ArrayRef HashRef Any Int Str);
api_version 0.1;
api_format 'json';
desc 'Items API';
resource items => sub {
summary 'Query items';
params(
optional('a', type => ArrayRef[Str], default => undef,
desc => 'test arrayref param'),
optional('s', type => Str, default => undef,
desc => 'test string')
);
get sub {
my $params = shift;
my $items = undef;
my $total = undef;
print "Params: " . Dumper($params);
};
};
run;
Queries:
curl 'http://localhost:5000/items?s=s1'
INFO 2016-11-22T15:09:51.790 `a` optional and empty
Params: $VAR1 = {
's' => 's1'
};
127.0.0.1 - - [22/Nov/2016:15:09:51 -0800] "GET /items?s=s1 HTTP/1.1" 200 1 "-" "curl/7.47.1
curl 'http://localhost:5000/items?s=s1&s=s2'
INFO 2016-11-22T15:10:32.437 `a` optional and empty
WARN 2016-11-22T15:10:32.439 Param `s` didn't pass constraint `Str` with value "ARRAY(0x2d9fda8)"
Params: $VAR1 = undef;
127.0.0.1 - - [22/Nov/2016:15:10:32 -0800] "GET /items?s=s1&s=s2 HTTP/1.1" 200 1 "-" "curl/7.47.1"
curl 'http://localhost:5000/items?a=a1'
WARN 2016-11-22T15:11:21.090 Param `a` didn't pass constraint `__ANON__` with value "a1"
INFO 2016-11-22T15:11:21.090 `s` optional and empty
Params: $VAR1 = undef;
127.0.0.1 - - [22/Nov/2016:15:11:21 -0800] "GET /items?a=a1 HTTP/1.1" 200 1 "-" "curl/7.47.1"
curl 'http://localhost:5000/items?a=a1&a=a2'
INFO 2016-11-22T15:11:51.596 `s` optional and empty
Params: $VAR1 = {
'a' => [
'a1',
'a2'
]
};
127.0.0.1 - - [22/Nov/2016:15:11:51 -0800] "GET /items?a=a1&a=a2 HTTP/1.1" 200 1 "-" "curl/7.47.1"
Maybe I'm misunderstanding how these parameters should be described?
--pryankster
The darth.pl example explodes when one tries to post a new user (via the swagger sandbox).
The script seems to be missing a definition for a max() function.
Adding
use List::Util qw(max);
e.g. after line 12 seems to fix it.
I'm using
allons:~ USERNAME$ perl -v
This is perl 5, version 18, subversion 4 (v5.18.4) built for darwin-2level
on a mac running OS X 10.9.5.
I set up my test environment via
allons:~ USERNAME$ cpanm -l ~/tmp/raisin-experiment Raisin
and ran the demo like this:
allons:~ USERNAME$ env PERL5LIB=/Users/USERNAME/tmp/raisin-experiment/lib/perl5 PATH=/Users/USERNAME/tmp/raisin-experiment/bin:$PATH plackup ~/.cpanm/latest-build/Raisin-0.58/examples/pod-synopsis-app/darth.pl
g.
Just a quick suggestion.
In the pod for HOOKS in Raisin.pm, its not mentioned what parameters are passed in.
Thats all :)
Parameters captured by route_param
can't contain dots.
For example, route '/:name'
params requires => { name => "name", type => Str };
route_param 'name' => sub {
get sub {
[ "hello" => shift->{name} ]
}
}
This is ok
$ curl 'http://0:5000/username'
["hello" : "username"]
but this isn't
$ curl 'http://0:5000/user.name'
Nothing found
Hi! I tried to add some cpanparty integration tests for Raisin, based on documentation page, but it seems POST /users route broken.
http://swatpm.org/cpanparty/#swat-raisin
Regards
Alexey
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.