From 8fe688cf1b389890fe2b0350adcd4dd12b55e23b Mon Sep 17 00:00:00 2001 From: Agustin Moyano Date: Tue, 6 Dec 2022 17:07:02 -0300 Subject: [PATCH] Bug 31794: Add REST endpoint to get an authority To test: 1. Apply patch 2. Set RESTBasicAuth preference to true 3. Get the id of an authority 4. Make a GET request to /api/v1/authorities/{authid} with one of the following content type header - application/json - application/marcxml+xml - application/marc-in-json - application/marc - text/plain 5. Check that you get the authority in the corresponding format 6. Sign off Signed-off-by: David Nind --- Koha/Authority.pm | 28 +++++-- Koha/REST/V1/Authorities.pm | 102 +++++++++++++++++++++++ api/v1/swagger/paths/authorities.yaml | 52 ++++++++++++ api/v1/swagger/swagger.yaml | 8 ++ t/db_dependent/Koha/Authorities.t | 38 ++++++++- t/db_dependent/api/v1/authorities.t | 112 ++++++++++++++++++++++++++ 6 files changed, 331 insertions(+), 9 deletions(-) create mode 100644 Koha/REST/V1/Authorities.pm create mode 100644 api/v1/swagger/paths/authorities.yaml create mode 100755 t/db_dependent/api/v1/authorities.t diff --git a/Koha/Authority.pm b/Koha/Authority.pm index ff45d20a3d..828d4c75b2 100644 --- a/Koha/Authority.pm +++ b/Koha/Authority.pm @@ -95,8 +95,7 @@ sub controlled_indicators { ? 'UNIMARCAUTH' : 'MARC21'; if( !$record ) { - $record = MARC::Record->new_from_xml( - $self->marcxml, 'UTF-8', $flavour ); + $record = $self->record; } if( !$self->{_report_tag} ) { @@ -125,12 +124,7 @@ Return a list of identifiers of the authors which are in 024$2$a sub get_identifiers { my ( $self, $params ) = @_; - my $flavour = - C4::Context->preference('marcflavour') eq 'UNIMARC' - ? 'UNIMARCAUTH' - : 'MARC21'; - my $record = - MARC::Record->new_from_xml( $self->marcxml, 'UTF-8', $flavour ); + my $record = $self->record; my @identifiers; for my $field ( $record->field('024') ) { @@ -143,6 +137,24 @@ sub get_identifiers { return \@identifiers; } +=head3 record + + my $record = $authority->record() + +Return the MARC::Record for this authority + +=cut + +sub record { + my ( $self ) = @_; + + my $flavour = + C4::Context->preference('marcflavour') eq 'UNIMARC' + ? 'UNIMARCAUTH' + : 'MARC21'; + return MARC::Record->new_from_xml( $self->marcxml, 'UTF-8', $flavour ); +} + =head2 Class Methods =head3 type diff --git a/Koha/REST/V1/Authorities.pm b/Koha/REST/V1/Authorities.pm new file mode 100644 index 0000000000..5266fab705 --- /dev/null +++ b/Koha/REST/V1/Authorities.pm @@ -0,0 +1,102 @@ +package Koha::REST::V1::Authorities; + +# 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 . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::Authorities; + +use List::MoreUtils qw( any ); +use MARC::Record::MiJ; + +use Try::Tiny qw( catch try ); + +=head1 API + +=head2 Methods + +=head3 get + +Controller function that handles retrieving a single authority object + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + my $authority = Koha::Authorities->find( { authid => $c->validation->param('authority_id') } ); + unless ( $authority ) { + return $c->render( + status => 404, + openapi => { + error => "Object not found." + } + ); + } + + return try { + + if ( $c->req->headers->accept =~ m/application\/json/ ) { + return $c->render( + status => 200, + json => $authority->to_api + ); + } + else { + my $record = $authority->record; + + $c->respond_to( + marcxml => { + status => 200, + format => 'marcxml', + text => $record->as_xml_record + }, + mij => { + status => 200, + format => 'mij', + data => $record->to_mij + }, + marc => { + status => 200, + format => 'marc', + text => $record->as_usmarc + }, + txt => { + status => 200, + format => 'text/plain', + text => $record->as_formatted + }, + any => { + status => 406, + openapi => [ + "application/json", + "application/marcxml+xml", + "application/marc-in-json", + "application/marc", + "text/plain" + ] + } + ); + } + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/api/v1/swagger/paths/authorities.yaml b/api/v1/swagger/paths/authorities.yaml new file mode 100644 index 0000000000..c5f1044b72 --- /dev/null +++ b/api/v1/swagger/paths/authorities.yaml @@ -0,0 +1,52 @@ +--- +"/authorities/{authority_id}": + get: + x-mojo-to: Authorities#get + operationId: getAuthority + tags: + - authorities + summary: Get authority + parameters: + - $ref: "../swagger.yaml#/parameters/authority_id_pp" + produces: + - application/json + - application/marcxml+xml + - application/marc-in-json + - application/marc + - text/plain + responses: + "200": + description: An authority + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Authority not found + schema: + $ref: "../swagger.yaml#/definitions/error" + "406": + description: Not acceptable + schema: + type: array + description: Accepted content-types + items: + type: string + "500": + description: | + Internal server error. Possible `error_code` attribute values: + + * `internal_server_error` + schema: + $ref: "../swagger.yaml#/definitions/error" + "503": + description: Under maintenance + schema: + $ref: "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "1" diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 696a73be8b..f71f1425b2 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -137,6 +137,8 @@ paths: $ref: paths/auth.yaml#/~1auth~1identity_providers~1{identity_provider_id}~1domains "/auth/identity_providers/{identity_provider_id}/domains/{identity_provider_domain_id}": $ref: paths/auth.yaml#/~1auth~1identity_providers~1{identity_provider_id}~1domains~1{identity_provider_domain_id} + "/authorities/{authority_id}": + $ref: paths/authorities.yaml#/~1authorities~1{authority_id} "/biblios/{biblio_id}": $ref: "./paths/biblios.yaml#/~1biblios~1{biblio_id}" "/biblios/{biblio_id}/checkouts": @@ -336,6 +338,12 @@ parameters: name: agreement_period_id required: true type: integer + authority_id_pp: + description: Authority identifier + in: path + name: authority_id + required: true + type: integer identity_provider_id_pp: description: Authentication provider internal identifier in: path diff --git a/t/db_dependent/Koha/Authorities.t b/t/db_dependent/Koha/Authorities.t index 5dbfaaa497..699ee1c34a 100755 --- a/t/db_dependent/Koha/Authorities.t +++ b/t/db_dependent/Koha/Authorities.t @@ -19,7 +19,7 @@ use Modern::Perl; -use Test::More tests => 8; +use Test::More tests => 9; use MARC::Field; use MARC::File::XML; use MARC::Record; @@ -285,4 +285,40 @@ subtest 'get_identifiers' => sub { ); }; +subtest 'record tests' => sub { + plan tests => 3; + + t::lib::Mocks::mock_preference( 'marcflavour', 'MARC21' ); + my $record = MARC::Record->new(); + $record->add_fields( + [ + '100', ' ', ' ', + a => 'Lastname, Firstname', + b => 'b', + c => 'c', + i => 'i' + ], + [ + '024', '', '', + a => '0000-0002-1234-5678', + 2 => 'orcid', + 6 => 'https://orcid.org/0000-0002-1234-5678' + ], + [ + '024', '', '', + a => '01234567890', + 2 => 'scopus', + 6 => 'https://www.scopus.com/authid/detail.uri?authorId=01234567890' + ], + ); + my $authid = C4::AuthoritiesMarc::AddAuthority($record, undef, 'PERSO_NAME'); + my $authority = Koha::Authorities->find($authid); + my $authority_record = $authority->record; + is ($authority_record->field('100')->subfield('a'), 'Lastname, Firstname'); + my @fields_024 = $authority_record->field('024'); + is ($fields_024[0]->subfield('a'), '0000-0002-1234-5678'); + is ($fields_024[1]->subfield('a'), '01234567890'); + +}; + $schema->storage->txn_rollback; diff --git a/t/db_dependent/api/v1/authorities.t b/t/db_dependent/api/v1/authorities.t new file mode 100755 index 0000000000..5edc612838 --- /dev/null +++ b/t/db_dependent/api/v1/authorities.t @@ -0,0 +1,112 @@ +#!/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 . + +use Modern::Perl; + +use utf8; +use Encode; + +use Test::More tests => 1; +use Test::MockModule; +use Test::Mojo; +use Test::Warn; + +use t::lib::Mocks; +use t::lib::TestBuilder; + +use C4::Auth; + +use Koha::Authorities; + +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 => 20; + + $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 $authority = $builder->build_object({ 'class' => 'Koha::Authorities', value => { + marcxml => q| + + 1001 + + 102 + My Corporation + +| + } }); + + $t->get_ok("//$userid:$password@/api/v1/authorities/" . $authority->authid) + ->status_is(403); + + $patron->flags(4)->store; + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/weird+format' } ) + ->status_is(400); + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/json' } ) + ->status_is(200) + ->json_is( '/authid', $authority->authid ) + ->json_is( '/authtypecode', $authority->authtypecode ); + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/marcxml+xml' } ) + ->status_is(200); + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/marc-in-json' } ) + ->status_is(200); + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/marc' } ) + ->status_is(200); + + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'text/plain' } ) + ->status_is(200) + ->content_is(q|LDR 00079 2200049 4500 +001 1001 +110 _9102 + _aMy Corporation|); + + $authority->delete; + $t->get_ok( "//$userid:$password@/api/v1/authorities/" . $authority->authid + => { Accept => 'application/marc' } ) + ->status_is(404) + ->json_is( '/error', 'Object not found.' ); + + $schema->storage->txn_rollback; +}; \ No newline at end of file -- 2.30.2