From 98ab918470472b5480ab91cc06ce0ac40a097762 Mon Sep 17 00:00:00 2001 From: Jonathan Druart Date: Wed, 3 Aug 2022 14:19:03 +0200 Subject: [PATCH] Bug 32030: Add/remove resources to/from EBSCO's holdings Add an "Add to holdings" and "Remove from holdings" buttons on the resource page. Signed-off-by: Jonathan Field Signed-off-by: Martin Renvoize Signed-off-by: Kyle M Hall Signed-off-by: Tomas Cohen Arazi --- Koha/ERM/Providers/EBSCO.pm | 13 ++-- Koha/REST/V1/ERM/EHoldings/Resources.pm | 17 ++++++ Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm | 70 ++++++++++++++++++++++ api/v1/swagger/paths/erm_eholdings_resources.yaml | 61 +++++++++++++++++++ .../components/ERM/EHoldingsEBSCOResourcesShow.vue | 49 +++++++++++++++ koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts | 10 +++- 6 files changed, 214 insertions(+), 6 deletions(-) diff --git a/Koha/ERM/Providers/EBSCO.pm b/Koha/ERM/Providers/EBSCO.pm index 13ec4c68ca..fbb6cfffce 100644 --- a/Koha/ERM/Providers/EBSCO.pm +++ b/Koha/ERM/Providers/EBSCO.pm @@ -4,7 +4,7 @@ use Modern::Perl; use HTTP::Request; use LWP::UserAgent; -use JSON qw( from_json decode_json ); +use JSON qw( from_json decode_json encode_json ); use List::Util qw( first ); use Koha::Exceptions; @@ -237,14 +237,16 @@ sub build_query { } sub request { - my ( $self, $method, $url, $params ) = @_; + my ( $self, $method, $url, $params, $payload ) = @_; $url = $self->build_query($url, $params) if $params; - warn $url; my $config = $self->config; my $base_url = 'https://api.ebsco.io/rm/rmaccounts/' . $config->{custid}; - my $request = HTTP::Request->new( $method => $base_url . $url); + my $request = HTTP::Request->new( + $method => $base_url . $url, + undef, ( $payload ? encode_json($payload) : undef ) + ); $request->header( 'x-api-key' => $config->{api_key} ); my $ua = LWP::UserAgent->new; my $response = $ua->simple_request($request); @@ -271,7 +273,10 @@ sub request { die sprintf "ERROR requesting EBSCO API\n%s\ncode %s: %s\n", $url, $response->code, $message; } + } elsif ( $response->code == 204 ) { # No content + return } + return decode_json( $response->decoded_content ); } diff --git a/Koha/REST/V1/ERM/EHoldings/Resources.pm b/Koha/REST/V1/ERM/EHoldings/Resources.pm index 707b10f7a9..76d6614527 100644 --- a/Koha/REST/V1/ERM/EHoldings/Resources.pm +++ b/Koha/REST/V1/ERM/EHoldings/Resources.pm @@ -61,4 +61,21 @@ sub get { } } +=head3 edit + +Controller function that handles editing a single Koha::ERM::EHoldings::Resource object + +=cut + +sub edit { + my $c = shift->openapi->valid_input or return; + + my $provider = $c->validation->param('provider'); + if ( $provider eq 'ebsco' ) { + return Koha::REST::V1::ERM::EHoldings::Resources::EBSCO::edit($c); + } else { + die "invalid action"; + } +} + 1; diff --git a/Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm b/Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm index c378ddb69a..6afe15d0f2 100644 --- a/Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm +++ b/Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm @@ -173,4 +173,74 @@ sub get { }; } +=head3 edit + +=cut + +sub edit { + my $c = shift->openapi->valid_input or return; + + return try { + my $body = $c->validation->param('body'); + my $is_selected = $body->{is_selected}; + my ( $vendor_id, $package_id, $resource_id ) = split '-', + $c->validation->param('resource_id'); + + my $ebsco = Koha::ERM::Providers::EBSCO->new; + my $t = try { + return $ebsco->request( GET => '/vendors/' + . $vendor_id + . '/packages/' + . $package_id + . '/titles/' + . $resource_id ); + + } + catch { + if ( blessed $_ ) { + if ( $_->isa('Koha::Exceptions::ObjectNotFound') ) { + return $c->render( + status => 404, + openapi => { error => $_->error } + ); + + } + } + + $c->unhandled_exception($_); + }; + + unless ($t) { + return $c->render( + status => 404, + openapi => { error => "Resource not found" } + ); + } + + $ebsco->request( + PUT => '/vendors/' + . $vendor_id + . '/packages/' + . $package_id + . '/titles/' + . $resource_id, + undef, + { + isSelected => $is_selected, + titleName => $t->{titleName}, + pubType => $t->{pubType} + } + ); + + return $c->render( + status => 200, + openapi => { is_selected => $is_selected } # We don't want to refetch the resource to make sure it has been updated + ); + } + catch { + $c->unhandled_exception($_); + }; + +} + 1; diff --git a/api/v1/swagger/paths/erm_eholdings_resources.yaml b/api/v1/swagger/paths/erm_eholdings_resources.yaml index 1d299f9f12..676c4e0931 100644 --- a/api/v1/swagger/paths/erm_eholdings_resources.yaml +++ b/api/v1/swagger/paths/erm_eholdings_resources.yaml @@ -129,3 +129,64 @@ x-koha-embed: - resources - resources.package + patch: + x-mojo-to: ERM::EHoldings::Resources#edit + operationId: editErmEHoldingsResources + tags: + - eholdings + summary: Edit a resource + produces: + - application/json + parameters: + - description: Provider name + in: path + name: provider + required: true + type: string + - description: A JSON object containing the fields to edit + in: body + name: body + required: true + schema: + type: object + properties: + is_selected: + description: Add or remove this resource from remote holdings + type: boolean + additionalProperties: false + - $ref: "../swagger.yaml#/parameters/eholdings_resource_id_pp" + responses: + 200: + description: What has been modified + schema: + type: object + properties: + is_selected: + description: Add or remove this resource from remote holdings + type: boolean + additionalProperties: false + 401: + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + 403: + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + 404: + description: Ressource not found + schema: + $ref: "../swagger.yaml#/definitions/error" + 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: + erm: 1 diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/EHoldingsEBSCOResourcesShow.vue b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/EHoldingsEBSCOResourcesShow.vue index 83d1554413..e71cb28cb5 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/EHoldingsEBSCOResourcesShow.vue +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/EHoldingsEBSCOResourcesShow.vue @@ -3,6 +3,23 @@

