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:
parent
29bf7500f9
commit
82bdf93ab7
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
Loading…
Reference in New Issue