Bug 31794: Add REST endpoint to get an authority
authorAgustin Moyano <agustinmoyano@theke.io>
Tue, 6 Dec 2022 20:07:02 +0000 (17:07 -0300)
committerTomas Cohen Arazi <tomascohen@theke.io>
Mon, 6 Mar 2023 17:45:26 +0000 (14:45 -0300)
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 <david@davidnind.com>
Signed-off-by: Pedro Amorim <pedro.amorim@ptfs-europe.com>
Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Koha/Authority.pm
Koha/REST/V1/Authorities.pm [new file with mode: 0644]
api/v1/swagger/paths/authorities.yaml [new file with mode: 0644]
api/v1/swagger/swagger.yaml
t/db_dependent/Koha/Authorities.t
t/db_dependent/api/v1/authorities.t [new file with mode: 0755]

index ff45d20..828d4c7 100644 (file)
@@ -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 (file)
index 0000000..5266fab
--- /dev/null
@@ -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 <http://www.gnu.org/licenses>.
+
+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 (file)
index 0000000..c5f1044
--- /dev/null
@@ -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"
index 9da6b77..e3ecac8 100644 (file)
@@ -151,6 +151,8 @@ paths:
     $ref: paths/authorised_value_categories.yaml#/~1authorised_value_categories
   "/authorised_value_categories/{authorised_value_category_name}/authorised_values":
     $ref: "./paths/authorised_values.yaml#/~1authorised_value_categories~1{authorised_value_category_name}~1authorised_values"
+  "/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":
@@ -364,6 +366,12 @@ parameters:
     name: authorised_value_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
index 5dbfaaa..699ee1c 100755 (executable)
@@ -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 (executable)
index 0000000..5edc612
--- /dev/null
@@ -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 <http://www.gnu.org/licenses>.
+
+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|<?xml version="1.0" encoding="UTF-8"?>
+<record xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.loc.gov/MARC21/slim" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd">
+    <controlfield tag="001">1001</controlfield>
+    <datafield tag="110" ind1=" " ind2=" ">
+        <subfield code="9">102</subfield>
+        <subfield code="a">My Corporation</subfield>
+    </datafield>
+</record>|
+    } });
+
+    $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