View | Details | Raw Unified | Return to bug 32370
Collapse All | Expand All

(-)a/Koha/BackgroundJob.pm (-51 / +14 lines)
Lines 17-23 package Koha::BackgroundJob; Link Here
17
17
18
use Modern::Perl;
18
use Modern::Perl;
19
use Encode qw();
19
use Encode qw();
20
use JSON;
21
use Carp qw( croak );
20
use Carp qw( croak );
22
use Net::Stomp;
21
use Net::Stomp;
23
use Try::Tiny qw( catch try );
22
use Try::Tiny qw( catch try );
Lines 28-34 use Koha::Exceptions; Link Here
28
use Koha::Plugins;
27
use Koha::Plugins;
29
use Koha::Exceptions::BackgroundJob;
28
use Koha::Exceptions::BackgroundJob;
30
29
31
use base qw( Koha::Object );
30
use base qw( Koha::Object Koha::Object::JSONFields );
32
31
33
=head1 NAME
32
=head1 NAME
34
33
Lines 101-121 sub enqueue { Link Here
101
    my $job_args    = $params->{job_args};
100
    my $job_args    = $params->{job_args};
102
    my $job_context = $params->{job_context} // C4::Context->userenv;
101
    my $job_context = $params->{job_context} // C4::Context->userenv;
103
    my $job_queue   = $params->{job_queue}  // 'default';
102
    my $job_queue   = $params->{job_queue}  // 'default';
104
    my $json = $self->json;
105
103
106
    my $borrowernumber = (C4::Context->userenv) ? C4::Context->userenv->{number} : undef;
104
    my $borrowernumber = (C4::Context->userenv) ? C4::Context->userenv->{number} : undef;
107
    $job_context->{interface} = C4::Context->interface;
105
    $job_context->{interface} = C4::Context->interface;
108
    my $json_context = $json->encode($job_context);
106
109
    my $json_args = $json->encode($job_args);
107
    $self->set_encoded_json_field( { data => $job_context, field => 'context' } );
108
    $self->set_encoded_json_field( { data => $job_args,    field => 'data' } );
110
109
111
    $self->set(
110
    $self->set(
112
        {
111
        {   status         => 'new',
113
            status         => 'new',
114
            type           => $job_type,
112
            type           => $job_type,
115
            queue          => $job_queue,
113
            queue          => $job_queue,
116
            size           => $job_size,
114
            size           => $job_size,
117
            data           => $json_args,
118
            context        => $json_context,
119
            enqueued_on    => dt_from_string,
115
            enqueued_on    => dt_from_string,
120
            borrowernumber => $borrowernumber,
116
            borrowernumber => $borrowernumber,
121
        }
117
        }
Lines 131-137 sub enqueue { Link Here
131
    };
127
    };
132
    return unless $conn;
128
    return unless $conn;
133
129
134
    $json_args = $json->encode($job_args);
130
    my $json_args = $self->data; # we pass the JSON string
135
    try {
131
    try {
136
        # This namespace is wrong, it must be a vhost instead.
132
        # This namespace is wrong, it must be a vhost instead.
137
        # But to do so it needs to be created on the server => much more work when a new Koha instance is created.
133
        # But to do so it needs to be created on the server => much more work when a new Koha instance is created.
Lines 169-175 sub process { Link Here
169
    $args ||= {};
165
    $args ||= {};
170
166
171
    if ( $self->context ) {
167
    if ( $self->context ) {
172
        my $context = $self->json->decode($self->context);
168
        my $context = $self->decode_json_field({ field => 'context' });
173
        C4::Context->_new_userenv(-1);
169
        C4::Context->_new_userenv(-1);
174
        C4::Context->interface( $context->{interface} );
170
        C4::Context->interface( $context->{interface} );
175
        C4::Context->set_userenv(
171
        C4::Context->set_userenv(
Lines 249-276 sub finish { Link Here
249
    my ( $self, $data ) = @_;
245
    my ( $self, $data ) = @_;
250
246
251
    $self->status('finished') unless $self->status eq 'cancelled' or $self->status eq 'failed';
247
    $self->status('finished') unless $self->status eq 'cancelled' or $self->status eq 'failed';
248
    $self->set_encoded_json_field({ data => $data, field => 'data' });
252
249
253
    return $self->set(
250
    return $self->set({ ended_on => \'NOW()' })->store;
254
        {
255
            ended_on => \'NOW()',
256
            data     => $self->json->encode($data),
257
        }
258
    )->store;
259
}
260
261
=head3 json
262
263
   my $JSON_object = $self->json;
264
265
Returns a JSON object with utf8 disabled. Encoding to UTF-8 should be
266
done later.
267
268
=cut
269
270
sub json {
271
    my ( $self ) = @_;
272
    $self->{_json} //= JSON->new->utf8(0); # TODO Should we allow_nonref ?
273
    return $self->{_json};
274
}
251
}
275
252
276
=head3 decoded_data
253
=head3 decoded_data
Lines 284-304 Returns the decoded JSON contents from $self->data. Link Here
284
sub decoded_data {
261
sub decoded_data {
285
    my ($self) = @_;
262
    my ($self) = @_;
286
263
287
    return $self->data ? $self->json->decode( $self->data ) : undef;
264
    return $self->decode_json_field({ field => 'data' });
288
}
289
290
=head3 set_encoded_data
291
292
    $self->set_encoded_data( $data );
293
294
Serializes I<$data> as a JSON string and sets the I<data> attribute with it.
295
296
=cut
297
298
sub set_encoded_data {
299
    my ( $self, $data ) = @_;
300
301
    return $self->data( $data ? $self->json->encode($data) : undef );
302
}
265
}
303
266
304
=head3 job_type
267
=head3 job_type
Lines 319-325 sub messages { Link Here
319
    my ( $self ) = @_;
282
    my ( $self ) = @_;
320
283
321
    my @messages;
284
    my @messages;
322
    my $data_dump = $self->json->decode($self->data);
285
    my $data_dump = $self->decode_json_field({ field => 'data' });
323
    if ( exists $data_dump->{messages} ) {
286
    if ( exists $data_dump->{messages} ) {
324
        @messages = @{ $data_dump->{messages} };
287
        @messages = @{ $data_dump->{messages} };
325
    }
288
    }
Lines 336-342 Report of the job. Link Here
336
sub report {
299
sub report {
337
    my ( $self ) = @_;
300
    my ( $self ) = @_;
338
301
339
    my $data_dump = $self->json->decode($self->data);
302
    my $data_dump = $self->decode_json_field({ field => 'data' });
340
    return $data_dump->{report} || {};
303
    return $data_dump->{report} || {};
341
}
304
}
342
305
Lines 489-497 sub to_api { Link Here
489
452
490
    my $json = $self->SUPER::to_api( $params );
453
    my $json = $self->SUPER::to_api( $params );
491
454
492
    $json->{context} = $self->json->decode($self->context)
455
    $json->{context} = $self->decode_json_field({ field => 'context' })
493
      if defined $self->context;
456
      if defined $self->context;
494
    $json->{data} = $self->decoded_data;
457
    $json->{data} = $self->decode_json_field({ field => 'data' });
495
458
496
    return $json;
459
    return $json;
497
}
460
}
(-)a/misc/background_jobs_worker.pl (-1 / +1 lines)
Lines 102-108 while (1) { Link Here
102
    } else {
102
    } else {
103
        my $jobs = Koha::BackgroundJobs->search({ status => 'new', queue => \@queues });
103
        my $jobs = Koha::BackgroundJobs->search({ status => 'new', queue => \@queues });
104
        while ( my $job = $jobs->next ) {
104
        while ( my $job = $jobs->next ) {
105
            my $args = $job->json->decode($job->data);
105
            my $args = $job->decoded_data;
106
            process_job( $job, { job_id => $job->id, %$args } );
106
            process_job( $job, { job_id => $job->id, %$args } );
107
        }
107
        }
108
        sleep 10;
108
        sleep 10;
(-)a/t/db_dependent/Koha/BackgroundJob.t (-10 / +10 lines)
Lines 108-114 subtest 'enqueue() tests' => sub { Link Here
108
    is( $job->size,           3,           'Three steps' );
108
    is( $job->size,           3,           'Three steps' );
109
    is( $job->status,         'new',       'Initial status set correctly' );
109
    is( $job->status,         'new',       'Initial status set correctly' );
110
    is( $job->borrowernumber, $patron->id, 'Borrowernumber set from userenv' );
110
    is( $job->borrowernumber, $patron->id, 'Borrowernumber set from userenv' );
111
    is_deeply( $job->json->decode( $job->context ), $job_context, 'Context set from userenv + interface' );
111
    is_deeply( $job->decode_json_field({ field => 'context' }), $job_context, 'Context set from userenv + interface' );
112
112
113
    $schema->storage->txn_rollback;
113
    $schema->storage->txn_rollback;
114
};
114
};
Lines 150-163 subtest 'start(), step() and finish() tests' => sub { Link Here
150
150
151
    is( $job->status, 'cancelled', "'finish' leaves 'cancelled' untouched" );
151
    is( $job->status, 'cancelled', "'finish' leaves 'cancelled' untouched" );
152
    isnt( $job->ended_on, undef, 'ended_on set' );
152
    isnt( $job->ended_on, undef, 'ended_on set' );
153
    is_deeply( $job->json->decode( $job->data ), $data );
153
    is_deeply( $job->decode_json_field({ field => 'data' }), $data );
154
154
155
    $job->status('started')->store;
155
    $job->status('started')->store;
156
    $job->finish( $data );
156
    $job->finish( $data );
157
157
158
    is( $job->status, 'finished' );
158
    is( $job->status, 'finished' );
159
    isnt( $job->ended_on, undef, 'ended_on set' );
159
    isnt( $job->ended_on, undef, 'ended_on set' );
160
    is_deeply( $job->json->decode( $job->data ), $data );
160
    is_deeply( $job->decode_json_field({ field => 'data' }), $data );
161
161
162
    throws_ok
162
    throws_ok
163
        { $job->start; }
163
        { $job->start; }
Lines 224-230 subtest 'process tests' => sub { Link Here
224
    is_deeply( C4::Context->interface, 'intranet', "Interface set from job context on process" );
224
    is_deeply( C4::Context->interface, 'intranet', "Interface set from job context on process" );
225
225
226
    # Manually add a job (->new->store) without context
226
    # Manually add a job (->new->store) without context
227
    my $json = $job->json; # sorry, quickly borrowing your json object
227
    my $json = $job->_json; # sorry, quickly borrowing your json object
228
    my $data = $json->encode({ a => 'a', b => 'b' });
228
    my $data = $json->encode({ a => 'a', b => 'b' });
229
    my $incomplete_job = t::lib::Koha::BackgroundJob::BatchTest->new(
229
    my $incomplete_job = t::lib::Koha::BackgroundJob::BatchTest->new(
230
        {   status         => 'new',
230
        {   status         => 'new',
Lines 242-265 subtest 'process tests' => sub { Link Here
242
    $schema->storage->txn_rollback;
242
    $schema->storage->txn_rollback;
243
};
243
};
244
244
245
subtest 'decoded_data() and set_encoded_data() tests' => sub {
245
subtest 'decoded_data() tests' => sub {
246
246
247
    plan tests => 8;
247
    plan tests => 8;
248
    $schema->storage->txn_begin;
248
    $schema->storage->txn_begin;
249
249
250
    my $job = Koha::BackgroundJob::BatchUpdateItem->new->set_encoded_data( undef );
250
    my $job = Koha::BackgroundJob::BatchUpdateItem->new->set_encoded_json_field({ data => undef, field => 'data' });
251
    is( $job->decoded_data, undef, 'undef is undef' );
251
    is( $job->decoded_data, undef, 'undef is undef' );
252
252
253
    my $data = { some => 'data' };
253
    my $data = { some => 'data' };
254
254
255
    $job->set_encoded_data( $data );
255
    $job->set_encoded_json_field({ data => $data, field => 'data' });
256
256
257
    is_deeply( $job->json->decode($job->data), $data, 'decode what we sent' );
257
    is_deeply( $job->_json->decode($job->data), $data, 'decode what we sent' );
258
    is_deeply( $job->decoded_data, $data, 'check with decoded_data' );
258
    is_deeply( $job->decoded_data, $data, 'check with decoded_data' );
259
259
260
    # Let's get some Unicode stuff into the game
260
    # Let's get some Unicode stuff into the game
261
    $data = { favorite_Chinese => [ '葑', '癱' ], latin_dancing => [ '¢', '¥', 'á', 'û' ] };
261
    $data = { favorite_Chinese => [ '葑', '癱' ], latin_dancing => [ '¢', '¥', 'á', 'û' ] };
262
    $job->set_encoded_data( $data )->store;
262
    $job->set_encoded_json_field({ data => $data, field => 'data' })->store;
263
263
264
    $job->discard_changes; # refresh
264
    $job->discard_changes; # refresh
265
    is_deeply( $job->decoded_data, $data, 'Deep compare with Unicode data' );
265
    is_deeply( $job->decoded_data, $data, 'Deep compare with Unicode data' );
Lines 274-280 subtest 'decoded_data() and set_encoded_data() tests' => sub { Link Here
274
            push @{$utf8_data->{$k}}, Encode::encode('UTF-8', $c);
274
            push @{$utf8_data->{$k}}, Encode::encode('UTF-8', $c);
275
        }
275
        }
276
    }
276
    }
277
    $job->set_encoded_data( $utf8_data )->store;
277
    $job->set_encoded_json_field({ data => $utf8_data, field => 'data' })->store;
278
    $job->discard_changes; # refresh
278
    $job->discard_changes; # refresh
279
    is_deeply( $job->decoded_data, $utf8_data, 'Deep compare with utf8_data' );
279
    is_deeply( $job->decoded_data, $utf8_data, 'Deep compare with utf8_data' );
280
    # Need more evidence?
280
    # Need more evidence?
(-)a/t/db_dependent/Koha/BackgroundJobs.t (-1 / +1 lines)
Lines 62-68 my $job_id = t::lib::Koha::BackgroundJob::BatchTest->new->enqueue( Link Here
62
# Enqueuing a new job
62
# Enqueuing a new job
63
my $new_job = Koha::BackgroundJobs->find($job_id);
63
my $new_job = Koha::BackgroundJobs->find($job_id);
64
ok( $new_job, 'New job correctly enqueued' );
64
ok( $new_job, 'New job correctly enqueued' );
65
is_deeply( $new_job->json->decode( $new_job->data ),
65
is_deeply( $new_job->_json->decode( $new_job->data ),
66
    $data, 'data retrieved and json encoded correctly' );
66
    $data, 'data retrieved and json encoded correctly' );
67
is( t::lib::Dates::compare( $new_job->enqueued_on, dt_from_string ),
67
is( t::lib::Dates::compare( $new_job->enqueued_on, dt_from_string ),
68
    0, 'enqueued_on correctly filled with now()' );
68
    0, 'enqueued_on correctly filled with now()' );
(-)a/t/db_dependent/Koha/BackgroundJobs/BatchUpdateBiblio.t (-3 / +10 lines)
Lines 51-63 subtest "Exceptions must be stringified" => sub { Link Here
51
            type           => 'batch_biblio_record_modification',
51
            type           => 'batch_biblio_record_modification',
52
        }
52
        }
53
    );
53
    );
54
    my $data = $job->json->encode({ record_ids => [ $biblio->biblionumber ] });
54
55
    $job->data( $data )->store;
55
    $job->set_encoded_json_field(
56
        {   data  => { record_ids => [ $biblio->biblionumber ] },
57
            field => 'data'
58
        }
59
    )->store;
60
56
    $job = Koha::BackgroundJobs->find( $job->id );
61
    $job = Koha::BackgroundJobs->find( $job->id );
57
    $job->process(
62
    $job->process(
58
        { job_id => $job->id, record_ids => [ $biblio->biblionumber ] } );
63
        { job_id => $job->id, record_ids => [ $biblio->biblionumber ] } );
59
64
60
    $data = $job->json->decode( $job->get_from_storage->data );
65
    $job = $job->get_from_storage;
66
67
    my $data = $job->decode_json_field({ field => 'data' });
61
    is_deeply(
68
    is_deeply(
62
        $data->{messages}->[0],
69
        $data->{messages}->[0],
63
        {
70
        {
(-)a/t/lib/Koha/BackgroundJob/BatchTest.pm (-3 / +2 lines)
Lines 56-67 sub process { Link Here
56
        $self->progress( ++$job_progress )->store;
56
        $self->progress( ++$job_progress )->store;
57
    }
57
    }
58
58
59
    my $job_data = $self->json->decode($self->data);
59
    my $job_data = $self->_json->decode($self->data);
60
    $job_data->{messages} = \@messages;
60
    $job_data->{messages} = \@messages;
61
    $job_data->{report} = $report;
61
    $job_data->{report} = $report;
62
62
63
    $self->ended_on(dt_from_string)
63
    $self->ended_on(dt_from_string)
64
        ->data( $self->json->encode($job_data) );
64
         ->set_encoded_json_field({ data => $job_data, field => 'data' });
65
    $self->status('finished') if $self->status ne 'cancelled';
65
    $self->status('finished') if $self->status ne 'cancelled';
66
    $self->store;
66
    $self->store;
67
}
67
}
68
- 

Return to bug 32370