Make some improvements to Cronscript.pm.
authorJason Stephenson <jstephenson@mvlc.org>
Thu, 1 Dec 2011 17:38:55 +0000 (12:38 -0500)
committerBill Erickson <berick@esilibrary.com>
Fri, 16 Dec 2011 18:46:06 +0000 (13:46 -0500)
Allow the session method to function when called more than once with
different service names.

Add some useful new methods:

logout: Calls open-ils.auth.session.delete if we're logged in.

die_event: Causes a script to croak if the object passed in is an event,
or optionally a certain named event.

warn_event: Causes a script to output an error message if the object passed
in is an event, or optionally a certain named event.

Use OpenILS::Application::AppUtils.

Default to staff login in Cronscript->authenticate method.

Improve the Croncript.pm.in POD to include a better synopsis of its features.

Clean up some other sections of the POD, remove the TODO section, and add
some SEE ALSO entries.

Signed-off-by: Jason Stephenson <jstephenson@mvlc.org>
Signed-off-by: Bill Erickson <berick@esilibrary.com>
Open-ILS/src/perlmods/lib/OpenILS/Utils/Cronscript.pm.in

index 472c445..e96866d 100644 (file)
@@ -3,6 +3,8 @@ package OpenILS::Utils::Cronscript;
 # ---------------------------------------------------------------
 # Copyright (C) 2010 Equinox Software, Inc
 # Author: Joe Atzberger <jatzberger@esilibrary.com>
+# Portions Copyright (C) 2011 Merrimack Valley Library Consortium
+# Author: Jason Stephenson <jstephenson@mvlc.org>
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -32,6 +34,7 @@ use OpenSRF::EX qw(:try);
 use OpenILS::Utils::Fieldmapper;
 use OpenILS::Utils::Lockfile;
 use OpenILS::Utils::CStoreEditor q/:funcs/;
+use OpenILS::Application::AppUtils;
 
 use File::Basename qw/fileparse/;
 
