From 07569723f25a9d628a9607cf0ba787dccdc80258 Mon Sep 17 00:00:00 2001 From: Kyle M Hall Date: Thu, 16 Nov 2023 10:15:06 -0500 Subject: [PATCH] Bug 35353: Add api endpoint to fetch patron's previous holds We have an endpoint to fetch a patron's current holds ( /api/va/patrons/$id/holds ) but some external services need to get previous holds to see if they were completed/canceled/etc. Test Plan: 1) Create some holds for a patron 2) Complete or cancel those holds 3) Browse to /api/va/patrons/$id/holds/previous where $id is the borrowernumber 4) You should get a list of previous holds! --- Koha/REST/V1/Patrons/Holds.pm | 34 ++++++++++++++ api/v1/swagger/paths/patrons_holds.yaml | 61 +++++++++++++++++++++++++ api/v1/swagger/swagger.yaml | 2 + t/db_dependent/api/v1/patrons_holds.t | 47 ++++++++++++++++++- 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/Koha/REST/V1/Patrons/Holds.pm b/Koha/REST/V1/Patrons/Holds.pm index 88eea15b537..1f62db4fc98 100644 --- a/Koha/REST/V1/Patrons/Holds.pm +++ b/Koha/REST/V1/Patrons/Holds.pm @@ -63,6 +63,40 @@ sub list { }; } +=head3 list_old + +Controller function that handles listing Koha::Old::Hold objects for the requested patron + +=cut + +sub list_old { + my $c = shift->openapi->valid_input or return; + + my $patron = Koha::Patrons->find( $c->param('patron_id') ); + + unless ( $patron ) { + return $c->render( + status => 404, + openapi => { + error => 'Patron not found' + } + ); + } + + return try { + + my $holds = $c->objects->search( $patron->old_holds ); + + return $c->render( + status => 200, + openapi => $holds + ); + } + catch { + $c->unhandled_exception($_); + }; +} + =head3 delete_public diff --git a/api/v1/swagger/paths/patrons_holds.yaml b/api/v1/swagger/paths/patrons_holds.yaml index e8e1335a87d..33d7d63f073 100644 --- a/api/v1/swagger/paths/patrons_holds.yaml +++ b/api/v1/swagger/paths/patrons_holds.yaml @@ -60,3 +60,64 @@ x-koha-authorization: permissions: borrowers: edit_borrowers +"/patrons/{patron_id}/holds/previous": + get: + x-mojo-to: Patrons::Holds#list_old + operationId: getPatronHoldsPrevious + tags: + - holds + summary: List previous holds for a patron + parameters: + - $ref: "../swagger.yaml#/parameters/patron_id_pp" + - $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/request_id_header" + - name: x-koha-embed + in: header + required: false + description: Embed list sent as a request header + type: array + items: + type: string + enum: + - cancellation_requested + collectionFormat: csv + produces: + - application/json + responses: + "200": + description: The patron holds + schema: + type: array + items: + $ref: "../swagger.yaml#/definitions/hold" + "401": + description: Authentication required + schema: + $ref: "../swagger.yaml#/definitions/error" + "403": + description: Access forbidden + schema: + $ref: "../swagger.yaml#/definitions/error" + "404": + description: Patron 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: + borrowers: edit_borrowers diff --git a/api/v1/swagger/swagger.yaml b/api/v1/swagger/swagger.yaml index 46966e40b2b..ca0d16d125e 100644 --- a/api/v1/swagger/swagger.yaml +++ b/api/v1/swagger/swagger.yaml @@ -419,6 +419,8 @@ paths: $ref: "./paths/patrons_extended_attributes.yaml#/~1patrons~1{patron_id}~1extended_attributes~1{extended_attribute_id}" "/patrons/{patron_id}/holds": $ref: "./paths/patrons_holds.yaml#/~1patrons~1{patron_id}~1holds" + "/patrons/{patron_id}/holds/previous": + $ref: "./paths/patrons_holds.yaml#/~1patrons~1{patron_id}~1holds~1previous" "/patrons/{patron_id}/password": $ref: "./paths/patrons_password.yaml#/~1patrons~1{patron_id}~1password" "/patrons/{patron_id}/password/expiration_date": diff --git a/t/db_dependent/api/v1/patrons_holds.t b/t/db_dependent/api/v1/patrons_holds.t index 98cffc375b1..3ed6e6a8daa 100755 --- a/t/db_dependent/api/v1/patrons_holds.t +++ b/t/db_dependent/api/v1/patrons_holds.t @@ -17,7 +17,7 @@ use Modern::Perl; -use Test::More tests => 2; +use Test::More tests => 3; use Test::MockModule; use Test::Mojo; @@ -70,6 +70,51 @@ subtest 'list() tests' => sub { $schema->storage->txn_rollback; }; +subtest 'list_old() tests' => sub { + + plan tests => 9; + + $schema->storage->txn_begin; + + my $patron = $builder->build_object({ + class => 'Koha::Patrons', + value => { flags => 2 ** 4 } # 'borrowers' flag == 4 + }); + my $password = 'thePassword123'; + $patron->set_password({ password => $password, skip_validation => 1 }); + my $userid = $patron->userid; + + $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id . '/holds/previous') + ->status_is( 200, 'SWAGGER3.2.2' ) + ->json_is( [] ); + + my $hold_1 = $builder->build_object({ class => 'Koha::Holds', value => { borrowernumber => $patron->id } }); + $hold_1->cancel(); + my $cancellationdate = $hold_1->cancellationdate; + $cancellationdate =~ s/ \d\d:\d\d:\d\d//; + $hold_1->cancellationdate( $cancellationdate ); + my $hold_2 = $builder->build_object({ class => 'Koha::Holds', value => { borrowernumber => $patron->id } }); + $hold_2->cancel(); + $cancellationdate = $hold_2->cancellationdate; + $cancellationdate =~ s/ \d\d:\d\d:\d\d//; + $hold_2->cancellationdate( $cancellationdate ); + + $t->get_ok("//$userid:$password@/api/v1/patrons/" . $patron->id . '/holds/previous?_order_by=+me.hold_id') + ->status_is( 200, 'SWAGGER3.2.2' ) + ->json_is( '' => [ $hold_1->to_api, $hold_2->to_api ], 'Holds retrieved' ); + + my $non_existent_patron = $builder->build_object({ class => 'Koha::Patrons' }); + my $non_existent_patron_id = $non_existent_patron->id; + # get rid of the patron + $non_existent_patron->delete; + + $t->get_ok("//$userid:$password@/api/v1/patrons/" . $non_existent_patron_id . '/holds/previous') + ->status_is( 404 ) + ->json_is( '/error' => 'Patron not found' ); + + $schema->storage->txn_rollback; +}; + subtest 'delete_public() tests' => sub { plan tests => 13; -- 2.30.2