From 79caa12d3b3b101eb9bce0f63ce411946fa4313e Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Tue, 7 Dec 2021 16:19:11 +0100 Subject: [PATCH] Bug 28201: Add API route to create a biblio Example usage: POST /api/v1/biblios Accept: application/json { "marcxml": "...", "framework_id": "FA" } It can return data in the same formats as GET /api/v1/biblios/:biblio_id depending on the value of the Accept request header: - application/json - application/marcxml+xml - application/marc-in-json - application/marc - text/plain Test plan: 1. Try requesting this endpoint with your favorite API tool (I recommend https://github.com/frigus02/RESTer) 2. Run `prove t/db_dependent/api/v1/biblios/post.t` --- Koha/REST/V1/Biblios.pm | 54 +++++++++++++++++++++- api/v1/swagger/paths/biblios.yaml | 58 ++++++++++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + t/db_dependent/api/v1/biblios/post.t | 68 ++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 t/db_dependent/api/v1/biblios/post.t diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm index c74f5180c6..9f68f6aedb 100644 --- a/Koha/REST/V1/Biblios.pm +++ b/Koha/REST/V1/Biblios.pm @@ -21,7 +21,7 @@ use Mojo::Base 'Mojolicious::Controller'; use Koha::Biblios; use Koha::RecordProcessor; -use C4::Biblio qw( DelBiblio ); +use C4::Biblio qw( AddBiblio DelBiblio ); use List::MoreUtils qw( any ); use MARC::Record::MiJ; @@ -32,6 +32,58 @@ use Try::Tiny qw( catch try ); =head2 Methods +=head2 add + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + my $body = $c->validation->param('body'); + my $marcxml = $body->{marcxml}; + my $frameworkcode = $body->{framework_id} // ''; + + try { + my $record = MARC::Record->new_from_xml($marcxml); + my ($biblionumber) = AddBiblio($record, $frameworkcode); + + my $biblio = Koha::Biblios->find($biblionumber); + + $c->respond_to( + json => sub { + $c->render(json => $biblio->to_api) + }, + marcxml => sub { + $c->render(format => 'marcxml', text => $biblio->metadata->record->as_xml_record) + }, + mij => sub { + $c->render(format => 'mij', data => $biblio->metadata->record->to_mij) + }, + marc => sub { + $c->render(format => 'marc', text => $biblio->metadata->record->as_usmarc) + }, + txt => sub { + $c->render(format => 'text/plain', text => $biblio->metadata->record->as_formatted) + }, + any => sub { + $c->render( + status => 406, + json => [ + "application/json", + "application/marcxml+xml", + "application/marc-in-json", + "application/marc", + "text/plain", + ], + ) + } + ); + } + catch { + $c->unhandled_exception($_); + }; +} + =head3 get Controller function that handles retrieving a single biblio object diff --git a/api/v1/swagger/paths/biblios.yaml b/api/v1/swagger/paths/biblios.yaml index 60e4e951a4..49aeec7f9b 100644 --- a/api/v1/swagger/paths/biblios.yaml +++ b/api/v1/swagger/paths/biblios.yaml @@ -1,4 +1,62 @@ --- +"/biblios": + post: + x-mojo-to: Biblios#add + operationId: addBiblio + tags: + - biblios + summary: Add biblio + parameters: + - name: body + in: body + required: true + schema: + type: object + required: + - marcxml + properties: + marcxml: + type: string + framework_id: + type: string + additionalProperties: false + consumes: + - application/json + produces: + - application/json + - application/marcxml+xml + - application/marc-in-json + - application/marc + - text/plain + responses: + "201": + description: A biblio + "401": + description: Authentication required + schema: + $ref: ../definitions/error.yaml + "403": + description: Access forbidden + schema: + $ref: ../definitions/error.yaml + "406": + description: Not acceptable + schema: + type: array + description: Accepted content-types + items: + type: string + "500": + description: Internal server error + schema: + $ref: ../definitions/error.yaml + "503": + description: Under maintenance + schema: + $ref: ../definitions/error.yaml + x-koha-authorization: + permissions: + catalogue: "1" "/biblios/{biblio_id}": get: x-mojo-to: Biblios#get diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 1ec3a5687d..98ba7a3823 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -93,6 +93,8 @@ paths: $ref: "./paths/advancededitormacros.yaml#/~1advanced_editor~1macros~1{advancededitormacro_id}" "/article_requests/{article_request_id}": $ref: "./paths/article_requests.yaml#/~1article_requests~1{article_request_id}" + "/biblios": + $ref: "./paths/biblios.yaml#/~1biblios" "/biblios/{biblio_id}": $ref: "./paths/biblios.yaml#/~1biblios~1{biblio_id}" "/biblios/{biblio_id}/checkouts": diff --git a/t/db_dependent/api/v1/biblios/post.t b/t/db_dependent/api/v1/biblios/post.t new file mode 100644 index 0000000000..90dff14aaf --- /dev/null +++ b/t/db_dependent/api/v1/biblios/post.t @@ -0,0 +1,68 @@ +#!/usr/bin/env perl + +use Modern::Perl; + +use Test::More tests => 17; +use Test::Mojo; + +use t::lib::Mocks; +use t::lib::TestBuilder; + +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'); + +$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({ + title => 'The unbearable lightness of being', + author => 'Milan Kundera' +}); + +my $url = "//$userid:$password@/api/v1/biblios"; +my $marcxml = $biblio->metadata->record->as_xml_record; + +$t->post_ok($url, json => { marcxml => $marcxml }) + ->status_is(403); + +$patron->flags(4)->store; + +$t->post_ok($url, { Accept => 'application/weird+format' }, json => { marcxml => $marcxml }) + ->status_is(406) + ->json_is( [ "application/json", + "application/marcxml+xml", + "application/marc-in-json", + "application/marc", + "text/plain" ] ); + +$t->post_ok($url, { Accept => 'application/json' }, json => { marcxml => $marcxml }) + ->status_is(200) + ->json_is( '/title', 'The unbearable lightness of being' ) + ->json_is( '/author', 'Milan Kundera' ); + +$t->post_ok($url, { Accept => 'application/marcxml+xml' }, json => { marcxml => $marcxml }) + ->status_is(200); + +$t->post_ok($url, { Accept => 'application/marc-in-json' }, json => { marcxml => $marcxml }) + ->status_is(200); + +$t->post_ok($url, { Accept => 'application/marc' }, json => { marcxml => $marcxml }) + ->status_is(200); + +$t->post_ok($url, { Accept => 'text/plain' }, json => { marcxml => $marcxml }) + ->status_is(200); + +$schema->storage->txn_rollback; -- 2.30.2