From 14fad177182d36a083968e38d44db05d7522c462 Mon Sep 17 00:00:00 2001 From: Andreas Jonsson Date: Sat, 16 Sep 2023 10:13:15 +0000 Subject: [PATCH] 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 --- Koha/REST/V1/ItemTypes.pm | 51 ++++++ api/v1/swagger/definitions/itemtype.yaml | 96 ++++++++++++ .../itemtype_translated_description.yaml | 13 ++ api/v1/swagger/paths/itemtypes.yaml | 59 +++++++ api/v1/swagger/swagger.yaml | 7 + t/db_dependent/api/v1/itemtypes.t | 145 ++++++++++++++++++ 6 files changed, 371 insertions(+) create mode 100644 Koha/REST/V1/ItemTypes.pm create mode 100644 api/v1/swagger/definitions/itemtype.yaml create mode 100644 api/v1/swagger/definitions/itemtype_translated_description.yaml create mode 100644 api/v1/swagger/paths/itemtypes.yaml create mode 100644 t/db_dependent/api/v1/itemtypes.t diff --git a/Koha/REST/V1/ItemTypes.pm b/Koha/REST/V1/ItemTypes.pm new file mode 100644 index 0000000000..e2b84e2815 --- /dev/null +++ b/Koha/REST/V1/ItemTypes.pm @@ -0,0 +1,51 @@ +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 . + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::ItemTypes; + +use Try::Tiny; + +=head1 API + +=head2 Methods + +=head3 list + +=cut + +use Data::Dumper; + +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, { embed => $c->stash('koha.embed') } ); + + return $c->render( status => 200, openapi => $itemtypes ); + } + catch { + $c->unhandled_exception($_); + }; + +} + +1; diff --git a/api/v1/swagger/definitions/itemtype.yaml b/api/v1/swagger/definitions/itemtype.yaml new file mode 100644 index 0000000000..e9e156db28 --- /dev/null +++ b/api/v1/swagger/definitions/itemtype.yaml @@ -0,0 +1,96 @@ +--- +type: object +properties: + itemtype: + 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 itemtype + 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: "itemtype_translated_description.yaml" + +additionalProperties: false +required: + - itemtype diff --git a/api/v1/swagger/definitions/itemtype_translated_description.yaml b/api/v1/swagger/definitions/itemtype_translated_description.yaml new file mode 100644 index 0000000000..8cb95287a6 --- /dev/null +++ b/api/v1/swagger/definitions/itemtype_translated_description.yaml @@ -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 diff --git a/api/v1/swagger/paths/itemtypes.yaml b/api/v1/swagger/paths/itemtypes.yaml new file mode 100644 index 0000000000..a13c53b894 --- /dev/null +++ b/api/v1/swagger/paths/itemtypes.yaml @@ -0,0 +1,59 @@ +--- +/itemtypes: + get: + x-mojo-to: ItemTypes#list + operationId: listItemTypes + tags: + - itemtypes + 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 itemtypes + schema: + items: + $ref: "../swagger.yaml#/definitions/itemtype" + 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 diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index c52d03462a..050eb849cb 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -76,6 +76,8 @@ definitions: $ref: ./definitions/item.yaml item_group: $ref: ./definitions/item_group.yaml + itemtype: + $ref: ./definitions/itemtype.yaml job: $ref: ./definitions/job.yaml library: @@ -275,6 +277,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}" + /itemtypes: + $ref: ./paths/itemtypes.yaml#/~1itemtypes /items: $ref: ./paths/items.yaml#/~1items "/items/{item_id}": @@ -863,6 +867,9 @@ tags: - description: "Manage items\n" name: items x-displayName: Items + - description: "Manage item types\n" + name: itemtypes + x-displayName: Item Types - description: "Manage jobs\n" name: jobs x-displayName: Jobs diff --git a/t/db_dependent/api/v1/itemtypes.t b/t/db_dependent/api/v1/itemtypes.t new file mode 100644 index 0000000000..e4e71e6c17 --- /dev/null +++ b/t/db_dependent/api/v1/itemtypes.t @@ -0,0 +1,145 @@ +#!/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 . + +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 ); + +use Data::Dumper; + +subtest 'list_itemtypes() tests' => sub { + + plan tests => 12; + + $schema->storage->txn_begin; + + my $itemtype = $builder->build_object( + { + class => 'Koha::ItemTypes', + value => { + itemtype => 'TEST_IT', + parent_type => undef, + description => 'Test itemtype', + 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 itemtype 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/itemtypes") + ->status_is(200) + ->json_has('/0'); + + for my $json (@{$t->tx->res->json}) { + if ($json->{itemtype} eq 'TEST_IT') { + is($json->{description}, 'Test itemtype'); + ok(!exists $json->{translated_descriptions}); + } + } + + $t->get_ok("//$userid:$password@/api/v1/itemtypes" => { 'x-koha-embed' => 'translated_descriptions' } ) + ->status_is(200) + ->json_has('/0'); + + for my $json (@{$t->tx->res->json}) { + if ($json->{itemtype} eq 'TEST_IT') { + is($json->{description}, 'Test itemtype'); + 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/itemtypes") + ->status_is(403); + + $schema->storage->txn_rollback; +}; -- 2.30.2