From b49f16e8e2a3c2dfcbdf2508575ffa8043bbfa62 Mon Sep 17 00:00:00 2001 From: Agustin Moyano Date: Fri, 23 Dec 2022 17:15:29 -0300 Subject: [PATCH] Bug 31800: Add REST endpoint to add a biblio To test: 1. Apply patch 2. Set RESTBasicAuth preference to true 3. Make a POST request to /api/v1/biblios with one of the following content type header - application/marcxml+xml - application/marc-in-json - application/marc 4. Add the following header to the request: "x-framework-id: " 5. Check that the biblio is created 6. Sign off Signed-off-by: Jan Kissig Signed-off-by: Kyle M Hall --- Koha/REST/V1/Biblios.pm | 63 ++++- api/v1/swagger/paths/biblios.yaml | 57 ++++ api/v1/swagger/swagger.yaml | 23 ++ t/db_dependent/api/v1/biblios.t | 431 +++++++++++++++++++++++++++++- 4 files changed, 572 insertions(+), 2 deletions(-) diff --git a/Koha/REST/V1/Biblios.pm b/Koha/REST/V1/Biblios.pm index f923255cfa..4f58a4001b 100644 --- a/Koha/REST/V1/Biblios.pm +++ b/Koha/REST/V1/Biblios.pm @@ -22,7 +22,8 @@ use Mojo::Base 'Mojolicious::Controller'; use Koha::Biblios; use Koha::Ratings; use Koha::RecordProcessor; -use C4::Biblio qw( DelBiblio ); +use C4::Biblio qw( DelBiblio AddBiblio ); +use C4::Search qw( FindDuplicate ); use List::MoreUtils qw( any ); use MARC::Record::MiJ; @@ -480,4 +481,64 @@ sub set_rating { }; } +=head3 add + +Controller function that handles creating a biblio object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + try { + my $body = $c->validation->param('Body'); + + my $flavour = $c->validation->param('x-marc-schema'); + $flavour = C4::Context->preference('marcflavour') unless $flavour; + + my $record; + + my $frameworkcode = $c->validation->param('x-framework-id'); + 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/marcxml+xml", + "application/marc-in-json", + "application/marc" + ] + ); + } + + my ( $duplicatebiblionumber, $duplicatetitle ); + ( $duplicatebiblionumber, $duplicatetitle ) = FindDuplicate($record); + + my $confirm_not_duplicate = $c->validation->param('x-confirm-not-duplicate'); + + return $c->render( + status => 400, + openapi => { + error => "Duplicate biblio $duplicatebiblionumber" + } + ) unless !$duplicatebiblionumber || $confirm_not_duplicate; + + my ( $biblionumber, $oldbibitemnum ); + ( $biblionumber, $oldbibitemnum ) = AddBiblio( $record, $frameworkcode ); + + $c->render( + status => 200, + openapi => { id => $biblionumber } + ); + } + catch { + $c->unhandled_exception($_); + }; +} + 1; diff --git a/api/v1/swagger/paths/biblios.yaml b/api/v1/swagger/paths/biblios.yaml index 782de211e7..c19ec73380 100644 --- a/api/v1/swagger/paths/biblios.yaml +++ b/api/v1/swagger/paths/biblios.yaml @@ -1,4 +1,61 @@ --- +"/biblios": + post: + x-mojo-to: Biblios#add + operationId: addBiblio + tags: + - biblios + summary: Add biblio + parameters: + - name: Body + in: body + description: A JSON object or the Marc string describing a biblio + required: true + schema: + type: + - string + - object + - $ref: "../swagger.yaml#/parameters/framework_id_header" + - $ref: "../swagger.yaml#/parameters/marc_schema_header" + - $ref: "../swagger.yaml#/parameters/confirm_not_duplicate_header" + produces: + - application/json + responses: + "200": + description: A biblio + "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 "/biblios/{biblio_id}": get: x-mojo-to: Biblios#get diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index c728f9d6d7..dd4ed9b66a 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -147,6 +147,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" + "/biblios": + $ref: "./paths/biblios.yaml#/~1biblios" "/biblios/{biblio_id}": $ref: "./paths/biblios.yaml#/~1biblios~1{biblio_id}" "/biblios/{biblio_id}/checkouts": @@ -352,6 +354,27 @@ parameters: name: authorised_value_id required: true type: integer + framework_id_header: + description: Framework id. Use when content type is not application/json + name: x-framework-id + in: header + required: false + type: string + marc_schema_header: + description: March schema. One of MARC21 or UNIMARC + name: x-marc-schema + in: header + required: false + type: string + enum: + - MARC21 + - UNIMARC + confirm_not_duplicate_header: + description: Confirm the posted element is not a duplicate + name: x-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/biblios.t b/t/db_dependent/api/v1/biblios.t index b4e98b17f4..c5909eece2 100755 --- a/t/db_dependent/api/v1/biblios.t +++ b/t/db_dependent/api/v1/biblios.t @@ -20,7 +20,7 @@ use Modern::Perl; use utf8; use Encode; -use Test::More tests => 8; +use Test::More tests => 9; use Test::MockModule; use Test::Mojo; use Test::Warn; @@ -713,3 +713,432 @@ subtest 'set_rating() tests' => sub { $schema->storage->txn_rollback; }; + + +subtest 'post() tests' => sub { + + plan tests => 13; + + $schema->storage->txn_begin; + + 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 $frameworkcode = 'BKS'; + my $marcxml = q| + + + 01102pam a2200289 a 7500 + 2504398 + 20200421093816.0 + 920610s1993 caub s001 0 eng + + 92021731 + + + 05200784381 (Test marcxml) + + + 05200784461 (Test marcxml) + + + DLC + DLC + DLC + + + enggrc + + + PA522 + .M38 1993 + + + 480 + 20 + + + Mastronarde, Donald J. + 389 + + + Introduction to Attic Greek (Using marcxml) / + Donald J. Mastronarde. + + + Berkeley : + University of California Press, + c1993. + + + ix, 425 p. : + maps ; + 26 cm. + + + Includes index. + + + Attic Greek dialect + 7 + + + Contributor biographical information + http://www.loc.gov/catdir/bios/ucal051/92021731.html + + + Publisher description + http://www.loc.gov/catdir/description/ucal041/92021731.html + + + 7 + cbc + orignew + 1 + ocip + 19 + y-gencatlg + + + ddc + BK + + + pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93 + + + 3 + 3 + +|; + + my $mij = q|{ + "fields": [ + { + "001": "2504398" + }, + { + "005": "20200421093816.0" + }, + { + "008": "920610s1993 caub s001 0 eng " + }, + { + "010": { + "ind1": " ", + "subfields": [ + { + "a": " 92021731 " + } + ], + "ind2": " " + } + }, + { + "020": { + "subfields": [ + { + "a": "05200784382 (Test mij)" + } + ], + "ind2": " ", + "ind1": " " + } + }, + { + "020": { + "subfields": [ + { + "a": "05200784462 (Test mij)" + } + ], + "ind1": " ", + "ind2": " " + } + }, + { + "040": { + "subfields": [ + { + "a": "DLC" + }, + { + "c": "DLC" + }, + { + "d": "DLC" + } + ], + "ind2": " ", + "ind1": " " + } + }, + { + "041": { + "ind2": " ", + "subfields": [ + { + "a": "enggrc" + } + ], + "ind1": "0" + } + }, + { + "050": { + "subfields": [ + { + "a": "PA522" + }, + { + "b": ".M38 1993" + } + ], + "ind1": "0", + "ind2": "0" + } + }, + { + "082": { + "subfields": [ + { + "a": "480" + }, + { + "2": "20" + } + ], + "ind2": "0", + "ind1": "0" + } + }, + { + "100": { + "ind2": " ", + "subfields": [ + { + "a": "Mastronarde, Donald J." + }, + { + "9": "389" + } + ], + "ind1": "1" + } + }, + { + "245": { + "ind1": "1", + "subfields": [ + { + "a": "Introduction to Attic Greek (Using mij) /" + }, + { + "c": "Donald J. Mastronarde." + } + ], + "ind2": "0" + } + }, + { + "260": { + "subfields": [ + { + "a": "Berkeley :" + }, + { + "b": "University of California Press," + }, + { + "c": "c1993." + } + ], + "ind2": " ", + "ind1": " " + } + }, + { + "300": { + "ind1": " ", + "subfields": [ + { + "a": "ix, 425 p. :" + }, + { + "b": "maps ;" + }, + { + "c": "26 cm." + } + ], + "ind2": " " + } + }, + { + "500": { + "subfields": [ + { + "a": "Includes index." + } + ], + "ind1": " ", + "ind2": " " + } + }, + { + "650": { + "subfields": [ + { + "a": "Attic Greek dialect" + }, + { + "9": "7" + } + ], + "ind2": "0", + "ind1": " " + } + }, + { + "856": { + "subfields": [ + { + "3": "Contributor biographical information" + }, + { + "u": "http://www.loc.gov/catdir/bios/ucal051/92021731.html" + } + ], + "ind2": "2", + "ind1": "4" + } + }, + { + "856": { + "ind1": "4", + "subfields": [ + { + "3": "Publisher description" + }, + { + "u": "http://www.loc.gov/catdir/description/ucal041/92021731.html" + } + ], + "ind2": "2" + } + }, + { + "906": { + "subfields": [ + { + "a": "7" + }, + { + "b": "cbc" + }, + { + "c": "orignew" + }, + { + "d": "1" + }, + { + "e": "ocip" + }, + { + "f": "19" + }, + { + "g": "y-gencatlg" + } + ], + "ind1": " ", + "ind2": " " + } + }, + { + "942": { + "subfields": [ + { + "2": "ddc" + }, + { + "c": "BK" + } + ], + "ind2": " ", + "ind1": " " + } + }, + { + "955": { + "subfields": [ + { + "a": "pc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93" + } + ], + "ind2": " ", + "ind1": " " + } + }, + { + "999": { + "subfields": [ + { + "c": "3" + }, + { + "d": "3" + } + ], + "ind1": " ", + "ind2": " " + } + } + ], + "leader": "01102pam a2200289 a 8500" +}|; + my $marc = q|01102pam a2200289 a 9500001000800000005001700008008004100025010001700066020002800083020003500111040001800146041001100164050002100175082001200196100003200208245005800240260005600298300003300354500002000387650002700407856009500434856008700529906004500616942001200661955013000673999000900803250439820200421093816.0920610s1993 caub s001 0 eng  a 92021731  a05200784383 (Test usmarc) a05200784463 (Test usmarc) aDLCcDLCdDLC0 aenggrc00aPA522b.M38 199300a4802201 aMastronarde, Donald J.938910aIntroduction to Attic Greek (Using usmarc) /cDonald J. Mastronarde. aBerkeley :bUniversity of California Press,cc1993. aix, 425 p. :bmaps ;c26 cm. aIncludes index. 0aAttic Greek dialect97423Contributor biographical informationuhttp://www.loc.gov/catdir/bios/ucal051/92021731.html423Publisher descriptionuhttp://www.loc.gov/catdir/description/ucal041/92021731.html a7bcbccorignewd1eocipf19gy-gencatlg 2ddccBK apc05 to ea00 06-11-92; ea04 to SCD 06-11-92; fd11 06-11-92 (PA522.M...); fr21 06-12-92; fs62 06-15-92; CIP ver. pv07 11-12-93 c3d3|; + + $t->post_ok("//$userid:$password@/api/v1/biblios") + ->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/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode, "x-march-schema" => 'INVALID'}) + ->status_is(400, 'Invalid header x-marc-schema'); + + $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marcxml+xml', 'x-framework-id' => $frameworkcode} => $marcxml) + ->status_is(200) + ->json_has('/id'); + + $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc-in-json', 'x-framework-id' => $frameworkcode, 'x-confirm-not-duplicate' => 1} => $mij) + ->status_is(200) + ->json_has('/id'); + + $t->post_ok("//$userid:$password@/api/v1/biblios" => {'Content-Type' => 'application/marc', 'x-framework-id' => $frameworkcode} => $marc) + ->status_is(200) + ->json_has('/id'); + + $schema->storage->txn_rollback; +}; \ No newline at end of file -- 2.30.2