Bugzilla – Attachment 60218 Details for
Bug 16826
REST API: Add API routes for getting item availability and holdability
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 16826: Controllers for availability
Bug-16826-Controllers-for-availability.patch (text/plain), 38.74 KB, created by
Lari Taskula
on 2017-02-14 17:03:33 UTC
(
hide
)
Description:
Bug 16826: Controllers for availability
Filename:
MIME Type:
Creator:
Lari Taskula
Created:
2017-02-14 17:03:33 UTC
Size:
38.74 KB
patch
obsolete
>From 314cd89068eb35f3b6255d391b9fdd4f7751c58b Mon Sep 17 00:00:00 2001 >From: Lari Taskula <lari.taskula@jns.fi> >Date: Thu, 22 Dec 2016 15:44:36 +0200 >Subject: [PATCH] Bug 16826: Controllers for availability > >GET /availability/biblio/hold >GET /availability/biblio/search >GET /availability/item/checkout >GET /availability/item/hold >GET /availability/item/search > >Includes API tests. > >To test: >1. prove t/db_dependent/api/v1/availability.t >2. Use Swagger-UI or make direct GET requests to specified availability > endpoints, and make sure the returned data looks ok. >--- > Koha/REST/V1/BiblioAvailability.pm | 145 ++++++++ > Koha/REST/V1/ItemAvailability.pm | 187 ++++++++++ > t/db_dependent/api/v1/availability.t | 685 +++++++++++++++++++++++++++++++++++ > 3 files changed, 1017 insertions(+) > create mode 100644 Koha/REST/V1/BiblioAvailability.pm > create mode 100644 Koha/REST/V1/ItemAvailability.pm > create mode 100644 t/db_dependent/api/v1/availability.t > >diff --git a/Koha/REST/V1/BiblioAvailability.pm b/Koha/REST/V1/BiblioAvailability.pm >new file mode 100644 >index 0000000..8f09155 >--- /dev/null >+++ b/Koha/REST/V1/BiblioAvailability.pm >@@ -0,0 +1,145 @@ >+package Koha::REST::V1::BiblioAvailability; >+ >+# 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use C4::Auth qw( haspermission ); >+ >+use Koha::Exceptions; >+ >+use Koha::Availability::Hold; >+use Koha::Availability::Search; >+ >+use Try::Tiny; >+ >+sub search { >+ my ($c, $args, $cb) = @_; >+ >+ my @availabilities; >+ >+ return try { >+ my $biblios = $args->{'biblionumber'}; >+ foreach my $biblionumber (@$biblios) { >+ if (my $biblio = Koha::Biblios->find($biblionumber)) { >+ push @availabilities, Koha::Availability::Search->biblio({ >+ biblio => $biblio >+ })->in_opac->swaggerize; >+ } >+ } >+ return $c->$cb(\@availabilities, 200); >+ } >+ catch { >+ if ( $_->isa('DBIx::Class::Exception') ) { >+ return $c->$cb( { error => $_->{msg} }, 500 ); >+ } >+ else { >+ return $c->$cb( >+ { error => "Something went wrong, check the logs." }, 500 ); >+ } >+ }; >+} >+ >+sub hold { >+ my ($c, $args, $cb) = @_; >+ >+ my @availabilities; >+ my $user = $c->stash('koha.user'); >+ my $borrowernumber = $args->{'borrowernumber'}; >+ my $to_branch = $args->{'branchcode'}; >+ my $limit_items = $args->{'limit_items'}; >+ my $patron; >+ my $librarian; >+ >+ return try { >+ ($patron, $librarian) = _get_patron($c, $user, $borrowernumber); >+ >+ my $biblios = $args->{'biblionumber'}; >+ my $params = { >+ patron => $patron, >+ }; >+ >+ $params->{'to_branch'} = $to_branch if $to_branch; >+ $params->{'limit'} = $limit_items if $limit_items; >+ >+ foreach my $biblionumber (@$biblios) { >+ if (my $biblio = Koha::Biblios->find($biblionumber)) { >+ $params->{'biblio'} = $biblio; >+ my $availability = Koha::Availability::Hold->biblio($params); >+ unless ($librarian) { >+ push @availabilities, $availability->in_opac->swaggerize; >+ } else { >+ push @availabilities, $availability->in_intranet->swaggerize; >+ } >+ } >+ } >+ >+ return $c->$cb(\@availabilities, 200); >+ } >+ catch { >+ if ( $_->isa('DBIx::Class::Exception') ) { >+ return $c->$cb( { error => $_->{msg} }, 500 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::AuthenticationRequired')) { >+ return $c->$cb( >+ { error => "Authentication required." }, 401 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::NoPermission')) { >+ return $c->$cb({ >+ error => "Authorization failure. Missing required permission(s).", >+ required_permissions => $_->required_permissions}, 403 ); >+ } >+ else { >+ return $c->$cb( >+ { error => "Something went wrong, check the logs. $_" }, 500 ); >+ } >+ }; >+} >+ >+sub _get_patron { >+ my ($c, $user, $borrowernumber) = @_; >+ >+ my $patron; >+ my $librarian = 0; >+ >+ unless ($user) { >+ Koha::Exceptions::AuthenticationRequired->throw; >+ } >+ if (haspermission($user->userid, { borrowers => 1 })) { >+ $librarian = 1; >+ } >+ if ($borrowernumber) { >+ if ($borrowernumber == $user->borrowernumber) { >+ $patron = $user; >+ } else { >+ if ($librarian) { >+ $patron = Koha::Patrons->find($borrowernumber); >+ } else { >+ Koha::Exceptions::NoPermission->throw( >+ required_permissions => "borrowers" >+ ); >+ } >+ } >+ } else { >+ $patron = $user; >+ } >+ >+ return ($patron, $librarian); >+} >+ >+1; >diff --git a/Koha/REST/V1/ItemAvailability.pm b/Koha/REST/V1/ItemAvailability.pm >new file mode 100644 >index 0000000..6368f5a >--- /dev/null >+++ b/Koha/REST/V1/ItemAvailability.pm >@@ -0,0 +1,187 @@ >+package Koha::REST::V1::ItemAvailability; >+ >+# 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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Mojo::Base 'Mojolicious::Controller'; >+ >+use C4::Auth qw( haspermission ); >+ >+use Koha::Exceptions; >+ >+use Koha::Availability::Checkout; >+use Koha::Availability::Hold; >+use Koha::Availability::Search; >+ >+use Try::Tiny; >+ >+sub checkout { >+ my ($c, $args, $cb) = @_; >+ >+ my @availabilities; >+ my $user = $c->stash('koha.user'); >+ my $borrowernumber = $args->{'borrowernumber'}; >+ my $patron; >+ my $librarian; >+ >+ return try { >+ ($patron, $librarian) = _get_patron($c, $user, $borrowernumber); >+ >+ my $items = $args->{'itemnumber'}; >+ foreach my $itemnumber (@$items) { >+ if (my $item = Koha::Items->find($itemnumber)) { >+ push @availabilities, Koha::Availability::Checkout->item({ >+ patron => $patron, >+ item => $item >+ })->in_intranet->swaggerize; >+ } >+ } >+ return $c->$cb(\@availabilities, 200); >+ } >+ catch { >+ if ( $_->isa('DBIx::Class::Exception') ) { >+ return $c->$cb( { error => $_->{msg} }, 500 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::AuthenticationRequired')) { >+ return $c->$cb( >+ { error => "Authentication required." }, 401 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::NoPermission')) { >+ return $c->$cb({ >+ error => "Authorization failure. Missing required permission(s).", >+ required_permissions => $_->required_permissions}, 403 ); >+ } >+ else { >+ return $c->$cb( >+ { error => "Something went wrong, check the logs. $_" }, 500 ); >+ } >+ }; >+} >+ >+sub hold { >+ my ($c, $args, $cb) = @_; >+ >+ my @availabilities; >+ my $user = $c->stash('koha.user'); >+ my $borrowernumber = $args->{'borrowernumber'}; >+ my $to_branch = $args->{'branchcode'}; >+ my $patron; >+ my $librarian; >+ >+ return try { >+ ($patron, $librarian) = _get_patron($c, $user, $borrowernumber); >+ >+ my $items = $args->{'itemnumber'}; >+ my $params = { >+ patron => $patron, >+ }; >+ if ($to_branch) { >+ $params->{'to_branch'} = $to_branch; >+ } >+ foreach my $itemnumber (@$items) { >+ if (my $item = Koha::Items->find($itemnumber)) { >+ $params->{'item'} = $item; >+ my $availability = Koha::Availability::Hold->item($params); >+ unless ($librarian) { >+ push @availabilities, $availability->in_opac->swaggerize; >+ } else { >+ push @availabilities, $availability->in_intranet->swaggerize; >+ } >+ } >+ } >+ >+ return $c->$cb(\@availabilities, 200); >+ } >+ catch { >+ if ( $_->isa('DBIx::Class::Exception') ) { >+ return $c->$cb( { error => $_->{msg} }, 500 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::AuthenticationRequired')) { >+ return $c->$cb( >+ { error => "Authentication required." }, 401 ); >+ } >+ elsif ($_->isa('Koha::Exceptions::NoPermission')) { >+ return $c->$cb({ >+ error => "Authorization failure. Missing required permission(s).", >+ required_permissions => $_->required_permissions}, 403 ); >+ } >+ else { >+ return $c->$cb( >+ { error => "Something went wrong, check the logs. $_" }, 500 ); >+ } >+ }; >+} >+ >+sub search { >+ my ($c, $args, $cb) = @_; >+ >+ my @availabilities; >+ >+ return try { >+ my $items = $args->{'itemnumber'}; >+ foreach my $itemnumber (@$items) { >+ if (my $item = Koha::Items->find($itemnumber)) { >+ push @availabilities, Koha::Availability::Search->item({ >+ item => $item >+ })->in_opac->swaggerize; >+ } >+ } >+ return $c->$cb(\@availabilities, 200); >+ } >+ catch { >+ if ( $_->isa('DBIx::Class::Exception') ) { >+ return $c->$cb( { error => $_->{msg} }, 500 ); >+ } >+ else { >+ return $c->$cb( >+ { error => "Something went wrong, check the logs. $_" }, 500 ); >+ } >+ }; >+} >+ >+sub _get_patron { >+ my ($c, $user, $borrowernumber) = @_; >+ >+ my $patron; >+ my $librarian = 0; >+ >+ unless ($user) { >+ Koha::Exceptions::AuthenticationRequired->throw; >+ } >+ if (haspermission($user->userid, { borrowers => 1 })) { >+ $librarian = 1; >+ } >+ if ($borrowernumber) { >+ if ($borrowernumber == $user->borrowernumber) { >+ $patron = $user; >+ } else { >+ if ($librarian) { >+ $patron = Koha::Patrons->find($borrowernumber); >+ } else { >+ Koha::Exceptions::NoPermission->throw( >+ required_permissions => "borrowers" >+ ); >+ } >+ } >+ } else { >+ $patron = $user; >+ } >+ >+ return ($patron, $librarian); >+} >+ >+1; >diff --git a/t/db_dependent/api/v1/availability.t b/t/db_dependent/api/v1/availability.t >new file mode 100644 >index 0000000..1e23bff >--- /dev/null >+++ b/t/db_dependent/api/v1/availability.t >@@ -0,0 +1,685 @@ >+#!/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, write to the Free Software Foundation, Inc., >+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. >+ >+use Modern::Perl; >+ >+use Test::More tests => 2; >+use Test::Mojo; >+use Test::Warn; >+ >+use t::lib::TestBuilder; >+use t::lib::Mocks; >+ >+use C4::Auth; >+use Koha::Biblio::Availability; >+use Koha::Item::Availability; >+use Koha::Database; >+ >+require t::db_dependent::Koha::Availability::Helpers; >+ >+my $schema = Koha::Database->new->schema; >+my $builder = t::lib::TestBuilder->new; >+ >+# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling >+# this affects the other REST api tests >+t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); >+ >+my $remote_address = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+subtest '/availability/biblio' => sub { >+ plan tests => 2; >+ >+ subtest '/hold' => sub { >+ plan tests => 30; >+ >+ $schema->storage->txn_begin; >+ >+ set_default_circulation_rules(); >+ set_default_system_preferences(); >+ >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item( >+ Koha::Biblios->find($item->biblionumber), >+ Koha::Biblioitems->find($item->biblioitemnumber) >+ ); >+ my ($patron, $session_id) = create_user_and_session(); >+ $patron = Koha::Patrons->find($patron); >+ my $route = '/api/v1/availability/biblio/hold'; >+ my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); >+ >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&limit_items=1' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_has('/0/item_availabilities/0') >+ ->json_hasnt('/0/item_availabilities/1'); >+ >+ $patron->gonenoaddress('1')->store; >+ $item2->notforloan('1')->store; >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/availability/unavailabilities/Patron::GoneNoAddress' => {}) >+ ->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { >+ code => "Not For Loan", >+ status => 1, >+ }); >+ $patron->gonenoaddress('0')->store; >+ $item2->notforloan('0')->store; >+ >+ my $patron2 = build_a_test_patron(); >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&borrowernumber='.$patron2->borrowernumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(403); >+ >+ my $branch = Koha::Libraries->find( >+ $builder->build({ source => 'Branch' })->{'branchcode'}); >+ t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1); >+ t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype'); >+ C4::Circulation::CreateBranchTransferLimit( >+ $branch->branchcode, >+ $item->holdingbranch, >+ $item->effective_itemtype >+ ); >+ >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber.'&branchcode='.$branch->branchcode ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::CannotBeTransferred' => { >+ from_library => $item->holdingbranch, >+ to_library => $branch->branchcode,}); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest '/search' => sub { >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ set_default_circulation_rules(); >+ set_default_system_preferences(); >+ >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item( >+ Koha::Biblios->find($item->biblionumber), >+ Koha::Biblioitems->find($item->biblioitemnumber) >+ ); >+ my $route = '/api/v1/availability/biblio/search'; >+ my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); >+ >+ $item2->notforloan('1')->store; >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { >+ code => "Not For Loan", >+ status => 1, >+ }); >+ >+ $schema->storage->txn_rollback; >+ }; >+}; >+ >+subtest '/availability/item' => sub { >+ plan tests => 3; >+ >+ subtest '/hold' => sub { >+ plan tests => 17; >+ >+ $schema->storage->txn_begin; >+ >+ set_default_circulation_rules(); >+ set_default_system_preferences(); >+ >+ my $item = build_a_test_item(); >+ my ($patron, $session_id) = create_user_and_session(); >+ $patron = Koha::Patrons->find($patron); >+ my $route = '/api/v1/availability/item/hold'; >+ my $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true); >+ >+ $patron->gonenoaddress('1')->store; >+ $item->notforloan('1')->store; >+ $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/itemnumber' => $item->itemnumber) >+ ->json_is('/0/availability/unavailabilities/Patron::GoneNoAddress' => {}) >+ ->json_is('/0/availability/unavailabilities/Item::NotForLoan' => { >+ code => "Not For Loan", >+ status => 1, >+ }); >+ $patron->gonenoaddress('0')->store; >+ $item->notforloan('0')->store; >+ >+ my $patron2 = build_a_test_patron(); >+ $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&borrowernumber='.$patron2->borrowernumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(403); >+ >+ my $branch = Koha::Libraries->find( >+ $builder->build({ source => 'Branch' })->{'branchcode'}); >+ t::lib::Mocks::mock_preference('UseBranchTransferLimits', 1); >+ t::lib::Mocks::mock_preference('BranchTransferLimitsType', 'itemtype'); >+ C4::Circulation::CreateBranchTransferLimit( >+ $branch->branchcode, >+ $item->holdingbranch, >+ $item->effective_itemtype >+ ); >+ >+ $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&branchcode='.$branch->branchcode ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_is('/0/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/availability/unavailabilities/Item::CannotBeTransferred' => { >+ from_library => $item->holdingbranch, >+ to_library => $branch->branchcode,}); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest '/checkout' => sub { >+ plan tests => 12; >+ >+ $schema->storage->txn_begin; >+ >+ set_default_circulation_rules(); >+ set_default_system_preferences(); >+ >+ my $item = build_a_test_item(); >+ my ($patron, $session_id) = create_user_and_session(); >+ $patron = Koha::Patrons->find($patron); >+ my $route = '/api/v1/availability/item/checkout'; >+ my $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true); >+ >+ $item->notforloan('1')->store; >+ $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/itemnumber' => $item->itemnumber) >+ ->json_is('/0/availability/unavailabilities/Item::NotForLoan' => { >+ code => "Not For Loan", >+ status => 1, >+ }); >+ $item->notforloan('0')->store; >+ >+ my $patron2 = build_a_test_patron(); >+ $tx = $t->ua->build_tx( GET => $route . '?itemnumber='.$item->itemnumber.'&borrowernumber='.$patron2->borrowernumber ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(403); >+ >+ $schema->storage->txn_rollback; >+ }; >+ >+ subtest '/search' => sub { >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ set_default_circulation_rules(); >+ set_default_system_preferences(); >+ >+ my $item = build_a_test_item(); >+ my $item2 = build_a_test_item( >+ Koha::Biblios->find($item->biblionumber), >+ Koha::Biblioitems->find($item->biblioitemnumber) >+ ); >+ my $route = '/api/v1/availability/biblio/search'; >+ my $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->true); >+ >+ $item2->notforloan('1')->store; >+ $tx = $t->ua->build_tx( GET => $route . '?biblionumber='.$item->biblionumber ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx) >+ ->status_is(200) >+ ->json_has('/0/availability') >+ ->json_is('/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/0/itemnumber' => $item->itemnumber) >+ ->json_is('/0/item_availabilities/0/availability/available' => Mojo::JSON->true) >+ ->json_is('/0/item_availabilities/1/itemnumber' => $item2->itemnumber) >+ ->json_is('/0/item_availabilities/1/availability/available' => Mojo::JSON->false) >+ ->json_is('/0/item_availabilities/1/availability/unavailabilities/Item::NotForLoan' => { >+ code => "Not For Loan", >+ status => 1, >+ }); >+ >+ $schema->storage->txn_rollback; >+ }; >+}; >+ >+=head3 >+subtest '' => sub { >+ >+ plan tests => 18; >+ >+ $schema->storage->txn_begin; >+ >+ Koha::Cities->search->delete; >+ my ( $borrowernumber, $session_id ) = >+ create_user_and_session( { authorized => 0 } ); >+ >+ ## Authorized user tests >+ # No cities, so empty array should be returned >+ my $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is( [] ); >+ >+ my $city_country = 'France'; >+ my $city = $builder->build( >+ { source => 'City', value => { city_country => $city_country } } ); >+ >+ # One city created, should get returned >+ $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is( [$city] ); >+ >+ my $another_city = $builder->build( >+ { source => 'City', value => { city_country => $city_country } } ); >+ my $city_with_another_country = $builder->build( { source => 'City' } ); >+ >+ # Two cities created, they should both be returned >+ $tx = $t->ua->build_tx( GET => '/api/v1/cities' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200) >+ ->json_is( [ $city, $another_city, $city_with_another_country ] ); >+ >+ # Filtering works, two cities sharing city_country >+ $tx = >+ $t->ua->build_tx( GET => "/api/v1/cities?city_country=" . $city_country ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ my $result = >+ $t->request_ok($tx)->status_is(200)->json_is( [ $city, $another_city ] ); >+ >+ $tx = $t->ua->build_tx( >+ GET => "/api/v1/cities?city_name=" . $city->{city_name} ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is( [$city] ); >+ >+ # Warn on unsupported query parameter >+ $tx = $t->ua->build_tx( GET => '/api/v1/cities?city_blah=blah' ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400) >+ ->json_is( [{ path => '/query/city_blah', message => 'Malformed query string'}] ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'get() tests' => sub { >+ >+ plan tests => 6; >+ >+ $schema->storage->txn_begin; >+ >+ my $city = $builder->build( { source => 'City' } ); >+ my ( $borrowernumber, $session_id ) = >+ create_user_and_session( { authorized => 0 } ); >+ >+ my $tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $city->{cityid} ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is($city); >+ >+ my $non_existent_id = $city->{cityid} + 1; >+ $tx = $t->ua->build_tx( GET => "/api/v1/cities/" . $non_existent_id ); >+ $tx->req->cookies( { name => 'CGISESSID', value => $session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(404) >+ ->json_is( '/error' => 'City not found' ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'add() tests' => sub { >+ >+ plan tests => 17; >+ >+ $schema->storage->txn_begin; >+ >+ my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = >+ create_user_and_session( { authorized => 0 } ); >+ my ( $authorized_borrowernumber, $authorized_session_id ) = >+ create_user_and_session( { authorized => 1 } ); >+ my $city = { >+ city_name => "City Name", >+ city_state => "City State", >+ city_zipcode => "City Zipcode", >+ city_country => "City Country" >+ }; >+ >+ # Unauthorized attempt to write >+ my $tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $unauthorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(403); >+ >+ # Authorized attempt to write invalid data >+ my $city_with_invalid_field = { >+ city_blah => "City Blah", >+ city_state => "City State", >+ city_zipcode => "City Zipcode", >+ city_country => "City Country" >+ }; >+ >+ $tx = $t->ua->build_tx( >+ POST => "/api/v1/cities/" => json => $city_with_invalid_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: city_blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ # Authorized attempt to write >+ $tx = $t->ua->build_tx( POST => "/api/v1/cities/" => json => $city ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ my $cityid = $t->request_ok($tx)->status_is(200) >+ ->json_is( '/city_name' => $city->{city_name} ) >+ ->json_is( '/city_state' => $city->{city_state} ) >+ ->json_is( '/city_zipcode' => $city->{city_zipcode} ) >+ ->json_is( '/city_country' => $city->{city_country} ) >+ ->tx->res->json->{cityid}; >+ >+ # Authorized attempt to create with null id >+ $city->{cityid} = undef; >+ $tx = $t->ua->build_tx( >+ POST => "/api/v1/cities/" => json => $city ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400)->json_has('/errors'); >+ >+ # Authorized attempt to create with existing id >+ $city->{cityid} = $cityid; >+ $tx = $t->ua->build_tx( >+ POST => "/api/v1/cities/" => json => $city ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Read-only.", >+ path => "/body/cityid" >+ } >+ ] >+ ); >+ >+ $schema->storage->txn_rollback; >+}; >+ >+subtest 'update() tests' => sub { >+ >+ plan tests => 15; >+ >+ $schema->storage->txn_begin; >+ >+ my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = >+ create_user_and_session( { authorized => 0 } ); >+ my ( $authorized_borrowernumber, $authorized_session_id ) = >+ create_user_and_session( { authorized => 1 } ); >+ >+ my $city_id = $builder->build( { source => 'City' } )->{cityid}; >+ >+ # Unauthorized attempt to update >+ my $tx = $t->ua->build_tx( PUT => "/api/v1/cities/$city_id" => json => >+ { city_name => 'New unauthorized name change' } ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $unauthorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(403); >+ >+ # Attempt partial update on a PUT >+ my $city_with_missing_field = { >+ city_name => 'New name', >+ city_state => 'New state', >+ city_country => 'New country' >+ }; >+ >+ $tx = $t->ua->build_tx( >+ PUT => "/api/v1/cities/$city_id" => json => $city_with_missing_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400) >+ ->json_is( "/errors" => >+ [ { message => "Missing property.", path => "/body/city_zipcode" } ] >+ ); >+ >+ # Full object update on PUT >+ my $city_with_updated_field = { >+ city_name => "London", >+ city_state => "City State", >+ city_zipcode => "City Zipcode", >+ city_country => "City Country" >+ }; >+ >+ $tx = $t->ua->build_tx( >+ PUT => "/api/v1/cities/$city_id" => json => $city_with_updated_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->json_is( '/city_name' => 'London' ); >+ >+ # Authorized attempt to write invalid data >+ my $city_with_invalid_field = { >+ city_blah => "City Blah", >+ city_state => "City State", >+ city_zipcode => "City Zipcode", >+ city_country => "City Country" >+ }; >+ >+ $tx = $t->ua->build_tx( >+ PUT => "/api/v1/cities/$city_id" => json => $city_with_invalid_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(400)->json_is( >+ "/errors" => [ >+ { >+ message => "Properties not allowed: city_blah.", >+ path => "/body" >+ } >+ ] >+ ); >+ >+ my $non_existent_id = $city_id + 1; >+ $tx = >+ $t->ua->build_tx( PUT => "/api/v1/cities/$non_existent_id" => json => >+ $city_with_updated_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(404); >+ >+ $schema->storage->txn_rollback; >+ >+ # Wrong mathod (POST) >+ $city_with_updated_field->{cityid} = 2; >+ >+ $tx = $t->ua->build_tx( >+ POST => "/api/v1/cities/$city_id" => json => $city_with_updated_field ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(404); >+}; >+ >+subtest 'delete() tests' => sub { >+ >+ plan tests => 7; >+ >+ $schema->storage->txn_begin; >+ >+ my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = >+ create_user_and_session( { authorized => 0 } ); >+ my ( $authorized_borrowernumber, $authorized_session_id ) = >+ create_user_and_session( { authorized => 1 } ); >+ >+ my $city_id = $builder->build( { source => 'City' } )->{cityid}; >+ >+ # Unauthorized attempt to update >+ my $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $unauthorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(403); >+ >+ $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(200)->content_is(''); >+ >+ $tx = $t->ua->build_tx( DELETE => "/api/v1/cities/$city_id" ); >+ $tx->req->cookies( >+ { name => 'CGISESSID', value => $authorized_session_id } ); >+ $tx->req->env( { REMOTE_ADDR => $remote_address } ); >+ $t->request_ok($tx)->status_is(404); >+ >+ $schema->storage->txn_rollback; >+}; >+=cut >+sub create_user_and_session { >+ >+ my $args = shift; >+ my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0; >+ my $dbh = C4::Context->dbh; >+ >+ my $user = $builder->build( >+ { >+ source => 'Borrower', >+ value => { >+ flags => $flags, >+ debarred => undef, >+ debarredcomment => undef, >+ lost => undef, >+ gonenoaddress => undef, >+ dateexpiry => output_pref({ dt => dt_from_string()->add_duration( # expires in 100 days >+ DateTime::Duration->new(days => 100)), dateformat => 'iso', dateonly => 1 }), >+ dateofbirth => '1950-10-10', >+ } >+ } >+ ); >+ >+ # Create a session for the authorized user >+ my $session = C4::Auth::get_session(''); >+ $session->param( 'number', $user->{borrowernumber} ); >+ $session->param( 'id', $user->{userid} ); >+ $session->param( 'ip', '127.0.0.1' ); >+ $session->param( 'lasttime', time() ); >+ $session->flush; >+ >+ if ( $args->{authorized} ) { >+ $dbh->do( " >+ INSERT INTO user_permissions (borrowernumber,module_bit,code) >+ VALUES (?,3,'parameters_remaining_permissions')", undef, >+ $user->{borrowernumber} ); >+ } >+ >+ return ( $user->{borrowernumber}, $session->id ); >+} >+ >+ >+1; >-- >1.9.1
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
|
Splinter Review
Attachments on
bug 16826
:
53271
|
53276
|
53371
|
53672
|
53675
|
53813
|
53826
|
53827
|
53922
|
54053
|
56128
|
56129
|
56130
|
56295
|
57271
|
57321
|
57369
|
57370
|
57371
|
57373
|
57382
|
58456
|
58457
|
58458
|
60155
|
60156
|
60157
|
60218
|
60219
|
60267
|
60268
|
60269
|
62727