Bug 33161: Add +strings support to GET /items and /items/:item_id
authorTomas Cohen Arazi <tomascohen@theke.io>
Tue, 7 Mar 2023 23:51:35 +0000 (20:51 -0300)
committerTomas Cohen Arazi <tomascohen@theke.io>
Fri, 10 Mar 2023 13:20:09 +0000 (10:20 -0300)
This patch introduces the `api_strings_mapping` method to the
*Koha::Item* class, and makes the API spec for the following routes:

* GET /items
* GET /items/:item_id
* GET /acquisitions/orders

accept the new `+strings` parameter that can be passed through the
`x-koha-embed` header and was introduced by bug 26635. In the case of
/acquisitions/orders, you will need to use

x-koha-embed: items+strings

I introduce it here to highlight the flebility we introduced with bug
26635.

The `api_strings_mapping` method has its roots on the cool
`columns_to_str` method already present. The main differences:

* It is aware of the `public_read_list` for attributes so no hidden
  information is exposed.
* Attribute names get mapped for consistency with the API (e.g.
  `homebranch` is converted into `home_library_id`, etc).
* The data structure it returns includes information about the source
  for the descriptions (e.g. it it is an authorised value, then `type`
  will be `av`, and the related category information is returned so
  dropdowns and such can be built. The same goes for other types as
 `library`, `item_type` and `call_number_source`.

To test:
1. Apply this patch
2. Reload everything
3. Play with your favourite REST tool (e.g. Postman)
4. Try:
GET http://localhost:8081/api/v1/items
x-koha-embed: +strings
=> SUCCESS: You get a list of items, they include the new _strings
structure, and the contents make sense!
5. Repeat with a specific item:
GET http://localhost:8081/api/v1/items/14
x-koha-embed: +strings
=> SUCCESS: It all makes sense!
6. Sign off :-D

Sponsored-by: Virginia Polytechnic Institute and State University
Signed-off-by: Pedro Amorim <pedro.amorim@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
Koha/Item.pm
Koha/REST/V1/Items.pm
api/v1/swagger/definitions/item.yaml
api/v1/swagger/paths/acquisitions_orders.yaml
api/v1/swagger/paths/items.yaml

index 89d1a94..3139bab 100644 (file)
@@ -2058,6 +2058,76 @@ sub is_denied_renewal {
     return 0;
 }
 
+=head3 api_strings_mapping
+
+Retrieves for each column name the unblessed authorised value.
+
+=cut
+
+sub api_strings_mapping {
+    my ( $self, $params ) = @_;
+
+    my $columns_info  = $self->_result->result_source->columns_info;
+    my $frameworkcode = $self->biblio->frameworkcode;
+    my $tagslib       = C4::Biblio::GetMarcStructure( 1, $frameworkcode );
+    my $mss           = C4::Biblio::GetMarcSubfieldStructure( $frameworkcode, { unsafe => 1 } );
+
+    my ( $itemtagfield, $itemtagsubfield ) = C4::Biblio::GetMarcFromKohaField("items.itemnumber");
+
+    my $public_read_list = $params->{public} ? $self->public_read_list : [];
+    my $to_api_mapping   = $self->to_api_mapping;
+
+    # Hardcoded known 'authorised_value' values mapped to API codes
+    my $code_to_type = {
+        branches  => 'library',
+        cn_source => 'call_number_source',
+        itemtypes => 'item_type',
+    };
+
+    # Handle not null and default values for integers and dates
+    my $strings = {};
+
+    foreach my $col ( keys %{$columns_info} ) {
+
+        # Skip columns not in public read list
+        next
+          unless !$params->{public}
+          || any { $col eq $_ } $public_read_list;
+
+        # Skip columns that are not exposed on the API by to_api_mapping
+        # i.e. mapping exists but points to undef
+        next
+          if $col eq 'more_subfields_xml'    # not dealt with as a regular field
+          || ( exists $to_api_mapping->{$col} && !defined $to_api_mapping->{$col} );
+
+        # By now, we are done with known columns, now check the framework for mappings
+        my $field = $self->_result->result_source->name . '.' . $col;
+
+        # Check there's an entry in the MARC subfield structure for the field
+        if (   exists $mss->{$field}
+            && scalar @{ $mss->{$field} } > 0
+            && $mss->{$field}[0]->{authorised_value} )
+        {
+            my $subfield = $mss->{$field}[0];
+            my $code     = $subfield->{authorised_value};
+
+            my $str  = C4::Biblio::GetAuthorisedValueDesc( $itemtagfield, $subfield->{tagsubfield}, $self->$col, '', $tagslib, undef, $params->{public} );
+            my $type = exists $code_to_type->{$code} ? $code_to_type->{$code} : 'av';
+
+            # The _strings entry should match the API attribute name
+            my $mapped_attr = exists $to_api_mapping->{$col} ? $to_api_mapping->{$col} : $col;
+
+            $strings->{$mapped_attr} = {
+                str  => $str,
+                type => $type,
+                ( $type eq 'av' ? ( category => $code ) : () ),
+            };
+        }
+    }
+
+    return $strings;
+}
+
 =head3 _type
 
 =cut
index 70abdfe..013ac03 100644 (file)
@@ -68,14 +68,15 @@ sub get {
     my $c = shift->openapi->valid_input or return;
 
     try {
-        my $item = Koha::Items->find($c->validation->param('item_id'));
+        my $items_rs = Koha::Items->new;
+        my $item = $c->objects->find($items_rs, $c->validation->param('item_id'));
         unless ( $item ) {
             return $c->render(
                 status => 404,
                 openapi => { error => 'Item not found'}
             );
         }
-        return $c->render( status => 200, openapi => $item->to_api );
+        return $c->render( status => 200, openapi => $item );
     }
     catch {
         $c->unhandled_exception($_);
index 069c0fa..21963d1 100644 (file)
@@ -227,6 +227,10 @@ properties:
     type:
       - object
       - "null"
+  _strings:
+    type:
+      - object
+      - "null"
     description: A return claims object if one exists that's unresolved
 additionalProperties: false
 required:
index 70668af..3f3d5e4 100644 (file)
@@ -63,6 +63,7 @@
             - current_item_level_holds+count
             - invoice
             - items
+            - items+strings
             - subscription
         collectionFormat: csv
     responses:
index 2bd8b21..a9c7ca4 100644 (file)
         description: Search on the item's barcode
         required: false
         type: string
+      - name: x-koha-embed
+        in: header
+        required: false
+        description: Embed list sent as a request header
+        type: array
+        items:
+          type: string
+          enum:
+            - +strings
+        collectionFormat: csv
       - $ref: "../swagger.yaml#/parameters/match"
       - $ref: "../swagger.yaml#/parameters/order_by"
       - $ref: "../swagger.yaml#/parameters/page"
     summary: Get item
     parameters:
       - $ref: "../swagger.yaml#/parameters/item_id_pp"
+      - name: x-koha-embed
+        in: header
+        required: false
+        description: Embed list sent as a request header
+        type: array
+        items:
+          type: string
+          enum:
+            - +strings
+        collectionFormat: csv
     consumes:
       - application/json
     produces: