Browse Source

Bug 17268: Advanced cataloging editor macros - add endpoint

Signed-off-by: Andrew Fuerste-Henry <andrew@bywatersolutions.com>
Signed-off-by: Heather Hernandez <Heather_Hernandez@nps.gov>
Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
20.05.x
Nick Clemens 2 years ago
committed by Martin Renvoize
parent
commit
537ba245ee
Signed by: martin.renvoize GPG Key ID: 422B469130441A0F
  1. 57
      Koha/AdvancedEditorMacro.pm
  2. 50
      Koha/AdvancedEditorMacros.pm
  3. 397
      Koha/REST/V1/AdvancedEditorMacro.pm
  4. 3
      api/v1/swagger/definitions.json
  5. 26
      api/v1/swagger/definitions/advancededitormacro.json
  6. 3
      api/v1/swagger/parameters.json
  7. 9
      api/v1/swagger/parameters/advancededitormacro.json
  8. 12
      api/v1/swagger/paths.json
  9. 521
      api/v1/swagger/paths/advancededitormacros.json
  10. 5
      api/v1/swagger/x-primitives.json
  11. 475
      t/db_dependent/api/v1/advanced_editor_macros.t

57
Koha/AdvancedEditorMacro.pm

@ -0,0 +1,57 @@
package Koha::AdvancedEditorMacro;
# 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 Carp;
use Koha::Database;
use base qw(Koha::Object);
=head1 NAME
Koha::AdvancedEditorMacro - Koha Advanced Editor Macro Object class
=head1 API
=head2 Class methods
=head3 to_api_mapping
=cut
sub to_api_mapping {
return {
id => 'macro_id',
macro => 'macro_text',
borrowernumber => 'patron_id',
};
}
=head2 Internal methods
=head3 _type
=cut
sub _type {
return 'AdvancedEditorMacro';
}
1;

50
Koha/AdvancedEditorMacros.pm

@ -0,0 +1,50 @@
package Koha::AdvancedEditorMacros;
# 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 Carp;
use Koha::Database;
use Koha::AdvancedEditorMacro;
use base qw(Koha::Objects);
=head1 NAME
Koha::AdvancedEditorMacros - Koha Advanced Editor Macro Object set class
=head1 API
=head2 Class Methods
=cut
=head3 type
=cut
sub _type {
return 'AdvancedEditorMacro';
}
sub object_class {
return 'Koha::AdvancedEditorMacro';
}
1;

397
Koha/REST/V1/AdvancedEditorMacro.pm

@ -0,0 +1,397 @@
package Koha::REST::V1::AdvancedEditorMacro;
# 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::AdvancedEditorMacros;
use Try::Tiny;
=head1 API
=head2 Class Methods
=cut
=head3 list
Controller function that handles listing Koha::AdvancedEditorMacro objects
=cut
sub list {
my $c = shift->openapi->valid_input or return;
my $patron = $c->stash('koha.user');
return try {
my $macros_set = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron->borrowernumber } });
my $macros = $c->objects->search( $macros_set, \&_to_model, \&_to_api );
return $c->render( status => 200, openapi => $macros );
}
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. $_"} );
}
};
}
=head3 get
Controller function that handles retrieving a single Koha::AdvancedEditorMacro
=cut
sub get {
my $c = shift->openapi->valid_input or return;
my $patron = $c->stash('koha.user');
my $macro = Koha::AdvancedEditorMacros->find({
id => $c->validation->param('advancededitormacro_id'),
});
unless ($macro) {
return $c->render( status => 404,
openapi => { error => "Macro not found" } );
}
if( $macro->shared ){
return $c->render( status => 403, openapi => {
error => "This macro is shared, you must access it via advancededitormacros/shared"
});
}
warn $macro->borrowernumber;
warn $patron->borrowernumber;
if( $macro->borrowernumber != $patron->borrowernumber ){
return $c->render( status => 403, openapi => {
error => "You do not have permission to access this macro"
});
}
return $c->render( status => 200, openapi => $macro->to_api );
}
=head3 get_shared
Controller function that handles retrieving a single Koha::AdvancedEditorMacro
=cut
sub get_shared {
my $c = shift->openapi->valid_input or return;
my $patron = $c->stash('koha.user');
my $macro = Koha::AdvancedEditorMacros->find({
id => $c->validation->param('advancededitormacro_id'),
});
unless ($macro) {
return $c->render( status => 404,
openapi => { error => "Macro not found" } );
}
unless( $macro->shared ){
return $c->render( status => 403, openapi => {
error => "This macro is not shared, you must access it via advancededitormacros"
});
}
return $c->render( status => 200, openapi => $macro->to_api );
}
=head3 add
Controller function that handles adding a new Koha::AdvancedEditorMacro object
=cut
sub add {
my $c = shift->openapi->valid_input or return;
if( defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
return $c->render( status => 403,
openapi => { error => "To create shared macros you must use advancededitor/shared" } );
}
return try {
my $macro = Koha::AdvancedEditorMacro->new( _to_model( $c->validation->param('body') ) );
$macro->store;
$c->res->headers->location( $c->req->url->to_string . '/' . $macro->id );
return $c->render(
status => 201,
openapi => $macro->to_api
);
}
catch { handle_error($_) };
}
=head3 add_shared
Controller function that handles adding a new shared Koha::AdvancedEditorMacro object
=cut
sub add_shared {
my $c = shift->openapi->valid_input or return;
unless( defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
return $c->render( status => 403,
openapi => { error => "To create private macros you must use advancededitor" } );
}
return try {
my $macro = Koha::AdvancedEditorMacro->new( _to_model( $c->validation->param('body') ) );
$macro->store;
$c->res->headers->location( $c->req->url->to_string . '/' . $macro->id );
return $c->render(
status => 201,
openapi => $macro->to_api
);
}
catch { handle_error($_) };
}
=head3 update
Controller function that handles updating a Koha::AdvancedEditorMacro object
=cut
sub update {
my $c = shift->openapi->valid_input or return;
my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
if ( not defined $macro ) {
return $c->render( status => 404,
openapi => { error => "Object not found" } );
}
my $patron = $c->stash('koha.user');
if( $macro->shared == 1 || defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
return $c->render( status => 403,
openapi => { error => "To update a macro as shared you must use the advancededitormacros/shared endpoint" } );
} else {
unless ( $macro->borrowernumber == $patron->borrowernumber ){
return $c->render( status => 403,
openapi => { error => "You can only edit macros you own" } );
}
}
return try {
my $params = $c->req->json;
$macro->set( _to_model($params) );
$macro->store();
return $c->render( status => 200, openapi => $macro->to_api );
}
catch { handle_error($_) };
}
=head3 update_shared
Controller function that handles updating a shared Koha::AdvancedEditorMacro object
=cut
sub update_shared {
my $c = shift->openapi->valid_input or return;
my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
if ( not defined $macro ) {
return $c->render( status => 404,
openapi => { error => "Object not found" } );
}
unless( $macro->shared == 1 || defined $c->validation->param('body')->{shared} && $c->validation->param('body')->{shared} == 1 ){
return $c->render( status => 403,
openapi => { error => "You can only update shared macros using this endpoint" } );
}
return try {
my $params = $c->req->json;
$macro->set( _to_model($params) );
$macro->store();
return $c->render( status => 200, openapi => $macro->to_api );
}
catch { handle_error($_) };
}
=head3 delete
Controller function that handles deleting a Koha::AdvancedEditorMacro object
=cut
sub delete {
my $c = shift->openapi->valid_input or return;
my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
if ( not defined $macro ) {
return $c->render( status => 404,
openapi => { error => "Object not found" } );
}
my $patron = $c->stash('koha.user');
if( $macro->shared == 1 ){
return $c->render( status => 403,
openapi => { error => "You cannot delete shared macros using this endpoint" } );
} else {
unless ( $macro->borrowernumber == $patron->borrowernumber ){
return $c->render( status => 403,
openapi => { error => "You can only delete macros you own" } );
}
}
return try {
$macro->delete;
return $c->render( status => 200, openapi => "" );
}
catch { handle_error($_) };
}
=head3 delete_shared
Controller function that handles deleting a shared Koha::AdvancedEditorMacro object
=cut
sub delete_shared {
my $c = shift->openapi->valid_input or return;
my $macro = Koha::AdvancedEditorMacros->find( $c->validation->param('advancededitormacro_id') );
if ( not defined $macro ) {
return $c->render( status => 404,
openapi => { error => "Object not found" } );
}
unless( $macro->shared == 1 ){
return $c->render( status => 403,
openapi => { error => "You can only delete shared macros using this endpoint" } );
}
return try {
$macro->delete;
return $c->render( status => 200, openapi => "" );
}
catch { handle_error($_,$c) };
}
=head3 _handle_error
Helper function that passes exception or error
=cut
sub _handle_error {
my ($err,$c) = @_;
if ( $err->isa('DBIx::Class::Exception') ) {
return $c->render( status => 500,
openapi => { error => $err->{msg} } );
}
else {
return $c->render( status => 500,
openapi => { error => "Something went wrong, check the logs."} );
}
};
=head3 _to_api
Helper function that maps a hashref of Koha::AdvancedEditorMacro attributes into REST api
attribute names.
=cut
sub _to_api {
my $macro = shift;
# Rename attributes
foreach my $column ( keys %{ $Koha::REST::V1::AdvancedEditorMacro::to_api_mapping } ) {
my $mapped_column = $Koha::REST::V1::AdvancedEditorMacro::to_api_mapping->{$column};
if ( exists $macro->{ $column }
&& defined $mapped_column )
{
# key /= undef
$macro->{ $mapped_column } = delete $macro->{ $column };
}
elsif ( exists $macro->{ $column }
&& !defined $mapped_column )
{
# key == undef => to be deleted
delete $macro->{ $column };
}
}
return $macro;
}
=head3 _to_model
Helper function that maps REST api objects into Koha::AdvancedEditorMacros
attribute names.
=cut
sub _to_model {
my $macro = shift;
foreach my $attribute ( keys %{ $Koha::REST::V1::AdvancedEditorMacro::to_model_mapping } ) {
my $mapped_attribute = $Koha::REST::V1::AdvancedEditorMacro::to_model_mapping->{$attribute};
if ( exists $macro->{ $attribute }
&& defined $mapped_attribute )
{
# key /= undef
$macro->{ $mapped_attribute } = delete $macro->{ $attribute };
}
elsif ( exists $macro->{ $attribute }
&& !defined $mapped_attribute )
{
# key == undef => to be deleted
delete $macro->{ $attribute };
}
}
if ( exists $macro->{shared} ) {
$macro->{shared} = ($macro->{shared}) ? 1 : 0;
}
return $macro;
}
=head2 Global variables
=head3 $to_api_mapping
=cut
our $to_api_mapping = {
id => 'macro_id',
macro => 'macro_text',
borrowernumber => 'patron_id',
};
=head3 $to_model_mapping
=cut
our $to_model_mapping = {
macro_id => 'id',
macro_text => 'macro',
patron_id => 'borrowernumber',
};
1;

