From a05280194c38d5727470d97cdc55d34d1f323811 Mon Sep 17 00:00:00 2001 From: Martin Renvoize Date: Tue, 13 Dec 2016 16:41:40 +0000 Subject: [PATCH] Bug 11897: REST API - add path defs, controller & tests * Koha/REST/V1/Stage.pm: New file * api/v1/swagger/paths.json: Add route for stage manipulation * api/v1/swagger/paths/rotas.json: New file * t/db_dependent/api/v1/stockrotationstage.t: New file --- Koha/REST/V1/Stage.pm | 47 ++++++++ api/v1/swagger/paths.json | 3 + api/v1/swagger/paths/rotas.json | 60 ++++++++++ t/db_dependent/api/v1/stockrotationstage.t | 172 +++++++++++++++++++++++++++++ 4 files changed, 282 insertions(+) create mode 100644 Koha/REST/V1/Stage.pm create mode 100644 api/v1/swagger/paths/rotas.json create mode 100644 t/db_dependent/api/v1/stockrotationstage.t diff --git a/Koha/REST/V1/Stage.pm b/Koha/REST/V1/Stage.pm new file mode 100644 index 0000000..42d4810 --- /dev/null +++ b/Koha/REST/V1/Stage.pm @@ -0,0 +1,47 @@ +package Koha::REST::V1::Stage; + +# 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 Koha::Stockrotationrotas; +use Koha::Stockrotationstages; + +sub move { + my ($c, $args, $cb) = @_; + + my $rota = Koha::Stockrotationrotas->find($args->{rota_id}); + my $stage = Koha::Stockrotationstages->find($args->{stage_id}); + + if ($stage && $rota) { + my $result = $stage->move_to($args->{position}); + return $c->$cb({}, 200) if $result; + return $c->$cb( + { error => "Bad request - new position invalid"}, + 400 + ); + } else { + return $c->$cb( + { error => "Not found - Invalid rota or stage ID"}, + 404 + ); + } + +} + +1; diff --git a/api/v1/swagger/paths.json b/api/v1/swagger/paths.json index b1fcf40..6bf862d 100644 --- a/api/v1/swagger/paths.json +++ b/api/v1/swagger/paths.json @@ -16,5 +16,8 @@ }, "/patrons/{borrowernumber}": { "$ref": "paths/patrons.json#/~1patrons~1{borrowernumber}" + }, + "/rotas/{rota_id}/stages/{stage_id}/position": { + "$ref": "paths/rotas.json#/~1rotas~1{rota_id}~1stages~1{stage_id}~1position" } } diff --git a/api/v1/swagger/paths/rotas.json b/api/v1/swagger/paths/rotas.json new file mode 100644 index 0000000..ff13ae1 --- /dev/null +++ b/api/v1/swagger/paths/rotas.json @@ -0,0 +1,60 @@ +{ + "/rotas/{rota_id}/stages/{stage_id}/position": { + "put": { + "operationId": "moveStage", + "tags": ["rotas"], + "parameters": [{ + "name": "rota_id", + "in": "path", + "required": true, + "description": "A rotas ID", + "type": "integer" + }, { + "name": "stage_id", + "in": "path", + "required": true, + "description": "A stages ID", + "type": "integer" + }, { + "name": "position", + "in": "body", + "required": true, + "description": "A stages position in the rota", + "schema": { + "type": "integer" + } + }], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "OK" + }, + "400": { + "description": "Bad request", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Position not found", + "schema": { + "$ref": "../definitions.json#/error" + } + } + }, + "x-koha-authorization": { + "permissions": { + "borrowers": "1" + } + } + } + } +} diff --git a/t/db_dependent/api/v1/stockrotationstage.t b/t/db_dependent/api/v1/stockrotationstage.t new file mode 100644 index 0000000..2ec3365 --- /dev/null +++ b/t/db_dependent/api/v1/stockrotationstage.t @@ -0,0 +1,172 @@ +#!/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 => 1; +use Test::Mojo; +use Test::Warn; + +use t::lib::TestBuilder; +use t::lib::Mocks; + +use C4::Auth; +use Koha::Stockrotationstages; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +# 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' ); + +my $remote_address = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +subtest 'move() tests' => sub { + + plan tests => 16; + + $schema->storage->txn_begin; + + my ( $unauthorized_borrowernumber, $unauthorized_session_id ) = + create_user_and_session( { authorized => 0 } ); + my ( $authorized_borrowernumber, $authorized_session_id ) = + create_user_and_session( { authorized => 1 } ); + + my $library1 = $builder->build({ source => 'Branch' }); + my $library2 = $builder->build({ source => 'Branch' }); + my $rota = $builder->build({ source => 'Stockrotationrota' }); + my $stage1 = $builder->build({ + source => 'Stockrotationstage', + value => { + branchcode_id => $library1->{branchcode}, + rota_id => $rota->{rota_id}, + } + }); + my $stage2 = $builder->build({ + source => 'Stockrotationstage', + value => { + branchcode_id => $library2->{branchcode}, + rota_id => $rota->{rota_id}, + } + }); + my $rota_id = $rota->{rota_id}; + my $stage1_id = $stage1->{stage_id}; + + # Unauthorized attempt to update + my $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/$rota_id/stages/$stage1_id/position" => + json => 2 + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $unauthorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(403); + + # Invalid attempt to move a stage on a non-existant rota + $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/99999999/stages/$stage1_id/position" => + json => 2 + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $authorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(404) + ->json_is( '/error' => "Not found - Invalid rota or stage ID" ); + + # Invalid attempt to move an non-existant stage + $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/$rota_id/stages/999999999/position" => + json => 2 + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $authorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(404) + ->json_is( '/error' => "Not found - Invalid rota or stage ID" ); + + # Invalid attempt to move stage to current position + my $curr_position = $stage1->{position}; + $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/$rota_id/stages/$stage1_id/position" => + json => $curr_position + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $authorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(400) + ->json_is( '/error' => "Bad request - new position invalid" ); + + # Invalid attempt to move stage to invalid position + $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/$rota_id/stages/$stage1_id/position" => + json => 99999999 + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $authorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(400) + ->json_is( '/error' => "Bad request - new position invalid" ); + + # Valid, authorised move + $tx = $t->ua->build_tx( + PUT => "/api/v1/rotas/$rota_id/stages/$stage1_id/position" => + json => 2 + ); + $tx->req->cookies( + { name => 'CGISESSID', value => $authorized_session_id } ); + $tx->req->env( { REMOTE_ADDR => $remote_address } ); + $t->request_ok($tx)->status_is(200); + + $schema->storage->txn_rollback; +}; + +sub create_user_and_session { + + my $args = shift; + 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( " + INSERT INTO user_permissions (borrowernumber,module_bit,code) + VALUES (?,3,'parameters_remaining_permissions')", undef, + $user->{borrowernumber} ); + } + + return ( $user->{borrowernumber}, $session->id ); +} + +1; -- 2.1.4