From 6e507284c78febceab525f1482f526acb63e754d Mon Sep 17 00:00:00 2001 From: Agustin Moyano Date: Tue, 13 Dec 2022 12:58:38 -0300 Subject: [PATCH] Bug 31795: Add REST endpoint to add an authority To test: 1. Apply patch 2. Set RESTBasicAuth preference to true 3. Make a POST request to /api/v1/authorities with one of the following content type header - application/json - application/marcxml+xml - application/marc-in-json - application/marc 5. Check that the authority is created 6. Sign off --- Koha/REST/V1/Authorities.pm | 71 ++++++++++++++++++++++++++- api/v1/swagger/paths/authorities.yaml | 56 +++++++++++++++++++++ api/v1/swagger/swagger.yaml | 14 ++++++ t/db_dependent/api/v1/authorities.t | 70 +++++++++++++++++++++++++- 4 files changed, 209 insertions(+), 2 deletions(-) diff --git a/Koha/REST/V1/Authorities.pm b/Koha/REST/V1/Authorities.pm index 136e3134e6..109c038273 100644 --- a/Koha/REST/V1/Authorities.pm +++ b/Koha/REST/V1/Authorities.pm @@ -20,7 +20,8 @@ use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; use Koha::Authorities; -use C4::AuthoritiesMarc qw( DelAuthority ); +use Koha::Authority; +use C4::AuthoritiesMarc qw( DelAuthority AddAuthority FindDuplicateAuthority); use List::MoreUtils qw( any ); use MARC::Record::MiJ; @@ -136,4 +137,72 @@ sub delete { }; } +=head3 add + +Controller function that handles creating an authority object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + try { + my $body = $c->validation->param('Body'); + + my $flavour = + C4::Context->preference('marcflavour') eq 'UNIMARC' + ? 'UNIMARCAUTH' + : 'MARC21'; + + my $record; + my $authtypecode; + + if ( $c->req->headers->content_type =~ m/application\/json/ ) { + $record = MARC::Record->new_from_xml( $body->{marcxml}, 'UTF-8', $flavour ); + $authtypecode = $body->{authtypecode}; + } else { + $authtypecode = $c->validation->param('authority_type'); + if ( $c->req->headers->content_type =~ m/application\/marcxml\+xml/ ) { + $record = MARC::Record->new_from_xml( $body, 'UTF-8', $flavour ); + } elsif ( $c->req->headers->content_type =~ m/application\/marc-in-json/ ) { + $record = MARC::Record->new_from_mij_structure( $body ); + } elsif ( $c->req->headers->content_type =~ m/application\/marc/ ) { + $record = MARC::Record->new_from_usmarc( $body ); + } else { + return $c->render( + status => 406, + openapi => [ + "application/json", + "application/marcxml+xml", + "application/marc-in-json", + "application/marc" + ] + ); + } + } + + my ($duplicateauthid,$duplicateauthvalue); + ($duplicateauthid,$duplicateauthvalue) = FindDuplicateAuthority($record,$authtypecode); + + my $confirm_not_duplicate = $c->validation->param('confirm_not_duplicate'); + + return $c->render( + status => 400, + openapi => { + error => "Duplicate authority $duplicateauthid" + } + ) unless !$duplicateauthid || $confirm_not_duplicate; + + my $authid = AddAuthority( $record, undef, $authtypecode ); + + $c->render( + status => 200, + openapi => { id => $authid } + ); + } + catch { + $c->unhandled_exception($_); + }; +} + 1; diff --git a/api/v1/swagger/paths/authorities.yaml b/api/v1/swagger/paths/authorities.yaml index 0b47d41389..92f58d2b57 100644 --- a/api/v1/swagger/paths/authorities.yaml +++ b/api/v1/swagger/paths/authorities.yaml @@ -1,4 +1,60 @@ --- +"/authorities": + post: + x-mojo-to: Authorities#add + operationId: addAuthority + tags: + - authorities + summary: Add authority + parameters: + - name: Body + in: body + description: A JSON object or the Marc string describing an authority + required: true + schema: + type: + - string + - object + - $ref: "../swagger.yaml#/parameters/authority_type_header" + - $ref: "../swagger.yaml#/parameters/confirm_not_duplicate_header" + produces: + - application/json + responses: + "200": + description: An authority + "400": + description: Bad request + schema: + $ref: "../swagger.yaml#/definitions/error" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + 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: + editcatalogue: edit_catalogue "/authorities/{authority_id}": get: x-mojo-to: Authorities#get diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 1796ba041e..f50153b68f 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": + $ref: paths/authorities.yaml#/~1authorities "/authorities/{authority_id}": $ref: paths/authorities.yaml#/~1authorities~1{authority_id} "/biblios/{biblio_id}": @@ -344,6 +346,18 @@ parameters: name: authority_id required: true type: integer + authority_type_header: + description: Authority type code. Use when content type is not application/json + name: authority_type + in: header + required: false + type: string + confirm_not_duplicate_header: + description: Confirm the posted element is not a duplicate + name: confirm_not_duplicate + in: header + required: false + type: integer identity_provider_id_pp: description: Authentication provider internal identifier in: path diff --git a/t/db_dependent/api/v1/authorities.t b/t/db_dependent/api/v1/authorities.t index 13e101f951..b9720163b5 100755 --- a/t/db_dependent/api/v1/authorities.t +++ b/t/db_dependent/api/v1/authorities.t @@ -20,7 +20,7 @@ use Modern::Perl; use utf8; use Encode; -use Test::More tests => 2; +use Test::More tests => 3; use Test::MockModule; use Test::Mojo; use Test::Warn; @@ -160,5 +160,73 @@ subtest 'delete() tests' => sub { $t->delete_ok("//$userid:$password@/api/v1/authorities/".$authority->authid) ->status_is(404); + $schema->storage->txn_rollback; +}; + +subtest 'post() tests' => sub { + + plan tests => 14; + + $schema->storage->txn_begin; + + Koha::Authorities->delete; + + my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } # no permissions + } + ); + my $password = 'thePassword123'; + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $patron->userid; + + my $marcxml = q| + + 1001 + + 102 + My Corporation + +|; + + my $mij = '{"fields":[{"001":"1001"},{"110":{"subfields":[{"9":"102"},{"a":"My Corporation"}],"ind1":" ","ind2":" "}}],"leader":" "}'; + my $marc = '00079 2200049 45000010005000001100024000051001 9102aMy Corporation'; + my $json = { + authtypecode => "CORPO_NAME", + marcxml => $marcxml + }; + + $t->post_ok("//$userid:$password@/api/v1/authorities") + ->status_is(403, 'Not enough permissions makes it return the right code'); + + # Add permissions + $builder->build( + { + source => 'UserPermission', + value => { + borrowernumber => $patron->borrowernumber, + module_bit => 9, + code => 'edit_catalogue' + } + } + ); + + $t->post_ok("//$userid:$password@/api/v1/authorities" => json => $json) + ->status_is(200) + ->json_has('/id'); + + $t->post_ok("//$userid:$password@/api/v1/authorities" => {'Content-Type' => 'application/marcxml+xml', authority_type => 'CORPO_NAME'} => $marcxml) + ->status_is(200) + ->json_has('/id'); + + $t->post_ok("//$userid:$password@/api/v1/authorities" => {'Content-Type' => 'application/marc-in-json', authority_type => 'CORPO_NAME'} => $mij) + ->status_is(200) + ->json_has('/id'); + + $t->post_ok("//$userid:$password@/api/v1/authorities" => {'Content-Type' => 'application/marc', authority_type => 'CORPO_NAME'} => $marc) + ->status_is(200) + ->json_has('/id'); + $schema->storage->txn_rollback; }; \ No newline at end of file -- 2.25.1