Koha/t/db_dependent/api/v1/biblios.t
Zeno Tajoli 50a2e9316a Bug 33036: REST API: Merge biblio records implements merging of records
+ attached items, subscriptions etc via the API as an alternative to the web interface: cgi-bin/koha/cataloguing/merge.pl

This is a slightly improved version of Zenos patch: I (domm) have converted the code in Koha::Biblio to a more DBICy style and packed it into a transaction (as requested in Comment 23)

Even the QA script is happy now!

To test:
    1) you need an API user with the permissions "editcatalogue"
    2) two records: one to be merged into (with biblio_id, eg 262) and another one from
       which to merge (with biblio_id_to_merge, eg 9) which will be deleted!
       both records may/should have items, subscription, subscriptionhistory, serial, suggestions
       orders and holds
    3) check both records via the web
    4) Apply patch
    5) Write a JSON file with inside the field 'biblio_id_to_merge' and the biblionumber from wihich to merge.
       As example:
       {
         "biblio_id_to_merge" : 9
       }
     6) Execute an API call with correct headers and location. For example:
        curl -s -u koha:koha --header "Content-Type: application/json" --header "Accept: application/marc-in-json"
                  --request POST "http://127.0.0.1:8080/api/v1/biblios/262/merge" -d @file.json
        You must to setup the headers and to use a json file with parameters
     7) The record with the id 9 is deleted now, the record with 262 has all items, etc attached,
        the return is: return code 200 and the changed record 262 in marc-in-json format
     8) It is possible to override biblio data with an external bib record. You need to put external bib record
        into the json file in marc-in-json format. To write use the json file uploaded as example
        You need to fill the fields 'rules' and 'datarecord'. The field 'rules' must contains 'override_ext'
        To do the call:
         curl -s -u koha:koha --header "Content-Type: application/json" --header "Accept: application/marc-in-json"
                  --request POST "http://127.0.0.1:8080/api/v1/biblios/XXX/merge" -d @file_with_recod.json
      9) The record in 'biblio_id_to_merge' is deleted now, in biblio XXX now there are the bibliographic data
         of field 'datarecord' of json file, the return is: return code 200 and the changed record XXX in marc-in-json format
      10) Go into intranet and do a search. Select two or (better) more record.
      11) Merge them; merge must be a success.
      12) Test with prove -v t/db_dependent/Koha/Biblio.t
      13) Test with prove -v t/db_dependent/api/v1/biblios.t

To test with curl the step 8 you can customize the json file attached in bugzilla.
The marc-in-json record inside follows the MAR21 standard

Sponsored-by: Technische Hochschule Wildau
Co-authored-by: Zeno Tajoli <ztajoli@gmail.com>
Co-authored-by: Thomas Klausner <domm@plix.at>
Co-authored-by: Mark Hofstetter <<mark@hofstetter.at>>
Signed-off-by: Jan Kissig <jkissig@th-wildau.de>

Bug 33036: Update of test number.

File ../biblios.t was update with a new subutest.
So we need this update to have a 'OK' after test running.

Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
Signed-off-by: Katrin Fischer <katrin.fischer@bsz-bw.de>
(cherry picked from commit c60a6d8cd9)
Signed-off-by: Fridolin Somers <fridolin.somers@biblibre.com>
2024-03-21 10:43:30 +01:00

