Bug 32030: Add/remove resources to/from EBSCO's holdings
authorJonathan Druart <jonathan.druart@bugs.koha-community.org>
Wed, 3 Aug 2022 12:19:03 +0000 (14:19 +0200)
committerTomas Cohen Arazi <tomascohen@theke.io>
Tue, 8 Nov 2022 12:44:23 +0000 (09:44 -0300)
Add an "Add to holdings" and "Remove from holdings" buttons on the
resource page.

Signed-off-by: Jonathan Field <jonathan.field@ptfs-europe.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Koha/ERM/Providers/EBSCO.pm
Koha/REST/V1/ERM/EHoldings/Resources.pm
Koha/REST/V1/ERM/EHoldings/Resources/EBSCO.pm
api/v1/swagger/paths/erm_eholdings_resources.yaml
koha-tmpl/intranet-tmpl/prog/js/vue/components/ERM/EHoldingsEBSCOResourcesShow.vue
koha-tmpl/intranet-tmpl/prog/js/vue/main-erm.ts

index 13ec4c6..fbb6cff 100644 (file)
@@ -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 );
 }
 
index 707b10f..76d6614 100644 (file)
@@ -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;
index c378ddb..6afe15d 100644 (file)
@@ -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;
index 1d299f9..676c4e0 100644 (file)
     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
index 83d1554..e71cb28 100644 (file)
@@ -3,6 +3,23 @@
     <div v-else-if="resource" id="eholdings_resources_show">
         <h2>
             {{ $t("Resource .id", { id: resource.resource_id }) }}
+            <span v-if="!updating_is_selected">
+                <a
+                    v-if="!resource.is_selected"
+                    class="btn btn-default btn-xs"
+                    role="button"
+                    @click="add_to_holdings"
+                    ><font-awesome-icon icon="plus" /> Add to holdings</a
+                >
+                <a
+                    v-else
+                    class="btn btn-default btn-xs"
+                    role="button"
+                    id="remove-from-holdings"
+                    @click="remove_from_holdings"
+                    ><font-awesome-icon icon="minus" /> Remove from holdings</a
+                > </span
+            ><span v-else><font-awesome-icon icon="spinner" /></span>
         </h2>
         <div>
             <fieldset class="rows">
 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",
index 21ae72e..f8606fd 100644 (file)
@@ -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";