GithubHelp home page GithubHelp logo

raisin's Issues

List::Util dependency

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?

Nested route_param loses params from parent route_param

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.

Trying to use MooseX:Types or Moose::Util::TypeConstraints fails

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"

Proposal for a new hook

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.

app->log() broken

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?

Optional params that fail type constraints do not return "Invalid Parameters" response.

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.

Can't bump user in sample-app via swagger.

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.

Raisin::Logger documentation contradicts itself

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.

ArrayRef[HashRef] param

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!

Undeclared dependency JSON.pm

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) ...

Need to 'use Raisin::Types' somewhere

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..

Examples of using Raisins built in logging features

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.

Missing Dumper in Raisin/Plugin/Logger.pm

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.

Round trip: Raisin -> Swagger -> codegen Perl Client

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?

APIDocs - basePath broken?

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.

accurate parsing of accept header

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

Bug in Exporter call

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.

access to json serializer?

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()

Problem with Content-Type

Hello,
The function Raisin::Util::detect_serializer does not support this value of Content-Type:
application/json; charset=UTF-8

Checking an OAuth2 bearer token

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?

Swagger file incomplete and with errors

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:

  • Error Message "required: &2 !!perl/scalar:JSON::PP::Boolean 1" in line 60
  • Missing definition of Album
  • Strange trailing hex codes in the object names. (why are they necessary? Names should be unique anyway)
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.

route_param regression from 0.86 -> 0.87

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)

Undeclared dependency Test::Exception

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 

Serialize JSON

Ist there an easier way to return Perl objects (blessed or Moose) than to

  • change the lib/Raisin/Encoder/JSON.pm to accomodate allow_blessed->convert_blessed->en/decode
  • implement or inherit a TO_JSON serializing function in each object to be returned.

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.

'Unexpected error' In the responses/default/descritption of swagger

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!

Boolean and JSON

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.

Coercion of Type::Tiny types

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

failed test t/unit/middleware/formatter.t

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.

null in body params is not the same as undef

Thanks for this great and simple descriptive API!

I am thinking that the following code:

foreach my $p (@$declared) {
my $name = $p->name;
my $value = $params{$name};
if (not $p->validate(\$value)) {
$p->required ? return : next;
}
$value //= $p->default if defined $p->default;
next if not defined($value);
$self->{'raisin.declared_params'}{$name} = $value;
}

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.

APIDocs - description editable pls

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.

docs/examples use different resource-notation

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.

Canonical way to read in config file?

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)

Missing SecurityDefinitionsObject on Raisin::Plugin::Swagger

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

Inconsistent behavior of present when no rows are found

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

Disable formatters

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.

Raisin in 5.10

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.

"using" in an entity without declaring an OpenAPI array

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

How to return 404?

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

Example shows '/users should be 'user'

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

Tags created with <null> description in swagger file

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:

  • name: ':mac'
    description: null
    `

Which the Swagger Editor marks as error.

utf8-encoding json-output twice?

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.

Swagger Schema Expected Response and Actual Response Do Not Match

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)); }

Screen Shot 2019-08-01 at 3 18 56 PM

Buggy log, variables not filled in

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.

multiple values for same paramter / ArrayRef[Str] parameters?

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 can't post new users, missing max().

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.

Pod: Hooks

Just a quick suggestion.

In the pod for HOOKS in Raisin.pm, its not mentioned what parameters are passed in.

Thats all :)

route_param and dot in parameter

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.