From 9b0046c4435949c572f91ad1c1fc6ab1e1b577d4 Mon Sep 17 00:00:00 2001 From: erickson Date: Mon, 26 Jun 2006 20:45:26 +0000 Subject: [PATCH] modified Editor to work with cstore git-svn-id: svn://svn.open-ils.org/ILS/trunk@4791 dcc99617-32d9-48b4-a31d-7c20da2025e4 --- .../src/perlmods/OpenILS/Utils/CStoreEditor.pm | 533 +++++++++++++++++++++ 1 file changed, 533 insertions(+) create mode 100644 Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm diff --git a/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm b/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm new file mode 100644 index 0000000000..77949ba2ea --- /dev/null +++ b/Open-ILS/src/perlmods/OpenILS/Utils/CStoreEditor.pm @@ -0,0 +1,533 @@ +use strict; use warnings; +package OpenILS::Utils::CStoreEditor; +use OpenILS::Application::AppUtils; +use OpenSRF::AppSession; +use OpenSRF::EX qw(:try); +use OpenILS::Utils::Fieldmapper; +use OpenILS::Event; +use Data::Dumper; +use JSON; +use OpenSRF::Utils::Logger qw($logger); +my $U = "OpenILS::Application::AppUtils"; +my %PERMS; + + +# ----------------------------------------------------------------------------- +# Export some useful functions +# ----------------------------------------------------------------------------- +use vars qw(@EXPORT_OK %EXPORT_TAGS); +use Exporter; +use base qw/Exporter/; +push @EXPORT_OK, 'new_editor'; +%EXPORT_TAGS = ( funcs => [ qw/ new_editor / ] ); + +sub new_editor { return OpenILS::Utils::CStoreEditor->new(@_); } + + +# ----------------------------------------------------------------------------- +# These need to be auto-generated +# ----------------------------------------------------------------------------- +use constant E => 'error'; +use constant W => 'warn'; +use constant I => 'info'; +use constant D => 'debug'; +use constant A => 'activity'; + + + +# ----------------------------------------------------------------------------- +# Params include: +# xact=> : creates a storage transaction +# authtoken=>$token : the login session key +# ----------------------------------------------------------------------------- +sub new { + my( $class, %params ) = @_; + $class = ref($class) || $class; + my $self = bless( \%params, $class ); + $self->{checked_perms} = {}; + return $self; +} + +# ----------------------------------------------------------------------------- +# Log the editor metadata along with the log string +# ----------------------------------------------------------------------------- +sub log { + my( $self, $lev, $str ) = @_; + my $s = "editor["; + $s .= "0|" unless $self->{xact}; + $s .= "1|" if $self->{xact}; + $s .= "0" unless $self->requestor; + $s .= $self->requestor->id if $self->requestor; + $s .= "]"; + $logger->$lev("$s $str"); +} + +# ----------------------------------------------------------------------------- +# Verifies the auth token and fetches the requestor object +# ----------------------------------------------------------------------------- +sub checkauth { + my $self = shift; + $self->log(D, "checking auth token ".$self->authtoken); + my ($reqr, $evt) = $U->checkses($self->authtoken); + $self->event($evt) if $evt; + return $self->{requestor} = $reqr; +} + + +# ----------------------------------------------------------------------------- +# Returns the last generated event +# ----------------------------------------------------------------------------- +sub event { + my( $self, $evt ) = @_; + $self->{event} = $evt if $evt; + return $self->{event}; +} + +# ----------------------------------------------------------------------------- +# Clears the last caught event +# ----------------------------------------------------------------------------- +sub clear_event { + my $self = shift; + $self->{event} = undef; +} + +sub authtoken { + my( $self, $auth ) = @_; + $self->{authtoken} = $auth if $auth; + return $self->{authtoken}; +} + +# ----------------------------------------------------------------------------- +# fetches the session, creating if necessary. If 'xact' is true on this +# object, a db session is created +# ----------------------------------------------------------------------------- +sub session { + my( $self, $session ) = @_; + $self->{session} = $session if $session; + + if(!$self->{session}) { + $self->{session} = OpenSRF::AppSession->create('open-ils.cstore'); + + if( ! $self->{session} ) { + my $str = "Error creating storage session with OpenSRF::AppSession->create()!"; + $self->log(E, $str); + throw OpenSRF::EX::ERROR ($str); + } + + $self->{session}->connect if $self->{xact} or $self->{connect}; + $self->xact_start if $self->{xact}; + } + return $self->{session}; +} + + +# ----------------------------------------------------------------------------- +# Starts a storage transaction +# ----------------------------------------------------------------------------- +sub xact_start { + my $self = shift; + $self->log(D, "starting new db session"); + my $stat = $self->request('open-ils.cstore.transaction.begin'); + $self->log(E, "error starting database transaction") unless $stat; + return $stat; +} + +# ----------------------------------------------------------------------------- +# Commits a storage transaction +# ----------------------------------------------------------------------------- +sub xact_commit { + my $self = shift; + $self->log(D, "comitting db session"); + my $stat = $self->request('open-ils.cstore.transaction.commit'); + $self->log(E, "error comitting database transaction") unless $stat; + return $stat; +} + +# ----------------------------------------------------------------------------- +# Rolls back a storage stransaction +# ----------------------------------------------------------------------------- +sub xact_rollback { + my $self = shift; + $self->log(I, "rolling back db session"); + return $self->request("open-ils.cstore.transaction.rollback"); +} + + +# ----------------------------------------------------------------------------- +# commits the db session and destroys the session +# ----------------------------------------------------------------------------- +sub commit { + my $self = shift; + return unless $self->{xact}; + $self->xact_commit; + $self->session->disconnect; + $self->{session} = undef; +} + +# ----------------------------------------------------------------------------- +# clears all object data. Does not commit the db transaction. +# ----------------------------------------------------------------------------- +sub reset { + my $self = shift; + $self->session->disconnect if $self->{session}; + $$self{$_} = undef for (keys %$self); +} + + +# ----------------------------------------------------------------------------- +# commits and resets +# ----------------------------------------------------------------------------- +sub finish { + my $self = shift; + $self->commit; + $self->reset; +} + + + +# ----------------------------------------------------------------------------- +# Does a simple storage request +# ----------------------------------------------------------------------------- +sub request { + my( $self, $method, @params ) = @_; + + my $val; + my $err; + my $argstr = __arg_to_string( (scalar(@params)) == 1 ? $params[0] : \@params); + + $self->log(I, "request $method : $argstr"); + + try { + $val = $self->session->request($method, @params)->gather(1); + + } catch Error with { + $err = shift; + $self->log(E, "request error $method : $argstr : $err"); + }; + + throw $err if $err; + return $val; +} + + +# ----------------------------------------------------------------------------- +# Sets / Returns the requstor object. This is set when checkauth succeeds. +# ----------------------------------------------------------------------------- +sub requestor { + my($self, $requestor) = @_; + $self->{requestor} = $requestor if $requestor; + return $self->{requestor}; +} + + + +# ----------------------------------------------------------------------------- +# Holds the last data received from a storage call +# ----------------------------------------------------------------------------- +sub data { + my( $self, $data ) = @_; + $self->{data} = $data if defined $data; + return $self->{data}; +} + + +# ----------------------------------------------------------------------------- +# True if this perm has already been checked at this org +# ----------------------------------------------------------------------------- +sub perm_checked { + my( $self, $perm, $org ) = @_; + $self->{checked_perms}->{$org} = {} + unless $self->{checked_perms}->{$org}; + my $checked = $self->{checked_perms}->{$org}->{$perm}; + if(!$checked) { + $self->{checked_perms}->{$org}->{$perm} = 1; + return 0; + } + return 1; +} + + + +# ----------------------------------------------------------------------------- +# Returns true if the requested perm is allowed. If the perm check fails, +# $e->event is set and undef is returned +# The perm user is $e->requestor->id and perm org defaults to the requestor's +# ws_ou +# If this perm at the given org has already been verified, true is returned +# and the perm is not re-checked +# ----------------------------------------------------------------------------- +sub allowed { + my( $self, $perm, $org ) = @_; + my $uid = $self->requestor->id; + $org ||= $self->requestor->ws_ou; + $self->log(I, "checking perms user=$uid, org=$org, perm=$perm"); + return 1 if $self->perm_checked($perm, $org); + return $self->checkperm($uid, $org, $perm); +} + +sub checkperm { + my($self, $userid, $org, $perm) = @_; + my $s = $U->storagereq( + "open-ils.storage.permission.user_has_perm", $userid, $perm, $org ); + + if(!$s) { + my $e = OpenILS::Event->new('PERM_FAILURE', ilsperm => $perm, ilspermloc => $org); + $self->event($e); + return undef; + } + + return 1; +} + + + +# ----------------------------------------------------------------------------- +# checks the appropriate perm for the operation +# ----------------------------------------------------------------------------- +sub _checkperm { + my( $self, $ptype, $action, $org ) = @_; + $org ||= $self->requestor->ws_ou; + my $perm = $PERMS{$ptype}{$action}; + if( $perm ) { + return undef if $self->perm_checked($perm, $org); + return $self->event unless $self->allowed($perm, $org); + } else { + $self->log(E, "no perm provided for $ptype.$action"); + } + return undef; +} + + + +# ----------------------------------------------------------------------------- +# Logs update actions to the activity log +# ----------------------------------------------------------------------------- +sub log_activity { + my( $self, $type, $action, $arg ) = @_; + my $str = "$type.$action"; + $str .= _prop_string($arg); + $self->log(A, $str); +} + + + +sub _prop_string { + my $obj = shift; + my @props = $obj->properties; + my $str = ""; + for(@props) { + my $prop = $obj->$_() || ""; + $prop = substr($prop, 0, 128) . "..." if length $prop > 131; + $str .= " $_=$prop"; + } + return $str; +} + + +sub __arg_to_string { + my $arg = shift; + return "" unless defined $arg; + return $arg->id if UNIVERSAL::isa($arg, "Fieldmapper"); + return JSON->perl2JSON($arg); +} + + +# ----------------------------------------------------------------------------- +# This does the actual storage query. +# +# 'search' calls become search_where calls and $arg can be a search hash or +# an array-ref of storage search options. +# +# 'retrieve' expects an id +# 'update' expects an object +# 'create' expects an object +# 'delete' expects an object +# +# All methods return true on success and undef on failure. On failure, +# $e->event is set to the generated event. +# Note: this method assumes that updating a non-changed object and +# thereby receiving a 0 from storage, is a successful update. +# +# The method will therefore return true so the caller can just do +# $e->update_blah($x) or return $e->event; +# The true value returned from storage for all methods will be stored in +# $e->data, until the next method is called. +# +# not-found events are generated on retrieve and serach methods. +# action=search methods will return [] (==true) if no data is found. If the +# caller is interested in the not found event, they can do: +# return $e->event unless @$results; +# ----------------------------------------------------------------------------- +sub runmethod { + my( $self, $action, $type, $arg, $options ) = @_; + + my @arg = ($arg); + my $method = "open-ils.cstore.direct.$type.$action"; + + if( $action eq 'search' ) { + $method = "$method.atomic"; + @arg = @$arg if ref($arg) eq 'ARRAY'; + + } elsif( $action eq 'batch_retrieve' ) { + $action = 'search'; + @arg = ( { id => $arg } ); + $method =~ s/batch_retrieve/search/o; + $method = "$method.atomic"; + + } elsif( $action eq 'retrieve_all' ) { + $action = 'search'; + $method =~ s/retrieve_all/search/o; + @arg = ( { id => { '!=' => 0 } } ); + $method = "$method.atomic"; + } + + $method =~ s/search/id_list/o if $options->{idlist}; + + # remove any stale events + $self->clear_event; + + if( $action eq 'update' or $action eq 'delete' or $action eq 'create' ) { + if(!$self->{xact}) { + $logger->error("Attempt to update DB while not in a transaction : $method"); + throw OpenSRF::EX::ERROR ("Attempt to update DB while not in a transaction : $method"); + } + $self->log_activity($type, $action, $arg); + } + + if($$options{checkperm}) { + my $a = ($action eq 'search') ? 'retrieve' : $action; + my $e = $self->_checkperm($type, $a, $$options{permorg}); + if($e) { + $self->event($e); + return undef; + } + } + + my $obj; + my $err; + + try { + $obj = $self->request($method, @arg); + } catch Error with { $err = shift; }; + + + if(!defined $obj) { + $self->log(I, "request returned no data"); + + if( $action eq 'retrieve' ) { + $self->event(_mk_not_found($type, $arg)); + + } elsif( $action eq 'update' or + $action eq 'delete' or $action eq 'create' ) { + my $evt = OpenILS::Event->new( + 'DATABASE_UPDATE_FAILED', payload => $arg, debug => "$err" ); + $self->event($evt); + } + + if( $err ) { + $self->event( + OpenILS::Event->new( 'DATABASE_QUERY_FAILED', + payload => $arg, debug => "$err" )); + return undef; + } + + return undef; + } + + if( $action eq 'create' and $obj == 0 ) { + my $evt = OpenILS::Event->new( + 'DATABASE_UPDATE_FAILED', payload => $arg, debug => "$err" ); + $self->event($evt); + return undef; + } + + # If we havn't dealt with the error in a nice way, go ahead and throw it + if( $err ) { + $self->event( + OpenILS::Event->new( 'DATABASE_QUERY_FAILED', + payload => $arg, debug => "$err" )); + return undef; + } + + if( $action eq 'search' or $action eq 'batch_retrieve' or $action eq 'retrieve_all') { + $self->log(I, "$type.$action : returned ".scalar(@$obj). " result(s)"); + $self->event(_mk_not_found($type, $arg)) unless @$obj; + } + + $arg->id($obj) if $action eq 'create'; # grabs the id on create + $self->data($obj); # cache the data for convenience + + return ($obj) ? $obj : 1; +} + + +sub _mk_not_found { + my( $type, $arg ) = @_; + (my $t = $type) =~ s/\./_/og; + $t = uc($t); + return OpenILS::Event->new("${t}_NOT_FOUND", payload => $arg); +} + + + +# utility method for loading +sub __fm2meth { + my $str = shift; + my $sep = shift; + $str =~ s/Fieldmapper:://o; + $str =~ s/::/$sep/g; + return $str; +} + + +# ------------------------------------------------------------- +# Load up the methods from the FM classes +# ------------------------------------------------------------- +my $map = $Fieldmapper::fieldmap; +for my $object (keys %$map) { + my $obj = __fm2meth($object,'_'); + my $type = __fm2meth($object, '.'); + + my $update = "update_$obj"; + my $updatef = + "sub $update {return shift()->runmethod('update', '$type', \@_);}"; + eval $updatef; + + my $retrieve = "retrieve_$obj"; + my $retrievef = + "sub $retrieve {return shift()->runmethod('retrieve', '$type', \@_);}"; + eval $retrievef; + + my $search = "search_$obj"; + my $searchf = + "sub $search {return shift()->runmethod('search', '$type', \@_);}"; + eval $searchf; + + my $create = "create_$obj"; + my $createf = + "sub $create {return shift()->runmethod('create', '$type', \@_);}"; + eval $createf; + + my $delete = "delete_$obj"; + my $deletef = + "sub $delete {return shift()->runmethod('delete', '$type', \@_);}"; + eval $deletef; + + my $bretrieve = "batch_retrieve_$obj"; + my $bretrievef = + "sub $bretrieve {return shift()->runmethod('batch_retrieve', '$type', \@_);}"; + eval $bretrievef; + + my $retrieveall = "retrieve_all_$obj"; + my $retrieveallf = + "sub $retrieveall {return shift()->runmethod('retrieve_all', '$type', \@_);}"; + eval $retrieveallf; + + +} + + + +1; + + -- 2.11.0