From 5ec673c7908864e31fa81a95c2ebbf81b35c6359 Mon Sep 17 00:00:00 2001 From: Lari Taskula Date: Mon, 27 Jun 2016 16:03:49 +0300 Subject: [PATCH] Bug 16825: Add API route for getting an item GET /api/v1/items/{itemnumber} Gets one Item This patch adds route to get one item from koha.items table. This table has a column "itemnotes_nonpublic", which should not be returned if the user has no catalogue permission. To test: 1. Open a browser tab on Koha staff and log in (to create CGISESSID cookie). 2. Send GET request to http://yourlibrary/api/v1/items/YYY where YYY is an existing itemnumber. 3. Make sure the returned data is correct. 4. Run unit tests in t/db_dependent/api/v1/items.t Signed-off-by: jirislav --- Koha/REST/V1/Item.pm | 45 +++++++++++ api/v1/definitions/index.json | 1 + api/v1/definitions/item.json | 177 ++++++++++++++++++++++++++++++++++++++++++ api/v1/swagger.json | 32 ++++++++ t/db_dependent/api/v1/items.t | 117 ++++++++++++++++++++++++++++ 5 files changed, 372 insertions(+) create mode 100644 Koha/REST/V1/Item.pm create mode 100644 api/v1/definitions/item.json create mode 100644 t/db_dependent/api/v1/items.t diff --git a/Koha/REST/V1/Item.pm b/Koha/REST/V1/Item.pm new file mode 100644 index 0000000..ccb6502 --- /dev/null +++ b/Koha/REST/V1/Item.pm @@ -0,0 +1,45 @@ +package Koha::REST::V1::Item; + +# 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 Mojo::JSON; + +use C4::Auth; + +use Koha::Items; + +sub get { + my ($c, $args, $cb) = @_; + + my $itemnumber = $c->param('itemnumber'); + my $item = Koha::Items->find($itemnumber); + unless ($item) { + return $c->$cb({error => "Item not found"}, 404); + } + + # Hide non-public itemnotes if user has no staff access + my $user = $c->stash('koha.user'); + unless ($user && haspermission($user->userid, {catalogue => 1})) { + $item->set({ itemnotes_nonpublic => undef }); + } + + return $c->$cb($item->unblessed, 200); +} + +1; diff --git a/api/v1/definitions/index.json b/api/v1/definitions/index.json index 3f69544..8baf7e3 100644 --- a/api/v1/definitions/index.json +++ b/api/v1/definitions/index.json @@ -2,5 +2,6 @@ "patron": { "$ref": "patron.json" }, "holds": { "$ref": "holds.json" }, "hold": { "$ref": "hold.json" }, + "item": { "$ref": "item.json" }, "error": { "$ref": "error.json" } } diff --git a/api/v1/definitions/item.json b/api/v1/definitions/item.json new file mode 100644 index 0000000..9c43f85 --- /dev/null +++ b/api/v1/definitions/item.json @@ -0,0 +1,177 @@ +{ + "type": "object", + "properties": { + "itemnumber": { + "type": "string", + "description": "internally assigned item identifier" + }, + "biblionumber": { + "type": "string", + "description": "internally assigned biblio identifier" + }, + "biblioitemnumber": { + "type": "string", + "description": "internally assigned biblio item identifier" + }, + "barcode": { + "type": ["string", "null"], + "description": "item barcode" + }, + "dateaccessioned": { + "type": ["string", "null"], + "description": "date the item was acquired or added to Koha" + }, + "booksellerid": { + "type": ["string", "null"], + "description": "where the item was purchased" + }, + "homebranch": { + "type": ["string", "null"], + "description": "library that owns this item" + }, + "price": { + "type": ["string", "null"], + "description": "purchase price" + }, + "replacementprice": { + "type": ["string", "null"], + "description": "cost the library charges to replace the item if it has been marked lost" + }, + "replacementpricedate": { + "type": ["string", "null"], + "description": "the date the price is effective from" + }, + "datelastborrowed": { + "type": ["string", "null"], + "description": "the date the item was last checked out/issued" + }, + "datelastseen": { + "type": ["string", "null"], + "description": "the date the item was last see (usually the last time the barcode was scanned or inventory was done)" + }, + "stack": { + "type": ["string", "null"], + "description": "?" + }, + "notforloan": { + "type": "string", + "description": "authorized value defining why this item is not for loan" + }, + "damaged": { + "type": "string", + "description": "authorized value defining this item as damaged" + }, + "itemlost": { + "type": "string", + "description": "authorized value defining this item as lost" + }, + "itemlost_on": { + "type": ["string", "null"], + "description": "the date and time an item was last marked as lost, NULL if not lost" + }, + "withdrawn": { + "type": "string", + "description": "authorized value defining this item as withdrawn" + }, + "withdrawn_on": { + "type": ["string", "null"], + "description": "the date and time an item was last marked as withdrawn, NULL if not withdrawn" + }, + "itemcallnumber": { + "type": ["string", "null"], + "description": "call number for this item" + }, + "coded_location_qualifier": { + "type": ["string", "null"], + "description": "coded location qualifier" + }, + "issues": { + "type": ["string", "null"], + "description": "number of times this item has been checked out/issued" + }, + "renewals": { + "type": ["string", "null"], + "description": "number of times this item has been renewed" + }, + "reserves": { + "type": ["string", "null"], + "description": "number of times this item has been placed on hold/reserved" + }, + "restricted": { + "type": ["string", "null"], + "description": "authorized value defining use restrictions for this item" + }, + "itemnotes": { + "type": ["string", "null"], + "description": "public notes on this item" + }, + "itemnotes_nonpublic": { + "type": ["string", "null"], + "description": "non-public notes on this item" + }, + "holdingbranch": { + "type": ["string", "null"], + "description": "library that is currently in possession item" + }, + "paidfor": { + "type": ["string", "null"], + "description": "?" + }, + "timestamp": { + "type": "string", + "description": "date and time this item was last altered" + }, + "location": { + "type": ["string", "null"], + "description": "authorized value for the shelving location for this item" + }, + "permanent_location": { + "type": ["string", "null"], + "description": "linked to the CART and PROC temporary locations feature, stores the permanent shelving location" + }, + "onloan": { + "type": ["string", "null"], + "description": "defines if item is checked out (NULL for not checked out, and checkout date for checked out)" + }, + "cn_source": { + "type": ["string", "null"], + "description": "classification source used on this item" + }, + "cn_sort": { + "type": ["string", "null"], + "description": "?" + }, + "ccode": { + "type": ["string", "null"], + "description": "authorized value for the collection code associated with this item" + }, + "materials": { + "type": ["string", "null"], + "description": "materials specified" + }, + "uri": { + "type": ["string", "null"], + "description": "URL for the item" + }, + "itype": { + "type": ["string", "null"], + "description": "itemtype defining the type for this item" + }, + "more_subfields_xml": { + "type": ["string", "null"], + "description": "additional 952 subfields in XML format" + }, + "enumchron": { + "type": ["string", "null"], + "description": "serial enumeration/chronology for the item" + }, + "copynumber": { + "type": ["string", "null"], + "description": "copy number" + }, + "stocknumber": { + "type": ["string", "null"], + "description": "inventory number" + } + } +} diff --git a/api/v1/swagger.json b/api/v1/swagger.json index 1488aba..fe63a5b 100644 --- a/api/v1/swagger.json +++ b/api/v1/swagger.json @@ -231,6 +231,31 @@ } } } + }, + "/items/{itemnumber}": { + "get": { + "operationId": "getItem", + "tags": ["items"], + "parameters": [ + { "$ref": "#/parameters/itemnumberPathParam" } + ], + "consumes": ["application/json"], + "produces": ["application/json"], + "responses": { + "200": { + "description": "An item", + "schema": { "$ref": "#/definitions/item" } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { "$ref": "#/definitions/error" } + }, + "404": { + "description": "Item not found", + "schema": { "$ref": "#/definitions/error" } + } + } + } } }, "definitions": { @@ -250,6 +275,13 @@ "description": "Internal hold identifier", "required": true, "type": "integer" + }, + "itemnumberPathParam": { + "name": "itemnumber", + "in": "path", + "description": "Internal item identifier", + "required": true, + "type": "integer" } } } diff --git a/t/db_dependent/api/v1/items.t b/t/db_dependent/api/v1/items.t new file mode 100644 index 0000000..0536e0f --- /dev/null +++ b/t/db_dependent/api/v1/items.t @@ -0,0 +1,117 @@ +#!/usr/bin/env perl + +# Copyright 2016 Koha-Suomi +# +# 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 => 12; +use Test::Mojo; +use t::lib::TestBuilder; + +use C4::Auth; +use C4::Biblio; +use C4::Context; +use C4::Items; + +use Koha::Database; +use Koha::Patron; +use Koha::Items; + +my $builder = t::lib::TestBuilder->new(); + +my $dbh = C4::Context->dbh; +$dbh->{AutoCommit} = 0; +$dbh->{RaiseError} = 1; + +$ENV{REMOTE_ADDR} = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +my $categorycode = $builder->build({ source => 'Category' })->{ categorycode }; +my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; +my $borrower = $builder->build({ + source => 'Borrower', + value => { + branchcode => $branchcode, + categorycode => $categorycode, + flags => 16, + } +}); + +my $librarian = $builder->build({ + source => "Borrower", + value => { + categorycode => $categorycode, + branchcode => $branchcode, + flags => 4, + }, +}); + +my ($session, $session2) = create_session($borrower, $librarian); + +my $biblio = $builder->build({ + source => 'Biblio' +}); +my $biblionumber = $biblio->{biblionumber}; +my $item = $builder->build({ + source => 'Item', + value => { + biblionumber => $biblionumber, + } +}); +my $itemnumber = $item->{itemnumber}; + +my $nonExistentItemnumber = -14362719; +my $tx = $t->ua->build_tx(GET => "/api/v1/items/$nonExistentItemnumber"); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$t->request_ok($tx) + ->status_is(404); + +$tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber"); +$tx->req->cookies({name => 'CGISESSID', value => $session->id}); +$t->request_ok($tx) + ->status_is(200) + ->json_is('/itemnumber' => $itemnumber) + ->json_is('/biblionumber' => $biblionumber) + ->json_is('/itemnotes_nonpublic' => undef); + +$tx = $t->ua->build_tx(GET => "/api/v1/items/$itemnumber"); +$tx->req->cookies({name => 'CGISESSID', value => $session2->id}); +$t->request_ok($tx) + ->status_is(200) + ->json_is('/itemnumber' => $itemnumber) + ->json_is('/biblionumber' => $biblionumber) + ->json_is('/itemnotes_nonpublic' => $item->{itemnotes_nonpublic}); + +$dbh->rollback; + +sub create_session { + my (@borrowers) = @_; + + my @sessions; + foreach $borrower (@borrowers) { + my $session = C4::Auth::get_session(''); + $session->param('number', $borrower->{borrowernumber}); + $session->param('id', $borrower->{userid}); + $session->param('ip', '127.0.0.1'); + $session->param('lasttime', time()); + $session->flush; + push @sessions, $session; + } + + return @sessions; +} -- 2.1.4