View | Details | Raw Unified | Return to bug 19410
Collapse All | Expand All

(-)a/Koha/Objects.pm (-41 lines)
Lines 171-217 sub search_related { Link Here
171
    }
171
    }
172
}
172
}
173
173
174
=head2 _build_query_params_from_api
175
176
    my $params = _build_query_params_from_api( $filtered_params, $reserved_params );
177
178
Builds the params for searching on DBIC based on the selected matching algorithm.
179
Valid options are I<contains>, I<starts_with>, I<ends_with> and I<exact>. Default is
180
I<contains>. If other value is passed, a Koha::Exceptions::WrongParameter exception
181
is raised.
182
183
=cut
184
185
sub _build_query_params_from_api {
186
187
    my ( $filtered_params, $reserved_params ) = @_;
188
189
    my $params;
190
    my $match = $reserved_params->{_match} // 'contains';
191
192
    foreach my $param ( keys %{$filtered_params} ) {
193
        if ( $match eq 'contains' ) {
194
            $params->{$param} =
195
              { like => '%' . $filtered_params->{$param} . '%' };
196
        }
197
        elsif ( $match eq 'starts_with' ) {
198
            $params->{$param} = { like => $filtered_params->{$param} . '%' };
199
        }
200
        elsif ( $match eq 'ends_with' ) {
201
            $params->{$param} = { like => '%' . $filtered_params->{$param} };
202
        }
203
        elsif ( $match eq 'exact' ) {
204
            $params->{$param} = $filtered_params->{$param};
205
        }
206
        else {
207
            Koha::Exceptions::WrongParameter->throw(
208
                "Invalid value for _match param ($match)");
209
        }
210
    }
211
212
    return $params;
213
}
214
215
=head3 single
174
=head3 single
216
175
217
my $object = Koha::Objects->search({}, { rows => 1 })->single
176
my $object = Koha::Objects->search({}, { rows => 1 })->single
(-)a/Koha/REST/Plugin/Objects.pm (+1 lines)
Lines 67-72 sub register { Link Here
67
                }
67
                }
68
            );
68
            );
69
69
70
            $filtered_params = $c->build_query_params( $filtered_params, $reserved_params );
70
            # Perform search
71
            # Perform search
71
            my $objects = $objects_set->search( $filtered_params, $attributes );
72
            my $objects = $objects_set->search( $filtered_params, $attributes );
72
73
(-)a/Koha/REST/Plugin/Query.pm (+47 lines)
Lines 19-24 use Modern::Perl; Link Here
19
19
20
use Mojo::Base 'Mojolicious::Plugin';
20
use Mojo::Base 'Mojolicious::Plugin';
21
21
22
use Koha::Exceptions;
23
22
=head1 NAME
24
=head1 NAME
23
25
24
Koha::REST::Plugin::Query
26
Koha::REST::Plugin::Query
Lines 88-93 Generates the DBIC order_by attributes based on I<$params>, and merges into I<$a Link Here
88
            return $attributes;
90
            return $attributes;
89
        }
91
        }
90
    );
92
    );
93
94
=head3 _build_query_params_from_api
95
96
    my $params = _build_query_params_from_api( $filtered_params, $reserved_params );
97
98
Builds the params for searching on DBIC based on the selected matching algorithm.
99
Valid options are I<contains>, I<starts_with>, I<ends_with> and I<exact>. Default is
100
I<contains>. If other value is passed, a Koha::Exceptions::WrongParameter exception
101
is raised.
102
103
=cut
104
105
    $app->helper(
106
        'build_query_params' => sub {
107
108
            my ( $c, $filtered_params, $reserved_params ) = @_;
109
110
            my $params;
111
            my $match = $reserved_params->{_match} // 'contains';
112
113
            foreach my $param ( keys %{$filtered_params} ) {
114
                if ( $match eq 'contains' ) {
115
                    $params->{$param} =
116
                      { like => '%' . $filtered_params->{$param} . '%' };
117
                }
118
                elsif ( $match eq 'starts_with' ) {
119
                    $params->{$param} = { like => $filtered_params->{$param} . '%' };
120
                }
121
                elsif ( $match eq 'ends_with' ) {
122
                    $params->{$param} = { like => '%' . $filtered_params->{$param} };
123
                }
124
                elsif ( $match eq 'exact' ) {
125
                    $params->{$param} = $filtered_params->{$param};
126
                }
127
                else {
128
                    # We should never reach here, because the OpenAPI plugin should
129
                    # prevent invalid params to be passed
130
                    Koha::Exceptions::WrongParameter->throw(
131
                        "Invalid value for _match param ($match)");
132
                }
133
            }
134
135
            return $params;
136
        }
137
    );
