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

(-)a/Koha/REST/Plugin/Objects.pm (+18 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 JSON qw(decode_json);
23
22
=head1 NAME
24
=head1 NAME
23
25
24
Koha::REST::Plugin::Objects
26
Koha::REST::Plugin::Objects
Lines 97-102 sub register { Link Here
97
                }
99
                }
98
            }
100
            }
99
101
102
            if( defined $reserved_params->{q} || defined $reserved_params->{query} || defined $reserved_params->{'x-koha-query'}) {
103
                $filtered_params //={};
104
                my @query_params_array;
105
                my $query_params;
106
                push @query_params_array, $reserved_params->{query} if defined $reserved_params->{query};
107
                push @query_params_array, decode_json($reserved_params->{q}) if defined $reserved_params->{q};
108
                push @query_params_array, decode_json($reserved_params->{'x-koha-query'}) if defined $reserved_params->{'x-koha-query'};
109
110
                if(scalar(@query_params_array) > 1) {
111
                    $query_params = {'-and' => \@query_params_array};
112
                } else {
113
                    $query_params = $query_params_array[0];
114
                }
115
116
                $filtered_params = $c->merge_q_params( $filtered_params, $query_params, $result_set );
117
            }
100
            # Perform search
118
            # Perform search
101
            my $objects = $result_set->search( $filtered_params, $attributes );
119
            my $objects = $result_set->search( $filtered_params, $attributes );
102
120
(-)a/Koha/REST/Plugin/Query.pm (-1 / +73 lines)
Lines 20-25 use Modern::Perl; Link Here
20
use Mojo::Base 'Mojolicious::Plugin';
20
use Mojo::Base 'Mojolicious::Plugin';
21
use List::MoreUtils qw(any);
21
use List::MoreUtils qw(any);
22
use Scalar::Util qw(reftype);
22
use Scalar::Util qw(reftype);
23
use JSON qw(decode_json);
23
24
24
use Koha::Exceptions;
25
use Koha::Exceptions;
25
26
Lines 179-184 is raised. Link Here
179
        }
180
        }
180
    );
181
    );
181
182
183
=head3 merge_q_params
184
185
    $c->merge_q_params( $filtered_params, $q_params, $result_set );
186
187
Merges parameters from $q_params into $filtered_params.
188
189
=cut
190
191
    $app->helper(
192
        'merge_q_params' => sub {
193
194
            my ( $c, $filtered_params, $q_params, $result_set ) = @_;
195
196
            $q_params = decode_json($q_params) unless reftype $q_params;
197
198
            my $params = _parse_dbic_query($q_params, $result_set);
199
200
            return $params unless scalar(keys %{$filtered_params});
201
            return {'-and' => [$params, $filtered_params ]};
202
        }
203
    );
204
182
=head3 stash_embed
205
=head3 stash_embed
183
206
184
    $c->stash_embed( $c->match->endpoint->pattern->defaults->{'openapi.op_spec'} );
207
    $c->stash_embed( $c->match->endpoint->pattern->defaults->{'openapi.op_spec'} );