3
api/v1/swagger/definitions.json

@ -47,6 +47,9 @@
"order": {
"$ref": "definitions/order.json"
},
"advancededitormacro": {
"$ref": "definitions/advancededitormacro.json"
},
"patron": {
"$ref": "definitions/patron.json"
},

26
api/v1/swagger/definitions/advancededitormacro.json

@ -0,0 +1,26 @@
{
"type": "object",
"properties": {
"macro_id": {
"$ref": "../x-primitives.json#/advancededitormacro_id"
},
"name": {
"description": "macro name",
"type": "string"
},
"macro_text": {
"description": "macro text",
"type": ["string", "null"]
},
"patron_id": {
"description": "borrower number",
"type": ["integer", "null"]
},
"shared": {
"description": "is macro shared",
"type": ["boolean", "null"]
}
},
"additionalProperties": false,
"required": ["name", "macro_text", "patron_id", "shared"]
}

3
api/v1/swagger/parameters.json

@ -2,6 +2,9 @@
"biblio_id_pp": {
"$ref": "parameters/biblio.json#/biblio_id_pp"
},
"advancededitormacro_id_pp": {
"$ref": "parameters/advancededitormacro.json#/advancededitormacro_id_pp"
},
"patron_id_pp": {
"$ref": "parameters/patron.json#/patron_id_pp"
},

