From 717975abebe938a5e79ffc07055ed6190b43f206 Mon Sep 17 00:00:00 2001 From: Matthias Meusburger Date: Thu, 12 Oct 2017 07:55:12 +0000 Subject: [PATCH] Bug 19661: REST API - Funds Endpoint This patch adds a new route to the REST api: /api/v1/acquisitions/funds/ Signed-off-by: David Bourgault Signed-off-by: Josef Moravec Signed-off-by: Martin Renvoize Signed-off-by: Nick Clemens --- Koha/REST/V1/Acquisitions/Funds.pm | 105 ++++++++++++++++ api/v1/swagger/definitions.json | 3 + api/v1/swagger/definitions/fund.json | 24 ++++ api/v1/swagger/parameters.json | 5 +- api/v1/swagger/parameters/fund.json | 9 ++ api/v1/swagger/paths.json | 3 + api/v1/swagger/paths/acquisitions_funds.json | 73 +++++++++++ api/v1/swagger/x-primitives.json | 6 + t/db_dependent/api/v1/acquisitions_funds.t | 125 +++++++++++++++++++ 9 files changed, 352 insertions(+), 1 deletion(-) create mode 100644 Koha/REST/V1/Acquisitions/Funds.pm create mode 100644 api/v1/swagger/definitions/fund.json create mode 100644 api/v1/swagger/parameters/fund.json create mode 100644 api/v1/swagger/paths/acquisitions_funds.json create mode 100644 t/db_dependent/api/v1/acquisitions_funds.t diff --git a/Koha/REST/V1/Acquisitions/Funds.pm b/Koha/REST/V1/Acquisitions/Funds.pm new file mode 100644 index 0000000000..a36d3c62e1 --- /dev/null +++ b/Koha/REST/V1/Acquisitions/Funds.pm @@ -0,0 +1,105 @@ +package Koha::REST::V1::Acquisitions::Funds; + +# 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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use C4::Budgets; +use JSON qw(to_json); + +use Try::Tiny; + +=head1 NAME + +Koha::REST::V1::Acquisitions::Funds + +=head1 API + +=head2 Methods + +=head3 list_funds + +Controller function that handles listing Funds + +=cut + +sub list_funds { + my $c = shift->openapi->valid_input or return; + + my $args = _to_model($c->req->params->to_hash); + my $filter; + + for my $filter_param ( keys %$args ) { + $filter->{$filter_param} = { LIKE => $args->{$filter_param} . "%" } + if $args->{$filter_param}; + } + + return try { + my $funds = GetBudgets($filter); + my @fundsArray = map { _to_api($_) } @$funds; + return $c->render( status => 200, + openapi => \@fundsArray); + } + catch { + if ( $_->isa('DBIx::Class::Exception') ) { + return $c->render( status => 500, + openapi => { error => $_->{msg} } ); + } + else { + return $c->render( status => 500, + openapi => { error => "Something went wrong, check the logs. $_ $filter" } ); + } + }; +} + +=head3 _to_api + +Helper function that maps a Fund into +the attribute names the exposed REST api spec. + +=cut + +sub _to_api { + my $fund = shift; + my $returnfund; + $returnfund->{id} = delete $fund->{budget_id}; + $returnfund->{code} = delete $fund->{budget_code}; + $returnfund->{name} = delete $fund->{budget_name}; + + return $returnfund; +} + +=head3 _to_model + +Helper function that maps REST api objects into Fund +attribute names. + +=cut + +sub _to_model { + my $fund = shift; + + # Rename back + $fund->{budget_id} = delete $fund->{id}; + $fund->{budget_code} = delete $fund->{code}; + $fund->{budget_name} = delete $fund->{name}; + + return $fund; +} + +1; diff --git a/api/v1/swagger/definitions.json b/api/v1/swagger/definitions.json index 95f91e067d..22f4f18546 100644 --- a/api/v1/swagger/definitions.json +++ b/api/v1/swagger/definitions.json @@ -28,5 +28,8 @@ }, "vendor": { "$ref": "definitions/vendor.json" + }, + "fund": { + "$ref": "definitions/fund.json" } } diff --git a/api/v1/swagger/definitions/fund.json b/api/v1/swagger/definitions/fund.json new file mode 100644 index 0000000000..f184d8a45e --- /dev/null +++ b/api/v1/swagger/definitions/fund.json @@ -0,0 +1,24 @@ +{ + "type": "object", + "properties": { + "id": { + "$ref": "../x-primitives.json#/fund_id" + }, + "code": { + "type": [ + "string", + "null" + ], + "description": "Fund code" + }, + "name": { + "type": [ + "string", + "null" + ], + "description": "Fund name" + } + }, + "additionalProperties": false, + "required": ["name"] +} diff --git a/api/v1/swagger/parameters.json b/api/v1/swagger/parameters.json index 7fab6ecb59..176d8beee8 100644 --- a/api/v1/swagger/parameters.json +++ b/api/v1/swagger/parameters.json @@ -54,5 +54,8 @@ "required": false, "description": "Page size, for paginated object listing", "type": "integer" - } + }, + "fundidPathParam": { + "$ref": "parameters/fund.json#/fundidPathParam" + } } diff --git a/api/v1/swagger/parameters/fund.json b/api/v1/swagger/parameters/fund.json new file mode 100644 index 0000000000..4434058048 --- /dev/null +++ b/api/v1/swagger/parameters/fund.json @@ -0,0 +1,9 @@ +{ + "fundidPathParam": { + "name": "fund_id", + "in": "path", + "description": "Fund id", + "required": true, + "type": "integer" + } +} diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index 3eab013b76..dcdc25063c 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -8,6 +8,9 @@ "/acquisitions/vendors/{vendor_id}": { "$ref": "paths/acquisitions_vendors.json#/~1acquisitions~1vendors~1{vendor_id}" }, + "/acquisitions/funds": { + "$ref": "paths/acquisitions_funds.json#/~1acquisitions~1funds" + }, "/cities": { "$ref": "paths/cities.json#/~1cities" }, diff --git a/api/v1/swagger/paths/acquisitions_funds.json b/api/v1/swagger/paths/acquisitions_funds.json new file mode 100644 index 0000000000..de0a12f83f --- /dev/null +++ b/api/v1/swagger/paths/acquisitions_funds.json @@ -0,0 +1,73 @@ +{ + "/acquisitions/funds": { + "get": { + "x-mojo-to": "Acquisitions::Funds#list_funds", + "operationId": "listFunds", + "tags": ["acquisitions","funds"], + "produces": [ + "application/json" + ], + "parameters": [{ + "name": "name", + "in": "query", + "description": "Case insensitive search on fund name", + "required": false, + "type": "string" + }, + { + "name": "budget_owner_id", + "in": "query", + "description": "Display only the funds that belongs to the given borrowernumber", + "required": false, + "type": "integer" + } + ], + "responses": { + "200": { + "description": "A list of funds", + "schema": { + "type": "array", + "items": { + "$ref": "../definitions.json#/fund" + } + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Fund not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal server error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "acquisition": "budget_manage_all" + } + } + } + } +} diff --git a/api/v1/swagger/x-primitives.json b/api/v1/swagger/x-primitives.json index b89f3e171f..ee15fa6aa1 100644 --- a/api/v1/swagger/x-primitives.json +++ b/api/v1/swagger/x-primitives.json @@ -42,5 +42,11 @@ "type": "integer", "description": "internally assigned vendor identifier", "readOnly": true + }, + "fund_id": { + "type": "integer", + "description": "internally assigned fund identifier", + "readOnly": true } + } diff --git a/t/db_dependent/api/v1/acquisitions_funds.t b/t/db_dependent/api/v1/acquisitions_funds.t new file mode 100644 index 0000000000..3408118647 --- /dev/null +++ b/t/db_dependent/api/v1/acquisitions_funds.t @@ -0,0 +1,125 @@ +#!/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, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Test::More tests => 14; +use Test::Mojo; +use t::lib::TestBuilder; +use t::lib::Mocks; + +use C4::Auth; +use C4::Context; +use C4::Budgets; + +use Koha::Database; +use Koha::Patron; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new(); + +$schema->storage->txn_begin; + +# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling +# this affects the other REST api tests +t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); + +$ENV{REMOTE_ADDR} = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +my $fund1 = { + budget_code => 'ABCD', + budget_amount => '123.132000', + budget_name => 'Periodiques', + budget_notes => 'This is a note', +}; +my $budget_id = AddBudget($fund1); +isnt( $budget_id, undef, 'AddBudget does not returns undef' ); + +$t->get_ok('/api/v1/acquisitions/funds') + ->status_is(401); + +$t->get_ok('/api/v1/acquisitions/funds/?name=testFund') + ->status_is(401); + +my ( $borrowernumber, $session_id ) + #= create_user_and_session( { authorized => 1 } ); + = create_user_and_session( ); + +my $tx = $t->ua->build_tx(GET => '/api/v1/acquisitions/funds'); +$tx->req->cookies({name => 'CGISESSID', value => $session_id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(403); + +$tx = $t->ua->build_tx(GET => "/api/v1/acquisitions/funds/?name=" . $fund1->{ budget_name }); +$tx->req->cookies({name => 'CGISESSID', value => $session_id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(403); + +( $borrowernumber, $session_id ) + = create_user_and_session( { authorized => 1 } ); + +$tx = $t->ua->build_tx(GET => '/api/v1/acquisitions/funds'); +$tx->req->cookies({name => 'CGISESSID', value => $session_id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200); + +$tx = $t->ua->build_tx(GET => "/api/v1/acquisitions/funds/?name=" . $fund1->{ budget_name }); +$tx->req->cookies({name => 'CGISESSID', value => $session_id}); +$tx->req->env({REMOTE_ADDR => '127.0.0.1'}); +$t->request_ok($tx) + ->status_is(200) + ->json_like('/0/name' => qr/$fund1->{ budget_name }/); + +$schema->storage->txn_rollback; + +sub create_user_and_session { + + my $args = shift; + my $flags = ( $args->{authorized} ) ? 2052 : 0; + + # my $flags = ( $args->{authorized} ) ? $args->{authorized} : 0; + my $dbh = C4::Context->dbh; + + my $user = $builder->build( + { source => 'Borrower', + value => { flags => $flags } + } + ); + + # Create a session for the authorized user + my $session = C4::Auth::get_session(''); + $session->param( 'number', $user->{borrowernumber} ); + $session->param( 'id', $user->{userid} ); + $session->param( 'ip', '127.0.0.1' ); + $session->param( 'lasttime', time() ); + $session->flush; + + if ( $args->{authorized} ) { + $dbh->do( + q{ + INSERT INTO user_permissions (borrowernumber,module_bit,code) + VALUES (?,11,'budget_manage_all')}, + undef, $user->{borrowernumber} + ); + } + + return ( $user->{borrowernumber}, $session->id ); +} -- 2.39.5