Lines 229-235 is raised. Link Here
229
252
230
sub _reserved_words {
253
sub _reserved_words {
231
254
232
    my @reserved_words = qw( _match _order_by _page _per_page );
255
    my @reserved_words = qw( _match _order_by _page _per_page q query x-koha-query);
233
    return \@reserved_words;
256
    return \@reserved_words;
234
}
257
}
235
258
Lines 346-349 sub _parse_prefetch { Link Here
346
    return $prefetch;
369
    return $prefetch;
347
}
370
}
348
371
372
sub _from_api_param {
373
    my ($key, $result_set) = @_;
374
375
    if($key =~ /\./) {
376
377
        my ($curr, $next) = split /\s*\.\s*/, $key, 2;
378
379
        return $curr.'.'._from_api_param($next, $result_set) if $curr eq 'me';
380
381
        my $ko_class = $result_set->prefetch_whitelist->{$curr};
382
383
        Koha::Exceptions::BadParameter->throw("Cannot find Koha::Object class for $curr")
384
            unless defined $ko_class;
385
386
        $result_set = $ko_class->new;
387
388
        if ($next =~ /\./) {
389
            return _from_api_param($next, $result_set);
390
        } else {
391
            return $curr.'.'.($result_set->from_api_mapping && defined $result_set->from_api_mapping->{$next} ? $result_set->from_api_mapping->{$next}:$next);
392
        }
393
    } else {
394
        return defined $result_set->from_api_mapping->{$key} ? $result_set->from_api_mapping->{$key} : $key;
395
    }
396
}
397
398
sub _parse_dbic_query {
399
    my ($q_params, $result_set) = @_;
400
401
    if(reftype($q_params) && reftype($q_params) eq 'HASH') {
402
        my $parsed_hash;
403
        foreach my $key (keys %{$q_params}) {
404
            if($key =~ /-?(not_?)?bool/i ) {
405
                $parsed_hash->{$key} = _from_api_param($q_params->{$key}, $result_set);
406
                next;
407
            }
408
            my $k = _from_api_param($key, $result_set);
409
            $parsed_hash->{$k} = _parse_dbic_query($q_params->{$key}, $result_set);
410
        }
411
        return $parsed_hash;
412
    } elsif (reftype($q_params) && reftype($q_params) eq 'ARRAY') {
413
        my @mapped = map{ _parse_dbic_query($_, $result_set) } @$q_params;
414
        return \@mapped;
415
    } else {
416
        return $q_params;
417
    }
418
419
}
420
349
1;
421
1;
(-)a/t/Koha/REST/Plugin/Query.t (-1 / +60 lines)
Lines 23-28 use Try::Tiny; Link Here
23
23
24
use Koha::Cities;
24
use Koha::Cities;
25
use Koha::Holds;
25
use Koha::Holds;
26
use Koha::Biblios;
26
27
27
app->log->level('error');
28
app->log->level('error');
28
29
Lines 134-139 get '/dbic_merge_prefetch' => sub { Link Here
134
    $c->render( json => $attributes, status => 200 );
135
    $c->render( json => $attributes, status => 200 );
135
};
136
};
136
137
138
get '/merge_q_params' => sub {
139
  my $c = shift;
140
  my $filtered_params = {'biblio_id' => 1};
141
  my $result_set = Koha::Biblios->new;
142
  $filtered_params = $c->merge_q_params($filtered_params, $c->req->json->{q}, $result_set);
143
144
  $c->render( json => $filtered_params, status => 200 );
145
};
146
137
get '/build_query' => sub {
147
get '/build_query' => sub {
138
    my $c = shift;
148
    my $c = shift;
139
    my ( $filtered_params, $reserved_params ) =
149
    my ( $filtered_params, $reserved_params ) =
Lines 209-215 sub to_model { Link Here
209
219
210
# The tests
220
# The tests
211
221
212
use Test::More tests => 5;
222
use Test::More tests => 6;
213
use Test::Mojo;
223
use Test::Mojo;
214
224
215
subtest 'extract_reserved_params() tests' => sub {
225
subtest 'extract_reserved_params() tests' => sub {
Lines 297-302 subtest '/dbic_merge_prefetch' => sub { Link Here
297
      ->json_like( '/prefetch/1' => qr/item|biblio/ );
307
      ->json_like( '/prefetch/1' => qr/item|biblio/ );
298
};
308
};
299
309
310
subtest '/merge_q_params' => sub {
311
  plan tests => 3;
312
  my $t = Test::Mojo->new;
313
314
  $t->get_ok('/merge_q_params' => json => {
315
    q => {
316
      "-not_bool" => "suggestions.suggester.patron_card_lost",
317
      "-or" => [
318
        {
319
          "creation_date" => {
320
            "!=" => ["fff", "zzz", "xxx"]
321
          }
322
        },
323
        { "suggestions.suggester.housebound_profile.frequency" => "123" },
324
        {
325
          "suggestions.suggester.library_id" => {"like" => "%CPL%"}
326
        }
327
      ]
328
    }
329
  })->status_is(200)
330
    ->json_is( '/-and' => [
331
        {
332
          "-not_bool" => "suggester.lost",
333
          "-or" => [
334
            {
335
              "datecreated" => {
336
                "!=" => [
337
                  "fff",
338
                  "zzz",
339
                  "xxx"
340
                ]
341
              }
342
            },
343
            {
344
              "housebound_profile.frequency" => 123
345
            },
346
            {
347
              "suggester.branchcode" => {
348
                "like" => "\%CPL\%"
349
              }
350
            }
351
          ]
352
        },
353
        {
354
          "biblio_id" => 1
355
        }
356
      ]);
357
};
358
300
subtest '_build_query_params_from_api' => sub {
359
subtest '_build_query_params_from_api' => sub {
301
360
302
    plan tests => 16;
361
    plan tests => 16;
(-)a/t/db_dependent/Koha/REST/Plugin/Objects.t (-2 / +117 lines)
Lines 20-25 use Modern::Perl; Link Here
20
use Koha::Acquisition::Orders;
20
use Koha::Acquisition::Orders;
21
use Koha::Cities;
21
use Koha::Cities;
22
use Koha::Holds;
22
use Koha::Holds;
23
use Koha::Biblios;
23
24
24
# Dummy app for testing the plugin
25
# Dummy app for testing the plugin
25
use Mojolicious::Lite;
26
use Mojolicious::Lite;
Lines 55-62 get '/patrons/:patron_id/holds' => sub { Link Here
55
    $c->render( status => 200, json => {count => scalar(@$holds)} );
56
    $c->render( status => 200, json => {count => scalar(@$holds)} );
56
};
57
};
57
58
59
get '/biblios' => sub {
60
    my $c = shift;
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", {
68
        "suggestions" => {
69
            children => {
70
                "suggester" => {}
71
            }
72
        }
73
    });
74
    my $biblios = $c->objects->search($biblios_set);
75
76
    $c->render( status => 200, json => {count => scalar(@$biblios)} );
77
};
78
58
# The tests
79
# The tests
59
use Test::More tests => 4;
80
use Test::More tests => 8;
60
use Test::Mojo;
81
use Test::Mojo;
61
82
62
use t::lib::TestBuilder;
83
use t::lib::TestBuilder;
Lines 231-233 subtest 'objects.search helper, with path parameters and _match' => sub { Link Here
231
252
232
    $schema->storage->txn_rollback;
253
    $schema->storage->txn_rollback;
233
};
254
};
234
- 
255
256
subtest 'object.search helper with query parameter' => sub {
257
    plan tests => 4;
258
259
    $schema->storage->txn_begin;
260
261
    my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
262
    my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
263
    my $biblio1 = $builder->build_sample_biblio;
264
    my $biblio2 = $builder->build_sample_biblio;
265
    my $biblio3 = $builder->build_sample_biblio;
266
    my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
267
    my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
268
    my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
269
270
    $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron1->borrowernumber })
271
      ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
272
273
    $t->get_ok('/biblios' => json => {"suggestions.suggester.patron_id" => $patron2->borrowernumber })
274
      ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
275
276
    $schema->storage->txn_rollback;
277
};
278
279
subtest 'object.search helper with q parameter' => sub {
280
    plan tests => 4;
281
282
    $schema->storage->txn_begin;
283
284
    my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
285
    my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
286
    my $biblio1 = $builder->build_sample_biblio;
287
    my $biblio2 = $builder->build_sample_biblio;
288
    my $biblio3 = $builder->build_sample_biblio;
289
    my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
290
    my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
291
    my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
292
293
    $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}')