9
api/v1/swagger/parameters/advancededitormacro.json

@ -0,0 +1,9 @@
{
"advancededitormacro_id_pp": {
"name": "advancededitormacro_id",
"in": "path",
"description": "Advanced Editor Macro internal identifier",
"required": true,
"type": "integer"
}
}

12
api/v1/swagger/paths.json

@ -68,6 +68,18 @@
"/checkouts/{checkout_id}/allows_renewal": {
"$ref": "paths/checkouts.json#/~1checkouts~1{checkout_id}~1allows_renewal"
},
"/advancededitormacros": {
"$ref": "paths/advancededitormacros.json#/~1advancededitormacros"
},
"/advancededitormacros/{advancededitormacro_id}": {
"$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1{advancededitormacro_id}"
},
"/advancededitormacros/shared": {
"$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1shared"
},
"/advancededitormacros/shared/{advancededitormacro_id}": {
"$ref": "paths/advancededitormacros.json#/~1advancededitormacros~1shared~1{advancededitormacro_id}"
},
"/patrons": {
"$ref": "paths/patrons.json#/~1patrons"
},

521
api/v1/swagger/paths/advancededitormacros.json

@ -0,0 +1,521 @@
{
"/advancededitormacros": {
"get": {
"x-mojo-to": "AdvancedEditorMacro#list",
"operationId": "listMacro",
"tags": ["advancededitormacro"],
"produces": [
"application/json"
],
"parameters": [
{
"name": "name",
"in": "query",
"description": "Case insensative search on macro name",
"required": false,
"type": "string"
},
{
"name": "macro_text",
"in": "query",
"description": "Case insensative search on macro text",
"required": false,
"type": "string"
},
{
"name": "patron_id",
"in": "query",
"description": "Search on internal patron_id",
"required": false,
"type": "string"
},
{
"name": "shared",
"in": "query",
"description": "Search on shared macros",
"required": false,
"type": "string"
}
],
"responses": {
"200": {
"description": "A list of macros",
"schema": {
"type": "array",
"items": {
"$ref": "../definitions.json#/advancededitormacro"
}
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
},
"post": {
"x-mojo-to": "AdvancedEditorMacro#add",
"operationId": "addAdvancedEditorMacro",
"tags": ["advancededitormacro"],
"parameters": [{
"name": "body",
"in": "body",
"description": "A JSON object containing informations about the new macro",
"required": true,
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
}],
"produces": [
"application/json"
],
"responses": {
"201": {
"description": "Macro added",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
}
},
"/advancededitormacros/shared": {
"post": {
"x-mojo-to": "AdvancedEditorMacro#add_shared",
"operationId": "addsharedAdvancedEditorMacro",
"tags": ["advancededitormacro"],
"parameters": [{
"name": "body",
"in": "body",
"description": "A JSON object containing informations about the new macro",
"required": true,
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
}],
"produces": [
"application/json"
],
"responses": {
"201": {
"description": "Macro added",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor",
"editcatalogue": "create_shared_macros"
}
}
}
},
"/advancededitormacros/{advancededitormacro_id}": {
"get": {
"x-mojo-to": "AdvancedEditorMacro#get",
"operationId": "getAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A macro",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "AdvancedEditorMacro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
},
"put": {
"x-mojo-to": "AdvancedEditorMacro#update",
"operationId": "updateAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}, {
"name": "body",
"in": "body",
"description": "An advanced editor macro object",
"required": true,
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "An advanced editor macro",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "Macro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
},
"delete": {
"x-mojo-to": "AdvancedEditorMacro#delete",
"operationId": "deleteAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "Advanced editor macro deleted",
"schema": {
"type": "string"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "Macro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
}
},
"/advancededitormacros/shared/{advancededitormacro_id}": {
"get": {
"x-mojo-to": "AdvancedEditorMacro#get_shared",
"operationId": "getsharedAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "A macro",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "AdvancedEditorMacro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor"
}
}
},
"put": {
"x-mojo-to": "AdvancedEditorMacro#update_shared",
"operationId": "updatesharedAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}, {
"name": "body",
"in": "body",
"description": "An advanced editor macro object",
"required": true,
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "An advanced editor macro",
"schema": {
"$ref": "../definitions.json#/advancededitormacro"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "Macro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor",
"editcatalogue": "create_shared_macros"
}
}
},
"delete": {
"x-mojo-to": "AdvancedEditorMacro#delete_shared",
"operationId": "deletesharedAdvancedEditorMacro",
"tags": ["advancededitormacros"],
"parameters": [{
"$ref": "../parameters.json#/advancededitormacro_id_pp"
}],
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "Advanced editor macro deleted",
"schema": {
"type": "string"
}
},
"401": {
"description": "Authentication required",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"403": {
"description": "Access forbidden",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"404": {
"description": "Macro not found",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"500": {
"description": "Internal error",
"schema": {
"$ref": "../definitions.json#/error"
}
},
"503": {
"description": "Under maintenance",
"schema": {
"$ref": "../definitions.json#/error"
}
}
},
"x-koha-authorization": {
"permissions": {
"editcatalogue": "advanced_editor",
"editcatalogue": "delete_shared_macros"
}
}
}
}
}

5
api/v1/swagger/x-primitives.json

@ -3,6 +3,11 @@
"type": "integer",
"description": "Internal biblio identifier"
},
"advancededitormacro_id": {
"type": "integer",
"description": "Internal advanced editor macro identifier",
"readOnly": true
},
"patron_id": {
"type": "integer",
"description": "Internal patron identifier"

475
t/db_dependent/api/v1/advanced_editor_macros.t

@ -0,0 +1,475 @@
#!/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 => 5;
use Test::Mojo;
use Test::Warn;
use t::lib::TestBuilder;
use t::lib::Mocks;
use Koha::AdvancedEditorMacros;
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;
subtest 'list() tests' => sub {
plan tests => 8;
my $patron_1 = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 9 }
});
my $patron_2 = $builder->build_object({
class => 'Koha::Patrons',
});
my $password = 'thePassword123';
$patron_1->set_password({ password => $password, skip_validation => 1 });
my $userid = $patron_1->userid;
# Create test context
my $macro_1 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test1',
macro => 'delete 100',
borrowernumber => $patron_1->borrowernumber,
}
});
my $macro_2 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test2',
macro => 'delete 100',
borrowernumber => $patron_1->borrowernumber,
shared=> 1,
}
});
my $macro_3 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test3',
macro => 'delete 100',
borrowernumber => $patron_2->borrowernumber,
}
});
my $macro_4 = $builder->build_object({ class => 'Koha::AdvancedEditorMacros', value =>
{
name => 'Test4',
macro => 'delete 100',
borrowernumber => $patron_2->borrowernumber,
shared => 1,
}
});
my $macros_index = Koha::AdvancedEditorMacros->search({ -or => { shared => 1, borrowernumber => $patron_1->borrowernumber } })->count-1;
## Authorized user tests
# Make sure we are returned with the correct amount of macros
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros" )
->status_is( 200, 'SWAGGER3.2.2' )
->json_has('/' . $macros_index . '/macro_id')
->json_hasnt('/' . ($macros_index + 1) . '/macro_id');
subtest 'query parameters' => sub {
plan tests => 15;
$t->get_ok("//$userid:$password@/api/v1/advancededitormacros?name=" . $macro_2->name)
->status_is(200)
->json_has( [ $macro_2 ] );
$t->get_ok("//$userid:$password@/api/v1/advancededitormacros?name=" . $macro_3->name)
->status_is(200)
->json_has( [ ] );
$t->get_ok("//$userid:$password@/api/v1/advancededitormacros?macro_text=delete 100")
->status_is(200)
->json_has( [ $macro_1, $macro_2, $macro_4 ] );
$t->get_ok("//$userid:$password@/api/v1/advancededitormacros?patron_id=" . $patron_1->borrowernumber)
->status_is(200)
->json_has( [ $macro_1, $macro_2 ] );
$t->get_ok("//$userid:$password@/api/v1/advancededitormacros?shared=1")
->status_is(200)
->json_has( [ $macro_2, $macro_4 ] );
};
# Warn on unsupported query parameter
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros?macro_blah=blah" )
->status_is(400)
->json_is( [{ path => '/query/macro_blah', message => 'Malformed query string'}] );
};
subtest 'get() tests' => sub {
plan tests => 15;
my $patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 1 }
});
my $password = 'thePassword123';
$patron->set_password({ password => $password, skip_validation => 1 });
my $userid = $patron->userid;
my $macro_1 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
shared => 1,
}
});
my $macro_2 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
shared => 0,
}
});
my $macro_3 = $builder->build_object( { class => 'Koha::AdvancedEditorMacros', value => {
borrowernumber => $patron->borrowernumber,
shared => 0,
}
});
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_1->id )
->status_is( 403, 'Cannot get a shared macro via regular endpoint' )
->json_is( '/error' => 'This macro is shared, you must access it via advancededitormacros/shared' );
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/shared/" . $macro_1->id )
->status_is( 200, 'Can get a shared macro via shared endpoint' )
->json_is( '' => Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro_1->TO_JSON ), 'Macro correctly retrieved' );
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_2->id )
->status_is( 403, 'Cannot access another users macro' )
->json_is( '/error' => 'You do not have permission to access this macro' );
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $macro_3->id )
->status_is( 200, 'Can get your own private macro' )
->json_is( '' => Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro_3->TO_JSON ), 'Macro correctly retrieved' );
my $non_existent_code = $macro_1->id;
$macro_1->delete;
$t->get_ok( "//$userid:$password@/api/v1/advancededitormacros/" . $non_existent_code )
->status_is(404)
->json_is( '/error' => 'Macro not found' );
};
subtest 'add() tests' => sub {
plan tests => 24;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { shared => 0 }
});
my $macro_values = Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro->TO_JSON );
delete $macro_values->{macro_id};
$macro->delete;
# Unauthorized attempt to write
$t->post_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
->status_is(403);
# Authorized attempt to write invalid data
my $macro_with_invalid_field = { %$macro_values };
$macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_with_invalid_field )
->status_is(400)
->json_is(
"/errors" => [
{
message => "Properties not allowed: big_mac_ro.",
path => "/body"
}
]
);
# Authorized attempt to write
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
->status_is( 201, 'SWAGGER3.2.1' )
->json_has( '/macro_id', 'We generated a new id' )
->json_is( '/name' => $macro_values->{name}, 'The name matches what we supplied' )
->json_is( '/macro_text' => $macro_values->{macro_text}, 'The text matches what we supplied' )
->json_is( '/patron_id' => $macro_values->{patron_id}, 'The borrower matches the borrower who submitted' )
->json_is( '/shared' => 0, 'The macro is not shared' )
->header_like( Location => qr|^\/api\/v1\/advancededitormacros\/d*|, 'Correct location' );
# save the library_id
my $macro_id = 999;
# Authorized attempt to create with existing id
$macro_values->{macro_id} = $macro_id;
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
->status_is(400)
->json_is( '/errors' => [
{
message => "Read-only.",
path => "/body/macro_id"
}
]
);
$macro_values->{shared} = 1;
delete $macro_values->{macro_id};
# Unauthorized attempt to write a shared macro on private endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
->status_is(403);
# Unauthorized attempt to write a private macro on shared endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared" => json => $macro_values )
->status_is(403);
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'create_shared_macros',
},
});
# Authorized attempt to write a shared macro on private endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros" => json => $macro_values )
->status_is(403);
# Authorized attempt to write a shared macro on shared endpoint
$t->post_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared" => json => $macro_values )
->status_is(201);
};
subtest 'update() tests' => sub {
plan tests => 32;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
});
my $macro_2 = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
});
my $macro_id = $macro->id;
my $macro_2_id = $macro_2->id;
my $macro_values = Koha::REST::V1::AdvancedEditorMacro::_to_api( $macro->TO_JSON );
delete $macro_values->{macro_id};
# Unauthorized attempt to update
$t->put_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros/$macro_id"
=> json => { name => 'New unauthorized name change' } )
->status_is(403);
# Attempt partial update on a PUT
my $macro_with_missing_field = {
name => "Call it macro-roni",
};
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_with_missing_field )
->status_is(400)
->json_has( "/errors" =>
[ { message => "Missing property.", path => "/body/macro_text" } ]
);
my $macro_update = {
name => "Macro-update",
macro_text => "delete 100",
patron_id => $authorized_patron->borrowernumber,
shared => 0,
};
my $test = $t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_update )
->status_is(200, 'Authorized user can update a macro')
->json_is( '/macro_id' => $macro_id, 'We get the id back' )
->json_is( '/name' => $macro_update->{name}, 'We get the name back' )
->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get the text back' )
->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get the patron_id back' )
->json_is( '/shared' => $macro_update->{shared}, 'It should still not be shared' );
# Now try to make the macro shared
$macro_update->{shared} = 1;
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared on private endpoint');
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared without permission');
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'create_shared_macros',
},
});
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_update )
->status_is(403, 'Cannot make your macro shared on the private endpoint');
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/shared/$macro_id" => json => $macro_update )
->status_is(200, 'Can update macro to shared with permission')
->json_is( '/macro_id' => $macro_id, 'We get back the id' )
->json_is( '/name' => $macro_update->{name}, 'We get back the name' )
->json_is( '/macro_text' => $macro_update->{macro_text}, 'We get back the text' )
->json_is( '/patron_id' => $macro_update->{patron_id}, 'We get back our patron id' )
->json_is( '/shared' => 1, 'It is shared' );
# Authorized attempt to write invalid data
my $macro_with_invalid_field = { %$macro_update };
$macro_with_invalid_field->{'big_mac_ro'} = 'Mac attack';
$t->put_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id" => json => $macro_with_invalid_field )
->status_is(400)
->json_is(
"/errors" => [
{
message => "Properties not allowed: big_mac_ro.",
path => "/body"
}
]
);
my $non_existent_macro = $builder->build_object({class => 'Koha::AdvancedEditorMacros'});
my $non_existent_code = $non_existent_macro->id;
$non_existent_macro->delete;
$t->put_ok("//$auth_userid:$password@/api/v1/advancededitormacros/$non_existent_code" => json => $macro_update)
->status_is(404);
$t->put_ok("//$auth_userid:$password@/api/v1/advancededitormacros/$macro_2_id" => json => $macro_update)
->status_is(403, "Cannot update other borrowers private macro");
};
subtest 'delete() tests' => sub {
plan tests => 12;
my $authorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$builder->build({
source => 'UserPermission',
value => {
borrowernumber => $authorized_patron->borrowernumber,
module_bit => 9,
code => 'advanced_editor',
},
});
my $password = 'thePassword123';
$authorized_patron->set_password({ password => $password, skip_validation => 1 });
my $auth_userid = $authorized_patron->userid;
my $unauthorized_patron = $builder->build_object({
class => 'Koha::Patrons',
value => { flags => 0 }
});
$unauthorized_patron->set_password({ password => $password, skip_validation => 1 });
my $unauth_userid = $unauthorized_patron->userid;
my $macro = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $authorized_patron->borrowernumber, shared => 0 }
});
my $macro_2 = $builder->build_object({
class => 'Koha::AdvancedEditorMacros',
value => { borrowernumber => $unauthorized_patron->borrowernumber, shared => 0 }
});
my $macro_id = $macro->id;
my $macro_2_id = $macro_2->id;
# Unauthorized attempt to delete
$t->delete_ok( "//$unauth_userid:$password@/api/v1/advancededitormacros/$macro_2_id")
->status_is(403, "Cannot delete macro without permission");
$t->delete_ok( "//$auth_userid:$password@/api/v1/advancededitormacros/$macro_id")
->status_is(200, 'Can delete macro with permission');