3 # This file is part of Koha.
5 # Koha is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 3 of the License, or
8 # (at your option) any later version.
10 # Koha is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Koha; if not, see <http://www.gnu.org/licenses>.
20 use Koha::Acquisition::Orders;
25 # Dummy app for testing the plugin
26 use Mojolicious::Lite;
28 app->log->level('error');
30 plugin 'Koha::REST::Plugin::Objects';
31 plugin 'Koha::REST::Plugin::Query';
32 plugin 'Koha::REST::Plugin::Pagination';
34 get '/cities' => sub {
36 $c->validation->output($c->req->params->to_hash);
37 my $cities = $c->objects->search(Koha::Cities->new);
38 $c->render( status => 200, json => $cities );
41 get '/orders' => sub {
43 $c->stash('koha.embed', ( { fund => {} } ) );
44 $c->validation->output($c->req->params->to_hash);
45 my $orders = $c->objects->search(Koha::Acquisition::Orders->new);
46 $c->render( status => 200, json => $orders );
49 get '/patrons/:patron_id/holds' => sub {
51 my $params = $c->req->params->to_hash;
52 $params->{patron_id} = $c->stash("patron_id");
53 $c->validation->output($params);
54 my $holds_set = Koha::Holds->new;
55 my $holds = $c->objects->search( $holds_set );
56 $c->render( status => 200, json => {count => scalar(@$holds)} );
59 get '/biblios' => sub {
61 my $output = $c->req->params->to_hash;
62 $output->{query} = $c->req->json if defined $c->req->json;
63 my $headers = $c->req->headers->to_hash;
64 $output->{'x-koha-query'} = $headers->{'x-koha-query'} if defined $headers->{'x-koha-query'};
65 $c->validation->output($output);
66 my $biblios_set = Koha::Biblios->new;
67 $c->stash("koha.embed", {
74 my $biblios = $c->objects->search($biblios_set);
75 $c->render( status => 200, json => {count => scalar(@$biblios), biblios => $biblios} );
80 use Test::More tests => 10;
83 use t::lib::TestBuilder;
86 my $t = Test::Mojo->new;
88 my $schema = Koha::Database->new()->schema();
89 my $builder = t::lib::TestBuilder->new;
91 subtest 'objects.search helper' => sub {
95 $schema->storage->txn_begin;
97 # Remove existing cities to have more control on the search results
100 # Create three sample cities that match the query. This makes sure we
101 # always have a "next" link regardless of Mojolicious::Plugin::OpenAPI version.
102 $builder->build_object({
103 class => 'Koha::Cities',
105 city_name => 'Manuel'
108 $builder->build_object({
109 class => 'Koha::Cities',
111 city_name => 'Manuela'
114 $builder->build_object({
115 class => 'Koha::Cities',
117 city_name => 'Manuelab'
121 $t->get_ok('/cities?name=manuel&_per_page=1&_page=1')
123 ->header_like( 'Link' => qr/<http:\/\/.*[\?&]_page=2.*>; rel="next",/ )
126 ->json_is('/0/name' => 'Manuel');
128 $builder->build_object({
129 class => 'Koha::Cities',
131 city_name => 'Emanuel'
136 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=starts_with')
142 ->json_is('/0/name' => 'Manuel')
143 ->json_is('/1/name' => 'Manuela')
144 ->json_is('/2/name' => 'Manuelab');
147 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=ends_with')
152 ->json_is('/0/name' => 'Manuel')
153 ->json_is('/1/name' => 'Emanuel');
156 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=exact')
160 ->json_is('/0/name' => 'Manuel');
163 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=contains')
170 ->json_is('/0/name' => 'Manuel')
171 ->json_is('/1/name' => 'Manuela')
172 ->json_is('/2/name' => 'Manuelab')
173 ->json_is('/3/name' => 'Emanuel');
175 $schema->storage->txn_rollback;
178 subtest 'objects.search helper, sorting on mapped column' => sub {
182 $schema->storage->txn_begin;
184 # Have complete control over the existing cities to ease testing
185 Koha::Cities->delete;
187 $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'A', city_country => 'Argentina' } });
188 $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'B', city_country => 'Argentina' } });
190 $t->get_ok('/cities?_order_by=%2Bname&_order_by=+country')
195 ->json_is('/0/name' => 'A')
196 ->json_is('/1/name' => 'B');
198 $t->get_ok('/cities?_order_by=-name')
203 ->json_is('/0/name' => 'B')
204 ->json_is('/1/name' => 'A');
206 $schema->storage->txn_rollback;
209 subtest 'objects.search helper, encoding' => sub {
213 $schema->storage->txn_begin;
215 Koha::Cities->delete;
217 $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'A', city_country => 'Argentina' } });
218 $builder->build_object({ class => 'Koha::Cities', value => { city_name => 'B', city_country => '❤Argentina❤' } });
220 $t->get_ok('/cities?q={"country": "❤Argentina❤"}')
224 ->json_is('/0/name' => 'B');
226 $schema->storage->txn_rollback;
229 subtest 'objects.search helper, embed' => sub {
233 $schema->storage->txn_begin;
235 my $order = $builder->build_object({ class => 'Koha::Acquisition::Orders' });
237 $t->get_ok('/orders?order_id=' . $order->ordernumber)
238 ->json_is('/0',$order->to_api({ embed => ( { fund => {} } ) }));
240 $schema->storage->txn_rollback;
243 subtest 'objects.search helper, with path parameters and _match' => sub {
246 $schema->storage->txn_begin;
248 Koha::Holds->search()->delete;
250 my $patron = Koha::Patrons->find(10);
251 $patron->delete if $patron;
252 $patron = $builder->build_object( { class => "Koha::Patrons" } );
253 $patron->borrowernumber(10)->store;
254 $builder->build_object(
256 class => "Koha::Holds",
257 value => { borrowernumber => $patron->borrowernumber }
261 $t->get_ok('/patrons/1/holds?_match=exact')
262 ->json_is('/count' => 0, 'there should be no holds for borrower 1 with _match=exact');
264 $t->get_ok('/patrons/1/holds?_match=contains')
265 ->json_is('/count' => 0, 'there should be no holds for borrower 1 with _match=contains');
267 $t->get_ok('/patrons/10/holds?_match=exact')
268 ->json_is('/count' => 1, 'there should be 1 hold for borrower 10 with _match=exact');
270 $t->get_ok('/patrons/10/holds?_match=contains')
271 ->json_is('/count' => 1, 'there should be 1 hold for borrower 10 with _match=contains');
273 $schema->storage->txn_rollback;
276 subtest 'object.search helper with query parameter' => sub {
279 $schema->storage->txn_begin;
281 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
282 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
283 my $biblio1 = $builder->build_sample_biblio;
284 my $biblio2 = $builder->build_sample_biblio;
285 my $biblio3 = $builder->build_sample_biblio;
286 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
287 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
288 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
290 $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron1->borrowernumber })
291 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
293 $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron2->borrowernumber })
294 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
296 $schema->storage->txn_rollback;
299 subtest 'object.search helper with q parameter' => sub {
302 $schema->storage->txn_begin;
304 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
305 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
306 my $biblio1 = $builder->build_sample_biblio;
307 my $biblio2 = $builder->build_sample_biblio;
308 my $biblio3 = $builder->build_sample_biblio;
309 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
310 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
311 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
313 $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}')
314 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
316 $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}')
317 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
319 $schema->storage->txn_rollback;
322 subtest 'object.search helper with x-koha-query header' => sub {
325 $schema->storage->txn_begin;
327 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
328 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
329 my $biblio1 = $builder->build_sample_biblio;
330 my $biblio2 = $builder->build_sample_biblio;
331 my $biblio3 = $builder->build_sample_biblio;
332 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
333 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
334 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
336 $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'})
337 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
339 $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'})
340 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
342 $schema->storage->txn_rollback;
345 subtest 'object.search helper with all query methods' => sub {
348 $schema->storage->txn_begin;
350 my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron1'} } );
351 my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron2'} } );
352 my $biblio1 = $builder->build_sample_biblio;
353 my $biblio2 = $builder->build_sample_biblio;
354 my $biblio3 = $builder->build_sample_biblio;
355 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
356 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
357 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
359 $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron1->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron1->cardnumber})
360 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
362 $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron2->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron2->cardnumber})
363 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
365 $t->get_ok('/biblios?q={"suggestions.suggester.firstname": "'.$patron1->firstname.'"}' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'} => json => {"suggestions.suggester.cardnumber" => $patron2->cardnumber})
366 ->json_is('/count' => 0, 'there shouldn\'t be biblios where suggester has patron1 fistname and patron2 id');
368 $schema->storage->txn_rollback;
371 subtest 'object.search helper order by embedded columns' => sub {
374 my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron1'} } );
375 my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value => {firstname=>'patron2'} } );
376 my $biblio1 = $builder->build_sample_biblio;
377 my $biblio2 = $builder->build_sample_biblio;
378 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
379 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
381 $t->get_ok('/biblios?_order_by=-suggestions.suggester.firstname' => json => [{"me.biblio_id" => $biblio1->biblionumber}, {"me.biblio_id" => $biblio2->biblionumber}])
382 ->json_is('/biblios/0/biblio_id' => $biblio2->biblionumber, 'Biblio 2 should be first')
383 ->json_is('/biblios/1/biblio_id' => $biblio1->biblionumber, 'Biblio 1 should be second');
385 $schema->storage->txn_begin;