294
      ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
295
296
    $t->get_ok('/biblios?q={"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}')
297
      ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
298
299
    $schema->storage->txn_rollback;
300
};
301
302
subtest 'object.search helper with x-koha-query header' => sub {
303
    plan tests => 4;
304
305
    $schema->storage->txn_begin;
306
307
    my $patron1 = $builder->build_object( { class => "Koha::Patrons" } );
308
    my $patron2 = $builder->build_object( { class => "Koha::Patrons" } );
309
    my $biblio1 = $builder->build_sample_biblio;
310
    my $biblio2 = $builder->build_sample_biblio;
311
    my $biblio3 = $builder->build_sample_biblio;
312
    my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
313
    my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
314
    my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
315
316
    $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron1->borrowernumber.'"}'})
317
      ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
318
319
    $t->get_ok('/biblios' => {'x-koha-query' => '{"suggestions.suggester.patron_id": "'.$patron2->borrowernumber.'"}'})
320
      ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
321
322
    $schema->storage->txn_rollback;
323
};
324
325
subtest 'object.search helper with all query methods' => sub {
326
    plan tests => 6;
327
328
    $schema->storage->txn_begin;
329
330
    my $patron1 = $builder->build_object( { class => "Koha::Patrons" , value => {cardnumber => 'cardpatron1', firstname=>'patron1'} } );
331
    my $patron2 = $builder->build_object( { class => "Koha::Patrons" , value => {cardnumber => 'cardpatron2', firstname=>'patron2'} } );
332
    my $biblio1 = $builder->build_sample_biblio;
333
    my $biblio2 = $builder->build_sample_biblio;
334
    my $biblio3 = $builder->build_sample_biblio;
335
    my $suggestion1 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron1->borrowernumber, biblionumber => $biblio1->biblionumber} } );
336
    my $suggestion2 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio2->biblionumber} } );
337
    my $suggestion3 = $builder->build_object( { class => "Koha::Suggestions", value => { suggestedby => $patron2->borrowernumber, biblionumber => $biblio3->biblionumber} } );
338
339
    $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})
340
      ->json_is('/count' => 1, 'there should be 1 biblio with suggestions of patron 1');
341
342
    $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})
343
      ->json_is('/count' => 2, 'there should be 2 biblios with suggestions of patron 2');
344
345
    $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})
346
      ->json_is('/count' => 0, 'there shouldn\'t be biblios where suggester has patron1 fistname and patron2 id');
347
348
    $schema->storage->txn_rollback;
349
};

Return to bug 24502