2162 lines
66 KiB
Perl
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env perl
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use utf8;
use Encode;
use Test::More tests => 15;
use Test::MockModule;
use Test::Mojo;
use Test::Warn;
use t::lib::Mocks;
use t::lib::TestBuilder;
use Mojo::JSON qw(encode_json);
use C4::Auth;
use C4::Circulation qw( AddIssue AddReturn );
use Koha::Biblios;
use Koha::Database;
use Koha::DateUtils qw (dt_from_string);
use Koha::Checkouts;
use Koha::Old::Checkouts;
use Mojo::JSON qw(encode_json);
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
my $t = Test::Mojo->new('Koha::REST::V1');
subtest 'get() tests' => sub {
plan tests => 22;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio({
title => 'The unbearable lightness of being',
author => 'Milan Kundera'
});
$t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber)
->status_is(403);
$patron->flags(4)->store;
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/weird+format' } )
->status_is(400);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/json' } )
->status_is(200)
->json_is( '/title', 'The unbearable lightness of being' )
->json_is( '/author', 'Milan Kundera' );
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marcxml+xml' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc-in-json' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200)
->content_is($biblio->metadata->record->as_formatted);
$biblio->delete;
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc' } )
->status_is(404)
->json_is( '/error', 'Object not found.' );
subtest 'marc-in-json encoding tests' => sub {
plan tests => 3;
my $title_with_diacritics = "L'insoutenable légèreté de l'être";
my $biblio = $builder->build_sample_biblio(
{
title => $title_with_diacritics,
author => "Milan Kundera"
}
);
my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc-in-json' } )
->status_is(200)->tx->res->body;
my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
};
subtest 'marcxml encoding tests' => sub {
plan tests => 3;
my $marcflavour = C4::Context->preference('marcflavour');
t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
my $title_with_diacritics = "L'insoutenable légèreté de l'être";
my $biblio = $builder->build_sample_biblio(
{
title => $title_with_diacritics,
author => "Milan Kundera"
}
);
my $record = $biblio->metadata->record;
$record->leader(' nam 3 4500');
$biblio->metadata->metadata($record->as_xml_record('UNIMARC'));
$biblio->metadata->store;
my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marcxml+xml' } )
->status_is(200)->tx->res->body;
my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
t::lib::Mocks::mock_preference('marcflavour', $marcflavour);
};
$schema->storage->txn_rollback;
};
subtest 'get_items() tests' => sub {
plan tests => 11;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
$t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
->status_is(403);
$patron->flags(4)->store;
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
->status_is(200)
->json_is( '' => [], 'No items on the biblio' );
my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items")
->status_is(200)
->json_is( '' => [ $item_1->to_api, $item_2->to_api ], 'The items are returned' );
$t->get_ok(
"//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/items" => { "x-koha-embed" => "+strings" } )
->status_is(200)
->json_has( '/0/_strings/home_library_id/str' => $item_1->holding_branch->branchname, '_strings are embedded' );
$schema->storage->txn_rollback;
};
subtest 'delete() tests' => sub {
plan tests => 9;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no permissions
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $item = $builder->build_sample_item();
my $biblio_id = $item->biblionumber;
$t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
->status_is(403, 'Not enough permissions makes it return the right code');
# Add permissions
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 9,
code => 'edit_catalogue'
}
}
);
# Bibs with items cannot be deleted
$t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
->status_is(409);
$item->delete();
# Bibs with no items can be deleted
$t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
->status_is(204, 'SWAGGER3.2.4')
->content_is('', 'SWAGGER3.3.4');
$t->delete_ok("//$userid:$password@/api/v1/biblios/$biblio_id")
->status_is(404);
$schema->storage->txn_rollback;
};
subtest 'get_public() tests' => sub {
plan tests => 25;
$schema->storage->txn_begin;
my $category = $builder->build_object({ class => 'Koha::Patron::Categories' });
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => {
flags => undef, # opac user
categorycode => $category->categorycode
}
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio({
title => 'The unbearable lightness of being',
author => 'Milan Kundera'
});
# Make sure author in shown in the OPAC
my $subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
while ( my $subfield = $subfields->next ) {
$subfield->set({ hidden => -1 })->store;
}
Koha::Caches->get_instance()->flush_all;
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/weird+format' } )
->status_is(400);
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200)
->content_like( qr{100\s+_aMilan Kundera} )
->content_like( qr{245\s+_aThe unbearable lightness of being} );
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marcxml+xml' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc-in-json' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200)
->content_is($biblio->metadata->record->as_formatted);
subtest 'anonymous access' => sub {
plan tests => 9;
$t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marcxml+xml' } )
->status_is(200);
$t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc-in-json' } )
->status_is(200);
$t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc' } )
->status_is(200);
$t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200)
->content_is($biblio->metadata->record->as_formatted);
};
subtest 'marc-in-json encoding tests' => sub {
plan tests => 3;
my $title_with_diacritics = "L'insoutenable légèreté de l'être";
my $biblio = $builder->build_sample_biblio(
{
title => $title_with_diacritics,
author => "Milan Kundera"
}
);
my $result = $t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc-in-json' } )
->status_is(200)->tx->res->body;
my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
};
# Hide author in OPAC
$subfields = Koha::MarcSubfieldStructures->search({ tagfield => '100' });
while ( my $subfield = $subfields->next ) {
$subfield->set({ hidden => 1 })->store;
}
Koha::Caches->get_instance()->flush_all;
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200)
->content_unlike( qr{100\s+_aMilan Kundera} )
->content_like( qr{245\s+_aThe unbearable lightness of being} );
subtest 'hidden_in_opac tests' => sub {
plan tests => 6;
my $biblio_hidden_in_opac = 1;
my $biblio_class = Test::MockModule->new('Koha::Biblio');
# force biblio hidden in OPAC
$biblio_class->mock( 'hidden_in_opac', sub { return $biblio_hidden_in_opac; } );
$t->get_ok( "/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(404, 'hidden_in_opac + anonymous => hidden');
my $category_override_hidden_items = 0;
my $category_class = Test::MockModule->new('Koha::Patron::Category');
$category_class->mock( 'override_hidden_items', sub { return $category_override_hidden_items; } );
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(404, "hidden_in_opac + patron whose category doesn't override => hidden");
# Make the category override
$category_override_hidden_items = 1;
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'text/plain' } )
->status_is(200, "hidden_in_opac + patron whose category that overrides => displayed");
t::lib::Mocks::mock_preference('OpacHiddenItems');
};
$biblio->delete;
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber
=> { Accept => 'application/marc' } )
->status_is(404)
->json_is( '/error', 'Object not found.' );
$schema->storage->txn_rollback;
};
subtest 'pickup_locations() tests' => sub {
plan tests => 16;
$schema->storage->txn_begin;
t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
# Small trick to ease testing
Koha::Libraries->search->update({ pickup_location => 0 });
my $library_1 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'A', pickup_location => 1 } });
my $library_2 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'B', pickup_location => 1 } });
my $library_3 = $builder->build_object({ class => 'Koha::Libraries', value => { marcorgcode => 'C', pickup_location => 1 } });
my $library_1_api = $library_1->to_api();
my $library_2_api = $library_2->to_api();
my $library_3_api = $library_3->to_api();
$library_1_api->{needs_override} = Mojo::JSON->false;
$library_2_api->{needs_override} = Mojo::JSON->false;
$library_3_api->{needs_override} = Mojo::JSON->true;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { userid => 'tomasito', flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 6,
code => 'place_holds',
},
}
);
my $biblio_class = Test::MockModule->new('Koha::Biblio');
$biblio_class->mock(
'pickup_locations',
sub {
my ( $self, $params ) = @_;
my $mock_patron = $params->{patron};
is( $mock_patron->borrowernumber,
$patron->borrowernumber, 'Patron passed correctly' );
return Koha::Libraries->search(
{
branchcode => {
'-in' => [
$library_1->branchcode,
$library_2->branchcode
]
}
},
{ # we make sure no surprises in the order of the result
order_by => { '-asc' => 'marcorgcode' }
}
);
}
);
my $biblio = $builder->build_sample_biblio;
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. "/pickup_locations?patron_id=" . $patron->id )
->json_is( [ $library_1_api, $library_2_api ] );
# filtering works!
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. '/pickup_locations?'
. 'patron_id=' . $patron->id . '&q={"marc_org_code": { "-like": "A%" }}' )
->json_is( [ $library_1_api ] );
t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
my $library_4 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 0, marcorgcode => 'X' } });
my $library_5 = $builder->build_object({ class => 'Koha::Libraries', value => { pickup_location => 1, marcorgcode => 'Y' } });
my $library_5_api = $library_5->to_api();
$library_5_api->{needs_override} = Mojo::JSON->true;
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. "/pickup_locations?"
. "patron_id=" . $patron->id . "&_order_by=marc_org_code" )
->json_is( [ $library_1_api, $library_2_api, $library_3_api, $library_5_api ] );
subtest 'Pagination and AllowHoldPolicyOverride tests' => sub {
plan tests => 27;
t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 1 );
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
->json_is( [$library_1_api] )
->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
->header_unlike( 'Link', qr|rel="prev"| )
->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. "/pickup_locations?"
. "patron_id="
. $patron->id
. "&_order_by=marc_org_code"
. "&_per_page=1&_page=3" ) # force the needs_override=1 check
->json_is( [$library_3_api] )
->header_is( 'X-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
->header_is( 'X-Base-Total-Count', '4', '4 is the count for libraries with pickup_location=1' )
->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="prev"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1.*)>\; rel="next"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=4.*|_page=4.*\&_per_page=1).*>\; rel="last"# );
t::lib::Mocks::mock_preference( 'AllowHoldPolicyOverride', 0 );
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->id . "/pickup_locations?" . "patron_id=" . $patron->id . "&_order_by=marc_org_code" . "&_per_page=1" )
->json_is( [$library_1_api] )
->header_is( 'X-Total-Count', '2' )
->header_is( 'X-Base-Total-Count', '2' )
->header_unlike( 'Link', qr|rel="prev"| )
->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1.*)>\; rel="next"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=1.*|_page=1.*\&_per_page=1).*>\; rel="first"# )
->header_like( 'Link', qr#(_per_page=1.*\&_page=2.*|_page=2.*\&_per_page=1).*>\; rel="last"# );
};
my $deleted_patron = $builder->build_object({ class => 'Koha::Patrons' });
my $deleted_patron_id = $deleted_patron->id;
$deleted_patron->delete;
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. "/pickup_locations?"
. "patron_id=" . $deleted_patron_id )
->status_is( 400 )
->json_is( '/error' => 'Patron not found' );
$biblio->delete;
$t->get_ok( "//$userid:$password@/api/v1/biblios/"
. $biblio->id
. "/pickup_locations?"
. "patron_id=" . $patron->id )
->status_is( 404 )
->json_is( '/error' => 'Biblio not found' );
$schema->storage->txn_rollback;
};
subtest 'get_items_public() tests' => sub {
plan tests => 15;
$schema->storage->txn_begin;
my $override_hidden_items = 0;
my $mocked_category = Test::MockModule->new('Koha::Patron::Category');
$mocked_category->mock(
'override_hidden_items',
sub {
return $override_hidden_items;
}
);
my $rules = undef;
my $mocked_context = Test::MockModule->new('C4::Context');
$mocked_context->mock(
'yaml_preference',
sub {
return $rules;
}
);
my $patron = $builder->build_object( { class => 'Koha::Patrons' } );
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
$t->get_ok(
"//$userid:$password@/api/v1/public/biblios/" . $biblio->id . "/items" )
->status_is(200)->json_is( '' => [], 'No items on the biblio' );
my $item_1 = $builder->build_sample_item( { biblionumber => $biblio->id } );
my $item_2 = $builder->build_sample_item(
{ biblionumber => $biblio->id, withdrawn => 1 } );
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
. $biblio->biblionumber
. "/items" )->status_is(200)->json_is(
'' => [
$item_1->to_api( { public => 1 } ),
$item_2->to_api( { public => 1 } )
],
'The items are returned'
);
$rules = { withdrawn => ['1'] };
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
. $biblio->biblionumber
. "/items" )->status_is(200)->json_is(
'' => [ $item_1->to_api( { public => 1 } ) ],
'The items are returned, hidden one is not returned'
);
$t->get_ok( "/api/v1/public/biblios/"
. $biblio->biblionumber
. "/items" )->status_is(200)->json_is(
'' => [ $item_1->to_api( { public => 1 } ) ],
'Anonymous user, items are returned, hidden one is not returned'
);
$override_hidden_items = 1;
$t->get_ok( "//$userid:$password@/api/v1/public/biblios/"
. $biblio->biblionumber
. "/items" )->status_is(200)->json_is(
'' => [
$item_1->to_api( { public => 1 } ),
$item_2->to_api( { public => 1 } )
],
'The items are returned, the patron category has an override'
);
$schema->storage->txn_rollback;
};
subtest 'get_bookings() tests' => sub {
plan tests => 8;
$schema->storage->txn_begin;
my $librarian = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no additional permissions
}
);
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $librarian->borrowernumber,
module_bit => 1,
code => 'manage_bookings',
},
}
);
my $password = 'thePassword123';
$librarian->set_password( { password => $password, skip_validation => 1 } );
my $userid = $librarian->userid;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
$patron->set_password( { password => $password, skip_validation => 1 } );
my $unauth_userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
my $item1 = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
my $item2 = $builder->build_sample_item( { bookable => 1, biblionumber => $biblio->id } );
$t->get_ok( "//$unauth_userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(403);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
->json_is( '' => [], 'No bookings on the biblio' );
# One booking
my $start_0 = dt_from_string->subtract( days => 2 )->truncate( to => 'day' );
my $end_0 = dt_from_string->add( days => 4 )->truncate( to => 'day' );
my $booking_0 = $builder->build_object(
{
class => 'Koha::Bookings',
value => {
biblio_id => $biblio->id,
item_id => $item1->id,
start_date => $start_0,
end_date => $end_0
}
}
);
my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/bookings" )->status_is(200)
->tx->res->json;
is_deeply( $ret, [ $booking_0->to_api ] );
$schema->storage->txn_rollback;
};
subtest 'get_checkouts() tests' => sub {
plan tests => 17;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
$t->get_ok("//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
->status_is(403);
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 1,
code => 'circulate_remaining_permissions',
},
}
);
$t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts" )
->status_is( 200, 'circulate_remaining_permissions allows checkouts access' )
->json_is( '' => [], 'No checkouts on the biblio' );
my $bookings_librarian = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no additional permissions
}
);
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $bookings_librarian->borrowernumber,
module_bit => 1,
code => 'manage_bookings',
},
}
);
$bookings_librarian->set_password( { password => $password, skip_validation => 1 } );
my $bookings_userid = $bookings_librarian->userid;
$t->get_ok( "//$bookings_userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts" )
->status_is( 200, 'manage_bookings allows checkouts access' )
->json_is( '' => [], 'No checkouts on the biblio' );
my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber });
AddIssue( $patron, $item_1->barcode );
AddIssue( $patron, $item_2->barcode );
my $ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
->status_is(200)
->tx->res->json;
my $checkout_1 = Koha::Checkouts->find({ itemnumber => $item_1->id });
my $checkout_2 = Koha::Checkouts->find({ itemnumber => $item_2->id });
is_deeply( $ret, [ $checkout_1->to_api, $checkout_2->to_api ] );
AddReturn( $item_1->barcode );
$ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts")
->status_is(200)
->tx->res->json;
is_deeply( $ret, [ $checkout_2->to_api ] );
$ret = $t->get_ok( "//$userid:$password@/api/v1/biblios/" . $biblio->biblionumber . "/checkouts?checked_in=1")
->status_is(200)
->tx->res->json;
my $old_checkout_1 = Koha::Old::Checkouts->find( $checkout_1->id );
is_deeply( $ret, [ $old_checkout_1->to_api ] );
$schema->storage->txn_rollback;
};
subtest 'set_rating() tests' => sub {
plan tests => 12;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
$t->post_ok("/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
->status_is(403);
$t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => 3 })
->status_is(200)
->json_is( '/rating', '3' )
->json_is( '/average', '3' )
->json_is( '/count', '1' );
$t->post_ok("//$userid:$password@/api/v1/public/biblios/" . $biblio->biblionumber . "/ratings" => json => { rating => undef })
->status_is(200)
->json_is( '/rating', undef )
->json_is( '/average', '0' )
->json_is( '/count', '0' );
$schema->storage->txn_rollback;
};
subtest 'post() tests' => sub {
plan tests => 13;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no permissions
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $frameworkcode = 'BKS';
my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
<record
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
xmlns="http://www.loc.gov/MARC21/slim">
<leader>01102pam a2200289 a 7500</leader>
<controlfield tag="001">2504398</controlfield>
<controlfield tag="005">20200421093816.0</controlfield>
<controlfield tag="008">920610s1993 caub s001 0 eng </controlfield>
<datafield tag="010" ind1=" " ind2=" ">
<subfield code="a"> 92021731 </subfield>
</datafield>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">05200784381 (Test marcxml)</subfield>
</datafield>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">05200784461 (Test marcxml)</subfield>
</datafield>
<datafield tag="040" ind1=" " ind2=" ">
<subfield code="a">DLC</subfield>
<subfield code="c">DLC</subfield>
<subfield code="d">DLC</subfield>
</datafield>
<datafield tag="041" ind1="0" ind2=" ">
<subfield code="a">enggrc</subfield>
</datafield>
<datafield tag="050" ind1="0" ind2="0">
<subfield code="a">PA522</subfield>
<subfield code="b">.M38 1993</subfield>
</datafield>
<datafield tag="082" ind1="0" ind2="0">
<subfield code="a">480</subfield>
<subfield code="2">20</subfield>
</datafield>
<datafield tag="100" ind1="1" ind2=" ">
<subfield code="a">Mastronarde, Donald J.</subfield>
<subfield code="9">389</subfield>
</datafield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
<subfield code="c">Donald J. Mastronarde.</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="a">Berkeley :</subfield>
<subfield code="b">University of California Press,</subfield>
<subfield code="c">c1993.</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">ix, 425 p. :</subfield>
<subfield code="b">maps ;</subfield>
<subfield code="c">26 cm.</subfield>
</datafield>
<datafield tag="500" ind1=" " ind2=" ">
<subfield code="a">Includes index.</subfield>
</datafield>
<datafield tag="650" ind1=" " ind2="0">
<subfield code="a">Attic Greek dialect</subfield>
<subfield code="9">7</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2="2">
<subfield code="3">Contributor biographical information</subfield>
<subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2="2">
<subfield code="3">Publisher description</subfield>
<subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
</datafield>
<datafield tag="906" ind1=" " ind2=" ">
<subfield code="a">7</subfield>
<subfield code="b">cbc</subfield>
<subfield code="c">orignew</subfield>
<subfield code="d">1</subfield>
<subfield code="e">ocip</subfield>
<subfield code="f">19</subfield>
<subfield code="g">y-gencatlg</subfield>
</datafield>
<datafield tag="942" ind1=" " ind2=" ">
<subfield code="2">ddc</subfield>
<subfield code="c">BK</subfield>
</datafield>
<datafield tag="955" ind1=" " ind2=" ">
<subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
</datafield>
<datafield tag="999" ind1=" " ind2=" ">
<subfield code="c">3</subfield>
<subfield code="d">3</subfield>
</datafield>
</record>|;
my $mij = q|{
"fields": [
{
"001": "2504398"
},
{
"005": "20200421093816.0"
},
{
"008": "920610s1993 caub s001 0 eng "
},
{
"010": {
"ind1": " ",
"subfields": [
{
"a": " 92021731 "
}
],
"ind2": " "
}
},
{
"020": {
"subfields": [
{
"a": "05200784382 (Test mij)"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"020": {
"subfields": [
{
"a": "05200784462 (Test mij)"
}
],
"ind1": " ",
"ind2": " "
}
},
{
"040": {
"subfields": [
{
"a": "DLC"
},
{
"c": "DLC"
},
{
"d": "DLC"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"041": {
"ind2": " ",
"subfields": [
{
"a": "enggrc"
}
],
"ind1": "0"
}
},
{
"050": {
"subfields": [
{
"a": "PA522"
},
{
"b": ".M38 1993"
}
],
"ind1": "0",
"ind2": "0"
}
},
{
"082": {
"subfields": [
{
"a": "480"
},
{
"2": "20"
}
],
"ind2": "0",
"ind1": "0"
}
},
{
"100": {
"ind2": " ",
"subfields": [
{
"a": "Mastronarde, Donald J."
},
{
"9": "389"
}
],
"ind1": "1"
}
},
{
"245": {
"ind1": "1",
"subfields": [
{
"a": "Introduction to Attic Greek (Using mij) /"
},
{
"c": "Donald J. Mastronarde."
}
],
"ind2": "0"
}
},
{
"260": {
"subfields": [
{
"a": "Berkeley :"
},
{
"b": "University of California Press,"
},
{
"c": "c1993."
}
],
"ind2": " ",
"ind1": " "
}
},
{
"300": {
"ind1": " ",
"subfields": [
{
"a": "ix, 425 p. :"
},
{
"b": "maps ;"
},
{
"c": "26 cm."
}
],
"ind2": " "
}
},
{
"500": {
"subfields": [
{
"a": "Includes index."
}
],
"ind1": " ",
"ind2": " "
}
},
{
"650": {
"subfields": [
{
"a": "Attic Greek dialect"
},
{
"9": "7"
}
],
"ind2": "0",
"ind1": " "
}
},
{
"856": {
"subfields": [
{
"3": "Contributor biographical information"
},
{
"u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
}
],
"ind2": "2",
"ind1": "4"
}
},
{
"856": {
"ind1": "4",
"subfields": [
{
"3": "Publisher description"
},
{
"u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
}
],
"ind2": "2"
}
},
{
"906": {
"subfields": [
{
"a": "7"
},
{
"b": "cbc"
},
{
"c": "orignew"
},
{
"d": "1"
},
{
"e": "ocip"
},
{
"f": "19"
},
{
"g": "y-gencatlg"
}
],
"ind1": " ",
"ind2": " "
}
},
{
"942": {
"subfields": [
{
"2": "ddc"
},
{
"c": "BK"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"955": {
"subfields": [
{
"a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"999": {
"subfields": [
{
"c": "3"
},
{
"d": "3"
}
],
"ind1": " ",
"ind2": " "
}
}
],
"leader": "01102pam a2200289 a 8500"
}|;
my $marc = q|01102pam a2200289 a 9500001000800000005001700008008004100025010001700066020002800083020003500111040001800146041001100164050002100175082001200196100003200208245005800240260005600298300003300354500002000387650002700407856009500434856008700529906004500616942001200661955013000673999000900803250439820200421093816.0920610s1993 caub s001 0 eng  a 92021731  a05200784383 (Test usmarc) a05200784463 (Test usmarc) aDLCcDLCdDLC0 aenggrc00aPA522b.M38 199300a4802201 aMastronarde, Donald J.938910aIntroduction to Attic Greek (Using usmarc) /cDonald J. Mastronarde. aBerkeley :bUniversity of California Press,cc1993. aix, 425 p. :bmaps ;c26 cm. aIncludes index. 0aAttic Greek dialect97423Contributor biographical informationuhttp://www.loc.gov/catdir/bios/ucal051/92021731.html423Publisher descriptionuhttp://www.loc.gov/catdir/description/ucal041/92021731.html a7bcbccorignewd1eocipf19gy-gencatlg 2ddccBK apc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93 c3d3|;
$t->post_ok("//$userid:$password@/api/v1/biblios")
->status_is(403, 'Not enough permissions makes it return the right code');
# Add permissions
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 9,
code => 'edit_catalogue'
}
}
);
$t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode, "x-record-schema" => 'INVALID'})
->status_is(400, 'Invalid header x-record-schema');
$t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
->status_is(200)
->json_has('/id');
$t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode, 'x-confirm-not-duplicate' => 1} => $mij)
->status_is(200)
->json_has('/id');
$t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
->status_is(200)
->json_has('/id');
$schema->storage->txn_rollback;
};
subtest 'put() tests' => sub {
plan tests => 14;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 } # no permissions
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $frameworkcode = 'BKS';
my $biblio = $builder->build_sample_biblio({frameworkcode => $frameworkcode});
my $biblionumber = $biblio->biblionumber;
my $marcxml = q|<?xml version="1.0" encoding="UTF-8"?>
<record
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd"
xmlns="http://www.loc.gov/MARC21/slim">
<leader>01102pam a2200289 a 6500</leader>
<controlfield tag="001">2504398</controlfield>
<controlfield tag="005">20200421093816.0</controlfield>
<controlfield tag="008">920610s1993 caub s001 0 eng </controlfield>
<datafield tag="010" ind1=" " ind2=" ">
<subfield code="a"> 92021731 </subfield>
</datafield>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">05200784384 (Test json)</subfield>
</datafield>
<datafield tag="020" ind1=" " ind2=" ">
<subfield code="a">05200784464 (Test json)</subfield>
</datafield>
<datafield tag="040" ind1=" " ind2=" ">
<subfield code="a">DLC</subfield>
<subfield code="c">DLC</subfield>
<subfield code="d">DLC</subfield>
</datafield>
<datafield tag="041" ind1="0" ind2=" ">
<subfield code="a">enggrc</subfield>
</datafield>
<datafield tag="050" ind1="0" ind2="0">
<subfield code="a">PA522</subfield>
<subfield code="b">.M38 1993</subfield>
</datafield>
<datafield tag="082" ind1="0" ind2="0">
<subfield code="a">480</subfield>
<subfield code="2">20</subfield>
</datafield>
<datafield tag="100" ind1="1" ind2=" ">
<subfield code="a">Mastronarde, Donald J.</subfield>
<subfield code="9">389</subfield>
</datafield>
<datafield tag="245" ind1="1" ind2="0">
<subfield code="a">Introduction to Attic Greek (Using marcxml) /</subfield>
<subfield code="c">Donald J. Mastronarde.</subfield>
</datafield>
<datafield tag="260" ind1=" " ind2=" ">
<subfield code="a">Berkeley :</subfield>
<subfield code="b">University of California Press,</subfield>
<subfield code="c">c1993.</subfield>
</datafield>
<datafield tag="300" ind1=" " ind2=" ">
<subfield code="a">ix, 425 p. :</subfield>
<subfield code="b">maps ;</subfield>
<subfield code="c">26 cm.</subfield>
</datafield>
<datafield tag="500" ind1=" " ind2=" ">
<subfield code="a">Includes index.</subfield>
</datafield>
<datafield tag="650" ind1=" " ind2="0">
<subfield code="a">Attic Greek dialect</subfield>
<subfield code="9">7</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2="2">
<subfield code="3">Contributor biographical information</subfield>
<subfield code="u">http://www.loc.gov/catdir/bios/ucal051/92021731.html</subfield>
</datafield>
<datafield tag="856" ind1="4" ind2="2">
<subfield code="3">Publisher description</subfield>
<subfield code="u">http://www.loc.gov/catdir/description/ucal041/92021731.html</subfield>
</datafield>
<datafield tag="906" ind1=" " ind2=" ">
<subfield code="a">7</subfield>
<subfield code="b">cbc</subfield>
<subfield code="c">orignew</subfield>
<subfield code="d">1</subfield>
<subfield code="e">ocip</subfield>
<subfield code="f">19</subfield>
<subfield code="g">y-gencatlg</subfield>
</datafield>
<datafield tag="942" ind1=" " ind2=" ">
<subfield code="2">ddc</subfield>
<subfield code="c">BK</subfield>
</datafield>
<datafield tag="955" ind1=" " ind2=" ">
<subfield code="a">pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93</subfield>
</datafield>
<datafield tag="999" ind1=" " ind2=" ">
<subfield code="c">3</subfield>
<subfield code="d">3</subfield>
</datafield>
</record>|;
my $mij = q|{
"fields": [
{
"001": "2504398"
},
{
"005": "20200421093816.0"
},
{
"008": "920610s1993 caub s001 0 eng "
},
{
"010": {
"ind1": " ",
"subfields": [
{
"a": " 92021731 "
}
],
"ind2": " "
}
},
{
"020": {
"subfields": [
{
"a": "05200784382 (Test mij)"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"020": {
"subfields": [
{
"a": "05200784462 (Test mij)"
}
],
"ind1": " ",
"ind2": " "
}
},
{
"040": {
"subfields": [
{
"a": "DLC"
},
{
"c": "DLC"
},
{
"d": "DLC"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"041": {
"ind2": " ",
"subfields": [
{
"a": "enggrc"
}
],
"ind1": "0"
}
},
{
"050": {
"subfields": [
{
"a": "PA522"
},
{
"b": ".M38 1993"
}
],
"ind1": "0",
"ind2": "0"
}
},
{
"082": {
"subfields": [
{
"a": "480"
},
{
"2": "20"
}
],
"ind2": "0",
"ind1": "0"
}
},
{
"100": {
"ind2": " ",
"subfields": [
{
"a": "Mastronarde, Donald J."
},
{
"9": "389"
}
],
"ind1": "1"
}
},
{
"245": {
"ind1": "1",
"subfields": [
{
"a": "Introduction to Attic Greek (Using mij) /"
},
{
"c": "Donald J. Mastronarde."
}
],
"ind2": "0"
}
},
{
"260": {
"subfields": [
{
"a": "Berkeley :"
},
{
"b": "University of California Press,"
},
{
"c": "c1993."
}
],
"ind2": " ",
"ind1": " "
}
},
{
"300": {
"ind1": " ",
"subfields": [
{
"a": "ix, 425 p. :"
},
{
"b": "maps ;"
},
{
"c": "26 cm."
}
],
"ind2": " "
}
},
{
"500": {
"subfields": [
{
"a": "Includes index."
}
],
"ind1": " ",
"ind2": " "
}
},
{
"650": {
"subfields": [
{
"a": "Attic Greek dialect"
},
{
"9": "7"
}
],
"ind2": "0",
"ind1": " "
}
},
{
"856": {
"subfields": [
{
"3": "Contributor biographical information"
},
{
"u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html"
}
],
"ind2": "2",
"ind1": "4"
}
},
{
"856": {
"ind1": "4",
"subfields": [
{
"3": "Publisher description"
},
{
"u": "http://www.loc.gov/catdir/description/ucal041/92021731.html"
}
],
"ind2": "2"
}
},
{
"906": {
"subfields": [
{
"a": "7"
},
{
"b": "cbc"
},
{
"c": "orignew"
},
{
"d": "1"
},
{
"e": "ocip"
},
{
"f": "19"
},
{
"g": "y-gencatlg"
}
],
"ind1": " ",
"ind2": " "
}
},
{
"942": {
"subfields": [
{
"2": "ddc"
},
{
"c": "BK"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"955": {
"subfields": [
{
"a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"999": {
"subfields": [
{
"c": "3"
},
{
"d": "3"
}
],
"ind1": " ",
"ind2": " "
}
}
],
"leader": "01102pam a2200289 a 8500"
}|;
my $marc = q|01116pam a2200289 a 4500001000800000005001700008008004100025010001700066020002800083020002800111040001800139041001100157050002100168082001200189100003200201245007500233260005600308300003300364500002000397650002700417856009500444856008700539906004500626942001200671955013000683999001300813250439820221223213433.0920610s1993 caub s001 0 eng  a 92021731  a05200784384 (Test json) a05200784464 (Test json) aDLCcDLCdDLC0 aenggrc00aPA522b.M38 199300a4802201 aMastronarde, Donald J.938910aIntroduction to Attic Greek (Using usmarc) /cDonald J. Mastronarde. aBerkeley :bUniversity of California Press,cc1993. aix, 425 p. :bmaps ;c26 cm. aIncludes index. 0aAttic Greek dialect97423Contributor biographical informationuhttp://www.loc.gov/catdir/bios/ucal051/92021731.html423Publisher descriptionuhttp://www.loc.gov/catdir/description/ucal041/92021731.html a7bcbccorignewd1eocipf19gy-gencatlg 2ddccBK apc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93 c715d715|;
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber")
->status_is(403, 'Not enough permissions makes it return the right code');
# Add permissions
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 9,
code => 'edit_catalogue'
}
}
);
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml)
->status_is(200)
->json_has('/id');
$biblio = Koha::Biblios->find($biblionumber);
is($biblio->title, 'Introduction to Attic Greek (Using marcxml) /');
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode} => $mij)
->status_is(200)
->json_has('/id');
$biblio = Koha::Biblios->find($biblionumber);
is($biblio->title, 'Introduction to Attic Greek (Using mij) /');
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblionumber" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc)
->status_is(200)
->json_has('/id');
$biblio = Koha::Biblios->find($biblionumber);
is($biblio->title, 'Introduction to Attic Greek (Using usmarc) /');
$schema->storage->txn_rollback;
};
subtest 'list() tests' => sub {
plan tests => 17;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
$patron->discard_changes;
my $userid = $patron->userid;
t::lib::Mocks::mock_preference('marcflavour', 'UNIMARC');
my $title_with_diacritics = "L'insoutenable légèreté de l'être";
my $biblio = $builder->build_sample_biblio(
{
title => $title_with_diacritics,
author => "Milan Kundera"
}
);
my $record = $biblio->metadata->record;
$record->leader(' nam 3 4500');
$biblio->metadata->metadata($record->as_xml_record('UNIMARC'))->store;
my $biblio_id_1 = $biblio->id;
t::lib::Mocks::mock_preference('marcflavour', 'MARC21');
my $biblio_id_2 = $builder->build_sample_biblio->id;
my $query = encode_json( [ { biblio_id => $biblio_id_1 }, { biblio_id => $biblio_id_2 } ] );
$t->get_ok("//$userid:$password@/api/v1/biblios?q=$query")->status_is(403);
$patron->flags(4)->store;
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/weird+format' } )
->status_is(400);
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/json' } )->status_is(200);
my $result = $t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marcxml+xml' } )
->status_is(200)->tx->res->body;
my $encoded_title = Encode::encode( "UTF-8", $title_with_diacritics );
like( $result, qr/\Q$encoded_title/, "The title is not double encoded" );
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc-in-json' } )
->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'application/marc' } )->status_is(200);
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$query" => { Accept => 'text/plain' } )->status_is(200);
# DELETE any biblio with ISBN = TOMAS
Koha::Biblios->search({ 'biblioitem.isbn' => 'TOMAS' }, { join => [ 'biblioitem' ] })
->delete;
my $isbn_query = encode_json({ isbn => 'TOMAS' });
$biblio->biblioitem->set({ isbn => 'TOMAS' })->store;
$t->get_ok( "//$userid:$password@/api/v1/biblios?q=$isbn_query" =>
{ Accept => 'text/plain' } )
->status_is(200);
$schema->storage->txn_rollback;
};
subtest 'add_item() tests' => sub {
plan tests => 7;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $biblio = $builder->build_sample_biblio();
my $biblio_id = $biblio->biblionumber;
my $barcode = 'mybarcode';
my $matching_items = Koha::Items->search({ barcode => $barcode });
while (my $item = $matching_items->next) {
$item->delete;
}
$t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => { external_id => $barcode })
->status_is(403, 'Not enough permissions to create an item');
# Add permissions
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 9,
code => 'edit_catalogue'
}
}
);
$t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
external_id => $barcode,
})
->status_is(201, 'Item created')
->json_is('/biblio_id', $biblio_id);
my $item = $builder->build_sample_item();
$t->post_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items" => json => {
external_id => $item->barcode,
})
->status_is(409, 'Duplicate barcode');
$schema->storage->txn_rollback;
};
subtest 'update_item() tests' => sub {
plan tests => 7;
$schema->storage->txn_begin;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $item = $builder->build_sample_item({ replacementprice => 5 });
my $biblio_id = $item->biblionumber;
my $item_id = $item->itemnumber;
my $biblio = Koha::Biblios->find($item->biblionumber);
my $matching_items = Koha::Items->search({ barcode => $item->barcode });
while (my $mbcitem = $matching_items->next) {
$mbcitem->delete if $mbcitem->biblionumber != $item->biblionumber;
}
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => { external_id => 'something' })
->status_is(403, 'Not enough permissions to update an item');
# Add permissions
$builder->build(
{
source => 'UserPermission',
value => {
borrowernumber => $patron->borrowernumber,
module_bit => 9,
code => 'edit_catalogue'
}
}
);
my $other_item = $builder->build_sample_item();
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
external_id => $other_item->barcode,
})
->status_is(409, 'Barcode not unique');
$t->put_ok("//$userid:$password@/api/v1/biblios/$biblio_id/items/$item_id" => json => {
replacement_price => 30,
})
->status_is(200, 'Item updated')
->json_is('/replacement_price', 30);
$schema->storage->txn_rollback;
};
subtest 'merge() tests' => sub {
plan tests => 12;
$schema->storage->txn_begin;
my $mij_rec = q|{
"fields": [
{
"001": "2504398"
},
{
"005": "20200421093816.0"
},
{
"008": "920610s1993 caub s001 0 eng "
},
{
"010": {
"ind1": " ",
"subfields": [
{
"a": " 92021731 "
}
],
"ind2": " "
}
},
{
"020": {
"subfields": [
{
"a": "05200784462 (Test mij)"
}
],
"ind1": " ",
"ind2": " "
}
},
{
"040": {
"subfields": [
{
"a": "DLC"
},
{
"c": "DLC"
},
{
"d": "DLC"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"041": {
"ind2": " ",
"subfields": [
{
"a": "enggrc"
}
],
"ind1": "0"
}
},
{
"082": {
"subfields": [
{
"a": "480"
},
{
"2": "20"
}
],
"ind2": "0",
"ind1": "0"
}
},
{
"100": {
"ind2": " ",
"subfields": [
{
"a": "Mastronarde, Donald J."
},
{
"9": "389"
}
],
"ind1": "1"
}
},
{
"245": {
"ind1": "1",
"subfields": [
{
"a": "Introduction to Attic Greek (Using mij) /"
},
{
"c": "Donald J. Mastronarde."
}
],
"ind2": "0"
}
},
{
"260": {
"subfields": [
{
"a": "Berkeley :"
},
{
"b": "University of California Press,"
},
{
"c": "c1993."
}
],
"ind2": " ",
"ind1": " "
}
},
{
"300": {
"ind1": " ",
"subfields": [
{
"a": "ix, 425 p. :"
},
{
"b": "maps ;"
},
{
"c": "26 cm."
}
],
"ind2": " "
}
},
{
"650": {
"subfields": [
{
"a": "Attic Greek dialect"
},
{
"9": "7"
}
],
"ind2": "0",
"ind1": " "
}
},
{
"942": {
"subfields": [
{
"2": "ddc"
},
{
"c": "BK"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"955": {
"subfields": [
{
"a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93"
}
],
"ind2": " ",
"ind1": " "
}
},
{
"999": {
"subfields": [
{
"c": "3"
},
{
"d": "3"
}
],
"ind1": " ",
"ind2": " "
}
}
],
"leader": "01102pam a2200289 a 8500"
}|;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
my $password = 'thePassword123';
$patron->set_password( { password => $password, skip_validation => 1 } );
my $userid = $patron->userid;
my $title_1 = 'Title number 1';
my $title_2 = 'Title number 2';
my $biblio1 = $builder->build_sample_biblio( { title => $title_1 } );
my $biblio2 = $builder->build_sample_biblio( { title => $title_2 } );
my $biblio_id1 = $biblio1->biblionumber;
my $biblio_id2 = $biblio2->biblionumber;
my $json_input1 = '{ "biblio_id_to_merge": "' . $biblio_id2 . '" }';
$t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
{ 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input1 )
->status_is( 403, 'Not enough permissions to merge two bib records' );
# Add permissions
$patron->flags(516)->store;
$t->post_ok(
"//$userid:$password@/api/v1/biblios/$biblio_id1/merge" => { 'Content-Type' => 'application/weird+format' } =>
$json_input1 )->status_is( 400, 'Not correct headers' );
my $result =
$t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
{ 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input1 )
->status_is(200)->tx->res->body;
like( $result, qr/$title_1/, "Merged record has the correct title" );
unlike( $result, qr/$title_2/, "Merged record doesn't have the wrong title" );
my $biblio3 = $builder->build_sample_biblio( { title => 'Title number 3' } );
my $biblio_id3 = $biblio3->biblionumber;
my $json_input2 = '{ "biblio_id_to_merge": "' . $biblio_id3 . '",
"rules": "override_ext",
"datarecord": ' . $mij_rec . ' }';
$result =
$t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id1/merge" =>
{ 'Content-Type' => 'application/json', 'Accept' => 'application/marc-in-json' } => $json_input2 )
->status_is(200)->tx->res->body;
like( $result, qr/Using mij/, "Update with Marc-in-json record" );
unlike( $result, qr/$title_1/, "Change all record with dat in the 'datarecord' field" );
$schema->storage->txn_rollback;
}