]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Storage/Publisher.pm
circ rules fixups
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Storage / Publisher.pm
1 package OpenILS::Application::Storage::Publisher;
2 use base qw/OpenILS::Application::Storage/;
3 our $VERSION = 1;
4
5 use Digest::MD5 qw/md5_hex/;
6 use OpenSRF::EX qw/:try/;;
7 use OpenSRF::Utils::Logger qw/:level/;
8 use OpenILS::Utils::Fieldmapper;
9
10 my $log = 'OpenSRF::Utils::Logger';
11
12
13 sub register_method {
14         my $class = shift;
15         my %args = @_;
16         my %dup_args = %args;
17
18         $class = ref($class) || $class;
19
20         $args{package} ||= $class;
21         __PACKAGE__->SUPER::register_method( %args );
22
23         if (exists($dup_args{cachable}) and $dup_args{cachable}) {
24                 (my $name = $dup_args{api_name}) =~ s/^open-ils\.storage/open-ils.storage.cachable/o;
25                 if ($name ne $dup_args{api_name}) {
26                         $dup_args{real_api_name} = $dup_args{api_name};
27                         $dup_args{method} = 'cachable_wrapper';
28                         $dup_args{api_name} = $name;
29                         $dup_args{package} = __PACKAGE__;
30                         __PACKAGE__->SUPER::register_method( %dup_args );
31                 }
32         }
33 }
34
35 sub cachable_wrapper {
36         my $self = shift;
37         my $client = shift;
38         my @args = @_;
39
40         my %cache_args = (
41                 limit   => 100,
42                 offset  => 0,
43                 timeout => 300,
44         );
45
46         my @real_args;
47         my $key_string = $self->api_name;
48         for (my $ind = 0; $ind < scalar(@args); $ind++) {
49                 if (    "$args[$ind]" eq 'limit' ||
50                         "$args[$ind]" eq 'offset' ||
51                         "$args[$ind]" eq 'timeout' ) {
52
53                         my $key_ind = $ind;
54                         $ind++;
55                         my $value_ind = $ind;
56                         $cache_args{$args[$key_ind]} = $args[$value_ind];
57                         $log->debug("Cache limiter value for $args[$key_ind] is $args[$value_ind]", INTERNAL);
58                         next;
59                 }
60                 $key_string .= $args[$ind];
61                 $log->debug("Partial cache key value is $args[$ind]", INTERNAL);
62                 push @real_args, $args[$ind];
63         }
64
65         my $cache_key = md5_hex($key_string);
66         $log->debug("Key string for cache lookup is $key_string -> $cache_key", DEBUG);
67
68         my $cached_res = OpenSRF::Utils::Cache->new->get_cache( $cache_key );
69         if (defined $cached_res) {
70                 $log->debug("Found ".scalar(@$cached_res)." records in the cache", INFO);
71                 $log->debug("Values from cache: ".join(', ', @$cached_res), INTERNAL);
72                 $client->respond( $_ ) for ( grep { defined } @$cached_res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
73                 return undef;
74         }
75
76         my $method = $self->method_lookup($self->{real_api_name});
77         my @res = $method->run(@real_args);
78
79
80         $client->respond( $_ ) for ( grep { defined } @res[$cache_args{offset} .. int($cache_args{offset} + $cache_args{limit} - 1)] );
81
82         OpenSRF::Utils::Cache->new->put_cache( $cache_key => \@res => $cache_args{timeout});
83
84         return undef;
85 }
86
87 sub retrieve_node {
88         my $self = shift;
89         my $client = shift;
90         my @ids = @_;
91
92         my $cdbi = $self->{cdbi};
93
94         for my $id ( @ids ) {
95                 next unless ($id);
96
97                 my ($rec) = $cdbi->fast_fieldmapper($id);
98                 if ($self->api_name !~ /batch/o) {
99                         return $rec if ($rec);
100                 }
101                 $client->respond($rec);
102         }
103         return undef;
104 }
105
106 sub search {
107         my $self = shift;
108         my $client = shift;
109         my $searches = shift;
110
111         my $cdbi = $self->{cdbi};
112
113         $log->debug("Searching $cdbi for { ".join(',', map { "$_ => $$searches{$_}" } keys %$searches).' }',DEBUG);
114
115         for my $obj ($cdbi->search($searches)) {
116                 $client->respond( $obj->to_fieldmapper );
117         }
118         return undef;
119 }
120
121 sub search_one_field {
122         my $self = shift;
123         my $client = shift;
124         my @terms = @_;
125
126         (my $search_type = $self->api_name) =~ s/.*\.(search[^.]*).*/$1/o;
127         (my $col = $self->api_name) =~ s/.*\.$search_type\.([^.]+).*/$1/;
128         my $cdbi = $self->{cdbi};
129
130         my $like = 0;
131         $like = 1 if ($search_type =~ /like$/o);
132
133         for my $term (@terms) {
134                 $log->debug("Searching $cdbi for $col using type $search_type, value '$term'",DEBUG);
135                 if (@terms == 1) {
136                         return [ $cdbi->fast_fieldmapper($term,$col,$like) ];
137                 }
138                 $client->respond( [ $cdbi->fast_fieldmapper($term,$col,$like) ] );
139         }
140         return undef;
141 }
142
143
144 sub create_node {
145         my $self = shift;
146         my $client = shift;
147         my $node = shift;
148
149         my $cdbi = $self->{cdbi};
150
151         my $success;
152         try {
153                 my $rec = $cdbi->create($node);
154                 $success = $rec->id if ($rec);
155         } catch Error with {
156                 $success = 0;
157         };
158
159         return $success;
160 }
161
162 sub update_node {
163         my $self = shift;
164         my $client = shift;
165         my $node = shift;
166
167         my $cdbi = $self->{cdbi};
168
169         return $cdbi->update($node);
170 }
171
172 sub mass_delete {
173         my $self = shift;
174         my $client = shift;
175         my $search = shift;
176
177         my $where = 'WHERE ';
178
179         my $cdbi = $self->{cdbi};
180         my $table = $cdbi->table;
181
182         my @keys = sort keys %$search;
183         
184         my @binds;
185         my @wheres;
186         for my $col ( @keys ) {
187                 if (ref($$search{$col}) and ref($$search{$col}) =~ /ARRAY/o) {
188                         push @wheres, "$col IN (" . join(',', map { '?' } @{ $$search{$col} }) . ')';
189                         push @binds, map { "$_" } @{ $$search{$col} };
190                 } else {
191                         push @wheres, "$col = ?";
192                         push @binds, $$search{$col};
193                 }
194         }
195         $where .= join ' AND ', @wheres;
196
197         my $delete = "DELETE FROM $table $where";
198
199         $log->debug("Performing MASS deletion : $delete",DEBUG);
200
201         my $dbh = $cdbi->db_Main;
202         my $success = 1;
203         try {
204                 my $sth = $dbh->prepare($delete);
205                 $sth->execute( @binds );
206                 $sth->finish;
207                 $log->debug("MASS Delete succeeded",DEBUG);
208         } catch Error with {
209                 $log->debug("MASS Delete FAILED : ".shift(),DEBUG);
210                 $success = 0;
211         };
212         return $success;
213 }
214
215 sub delete_node {
216         my $self = shift;
217         my $client = shift;
218         my $node = shift;
219
220         my $cdbi = $self->{cdbi};
221
222         my $success = 1;
223         try {
224                 $success = $cdbi->delete($node);
225         } catch Error with {
226                 $success = 0;
227         };
228         return $success;
229 }
230
231 sub batch_call {
232         my $self = shift;
233         my $client = shift;
234         my @nodes = @_;
235
236         my $cdbi = $self->{cdbi};
237         my $api_name = $self->api_name;
238         (my $single_call_api_name = $api_name) =~ s/batch\.//o;
239
240         $log->debug("Default $api_name looking up $single_call_api_name...",INTERNAL);
241         my $method = $self->method_lookup($single_call_api_name);
242
243         my @success;
244         while ( my $node = shift(@nodes) ) {
245                 my ($res) = $method->run( $node ); 
246                 push(@success, 1) if ($res >= 0);
247         }
248
249         my $insert_total = 0;
250         $insert_total += $_ for (@success);
251
252         return $insert_total;
253 }
254
255 eval '
256 use OpenILS::Application::Storage::Publisher::actor;
257 use OpenILS::Application::Storage::Publisher::action;
258 use OpenILS::Application::Storage::Publisher::asset;
259 use OpenILS::Application::Storage::Publisher::biblio;
260 use OpenILS::Application::Storage::Publisher::config;
261 use OpenILS::Application::Storage::Publisher::metabib;
262 ';
263
264 for my $fmclass ( (Fieldmapper->classes) ) {
265         $log->debug("Generating methods for Fieldmapper class $fmclass", DEBUG);
266
267         (my $cdbi = $fmclass) =~ s/^Fieldmapper:://o;
268         (my $class = $cdbi) =~ s/::.*//o;
269         (my $api_class = $cdbi) =~ s/::/./go;
270         my $registration_class = __PACKAGE__ . "::$class";
271         my $api_prefix = 'open-ils.storage.direct.'.$api_class;
272
273         # Create the search method
274         unless ( __PACKAGE__->is_registered( $api_prefix.'.search' ) ) {
275                 __PACKAGE__->register_method(
276                         api_name        => $api_prefix.'.search',
277                         method          => 'search',
278                         api_level       => 1,
279                         stream          => 1,
280                         cdbi            => $cdbi,
281                         cachable        => 1,
282                 );
283         }
284
285         # Create the retrieve method
286         unless ( __PACKAGE__->is_registered( $api_prefix.'.retrieve' ) ) {
287                 __PACKAGE__->register_method(
288                         api_name        => $api_prefix.'.retrieve',
289                         method          => 'retrieve_node',
290                         api_level       => 1,
291                         cdbi            => $cdbi,
292                         cachable        => 1,
293                 );
294         }
295
296         # Create the batch retrieve method
297         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.retrieve' ) ) {
298                 __PACKAGE__->register_method(
299                         api_name        => $api_prefix.'.batch.retrieve',
300                         method          => 'retrieve_node',
301                         api_level       => 1,
302                         stream          => 1,
303                         cdbi            => $cdbi,
304                         cachable        => 1,
305                 );
306         }
307
308         unless ($fmclass->is_virtual) {
309                 for my $field ($fmclass->real_fields) {
310                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search.'.$field ) ) {
311                                 __PACKAGE__->register_method(
312                                         api_name        => $api_prefix.'.search.'.$field,
313                                         method          => 'search_one_field',
314                                         api_level       => 1,
315                                         cdbi            => $cdbi,
316                                         cachable        => 1,
317                                 );
318                         }
319                         unless ( __PACKAGE__->is_registered( $api_prefix.'.search_like.'.$field ) ) {
320                                 __PACKAGE__->register_method(
321                                         api_name        => $api_prefix.'.search_like.'.$field,
322                                         method          => 'search_one_field',
323                                         api_level       => 1,
324                                         cdbi            => $cdbi,
325                                         cachable        => 1,
326                                 );
327                         }
328                 }
329         }
330
331
332         # Create the create method
333         unless ( __PACKAGE__->is_registered( $api_prefix.'.create' ) ) {
334                 __PACKAGE__->register_method(
335                         api_name        => $api_prefix.'.create',
336                         method          => 'create_node',
337                         api_level       => 1,
338                         cdbi            => $cdbi,
339                 );
340         }
341
342         # Create the batch create method
343         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.create' ) ) {
344                 __PACKAGE__->register_method(
345                         api_name        => $api_prefix.'.batch.create',
346                         method          => 'batch_call',
347                         api_level       => 1,
348                         cdbi            => $cdbi,
349                 );
350         }
351
352         # Create the update method
353         unless ( __PACKAGE__->is_registered( $api_prefix.'.update' ) ) {
354                 __PACKAGE__->register_method(
355                         api_name        => $api_prefix.'.update',
356                         method          => 'update_node',
357                         api_level       => 1,
358                         cdbi            => $cdbi,
359                 );
360         }
361
362         # Create the batch update method
363         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.update' ) ) {
364                 __PACKAGE__->register_method(
365                         api_name        => $api_prefix.'.batch.update',
366                         method          => 'batch_call',
367                         api_level       => 1,
368                         cdbi            => $cdbi,
369                 );
370         }
371
372         # Create the delete method
373         unless ( __PACKAGE__->is_registered( $api_prefix.'.delete' ) ) {
374                 __PACKAGE__->register_method(
375                         api_name        => $api_prefix.'.delete',
376                         method          => 'delete_node',
377                         api_level       => 1,
378                         cdbi            => $cdbi,
379                 );
380         }
381
382         # Create the batch delete method
383         unless ( __PACKAGE__->is_registered( $api_prefix.'.batch.delete' ) ) {
384                 __PACKAGE__->register_method(
385                         api_name        => $api_prefix.'.batch.delete',
386                         method          => 'batch_call',
387                         api_level       => 1,
388                         cdbi            => $cdbi,
389                 );
390         }
391
392         # Create the search-based mass delete method
393         unless ( __PACKAGE__->is_registered( $api_prefix.'.mass_delete' ) ) {
394                 __PACKAGE__->register_method(
395                         api_name        => $api_prefix.'.mass_delete',
396                         method          => 'mass_delete',
397                         api_level       => 1,
398                         cdbi            => $cdbi,
399                 );
400         }
401
402 }
403
404 1;