Bug 34008: Add REST endpoint for list of itemtypes

Test plan:
* Enable the system preference RESTBasicAuth
* curl -s --request GET http://kohadev-intra.mydnsname.org:8081/api/v1/itemtypes
  should give 401 Unauthorized
* curl -s -u koha:koha --request GET http://kohadev-intra.mydnsname.org:8081/api/v1/itemtypes
  should produce JSON-list of itemtypes
* curl -s -u koha:koha --header "x-koha-embed: translated_descriptions" --request GET http://kohadev-intra.mydnsname.org:8081/api/v1/itemtypes
  should include the field translated_descriptions containing the translated descriptions, if any

Signed-off-by: David Nind <david@davidnind.com>

Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl>
[EDIT] perltidy -b t/db_dependent/api/v1/itemtypes.t # Resolve bad score of 44
[EDIT] chmod 755 t/db_dependent/api/v1/itemtypes.t
[EDIT] perltidy -b Koha/REST/V1/ItemTypes.pm
Lesson: Please run qa tools yourself and adjust accordingly?
Edit (tcohen): I restored the item_type_translated_description.yaml file
as the entire API was broken because of the lack of it.
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
Andreas Jonsson 2023-09-16 10:13:15 +00:00 committed by Tomas Cohen Arazi
parent 29bf7500f9
commit 82bdf93ab7
No known key found for this signature in database
GPG Key ID: 0A272EA1B2F3C15F
7 changed files with 418 additions and 32 deletions

View File

