From 1785c42d01d0f89df90e20ca23c165b6036bb2f8 Mon Sep 17 00:00:00 2001 From: Johanna Raisa Date: Tue, 17 May 2022 11:12:21 +0300 Subject: [PATCH 2/2] Bug 30661: [FOLLOW-UP] add separate endpoints This patch adds separate endpoints for updating hold_date and expiration_date. Test plan: 1) prove t/db_dependent/api/v1/holds.t Sponsored-by: Koha-Suomi Oy --- Koha/REST/V1/Holds.pm | 88 ++++++++++++++++++++ api/v1/swagger/paths/holds.yaml | 136 +++++++++++++++++++++++++++++++ api/v1/swagger/swagger.yaml | 4 + t/db_dependent/api/v1/holds.t | 139 +++++++++++++++++++++++++++++++- 4 files changed, 366 insertions(+), 1 deletion(-) diff --git a/Koha/REST/V1/Holds.pm b/Koha/REST/V1/Holds.pm index 0d445aa3b4..a761ac4a94 100644 --- a/Koha/REST/V1/Holds.pm +++ b/Koha/REST/V1/Holds.pm @@ -555,5 +555,93 @@ sub update_pickup_location { }; } +=head3 update_hold_date + +Method that handles modifying the hold date of a Koha::Hold object + +=cut + +sub update_hold_date { + my $c = shift->openapi->valid_input or return; + + my $hold_id = $c->validation->param('hold_id'); + my $body = $c->validation->param('body'); + my $hold_date = $body->{hold_date}; + + my $hold = Koha::Holds->find($hold_id); + + unless (C4::Context->preference('AllowHoldDateInFuture')) { + return $c->render( + status => 403, + openapi => { error => "Update not allowed" } + ); + } + + unless ($hold) { + return $c->render( + status => 404, + openapi => { error => "Hold not found" } + ); + } + + return try { + + $hold->set({reservedate => $hold_date})->store; + + return $c->render( + status => 200, + openapi => { + hold_date => $hold_date + } + ); + } + catch { + $c->unhandled_exception($_); + }; +} + +=head3 update_expiration_date + +Method that handles modifying the expiration date of a Koha::Hold object + +=cut + +sub update_expiration_date { + my $c = shift->openapi->valid_input or return; + + my $hold_id = $c->validation->param('hold_id'); + my $body = $c->validation->param('body'); + my $expiration_date = $body->{expiration_date}; + + my $hold = Koha::Holds->find($hold_id); + + unless ($hold) { + return $c->render( + status => 404, + openapi => { error => "Hold not found" } + ); + } + + return try { + + my $params = { + reserve_id => $hold_id, + expirationdate => $expiration_date + }; + + C4::Reserves::ModReserve($params); + + return $c->render( + status => 200, + openapi => { + expiration_date => $expiration_date + } + ); + } + catch { + $c->unhandled_exception($_); + }; +} + 1; diff --git a/api/v1/swagger/paths/holds.yaml b/api/v1/swagger/paths/holds.yaml index f954f7dd8b..990a965024 100644 --- a/api/v1/swagger/paths/holds.yaml +++ b/api/v1/swagger/paths/holds.yaml @@ -695,3 +695,139 @@ x-koha-authorization: permissions: reserveforothers: place_holds +"/holds/{hold_id}/hold_date": + put: + x-mojo-to: Holds#update_hold_date + operationId: updateHoldDate + tags: + - holds + summary: Update hold date for the hold + description: Set an hold date for the hold + parameters: + - $ref: "../swagger.yaml#/parameters/hold_id_pp" + - name: body + in: body + description: Hold date + required: true + schema: + type: object + properties: + hold_date: + type: + - string + - 'null' + description: Hold date + additionalProperties: false + produces: + - application/json + responses: + "200": + description: The hold date value for the hold + schema: + type: object + properties: + hold_date: + type: + - string + - 'null' + description: The hold date + additionalProperties: false + "400": + description: Missing or wrong parameters + 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: Hold not found + schema: + $ref: ../swagger.yaml#/definitions/error + "409": + description: Unable to perform action on hold + 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: + reserveforothers: place_holds +"/holds/{hold_id}/expiration_date": + put: + x-mojo-to: Holds#update_expiration_date + operationId: updateHoldExpirationDate + tags: + - holds + summary: Update expiration date for the hold + description: Set an expiration date for the hold + parameters: + - $ref: "../swagger.yaml#/parameters/hold_id_pp" + - name: body + in: body + description: Expiration date + required: true + schema: + type: object + properties: + expiration_date: + type: + - string + - 'null' + description: Expiration date + additionalProperties: false + produces: + - application/json + responses: + "200": + description: The expiration date value for the hold + schema: + type: object + properties: + expiration_date: + type: + - string + - 'null' + description: The expiration date + additionalProperties: false + "400": + description: Missing or wrong parameters + 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: Hold not found + schema: + $ref: ../swagger.yaml#/definitions/error + "409": + description: Unable to perform action on hold + 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: + reserveforothers: place_holds diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index a10f2408b6..9afb534fd7 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -157,6 +157,10 @@ paths: $ref: "./paths/holds.yaml#/~1holds~1{hold_id}~1priority" "/holds/{hold_id}/suspension": $ref: "./paths/holds.yaml#/~1holds~1{hold_id}~1suspension" + "/holds/{hold_id}/hold_date": + $ref: ./paths/holds.yaml#/~1holds~1{hold_id}~1hold_date + "/holds/{hold_id}/expiration_date": + $ref: ./paths/holds.yaml#/~1holds~1{hold_id}~1expiration_date /ill_backends: $ref: ./paths/ill_backends.yaml#/~1ill_backends "/ill_backends/{ill_backend_id}": diff --git a/t/db_dependent/api/v1/holds.t b/t/db_dependent/api/v1/holds.t index cc666c4b02..b538449c6e 100755 --- a/t/db_dependent/api/v1/holds.t +++ b/t/db_dependent/api/v1/holds.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 14; +use Test::More tests => 16; use Test::MockModule; use Test::Mojo; use t::lib::TestBuilder; @@ -1461,3 +1461,140 @@ subtest 'delete() tests' => sub { $schema->storage->txn_rollback; }; + +subtest 'PUT /holds/{hold_id}/hold_date tests' => sub { + + plan tests => 10; + + $schema->storage->txn_begin; + + my $password = 'AbcdEFG123'; + + my $library_1 = $builder->build_object( + { class => 'Koha::Libraries', value => { pickup_location => 1 } } ); + + my $patron = $builder->build_object( + { class => 'Koha::Patrons', value => { flags => 0 } } ); + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $patron->userid; + $builder->build( + { + source => 'UserPermission', + value => { + borrowernumber => $patron->borrowernumber, + module_bit => 6, + code => 'place_holds', + }, + } + ); + + # Disable logging + t::lib::Mocks::mock_preference( 'HoldsLog', 0 ); + t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + + my $biblio = $builder->build_sample_biblio; + + # biblio-level hold + my $hold = Koha::Holds->find( + AddReserve( + { + branchcode => $library_1->branchcode, + borrowernumber => $patron->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => undef, + } + ) + ); + + t::lib::Mocks::mock_preference( 'AllowHoldDateInFuture', 0 ); + + $t->put_ok( "//$userid:$password@/api/v1/holds/" + . $hold->id + . "/hold_date" => json => { hold_date => '2022-01-01' } ) + ->status_is(403) + ->json_is({ error => 'Update not allowed' }); + + + t::lib::Mocks::mock_preference( 'AllowHoldDateInFuture', 1 ); + + # Attempt to use an invalid pickup locations ends in 400 + $t->put_ok( "//$userid:$password@/api/v1/holds/0" + . "/hold_date" => json => { hold_date => '2022-01-01' } ) + ->status_is(404) + ->json_is({ error => 'Hold not found' }); + + + $t->put_ok( "//$userid:$password@/api/v1/holds/" + . $hold->id + . "/hold_date" => json => { hold_date => '2022-01-01' } ) + ->status_is(200) + ->json_is({ hold_date => '2022-01-01' }); + + is( $hold->discard_changes->reservedate, '2022-01-01', 'hold date changed correctly' ); + + $schema->storage->txn_rollback; +}; + +subtest 'PUT /holds/{hold_id}/expiration_date tests' => sub { + + plan tests => 7; + + $schema->storage->txn_begin; + + my $password = 'AbcdEFG123'; + + my $library_1 = $builder->build_object( + { class => 'Koha::Libraries', value => { pickup_location => 1 } } ); + + my $patron = $builder->build_object( + { class => 'Koha::Patrons', value => { flags => 0 } } ); + $patron->set_password( { password => $password, skip_validation => 1 } ); + my $userid = $patron->userid; + $builder->build( + { + source => 'UserPermission', + value => { + borrowernumber => $patron->borrowernumber, + module_bit => 6, + code => 'place_holds', + }, + } + ); + + # Disable logging + t::lib::Mocks::mock_preference( 'HoldsLog', 0 ); + t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + + my $biblio = $builder->build_sample_biblio; + + # biblio-level hold + my $hold = Koha::Holds->find( + AddReserve( + { + branchcode => $library_1->branchcode, + borrowernumber => $patron->borrowernumber, + biblionumber => $biblio->biblionumber, + priority => 1, + itemnumber => undef, + } + ) + ); + + # Attempt to use an invalid pickup locations ends in 400 + $t->put_ok( "//$userid:$password@/api/v1/holds/0" + . "/expiration_date" => json => { expiration_date => '2022-01-01' } ) + ->status_is(404) + ->json_is({ error => 'Hold not found' }); + + + $t->put_ok( "//$userid:$password@/api/v1/holds/" + . $hold->id + . "/expiration_date" => json => { expiration_date => '2022-01-01' } ) + ->status_is(200) + ->json_is({ expiration_date => '2022-01-01' }); + + is( $hold->discard_changes->expirationdate, '2022-01-01', 'expiration date changed correctly' ); + + $schema->storage->txn_rollback; +}; -- 2.25.1