@@ -47,6 +50,8 @@ our @extra_opts = (     # additional keys are stored here
 
 our $debug = 0;
 
+my $apputils = 'OpenILS::Application::AppUtils';
+
 sub _default_self {
     return {
     #   opts       => {},
@@ -251,7 +256,7 @@ sub session {
     my $self = shift or return;
     $self->{bootstrapped} or $self->bootstrap();
     @_ or croak "session() called without required argument (app_name, e.g. 'open-ils.acq')";
-    return ($self->{session} ||= OpenSRF::AppSession->create(@_));
+    return OpenSRF::AppSession->create(@_);
 }
 
 sub bootstrap {
@@ -285,6 +290,34 @@ sub editor {
     return new_editor(@_);
 }
 
+# Die on an event. Takes an optional third parameter for the textcode
+# of an event to die on. If the event textcode does not match the
+# third parameter, will warn on the event instead of dying.
+sub die_event {
+    my $self = shift;
+    my $e = shift;
+    my $name = shift;
+    if ($apputils->event_code($e)) {
+        if (!defined($name) || $apputils->event_equals($e,$name)) {
+            croak(Dumper $e);
+        } else {
+            carp(Dumper $e);
+        }
+    }
+}
+
+# Prints warning on an even. Takes an optional third parameter for the
+# textcode of an event to warn on.
+sub warn_event {
+    my $self = shift;
+    my $e = shift;
+    my $name = shift;
+    if ($apputils->event_code($e)
+            && (!defined($name) || $apputils->event_equals($e, $name))) {
+        carp(Dumper $e);
+    }
+}
+
 # Authenticate with the open-ils.auth module.
 # Takes a hash ref of arguments:
 # {
@@ -300,9 +333,10 @@ sub authenticate {
     my $self = shift or return;
     my $args = shift or return;
     if ($args && ref($args) eq 'HASH') {
-        $self->{bootstrapped} or $self->bootstrap();
+        # Default to staff in case the back end ever stops doing so.
+        $args->{type} = 'staff' unless (defined($args->{type}));
 
-        my $session = OpenSRF::AppSession->create('open-ils.auth');
+        my $session = $self->session('open-ils.auth');
         my $seed = $session->request(
             'open-ils.auth.authenticate.init', $args->{'username'}
         )->gather(1);
@@ -319,6 +353,7 @@ sub authenticate {
         } else {
             $self->{authtoken} = undef;
             $self->{authtime} = undef;
+            carp("Authentication failed");
         }
         $session->disconnect();
         return $self->authtoken;
@@ -327,6 +362,27 @@ sub authenticate {
     }
 }
 
+# Deletes the session for our authtoken if we have logged in with the
+# authenticate method.
+sub logout {
+    my $self = shift or return;
+    my $token = shift || $self->{authtoken};
+    if ($token) {
+        my $session = $self->session('open-ils.auth');
+        if ($session->request('open-ils.auth.session.delete', $token)->gather(1)) {
+            if ($token eq $self->{authtoken}) {
+                $self->{authtoken} = undef;
+                $self->{authtime} = undef;
+            }
+        } else {
+            carp("Not authenticated");
+        }
+        $session->disconnect();
+    } else {
+        carp("No authtoken");
+    }
+}
+
 sub authtoken {
     my $self = shift;
     return $self->{authtoken};
@@ -344,7 +400,8 @@ __END__
 
 =head1 NAME
 
-OpenILS::Utils::Cronscript - Consolidated options handling for any script (not just cron, really)
+OpenILS::Utils::Cronscript - Consolidated options handling and utility
+methods for any script (not just cron, really)
 
 =head1 SYNOPSIS
 
@@ -356,47 +413,131 @@ OpenILS::Utils::Cronscript - Consolidated options handling for any script (not j
         'user=s'     => 'admin',
         'password=s' => '',
         'nolockfile' => 1,
-    };
+    );
 
     my $core = OpenILS::Utils::Cronscript->new(\%defaults);
     my $opts = $core->MyGetOptions();   # options now in, e.g.: $opts->{max}
     $core->bootstrap;
 
-Or if you don't need any additional options and just want to get a session going:
-    
+You can skip alot of the above if you're happy with the defaults:
+
+    my $script = OpenILS::Utils::Cronscript->new();
+
+If you just don't want a lock file:
+
+    my $core = OpenILS::Utils::Cronscript->new({nolockfile=>1});
+
+Or if you don't need any additional options and just want to get a
+session going:
+
     use OpenILS::Utils::Cronscript;
     my $session = OpenILS::Utils::Cronscript->new()->session('open-ils.acq');
 
+Cronscript gives you access to many useful methods:
+
+You can login if necessary:
+
+    my $account = {
+        username => 'admin',
+        password => 'password',
+        workstation => 'workstation_name', # optional
+        type => 'staff' # optional, but staff is the default
+    };
+    my $authtoken = $core->authenticate($account);
+
+You can logout a session given its authtoken:
+
+    $core->logout($authtoken);
+
+Or, if you've authenticated with the authenticate method, you can
+logout the most recently authenticated session:
+
+    $core->logout();
+
+If you have logged in with the authenticate method, you can retrieve
+your current authtoken or authtime values:
+
+    my $token = $core->authtoken;
+    my $authtime = $core->authtime;
+
+You can create a CStoreEdtor object:
+
+    my $editor = $core->editor(); # With defaults.
+    my $editor = $core->editor(authtoken=>$authtoken); # with a given
+                                                       # session
+    my $editor = $core->editor(xact=>1); # With transactions or any
+                                         # other CStoreEditor options.
+
+You can create OpenSRF AppSesions to run commands:
+
+    my $pcrud = $core->session('open-ils.pcrud');
+    #...Do some pcrud stuff here.
+
+You can print warnings or die on events:
+
+    my $evt ...;
+    $core->warn_event($evt);
+    $core->die_event($evt);
+
+Or only on certain events:
+
+     $core->warn_event($evt, 'PERM_FAILURE');
+     $core->die_event($evt, 'PERM_FAILURE');
+
+Includes a shared debug flag so you can turn debug mode on and off:
+
+    $OpenILS::Utils::Cronscript::debug = 1; # Debugging on
+    $OpenILS::Utils::Cronscript::debug = 0; # Debugging off
+
+Includes OpenILS::Application::Apputils so using AppUtils methods is
+as simple as:
+
+    my $apputils = 'OpenILS::Application::AppUtils';
+    $apputils->event_code($evt);
+
+Uses and imports the OpenILS::Utils::Fieldmapper so you don't have to.
+
+Includes Data::Dumper, so you don't have to.
+
 =head1 DESCRIPTION
 
-There are a few main problems when writing a new script for Evergreen. 
+There are a few main problems when writing a new script for Evergreen.
 
 =head2 Initialization
 
-The runtime 
-environment for the application requires a lot of initialization, but during normal operation it
-has already occured (when Evergreen was started).  So most of the EG code never has to deal with 
-this problem, but standalone scripts do.  The timing and sequence of requisite events is important and not obvious.
+The runtime environment for the application requires a lot of
+initialization, but during normal operation it has already occured
+(when Evergreen was started).  So most of the EG code never has to
+deal with this problem, but standalone scripts do.  The timing and
+sequence of requisite events is important and not obvious.
 
 =head2 Common Options, Consistent Options
 
-We need several common options for each script that accesses the database or
-uses EG data objects and methods.  Logically, these options often deal with initialization.  They
-should take the B<exact> same form(s) for each script and should not be 
-dependent on the local author to copy and paste them from some reference source.  We really don't want to encourage (let alone force)
-admins to use C<--config>, C<--osrf-confg>, C<-c>, and C<@ARGV[2]> for the same purpose in different scripts, with different
-default handling, help descriptions and error messages (or lack thereof).
+We need several common options for each script that accesses the
+database or uses EG data objects and methods.  Logically, these
+options often deal with initialization.  They should take the B<exact>
+same form(s) for each script and should not be dependent on the local
+author to copy and paste them from some reference source.  We really
+don't want to encourage (let alone force) admins to use C<--config>,
+C<--osrf-confg>, C<-c>, and C<@ARGV[2]> for the same purpose in
+different scripts, with different default handling, help descriptions
+and error messages (or lack thereof).
 
-This suggests broader problem of UI consistency and uniformity, also partially addressed by this module.
+This suggests broader problem of UI consistency and uniformity, also
+partially addressed by this module.
 
 =head2 Lockfiles
 
-A lockfile is necessary for a script that wants to prevent possible simultaneous execution.  For example, consider a script 
-that is scheduled to run frequently, but that experiences occasional high load: you wouldn't want crontab to start running
-it again if the first instance had not yet finished.  
+A lockfile is necessary for a script that wants to prevent possible
+simultaneous execution.  For example, consider a script that is
+scheduled to run frequently, but that experiences occasional high
+load: you wouldn't want crontab to start running it again if the first
+instance had not yet finished.
 
-But the code for creating, writing to, checking for, reading and cleaning up a lockfile for the script bloats what might otherwise be a terse 
-method call.  Conscript handles lockfile generation and removal automatically.
+But the code for creating, writing to, checking for, reading and
+cleaning up a lockfile for the script bloats what might otherwise be a
+terse method call.  Conscript handles lockfile generation and removal
+automatically.
 
 =head1 OPTIONS
 
@@ -408,19 +549,17 @@ The common options (and default values) are:
     'verbose+'      => 0,
     'help'          => 0,
 
-=head1 TODO 
-
-More docs here.
-
 =head1 SEE ALSO
 
     Getopt::Long
+    OpenILS::Application::AppUtils
+    OpenILS::Utils::Fieldmapper
     OpenILS::Utils::Lockfile
-    oils_header.pl
 
-=head1 AUTHOR
+=head1 AUTHORS
 
-Joe Atzberger <jatzberger@esilibrary.com>
+    Joe Atzberger <jatzberger@esilibrary.com>
+    Jason Stephenson <jstephenson@mvlc.org>
 
 =cut