]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Trigger/Event.pm
leave existing intermediate objects alone in the path if they are already fleshed
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Trigger / Event.pm
1 package OpenILS::Application::Trigger::Event;
2 use OpenSRF::EX qw/:try/;
3
4 use OpenSRF::Utils::Logger qw/:logger/;
5
6 use OpenILS::Utils::Fieldmapper;
7 use OpenILS::Utils::CStoreEditor q/:funcs/;
8 use OpenILS::Application::Trigger::ModRunner;
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12 sub new {
13     my $class = shift;
14     my $id = shift;
15     my $editor = shift;
16     $class = ref($class) || $class;
17
18     return $id if (ref($id) && ref($id) == $class);
19
20     my $standalone = $editor ? 0 : 1;
21     $editor ||= new_editor();
22
23     my $self = bless { id => $id, editor => $editor, standalone => $standalone } => $class;
24
25     return $self->init()
26 }
27
28 sub init {
29     my $self = shift;
30     my $id = shift;
31
32     return $self if ($self->event);
33
34     $self->id( $id ); 
35     $self->environment( {} ); 
36
37     if (!$self->id) {
38         $log->error("No Event ID provided");
39         die "No Event ID provided";
40     }
41
42     return $self if (!$self->id);
43
44     $self->event(
45         $self->editor->retrieve_action_trigger_event([
46             $self->id, {
47                 flesh => 2,
48                 flesh_fields => {
49                     atev    => [ qw/event_def/ ],
50                     atevdef => [ qw/hook env params/ ]
51                 }
52             }
53         ])
54     );
55
56     if ($self->event->state eq 'valid') {
57         $self->valid(1);
58     } elsif ($self->event->state eq 'invalid') {
59         $self->valid(0);
60     } elsif ($self->event->state eq 'reacting') {
61         $self->valid(1);
62     } elsif ($self->event->state eq 'reacted') {
63         $self->valid(1);
64         $self->reacted(1);
65     } elsif ($self->event->state eq 'cleaning') {
66         $self->valid(1);
67         $self->reacted(1);
68     } elsif ($self->event->state eq 'complete') {
69         $self->valid(1);
70         $self->reacted(1);
71         $self->cleanedup(1);
72     } elsif ($self->event->state eq 'error') {
73         $self->valid(0);
74         $self->reacted(0);
75         $self->cleanedup(0);
76     }
77
78
79     $self->update_state('found') || die 'Unable to update event state';
80
81     my $class = $self->_fm_class_by_hint( $self->event->event_def->hook->core_type );
82     
83     my $meth = "retrieve_" . $class;
84     $meth =~ s/Fieldmapper:://;
85     $meth =~ s/::/_/;
86     
87     $self->target( $self->editor->$meth( $self->event->target ) );
88
89     return $self;
90 }
91
92 sub cleanup {
93     my $self = shift;
94     my $env = shift || $self->environment;
95
96     return $self if (defined $self->cleanedup);
97
98     if (defined $self->reacted) {
99         $self->update_state( 'cleaning') || die 'Unable to update event state';
100         try {
101             my $cleanup = $self->reacted ? $self->event->event_def->cleanup_success : $self->event->event_def->cleanup_failure;
102             if($cleanup) {
103                 $self->cleanedup(
104                     OpenILS::Application::Trigger::ModRunner::Cleanup
105                         ->new( $cleanup, $env)
106                         ->run
107                         ->final_result
108                 );
109             } else {
110                 $self->cleanedup(1);
111             }
112         } otherwise {
113             $log->error( shift() );
114             $self->update_state( 'error' ) || die 'Unable to update event state';
115         };
116
117         if ($self->cleanedup) {
118             $self->update_state( 'complete' ) || die 'Unable to update event state';
119         } else {
120             $self->update_state( 'error' ) || die 'Unable to update event state';
121         }
122
123     } else {
124         $self->{cleanedup} = undef;
125     }
126     return $self;
127 }
128
129 sub react {
130     my $self = shift;
131     my $env = shift || $self->environment;
132
133     return $self if (defined $self->reacted);
134
135     if ($self->valid) {
136         if ($self->event->event_def->group_field) { # can't react individually to a grouped definition
137             $self->{reacted} = undef;
138         } else {
139             $self->update_state( 'reacting') || die 'Unable to update event state';
140             try {
141                 $self->reacted(
142                     OpenILS::Application::Trigger::ModRunner::Reactor
143                         ->new( $self->event->event_def->reactor, $env )
144                         ->run
145                         ->final_result
146                 );
147             } otherwise {
148                 $log->error( shift() );
149                 $self->update_state( 'error' ) || die 'Unable to update event state';
150             };
151
152             if (defined $self->reacted) {
153                 $self->update_state( 'reacted' ) || die 'Unable to update event state';
154             } else {
155                 $self->update_state( 'error' ) || die 'Unable to update event state';
156             }
157         }
158     } else {
159         $self->{reacted} = undef;
160     }
161     return $self;
162 }
163
164 sub validate {
165     my $self = shift;
166
167     return $self if (defined $self->valid);
168
169     if ($self->build_environment->environment->{complete}) {
170         $self->update_state( 'validating') || die 'Unable to update event state';
171         try {
172             $self->valid(
173                 OpenILS::Application::Trigger::ModRunner::Validator
174                     ->new( $self->event->event_def->validator, $self->environment )
175                     ->run
176                     ->final_result
177             );
178         } otherwise {
179             $log->error( shift() );
180             $self->update_state( 'error' ) || die 'Unable to update event state';
181         };
182
183         if (defined $self->valid) {
184             if ($self->valid) {
185                 $self->update_state( 'valid' ) || die 'Unable to update event state';
186             } else {
187                 $self->update_state( 'invalid' ) || die 'Unable to update event state';
188             }
189         } else {
190             $self->update_state( 'error' ) || die 'Unable to update event state';
191         }
192     } else {
193         $self->{valid} = undef
194     }
195
196     return $self;
197 }
198  
199 sub cleanedup {
200     my $self = shift;
201     return undef unless (ref $self);
202
203     my $c = shift;
204     $self->{cleanedup} = $c if (defined $c);
205     return $self->{cleanedup};
206 }
207
208 sub reacted {
209     my $self = shift;
210     return undef unless (ref $self);
211
212     my $r = shift;
213     $self->{reacted} = $r if (defined $r);
214     return $self->{reacted};
215 }
216
217 sub valid {
218     my $self = shift;
219     return undef unless (ref $self);
220
221     my $v = shift;
222     $self->{valid} = $v if (defined $v);
223     return $self->{valid};
224 }
225
226 sub event {
227     my $self = shift;
228     return undef unless (ref $self);
229
230     my $e = shift;
231     $self->{event} = $e if (defined $e);
232     return $self->{event};
233 }
234
235 sub id {
236     my $self = shift;
237     return undef unless (ref $self);
238
239     my $i = shift;
240     $self->{id} = $i if (defined $i);
241     return $self->{id};
242 }
243
244 sub environment {
245     my $self = shift;
246     return undef unless (ref $self);
247
248     my $e = shift;
249     $self->{environment} = $e if (defined $e);
250     return $self->{environment};
251 }
252
253 sub editor {
254     my $self = shift;
255     return undef unless (ref $self);
256
257     my $e = shift;
258     $self->{editor} = $e if (defined $e);
259     return $self->{editor};
260 }
261
262 sub unfind {
263     my $self = shift;
264     return undef unless (ref $self);
265
266     die 'Cannot unfind a reacted event' if (defined $self->reacted);
267
268     $self->update_state( 'pending' ) || die 'Unable to update event state';
269     $self->{id} = undef;
270     $self->{event} = undef;
271     $self->{environment} = undef;
272     return $self;
273 }
274
275 sub target {
276     my $self = shift;
277     return undef unless (ref $self);
278
279     my $t = shift;
280     $self->{target} = $t if (defined $t);
281     return $self->{target};
282 }
283
284 sub standalone {
285     my $self = shift;
286     return undef unless (ref $self);
287
288     my $t = shift;
289     $self->{standalone} = $t if (defined $t);
290     return $self->{standalone};
291 }
292
293 sub update_state {
294     my $self = shift;
295     return undef unless ($self && ref $self);
296
297     my $state = shift;
298     return undef unless ($state);
299
300     my $fields = shift;
301
302     if ($self->standalone) {
303         $self->editor->xact_begin || return undef;
304     }
305
306     my $e = $self->editor->retrieve_action_trigger_event( $self->id );
307     if (!$e) {
308         $log->error( "Could not retrieve object ".$self->id." for update" ) if (!$e);
309         return undef;
310     }
311
312     if ($fields && ref($fields)) {
313         $e->$_($$fields{$_}) for (keys %$fields);
314     }
315
316     $log->info( "Retrieved object ".$self->id." for update" );
317     $e->start_time( 'now' ) unless $e->start_time;
318     $e->update_time( 'now' );
319     $e->update_process( $$ );
320     $e->state( $state );
321
322     $e->clear_start_time() if ($e->state eq 'pending');
323
324     my $ok = $self->editor->update_action_trigger_event( $e );
325     if (!$ok) {
326         $self->editor->xact_rollback if ($self->standalone);
327         $log->error( "Update of event ".$self->id." failed" );
328         return undef;
329     } else {
330         $e = $self->editor->data;
331         $e = $self->editor->retrieve_action_trigger_event( $e ) if (!ref($e));
332         if (!$e) {
333             $log->error( "Update of event ".$self->id." did not return an object" );
334             return undef;
335         }
336         $log->info( "Update of event ".$e->id." suceeded" );
337         $ok = $self->editor->xact_commit if ($self->standalone);
338     }
339
340     if ($ok) {
341         $self->event->start_time( $e->start_time );
342         $self->event->update_time( $e->update_time );
343         $self->event->update_process( $e->update_process );
344         $self->event->state( $e->state );
345     }
346
347     return $ok || undef;
348 }
349
350 sub build_environment {
351     my $self = shift;
352     return $self if ($self->environment->{complete});
353
354     $self->update_state( 'collecting') || die 'Unable to update event state';
355
356     try {
357    
358         $self->environment->{target} = $self->target;
359         $self->environment->{event} = $self->event;
360         $self->environment->{template} = $self->event->event_def->template;
361
362         $self->environment->{params}{ $_->param } = eval $_->value for ( @{$self->event->event_def->params} );
363     
364         for my $e ( @{$self->event->event_def->env} ) {
365             my (@label, @path);
366             @path = split(/\./, $e->path) if ($e->path);
367             @label = split(/\./, $e->label) if ($e->label);
368     
369             $self->_object_by_path( $self->target, $e->collector, \@label, \@path );
370         }
371
372         if ($self->event->event_def->group_field) {
373             my @group_path = split(/\./, $self->event->event_def->group_field);
374             my $group_object = $self->_object_by_path( $self->target, undef, [], \@group_path );
375         }
376     
377         $self->environment->{complete} = 1;
378     } otherwise {
379         $log->error( shift() );
380         $self->update_state( 'error' ) || die 'Unable to update event state';
381     };
382
383     if ($self->environment->{complete}) {
384         $self->update_state( 'collected' ) || die 'Unable to update event state';
385     } else {
386         $self->update_state( 'error' ) || die 'Unable to update event state';
387     }
388
389     return $self;
390 }
391
392 sub _fm_class_by_hint {
393     my $self = shift;
394     my $hint = shift;
395
396     my ($class) = grep {
397         Fieldmapper->publish_fieldmapper->{$_}->{hint} eq $hint
398     } keys %{ Fieldmapper->publish_fieldmapper };
399
400     return $class;
401 }
402
403 sub _object_by_path {
404     my $self = shift;
405     my $context = shift;
406     my $collector = shift;
407     my $label = shift;
408     my $path = shift;
409
410     my $step = shift(@$path);
411     
412     my $fhint = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{class};
413     my $fclass = $self->_fm_class_by_hint( $fhint );
414
415     my $ffield = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{key};
416     my $rtype = Fieldmapper->publish_fieldmapper->{$context->class_name}{links}{$step}{reltype};
417
418     my $meth = 'retrieve_';
419     my $multi = 0;
420     my $lfield = $step;
421     if ($rtype ne 'has_a') {
422         $meth = 'search_';
423         $multi = 1;
424         $lfield = $context->Identity;
425     }
426
427     $meth .= $fclass;
428     $meth =~ s/Fieldmapper:://;
429     $meth =~ s/::/_/;
430
431     my $ed = grep( /open-ils.cstore/, @{$fclass->Controller} ) ?
432             $self->editor :
433             new_rstore_editor();
434
435     my $obj = $context->$step(); 
436
437     if (!ref $obj) {
438         $obj = $ed->$meth( 
439             ($multi) ?
440                 { $ffield => $context->$lfield() } :
441                 $context->$lfield()
442         );
443     }
444
445     if (@$path) {
446
447         my $obj_list = [];
448         if (!$multi || $rtype eq 'might_have') {
449             $obj_list = [$obj] if ($obj);
450         } else {
451             $obj_list = $obj;
452         }
453
454         $self->_object_by_path( $_, $collector, $label, $path ) for (@$obj_list);
455
456         $obj = $$obj_list[0] if (!$multi || $rtype eq 'might_have');
457         $context->$step( $obj ) if ($obj && (!$label || !@$label));
458
459     } else {
460
461         if ($collector) {
462             my $obj_list = [$obj] if ($obj && !$multi);
463             $obj_list = $obj if ($multi);
464
465             my @new_obj_list;
466             for my $o ( @$obj_list ) {
467                 push @new_obj_list,
468                     OpenILS::Application::Trigger::ModRunner::Collector
469                         ->new( $collector, $o )
470                         ->run
471                         ->final_result
472             }
473
474             if (!$multi) {
475                 $obj = $new_obj_list[0];
476             } else {
477                 $obj = \@new_obj_list;
478             }
479         }
480
481         if ($label && @$label) {
482             my $node = $self->environment;
483             my $i = 0; my $max = scalar(@$label);
484             for (; $i < $max; $i++) {
485                 my $part = $$label[$i];
486                 $$node{$part} ||= {};
487                 $node = $$node{$part};
488             }
489             $$node{$$label[-1]} = $obj;
490         } else {
491             $context->$step( $obj ) if ($obj);
492         }
493     }
494
495     return $obj;
496 }
497
498 1;