Bug 19661: REST API - Funds Endpoint
authorMatthias Meusburger <matthias.meusburger@biblibre.com>
Thu, 12 Oct 2017 07:55:12 +0000 (07:55 +0000)
committerNick Clemens <nick@bywatersolutions.com>
Fri, 22 Mar 2019 19:40:36 +0000 (19:40 +0000)
This patch adds a new route to the REST api:

/api/v1/acquisitions/funds/

Signed-off-by: David Bourgault <david.bourgault@inlibro.com>

Signed-off-by: Josef Moravec <josef.moravec@gmail.com>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>

Signed-off-by: Nick Clemens <nick@bywatersolutions.com>

Koha/REST/V1/Acquisitions/Funds.pm [new file with mode: 0644]
api/v1/swagger/definitions.json
api/v1/swagger/definitions/fund.json [new file with mode: 0644]
api/v1/swagger/parameters.json
api/v1/swagger/parameters/fund.json [new file with mode: 0644]
api/v1/swagger/paths.json
api/v1/swagger/paths/acquisitions_funds.json [new file with mode: 0644]
api/v1/swagger/x-primitives.json
t/db_dependent/api/v1/acquisitions_funds.t [new file with mode: 0644]

diff --git a/Koha/REST/V1/Acquisitions/Funds.pm b/Koha/REST/V1/Acquisitions/Funds.pm
new file mode 100644 (file)
index 0000000..a36d3c6
--- /dev/null
@@ -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;
index 95f91e0..22f4f18 100644 (file)
@@ -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 (file)
index 0000000..f184d8a
--- /dev/null
@@ -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"]
+}
index 7fab6ec..176d8be 100644 (file)
@@ -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 (file)
index 0000000..4434058
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "fundidPathParam": {
+      "name": "fund_id",
+      "in": "path",
+      "description": "Fund id",
+      "required": true,
+      "type": "integer"
+    }
+}
index 3eab013..dcdc250 100644 (file)
@@ -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 (file)
index 0000000..de0a12f
--- /dev/null
@@ -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"
+        }
+      }
+    }
+  }
+}
index b89f3e1..ee15fa6 100644 (file)
     "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 (file)
index 0000000..3408118
--- /dev/null
@@ -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 );
+}