Bugzilla – Attachment 55898 Details for
Bug 17371
Add routes for biblios [OMNIBUS]
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Bug 17371 - REST API: add CRUD for biblios
Bug-17371---REST-API-add-CRUD-for-biblios.patch (text/plain), 17.73 KB, created by
Benjamin Rokseth
on 2016-09-29 00:20:14 UTC
(
hide
)
Description:
Bug 17371 - REST API: add CRUD for biblios
Filename:
MIME Type:
Creator:
Benjamin Rokseth
Created:
2016-09-29 00:20:14 UTC
Size:
17.73 KB
patch
obsolete
>From 793f95f8cce24dc0db359bf479e750e5df253ed5 Mon Sep 17 00:00:00 2001 >From: Benjamin Rokseth <benjamin.rokseth@kul.oslo.kommune.no> >Date: Thu, 29 Sep 2016 02:16:45 +0200 >Subject: [PATCH] Bug 17371 - REST API: add CRUD for biblios > >Patch to add CRUD to biblios. > >Test plan: >1) add dependent bug 17004 >2) create some staff user with editcatalogue permission >3) play with API with routes explained in bug description >4) run test t/db_dependent/api/v1/biblios.t >--- > Koha/REST/V1/Biblio.pm | 173 ++++++++++++++++++++++++++++++++ > api/v1/swagger/definitions.json | 6 ++ > api/v1/swagger/definitions/biblio.json | 52 ++++++++++ > api/v1/swagger/definitions/biblios.json | 4 + > api/v1/swagger/parameters.json | 3 + > api/v1/swagger/parameters/biblio.json | 9 ++ > api/v1/swagger/paths.json | 12 +++ > api/v1/swagger/paths/biblios.json | 153 ++++++++++++++++++++++++++++ > t/db_dependent/api/v1/biblios.t | 90 +++++++++++++++++ > 9 files changed, 502 insertions(+) > create mode 100644 Koha/REST/V1/Biblio.pm > create mode 100644 api/v1/swagger/definitions/biblio.json > create mode 100644 api/v1/swagger/definitions/biblios.json > create mode 100644 api/v1/swagger/parameters/biblio.json > create mode 100644 api/v1/swagger/paths/biblios.json > create mode 100644 t/db_dependent/api/v1/biblios.t > >diff --git a/Koha/REST/V1/Biblio.pm b/Koha/REST/V1/Biblio.pm >new file mode 100644 >index 0000000..d936530 >--- /dev/null >+++ b/Koha/REST/V1/Biblio.pm >@@ -0,0 +1,173 @@ >+package Koha::REST::V1::Biblio; >+ >+# 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::Biblio qw( GetBiblioData AddBiblio ModBiblio DelBiblio ); >+use C4::Items qw ( AddItemBatchFromMarc ); >+use Koha::Biblios; >+use MARC::Record; >+use MARC::Batch; >+use MARC::File::USMARC; >+use MARC::File::XML; >+ >+use Data::Dumper; >+ >+sub get { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblio = &GetBiblioData($args->{biblionumber}); >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ return $c->$cb($biblio, 200); >+} >+ >+sub getitems { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblio = Koha::Biblios->find($args->{biblionumber}); >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ return $c->$cb({ biblio => $biblio->unblessed, items => $biblio->items->unblessed }, 200); >+} >+ >+sub getexpanded { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblio = Koha::Biblios->find($args->{biblionumber}); >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ my $expanded = $biblio->items->unblessed; >+ for my $item (@{$expanded}) { >+ >+ # we assume item is available by default >+ $item->{status} = "available"; >+ >+ if ($item->{onloan}) { >+ $item->{status} = "onloan" >+ } >+ >+ if ($item->{restricted}) { >+ $item->{status} = "restricted"; >+ } >+ >+ # mark as unavailable if notforloan, damaged, lost, or withdrawn >+ if ($item->{damaged} || $item->{itemlost} || $item->{withdrawn} || $item->{notforloan}) { >+ $item->{status} = "unavailable"; >+ } >+ >+ my $holds = Koha::Holds->search({itemnumber => $item->{itemnumber}})->unblessed; >+ >+ # mark as onhold if item marked as hold >+ if (scalar(@{$holds}) > 0) { >+ $item->{status} = "onhold"; >+ } >+ } >+ >+ return $c->$cb({ biblio => $biblio->unblessed, items => $expanded }, 200); >+} >+ >+sub add { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblionumber; >+ my $biblioitemnumber; >+ >+ my $body = $c->req->body; >+ unless ($body) { >+ return $c->$cb({error => "Missing MARCXML body"}, 400); >+ } >+ >+ my $record = eval {MARC::Record::new_from_xml( $body, "utf8", '')}; >+ if ($@) { >+ return $c->$cb({error => $@}, 400); >+ } else { >+ ( $biblionumber, $biblioitemnumber ) = &AddBiblio($record, ''); >+ } >+ if ($biblionumber) { >+ $c->res->headers->location($c->url_for('/api/v1/biblios/')->to_abs . $biblionumber); >+ my ( $itemnumbers, $errors ) = &AddItemBatchFromMarc( $record, $biblionumber, $biblioitemnumber, '' ); >+ unless (@{$errors}) { >+ return $c->$cb({biblionumber => $biblionumber, items => join(",", @{$itemnumbers})}, 201); >+ } else { >+ warn Dumper($errors); >+ return $c->$cb({error => "Error creating items, see Koha Logs for details.", biblionumber => $biblionumber, items => join(",", @{$itemnumbers})}, 400); >+ } >+ } else { >+ return $c->$cb({error => "unable to create record"}, 400); >+ } >+} >+ >+# NB: This will not update any items, Items should be a separate API route >+sub update { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblionumber = $args->{biblionumber}; >+ >+ my $biblio = Koha::Biblios->find($biblionumber); >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ >+ my $success; >+ my $body = $c->req->body; >+ my $record = eval {MARC::Record::new_from_xml( $body, "utf8", '')}; >+ if ($@) { >+ return $c->$cb({error => $@}, 400); >+ } else { >+ $success = &ModBiblio($record, $biblionumber, ''); >+ } >+ if ($success) { >+ $c->res->headers->location($c->url_for('/api/v1/biblios/')->to_abs . $biblionumber); >+ return $c->$cb({biblio => Koha::Biblios->find($biblionumber)->unblessed}, 200); >+ } else { >+ return $c->$cb({error => "unable to update record"}, 400); >+ } >+} >+ >+sub delete { >+ my ($c, $args, $cb) = @_; >+ >+ my $biblio = Koha::Biblios->find($args->{biblionumber}); >+ unless ($biblio) { >+ return $c->$cb({error => "Biblio not found"}, 404); >+ } >+ my @items = $biblio->items; >+ # Delete items first >+ my @item_errors = (); >+ foreach my $item (@items) { >+ my $res = $item->delete; >+ unless ($res eq 1) { >+ push @item_errors, $item->unblessed->{itemnumber}; >+ } >+ } >+ my $res = $biblio->delete; >+ if ($res eq '1') { >+ return $c->$cb({}, 200); >+ } elsif ($res eq '-1') { >+ return $c->$cb({error => "Not found. Error code: " . $res, items => @item_errors}, 404); >+ } else { >+ return $c->$cb({error => "Error code: " . $res, items => @item_errors}, 400); >+ } >+} >+ >+1; >diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json >index a17c793..76c1484 100644 >--- a/api/v1/swagger/definitions.json >+++ b/api/v1/swagger/definitions.json >@@ -13,5 +13,11 @@ > }, > "error": { > "$ref": "definitions/error.json" >+ }, >+ "biblios": { >+ "$ref": "definitions/biblios.json" >+ }, >+ "biblio": { >+ "$ref": "definitions/biblio.json" > } > } >diff --git a/api/v1/swagger/definitions/biblio.json b/api/v1/swagger/definitions/biblio.json >new file mode 100644 >index 0000000..db2f40b >--- /dev/null >+++ b/api/v1/swagger/definitions/biblio.json >@@ -0,0 +1,52 @@ >+{ >+ "type": "object", >+ "properties": { >+ "biblionumber": { >+ "$ref": "../x-primitives.json#/biblionumber" >+ }, >+ "author": { >+ "type": ["string", "null"], >+ "description": "statement of responsibility from MARC record (100$a in MARC21)" >+ }, >+ "title": { >+ "type": ["string", "null"], >+ "description": "title (without the subtitle) from the MARC record (245$a in MARC21)" >+ }, >+ "unititle": { >+ "type": ["string", "null"], >+ "description": "uniform title (without the subtitle) from the MARC record (240$a in MARC21)" >+ }, >+ "notes": { >+ "type": ["string", "null"], >+ "description": "values from the general notes field in the MARC record (500$a in MARC21) split by bar (|)" >+ }, >+ "serial": { >+ "type": ["string", "null"], >+ "description": "Boolean indicating whether biblio is for a serial" >+ }, >+ "seriestitle": { >+ "type": ["string", "null"], >+ "description": "" >+ }, >+ "copyrightdate": { >+ "type": ["string", "null"], >+ "description": "publication or copyright date from the MARC record" >+ }, >+ "timestamp": { >+ "type": ["string", "null"], >+ "description": "date and time this record was last touched" >+ }, >+ "datecreated": { >+ "type": ["string", "null"], >+ "description": "the date this record was added to Koha" >+ }, >+ "abstract": { >+ "type": ["string", "null"], >+ "description": "summary from the MARC record (520$a in MARC21)" >+ }, >+ "frameworkcode": { >+ "type": ["string", "null"], >+ "description": "framework used in cataloging this record" >+ } >+ } >+} >diff --git a/api/v1/swagger/definitions/biblios.json b/api/v1/swagger/definitions/biblios.json >new file mode 100644 >index 0000000..b0600e8 >--- /dev/null >+++ b/api/v1/swagger/definitions/biblios.json >@@ -0,0 +1,4 @@ >+{ >+ "type": "array", >+ "items": { "$ref": "biblio.json" } >+} >diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json >index 87380a8..1a61183 100644 >--- a/api/v1/swagger/parameters.json >+++ b/api/v1/swagger/parameters.json >@@ -19,5 +19,8 @@ > }, > "sessionidBodyParam": { > "$ref": "parameters/auth.json#/sessionidBodyParam" >+ }, >+ "biblionumberPathParam": { >+ "$ref": "parameters/biblio.json#/biblionumberPathParam" > } > } >diff --git a/api/v1/swagger/parameters/biblio.json b/api/v1/swagger/parameters/biblio.json >new file mode 100644 >index 0000000..1d1316a >--- /dev/null >+++ b/api/v1/swagger/parameters/biblio.json >@@ -0,0 +1,9 @@ >+{ >+ "biblionumberPathParam": { >+ "name": "biblionumber", >+ "in": "path", >+ "description": "Internal biblio identifier", >+ "required": true, >+ "type": "integer" >+ } >+} >diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json >index 2e8fd38..99f55b2 100644 >--- a/api/v1/swagger/paths.json >+++ b/api/v1/swagger/paths.json >@@ -13,5 +13,17 @@ > }, > "/patrons/{borrowernumber}": { > "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}" >+ }, >+ "/biblios": { >+ "$ref": "paths/biblios.json#/~1biblios" >+ }, >+ "/biblios/{biblionumber}": { >+ "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}" >+ }, >+ "/biblios/{biblionumber}/items": { >+ "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}~1items" >+ }, >+ "/biblios/{biblionumber}/expanded": { >+ "$ref": "paths/biblios.json#/~1biblios~1{biblionumber}~1expanded" > } > } >diff --git a/api/v1/swagger/paths/biblios.json b/api/v1/swagger/paths/biblios.json >new file mode 100644 >index 0000000..d58f205 >--- /dev/null >+++ b/api/v1/swagger/paths/biblios.json >@@ -0,0 +1,153 @@ >+{ >+ "/biblios/{biblionumber}": { >+ "get": { >+ "operationId": "getBiblio", >+ "tags": ["biblios"], >+ "parameters": [ >+ { "$ref": "../parameters.json#/biblionumberPathParam" } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A biblio record", >+ "schema": { "$ref": "../definitions.json#/biblio" } >+ >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ } >+ }, >+ "put": { >+ "operationId": "updateBiblio", >+ "tags": ["biblios"], >+ "parameters": [ >+ { "$ref": "../parameters.json#/biblionumberPathParam" } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "An updated biblio record" >+ }, >+ "400": { >+ "description": "Biblio update failed", >+ "schema": { "$ref": "../definitions.json#/error" } >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "editcatalogue": "1" >+ } >+ } >+ }, >+ "delete": { >+ "operationId": "deleteBiblio", >+ "tags": ["biblios"], >+ "parameters": [ >+ { "$ref": "../parameters.json#/biblionumberPathParam" } >+ ], >+ "produces": ["application/json"], >+ "responses": { >+ "200": { >+ "description": "Biblio record deleted successfully", >+ "schema": { >+ "type": "object" >+ } >+ }, >+ "400": { >+ "description": "Biblio deletion failed", >+ "schema": { "$ref": "../definitions.json#/error" } >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { "$ref": "../definitions.json#/error" } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "editcatalogue": "1" >+ } >+ } >+ } >+ }, >+ "/biblios/{biblionumber}/items": { >+ "get": { >+ "operationId": "getitemsByBiblio", >+ "tags": ["biblios", "items"], >+ "parameters": [ >+ { "$ref": "../parameters.json#/biblionumberPathParam" } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A biblio record with items" >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ } >+ } >+ }, >+ "/biblios/{biblionumber}/expanded": { >+ "get": { >+ "operationId": "getexpandedByBiblio", >+ "tags": ["biblios", "items", "item status"], >+ "parameters": [ >+ { "$ref": "../parameters.json#/biblionumberPathParam" } >+ ], >+ "produces": [ >+ "application/json" >+ ], >+ "responses": { >+ "200": { >+ "description": "A biblio record with items and item statuses" >+ }, >+ "404": { >+ "description": "Biblio not found", >+ "schema": { >+ "$ref": "../definitions.json#/error" >+ } >+ } >+ } >+ } >+ }, >+ "/biblios": { >+ "post": { >+ "operationId": "addBiblio", >+ "tags": ["biblios"], >+ "produces": ["application/json"], >+ "responses": { >+ "201": { >+ "description": "A new biblio record" >+ }, >+ "400": { >+ "description": "Unable to create biblio record", >+ "schema": { "$ref": "../definitions.json#/error" } >+ } >+ } >+ }, >+ "x-koha-authorization": { >+ "permissions": { >+ "editcatalogue": "1" >+ } >+ } >+ } >+} >diff --git a/t/db_dependent/api/v1/biblios.t b/t/db_dependent/api/v1/biblios.t >new file mode 100644 >index 0000000..08b9b7d >--- /dev/null >+++ b/t/db_dependent/api/v1/biblios.t >@@ -0,0 +1,90 @@ >+#!/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 t::lib::TestBuilder; >+ >+use Test::More tests => 3; >+use Test::Mojo; >+use Data::Dumper; >+use C4::Auth; >+use C4::Context; >+use Koha::Database; >+use MARC::File::XML ( BinaryEncoding => 'utf8', RecordFormat => 'UNIMARC' ); >+ >+BEGIN { >+ use_ok('Koha::Biblios'); >+} >+ >+my $schema = Koha::Database->schema; >+my $dbh = C4::Context->dbh; >+my $builder = t::lib::TestBuilder->new; >+ >+$ENV{REMOTE_ADDR} = '127.0.0.1'; >+my $t = Test::Mojo->new('Koha::REST::V1'); >+ >+$schema->storage->txn_begin; >+ >+my $file = MARC::File::XML->in( 't/db_dependent/Record/testrecords/marcxml_utf8.xml' ); >+my $record = $file->next(); >+my ( $biblionumber, $itemnumber ); >+ >+my $librarian = $builder->build({ >+ source => "Borrower", >+ value => { >+ categorycode => 'S', >+ branchcode => 'NPL', >+ flags => 1, # editcatalogue >+ }, >+}); >+ >+my $session = C4::Auth::get_session(''); >+$session->param('number', $librarian->{ borrowernumber }); >+$session->param('id', $librarian->{ userid }); >+$session->param('ip', '127.0.0.1'); >+$session->param('lasttime', time()); >+$session->flush; >+ >+subtest 'Create biblio' => sub { >+ plan tests => 5; >+ >+ my $tx = $t->ua->build_tx(POST => '/api/v1/biblios' => $record->as_xml()); >+ $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); >+ $tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+ $t->request_ok($tx) >+ ->status_is(201); >+ $biblionumber = $tx->res->json->{biblionumber}; >+ $itemnumber = $tx->res->json->{items}; >+ >+ $t->json_is('/biblionumber' => $biblionumber) >+ ->json_is('/items' => $itemnumber) >+ ->header_like(Location => qr/$biblionumber/, 'Location header contains biblionumber'); >+}; >+ >+subtest 'Delete biblio' => sub { >+ plan tests => 2; >+ >+ my $tx = $t->ua->build_tx(DELETE => "/api/v1/biblios/$biblionumber"); >+ $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); >+ $tx->req->cookies({name => 'CGISESSID', value => $session->id}); >+ $t->request_ok($tx) >+ ->status_is(200); >+}; >+ >+ >+$schema->storage->txn_rollback; >-- >2.1.4
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 17371
:
55898
|
58995
|
58996
|
64842