Raisin icon indicating copy to clipboard operation
Raisin copied to clipboard

route_param regression from 0.86 -> 0.87

Open djzort opened this issue 4 years ago • 9 comments

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)

djzort avatar Oct 08 '19 06:10 djzort

I'll take a look ASAP. Thanks for reporting.

khrt avatar Oct 08 '19 09:10 khrt

Maybe I misunderstand how route_param is supposed to be used, but aren't you supposed to pair route_param with a params() call describing the route param like this?

params( requires('locale', type => Locale, desc => '...') )
route_param locale => sub {
   ...
};

mschout avatar Oct 09 '19 12:10 mschout

To me, its not clear how route_params can be mixed with query params... but saying that the above code works as described in 0.86 and doesnt in 0.87. But strangely, it routes correctly in 0.87 but doesnt populate the $_[0] with a hashref of parameters.

If i have misinterpreted the documentation and somehow relied upon non-intentional behaviour i am happy to change my code. But for now i have Raisin pinned to 0.86 in my cpanfile

djzort avatar Oct 10 '19 01:10 djzort

Part of the problem is that nested route_param was broken badly in 0.86 and prior, in that the inner route_param would blow away all param definitions of previous route_param declarations.

This was fixed in 0.87.

I suspect this is what you need for 0.87:

resource 'etd' => sub {
    summary 'Get an estimate';
    desc 'Estimated Time of Delivery';
    params( requires( 'locale',  type => Locale, desc => 'Locale (AU/NZ)' ) );
    route_param 'locale' => sub {
        params( requires( 'postcode', type => PostCode, desc => 'Post Code' ) );
        route_param 'postcode' => sub {
           ...
            # query param style
            before_validation \&fix_material;
            params(
                requires( 'material', ... );
                optional( 'orderdate', ... );
            );
            summary 'GET one or more products via material code as query parameter';
            get \&guess_etd;

            # url path style
            params( requires( 'material', ... ) );
            route_param 'material' => sub {
                summary 'GET a single product via material code in url';
                params( optional( 'orderdate', ... ) ):
                get \&guess_etd;
            };    # route_param 'material'
        };    # route_param 'postcode'
    };    # route_param 'locale'
};    # resource 'etd'

And as a nice benefit you don't have to repeat the postcode and locale params for every internal endpoint.

But per the raisin docs, preceding the route_param with a params() call that describes the route param seems to be the recommended way to deal with route params.

mschout avatar Oct 10 '19 04:10 mschout

Ok this seems to work but has the annoying side effect that previously if the route_param value type didnt match the type it would return 400. Where as now it returns a 404. I am guessing this is due to failing to match causing it to not route past the handler code.

djzort avatar Nov 25 '19 05:11 djzort

That indeed is not fixed yet. It's on my roadmap.

khrt avatar Nov 27 '19 08:11 khrt

Just out of curiosity, because I did not find it anywhere, and before digging into the code, I would ask: How do you access the postcode and locale variables in guess_etd? From what I see while playing around with it is that the get sub's first param is only returning the params on the level it is defined...

matya avatar Oct 07 '20 14:10 matya

@matya,

that would be something like:

get sub {
        my $params = shift;
        $params->{postcode};
        $params->{locale};
        $params->{...};
}

Where $params is a hashref to all the parameters parsed by: https://github.com/khrt/Raisin/blob/5f77df30dcaa41b293b0037b1e64935462d1d30f/lib/Raisin/Request.pm#L12

The problem here must be that nested path arguments are not stored properly and/or got overwritten somewhere inside that function: https://github.com/khrt/Raisin/blob/5f77df30dcaa41b293b0037b1e64935462d1d30f/lib/Raisin/API.pm#L102

This would be the patch which introduced the issue: https://github.com/khrt/Raisin/commit/06d50a9511ec11ea82eb5de9e2820479cd5e36bb

khrt avatar Oct 07 '20 14:10 khrt

my bad, I messed up the params block. thanks! I have found some examples I was looking for in the test cases (which I didn't think of looking at).

matya avatar Oct 07 '20 15:10 matya