use Koha::ERM::Agreements;
+use Scalar::Util qw( blessed );
use Try::Tiny qw( catch try );
=head1 API
}
+=head3 get
+
+Controller function that handles retrieving a single Koha::ERM::Agreement object
+
+=cut
+
+sub get {
+ my $c = shift->openapi->valid_input or return;
+
+ return try {
+ my $agreement_id = $c->validation->param('agreement_id');
+ my $agreement = $c->objects->find( Koha::ERM::Agreements->search, $agreement_id );
+
+ unless ($agreement) {
+ return $c->render(
+ status => 404,
+ openapi => { error => "Agreement not found" }
+ );
+ }
+
+ return $c->render(
+ status => 200,
+ openapi => $agreement
+ );
+ }
+ catch {
+ $c->unhandled_exception($_);
+ };
+}
+
+=head3 add
+
+Controller function that handles adding a new Koha::ERM::Agreement object
+
+=cut
+
+sub add {
+ my $c = shift->openapi->valid_input or return;
+
+ return try {
+ Koha::Database->new->schema->txn_do(
+ sub {
+
+ my $body = $c->validation->param('body');
+
+ my $periods = delete $body->{periods} // [];
+ my $user_roles = delete $body->{user_roles} // [];
+
+ my $agreement = Koha::ERM::Agreement->new_from_api($body)->store;
+ $agreement->periods($periods);
+ $agreement->user_roles($user_roles);
+
+ $c->res->headers->location($c->req->url->to_string . '/' . $agreement->agreement_id);
+ return $c->render(
+ status => 201,
+ openapi => $agreement->to_api
+ );
+ }
+ );
+ }
+ catch {
+
+ my $to_api_mapping = Koha::ERM::Agreement->new->to_api_mapping;
+
+ if ( blessed $_ ) {
+ if ( $_->isa('Koha::Exceptions::Object::DuplicateID') ) {
+ return $c->render(
+ status => 409,
+ openapi => { error => $_->error, conflict => $_->duplicate_id }
+ );
+ }
+ elsif ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
+ return $c->render(
+ status => 400,
+ openapi => {
+ error => "Given "
+ . $to_api_mapping->{ $_->broken_fk }
+ . " does not exist"
+ }
+ );
+ }
+ elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
+ return $c->render(
+ status => 400,
+ openapi => {
+ error => "Given "
+ . $to_api_mapping->{ $_->parameter }
+ . " does not exist"
+ }
+ );
+ }
+ }
+
+ $c->unhandled_exception($_);
+ };
+}
+
+=head3 update
+
+Controller function that handles updating a Koha::ERM::Agreement object
+
+=cut
+
+sub update {
+ my $c = shift->openapi->valid_input or return;
+
+ my $agreement_id = $c->validation->param('agreement_id');
+ my $agreement = Koha::ERM::Agreements->find( $agreement_id );
+
+ unless ($agreement) {
+ return $c->render(
+ status => 404,
+ openapi => { error => "Agreement not found" }
+ );
+ }
+
+ return try {
+ Koha::Database->new->schema->txn_do(
+ sub {
+
+ my $body = $c->validation->param('body');
+
+ my $periods = delete $body->{periods} // [];
+ my $user_roles = delete $body->{user_roles} // [];
+
+ $agreement->set_from_api($body)->store;
+ $agreement->periods($periods);
+ $agreement->user_roles($user_roles);
+
+ $c->res->headers->location($c->req->url->to_string . '/' . $agreement->agreement_id);
+ return $c->render(
+ status => 200,
+ openapi => $agreement->to_api
+ );
+ }
+ );
+ }
+ catch {
+ my $to_api_mapping = Koha::ERM::Agreement->new->to_api_mapping;
+
+ if ( blessed $_ ) {
+ if ( $_->isa('Koha::Exceptions::Object::FKConstraint') ) {
+ return $c->render(
+ status => 400,
+ openapi => {
+ error => "Given "
+ . $to_api_mapping->{ $_->broken_fk }
+ . " does not exist"
+ }
+ );
+ }
+ elsif ( $_->isa('Koha::Exceptions::BadParameter') ) {
+ return $c->render(
+ status => 400,
+ openapi => {
+ error => "Given "
+ . $to_api_mapping->{ $_->parameter }
+ . " does not exist"
+ }
+ );
+ }
+ }
+
+ $c->unhandled_exception($_);
+ };
+};
+
+=head3 delete
+
+=cut
+
+sub delete {
+ my $c = shift->openapi->valid_input or return;
+
+ my $agreement = Koha::ERM::Agreements->find( $c->validation->param('agreement_id') );
+ unless ($agreement) {
+ return $c->render(
+ status => 404,
+ openapi => { error => "Agreement not found" }
+ );
+ }
+
+ return try {
+ $agreement->delete;
+ return $c->render(
+ status => 204,
+ openapi => q{}
+ );
+ }
+ catch {
+ $c->unhandled_exception($_);
+ };
+}
+
1;
operationId: listErmAgreements
tags:
- agreement
- summary: List erm_agreements
+ summary: List agreements
produces:
- application/json
parameters:
x-koha-authorization:
permissions:
erm: 1
+ post:
+ x-mojo-to: ERM::Agreements#add
+ operationId: addErmAgreements
+ tags:
+ - agreement
+ summary: Add agreement
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - description: A JSON object containing information about the new agreement
+ in: body
+ name: body
+ required: true
+ schema:
+ $ref: ../definitions.yaml#/erm_agreement
+ responses:
+ 201:
+ description: A successfully created agreement
+ schema:
+ items:
+ $ref: ../definitions.yaml#/erm_agreement
+ 400:
+ description: Bad parameter
+ schema:
+ $ref: ../definitions.yaml#/error
+ 401:
+ description: Authentication required
+ schema:
+ $ref: ../definitions.yaml#/error
+ 403:
+ description: Access forbidden
+ schema:
+ $ref: ../definitions.yaml#/error
+ 404:
+ description: Ressource not found
+ schema:
+ $ref: ../definitions.yaml#/error
+ 409:
+ description: Conflict in creating resource
+ schema:
+ $ref: ../definitions.yaml#/error
+ 500:
+ description: |-
+ Internal server error. Possible `error_code` attribute values:
+ * `internal_server_error`
+ schema:
+ $ref: ../definitions.yaml#/error
+ 503:
+ description: Under maintenance
+ schema:
+ $ref: ../definitions.yaml#/error
+ x-koha-authorization:
+ permissions:
+ erm: 1
+"/erm/agreements/{agreement_id}":
+ get:
+ x-mojo-to: ERM::Agreements#get
+ operationId: getErmAgreements
+ tags:
+ - agreement
+ summary: Get agreement
+ produces:
+ - application/json
+ parameters:
+ - $ref: ../parameters.yaml#/agreement_id_pp
+ responses:
+ 200:
+ description: An agreement
+ schema:
+ items:
+ $ref: ../definitions.yaml#/erm_agreement
+ 401:
+ description: Authentication required
+ schema:
+ $ref: ../definitions.yaml#/error
+ 403:
+ description: Access forbidden
+ schema:
+ $ref: ../definitions.yaml#/error
+ 404:
+ description: Ressource not found
+ schema:
+ $ref: ../definitions.yaml#/error
+ 500:
+ description: |-
+ Internal server error. Possible `error_code` attribute values:
+ * `internal_server_error`
+ schema:
+ $ref: ../definitions.yaml#/error
+ 503:
+ description: Under maintenance
+ schema:
+ $ref: ../definitions.yaml#/error
+ x-koha-authorization:
+ permissions:
+ erm: 1
+ x-koha-embed:
+ - periods
+ - user_roles
+ - user_roles.patron
+ put:
+ x-mojo-to: ERM::Agreements#update
+ operationId: updateErmAgreements
+ tags:
+ - agreement
+ summary: Update agreement
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ parameters:
+ - $ref: ../parameters.yaml#/agreement_id_pp
+ - name: body
+ in: body
+ description: A JSON object containing new information about existing agreement
+ required: true
+ schema:
+ $ref: ../definitions.yaml#/erm_agreement
+
+ responses:
+ 200:
+ description: A successfully updated agreement
+ schema:
+ items:
+ $ref: ../definitions.yaml#/erm_agreement
+ 400:
+ description: Bad parameter
+ schema:
+ $ref: ../definitions.yaml#/error
+ 403:
+ description: Access forbidden
+ schema:
+ $ref: ../definitions.yaml#/error
+ 404:
+ description: Ressource not found
+ schema:
+ $ref: ../definitions.yaml#/error
+ 409:
+ description: Conflict in updating resource
+ schema:
+ $ref: ../definitions.yaml#/error
+ 500:
+ description: |-
+ Internal server error. Possible `error_code` attribute values:
+ * `internal_server_error`
+ schema:
+ $ref: ../definitions.yaml#/error
+ 503:
+ description: Under maintenance
+ schema:
+ $ref: ../definitions.yaml#/error
+ x-koha-authorization:
+ permissions:
+ erm: 1
+ x-koha-embed:
+ - periods
+ - user_roles
+ delete:
+ x-mojo-to: ERM::Agreements#delete
+ operationId: deleteErmAgreements
+ tags:
+ - agreement
+ summary: Delete agreement
+ produces:
+ - application/json
+ parameters:
+ - $ref: ../parameters.yaml#/agreement_id_pp
+ responses:
+ 204:
+ description: Agreement deleted
+ 400:
+ description: Agreement deletion failed
+ schema:
+ $ref: ../definitions.yaml#/error
+ 401:
+ description: Authentication required
+ schema:
+ $ref: ../definitions.yaml#/error
+ 403:
+ description: Access forbidden
+ schema:
+ $ref: ../definitions.yaml#/error
+ 404:
+ description: Ressource not found
+ schema:
+ $ref: ../definitions.yaml#/error
+ 409:
+ description: Conflict in deleting resource
+ schema:
+ $ref: ../definitions.yaml#/error
+ 500:
+ description: |-
+ Internal server error. Possible `error_code` attribute values:
+ * `internal_server_error`
+ schema:
+ $ref: ../definitions.yaml#/error
+ 503:
+ description: Under maintenance
+ schema:
+ $ref: ../definitions.yaml#/error
+ x-koha-authorization:
+ permissions:
+ erm: 1
use Modern::Perl;
-use Test::More tests => 1;
+use Test::More tests => 5;
use Test::Mojo;
use t::lib::TestBuilder;
my $builder = t::lib::TestBuilder->new;
my $t = Test::Mojo->new('Koha::REST::V1');
-t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
subtest 'list() tests' => sub {
my $librarian = $builder->build_object(
{
class => 'Koha::Patrons',
- value => { flags => 27 ** 2 }
+ value => { flags => 2**28}
}
);
my $password = 'thePassword123';
## Authorized user tests
# No agreements, so empty array should be returned
- $t->get_ok("//$userid:$password@/api/v1/erm/agreements")
- ->status_is(200)
+ $t->get_ok("//$userid:$password@/api/v1/erm/agreements")->status_is(200)
->json_is( [] );
- my $agreement = $builder->build_object({ class => 'Koha::ERM::Agreements' });
+ my $agreement =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } );
# One agreement created, should get returned
- $t->get_ok("//$userid:$password@/api/v1/erm/agreements")
- ->status_is(200)
- ->json_is( [$agreement->to_api] );
+ $t->get_ok("//$userid:$password@/api/v1/erm/agreements")->status_is(200)
+ ->json_is( [ $agreement->to_api ] );
my $another_agreement = $builder->build_object(
- { class => 'Koha::ERM::Agreements', value => { vendor_id => $agreement->vendor_id } } );
- my $agreement_with_another_vendor_id = $builder->build_object({ class => 'Koha::ERM::Agreements' });
+ {
+ class => 'Koha::ERM::Agreements',
+ value => { vendor_id => $agreement->vendor_id }
+ }
+ );
+ my $agreement_with_another_vendor_id =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } );
# Two agreements created, they should both be returned
- $t->get_ok("//$userid:$password@/api/v1/erm/agreements")
- ->status_is(200)
- ->json_is([$agreement->to_api,
- $another_agreement->to_api,
- $agreement_with_another_vendor_id->to_api
- ] );
+ $t->get_ok("//$userid:$password@/api/v1/erm/agreements")->status_is(200)
+ ->json_is(
+ [
+ $agreement->to_api,
+ $another_agreement->to_api,
+ $agreement_with_another_vendor_id->to_api
+ ]
+ );
# Filtering works, two agreements sharing vendor_id
- $t->get_ok("//$userid:$password@/api/v1/erm/agreements?vendor_id=" . $agreement->vendor_id )
- ->status_is(200)
- ->json_is([ $agreement->to_api,
- $another_agreement->to_api
- ]);
+ $t->get_ok( "//$userid:$password@/api/v1/erm/agreements?vendor_id="
+ . $agreement->vendor_id )->status_is(200)
+ ->json_is( [ $agreement->to_api, $another_agreement->to_api ] );
# Warn on unsupported query parameter
- $t->get_ok("//$userid:$password@/api/v1/erm/agreements?blah=blah" )
+ $t->get_ok("//$userid:$password@/api/v1/erm/agreements?blah=blah")
->status_is(400)
- ->json_is( [{ path => '/query/blah', message => 'Malformed query string'}] );
+ ->json_is(
+ [ { path => '/query/blah', message => 'Malformed query string' } ] );
# Unauthorized access
$t->get_ok("//$unauth_userid:$password@/api/v1/erm/agreements")
$schema->storage->txn_rollback;
};
+subtest 'get() tests' => sub {
+
+ plan tests => 8;
+
+ $schema->storage->txn_begin;
+
+ my $agreement =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } );
+ my $librarian = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 2**28 }
+ }
+ );
+ my $password = 'thePassword123';
+ $librarian->set_password( { password => $password, skip_validation => 1 } );
+ my $userid = $librarian->userid;
+
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ }
+ );
+
+ $patron->set_password( { password => $password, skip_validation => 1 } );
+ my $unauth_userid = $patron->userid;
+
+ $t->get_ok( "//$userid:$password@/api/v1/erm/agreements/"
+ . $agreement->agreement_id )->status_is(200)
+ ->json_is( $agreement->to_api );
+
+ $t->get_ok( "//$unauth_userid:$password@/api/v1/erm/agreements/"
+ . $agreement->agreement_id )->status_is(403);
+
+ my $agreement_to_delete =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } );
+ my $non_existent_id = $agreement_to_delete->id;
+ $agreement_to_delete->delete;
+
+ $t->get_ok("//$userid:$password@/api/v1/erm/agreements/$non_existent_id")
+ ->status_is(404)->json_is( '/error' => 'Agreement not found' );
+
+ $schema->storage->txn_rollback;
+};
+
+subtest 'add() tests' => sub {
+
+ plan tests => 22;
+
+ $schema->storage->txn_begin;
+
+ my $librarian = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 2**28 }
+ }
+ );
+ my $password = 'thePassword123';
+ $librarian->set_password( { password => $password, skip_validation => 1 } );
+ my $userid = $librarian->userid;
+
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ }
+ );
+
+ $patron->set_password( { password => $password, skip_validation => 1 } );
+ my $unauth_userid = $patron->userid;
+
+ my $agreement = {
+ vendor_id => undef,
+ name => "Agreement name",
+ description => "Agreement description",
+ status => "active",
+ closure_reason => "",
+ is_perpetual => 1,
+ renewal_priority => "",
+ license_info => "Agreement license_info",
+ };
+
+ # Unauthorized attempt to write
+ $t->post_ok( "//$unauth_userid:$password@/api/v1/erm/agreements" => json =>
+ $agreement )->status_is(403);
+
+ # Authorized attempt to write invalid data
+ my $agreement_with_invalid_field = {
+ blah => "Agreement Blah",
+ name => "Agreement name",
+ description => "Agreement description",
+ status => "active",
+ closure_reason => "",
+ is_perpetual => 1,
+ renewal_priority => "",
+ license_info => "Agreement license_info",
+ };
+
+ $t->post_ok( "//$userid:$password@/api/v1/erm/agreements" => json =>
+ $agreement_with_invalid_field )->status_is(400)->json_is(
+ "/errors" => [
+ {
+ message => "Properties not allowed: blah.",
+ path => "/body"
+ }
+ ]
+ );
+
+ # Authorized attempt to write
+ my $agreement_id =
+ $t->post_ok(
+ "//$userid:$password@/api/v1/erm/agreements" => json => $agreement )
+ ->status_is( 201, 'SWAGGER3.2.1' )->header_like(
+ Location => qr|^/api/v1/erm/agreements/\d*|,
+ 'SWAGGER3.4.1'
+ )->json_is( '/vendor_id' => $agreement->{vendor_id} )
+ ->json_is( '/name' => $agreement->{name} )
+ ->json_is( '/description' => $agreement->{description} )
+ ->json_is( '/status' => $agreement->{status} )
+ ->json_is( '/closure_reason' => $agreement->{closure_reason} )
+ ->json_is( '/is_perpetual' => $agreement->{is_perpetual} )
+ ->json_is( '/renewal_priority' => $agreement->{renewal_priority} )
+ ->json_is( '/license_info' => $agreement->{license_info} )
+ ->tx->res->json->{agreement_id};
+
+ # Authorized attempt to create with null id
+ $agreement->{agreement_id} = undef;
+ $t->post_ok(
+ "//$userid:$password@/api/v1/erm/agreements" => json => $agreement )
+ ->status_is(400)->json_has('/errors');
+
+ # Authorized attempt to create with existing id
+ $agreement->{agreement_id} = $agreement_id;
+ $t->post_ok(
+ "//$userid:$password@/api/v1/erm/agreements" => json => $agreement )
+ ->status_is(400)->json_is(
+ "/errors" => [
+ {
+ message => "Read-only.",
+ path => "/body/agreement_id"
+ }
+ ]
+ );
+
+ $schema->storage->txn_rollback;
+};
+
+subtest 'update() tests' => sub {
+
+ plan tests => 15;
+
+ $schema->storage->txn_begin;
+
+ my $librarian = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 2**28 }
+ }
+ );
+ my $password = 'thePassword123';
+ $librarian->set_password( { password => $password, skip_validation => 1 } );
+ my $userid = $librarian->userid;
+
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ }
+ );
+
+ $patron->set_password( { password => $password, skip_validation => 1 } );
+ my $unauth_userid = $patron->userid;
+
+ my $agreement_id =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } )->agreement_id;
+
+ # Unauthorized attempt to update
+ $t->put_ok(
+ "//$unauth_userid:$password@/api/v1/erm/agreements/$agreement_id" =>
+ json => { name => 'New unauthorized name change' } )->status_is(403);
+
+ # Attempt partial update on a PUT
+ my $agreement_with_missing_field = {
+ description => 'New description',
+ status => 'active',
+ closure_reason => undef,
+ is_perpetual => 1,
+ renewal_priority => undef,
+ license_info => 'New license_info',
+ };
+
+ $t->put_ok(
+ "//$userid:$password@/api/v1/erm/agreements/$agreement_id" => json =>
+ $agreement_with_missing_field )->status_is(400)
+ ->json_is( "/errors" =>
+ [ { message => "Missing property.", path => "/body/name" } ] );
+
+ # Full object update on PUT
+ my $agreement_with_updated_field = {
+ vendor_id => undef,
+ name => 'New name',
+ description => 'New description',
+ status => 'closed',
+ closure_reason => undef,
+ is_perpetual => 1,
+ renewal_priority => undef,
+ license_info => 'New license_info',
+ };
+
+ $t->put_ok(
+ "//$userid:$password@/api/v1/erm/agreements/$agreement_id" => json =>
+ $agreement_with_updated_field )->status_is(200)
+ ->json_is( '/name' => 'New name' );
+
+ # Authorized attempt to write invalid data
+ my $agreement_with_invalid_field = {
+ blah => "Agreement Blah",
+ name => "Agreement name",
+ description => "Agreement description",
+ status => "closed",
+ closure_reason => undef,
+ is_perpetual => 1,
+ renewal_priority => undef,
+ license_info => "Agreement license_info",
+ };
+
+ $t->put_ok(
+ "//$userid:$password@/api/v1/erm/agreements/$agreement_id" => json =>
+ $agreement_with_invalid_field )->status_is(400)->json_is(
+ "/errors" => [
+ {
+ message => "Properties not allowed: blah.",
+ path => "/body"
+ }
+ ]
+ );
+
+ my $agreement_to_delete =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } );
+ my $non_existent_id = $agreement_to_delete->id;
+ $agreement_to_delete->delete;
+
+ $t->put_ok( "//$userid:$password@/api/v1/erm/agreements/$non_existent_id" =>
+ json => $agreement_with_updated_field )->status_is(404);
+
+ # Wrong method (POST)
+ $agreement_with_updated_field->{agreement_id} = 2;
+
+ $t->post_ok(
+ "//$userid:$password@/api/v1/erm/agreements/$agreement_id" => json =>
+ $agreement_with_updated_field )->status_is(404);
+
+ $schema->storage->txn_rollback;
+};
+
+subtest 'delete() tests' => sub {
+
+ plan tests => 7;
+
+ $schema->storage->txn_begin;
+
+ my $librarian = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 2**28 }
+ }
+ );
+ my $password = 'thePassword123';
+ $librarian->set_password( { password => $password, skip_validation => 1 } );
+ my $userid = $librarian->userid;
+
+ my $patron = $builder->build_object(
+ {
+ class => 'Koha::Patrons',
+ value => { flags => 0 }
+ }
+ );
+
+ $patron->set_password( { password => $password, skip_validation => 1 } );
+ my $unauth_userid = $patron->userid;
+
+ my $agreement_id =
+ $builder->build_object( { class => 'Koha::ERM::Agreements' } )->id;
+
+ # Unauthorized attempt to delete
+ $t->delete_ok(
+ "//$unauth_userid:$password@/api/v1/erm/agreements/$agreement_id")
+ ->status_is(403);
+
+ $t->delete_ok("//$userid:$password@/api/v1/erm/agreements/$agreement_id")
+ ->status_is( 204, 'SWAGGER3.2.4' )->content_is( '', 'SWAGGER3.3.4' );
+
+ $t->delete_ok("//$userid:$password@/api/v1/erm/agreements/$agreement_id")
+ ->status_is(404);
+
+ $schema->storage->txn_rollback;
+};
+