{{ $t("Resource .id", { id: resource.resource_id }) }} + + Add to holdings + Remove from holdings

@@ -98,6 +115,8 @@ import { fetchEBSCOResource } from "../../fetch" import { useVendorStore } from "../../stores/vendors" import { storeToRefs } from "pinia" +import { checkError } from '../../fetch.js' + export default { setup() { const format_date = $date @@ -122,6 +141,7 @@ export default { package: {}, }, initialized: false, + updating_is_selected: false, } }, @@ -138,6 +158,35 @@ export default { const resource = await fetchEBSCOResource(resource_id) this.resource = resource this.initialized = true + this.updating_is_selected = false + }, + edit_selected(is_selected) { + this.updating_is_selected = true + fetch('/api/v1/erm/eholdings/ebsco/resources/' + this.resource.resource_id, { + method: "PATCH", + body: JSON.stringify({ is_selected }), + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + }) + .then(checkError) + .then( + (result) => { + // Refresh the page. We should not need that actually. + this.getResource(this.resource.resource_id) + }, + ).catch( + (error) => { + setError(error) + } + ) + }, + add_to_holdings() { + this.edit_selected(true) + }, + remove_from_holdings() { + this.edit_selected(false) }, }, name: "EHoldingsEBSCOResourcesShow", diff --git a/koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts b/koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts index 21ae72eb12..f8606fd42e 100644 --- a/koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts +++ b/koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts @@ -3,11 +3,17 @@ import { createWebHistory, createRouter } from "vue-router"; import { createPinia } from "pinia"; import { library } from "@fortawesome/fontawesome-svg-core"; -import { faPlus, faPencil, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { + faPlus, + faMinus, + faPencil, + faTrash, + faSpinner, +} from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import vSelect from "vue-select"; -library.add(faPlus, faPencil, faTrash); +library.add(faPlus, faMinus, faPencil, faTrash, faSpinner); import App from "./components/ERM/ERMMain.vue"; -- 2.11.0