From: Alexander Hartmaier Date: Mon, 25 Aug 2014 16:16:44 +0000 (+0200) Subject: add support for the -and, -not and -or operators (RT93864) X-Git-Tag: 2.006001~1 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Controller-DBIC-API.git;a=commitdiff_plain;h=4cb8623abade7b9f100b3892df93ddcfb1168e01 add support for the -and, -not and -or operators (RT93864) --- diff --git a/Changes b/Changes index 261ffd1..9c6293e 100644 --- a/Changes +++ b/Changes @@ -2,6 +2,7 @@ Revision history for Catalyst-Controller-DBIC-API: {{ $dist->version }} {{ $NEXT }} - Fix links to ::Request::Context + - Add support for the -and, -not and -or operators (RT93864) 2.005001 2014-01-13 21:43:59+01:00 Europe/Vienna - Fixed test failures with JSON 2.90 (RT#90188, thanks Samuel Kaufman!) diff --git a/lib/Catalyst/Controller/DBIC/API.pm b/lib/Catalyst/Controller/DBIC/API.pm index bfdfd76..f90891a 100644 --- a/lib/Catalyst/Controller/DBIC/API.pm +++ b/lib/Catalyst/Controller/DBIC/API.pm @@ -1324,12 +1324,15 @@ The validator is set in "loose" mode meaning only one path is required to match. For more information, please see L and more specifically L. -Since 2.00100: +Since 2.001: Transactions are used. The stash is put aside in favor of roles applied to the request object with additional accessors. Error handling is now much more consistent with most errors immediately detaching. The internals are much easier to read and understand with lots more documentation. +Since 2.006: +The SQL::Abstract -and, -not and -or operators are supported. + =cut 1; diff --git a/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm b/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm index 107dc80..bbc13b1 100644 --- a/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm +++ b/lib/Catalyst/Controller/DBIC/API/RequestArguments.pm @@ -443,16 +443,20 @@ JoinBuilder each layer of recursion. $base ||= 'me'; my $search_params = {}; + # return non-hashref params unaltered + return $param + unless ref $param eq 'HASH'; + # build up condition foreach my $column ( keys %$param ) { + my $value = $param->{$column}; if ( $source->has_relationship($column) ) { # check if the value isn't a hashref - unless ( ref( $param->{$column} ) - && reftype( $param->{$column} ) eq 'HASH' ) + unless ( ref $value eq 'HASH' ) { $search_params->{ join( '.', $base, $column ) } = - $param->{$column}; + $value; next; } @@ -460,7 +464,7 @@ JoinBuilder each layer of recursion. %$search_params, %{ $self->generate_column_parameters( $source->related_source($column), - $param->{$column}, + $value, Catalyst::Controller::DBIC::API::JoinBuilder->new( parent => $join, name => $column @@ -474,19 +478,41 @@ JoinBuilder each layer of recursion. $search_params->{ join( '.', $base, $column ) } = $param->{$column}; } + elsif ( $column eq '-or' || $column eq '-and' || $column eq '-not' ) { + # either an arrayref or hashref + if ( ref $value eq 'HASH' ) { + $search_params->{$column} = $self->generate_column_parameters( + $source, + $value, + $join, + $base, + ); + } + elsif ( ref $value eq 'ARRAY' ) { + push @{$search_params->{$column}}, + $self->generate_column_parameters( + $source, + $_, + $join, + $base, + ) + for @$value; + } + else { + die "unsupported value '$value' for column '$column'\n"; + } + } # might be a sql function instead of a column name # e.g. {colname => {like => '%foo%'}} else { # but only if it's not a hashref - unless ( ref( $param->{$column} ) - && reftype( $param->{$column} ) eq 'HASH' ) - { + unless ( ref $value eq 'HASH' ) { $search_params->{ join( '.', $base, $column ) } = $param->{$column}; } else { - die "$column is neither a relationship nor a column\n"; + die "unsupported value '$value' for column '$column'\n"; } } } diff --git a/t/rest/list.t b/t/rest/list.t index a7a6eb6..b63cc98 100644 --- a/t/rest/list.t +++ b/t/rest/list.t @@ -12,6 +12,7 @@ use Test::More; use Test::WWW::Mechanize::Catalyst 'RestTest'; use HTTP::Request::Common; use JSON; +use Data::Printer; my $json = JSON->new->utf8; @@ -210,6 +211,128 @@ my $track_list_url = "$base/api/rest/track"; ); } +# -and|-or condition +{ + my @variants = ( + # -or + { + search => { + title => [qw(Yowlin Howlin)], + }, + }, + { + search => { + -or => [ + title => [qw(Yowlin Howlin)], + ], + }, + }, + { + search => { + -or => [ + title => [qw(Yowlin)], + title => [qw(Howlin)], + ], + }, + }, + { + search => { + -or => [ + { title => [qw(Yowlin)] }, + { title => [qw(Howlin)] }, + ], + }, + }, + # -and + { + search => { + cd => 2, + position => [1, 2], + }, + }, + { + search => { + -and => [ + cd => 2, + position => [1, 2], + ], + }, + }, + # -and & -or + { + search => { + -or => [ + -and => [ + cd => 2, + position => [0, 1], + ], + -and => [ + cd => 2, + position => [0, 2], + ], + ], + }, + }, + { + search => { + -or => [ + { + -and => [ + cd => 2, + position => [0, 1], + ], + }, + { + -and => [ + cd => 2, + position => [0, 2], + ], + }, + ], + }, + }, + { + search => { + -or => [ + { + -and => [ + cd => 2, + position => [0, 1], + ], + }, + { + -and => [ + cd => 2, + position => [0, 2], + ], + }, + ], + }, + }, + ); + + for my $case ( @variants ) { + is $schema->resultset('Track')->search($case->{search})->count, 2, 'check -and|-or search param correctness'; + + my $uri = URI->new($track_list_url); + $uri->query_form( map { $_ => encode_json($case->{$_}) } keys %$case ); + my $req = GET( $uri, 'Accept' => 'text/x-json' ); + $mech->request($req); + cmp_ok( $mech->status, '==', 200, 'attempt with -or search okay' ); + my $response = $json->decode( $mech->content ); + my @expected_response = map { + { $_->get_columns } + } $schema->resultset('Track')->search($case->{search})->all; + is_deeply( + $response, + # track does set use_json_boolean + { list => \@expected_response, success => JSON::true, totalcount => 2 }, + 'correct data returned for -and|-or search param' + ) + or diag p($case) . p($response); + } +} + { my $uri = URI->new($artist_list_url); $uri->query_form( { 'search.cds.track.title' => 'Suicidal' } ); @@ -218,9 +341,9 @@ my $track_list_url = "$base/api/rest/track"; cmp_ok( $mech->status, '==', 400, 'attempt with nonexisting relationship fails' ); my $response = $json->decode( $mech->content ); - is_deeply( - $response->{messages}, - ['track is neither a relationship nor a column'], + like( + $response->{messages}->[0], + qr/unsupported value 'HASH\([^\)]+\)' for column 'track'/, 'correct error message returned' ); }