[Bug 43602] New: Add REST API endpoints for article request status transitions
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 Bug ID: 43602 Summary: Add REST API endpoints for article request status transitions Initiative type: --- Sponsorship --- status: Product: Koha Version: Main Hardware: All OS: All Status: NEW Severity: enhancement Priority: P5 - low Component: REST API Assignee: koha-bugs@lists.koha-community.org Reporter: martin.renvoize@openfifth.co.uk QA Contact: tomascohen@gmail.com CC: tomascohen@gmail.com Target Milestone: --- Currently the REST API only exposes cancellation for article requests: - DELETE /api/v1/article_requests/{article_request_id} (Koha::REST::V1::ArticleRequests#cancel) - DELETE /api/public/patrons/{patron_id}/article_requests/{article_request_id} (#patron_cancel) Every other step of the article request lifecycle is missing from the REST API and instead goes through the legacy cookie-authenticated CGI script svc/article_request (op=cud-process|cud-pending|cud-complete|cud-update_urls|cud-update_library_id), called from koha-tmpl/intranet-tmpl/prog/js/fetch/article-request-api-client.js: - Set request as pending (Koha::ArticleRequest->set_pending, status -> PENDING) - Process request (Koha::ArticleRequest->process, status -> PROCESSING) - Complete request (Koha::ArticleRequest->complete, status -> COMPLETED) - Update delivery URL(s) for SCAN format requests (the urls column) - Update pickup library (the branchcode column) There is also no REST endpoint to create an article request (Koha::ArticleRequest->request), used by opac/opac-request-article.pl and circ/request-article.pl. This means there is no documented/stable way to drive the article request fulfilment workflow (the staff queue at circ/article-requests.pl) programmatically via API key/OAuth2 - only cancellation is covered by the documented API. Suggested scope: 1. POST /api/v1/article_requests (and/or a nested /api/v1/biblios/{biblio_id}/article_requests) to create a request 2. PATCH /api/v1/article_requests/{article_request_id} to support status transitions (pending/processing/completed) and updates to urls/branchcode, reusing the same authorization (circulate: circulate_remaining_permissions) as the existing cancel route 3. An equivalent public route under /api/public/patrons/{patron_id}/article_requests for OPAC self-service creation, mirroring the existing patron_cancel route Filling this gap would let svc/article_request be retired in favour of the REST API, and let koha-tmpl/intranet-tmpl/prog/js/fetch/article-request-api-client.js move off the legacy endpoint, consistent with Koha's move away from ad hoc svc/ scripts. -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 --- Comment #1 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Suggested API shape, based on comparing this to two other request-workflow modules already in the codebase (Tickets/catalog concerns, and the newer ISO18626 supplying-agency API): Design precedent ----------------- Two existing patterns were compared: 1. Tickets (Koha::REST::V1::Tickets, api/v1/swagger/paths/tickets.yaml): full CRUD resource. Status/assignee changes are just fields on the same object, applied via a generic PUT /tickets/{id}; side effects (notices, resolver_id/resolved_date, a Ticket::Update audit row) are triggered by inspecting which columns changed (get_dirty_columns), not by one bespoke endpoint per transition. 2. ISO18626 supplying-agency requests (Koha::REST::V1::ILL::ISO18626::Requests, api/v1/swagger/paths/ill_iso18626_requests.yaml): GET list/single plus PUT .../{id} (the "edit" action), which accepts a JSON body containing "status" (plus protocol-specific fields) and passes it straight to $request->progress_request('supplyingAgency', $body) to drive the underlying state machine and emit the correct outbound message. This is the newest ILL-adjacent code in the module and is entirely Vue + REST driven (SupplyingResource.vue / RequestingAgencyResource.vue), with no legacy op=cud-* CGI involved at all. Both converge on: one resource, one generic update verb, status supplied in the body, business logic and side effects staying in the object layer rather than in bespoke controller actions per transition. This is the opposite of the current article request and legacy ILL request design, where each transition is its own Perl method invoked via a distinct op= value on a cookie-authenticated CGI/svc script. Suggested shape for article requests ------------------------------------- Add a new swagger definition "article_request" (Koha::ArticleRequest currently has no to_api_mapping/public_read_list at all, so this is new plumbing, not just new routes). Fields: article_request_id, patron_id, biblio_id, item_id, library_id (branchcode), title, author, volume, issue, date, pages, chapters, patron_notes, toc_request, status (read-only), format, urls, cancellation_reason, notes, debit_id (read-only), created_on (read-only), updated_on (read-only), with optional x-koha-embed for biblio/item/patron/library. Endpoints: GET /api/v1/article_requests list (staff queue; filter by status/branchcode via -q-/embed, replacing the three canned queries requested()/pending()/processing() the .pl script currently builds by hand) GET /api/v1/article_requests/{article_request_id} get single POST /api/v1/article_requests create on a patron's behalf (staff), wraps Koha::ArticleRequest->request PUT /api/v1/article_requests/{article_request_id} generic update: body may set status (PENDING|PROCESSING|COMPLETED|CANCELED), urls, library_id. Controller maps a changed "status" to the matching existing object method (set_pending/process/complete/cancel) rather than writing the column directly, so fee charging/refunding and notify() stay centralised in Koha::ArticleRequest exactly as they are today. DELETE /api/v1/article_requests/{article_request_id} unchanged (existing cancel action, already released - keep for backward compatibility; PUT status=CANCELED becomes an equivalent path going forward) POST /api/public/patrons/{patron_id}/article_requests patron self-service create (OPAC), wraps the same ->request call used by opac-request-article.pl. Currently missing entirely - only the public cancel route exists. DELETE /api/public/patrons/{patron_id}/article_requests/{article_request_id} unchanged (existing patron_cancel action) Permissions unchanged from what's already enforced: circulate/circulate_remaining_permissions for staff routes (matches the existing DELETE route and the svc/article_request auth check), patron auth for the public routes (matches the existing patron_cancel route). Once this exists, koha-tmpl/intranet-tmpl/prog/js/fetch/article-request-api-client.js and the JS in circ/article-requests.tt / circ/request-article.tt can be moved off svc/article_request onto the new endpoints, and that legacy script can eventually be retired. That migration/retirement would be a sensible follow-up rather than part of this bug's initial scope. -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 --- Comment #2 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 206596 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=206596&action=edit Bug 43602: Add GET /api/v1/article_requests/{article_request_id} and public_read_list Adds the single-resource read action alongside the existing list action, using the same objects.to_api helper, and a _find($c) helper that looks requests up via Koha::ArticleRequests->search_limited so both actions honor the same branch-visibility restriction (a staff user without borrowers: view_borrower_infos_from_any_libraries can only see requests from patrons in libraries they're allowed to see). Also adds public_read_list to Koha::ArticleRequest, needed so a later public-facing endpoint can safely return the article request representation to the patron without leaking the internal debit_id accounting reference. Koha::Object::to_api filters public_read_list BEFORE renaming columns via to_api_mapping, so the list is expressed in DB column names (id, borrowernumber, biblionumber, itemnumber, branchcode, ...), not their API-facing equivalents - getting this wrong would silently drop patron_id/biblio_id/article_request_id/item_id/ library_id from every public response, since Koha::Object::TO_JSON hasn't renamed anything yet at the point the filter runs. Covered by a behavioral test that builds a real article request and asserts the actual keys present in to_api({public => 1}) output, rather than just checking the method's return value against a copy of itself. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 --- Comment #3 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 206597 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=206597&action=edit Bug 43602: Add POST /api/v1/article_requests and public patron_add Adds staff and public creation endpoints for article requests, backed by Koha::ArticleRequest->new_from_api($body)->request (the existing Koha::ArticleRequest workflow method, unchanged). add() and patron_add() validate patron_id/biblio_id against the database before attempting the write, following the pattern already used in Koha::REST::V1::Holds, rather than relying on DBIx::Class to raise (and suppressing its warning) on the resulting FK constraint violation - a suppressed warning is easy to lose track of and gives a worse error to the API client. A shared _handle_write_exception($c, $exception) helper maps the remaining Koha::Exceptions (article request limit reached, unsupported format, FK constraint) to the matching HTTP status and error_code, so add() and patron_add() do not duplicate that mapping. Both actions call ->discard_changes on the created object before serializing the response, so DB-computed columns such as updated_on (ON UPDATE current_timestamp()) are reflected accurately rather than echoing back the client's request values. patron_add is the public counterpart, reachable only via $c->auth->public and always overwriting patron_id from the URL's patron_id rather than trusting the request body, so a patron can only ever create a request for themselves. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are watching all bug changes. You are the assignee for the bug.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 --- Comment #4 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 206598 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=206598&action=edit Bug 43602: Replace complete/cancel with a generic PATCH edit action Replaces the three narrow, non-REST actions (complete, cancel, patron_cancel - the last two DELETE-based) with a single PATCH edit()/patron_edit() action that drives every status transition (pending/processing/completed/canceled), delivery URL updates and pickup library reassignment through one endpoint, matching the shape of Koha::REST::V1::ILL::ISO18626::Requests#edit. The previous DELETE-based cancel routes are confirmed unused outside this codebase (only reachable from our own staff and OPAC templates, migrated in the next commit), so removing them is safe. patron_edit is intentionally narrower than the staff edit action: a patron may only set status to CANCELED on their own request, matching what patron_cancel used to allow. A new _rejected_status_transition($article_request, $new_status) guard, shared by edit() and patron_edit(), rejects two cases the underlying Koha::ArticleRequest status methods (set_pending/process/complete/cancel) deliberately do not guard against themselves, so as not to change behavior for their other callers (opac-article-request-cancel.pl, Koha::ArticleRequest::ScanImport): re-running a transition on a request already in a terminal status - notably re-cancelling an already-CANCELED request, which would re-run the fee refund logic - and "transitioning" to the status the request is already in. The guard runs before any database mutation in edit(), so a rejected transition returns 400 without partially applying the urls/library_id changes from the same request body. Also wraps add()/patron_add()'s request() + store() calls in Koha::Database->new->schema->txn_do(sub {...}), matching Koha::REST::V1::ILL::Requests#add: request() charges an article-request fee debit before store() runs, and store() can still throw (WrongFormat, or an FK constraint not caught by the pre-validation added in the previous commit); without the transaction a thrown exception would leave that fee debit orphaned with no corresponding article request. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 --- Comment #5 from Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> --- Created attachment 206599 --> https://bugs.koha-community.org/bugzilla3/attachment.cgi?id=206599&action=edit Bug 43602: Migrate staff and OPAC UI off svc/article_request Rewrites koha-tmpl/intranet-tmpl/prog/js/fetch/article-request-api-client.js to wrap the new REST endpoints (GET/POST/PATCH under /api/v1/article_requests and /api/v1/public/patrons/{patron_id}/article_requests) instead of posting op=cud-* actions to the legacy cookie-authenticated svc/article_request CGI script, and updates the staff article-requests queue (circ/article-requests.tt), the staff request-article form (circ/request-article.tt) and the OPAC patron account page (opac-user.tt) to call the client's new methods. The previous DELETE-based cancel routes are already gone as of the previous commit, so this finishes moving every caller in this codebase onto the REST client. Removing svc/article_request itself is left as a separate follow-up rather than folded into this bug, to keep this series focused on the new API surface. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- Status|NEW |Needs Signoff Assignee|koha-bugs@lists.koha-commun |martin.renvoize@openfifth.c |ity.org |o.uk -- You are receiving this mail because: You are the assignee for the bug. You are watching all bug changes.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=43602 Martin Renvoize (ashimema) <martin.renvoize@openfifth.co.uk> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |m.de.rooy@rijksmuseum.nl -- You are receiving this mail because: You are watching all bug changes.
participants (1)
-
bugzilla-daemon@bugs.koha-community.org