91
}
138
}
92
139
93
=head2 Internal methods
140
=head2 Internal methods
(-)a/t/Koha/REST/Plugin/Query.t (-1 / +56 lines)
Lines 19-24 use Modern::Perl; Link Here
19
19
20
# Dummy app for testing the plugin
20
# Dummy app for testing the plugin
21
use Mojolicious::Lite;
21
use Mojolicious::Lite;
22
use Try::Tiny;
22
23
23
app->log->level('error');
24
app->log->level('error');
24
25
Lines 79-87 get '/dbic_merge_sorting' => sub { Link Here
79
    $c->render( json => $attributes, status => 200 );
80
    $c->render( json => $attributes, status => 200 );
80
};
81
};
81
82
83
get '/build_query' => sub {
84
    my $c = shift;
85
    my ( $filtered_params, $reserved_params ) =
86
      $c->extract_reserved_params( $c->req->params->to_hash );
87
    my $query;
88
    try {
89
        $query = $c->build_query_params( $filtered_params, $reserved_params );
90
        $c->render( json => { query => $query }, status => 200 );
91
    }
92
    catch {
93
        $c->render(
94
            json => { exception_msg => $_->message, exception_type => ref($_) },
95
            status => 400
96
        );
97
    };
98
};
99
82
# The tests
100
# The tests
83
101
84
use Test::More tests => 2;
102
use Test::More tests => 3;
85
use Test::Mojo;
103
use Test::Mojo;
86
104
87
subtest 'extract_reserved_params() tests' => sub {
105
subtest 'extract_reserved_params() tests' => sub {
Lines 123-125 subtest 'dbic_merge_sorting() tests' => sub { Link Here
123
      ->json_is( '/b' => 'b', 'Existing values are kept (b)' )
141
      ->json_is( '/b' => 'b', 'Existing values are kept (b)' )
124
      ->json_is( '/order_by' => [ 'uno', { -desc => 'dos' }, { -asc => 'tres' } ] );
142
      ->json_is( '/order_by' => [ 'uno', { -desc => 'dos' }, { -asc => 'tres' } ] );
125
};
143
};
144
145
subtest '_build_query_params_from_api' => sub {
146
147
    plan tests => 16;
148
149
    my $t = Test::Mojo->new;
150
151
    # _match => contains
152
    $t->get_ok('/build_query?_match=contains&title=Ender&author=Orson')
153
      ->status_is(200)
154
      ->json_is( '/query' =>
155
          { author => { like => '%Orson%' }, title => { like => '%Ender%' } } );
156
157
    # _match => starts_with
158
    $t->get_ok('/build_query?_match=starts_with&title=Ender&author=Orson')
159
      ->status_is(200)
160
      ->json_is( '/query' =>
161
          { author => { like => 'Orson%' }, title => { like => 'Ender%' } } );
162
163
    # _match => ends_with
164
    $t->get_ok('/build_query?_match=ends_with&title=Ender&author=Orson')
165
      ->status_is(200)
166
      ->json_is( '/query' =>
167
          { author => { like => '%Orson' }, title => { like => '%Ender' } } );
168
169
    # _match => exact
170
    $t->get_ok('/build_query?_match=exact&title=Ender&author=Orson')
171
      ->status_is(200)
172
      ->json_is( '/query' => { author => 'Orson', title => 'Ender' } );
173
174
    # _match => blah
175
    $t->get_ok('/build_query?_match=blah&title=Ender&author=Orson')
176
      ->status_is(400)
177
      ->json_is( '/exception_msg'  => 'Invalid value for _match param (blah)' )
178
      ->json_is( '/exception_type' => 'Koha::Exceptions::WrongParameter' );
179
180
};
(-)a/t/db_dependent/Koha/Objects.t (-64 / +1 lines)
Lines 19-25 Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Test::More tests => 16;
22
use Test::More tests => 15;
23
use Test::Exception;
23
use Test::Exception;
24
use Test::Warn;
24
use Test::Warn;
25
25
Lines 239-304 subtest '->is_paged and ->pager tests' => sub { Link Here
239
239
240
    $schema->storage->txn_rollback;
240
    $schema->storage->txn_rollback;
241
};
241
};
242
243
subtest '_build_query_params_from_api' => sub {
244
245
    plan tests => 5;
246
247
    my $filtered_params = {
248
        title  => "Ender",
249
        author => "Orson"
250
    };
251
252
    subtest '_match => contains' => sub {
253
        plan tests => 2;
254
        my $reserved_params = { _match => 'contains' };
255
        my $params = Koha::Objects::_build_query_params_from_api( $filtered_params, $reserved_params );
256
257
        is_deeply( $params->{author}, { like => '%Orson%' } );
258
        is_deeply( $params->{title}, { like => '%Ender%' } );
259
    };
260
261
    subtest '_match => starts_with' => sub {
262
        plan tests => 2;
263
        my $reserved_params = { _match => 'starts_with' };
264
        my $params = Koha::Objects::_build_query_params_from_api( $filtered_params, $reserved_params );
265
266
        is_deeply( $params->{author}, { like => 'Orson%' } );
267
        is_deeply( $params->{title}, { like => 'Ender%' } );
268
    };
269
270
    subtest '_match => ends_with' => sub {
271
        plan tests => 2;
272
        my $reserved_params = { _match => 'ends_with' };
273
        my $params = Koha::Objects::_build_query_params_from_api( $filtered_params, $reserved_params );
274
275
        is_deeply( $params->{author}, { like => '%Orson' } );
276
        is_deeply( $params->{title}, { like => '%Ender' } );
277
    };
278
279
    subtest '_match => exact' => sub {
280
        plan tests => 2;
281
        my $reserved_params = { _match => 'exact' };
282
        my $params = Koha::Objects::_build_query_params_from_api( $filtered_params, $reserved_params );
283
284
        is( $params->{author}, 'Orson' );
285
        is( $params->{title},  'Ender' );
286
    };
287
288
    subtest '_match => blah' => sub {
289
        plan tests => 2;
290
291
        my $reserved_params = { _match => 'blah' };
292
        throws_ok {
293
            Koha::Objects::_build_query_params_from_api( $filtered_params,
294
                $reserved_params );
295
        }
296
        'Koha::Exceptions::WrongParameter',
297
          'Exception thrown on invalid _match parameter';
298
299
        is( $@,
300
            'Invalid value for _match param (blah)',
301
            'Exception carries the right message'
302
        );
303
    };
304
};
(-)a/t/db_dependent/Koha/REST/Plugin/Objects.t (-20 / +67 lines)
Lines 31-42 get '/patrons' => sub { Link Here
31
    my $c = shift;
31
    my $c = shift;
32
    $c->validation->output($c->req->params->to_hash);
32
    $c->validation->output($c->req->params->to_hash);
33
    my $patrons = $c->objects->search(Koha::Patrons->new);
33
    my $patrons = $c->objects->search(Koha::Patrons->new);
34
    $c->render( status => 200, json => $patrons->TO_JSON );
34
    $c->render( status => 200, json => $patrons );
35
};
35
};
36
36
37
37
38
# The tests
38
# The tests
39
40
use Test::More tests => 1;
39
use Test::More tests => 1;
41
use Test::Mojo;
40
use Test::Mojo;
42
41
Lines 44-77 use t::lib::TestBuilder; Link Here
44
use Koha::Database;
43
use Koha::Database;
45
44
46
my $schema = Koha::Database->new()->schema();
45
my $schema = Koha::Database->new()->schema();
47
$schema->storage->txn_begin();
46
48
47
49
my $builder = t::lib::TestBuilder->new;
48
my $builder = t::lib::TestBuilder->new;
50
$builder->build({
51
    source => 'Borrower',
52
    value => {
53
        firstname => 'Manuel',
54
    },
55
});
56
$builder->build({
57
    source => 'Borrower',
58
    value => {
59
        firstname => 'Manuel',
60
    },
61
});
62
49
63
subtest 'objects.search helper' => sub {
50
subtest 'objects.search helper' => sub {
64
51
65
    plan tests => 6;
52
    plan tests => 34;
66
53
67
    my $t = Test::Mojo->new;
54
    my $t = Test::Mojo->new;
68
55
69
    $t->get_ok('/patrons?firstname=Manuel&_per_page=1&_page=1')
56
    $schema->storage->txn_begin;
57
58
    # Delete existing patrons
59
    Koha::Patrons->search->delete;
60
    # Create two sample patrons that match the query
61
    $builder->build_object({
62
        class => 'Koha::Patrons',
63
        value => {
64
            firstname => 'Manuel'
65
        }
66
    });
67
    $builder->build_object({
68
        class => 'Koha::Patrons',
69
        value => {
70
            firstname => 'Manuela'
71
        }
72
    });
73
74
    $t->get_ok('/patrons?firstname=manuel&_per_page=1&_page=1')
70
        ->status_is(200)
75
        ->status_is(200)
71
        ->header_like( 'Link' => qr/<http:\/\/.*\?.*&_page=2.*>; rel="next",/ )
76
        ->header_like( 'Link' => qr/<http:\/\/.*\?.*&_page=2.*>; rel="next",/ )
72
        ->json_has('/0')
77
        ->json_has('/0')
73
        ->json_hasnt('/1')
78
        ->json_hasnt('/1')
74
        ->json_is('/0/firstname' => 'Manuel');
79
        ->json_is('/0/firstname' => 'Manuel');
75
};
76
80
77
$schema->storage->txn_rollback();
81
    $builder->build_object({
82
        class => 'Koha::Patrons',
83
        value => {
84
            firstname => 'Emanuel'
85
        }
86
    });
87
88
    # _match=starts_with
89
    $t->get_ok('/patrons?firstname=manuel&_per_page=3&_page=1&_match=starts_with')
90
        ->status_is(200)
91
        ->json_has('/0')
92
        ->json_has('/1')
93
        ->json_hasnt('/2')
94
        ->json_is('/0/firstname' => 'Manuel')
95
        ->json_is('/1/firstname' => 'Manuela');
96
97
    # _match=ends_with
98
    $t->get_ok('/patrons?firstname=manuel&_per_page=3&_page=1&_match=ends_with')
99
        ->status_is(200)
100
        ->json_has('/0')
101
        ->json_has('/1')
102
        ->json_hasnt('/2')
103
        ->json_is('/0/firstname' => 'Manuel')
104
        ->json_is('/1/firstname' => 'Emanuel');
105
106
    # _match=exact
107
    $t->get_ok('/patrons?firstname=manuel&_per_page=3&_page=1&_match=exact')
108
        ->status_is(200)
109
        ->json_has('/0')
110
        ->json_hasnt('/1')
111
        ->json_is('/0/firstname' => 'Manuel');
112
113
    # _match=contains
114
    $t->get_ok('/patrons?firstname=manuel&_per_page=3&_page=1&_match=contains')
115
        ->status_is(200)
116
        ->json_has('/0')
117
        ->json_has('/1')
118
        ->json_has('/2')
119
        ->json_hasnt('/3')
120
        ->json_is('/0/firstname' => 'Manuel')
121
        ->json_is('/1/firstname' => 'Manuela')
122
        ->json_is('/2/firstname' => 'Emanuel');
123
124
    $schema->storage->txn_rollback;
125
};
78
- 

Return to bug 19410