From dfb4f8795787d1e898ffe7fc6dabe14472bf963a Mon Sep 17 00:00:00 2001 From: Nick Clemens Date: Tue, 8 Mar 2022 11:34:40 +0000 Subject: [PATCH] Bug 24857: API spec To test: 1 - prove t/db_dependent/api/v1/item_groups.t --- Koha/REST/V1/Biblios/ItemGroups.pm | 222 ++++++++++ Koha/REST/V1/Biblios/ItemGroups/Items.pm | 159 +++++++ api/v1/swagger/definitions/item_group.yaml | 37 ++ api/v1/swagger/paths/biblios_item_groups.yaml | 388 ++++++++++++++++++ api/v1/swagger/swagger.yaml | 10 + t/db_dependent/api/v1/item_groups.t | 265 ++++++++++++ 6 files changed, 1081 insertions(+) create mode 100644 Koha/REST/V1/Biblios/ItemGroups.pm create mode 100644 Koha/REST/V1/Biblios/ItemGroups/Items.pm create mode 100644 api/v1/swagger/definitions/item_group.yaml create mode 100644 api/v1/swagger/paths/biblios_item_groups.yaml create mode 100755 t/db_dependent/api/v1/item_groups.t diff --git a/Koha/REST/V1/Biblios/ItemGroups.pm b/Koha/REST/V1/Biblios/ItemGroups.pm new file mode 100644 index 0000000000..ddfdac3aa5 --- /dev/null +++ b/Koha/REST/V1/Biblios/ItemGroups.pm @@ -0,0 +1,222 @@ +package Koha::REST::V1::Biblios::ItemGroups; + +# 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::Biblio::ItemGroups; + +use Scalar::Util qw(blessed); +use Try::Tiny; + +=head1 NAME + +Koha::REST::V1::Biblios::ItemGroups - Koha REST API for handling item groups (V1) + +=head1 API + +=head2 Methods + +=cut + +=head3 list + +Controller function that handles listing Koha::Biblio::ItemGroup objects + +=cut + +sub list { + my $c = shift->openapi->valid_input or return; + my $biblio_id = $c->validation->param('biblio_id'); + + my $biblio=Koha::Biblios->find( $biblio_id); + + return try { +#my $item_groups_set = Koha::Biblio::ItemGroups->new; + my $item_groups_set = $biblio->item_groups; + my $item_groups = $c->objects->search( $item_groups_set ); + return $c->render( + status => 200, + openapi => $item_groups + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 get + +Controller function that handles retrieving a single Koha::Biblio::ItemGroup + +=cut + +sub get { + my $c = shift->openapi->valid_input or return; + + try { + my $item_group_id = $c->validation->param('item_group_id'); + my $biblio_id = $c->validation->param('biblio_id'); + + my $item_group = $c->objects->find( Koha::Biblio::ItemGroups->new, $item_group_id ); + + if ( $item_group && $item_group->{biblio_id} eq $biblio_id ) { + return $c->render( + status => 200, + openapi => $item_group + ); + } + else { + return $c->render( + status => 404, + openapi => { + error => 'Item group not found' + } + ); + } + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 add + +Controller function to handle adding a Koha::Biblio::ItemGroup object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + return try { + my $item_group_data = $c->validation->param('body'); + # biblio_id comes from the path + $item_group_data->{biblio_id} = $c->validation->param('biblio_id'); + + my $item_group = Koha::Biblio::ItemGroup->new_from_api($item_group_data); + $item_group->store->discard_changes(); + + $c->res->headers->location( $c->req->url->to_string . '/' . $item_group->id ); + + return $c->render( + status => 201, + openapi => $item_group->to_api + ); + } + catch { + if ( blessed($_) ) { + my $to_api_mapping = Koha::Biblio::ItemGroup->new->to_api_mapping; + + if ( $_->isa('Koha::Exceptions::Object::FKConstraint') + and $_->broken_fk eq 'biblio_id' ) + { + return $c->render( + status => 404, + openapi => { error => "Biblio not found" } + ); + } + } + + $c->unhandled_exception($_); + }; +} + +=head3 update + +Controller function to handle updating a Koha::Biblio::ItemGroup object + +=cut + +sub update { + my $c = shift->openapi->valid_input or return; + + return try { + my $item_group_id = $c->validation->param('item_group_id'); + my $biblio_id = $c->validation->param('biblio_id'); + + my $item_group = Koha::Biblio::ItemGroups->find( $item_group_id ); + + unless ( $item_group && $item_group->biblio_id eq $biblio_id ) { + return $c->render( + status => 404, + openapi => { + error => 'Item group not found' + } + ); + } + + my $item_group_data = $c->validation->param('body'); + $item_group->set_from_api( $item_group_data )->store->discard_changes(); + + return $c->render( + status => 200, + openapi => $item_group->to_api + ); + } + catch { + if ( blessed($_) ) { + my $to_api_mapping = Koha::Biblio::ItemGroup->new->to_api_mapping; + + if ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) { + return $c->render( + status => 409, + openapi => { + error => "Given " . $to_api_mapping->{ $_->broken_fk } . " does not exist" + } + ); + } + } + + $c->unhandled_exception($_); + }; +} + +=head3 delete + +Controller function that handles deleting a Koha::Biblio::ItemGroup object + +=cut + +sub delete { + + my $c = shift->openapi->valid_input or return; + + my $item_group_id = $c->validation->param('item_group_id'); + my $biblio_id = $c->validation->param('biblio_id'); + + my $item_group = Koha::Biblio::ItemGroups->find( + { item_group_id => $item_group_id, biblio_id => $biblio_id } ); + + if ( not defined $item_group ) { + return $c->render( + status => 404, + openapi => { error => "Item group not found" } + ); + } + + return try { + $item_group->delete; + return $c->render( status => 204, openapi => '' ); + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/Koha/REST/V1/Biblios/ItemGroups/Items.pm b/Koha/REST/V1/Biblios/ItemGroups/Items.pm new file mode 100644 index 0000000000..a946e5fefc --- /dev/null +++ b/Koha/REST/V1/Biblios/ItemGroups/Items.pm @@ -0,0 +1,159 @@ +package Koha::REST::V1::Biblios::ItemGroups::Items; + +# 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::Biblio::ItemGroup::Items; + +use Scalar::Util qw(blessed); +use Try::Tiny; + +=head1 NAME + +Koha::REST::V1::Biblios::ItemGroups::Items - Koha REST API for handling item group items (V1) + +=head1 API + +=head2 Methods + +=head3 add + +Controller function to handle linking an item to a Koha::Biblio::ItemGroup object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + return try { + + my $item_group = Koha::Biblio::ItemGroups->find( + $c->validation->param('item_group_id') + ); + + unless ( $item_group ) { + return $c->render( + status => 404, + openapi => { + error => 'Item group not found' + } + ); + } + + unless ( $item_group->biblio_id eq $c->validation->param('biblio_id') ) { + return $c->render( + status => 409, + openapi => { + error => 'Item group does not belong to passed biblio_id' + } + ); + } + + # All good, add the item + my $body = $c->validation->param('body'); + my $item_id = $body->{item_id}; + + $item_group->add_item({ item_id => $item_id }); + + $c->res->headers->location( $c->req->url->to_string . '/' . $item_id ); + + my $embed = $c->stash('koha.embed'); + + return $c->render( + status => 201, + openapi => $item_group->to_api({ embed => $embed }) + ); + } + catch { + if ( blessed($_) ) { + + if ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) { + if ( $_->broken_fk eq 'itemnumber' ) { + return $c->render( + status => 409, + openapi => { + error => "Given item_id does not exist" + } + ); + } + elsif ( $_->broken_fk eq 'biblio_id' ) { + return $c->render( + status => 409, + openapi => { + error => "Given item_id does not belong to the item group's biblio" + } + ); + } + } + elsif ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) { + + return $c->render( + status => 409, + openapi => { + error => "The given item_id is already linked to the item group" + } + ); + } + } + + $c->unhandled_exception($_); + }; +} + +=head3 delete + +Controller function that handles unlinking an item from a Koha::Biblio::ItemGroup object + +=cut + +sub delete { + my $c = shift->openapi->valid_input or return; + + my $item_group_id = $c->validation->param('item_group_id'); + my $item_id = $c->validation->param('item_id'); + + my $item_link = Koha::Biblio::ItemGroup::Items->find( + { + item_id => $item_id, + item_group_id => $item_group_id + } + ); + + unless ( $item_link ) { + return $c->render( + status => 404, + openapi => { + error => 'No such item group <-> item relationship' + } + ); + } + + return try { + $item_link->delete; + return $c->render( + status => 204, + openapi => q{} + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +1; diff --git a/api/v1/swagger/definitions/item_group.yaml b/api/v1/swagger/definitions/item_group.yaml new file mode 100644 index 0000000000..c235d408ef --- /dev/null +++ b/api/v1/swagger/definitions/item_group.yaml @@ -0,0 +1,37 @@ +--- +type: object +properties: + item_group_id: + type: integer + readOnly: true + description: Internal identifier for the item group + biblio_id: + type: integer + readOnly: true + description: Internal identifier for the parent bibliographic record + description: + type: string + description: Item group description + display_order: + type: integer + description: Item group description + creation_date: + type: string + format: date-time + readOnly: true + description: Date and time the item group was created + modification_date: + type: string + format: date-time + readOnly: true + description: Date and time the item group was last modified + items: + type: + - array + - 'null' + readOnly: true + description: A list of items that belong to the volume (x-koha-embed) +additionalProperties: false +required: +- item_group_id +- biblio_id diff --git a/api/v1/swagger/paths/biblios_item_groups.yaml b/api/v1/swagger/paths/biblios_item_groups.yaml new file mode 100644 index 0000000000..99dff022ec --- /dev/null +++ b/api/v1/swagger/paths/biblios_item_groups.yaml @@ -0,0 +1,388 @@ +--- +"/biblios/{biblio_id}/item_groups": + get: + x-mojo-to: Biblios::ItemGroups#list + operationId: listItemGroups + tags: + - item_groups + summary: List item_groups + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - "$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" + - "$ref": "../swagger.yaml#/parameters/q_header" + produces: + - application/yaml + responses: + '200': + description: A list of item_groups + schema: + type: array + items: + "$ref": "../swagger.yaml#/definitions/item_group" + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal server error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" + x-koha-embed: + - items + post: + x-mojo-to: Biblios::ItemGroups#add + operationId: addItemGroup + tags: + - item_groups + summary: Add item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: body + in: body + description: A JSON object representing an item group + required: true + schema: + type: object + properties: + description: + type: string + description: ItemGroup description + display_order: + type: integer + description: Position in waiting queue + produces: + - application/yaml + responses: + '201': + description: A successfully created item group + schema: + "$ref": "../swagger.yaml#/definitions/item_group" + '400': + description: Bad parameter + schema: + "$ref": "../swagger.yaml#/definitions/error" + '401': + description: Authentication required + 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" + '409': + description: Conflict in creating resource + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal server error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" +"/biblios/{biblio_id}/item_groups/{item_group_id}": + get: + x-mojo-to: Biblios::ItemGroups#get + operationId: getItemGroup + tags: + - item_groups + summary: Get item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: item_group_id + in: path + description: Internal identifier for the item_group + required: true + type: string + produces: + - application/yaml + responses: + '200': + description: An item group + schema: + "$ref": "../swagger.yaml#/definitions/item_group" + '400': + description: Missing or wrong parameters + schema: + "$ref": "../swagger.yaml#/definitions/error" + '404': + description: ItemGroup not found + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal server error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-embed: + - items + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" + put: + x-mojo-to: Biblios::ItemGroups#update + operationId: updateItemGroup + tags: + - item_groups + summary: Update item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: item_group_id + in: path + description: Internal identifier for the item group + required: true + type: string + - name: body + in: body + description: A JSON object with the new values for the item group + required: true + schema: + type: object + properties: + description: + type: string + description: ItemGroup description + display_order: + type: integer + description: Position in waiting queue + produces: + - application/yaml + responses: + '200': + description: The updated item group + schema: + "$ref": "../swagger.yaml#/definitions/item_group" + '400': + description: Bad request + schema: + "$ref": "../swagger.yaml#/definitions/error" + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '404': + description: ItemGroup not found + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" + x-koha-embed: + - items + delete: + x-mojo-to: Biblios::ItemGroups#delete + operationId: deleteItemGroup + tags: + - item_groups + summary: Delete item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: item_group_id + in: path + description: Internal identifier for the item group + required: true + type: string + produces: + - application/yaml + responses: + '204': + description: ItemGroup deleted + schema: + type: string + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '404': + description: ItemGroup not found + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" +"/biblios/{biblio_id}/item_groups/{item_group_id}/items": + post: + x-mojo-to: Biblios::ItemGroups::Items#add + operationId: addItemGroupItem + tags: + - item_groups + summary: Add item to item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: item_group_id + in: path + description: Internal identifier for the item group + required: true + type: string + - name: body + in: body + description: A JSON object containing an item_id + required: true + schema: + type: object + properties: + item_id: + type: integer + description: Internal identifier for an item to be linked + produces: + - application/yaml + responses: + '201': + description: Item linked to item group + '400': + description: Bad request + schema: + "$ref": "../swagger.yaml#/definitions/error" + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '409': + description: Request conflicts + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" + x-koha-embed: + - items +"/biblios/{biblio_id}/item_groups/{item_group_id}/items/{item_id}": + delete: + x-mojo-to: Biblios::ItemGroups::Items#delete + operationId: deleteItemGroupItems + tags: + - item_groups + summary: Delete item from item group + parameters: + - name: biblio_id + in: path + description: Internal identifier for the parent bibliographic record + required: true + type: string + - name: item_group_id + in: path + description: Internal identifier for the item group + required: true + type: string + - name: item_id + in: path + description: Internal identifier for the item + required: true + type: string + produces: + - application/yaml + responses: + '204': + description: Item unlinked from item group + schema: + type: string + '401': + description: Authentication required + schema: + "$ref": "../swagger.yaml#/definitions/error" + '403': + description: Access forbidden + schema: + "$ref": "../swagger.yaml#/definitions/error" + '404': + description: Item not linked to item group + schema: + "$ref": "../swagger.yaml#/definitions/error" + '500': + description: Internal error + schema: + "$ref": "../swagger.yaml#/definitions/error" + '503': + description: Under maintenance + schema: + "$ref": "../swagger.yaml#/definitions/error" + x-koha-authorization: + permissions: + catalogue: "manage_item_groups" diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 4233f0d6b6..79a998ab5b 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -42,6 +42,8 @@ definitions: $ref: ./definitions/invoice.yaml item: $ref: ./definitions/item.yaml + item_group: + $ref: ./definitions/item_group.yaml library: $ref: ./definitions/library.yaml order: @@ -101,6 +103,14 @@ paths: $ref: "./paths/biblios.yaml#/~1biblios~1{biblio_id}~1items" "/biblios/{biblio_id}/pickup_locations": $ref: "./paths/biblios.yaml#/~1biblios~1{biblio_id}~1pickup_locations" + "/biblios/{biblio_id}/item_groups": + $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups" + "/biblios/{biblio_id}/item_groups/{item_group_id}": + $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}" + "/biblios/{biblio_id}/item_groups/{item_group_id}/items": + $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items" + "/biblios/{biblio_id}/item_groups/{item_group_id}/items/{item_id}": + $ref: "./paths/biblios_item_groups.yaml#/~1biblios~1{biblio_id}~1item_groups~1{item_group_id}~1items~1{item_id}" "/cash_registers/{cash_register_id}/cashups": $ref: "./paths/cash_registers.yaml#/~1cash_registers~1{cash_register_id}~1cashups" "/cashups/{cashup_id}": diff --git a/t/db_dependent/api/v1/item_groups.t b/t/db_dependent/api/v1/item_groups.t new file mode 100755 index 0000000000..3506eae582 --- /dev/null +++ b/t/db_dependent/api/v1/item_groups.t @@ -0,0 +1,265 @@ +#!/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 => 5; +use Test::Mojo; +use Test::Warn; + +use t::lib::TestBuilder; +use t::lib::Mocks; + +use List::Util qw(min); + +use Koha::Biblio::ItemGroups; +use Koha::Libraries; +use Koha::Database; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); +t::lib::Mocks::mock_preference( 'EnableItemGroups', 1 ); + +my $t = Test::Mojo->new('Koha::REST::V1'); + +subtest 'list() tests' => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + my $patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 4 } + }); + my $password = 'thePassword123'; + $patron->set_password({ password => $password, skip_validation => 1 }); + my $userid = $patron->userid; + + my $biblio = $builder->build_sample_biblio(); + my $biblio_id = $biblio->id; + + $t->get_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups" ) + ->status_is( 200, 'SWAGGER3.2.2' ); + my $response_count = scalar @{ $t->tx->res->json }; + is( $response_count, 0, 'Results count is 2'); + + my $item_group_1 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id, display_order => 1, description => "Vol 1" } )->store(); + + $t->get_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups" ) + ->status_is( 200, 'SWAGGER3.2.2' ); + $response_count = scalar @{ $t->tx->res->json }; + is( $response_count, 1, 'Results count is 2'); + + my $item_group_2 = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id, display_order => 2, description => "Vol 2" } )->store(); + + $t->get_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups" ) + ->status_is( 200, 'SWAGGER3.2.2' ); + + $response_count = scalar @{ $t->tx->res->json }; + is( $response_count, 2, 'Results count is 2'); + + $schema->storage->txn_rollback; +}; + +subtest 'add() tests' => sub { + + plan tests => 6; + + $schema->storage->txn_begin; + + my $authorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 1 } + }); + my $password = 'thePassword123'; + $authorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $auth_userid = $authorized_patron->userid; + + my $unauthorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 0 } + }); + $unauthorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $unauth_userid = $unauthorized_patron->userid; + + my $biblio = $builder->build_sample_biblio(); + my $biblio_id = $biblio->id; + my $item_group = { description => 'Vol 1', display_order => 1 }; + + # Unauthorized attempt + $t->post_ok( "//$unauth_userid:$password@/api/v1/biblios/$biblio_id/item_groups" => json => $item_group ) + ->status_is(403); + + # Authorized attempt + $t->post_ok( "//$auth_userid:$password@/api/v1/biblios/$biblio_id/item_groups" => json => $item_group ) + ->status_is( 201, 'SWAGGER3.2.1' ); + + # Invalid biblio id + { # hide useless warnings + local *STDERR; + open STDERR, '>', '/dev/null'; + $t->post_ok( "//$auth_userid:$password@/api/v1/biblios/XXX/item_groups" => json => $item_group ) + ->status_is( 404 ); + close STDERR; + } + + $schema->storage->txn_rollback; +}; + +subtest 'update() tests' => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + my $authorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 1 } + }); + my $password = 'thePassword123'; + $authorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $auth_userid = $authorized_patron->userid; + + my $unauthorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 0 } + }); + $unauthorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $unauth_userid = $unauthorized_patron->userid; + + my $biblio = $builder->build_sample_biblio(); + my $biblio_id = $biblio->id; + my $item_group = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id, display_order => 1, description => "Vol 1" } )->store(); + my $item_group_id = $item_group->id; + + # Unauthorized attempt + $t->put_ok( "//$unauth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_group_id" + => json => { description => 'New unauthorized desc change' } ) + ->status_is(403); + + # Authorized attempt + $t->put_ok( "//$auth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_group_id" => json => { description => "Vol A" } ) + ->status_is(200, 'SWAGGER3.2.1') + ->json_has( '/description' => "Vol A", 'SWAGGER3.3.3' ); + + # Invalid biblio id + $t->put_ok( "//$auth_userid:$password@/api/v1/biblios/XXX/item_groups/$item_group_id" => json => { description => "Vol A" } ) + ->status_is(404); + + # Invalid item group id + $t->put_ok( "//$auth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/XXX" => json => { description => "Vol A" } ) + ->status_is(404); + + $schema->storage->txn_rollback; +}; + +subtest 'delete() tests' => sub { + plan tests => 9; + + $schema->storage->txn_begin; + + my $authorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 1 } + }); + my $password = 'thePassword123'; + $authorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $auth_userid = $authorized_patron->userid; + + my $unauthorized_patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 0 } + }); + $unauthorized_patron->set_password({ password => $password, skip_validation => 1 }); + my $unauth_userid = $unauthorized_patron->userid; + + my $biblio = $builder->build_sample_biblio(); + my $biblio_id = $biblio->id; + my $item_group = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id, display_order => 1, description => "Vol 1" } )->store(); + my $item_groupid = $item_group->id; + + $t->delete_ok( "//$auth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_groupid" ) + ->status_is(204, 'SWAGGER3.2.4') + ->content_is('', 'SWAGGER3.3.4'); + + # Unauthorized attempt to delete + $t->delete_ok( "//$unauth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_groupid" ) + ->status_is(403); + + $t->delete_ok( "//$auth_userid:$password@/api/v1/biblios/XXX/item_groups/$item_groupid" ) + ->status_is(404); + + $t->delete_ok( "//$auth_userid:$password@/api/v1/biblios/$biblio_id/item_groups/XXX" ) + ->status_is(404); + + $schema->storage->txn_rollback; +}; + +subtest 'volume items add() + delete() tests' => sub { + plan tests => 14; + + $schema->storage->txn_begin; + + my $patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 4 } + }); + my $password = 'thePassword123'; + $patron->set_password({ password => $password, skip_validation => 1 }); + my $userid = $patron->userid; + + my $biblio = $builder->build_sample_biblio(); + my $biblio_id = $biblio->id; + + my $item_group = Koha::Biblio::ItemGroup->new( { biblio_id => $biblio->id, display_order => 1, description => "Vol 1" } )->store(); + my $item_groupid = $item_group->id; + + my @items = $item_group->items->as_list; + is( scalar(@items), 0, 'Item group has no items'); + + my $item_1 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber }); + my $item_1_id = $item_1->id; + + $t->post_ok( "//$userid:$password@/api/v1/biblios/XXX/item_groups/$item_groupid/items" => json => { item_id => $item_1->id } ) + ->status_is( 409 ) + ->json_is( { error => 'Item group does not belong to passed biblio_id' } ); + + $t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_groupid/items" => json => { item_id => $item_1->id } ) + ->status_is( 201, 'SWAGGER3.2.1' ); + + @items = $item_group->items; + is( scalar(@items), 1, 'Item group now has one item'); + + my $item_2 = $builder->build_sample_item({ biblionumber => $biblio->biblionumber }); + my $item_2_id = $item_2->id; + + $t->post_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_groupid/items" => json => { item_id => $item_2->id } ) + ->status_is( 201, 'SWAGGER3.2.1' ); + + @items = $item_group->items->as_list; + is( scalar(@items), 2, 'Item group now has two items'); + + $t->delete_ok( "//$userid:$password@/api/v1/biblios/$biblio_id/item_groups/$item_groupid/items/$item_1_id" ) + ->status_is(204, 'SWAGGER3.2.4') + ->content_is('', 'SWAGGER3.3.4'); + + @items = $item_group->items; + is( scalar(@items), 1, 'Item group now has one item'); + + $schema->storage->txn_rollback; +}; -- 2.30.2