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;
84 use t
::lib
::TestBuilder
;
87 my $t = Test
::Mojo
->new;
89 my $schema = Koha
::Database
->new()->schema();
90 my $builder = t
::lib
::TestBuilder
->new;
92 subtest
'objects.search helper' => sub {
96 $schema->storage->txn_begin;
98 # Remove existing cities to have more control on the search results
101 # Create three sample cities that match the query. This makes sure we
102 # always have a "next" link regardless of Mojolicious::Plugin::OpenAPI version.
103 $builder->build_object({
104 class => 'Koha::Cities',
106 city_name
=> 'Manuel'
109 $builder->build_object({
110 class => 'Koha::Cities',
112 city_name
=> 'Manuela'
115 $builder->build_object({
116 class => 'Koha::Cities',
118 city_name
=> 'Manuelab'
122 $t->get_ok('/cities?name=manuel&_per_page=1&_page=1')
124 ->header_like( 'Link' => qr/<http:\/\
/.*[\?&]_page=2.*>; rel="next",/ )
127 ->json_is('/0/name' => 'Manuel');
129 $builder->build_object({
130 class => 'Koha::Cities',
132 city_name
=> 'Emanuel'
137 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=starts_with')
143 ->json_is('/0/name' => 'Manuel')
144 ->json_is('/1/name' => 'Manuela')
145 ->json_is('/2/name' => 'Manuelab');
148 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=ends_with')
153 ->json_is('/0/name' => 'Manuel')
154 ->json_is('/1/name' => 'Emanuel');
157 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=exact')
161 ->json_is('/0/name' => 'Manuel');
164 $t->get_ok('/cities?name=manuel&_per_page=4&_page=1&_match=contains')
171 ->json_is('/0/name' => 'Manuel')
172 ->json_is('/1/name' => 'Manuela')
173 ->json_is('/2/name' => 'Manuelab')
174 ->json_is('/3/name' => 'Emanuel');
178 $builder->build_object({ class => 'Koha::Cities' });
181 t
::lib
::Mocks
::mock_preference
('RESTdefaultPageSize', 20 );
182 $t->get_ok('/cities')
185 my $response_count = scalar @
{ $t->tx->res->json };
186 is
( $response_count, 20, 'RESTdefaultPageSize is honoured by default (20)' );
188 t
::lib
::Mocks
::mock_preference
('RESTdefaultPageSize', 5 );
189 $t->get_ok('/cities')
192 $response_count = scalar @
{ $t->tx->res->json };
193 is
( $response_count, 5, 'RESTdefaultPageSize is honoured by default (5)' );
195 $t->get_ok('/cities?_page=1&_per_page=-1')
198 $response_count = scalar @
{ $t->tx->res->json };
199 is
( $response_count, 24, '_per_page=-1 means all resources' );
201 $t->get_ok('/cities?_page=100&_per_page=-1')
204 $response_count = scalar @
{ $t->tx->res->json };
205 is
( $response_count, 24, 'When _per_page=-1 the page param is not considered' );
207 $schema->storage->txn_rollback;
210 subtest
'objects.search helper, sorting on mapped column' => sub {
214 $schema->storage->txn_begin;
216 # Have complete control over the existing cities to ease testing
217 Koha
::Cities
->delete;
219 $builder->build_object({ class => 'Koha::Cities', value
=> { city_name
=> 'A', city_country
=> 'Argentina' } });
220 $builder->build_object({ class => 'Koha::Cities', value
=> { city_name
=> 'B', city_country
=> 'Argentina' } });
222 $t->get_ok('/cities?_order_by=%2Bname&_order_by=+country')
227 ->json_is('/0/name' => 'A')
228 ->json_is('/1/name' => 'B');
230 $t->get_ok('/cities?_order_by=-name')
235 ->json_is('/0/name' => 'B')
236 ->json_is('/1/name' => 'A');
238 $schema->storage->txn_rollback;
241 subtest
'objects.search helper, encoding' => sub {
245 $schema->storage->txn_begin;
247 Koha
::Cities
->delete;
249 $builder->build_object({ class => 'Koha::Cities', value
=> { city_name
=> 'A', city_country
=> 'Argentina' } });
250 $builder->build_object({ class => 'Koha::Cities', value
=> { city_name
=> 'B', city_country
=> '❤Argentina❤' } });
252 $t->get_ok('/cities?q={"country": "❤Argentina❤"}')
256 ->json_is('/0/name' => 'B');
258 $schema->storage->txn_rollback;
261 subtest
'objects.search helper, embed' => sub {
265 $schema->storage->txn_begin;
267 my $order = $builder->build_object({ class => 'Koha::Acquisition::Orders' });
269 $t->get_ok('/orders?order_id=' . $order->ordernumber)
270 ->json_is('/0',$order->to_api({ embed
=> ( { fund
=> {} } ) }));
272 $schema->storage->txn_rollback;
275 subtest
'objects.search helper, with path parameters and _match' => sub {
278 $schema->storage->txn_begin;
280 Koha
::Holds
->search()->delete;
282 my $patron = Koha
::Patrons
->find(10);
283 $patron->delete if $patron;
284 $patron = $builder->build_object( { class => "Koha::Patrons" } );
285 $patron->borrowernumber(10)->store;
286 $builder->build_object(
288 class => "Koha::Holds",
289 value
=> { borrowernumber
=> $patron->borrowernumber }
293 $t->get_ok('/patrons/1/holds?_match=exact')
294 ->json_is('/count' => 0, 'there should be no holds for borrower 1 with _match=exact');
296 $t->get_ok('/patrons/1/holds?_match=contains')
297 ->json_is('/count' => 0, 'there should be no holds for borrower 1 with _match=contains');
299 $t->get_ok('/patrons/10/holds?_match=exact')
300 ->json_is('/count' => 1, 'there should be 1 hold for borrower 10 with _match=exact');
302 $t->get_ok('/patrons/10/holds?_match=contains')
303 ->json_is('/count' => 1, 'there should be 1 hold for borrower 10 with _match=contains');
305 $schema->storage->txn_rollback;
308 subtest
'object.search helper with query parameter' => sub {
311 $schema->storage->txn_begin;
313 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
314 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
315 my $biblio1 = $builder->build_sample_biblio;
316 my $biblio2 = $builder->build_sample_biblio;
317 my $biblio3 = $builder->build_sample_biblio;
318 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron1->borrowernumber, biblionumber
=> $biblio1->biblionumber} } );
319 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio2->biblionumber} } );
320 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio3->biblionumber} } );
322 $t->get_ok('/biblios' => json
=> {"suggestions.suggester.patron_id" => $patron1->borrowernumber })
323 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
325 $t->get_ok('/biblios' => json
=> {"suggestions.suggester.patron_id" => $patron2->borrowernumber })
326 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
328 $schema->storage->txn_rollback;
331 subtest
'object.search helper with q parameter' => sub {
334 $schema->storage->txn_begin;
336 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
337 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
338 my $biblio1 = $builder->build_sample_biblio;
339 my $biblio2 = $builder->build_sample_biblio;
340 my $biblio3 = $builder->build_sample_biblio;
341 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron1->borrowernumber, biblionumber
=> $biblio1->biblionumber} } );
342 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio2->biblionumber} } );
343 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio3->biblionumber} } );
345 $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}')
346 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
348 $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}')
349 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
351 $schema->storage->txn_rollback;
354 subtest
'object.search helper with x-koha-query header' => sub {
357 $schema->storage->txn_begin;
359 my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
360 my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
361 my $biblio1 = $builder->build_sample_biblio;
362 my $biblio2 = $builder->build_sample_biblio;
363 my $biblio3 = $builder->build_sample_biblio;
364 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron1->borrowernumber, biblionumber
=> $biblio1->biblionumber} } );
365 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio2->biblionumber} } );
366 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio3->biblionumber} } );
368 $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'})
369 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
371 $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'})
372 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
374 $schema->storage->txn_rollback;
377 subtest
'object.search helper with all query methods' => sub {
380 $schema->storage->txn_begin;
382 my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value
=> {firstname
=>'patron1'} } );
383 my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value
=> {firstname
=>'patron2'} } );
384 my $biblio1 = $builder->build_sample_biblio;
385 my $biblio2 = $builder->build_sample_biblio;
386 my $biblio3 = $builder->build_sample_biblio;
387 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron1->borrowernumber, biblionumber
=> $biblio1->biblionumber} } );
388 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio2->biblionumber} } );
389 my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio3->biblionumber} } );
391 $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})
392 ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
394 $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})
395 ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
397 $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})
398 ->json_is('/count' => 0, 'there shouldn\'t be biblios where suggester has patron1 fistname and patron2 id');
400 $schema->storage->txn_rollback;
403 subtest
'object.search helper order by embedded columns' => sub {
406 my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value
=> {firstname
=>'patron1'} } );
407 my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value
=> {firstname
=>'patron2'} } );
408 my $biblio1 = $builder->build_sample_biblio;
409 my $biblio2 = $builder->build_sample_biblio;
410 my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron1->borrowernumber, biblionumber
=> $biblio1->biblionumber} } );
411 my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value
=> { suggestedby
=> $patron2->borrowernumber, biblionumber
=> $biblio2->biblionumber} } );
413 $t->get_ok('/biblios?_order_by=-suggestions.suggester.firstname' => json
=> [{"me.biblio_id" => $biblio1->biblionumber}, {"me.biblio_id" => $biblio2->biblionumber}])
414 ->json_is('/biblios/0/biblio_id' => $biblio2->biblionumber, 'Biblio 2 should be first')
415 ->json_is('/biblios/1/biblio_id' => $biblio1->biblionumber, 'Biblio 1 should be second');
417 $schema->storage->txn_begin;