@@ -, +, @@ in interface correctly displayed under the button, and they are active by default API keys". Click on it. --- Koha/ApiKey.pm | 46 +++++ Koha/ApiKeys.pm | 121 +++++++++++++ Koha/Borrower.pm | 32 ++++ Koha/Schema/Result/ApiKey.pm | 122 +++++++++++++ Koha/Schema/Result/Borrower.pm | 15 -- installer/data/mysql/kohastructure.sql | 19 ++ installer/data/mysql/updatedatabase.pl | 30 ++++ .../prog/en/includes/members-toolbar.inc | 5 + .../prog/en/modules/members/apikeys.tt | 78 ++++++++ .../opac-tmpl/bootstrap/en/includes/usermenu.inc | 8 + .../opac-tmpl/bootstrap/en/modules/opac-apikeys.tt | 82 +++++++++ members/apikeys.pl | 82 +++++++++ opac/opac-apikeys.pl | 81 +++++++++ t/db_dependent/Koha/ApiKeys.t | 124 +++++++++++++ t/lib/Page/Members/ApiKeys.pm | 200 +++++++++++++++++++++ t/lib/Page/Members/MemberFlags.pm | 2 +- t/lib/Page/Members/Moremember.pm | 6 +- t/lib/Page/Members/Toolbar.pm | 135 ++++++++++++++ t/lib/Page/Opac/LeftNavigation.pm | 109 +++++++++++ t/lib/Page/Opac/OpacApiKeys.pm | 170 ++++++++++++++++++ t/lib/Page/Opac/OpacSearchHistory.pm | 2 +- t/lib/Page/Opac/OpacUser.pm | 4 +- 22 files changed, 1452 insertions(+), 21 deletions(-) create mode 100644 Koha/ApiKey.pm create mode 100644 Koha/ApiKeys.pm create mode 100644 Koha/Schema/Result/ApiKey.pm create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt create mode 100644 koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt create mode 100755 members/apikeys.pl create mode 100755 opac/opac-apikeys.pl create mode 100644 t/db_dependent/Koha/ApiKeys.t create mode 100644 t/lib/Page/Members/ApiKeys.pm create mode 100644 t/lib/Page/Members/Toolbar.pm create mode 100644 t/lib/Page/Opac/LeftNavigation.pm create mode 100644 t/lib/Page/Opac/OpacApiKeys.pm --- a/Koha/ApiKey.pm +++ a/Koha/ApiKey.pm @@ -0,0 +1,46 @@ +package Koha::ApiKey; + +# Copyright BibLibre 2015 +# +# 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::ApiKey - Koha API Key Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub type { + return 'ApiKey'; +} + +1; --- a/Koha/ApiKeys.pm +++ a/Koha/ApiKeys.pm @@ -0,0 +1,121 @@ +package Koha::ApiKeys; + +# Copyright BibLibre 2015 +# +# 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 Scalar::Util qw(blessed); + +use Koha::Borrowers; +use Koha::ApiKey; + +use base qw(Koha::Objects); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head1 NAME + +Koha::ApiKeys - Koha API Keys Object class + +=head1 API + +=head2 Class Methods + +=cut + +=head3 type + +=cut + +sub type { + return 'ApiKey'; +} + +sub object_class { + return 'Koha::ApiKey'; +} + +sub _get_castable_unique_columns { + return ['api_key_id', 'api_key']; +} + +=head grant + + my $apiKey = Koha::ApiKey->grant({borrower => $borrower, + apiKey => $apiKey + }); + +Granting an ApiKey should be easy. This creates a new ApiKey for the given Borrower, +or sets the owner of an existing key. +$PARAM1 HASHRef of params, { + borrower => MANDATORY, a Koha::Borrower or something castable to one. + apiKey => OPTIONAL, an existing Koha::ApiKEy to give to somebody else. + not sure why anybody would want to do that, but + provided as a convenience for testing. +} +@THROWS Koha::Exception::BadParameter +=cut + +sub grant { + my ($self, $borrower, $apiKey) = @_; + $borrower = Koha::Borrowers->cast($borrower); + if ($apiKey) { + $apiKey = Koha::ApiKeys->cast($apiKey); + $apiKey->borrowernumber($borrower->borrowernumber); + } + else { + $apiKey = new Koha::ApiKey; + $apiKey->borrowernumber($borrower->borrowernumber); + $apiKey->api_key(String::Random->new->randregex('[a-zA-Z0-9]{32}')); + } + $apiKey->store; + return $apiKey; +} + +sub delete { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->delete; + } +} + +sub revoke { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->active(0); + $apiKey->store; + } + return $apiKey; +} + +sub activate { + my ($self, $apiKey) = @_; + $apiKey = Koha::ApiKeys->cast($apiKey); + + if ($apiKey) { + $apiKey->active(1); + $apiKey->store; + } + return $apiKey; +} + +1; --- a/Koha/Borrower.pm +++ a/Koha/Borrower.pm @@ -69,6 +69,38 @@ sub isSuperuser { return (exists($self->{superuser}) && $self->{superuser}) ? 1 : undef; } +=head getApiKeys + + my @apiKeys = $borrower->getApiKeys( $activeOnly ); + +=cut + +sub getApiKeys { + my ($self, $activeOnly) = @_; + + my @dbix_objects = $self->_result()->api_keys({active => 1}); + for (my $i=0 ; $i_new_from_dbic($dbix_objects[$i]); + } + + return \@dbix_objects; +} + +=head getApiKey + + my $apiKey = $borrower->getApiKeys( $activeOnly ); + +=cut + +sub getApiKey { + my ($self, $activeOnly) = @_; + + my $dbix_object = $self->_result()->api_keys({active => 1})->next(); + my $object = Koha::ApiKey->_new_from_dbic($dbix_object); + + return $object; +} + =head1 AUTHOR Kyle M Hall --- a/Koha/Schema/Result/ApiKey.pm +++ a/Koha/Schema/Result/ApiKey.pm @@ -0,0 +1,122 @@ +use utf8; +package Koha::Schema::Result::ApiKey; + +# Created by DBIx::Class::Schema::Loader +# DO NOT MODIFY THE FIRST PART OF THIS FILE + +=head1 NAME + +Koha::Schema::Result::ApiKey + +=cut + +use strict; +use warnings; + +use base 'DBIx::Class::Core'; + +=head1 TABLE: C + +=cut + +__PACKAGE__->table("api_keys"); + +=head1 ACCESSORS + +=head2 api_key_id + + data_type: 'integer' + is_auto_increment: 1 + is_nullable: 0 + +=head2 borrowernumber + + data_type: 'integer' + is_foreign_key: 1 + is_nullable: 0 + +=head2 api_key + + data_type: 'varchar' + is_nullable: 0 + size: 255 + +=head2 last_request_time + + data_type: 'integer' + default_value: 0 + is_nullable: 1 + +=head2 active + + data_type: 'integer' + default_value: 1 + is_nullable: 1 + +=cut + +__PACKAGE__->add_columns( + "api_key_id", + { data_type => "integer", is_auto_increment => 1, is_nullable => 0 }, + "borrowernumber", + { data_type => "integer", is_foreign_key => 1, is_nullable => 0 }, + "api_key", + { data_type => "varchar", is_nullable => 0, size => 255 }, + "last_request_time", + { data_type => "integer", default_value => 0, is_nullable => 1 }, + "active", + { data_type => "integer", default_value => 1, is_nullable => 1 }, +); + +=head1 PRIMARY KEY + +=over 4 + +=item * L + +=back + +=cut + +__PACKAGE__->set_primary_key("api_key_id"); + +=head1 UNIQUE CONSTRAINTS + +=head2 C + +=over 4 + +=item * L + +=item * L + +=back + +=cut + +__PACKAGE__->add_unique_constraint("apk_bornumkey_idx", ["borrowernumber", "api_key"]); + +=head1 RELATIONS + +=head2 borrowernumber + +Type: belongs_to + +Related object: L + +=cut + +__PACKAGE__->belongs_to( + "borrowernumber", + "Koha::Schema::Result::Borrower", + { borrowernumber => "borrowernumber" }, + { is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" }, +); + + +# Created by DBIx::Class::Schema::Loader v0.07039 @ 2015-07-31 11:03:00 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:8nljlCCakQs1X0kTmW7PYw + + +# You can replace this text with custom code or comments, and it will be preserved on regeneration +1; --- a/Koha/Schema/Result/Borrower.pm +++ a/Koha/Schema/Result/Borrower.pm @@ -656,21 +656,6 @@ __PACKAGE__->has_many( { cascade_copy => 0, cascade_delete => 0 }, ); -=head2 api_timestamp - -Type: might_have - -Related object: L - -=cut - -__PACKAGE__->might_have( - "api_timestamp", - "Koha::Schema::Result::ApiTimestamp", - { "foreign.borrowernumber" => "self.borrowernumber" }, - { cascade_copy => 0, cascade_delete => 0 }, -); - =head2 aqbasketusers Type: has_many --- a/installer/data/mysql/kohastructure.sql +++ a/installer/data/mysql/kohastructure.sql @@ -16,6 +16,25 @@ /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -- +-- Table structure for table api_keys +-- + +DROP TABLE IF EXISTS api_keys; +CREATE TABLE api_keys ( + api_key_id INT(11) NOT NULL auto_increment, + borrowernumber INT(11) NOT NULL, -- foreign key to the borrowers table + api_key VARCHAR(255) NOT NULL, -- API key used for API authentication + last_request_time INT(11) default 0, -- UNIX timestamp of when was the last transaction for this API-key? Used for request replay control. + active INT(1) DEFAULT 1, -- 0 means this API key is revoked + PRIMARY KEY (api_key_id), + UNIQUE KEY apk_bornumkey_idx (borrowernumber, api_key), + CONSTRAINT api_keys_fk_borrowernumber + FOREIGN KEY (borrowernumber) + REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- -- Table structure for table `auth_header` -- --- a/installer/data/mysql/updatedatabase.pl +++ a/installer/data/mysql/updatedatabase.pl @@ -10730,6 +10730,36 @@ if ( CheckVersion($DBversion) ) { SetVersion ($DBversion); } +$DBversion = "XXX"; +if(CheckVersion($DBversion)) { + $dbh->do(q{ + DROP TABLE IF EXISTS api_keys; + }); + $dbh->do(q{ + CREATE TABLE api_keys ( + api_key_id INT(11) NOT NULL auto_increment, + borrowernumber INT(11) NOT NULL, -- foreign key to the borrowers table + api_key VARCHAR(255) NOT NULL, -- API key used for API authentication + last_request_time INT(11) default 0, -- UNIX timestamp of when was the last transaction for this API-key? Used for request replay control. + active INT(1) DEFAULT 1, -- 0 means this API key is revoked + PRIMARY KEY (api_key_id), + UNIQUE KEY apk_bornumkey_idx (borrowernumber, api_key), + CONSTRAINT api_keys_fk_borrowernumber + FOREIGN KEY (borrowernumber) + REFERENCES borrowers (borrowernumber) + ON DELETE CASCADE ON UPDATE CASCADE + ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + }); + + use Koha::Auth::PermissionManager; + my $pm = Koha::Auth::PermissionManager->new(); + $pm->addPermission({code => 'manage_api_keys', description => "Manage Borrowers' REST API keys"}); + + print "Upgrade to $DBversion done (Bug 13920: Add API keys table)\n"; + SetVersion($DBversion); +} + + # DEVELOPER PROCESS, search for anything to execute in the db_update directory # SEE bug 13068 # if there is anything in the atomicupdate, read and execute it. --- a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc @@ -170,6 +170,11 @@ function searchToHold(){ [% ELSE %]
  • Set permissions
  • [% END %] + [% IF ( CAN_user_borrowers_manage_api_keys ) %] +
  • Manage API keys
  • + [% ELSE %] +
  • Manage API keys
  • + [% END %] [% IF ( CAN_user_borrowers ) %] [% IF ( NorwegianPatronDBEnable == 1 ) %]
  • Delete local
  • --- a/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/members/apikeys.tt @@ -0,0 +1,78 @@ +[% USE Koha %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Patrons › API Keys +[% INCLUDE 'doc-head-close.inc' %] + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'patron-search.inc' %] + + + +
    +
    +
    +
    + [% INCLUDE 'members-toolbar.inc' %] + +

    API keys for [% INCLUDE 'patron-title.inc' %]

    +
    +
    + + + +
    +
    + [% IF api_keys.size > 0 %] + + + + + + + + + + + [% FOREACH key IN api_keys %] + + + + + + + [% END %] + +
    KeyActiveLast transactionActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %][% key.last_request_time || '' %] +
    + + + + +
    +
    + + + [% IF key.active %] + + + [% ELSE %] + + + [% END %] +
    +
    + [% END %] +
    +
    +
    + [% INCLUDE 'circ-menu.inc' %] +
    +
    +[% INCLUDE 'intranet-bottom.inc' %] --- a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc +++ a/koha-tmpl/opac-tmpl/bootstrap/en/includes/usermenu.inc @@ -1,3 +1,4 @@ +[% USE Koha %] [% IF ( ( Koha.Preference( 'opacuserlogin' ) == 1 ) && loggedinusername ) %] [% END %] --- a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt +++ a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-apikeys.tt @@ -0,0 +1,82 @@ +[% INCLUDE 'doc-head-open.inc' %] +[% IF ( LibraryNameTitle ) %][% LibraryNameTitle %][% ELSE %]Koha online[% END %] catalog › Your library home › Your API keys +[% INCLUDE 'doc-head-close.inc' %] +[% BLOCK cssinclude %][% END %] + +[% INCLUDE 'bodytag.inc' bodyid='opac-user' bodyclass='scrollto' %] +[% INCLUDE 'masthead.inc' %] + +
    + + +
    +
    +
    + +
    +
    +
    +

    Your API keys

    +
    +
    + + +
    +
    + [% IF api_keys.size > 0 %] + + + + + + + + + + + [% FOREACH key IN api_keys %] + + + + + + + [% END %] + +
    KeyActiveLast transactionActions
    [% key.api_key %][% IF key.active %]Yes[% ELSE %]No[% END %][% key.last_request_time || '' %] +
    + + + +
    +
    + + [% IF key.active %] + + + [% ELSE %] + + + [% END %] +
    +
    + [% END %] +
    +
    +
    +
    +
    + +[% BLOCK jsinclude %][% END %] +[% INCLUDE 'opac-bottom.inc' %] --- a/members/apikeys.pl +++ a/members/apikeys.pl @@ -0,0 +1,82 @@ +#!/usr/bin/perl + +# Copyright 2015 BibLibre +# +# 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 2 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 CGI; +use String::Random; + +use C4::Auth; +use C4::Members; +use C4::Output; +use Koha::ApiKeys; +use Koha::ApiKey; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'members/apikeys.tt', + query => $cgi, + type => 'intranet', + authnotrequired => 0, + flagsrequired => {borrowers => 'manage_api_keys'}, +}); + +my $borrowernumber = $cgi->param('borrowernumber'); +my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); +my $op = $cgi->param('op'); + +if ($op) { + if ($op eq 'generate') { + Koha::ApiKeys->grant($borrower); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'delete') { + my $key = $cgi->param('key'); + Koha::ApiKeys->delete($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'revoke') { + my $key = $cgi->param('key'); + Koha::ApiKeys->revoke($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } + + if ($op eq 'activate') { + my $key = $cgi->param('key'); + Koha::ApiKeys->activate($key); + print $cgi->redirect('/cgi-bin/koha/members/apikeys.pl?borrowernumber=' . $borrowernumber); + exit; + } +} + +my @api_keys = Koha::ApiKeys->search({borrowernumber => $borrowernumber}); + +$template->param( + api_keys => \@api_keys, + borrower => $borrower, + borrowernumber => $borrowernumber, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; --- a/opac/opac-apikeys.pl +++ a/opac/opac-apikeys.pl @@ -0,0 +1,81 @@ +#!/usr/bin/env perl + +# Copyright 2015 BibLibre +# +# 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 2 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 CGI; +use String::Random; + +use C4::Auth; +use C4::Members; +use C4::Output; +use Koha::ApiKeys; + +my $cgi = new CGI; + +my ($template, $loggedinuser, $cookie) = get_template_and_user({ + template_name => 'opac-apikeys.tt', + query => $cgi, + type => 'opac', + authnotrequired => 0, +}); + +my $borrowernumber = $loggedinuser; +my $borrower = C4::Members::GetMember(borrowernumber => $borrowernumber); +my $op = $cgi->param('op'); + +if ($op) { + if ($op eq 'generate') { + Koha::ApiKeys->grant($borrower); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'delete') { + my $key = $cgi->param('key'); + Koha::ApiKeys->delete($key); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'revoke') { + my $key = $cgi->param('key'); + Koha::ApiKeys->revoke($key); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } + + if ($op eq 'activate') { + my $key = $cgi->param('key'); + Koha::ApiKeys->activate($key); + print $cgi->redirect('/cgi-bin/koha/opac-apikeys.pl'); + exit; + } +} + +my @api_keys = Koha::ApiKeys->search({borrowernumber => $borrowernumber}); + +$template->param( + apikeysview => 1, + api_keys => \@api_keys, + borrower => $borrower, + borrowernumber => $borrowernumber, +); + +output_html_with_http_headers $cgi, $cookie, $template->output; --- a/t/db_dependent/Koha/ApiKeys.t +++ a/t/db_dependent/Koha/ApiKeys.t @@ -0,0 +1,124 @@ +#!/usr/bin/perl + +# Copyright 2015 Vaara-kirjastot +# +# 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, see . + +use Modern::Perl; +use Test::More; +use Try::Tiny; +use Scalar::Util qw(blessed); + +use Koha::Auth::PermissionManager; +use Koha::ApiKeys; + +use t::lib::TestObjects::ObjectFactory; +use t::lib::TestObjects::BorrowerFactory; +use t::lib::Page::Members::Moremember; +use t::lib::Page::Opac::OpacMain; + +##Setting up the test context +my $testContext = {}; + +my $password = '1234'; +my $borrowerFactory = t::lib::TestObjects::BorrowerFactory->new(); +my $borrowers = $borrowerFactory->createTestGroup([ + {firstname => 'Olli-Antti', + surname => 'Kivi', + cardnumber => '1A01', + branchcode => 'CPL', + password => $password, + }, + {firstname => 'Alli-Ontti', + surname => 'Ivik', + cardnumber => '1A02', + branchcode => 'CPL', + password => $password, + }, + ], undef, $testContext); +my $borrowerKivi = $borrowers->{'1A01'}; +my $borrowerIvik = $borrowers->{'1A02'}; +my $permissionManager = Koha::Auth::PermissionManager->new(); +$permissionManager->grantPermission($borrowerKivi, 'borrowers', 'manage_api_keys'); + + +##Test context set, starting testing: +eval { #run in a eval-block so we don't die without tearing down the test context +subtest "ApiKeys API Unit tests" => sub { + my $borrowerKivi = $borrowers->{'1A01'}; + my $borrowerIvik = $borrowers->{'1A02'}; + + + my $apiKey = Koha::ApiKeys->grant($borrowerKivi); + is($apiKey->borrowernumber, $borrowerKivi->borrowernumber, "ApiKey granted"); + + Koha::ApiKeys->revoke($apiKey); + is($apiKey->active, 0, "ApiKey revoked"); + + Koha::ApiKeys->activate($apiKey); + is($apiKey->active, 1, "ApiKey activated"); + + Koha::ApiKeys->grant($borrowerIvik, $apiKey); + is($apiKey->borrowernumber, $borrowerIvik->borrowernumber, "ApiKey granted to another Borrower"); + + Koha::ApiKeys->delete($apiKey); + $apiKey = Koha::ApiKeys->find({api_key_id => $apiKey->api_key_id}); + ok(not($apiKey), "ApiKey deleted"); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +eval { +subtest "ApiKeys Intra Integration tests" => sub { + my $agent = t::lib::Page::Members::Moremember->new({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->doPasswordLogin($borrowerKivi->userid, $password)->navigateManageApiKeys()->generateNewApiKey(); + my @apiKeys = Koha::ApiKeys->search({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->revokeApiKey($apiKeys[0]->api_key)->deleteApiKey($apiKeys[0]->api_key) + ->quit(); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +eval { +subtest "ApiKeys OPAC Integration tests" => sub { + my $agent = t::lib::Page::Opac::OpacMain->new(); + $agent->doPasswordLogin($borrowerKivi->userid, $password)->navigateYourAPIKeys()->generateNewApiKey(); + my @apiKeys = Koha::ApiKeys->search({borrowernumber => $borrowerKivi->borrowernumber}); + $agent->revokeApiKey($apiKeys[0]->api_key)->deleteApiKey($apiKeys[0]->api_key) + ->quit(); +} +}; +if ($@) { #Catch all leaking errors and gracefully terminate. + warn $@; + tearDown(); + exit 1; +} + +##All tests done, tear down test context +tearDown(); +done_testing; + +sub tearDown { + t::lib::TestObjects::ObjectFactory->tearDownTestContext($testContext); +} --- a/t/lib/Page/Members/ApiKeys.pm +++ a/t/lib/Page/Members/ApiKeys.pm @@ -0,0 +1,200 @@ +package t::lib::Page::Members::ApiKeys; + +# Copyright 2015 KohaSuomi! +# +# 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, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::Members::ApiKeys + +=head SYNOPSIS + +apikeys.pl PageObject providing page functionality as a service! + +=cut + +=head new + + my $apikeys = t::lib::Page::Members::ApiKeys->new({borrowernumber => "1"}); + +Instantiates a WebDriver and loads the members/apikeys.pl. +@PARAM1 HASHRef of optional and MANDATORY parameters +MANDATORY extra parameters: + borrowernumber => loads the page to display Borrower matching the given borrowernumber + +@RETURNS t::lib::Page::Members::ApiKeys, ready for user actions! +=cut + +sub new { + my ($class, $params) = @_; + unless (ref($params) eq 'HASH' || (blessed($params) && $params->isa('t::lib::Page') )) { + $params = {}; + } + $params->{resource} = '/cgi-bin/koha/members/apikeys.pl'; + $params->{type} = 'staff'; + + $params->{getParams} = []; + #Handle MANDATORY parameters + if ($params->{borrowernumber}) { + push @{$params->{getParams}}, "borrowernumber=".$params->{borrowernumber}; + } + else { + Koha::Exception::BadParameter->throw(error => __PACKAGE__."->new():> Parameter 'borrowernumber' is missing."); + } + + my $self = $class->SUPER::new($params); + + return $self; +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getActionsAndTableElements +@RETURNS List of + HASHRef of Selenium::Driver::Webelement-objects matching the generic + actions on this page, eg. 'generateNewKey'. + HASHRef of Selenium::Driver::Webelement-objects keyed with the apiKey hash/text. + These are all the apiKey table rows present, and have the + elements prefetched for easy access. + +=cut + +sub _getActionsAndTableElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $generateNewKeySubmit = $d->find_element("#generatenewkey", 'css'); + + my $a = {}; #Collect action elements here + $a->{generateNewKey} = $generateNewKeySubmit; #Bind the global action here for easy reference. + + my $apiKeyRows; + eval { #We might not have ApiKeys yet. + $apiKeyRows = $d->find_elements("#apikeystable tr", 'css'); + shift @$apiKeyRows; #Remove the table header row + }; + my %apiKeys; + for(my $i=0 ; $i[$i]; + $row->{'nth-of-type'} = $i+1; #starts from 1 + $row->{key} = $d->find_child_element($row, "td.apikeykey", 'css')->get_text(); + $row->{active} = $d->find_child_element($row, "td.apikeyactive", 'css')->get_text(); + $row->{lastTransaction} = $d->find_child_element($row, "td.apikeylastransaction", 'css')->get_text(); + $row->{delete} = $d->find_child_element($row, "input.apikeydelete", 'css'); + eval { + $row->{revoke} = $d->find_child_element($row, "input.apikeyrevoke", 'css'); + }; + eval { + $row->{activate} = $d->find_child_element($row, "input.apikeyactivate", 'css'); + }; + $apiKeys{$row->{key}} = $row; + } + + return ($a, \%apiKeys); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub generateNewApiKey { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + $actionElements->{generateNewKey}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre+1, $apiKeyRowsCountPost, "ApiKey generated"); + return $self; +} + +sub revokeApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{revoke}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after revoking it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'No', "ApiKey revoked"); + return $self; +} + +sub activateApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{activate}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after activating it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'Yes', "ApiKey activated"); + return $self; +} + +sub deleteApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{delete}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre-1, $apiKeyRowsCountPost, "ApiKey deleted"); + return $self; +} + +1; #Make the compiler happy! --- a/t/lib/Page/Members/MemberFlags.pm +++ a/t/lib/Page/Members/MemberFlags.pm @@ -22,7 +22,7 @@ use Test::More; use t::lib::Page::Members::Moremember; -use base qw(t::lib::Page::Intra); +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar); =head NAME t::lib::Page::Members::MemberFlags --- a/t/lib/Page/Members/Moremember.pm +++ a/t/lib/Page/Members/Moremember.pm @@ -20,7 +20,9 @@ package t::lib::Page::Members::Moremember; use Modern::Perl; use Scalar::Util qw(blessed); -use base qw(t::lib::Page::Intra); +use base qw(t::lib::Page::Intra t::lib::Page::Members::Toolbar); + +use t::lib::Page::Members::ApiKeys; use Koha::Exception::BadParameter; @@ -86,4 +88,4 @@ See. Selenium documentation best practices for UI element mapping to common lang -1; #Make the compiler happy! +1; #Make the compiler happy! --- a/t/lib/Page/Members/Toolbar.pm +++ a/t/lib/Page/Members/Toolbar.pm @@ -0,0 +1,135 @@ +package t::lib::Page::Members::Toolbar; + +# Copyright 2015 KohaSuomi! +# +# 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, see . + +use Modern::Perl; +use Test::More; + +use Koha::Exception::BadParameter; + +=head NAME t::lib::Page::Members::Toolbar + +=head SYNOPSIS + +PageObject Accessory encapsulating specific page-related functionality. +Is a static class and should not be attached to the instantiated PageObject. +this way we preserve the interoperability of the UserAgent when it moves from page +to page. + +In this case this encapsulates the members-module toolbar's services and provides +a reusable class from all member-module PageObjects. + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getToolbarActionElements +Shares the same toolbar with moremember.pl +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the actions toolbar over the Borrower information. +=cut + +sub _getToolbarActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $editA = $d->find_element("#editpatron", 'css'); + my $changePasswordA = $d->find_element("#changepassword", 'css'); + my $duplicateA = $d->find_element("#duplicate", 'css'); + my $printButton = $d->find_element("#duplicate + div > button", 'css'); + my $searchToHoldA = $d->find_element("#searchtohold", 'css'); + my $moreButton = $d->find_element("#searchtohold + div > button", 'css'); + + my $e = {}; + $e->{edit} = $editA if $editA; + $e->{changePassword} = $changePasswordA if $changePasswordA; + $e->{duplicate} = $duplicateA if $duplicateA; + $e->{print} = $printButton if $printButton; + $e->{searchToHold} = $searchToHoldA if $searchToHoldA; + $e->{more} = $moreButton if $moreButton; + return $e; +} + +=head _getMoreDropdownElements +Clicks the dropdown open if it isnt yet. +@RETURNS HASHRef of all the dropdown elements under the More button in the toolbar + over Borrower information. +=cut + +sub _getMoreDropdownElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $toolbarElements = $self->_getToolbarActionElements(); + my $moreButton = $toolbarElements->{more}; + my $deleteA; + eval { + $deleteA = $d->find_child_element($moreButton, "#deletepatron", 'css'); + }; + unless ($deleteA && $deleteA->is_visible()) { + $moreButton->click(); + $self->debugTakeSessionSnapshot(); + } + + my $renewPatronA = $d->find_element("#renewpatron", 'css'); + my $setPermissionsA = $d->find_element("#patronflags", 'css'); + my $manageApiKeysA = $d->find_element("#apikeys", 'css'); + $deleteA = $d->find_element("#deletepatron", 'css'); + $self->debugTakeSessionSnapshot(); + my $updateChildToAdultPatronA = $d->find_element("#updatechild", 'css'); + my $exportCheckinBarcodesA = $d->find_element("#exportcheckins", 'css'); + + my $e = {}; + $e->{renewPatron} = $renewPatronA if $renewPatronA; + $e->{setPermissions} = $setPermissionsA if $setPermissionsA; + $e->{manageApiKeys} = $manageApiKeysA if $manageApiKeysA; + $e->{delete} = $deleteA if $deleteA; + $e->{updateChildToAdultPatron} = $updateChildToAdultPatronA if $updateChildToAdultPatronA; + $e->{exportCheckinBarcodes} = $exportCheckinBarcodesA if $exportCheckinBarcodesA; + return $e; +} + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub navigateManageApiKeys { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getMoreDropdownElements(); + $elements->{manageApiKeys}->click(); + ok($d->get_title() =~ m/API Keys/, "Intra Navigate to Manage API Keys"); + + $self->debugTakeSessionSnapshot(); + + return t::lib::Page::Members::ApiKeys->rebrandFromPageObject($self); +} + + + + +1; #Make the compiler happy! --- a/t/lib/Page/Opac/LeftNavigation.pm +++ a/t/lib/Page/Opac/LeftNavigation.pm @@ -0,0 +1,109 @@ +package t::lib::Page::Opac::LeftNavigation; + +# Copyright 2015 Open Source Freedom Fighters +# +# 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, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use t::lib::Page::Opac::OpacApiKeys; + +=head NAME t::lib::Page::Opac::LeftNavigation + +=head SYNOPSIS + +Provides the services of the Opac left navigation column/frame for the implementing PageObject + +=cut + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getLeftNavigationActionElements +@RETURNS HASHRef of Selenium::Driver::Webelements matching all the clickable elements + in the left navigation frame/column at all Opac pages requiring login. +=cut + +sub _getLeftNavigationActionElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $e = {}; + eval { + $e->{yourSummary} = $d->find_element("a[href*='opac-user.pl']", 'css'); + }; + eval { + $e->{yourFines} = $d->find_element("a[href*='opac-account.pl']", 'css'); + }; + eval { + $e->{yourPersonalDetails} = $d->find_element("a[href*='opac-memberentry.pl']", 'css'); + }; + eval { + $e->{yourTags} = $d->find_element("a[href*='opac-tags.pl']", 'css'); + }; + eval { + $e->{changeYourPassword} = $d->find_element("a[href*='opac-passwd.pl']", 'css'); + }; + eval { + $e->{yourSearchHistory} = $d->find_element("a[href*='opac-search-history.pl']", 'css'); + }; + eval { + $e->{yourReadingHistory} = $d->find_element("a[href*='opac-readingrecord.pl']", 'css'); + }; + eval { + $e->{yourPurchaseSuggestions} = $d->find_element("a[href*='opac-suggestions.pl']", 'css'); + }; + eval { + $e->{yourLists} = $d->find_element("a[href*='opac-shelves.pl']", 'css'); + }; + eval { + $e->{yourAPIKeys} = $d->find_element("a[href*='opac-apikeys.pl']", 'css'); + }; + return $e; +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub navigateYourAPIKeys { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my $elements = $self->_getLeftNavigationActionElements(); + $elements->{yourAPIKeys}->click(); + $self->debugTakeSessionSnapshot(); + + my $breadcrumbs = $self->_getBreadcrumbLinks(); + + ok(ref($breadcrumbs) eq 'ARRAY' && + $breadcrumbs->[scalar(@$breadcrumbs)-1]->get_text() =~ m/API keys/i, + "Opac Navigate to Your API Keys"); + + return t::lib::Page::Opac::OpacApiKeys->rebrandFromPageObject($self); +} + +1; #Make the compiler happy! --- a/t/lib/Page/Opac/OpacApiKeys.pm +++ a/t/lib/Page/Opac/OpacApiKeys.pm @@ -0,0 +1,170 @@ +package t::lib::Page::Opac::OpacApiKeys; + +# Copyright 2015 KohaSuomi! +# +# 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, see . + +use Modern::Perl; +use Scalar::Util qw(blessed); +use Test::More; + +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); + +use Koha::Exception::BadParameter; +use Koha::Exception::UnknownObject; + +=head NAME t::lib::Page::Members::ApiKeys + +=head SYNOPSIS + +apikeys.pl PageObject providing page functionality as a service! + +=cut + +sub new { + Koha::Exception::FeatureUnavailable->throw(error => __PACKAGE__."->new():> You must login first to navigate to this page!"); +} + +################################################################################ +=head UI Mapping helper subroutines +See. Selenium documentation best practices for UI element mapping to common language descriptions. +=cut +################################################################################ + +=head _getActionsAndTableElements +@RETURNS List of + HASHRef of Selenium::Driver::Webelement-objects matching the generic + actions on this page, eg. 'generateNewKey'. + HASHRef of Selenium::Driver::Webelement-objects keyed with the apiKey hash/text. + These are all the apiKey table rows present, and have the + elements prefetched for easy access. + +=cut + +sub _getActionsAndTableElements { + my ($self) = @_; + my $d = $self->getDriver(); + + my $generateNewKeySubmit = $d->find_element("#generatenewkey", 'css'); + + my $a = {}; #Collect action elements here + $a->{generateNewKey} = $generateNewKeySubmit; #Bind the global action here for easy reference. + + my $apiKeyRows; + eval { #We might not have ApiKeys yet. + $apiKeyRows = $d->find_elements("#apikeystable tr", 'css'); + shift @$apiKeyRows; #Remove the table header row + }; + my %apiKeys; + for(my $i=0 ; $i[$i]; + $row->{'nth-of-type'} = $i+1; #starts from 1 + $row->{key} = $d->find_child_element($row, "td.apikeykey", 'css')->get_text(); + $row->{active} = $d->find_child_element($row, "td.apikeyactive", 'css')->get_text(); + $row->{lastTransaction} = $d->find_child_element($row, "td.apikeylastransaction", 'css')->get_text(); + $row->{delete} = $d->find_child_element($row, "input.apikeydelete", 'css'); + eval { + $row->{revoke} = $d->find_child_element($row, "input.apikeyrevoke", 'css'); + }; + eval { + $row->{activate} = $d->find_child_element($row, "input.apikeyactivate", 'css'); + }; + $apiKeys{$row->{key}} = $row; + } + + return ($a, \%apiKeys); +} + + + +################################################################################ +=head PageObject Services + +=cut +################################################################################ + +sub generateNewApiKey { + my ($self) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + $actionElements->{generateNewKey}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre+1, $apiKeyRowsCountPost, "ApiKey generated"); + return $self; +} + +sub revokeApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{revoke}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after revoking it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'No', "ApiKey revoked"); + return $self; +} + +sub activateApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{activate}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found after activating it.") unless $apiKeyRow; + is($apiKeyRow->{active}, 'Yes', "ApiKey activated"); + return $self; +} + +sub deleteApiKey { + my ($self, $apiKey) = @_; + my $d = $self->getDriver(); + $self->debugTakeSessionSnapshot(); + + my ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPre = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + my $apiKeyRow = $apiKeyRows->{$apiKey}; + Koha::Exception::UnknownObject->throw(error => __PACKAGE__."revokeApiKey():> No matching apiKey '$apiKey' found.") unless $apiKeyRow; + $apiKeyRow->{delete}->click(); + $self->debugTakeSessionSnapshot(); + + ($actionElements, $apiKeyRows) = $self->_getActionsAndTableElements(); + my $apiKeyRowsCountPost = (ref $apiKeyRows eq 'HASH') ? scalar(keys(%$apiKeyRows)) : 0; + is($apiKeyRowsCountPre-1, $apiKeyRowsCountPost, "ApiKey deleted"); + return $self; +} + +1; #Make the compiler happy! --- a/t/lib/Page/Opac/OpacSearchHistory.pm +++ a/t/lib/Page/Opac/OpacSearchHistory.pm @@ -20,7 +20,7 @@ package t::lib::Page::Opac::OpacSearchHistory; use Modern::Perl; use Test::More; -use base qw(t::lib::Page::Opac); +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); use Koha::Exception::FeatureUnavailable; --- a/t/lib/Page/Opac/OpacUser.pm +++ a/t/lib/Page/Opac/OpacUser.pm @@ -19,7 +19,7 @@ package t::lib::Page::Opac::OpacUser; use Modern::Perl; -use base qw(t::lib::Page::Opac); +use base qw(t::lib::Page::Opac t::lib::Page::Opac::LeftNavigation); use Koha::Exception::FeatureUnavailable; @@ -61,4 +61,4 @@ See. Selenium documentation best practices for UI element mapping to common lang -1; #Make the compiler happy! +1; #Make the compiler happy! --