From 35077b01b06ab6e0f294054ec4f9e94ed13fb573 Mon Sep 17 00:00:00 2001 From: Julian Maurice Date: Wed, 5 Nov 2025 13:36:43 -0300 Subject: [PATCH] Bug 29668: Add API route to add a basket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Example usage: POST /api/v1/acquisitions/baskets { "vendor_id": 1, "name": "Basket #1", } Test plan: 1. Try requesting this endpoint with your favorite API tool (I recommend https://github.com/frigus02/RESTer) 2. Run `prove t/db_dependent/api/v1/acquisitions_baskets/post.t` Edit (tcohen): Re-did the commit with some fixes in the spec Signed-off-by: Tomás Cohen Arazi Signed-off-by: Laura_Escamilla --- Koha/Acquisition/Basket.pm | 1 + Koha/REST/V1/Acquisitions/Baskets.pm | 30 ++++++++- api/v1/swagger/definitions/basket.yaml | 11 ++-- .../swagger/paths/acquisitions_baskets.yaml | 58 +++++++++++++++++ .../api/v1/acquisitions_baskets/post.t | 63 +++++++++++++++++++ 5 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 t/db_dependent/api/v1/acquisitions_baskets/post.t diff --git a/Koha/Acquisition/Basket.pm b/Koha/Acquisition/Basket.pm index 9ea54b48e4..26c9544ca6 100644 --- a/Koha/Acquisition/Basket.pm +++ b/Koha/Acquisition/Basket.pm @@ -309,6 +309,7 @@ sub to_api_mapping { return { basketno => 'basket_id', basketname => 'name', + note => 'internal_note', booksellernote => 'vendor_note', contractnumber => 'contract_id', creationdate => 'creation_date', diff --git a/Koha/REST/V1/Acquisitions/Baskets.pm b/Koha/REST/V1/Acquisitions/Baskets.pm index 133185ec00..f1f126d980 100644 --- a/Koha/REST/V1/Acquisitions/Baskets.pm +++ b/Koha/REST/V1/Acquisitions/Baskets.pm @@ -19,7 +19,11 @@ use Modern::Perl; use Mojo::Base 'Mojolicious::Controller'; -use Try::Tiny qw( catch try ); +use Koha::Acquisition::Baskets; + +use Clone qw( clone ); +use Scalar::Util qw( blessed ); +use Try::Tiny qw( catch try ); =head1 NAME @@ -29,6 +33,30 @@ Koha::REST::V1::Acquisitions::Baskets =head2 Class methods +=head3 add + +Controller function that handles adding a new Koha::Acquisition::Basket object + +=cut + +sub add { + my $c = shift->openapi->valid_input or return; + + return try { + my $basket = Koha::Acquisition::Basket->new_from_api( $c->validation->param('body') ); + $basket->store->discard_changes; + + $c->res->headers->location( $c->req->url->to_string . '/' . $basket->basketno ); + + return $c->render( + status => 201, + openapi => $basket->to_api + ); + } catch { + $c->unhandled_exception($_); + }; +} + =head3 list Controller function that handles baskets diff --git a/api/v1/swagger/definitions/basket.yaml b/api/v1/swagger/definitions/basket.yaml index 46d384c9e5..4ceba2138c 100644 --- a/api/v1/swagger/definitions/basket.yaml +++ b/api/v1/swagger/definitions/basket.yaml @@ -5,7 +5,9 @@ properties: type: integer description: Internal identifier for the basket name: - type: string + type: + - string + - "null" description: Basket name internal_note: type: @@ -68,11 +70,6 @@ properties: closed: type: boolean description: Is the basket closed - note: - type: - - string - - "null" - description: basket notes create_items: type: - string @@ -90,3 +87,5 @@ properties: - "null" description: The number of days the basket is late (x-koha-embed) additionalProperties: false +required: + - vendor_id diff --git a/api/v1/swagger/paths/acquisitions_baskets.yaml b/api/v1/swagger/paths/acquisitions_baskets.yaml index 7c2821f011..a4699de8d6 100644 --- a/api/v1/swagger/paths/acquisitions_baskets.yaml +++ b/api/v1/swagger/paths/acquisitions_baskets.yaml @@ -60,6 +60,64 @@ x-koha-authorization: permissions: acquisition: order_manage + post: + x-mojo-to: Acquisitions::Baskets#add + operationId: addBasket + tags: + - baskets + summary: Add basket + consumes: + - application/json + produces: + - application/json + parameters: + - name: body + in: body + description: A basket + required: true + schema: + $ref: "../swagger.yaml#/definitions/basket" + responses: + "201": + description: A successfully created basket + schema: + items: + $ref: "../swagger.yaml#/definitions/basket" + "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: 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. 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: + acquisition: + - order_manage /acquisitions/baskets/managers: get: x-mojo-to: Acquisitions::Baskets#list_managers diff --git a/t/db_dependent/api/v1/acquisitions_baskets/post.t b/t/db_dependent/api/v1/acquisitions_baskets/post.t new file mode 100644 index 0000000000..d0daabf14b --- /dev/null +++ b/t/db_dependent/api/v1/acquisitions_baskets/post.t @@ -0,0 +1,63 @@ +#!/usr/bin/env perl + +use Modern::Perl; + +use Test::More tests => 14; +use Test::Mojo; + +use t::lib::Mocks; +use t::lib::TestBuilder; + +use Koha::Database; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 ); + +my $t = Test::Mojo->new('Koha::REST::V1'); + +$schema->storage->txn_begin; + +my $bookseller = $builder->build_object( + { + class => 'Koha::Acquisition::Booksellers', + } +); +my $patron = $builder->build_object( + { + class => 'Koha::Patrons', + value => { flags => 0 } + } +); +my $password = 'thePassword123'; +$patron->set_password( { password => $password, skip_validation => 1 } ); +my $userid = $patron->userid; + +my $url = "//$userid:$password@/api/v1/acquisitions/baskets"; + +$t->post_ok( $url, json => {} )->status_is(403); + +$schema->resultset('UserPermission')->create( + { + borrowernumber => $patron->borrowernumber, + module_bit => 11, + code => 'order_manage', + } +); + +$t->post_ok( $url, json => {} )->status_is(400); + +$t->post_ok( $url, json => { vendor_id => $bookseller->id } )->status_is(201)->json_has('/basket_id') + ->json_is( '/vendor_id', $bookseller->id ); + +my $basket = { + vendor_id => $bookseller->id, + name => 'Basket #1', + vendor_note => 'Vendor note', +}; +$t->post_ok( $url, json => $basket )->status_is(201)->json_has('/basket_id') + ->json_is( '/vendor_id', $basket->{vendor_id} )->json_is( '/name', $basket->{name} ) + ->json_is( '/vendor_note', $basket->{vendor_note} ); + +$schema->storage->txn_rollback; -- 2.39.5