@ -17,7 +17,6 @@ package Koha::ItemType;
use Modern::Perl;
use C4::Koha qw( getitemtypeimagelocation );
use C4::Languages;
use Koha::Caches;
@ -52,8 +51,7 @@ sub store {
if ( !$self->in_storage ) {
$flush = 1;
}
else {
} else {
my $self_from_storage = $self->get_from_storage;
$flush = 1 if ( $self_from_storage->description ne $self->description );
}
@ -96,22 +94,25 @@ sub image_location {
sub translated_description {
my ( $self, $lang ) = @_;
if ( my $translated_description = eval { $self->get_column('translated_description') } ) {
# If the value has already been fetched (eg. from sarch_with_localization),
# do not search for it again
# Note: This is a bit hacky but should be fast
return $translated_description
? $translated_description
: $self->description;
? $translated_description
: $self->description;
}
$lang ||= C4::Languages::getlanguage;
my $translated_description = Koha::Localizations->search({
code => $self->itemtype,
entity => 'itemtypes',
lang => $lang
})->next;
my $translated_description = Koha::Localizations->search(
{
code => $self->itemtype,
entity => 'itemtypes',
lang => $lang
}
)->next;
return $translated_description
? $translated_description->translation
: $self->description;
? $translated_description->translation
: $self->description;
}
=head3 translated_descriptions
@ -119,18 +120,21 @@ sub translated_description {
=cut
sub translated_descriptions {
my ( $self ) = @_;
my ($self) = @_;
my @translated_descriptions = Koha::Localizations->search(
{ entity => 'itemtypes',
{
entity => 'itemtypes',
code => $self->itemtype,
}
)->as_list;
return [ map {
{
lang => $_->lang,
translation => $_->translation,
}
} @translated_descriptions ];
return [
map {
{
lang => $_->lang,
translation => $_->translation,
}
} @translated_descriptions
];
}
=head3 can_be_deleted
@ -142,8 +146,8 @@ Counts up the number of biblioitems and items with itemtype (code) and hands bac
=cut
sub can_be_deleted {
my ($self) = @_;
my $nb_items = Koha::Items->search( { itype => $self->itemtype } )->count;
my ($self) = @_;
my $nb_items = Koha::Items->search( { itype => $self->itemtype } )->count;
my $nb_biblioitems = Koha::Biblioitems->search( { itemtype => $self->itemtype } )->count;
return $nb_items + $nb_biblioitems == 0 ? 1 : 0;
}
@ -162,10 +166,12 @@ sub may_article_request {
my $itemtype = $self->itemtype;
my $category = $params->{categorycode};
my $guess = Koha::CirculationRules->guess_article_requestable_itemtypes({
$category ? ( categorycode => $category ) : (),
});
return ( $guess->{ $itemtype // q{} } || $guess->{ '*' } ) ? 1 : q{};
my $guess = Koha::CirculationRules->guess_article_requestable_itemtypes(
{
$category ? ( categorycode => $category ) : (),
}
);
return ( $guess->{ $itemtype // q{} } || $guess->{'*'} ) ? 1 : q{};
}
=head3 _library_limits
@ -176,8 +182,8 @@ sub may_article_request {
sub _library_limits {
return {
class => "ItemtypesBranch",
id => "itemtype",
class => "ItemtypesBranch",
id => "itemtype",
library => "branchcode",
};
}
@ -189,10 +195,10 @@ sub _library_limits {
=cut
sub parent {
my ( $self ) = @_;
my ($self) = @_;
my $parent_rs = $self->_result->parent_type;
return unless $parent_rs;
return Koha::ItemType->_new_from_dbic( $parent_rs );
return Koha::ItemType->_new_from_dbic($parent_rs);
}
@ -203,8 +209,23 @@ sub parent {
=cut
sub children_with_localization {
my ( $self ) = @_;
return Koha::ItemTypes->search_with_localization({ parent_type => $self->itemtype });
my ($self) = @_;
return Koha::ItemTypes->search_with_localization( { parent_type => $self->itemtype } );
}
=head3 to_api_mapping
This method returns the mapping for representing a Koha::ItemType object
on the API.
=cut
sub to_api_mapping {
return { 'itemtype' => 'item_type' };
}
sub from_api_mapping {
return { 'item_type' => 'itemtype' };
}
=head3 type

48
Koha/REST/V1/ItemTypes.pm Normal file
View File

@ -0,0 +1,48 @@
package Koha::REST::V1::ItemTypes;
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use Mojo::Base 'Mojolicious::Controller';
use Koha::ItemTypes;
use Try::Tiny;
=head1 API
=head2 Methods
=head3 list
=cut
sub list {
my $c = shift->openapi->valid_input or return;
return try {
my $itemtypes_set = Koha::ItemTypes->new;
my $itemtypes = $c->objects->search( $itemtypes_set );
return $c->render( status => 200, openapi => $itemtypes );
} catch {
$c->unhandled_exception($_);
};
}
1;

View File

@ -0,0 +1,96 @@
---
type: object
properties:
item_type:
type: string
description: Unique key, a code associated with the item type
readOnly: true
parent_type:
description: Unique key, a code associated with the parent item type
type:
- string
- "null"
description:
description: A plain text explanation of the item type
type: string
rentalcharge:
description: The amount charged when this item is checked out/issued
type:
- number
- "null"
rentalcharge_daily:
description: The amount charged for each day between checkout date and due date
type:
- number
- "null"
rentalcharge_daily_calendar:
description: Controls if the daily rental fee is calculated directly or using finesCalendar
type: boolean
rentalcharge_hourly:
description: The amount charged for each hour between checkout date and due date
type:
- number
- "null"
rentalcharge_hourly_calendar:
description: Controls if the hourly rental fee is calculated directly or using finesCalendar
type: boolean
defaultreplacecost:
description: Default replacement cost
type:
- number
- "null"
processfee:
description: Default text be recorded in the column note when the processing fee is applied
type:
- number
- "null"
notforloan:
description: Controls if the item is available for loan
type:
- boolean
- "null"
imageurl:
description: URL for the item type icon
type:
- string
- "null"
summary:
description: Information from the summary field, may include HTML
type:
- string
- "null"
checkinmsg:
description: Message that is displayed when an item with the given item type is checked in
type:
- string
- "null"
checkinmsgtype:
description: Type (CSS class) for the checkinmsg, can be 'alert' or 'message'
type:
- string
- "null"
sip_media_type:
description: SIP2 protocol media type for this item type
type:
- string
- "null"
hideinopac:
description: Hide the item type from the search options in OPAC
type: boolean
searchcategory:
description: Group this item type with others with the same value on OPAC search options
type:
- string
- "null"
automatic_checkin:
description: Controls if automatic checkin is enabled for items of this type
type: boolean
translated_descriptions:
description: Translations of description plain text
type: array
items:
$ref: "item_type_translated_description.yaml"
additionalProperties: false
required:
- item_type

View File

@ -0,0 +1,13 @@
---
type: object
properties:
lang:
description: Language identifier
type: string
translation:
description: Translated plain text
type: string
additionalProperties: false
required:
- lang
- translation

View File

@ -0,0 +1,59 @@
---
/item_types:
get:
x-mojo-to: ItemTypes#list
operationId: listItemTypes
tags:
- item_types
summary: List item types
produces:
- application/json
parameters:
- name: x-koha-embed
in: header
required: false
description: Embed list sent as a request header
type: array
items:
type: string
enum:
- translated_descriptions
collectionFormat: csv
- $ref: "../swagger.yaml#/parameters/match"
- $ref: "../swagger.yaml#/parameters/order_by"
- $ref: "../swagger.yaml#/parameters/page"
- $ref: "../swagger.yaml#/parameters/per_page"
- $ref: "../swagger.yaml#/parameters/q_param"
- $ref: "../swagger.yaml#/parameters/q_body"
responses:
200:
description: A list of item types
schema:
items:
$ref: "../swagger.yaml#/definitions/item_type"
type: array
400:
description: Bad request
schema:
$ref: "../swagger.yaml#/definitions/error"
403:
description: Access forbidden
schema:
$ref: "../swagger.yaml#/definitions/error"
404:
description: Resource 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:
catalogue: 1

View File

@ -110,6 +110,8 @@ definitions:
$ref: ./definitions/item.yaml
item_group:
$ref: ./definitions/item_group.yaml
item_type:
$ref: ./definitions/item_type.yaml
job:
$ref: ./definitions/job.yaml
library:
@ -367,6 +369,8 @@ paths:
$ref: ./paths/import_batch_profiles.yaml#/~1import_batch_profiles
"/import_batch_profiles/{import_batch_profile_id}":
$ref: "./paths/import_batch_profiles.yaml#/~1import_batch_profiles~1{import_batch_profile_id}"
/item_types:
$ref: ./paths/item_types.yaml#/~1item_types
/items:
$ref: ./paths/items.yaml#/~1items
"/items/{item_id}":
@ -1062,6 +1066,9 @@ tags:
- description: "Manage items\n"
name: items
x-displayName: Items
- description: "Manage item types\n"
name: item_types
x-displayName: Item Types
- description: "Manage jobs\n"
name: jobs
x-displayName: Jobs

View File

@ -0,0 +1,142 @@
#!/usr/bin/env perl
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Koha is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Koha; if not, see <http://www.gnu.org/licenses>.
use Modern::Perl;
use Test::More tests => 1;
use Test::Mojo;
use t::lib::TestBuilder;
use t::lib::Mocks;
use Koha::ItemTypes;
use Koha::Database;
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
my $t = Test::Mojo->new('Koha::REST::V1');
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
subtest 'list_item_types() tests' => sub {
plan tests => 12;
$schema->storage->txn_begin;
my $item_type = $builder->build_object(
{
class => 'Koha::ItemTypes',
value => {
itemtype => 'TEST_IT',
parent_type => undef,
description => 'Test item type',
rentalcharge => 100.0,
rentalcharge_daily => 50.,
rentalcharge_daily_calendar => 0,
rentalcharge_hourly => 0.60,
rentalcharge_hourly_calendar => 1,
defaultreplacecost => 1000.0,
processfee => 20.0,
notforloan => 0,
imageurl => 'https://upload.wikimedia.org/wikipedia/commons/1/1f/202208_test-tube-4.svg',
summary => 'An item type for testing',
checkinmsg => 'Checking in test',
checkinmsgtype => 'message',
sip_media_type => 'spt',
hideinopac => 1,
searchcategory => 'test search category',
automatic_checkin => 0,
}
}
);
my $en = $builder->build_object(
{
class => 'Koha::Localizations',
value => {
entity => 'itemtypes',
code => 'TEST_IT',
lang => 'en',
translation => 'English word "test"',
}
}
);
my $sv = $builder->build_object(
{
class => 'Koha::Localizations',
value => {
entity => 'itemtypes',
code => 'TEST_IT',
lang => 'sv_SE',
translation => 'Swedish word "test"',
}
}
);
my $librarian = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 2**2 } # catalogue flag = 2
}
);
my $password = 'thePassword123';
$librarian->set_password( { password => $password, skip_validation => 1 } );
my $userid = $librarian->userid;
my $patron = $builder->build_object(
{
class => 'Koha::Patrons',
value => { flags => 0 }
}
);
$patron->set_password( { password => $password, skip_validation => 1 } );
my $unauth_userid = $patron->userid;
## Authorized user tests
# No category, 404 expected
$t->get_ok("//$userid:$password@/api/v1/item_types")->status_is(200)->json_has('/0');
for my $json ( @{ $t->tx->res->json } ) {
if ( $json->{item_type} eq 'TEST_IT' ) {
is( $json->{description}, 'Test item type' );
ok( !exists $json->{translated_descriptions} );
}
}
$t->get_ok( "//$userid:$password@/api/v1/item_types" => { 'x-koha-embed' => 'translated_descriptions' } )
->status_is(200)->json_has('/0');
for my $json ( @{ $t->tx->res->json } ) {
if ( $json->{item_type} eq 'TEST_IT' ) {
is( $json->{description}, 'Test item type' );
is_deeply(
$json->{translated_descriptions},
[
{ lang => 'en', translation => 'English word "test"' },
{ lang => 'sv_SE', translation => 'Swedish word "test"' },
]
);
}
}
# Unauthorized access
$t->get_ok("//$unauth_userid:$password@/api/v1/item_types")->status_is(403);
$schema->storage->txn_rollback;
};