Bug 30982: [22.05.x] Use the REST API for background job list view
Bug 30982: REST API specs Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: (QA follow-up) Double quoting and console.log This patch fixes the issues highlighted by the QA script; We use double quotes for translatable strings in JS and remove an errant console.log call. Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Add tests and implement GET /background_jobs/$id Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Make code more re-usable Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Add 'context' to the REST API specs context has been added by bug 30889 Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Add Koha::BackgroundJobs->filter_by_current Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: API tweaks This patch makes the following changes to the 'background_jobs' API: * We now call them 'jobs' * Removed deprecated query parameter definitions * Added only_current query parameter * Controller gets adapted to use $rs->filter_by_current when only_current is passed Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Adapt table to new API spec Disclaimer: this patch is highly opinionated :-D When I started looking at this patch I felt like the two tables (current/past jobs) implemented on bug 30462 was the way to go. In order to make this patches apply after it I had to redo all the things. Or most of them. But I decided to keep the idea of filtering out completed tasks, not just having the option to display 'the last hour' tasks. For the task I added some required helper methods and the relevant tests as well. So a behavior change. Hope you all agree with it. Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: Rename 'Background Jobs' => 'Jobs' Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: (QA follow-up) Resolve pod warning Empty section in previous paragraph at line 32 in file Koha/BackgroundJobs.pm Test plan: Run podchecker again on this module. Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: (QA follow-up) Spelling [1] Correct: BackgrounJob [2] If should filter out not current jobs => Had a hard time reading that one until I replaced if by it. => Decided to rephrase it in a more positive way. Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: (QA follow-up) Remove redundancy from template The template now contains two lists for both status and type: a TT list and a JS list. The type list already proves that redundancy leads to bugs. We miss three types at one side: Unknown job type 'stage_marc_for_import' Unknown job type 'marc_import_commit_batch' Unknown job type 'marc_import_revert_batch' This patch removes the TT list. And gets the status and type via an additional js call. For that reason I hide the fieldset until document ready. This can be improved later when needed. Test plan: Look at status and type on both job list and detail view. Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Bug 30982: (QA follow-up) No userenv, no jobs + # Assume permission if context has no user + my $can_manage_background_jobs = 1; => This felt a bit unsafe. Test plan: Try interface for jobs. Call API with cookie. Call API with OAuth. Run t/db_dependent/Koha/BackgroundJobs.t Signed-off-by: Marcel de Rooy <m.de.rooy@rijksmuseum.nl> Signed-off-by: Lucas Gass <lucas@bywatersolutions.com>
This commit is contained in:
parent
ae9d3b0477
commit
eb2dfad0d1
13 changed files with 740 additions and 209 deletions
|
@ -47,7 +47,7 @@ my $job_id = Koha::BackgroundJob->enqueue(
|
||||||
);
|
);
|
||||||
|
|
||||||
Consumer:
|
Consumer:
|
||||||
Koha::BackgrounJobs->find($job_id)->process;
|
Koha::BackgroundJobs->find($job_id)->process;
|
||||||
See also C<misc/background_jobs_worker.pl> for a full example
|
See also C<misc/background_jobs_worker.pl> for a full example
|
||||||
|
|
||||||
=head1 API
|
=head1 API
|
||||||
|
@ -386,7 +386,7 @@ sub _derived_class {
|
||||||
|
|
||||||
=head3 type_to_class_mapping
|
=head3 type_to_class_mapping
|
||||||
|
|
||||||
my $mapping = Koha::BackgrounJob->new->type_to_class_mapping;
|
my $mapping = Koha::BackgroundJob->new->type_to_class_mapping;
|
||||||
|
|
||||||
Returns the available types to class mappings.
|
Returns the available types to class mappings.
|
||||||
|
|
||||||
|
@ -404,7 +404,7 @@ sub type_to_class_mapping {
|
||||||
|
|
||||||
=head3 core_types_to_classes
|
=head3 core_types_to_classes
|
||||||
|
|
||||||
my $mappings = Koha::BackgrounJob->new->core_types_to_classes
|
my $mappings = Koha::BackgroundJob->new->core_types_to_classes
|
||||||
|
|
||||||
Returns the core background jobs types to class mappings.
|
Returns the core background jobs types to class mappings.
|
||||||
|
|
||||||
|
@ -469,6 +469,44 @@ sub plugin_types_to_classes {
|
||||||
return $self->{_plugin_mapping};
|
return $self->{_plugin_mapping};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
=head3 to_api
|
||||||
|
|
||||||
|
my $json = $job->to_api;
|
||||||
|
|
||||||
|
Overloaded method that returns a JSON representation of the Koha::BackgroundJob object,
|
||||||
|
suitable for API output.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub to_api {
|
||||||
|
my ( $self, $params ) = @_;
|
||||||
|
|
||||||
|
my $json = $self->SUPER::to_api( $params );
|
||||||
|
|
||||||
|
$json->{context} = $self->json->decode($self->context)
|
||||||
|
if defined $self->context;
|
||||||
|
$json->{data} = $self->decoded_data;
|
||||||
|
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
=head3 to_api_mapping
|
||||||
|
|
||||||
|
This method returns the mapping for representing a Koha::BackgroundJob object
|
||||||
|
on the API.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub to_api_mapping {
|
||||||
|
return {
|
||||||
|
id => 'job_id',
|
||||||
|
borrowernumber => 'patron_id',
|
||||||
|
ended_on => 'ended_date',
|
||||||
|
enqueued_on => 'enqueued_date',
|
||||||
|
started_on => 'started_date',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
=head3 _type
|
=head3 _type
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
|
@ -16,19 +16,64 @@ package Koha::BackgroundJobs;
|
||||||
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||||
|
|
||||||
use Modern::Perl;
|
use Modern::Perl;
|
||||||
use base qw(Koha::Objects);
|
|
||||||
use Koha::BackgroundJob;
|
use Koha::BackgroundJob;
|
||||||
|
|
||||||
|
use base qw(Koha::Objects);
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
|
|
||||||
Koha::BackgroundJobs - Koha BackgroundJob Object set class
|
Koha::BackgroundJobs - Koha BackgroundJob Object set class
|
||||||
|
|
||||||
=head1 API
|
=head1 API
|
||||||
|
|
||||||
=head2 Class Methods
|
=head2 Class methods
|
||||||
|
|
||||||
|
=head3 search_limited
|
||||||
|
|
||||||
|
my $background_jobs = Koha::BackgroundJobs->search_limited( $params, $attributes );
|
||||||
|
|
||||||
|
Returns all background jobs the logged in user should be allowed to see
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
|
sub search_limited {
|
||||||
|
my ( $self, $params, $attributes ) = @_;
|
||||||
|
|
||||||
|
my $can_manage_background_jobs;
|
||||||
|
my $logged_in_user;
|
||||||
|
my $userenv = C4::Context->userenv;
|
||||||
|
if ( $userenv and $userenv->{number} ) {
|
||||||
|
$logged_in_user = Koha::Patrons->find( $userenv->{number} );
|
||||||
|
$can_manage_background_jobs = $logged_in_user->has_permission(
|
||||||
|
{ parameters => 'manage_background_jobs' } );
|
||||||
|
}
|
||||||
|
|
||||||
|
return $self->search( $params, $attributes ) if $can_manage_background_jobs;
|
||||||
|
my $id = $logged_in_user ? $logged_in_user->borrowernumber : undef;
|
||||||
|
return $self->search({ borrowernumber => $id })->search( $params, $attributes );
|
||||||
|
}
|
||||||
|
|
||||||
|
=head3 filter_by_current
|
||||||
|
|
||||||
|
my $current_jobs = $jobs->filter_by_current;
|
||||||
|
|
||||||
|
Returns a new resultset, filtering out finished jobs.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub filter_by_current {
|
||||||
|
my ($self) = @_;
|
||||||
|
|
||||||
|
return $self->search(
|
||||||
|
{
|
||||||
|
status => { not_in => [ 'cancelled', 'failed', 'finished' ] }
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
=head2 Internal methods
|
||||||
|
|
||||||
=head3 _type
|
=head3 _type
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
99
Koha/REST/V1/BackgroundJobs.pm
Normal file
99
Koha/REST/V1/BackgroundJobs.pm
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package Koha::REST::V1::BackgroundJobs;
|
||||||
|
|
||||||
|
# This file is part of Koha.
|
||||||
|
#
|
||||||
|
# Koha is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Koha is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||||
|
|
||||||
|
use Modern::Perl;
|
||||||
|
|
||||||
|
use Mojo::Base 'Mojolicious::Controller';
|
||||||
|
|
||||||
|
use Koha::BackgroundJobs;
|
||||||
|
|
||||||
|
use Try::Tiny;
|
||||||
|
|
||||||
|
=head1 API
|
||||||
|
|
||||||
|
=head2 Methods
|
||||||
|
|
||||||
|
=head3 list
|
||||||
|
|
||||||
|
Controller function that handles listing Koha::BackgroundJob objects
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub list {
|
||||||
|
my $c = shift->openapi->valid_input or return;
|
||||||
|
|
||||||
|
return try {
|
||||||
|
|
||||||
|
my $only_current = delete $c->validation->output->{only_current};
|
||||||
|
|
||||||
|
my $bj_rs = Koha::BackgroundJobs->new;
|
||||||
|
|
||||||
|
if ($only_current) {
|
||||||
|
$bj_rs = $bj_rs->filter_by_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $c->render(
|
||||||
|
status => 200,
|
||||||
|
openapi => $c->objects->search($bj_rs)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
$c->unhandled_exception($_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
=head3 get
|
||||||
|
|
||||||
|
Controller function that handles retrieving a single Koha::BackgroundJob object
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub get {
|
||||||
|
my $c = shift->openapi->valid_input or return;
|
||||||
|
|
||||||
|
return try {
|
||||||
|
|
||||||
|
my $job_id = $c->validation->param('job_id');
|
||||||
|
my $patron = $c->stash('koha.user');
|
||||||
|
|
||||||
|
my $can_manage_background_jobs =
|
||||||
|
$patron->has_permission( { parameters => 'manage_background_jobs' } );
|
||||||
|
|
||||||
|
my $job = Koha::BackgroundJobs->find($job_id);
|
||||||
|
|
||||||
|
return $c->render(
|
||||||
|
status => 404,
|
||||||
|
openapi => { error => "Object not found" }
|
||||||
|
) unless $job;
|
||||||
|
|
||||||
|
return $c->render(
|
||||||
|
status => 403,
|
||||||
|
openapi => { error => "Cannot see background job info" }
|
||||||
|
)
|
||||||
|
if !$can_manage_background_jobs
|
||||||
|
&& $job->borrowernumber != $patron->borrowernumber;
|
||||||
|
|
||||||
|
return $c->render(
|
||||||
|
status => 200,
|
||||||
|
openapi => $job->to_api
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
$c->unhandled_exception($_);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
|
@ -77,39 +77,6 @@ if ( $op eq 'cancel' ) {
|
||||||
$op = 'list';
|
$op = 'list';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ( $op eq 'list' ) {
|
|
||||||
my $queued_jobs =
|
|
||||||
$can_manage_background_jobs
|
|
||||||
? Koha::BackgroundJobs->search( { ended_on => undef },
|
|
||||||
{ order_by => { -desc => 'enqueued_on' } } )
|
|
||||||
: Koha::BackgroundJobs->search(
|
|
||||||
{ borrowernumber => $logged_in_user->borrowernumber, ended_on => undef },
|
|
||||||
{ order_by => { -desc => 'enqueued_on' } }
|
|
||||||
);
|
|
||||||
$template->param( queued => $queued_jobs );
|
|
||||||
|
|
||||||
my $ended_since = dt_from_string->subtract( minutes => '60' );
|
|
||||||
my $dtf = Koha::Database->new->schema->storage->datetime_parser;
|
|
||||||
|
|
||||||
my $complete_jobs =
|
|
||||||
$can_manage_background_jobs
|
|
||||||
? Koha::BackgroundJobs->search(
|
|
||||||
{
|
|
||||||
ended_on => { '>=' => $dtf->format_datetime($ended_since) }
|
|
||||||
},
|
|
||||||
{ order_by => { -desc => 'enqueued_on' } }
|
|
||||||
)
|
|
||||||
: Koha::BackgroundJobs->search(
|
|
||||||
{
|
|
||||||
borrowernumber => $logged_in_user->borrowernumber,
|
|
||||||
ended_on => { '>=' => $dtf->format_datetime($ended_since) }
|
|
||||||
},
|
|
||||||
{ order_by => { -desc => 'enqueued_on' } }
|
|
||||||
);
|
|
||||||
$template->param( complete => $complete_jobs );
|
|
||||||
}
|
|
||||||
|
|
||||||
$template->param(
|
$template->param(
|
||||||
messages => \@messages,
|
messages => \@messages,
|
||||||
op => $op,
|
op => $op,
|
||||||
|
|
54
api/v1/swagger/definitions/job.yaml
Normal file
54
api/v1/swagger/definitions/job.yaml
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
job_id:
|
||||||
|
type: integer
|
||||||
|
description: internally assigned job identifier
|
||||||
|
readOnly: true
|
||||||
|
status:
|
||||||
|
description: job status
|
||||||
|
type: string
|
||||||
|
progress:
|
||||||
|
description: job progress
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
size:
|
||||||
|
description: job size
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
patron_id:
|
||||||
|
description: job enqueuer
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
type:
|
||||||
|
description: job type
|
||||||
|
type: string
|
||||||
|
queue:
|
||||||
|
description: job queue
|
||||||
|
type: string
|
||||||
|
data:
|
||||||
|
description: job data
|
||||||
|
type: object
|
||||||
|
context:
|
||||||
|
description: job context
|
||||||
|
type: object
|
||||||
|
enqueued_date:
|
||||||
|
description: job enqueue date
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
started_date:
|
||||||
|
description: job start date
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
format: date-time
|
||||||
|
ended_date:
|
||||||
|
description: job end date
|
||||||
|
type:
|
||||||
|
- string
|
||||||
|
- "null"
|
||||||
|
format: date-time
|
||||||
|
additionalProperties: false
|
87
api/v1/swagger/paths/jobs.yaml
Normal file
87
api/v1/swagger/paths/jobs.yaml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
---
|
||||||
|
/jobs:
|
||||||
|
get:
|
||||||
|
x-mojo-to: BackgroundJobs#list
|
||||||
|
operationId: listJobs
|
||||||
|
tags:
|
||||||
|
- jobs
|
||||||
|
summary: List jobs
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: only_current
|
||||||
|
in: query
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
description: Only include current jobs
|
||||||
|
- $ref: "../swagger.yaml#/parameters/match"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/order_by"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/page"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/per_page"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/q_param"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/q_body"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/q_header"
|
||||||
|
- $ref: "../swagger.yaml#/parameters/request_id_header"
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: A list of jobs
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: "../swagger.yaml#/definitions/job"
|
||||||
|
"403":
|
||||||
|
description: Access forbidden
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
"500":
|
||||||
|
description: |
|
||||||
|
Internal server error. Possible `error_code` attribute values:
|
||||||
|
|
||||||
|
* `internal_server_error`
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
"503":
|
||||||
|
description: Under maintenance
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
x-koha-authorization:
|
||||||
|
permissions:
|
||||||
|
catalogue: "1"
|
||||||
|
"/jobs/{job_id}":
|
||||||
|
get:
|
||||||
|
x-mojo-to: BackgroundJobs#get
|
||||||
|
operationId: getJob
|
||||||
|
tags:
|
||||||
|
- jobs
|
||||||
|
summary: Get a job
|
||||||
|
parameters:
|
||||||
|
- $ref: "../swagger.yaml#/parameters/job_id_pp"
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: A job
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/job"
|
||||||
|
"403":
|
||||||
|
description: Access forbidden
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
"404":
|
||||||
|
description: Job not found
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
"500":
|
||||||
|
description: |
|
||||||
|
Internal server error. Possible `error_code` attribute values:
|
||||||
|
|
||||||
|
* `internal_server_error`
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
"503":
|
||||||
|
description: Under maintenance
|
||||||
|
schema:
|
||||||
|
$ref: "../swagger.yaml#/definitions/error"
|
||||||
|
x-koha-authorization:
|
||||||
|
permissions:
|
||||||
|
catalogue: "1"
|
|
@ -42,6 +42,8 @@ definitions:
|
||||||
$ref: ./definitions/invoice.yaml
|
$ref: ./definitions/invoice.yaml
|
||||||
item:
|
item:
|
||||||
$ref: ./definitions/item.yaml
|
$ref: ./definitions/item.yaml
|
||||||
|
job:
|
||||||
|
$ref: ./definitions/job.yaml
|
||||||
library:
|
library:
|
||||||
$ref: ./definitions/library.yaml
|
$ref: ./definitions/library.yaml
|
||||||
order:
|
order:
|
||||||
|
@ -155,6 +157,10 @@ paths:
|
||||||
$ref: "./paths/items.yaml#/~1items~1{item_id}"
|
$ref: "./paths/items.yaml#/~1items~1{item_id}"
|
||||||
"/items/{item_id}/pickup_locations":
|
"/items/{item_id}/pickup_locations":
|
||||||
$ref: "./paths/items.yaml#/~1items~1{item_id}~1pickup_locations"
|
$ref: "./paths/items.yaml#/~1items~1{item_id}~1pickup_locations"
|
||||||
|
/jobs:
|
||||||
|
$ref: ./paths/jobs.yaml#/~1jobs
|
||||||
|
"/jobs/{job_id}":
|
||||||
|
$ref: "./paths/jobs.yaml#/~1jobs~1{job_id}"
|
||||||
/libraries:
|
/libraries:
|
||||||
$ref: ./paths/libraries.yaml#/~1libraries
|
$ref: ./paths/libraries.yaml#/~1libraries
|
||||||
"/libraries/{library_id}":
|
"/libraries/{library_id}":
|
||||||
|
@ -300,6 +306,12 @@ parameters:
|
||||||
name: item_id
|
name: item_id
|
||||||
required: true
|
required: true
|
||||||
type: integer
|
type: integer
|
||||||
|
job_id_pp:
|
||||||
|
description: Job internal identifier
|
||||||
|
in: path
|
||||||
|
name: job_id
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
library_id_pp:
|
library_id_pp:
|
||||||
description: Internal library identifier
|
description: Internal library identifier
|
||||||
in: path
|
in: path
|
||||||
|
@ -560,6 +572,9 @@ tags:
|
||||||
- description: "Manage items\n"
|
- description: "Manage items\n"
|
||||||
name: items
|
name: items
|
||||||
x-displayName: Items
|
x-displayName: Items
|
||||||
|
- description: "Manage jobs\n"
|
||||||
|
name: jobs
|
||||||
|
x-displayName: Jobs
|
||||||
- description: "Manage libraries\n"
|
- description: "Manage libraries\n"
|
||||||
name: libraries
|
name: libraries
|
||||||
x-displayName: Libraries
|
x-displayName: Libraries
|
||||||
|
|
|
@ -75,9 +75,9 @@
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
[% IF CAN_user_parameters_manage_background_jobs %]
|
[% IF CAN_user_parameters_manage_background_jobs %]
|
||||||
<h5>Background jobs</h5>
|
<h5>Jobs</h5>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/cgi-bin/koha/admin/background_jobs.pl">Background jobs</a></li>
|
<li><a href="/cgi-bin/koha/admin/background_jobs.pl">Jobs</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
|
|
|
@ -147,10 +147,10 @@
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
[% IF CAN_user_parameters_manage_background_jobs %]
|
[% IF CAN_user_parameters_manage_background_jobs %]
|
||||||
<h3>Background jobs</h3>
|
<h3>Jobs</h3>
|
||||||
<dl>
|
<dl>
|
||||||
<dt><a href="/cgi-bin/koha/admin/background_jobs.pl">Manage background jobs</a></dt>
|
<dt><a href="/cgi-bin/koha/admin/background_jobs.pl">Manage jobs</a></dt>
|
||||||
<dd>View, manage and cancel background jobs.</dd>
|
<dd>View, manage and cancel jobs.</dd>
|
||||||
</dl>
|
</dl>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
|
|
|
@ -3,54 +3,12 @@
|
||||||
[% USE Asset %]
|
[% USE Asset %]
|
||||||
[% USE KohaDates %]
|
[% USE KohaDates %]
|
||||||
[% SET footerjs = 1 %]
|
[% SET footerjs = 1 %]
|
||||||
[% BLOCK show_job_status %]
|
|
||||||
[% SWITCH job.status %]
|
|
||||||
[% CASE "new" %]
|
|
||||||
<span>New</span>
|
|
||||||
[% CASE "cancelled" %]
|
|
||||||
<span>Cancelled</span>
|
|
||||||
[% CASE "finished" %]
|
|
||||||
<span>Finished</span>
|
|
||||||
[% CASE "started" %]
|
|
||||||
<span>Started</span>
|
|
||||||
[% CASE "running" %]
|
|
||||||
<span>Running</span>
|
|
||||||
[% CASE "failed" %]
|
|
||||||
<span>Failed</span>
|
|
||||||
[% CASE # Default case %]
|
|
||||||
[% job.status | html %]
|
|
||||||
[% END -%]
|
|
||||||
[% END %]
|
|
||||||
[% BLOCK show_job_type %]
|
|
||||||
[% SWITCH job_type %]
|
|
||||||
[% CASE 'batch_biblio_record_modification' %]
|
|
||||||
<span>Batch bibliographic record modification</span>
|
|
||||||
[% CASE 'batch_biblio_record_deletion' %]
|
|
||||||
<span>Batch bibliographic record record deletion</span>
|
|
||||||
[% CASE 'batch_authority_record_modification' %]
|
|
||||||
<span>Batch authority record modification</span>
|
|
||||||
[% CASE 'batch_authority_record_deletion' %]
|
|
||||||
<span>Batch authority record deletion</span>
|
|
||||||
[% CASE 'batch_item_record_modification' %]
|
|
||||||
<span>Batch item record modification</span>
|
|
||||||
[% CASE 'batch_item_record_deletion' %]
|
|
||||||
<span>Batch item record deletion</span>
|
|
||||||
[% CASE "batch_hold_cancel" %]
|
|
||||||
<span>Batch hold cancellation</span>
|
|
||||||
[% CASE 'update_elastic_index' %]
|
|
||||||
<span>Update Elasticsearch index</span>
|
|
||||||
[% CASE 'update_holds_queue_for_biblios' %]
|
|
||||||
<span>Holds queue update</span>
|
|
||||||
[% CASE %]<span>Unknown job type '[% job_type | html %]'</span>
|
|
||||||
[% END %]
|
|
||||||
|
|
||||||
[% END %]
|
|
||||||
[% INCLUDE 'doc-head-open.inc' %]
|
[% INCLUDE 'doc-head-open.inc' %]
|
||||||
<title>
|
<title>
|
||||||
[% IF op == 'view' %]
|
[% IF op == 'view' %]
|
||||||
Details of job #[% job.id | html %] ›
|
Details of job #[% job.id | html %] ›
|
||||||
[% END %]
|
[% END %]
|
||||||
Background jobs ›
|
Jobs ›
|
||||||
Administration › Koha
|
Administration › Koha
|
||||||
</title>
|
</title>
|
||||||
|
|
||||||
|
@ -73,14 +31,14 @@
|
||||||
</li>
|
</li>
|
||||||
[% IF op == 'view' %]
|
[% IF op == 'view' %]
|
||||||
<li>
|
<li>
|
||||||
<a href="/cgi-bin/koha/admin/background_jobs.pl">Background jobs</a>
|
<a href="/cgi-bin/koha/admin/background_jobs.pl">Jobs</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" aria-current="page">Details of job #[% job.id | html %]</a>
|
<a href="#" aria-current="page">Details of job #[% job.id | html %]</a>
|
||||||
</li>
|
</li>
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
<li>
|
<li>
|
||||||
<a href="#" aria-current="page">Background jobs</a>
|
<a href="#" aria-current="page">Jobs</a>
|
||||||
</li>
|
</li>
|
||||||
[% END %]
|
[% END %]
|
||||||
[% ELSE %]
|
[% ELSE %]
|
||||||
|
@ -112,17 +70,17 @@
|
||||||
|
|
||||||
[% PROCESS "background_jobs/${job.type}.inc" %]
|
[% PROCESS "background_jobs/${job.type}.inc" %]
|
||||||
|
|
||||||
<fieldset class="rows">
|
<fieldset class="rows" style="display:none;">
|
||||||
<ol>
|
<ol>
|
||||||
<li><span class="label">Job ID: </span>[% job.id | html %]</li>
|
<li><span class="label">Job ID: </span>[% job.id | html %]</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="job_status">Status: </label>
|
<label for="job_status">Status: </label>
|
||||||
[% PROCESS show_job_status %]
|
<span id="job_status_description"></span>
|
||||||
</li>
|
</li>
|
||||||
<li><label for="job_progress">Progress: </label>[% job.progress || 0 | html %] / [% job.size | html %]</li>
|
<li><label for="job_progress">Progress: </label>[% job.progress || 0 | html %] / [% job.size | html %]</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="job_type">Type: </label>
|
<label for="job_type">Type: </label>
|
||||||
[% PROCESS show_job_type job_type => job.type %]
|
<span id="job_type_description"></span>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="job_enqueued_on">Queued: </label>
|
<label for="job_enqueued_on">Queued: </label>
|
||||||
|
@ -154,108 +112,32 @@
|
||||||
|
|
||||||
[% IF op == 'list' %]
|
[% IF op == 'list' %]
|
||||||
|
|
||||||
<h1>Background jobs</h1>
|
<h1>Jobs</h1>
|
||||||
|
|
||||||
<div id="taskstabs" class="toptabs">
|
<div>
|
||||||
<ul class="nav nav-tabs" role="tablist">
|
<input type="checkbox" id="only_current" checked />
|
||||||
<li role="presentation" class="active"><a href="#queued" aria-controls="queued" role="tab" data-toggle="tab">Queued jobs</a></li>
|
<label for="only_current">Current jobs only</label>
|
||||||
<li role="presentation"><a href="#complete" aria-controls="complete" role="tab" data-toggle="tab">Completed jobs</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<div class="tab-content">
|
|
||||||
<div role="tabpanel" class="tab-pane active" id="queued">
|
|
||||||
[% IF queued.count %]
|
|
||||||
<table id="table_queued_jobs">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Job ID</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Progress</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Queued</th>
|
|
||||||
<th>Started</th>
|
|
||||||
<th class="noExport">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
[% FOREACH job IN queued %]
|
|
||||||
<tr>
|
|
||||||
<td>[% job.id | html %]</td>
|
|
||||||
<td>
|
|
||||||
[% PROCESS show_job_status %]
|
|
||||||
</td>
|
|
||||||
<td>[% job.progress || 0 | html %] / [% job.size | html %]</td>
|
|
||||||
<td>
|
|
||||||
[% PROCESS show_job_type job_type => job.type %]
|
|
||||||
</td>
|
|
||||||
<td>[% job.enqueued_on | $KohaDates with_hours = 1 %]</td>
|
|
||||||
<td>[% job.started_on| $KohaDates with_hours = 1 %]</td>
|
|
||||||
<td class="actions">
|
|
||||||
<a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id=[% job.id | html %]"><i class="fa fa-eye"></i> View</a>
|
|
||||||
[% IF job.status == 'new' || job.status == 'started' %]
|
|
||||||
<a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/background_jobs.pl?op=cancel&id=[% job.id | html %]"><i class="fa fa-trash"></i> Cancel</a>
|
|
||||||
[% END %]
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
[% END %]
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
[% ELSE %]
|
|
||||||
<div class="dialog message">
|
|
||||||
There are no queued background jobs yet.
|
|
||||||
</div>
|
|
||||||
[% END %]
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div role="tabpanel" class="tab-pane" id="complete">
|
|
||||||
[% IF complete.count %]
|
|
||||||
<p>Jobs completed in the last 60 minutes.</p>
|
|
||||||
<table id="table_complete_jobs">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Job ID</th>
|
|
||||||
<th>Status</th>
|
|
||||||
<th>Progress</th>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Queued</th>
|
|
||||||
<th>Started</th>
|
|
||||||
<th>Ended</th>
|
|
||||||
<th class="noExport">Actions</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
[% FOREACH job IN complete %]
|
|
||||||
<tr>
|
|
||||||
<td>[% job.id | html %]</td>
|
|
||||||
<td>
|
|
||||||
[% PROCESS show_job_status %]
|
|
||||||
</td>
|
|
||||||
<td>[% job.progress || 0 | html %] / [% job.size | html %]</td>
|
|
||||||
<td>
|
|
||||||
[% PROCESS show_job_type job_type => job.type %]
|
|
||||||
</td>
|
|
||||||
<td>[% job.enqueued_on | $KohaDates with_hours = 1 %]</td>
|
|
||||||
<td>[% job.started_on| $KohaDates with_hours = 1 %]</td>
|
|
||||||
<td>[% job.ended_on| $KohaDates with_hours = 1 %]</td>
|
|
||||||
<td class="actions">
|
|
||||||
<a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id=[% job.id | html %]"><i class="fa fa-eye"></i> View</a>
|
|
||||||
[% IF job.status == 'new' || job.status == 'started' %]
|
|
||||||
<a class="btn btn-default btn-xs" href="/cgi-bin/koha/admin/background_jobs.pl?op=cancel&id=[% job.id | html %]"><i class="fa fa-trash"></i> Cancel</a>
|
|
||||||
[% END %]
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
[% END %]
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
[% ELSE %]
|
|
||||||
<div class="dialog message">
|
|
||||||
There were no completed background jobs completed in the last 60 minutes.
|
|
||||||
</div>
|
|
||||||
[% END %]
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type="checkbox" id="include_last_hour" checked />
|
||||||
|
<label for="include_last_hour">Only include jobs started in the last hour</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="table_jobs">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Job ID</th>
|
||||||
|
<th data-filter="job_statuses">Status</th>
|
||||||
|
<th>Progress</th>
|
||||||
|
<th data-filter="job_types">Type</th>
|
||||||
|
<th>Queued</th>
|
||||||
|
<th>Started</th>
|
||||||
|
<th>Ended</th>
|
||||||
|
<th class="noExport">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
@ -270,26 +152,193 @@
|
||||||
|
|
||||||
[% MACRO jsinclude BLOCK %]
|
[% MACRO jsinclude BLOCK %]
|
||||||
[% Asset.js("js/admin-menu.js") | $raw %]
|
[% Asset.js("js/admin-menu.js") | $raw %]
|
||||||
|
[% INCLUDE 'js-date-format.inc' %]
|
||||||
[% INCLUDE 'datatables.inc' %]
|
[% INCLUDE 'datatables.inc' %]
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
const job_statuses = [
|
||||||
$("#table_queued_jobs").dataTable($.extend(true, {}, dataTablesDefaults, {
|
{'_id': 'new', '_str': _("New")},
|
||||||
"aoColumnDefs": [
|
{'_id': 'cancelled', '_str': _("Cancelled")},
|
||||||
{ "aTargets": [ -1, -2 ], "bSortable": false, "bSearchable": false },
|
{'_id': 'finished', '_str': _("Finished")},
|
||||||
],
|
{'_id': 'started', '_str': _("Started")},
|
||||||
"aaSorting": [[ 0, "desc" ]],
|
{'_id': 'running', '_str': _("Running")},
|
||||||
"iDisplayLength": 10,
|
{'_id': 'failed', '_str': _("Failed")},
|
||||||
"sPaginationType": "full_numbers"
|
];
|
||||||
}));
|
function get_job_status (status) {
|
||||||
|
let status_lib = job_statuses.find( s => s._id == status );
|
||||||
|
if (status_lib) {
|
||||||
|
return status_lib._str;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
$("#table_complete_jobs").dataTable($.extend(true, {}, dataTablesDefaults, {
|
const job_types = [
|
||||||
"aoColumnDefs": [
|
{
|
||||||
{ "aTargets": [ -1, -2 ], "bSortable": false, "bSearchable": false },
|
'_id': 'batch_biblio_record_modification',
|
||||||
],
|
'_str': _("Batch bibliographic record modification")
|
||||||
"aaSorting": [[ 0, "desc" ]],
|
},
|
||||||
"iDisplayLength": 10,
|
{
|
||||||
"sPaginationType": "full_numbers"
|
'_id': 'batch_biblio_record_deletion',
|
||||||
}));
|
'_str': _("Batch bibliographic record record deletion")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'batch_authority_record_modification',
|
||||||
|
'_str': _("Batch authority record modification")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'batch_authority_record_deletion',
|
||||||
|
'_str': _("Batch authority record deletion")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'batch_item_record_modification',
|
||||||
|
'_str': _("Batch item record modification")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'batch_item_record_deletion',
|
||||||
|
'_str': _("Batch item record deletion")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'batch_hold_cancel',
|
||||||
|
'_str': _("Batch hold cancellation")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'update_elastic_index',
|
||||||
|
'_str': _("Update Elasticsearch index")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'update_holds_queue_for_biblios',
|
||||||
|
'_str': _("Holds queue update")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'stage_marc_for_import',
|
||||||
|
'_str': _("Staged MARC records for import")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'marc_import_commit_batch',
|
||||||
|
'_str': _("Import MARC records")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'_id': 'marc_import_revert_batch',
|
||||||
|
'_str': _("Revert import MARC records")
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function get_job_type (job_type) {
|
||||||
|
let job_type_lib = job_types.find( t => t._id == job_type );
|
||||||
|
if ( job_type_lib ) {
|
||||||
|
return job_type_lib._str;
|
||||||
|
}
|
||||||
|
return _("Unknown job type '%s'").format(job_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
[% IF op == 'view' %]
|
||||||
|
$("#job_status_description").html( get_job_status("[% job.status | html %]") );
|
||||||
|
$("#job_type_description").html( get_job_type("[% job.type | html %]") );
|
||||||
|
$("fieldset.rows").show();
|
||||||
|
[% END %]
|
||||||
|
|
||||||
|
let additional_filters = {
|
||||||
|
started_on: function(){
|
||||||
|
let now = new Date();
|
||||||
|
if ( $("#include_last_hour").is(":checked") ) {
|
||||||
|
now.setHours(now.getHours() - 1);
|
||||||
|
return { ">": now.toISOString() };
|
||||||
|
} else {
|
||||||
|
return { "<": now.toISOString() };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let only_current_filter = function(){
|
||||||
|
if ( $("#only_current").is(":checked") ) {
|
||||||
|
return 'only_current=1';
|
||||||
|
} else {
|
||||||
|
return 'only_current=0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let jobs_table = $("#table_jobs").kohaTable({
|
||||||
|
"ajax": {
|
||||||
|
"url": "/api/v1/jobs?" + only_current_filter()
|
||||||
|
},
|
||||||
|
"order": [[ 1, "desc" ]],
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"data": "job_id",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "status",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return get_job_status(row.status).escapeHtml();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "progress,size",
|
||||||
|
"searchable": false,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return "%s/%s".format(row.progress, row.size).escapeHtml();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "type",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return get_job_type(row.type).escapeHtml();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "enqueued_date",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return $datetime(row.enqueued_date);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "started_date",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return $datetime(row.started_date);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": "ended_date",
|
||||||
|
"searchable": true,
|
||||||
|
"orderable": true,
|
||||||
|
"render": function(data, type, row, meta) {
|
||||||
|
return $datetime(row.ended_date);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"data": function( row, type, val, meta ) {
|
||||||
|
var result = '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id='+ encodeURIComponent(row.job_id) +'"><i class="fa fa-eye" aria-hidden="true"></i> '+_("View")+'</a>'+"\n";
|
||||||
|
if ( row.status == 'new' || row.status == 'started' ) {
|
||||||
|
result += '<a class="btn btn-default btn-xs" role="button" href="/cgi-bin/koha/admin/bakcground_jobs.pl?op=cancel&id='+ encodeURIComponent(row.job_id) +'"><i class="fa fa-trash" aria-hidden="true"></i> '+_("Cancel")+'</a>';
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
"searchable": false,
|
||||||
|
"orderable": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, null, 1, additional_filters);
|
||||||
|
|
||||||
|
$("#include_last_hour").on("change", function(){
|
||||||
|
jobs_table.DataTable().draw();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#only_current").on("change", function(){
|
||||||
|
jobs_table.DataTable().ajax.url("/api/v1/jobs?" + only_current_filter()).load();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
[% IF op == 'view' %]
|
[% IF op == 'view' %]
|
||||||
|
|
|
@ -19,9 +19,11 @@
|
||||||
|
|
||||||
use Modern::Perl;
|
use Modern::Perl;
|
||||||
|
|
||||||
use Test::More tests => 12;
|
use Test::More tests => 14;
|
||||||
use Test::MockModule;
|
use Test::MockModule;
|
||||||
|
|
||||||
|
use List::MoreUtils qw(any);
|
||||||
|
|
||||||
use Koha::Database;
|
use Koha::Database;
|
||||||
use Koha::BackgroundJobs;
|
use Koha::BackgroundJobs;
|
||||||
use Koha::DateUtils qw( dt_from_string );
|
use Koha::DateUtils qw( dt_from_string );
|
||||||
|
@ -31,7 +33,8 @@ use t::lib::Mocks;
|
||||||
use t::lib::Dates;
|
use t::lib::Dates;
|
||||||
use t::lib::Koha::BackgroundJob::BatchTest;
|
use t::lib::Koha::BackgroundJob::BatchTest;
|
||||||
|
|
||||||
my $schema = Koha::Database->new->schema;
|
my $builder = t::lib::TestBuilder->new;
|
||||||
|
my $schema = Koha::Database->new->schema;
|
||||||
$schema->storage->txn_begin;
|
$schema->storage->txn_begin;
|
||||||
|
|
||||||
t::lib::Mocks::mock_userenv;
|
t::lib::Mocks::mock_userenv;
|
||||||
|
@ -91,3 +94,49 @@ is_deeply(
|
||||||
is_deeply( $new_job->additional_report(), {} );
|
is_deeply( $new_job->additional_report(), {} );
|
||||||
|
|
||||||
$schema->storage->txn_rollback;
|
$schema->storage->txn_rollback;
|
||||||
|
|
||||||
|
subtest 'filter_by_current() tests' => sub {
|
||||||
|
|
||||||
|
plan tests => 4;
|
||||||
|
|
||||||
|
$schema->storage->txn_begin;
|
||||||
|
|
||||||
|
my $job_new = $builder->build_object( { class => 'Koha::BackgroundJobs', value => { status => 'new' } } );
|
||||||
|
my $job_cancelled = $builder->build_object( { class => 'Koha::BackgroundJobs', value => { status => 'cancelled' } } );
|
||||||
|
my $job_failed = $builder->build_object( { class => 'Koha::BackgroundJobs', value => { status => 'failed' } } );
|
||||||
|
my $job_finished = $builder->build_object( { class => 'Koha::BackgroundJobs', value => { status => 'finished' } } );
|
||||||
|
|
||||||
|
my $rs = Koha::BackgroundJobs->search(
|
||||||
|
{
|
||||||
|
id => [ $job_new->id, $job_cancelled->id, $job_failed->id, $job_finished->id ]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
is( $rs->count, 4, '4 jobs in resultset' );
|
||||||
|
ok( any {$_->status eq 'new'} @{$rs->as_list}, "There is a 'new' job" );
|
||||||
|
|
||||||
|
$rs = $rs->filter_by_current;
|
||||||
|
|
||||||
|
is( $rs->count, 1, 'Only 1 job in filtered resultset' );
|
||||||
|
is( $rs->next->status, 'new', "The only job in resultset is 'new'" );
|
||||||
|
|
||||||
|
$schema->storage->txn_rollback;
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'search_limited' => sub {
|
||||||
|
plan tests => 3;
|
||||||
|
|
||||||
|
$schema->storage->txn_begin;
|
||||||
|
my $patron1 = $builder->build_object( { class => 'Koha::Patrons', value => { flags => 0 } } );
|
||||||
|
my $patron2 = $builder->build_object( { class => 'Koha::Patrons', value => { flags => 0 } } );
|
||||||
|
my $job1 = $builder->build_object( { class => 'Koha::BackgroundJobs', value => { borrowernumber => $patron1->id } } );
|
||||||
|
|
||||||
|
C4::Context->set_userenv( undef, q{} );
|
||||||
|
is( Koha::BackgroundJobs->search_limited->count, 0, 'No jobs found without userenv' );
|
||||||
|
C4::Context->set_userenv( $patron1->id, $patron1->userid );
|
||||||
|
is( Koha::BackgroundJobs->search_limited->count, 1, 'My job found' );
|
||||||
|
C4::Context->set_userenv( $patron2->id, $patron2->userid );
|
||||||
|
is( Koha::BackgroundJobs->search_limited->count, 0, 'No jobs for me' );
|
||||||
|
|
||||||
|
$schema->storage->txn_rollback;
|
||||||
|
};
|
||||||
|
|
125
t/db_dependent/api/v1/jobs.t
Executable file
125
t/db_dependent/api/v1/jobs.t
Executable file
|
@ -0,0 +1,125 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
# This file is part of Koha.
|
||||||
|
#
|
||||||
|
# Koha is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# Koha is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||||
|
|
||||||
|
use Modern::Perl;
|
||||||
|
|
||||||
|
use Test::More tests => 25;
|
||||||
|
use Test::Mojo;
|
||||||
|
|
||||||
|
use t::lib::TestBuilder;
|
||||||
|
use t::lib::Mocks;
|
||||||
|
|
||||||
|
use Koha::BackgroundJobs;
|
||||||
|
use Koha::Database;
|
||||||
|
|
||||||
|
my $schema = Koha::Database->new->schema;
|
||||||
|
my $builder = t::lib::TestBuilder->new;
|
||||||
|
|
||||||
|
my $t = Test::Mojo->new('Koha::REST::V1');
|
||||||
|
#use t::lib::Mojo;
|
||||||
|
#my $t = t::lib::Mojo->new('Koha::REST::V1');
|
||||||
|
t::lib::Mocks::mock_preference( 'RESTBasicAuth', 1 );
|
||||||
|
|
||||||
|
$schema->storage->txn_begin;
|
||||||
|
|
||||||
|
Koha::BackgroundJobs->delete;
|
||||||
|
my $superlibrarian = $builder->build_object(
|
||||||
|
{
|
||||||
|
class => 'Koha::Patrons',
|
||||||
|
value => { flags => 1 },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
my $password = 'thePassword123';
|
||||||
|
$superlibrarian->set_password( { password => $password, skip_validation => 1 } );
|
||||||
|
my $superlibrarian_userid = $superlibrarian->userid;
|
||||||
|
|
||||||
|
my $librarian = $builder->build_object(
|
||||||
|
{
|
||||||
|
class => 'Koha::Patrons',
|
||||||
|
value => { flags => 2 ** 2 }, # catalogue flag = 2
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$librarian->set_password( { password => $password, skip_validation => 1 } );
|
||||||
|
my $librarian_userid = $librarian->userid;
|
||||||
|
|
||||||
|
my $patron = $builder->build_object(
|
||||||
|
{
|
||||||
|
class => 'Koha::Patrons',
|
||||||
|
value => { flags => 0 },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$patron->set_password( { password => $password, skip_validation => 1 } );
|
||||||
|
my $patron_userid = $patron->userid;
|
||||||
|
|
||||||
|
$t->get_ok("//$librarian_userid:$password@/api/v1/jobs")
|
||||||
|
->status_is(200)
|
||||||
|
->json_is( [] );
|
||||||
|
|
||||||
|
my $job = $builder->build_object(
|
||||||
|
{
|
||||||
|
class => 'Koha::BackgroundJobs',
|
||||||
|
value => {
|
||||||
|
status => 'finished',
|
||||||
|
progress => 100,
|
||||||
|
size => 100,
|
||||||
|
borrowernumber => $patron->borrowernumber,
|
||||||
|
type => 'batch_item_record_modification',
|
||||||
|
queue => 'default',
|
||||||
|
#data => '{"record_ids":["1"],"regex_mod":null,"exclude_from_local_holds_priority":null,"new_values":{"itemnotes":"xxx"}}' ,
|
||||||
|
data => '{"regex_mod":null,"report":{"total_records":1,"modified_fields":1,"modified_itemnumbers":[1]},"new_values":{"itemnotes":"xxx"},"record_ids":["1"],"exclude_from_local_holds_priority":null}',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
$t->get_ok("//$superlibrarian_userid:$password@/api/v1/jobs")
|
||||||
|
->status_is(200)->json_is( [ $job->to_api ] );
|
||||||
|
|
||||||
|
$t->get_ok("//$librarian_userid:$password@/api/v1/jobs")
|
||||||
|
->status_is(200)->json_is( [] );
|
||||||
|
|
||||||
|
$t->get_ok("//$patron_userid:$password@/api/v1/jobs")
|
||||||
|
->status_is(403);
|
||||||
|
|
||||||
|
$job->borrowernumber( $librarian->borrowernumber )->store;
|
||||||
|
|
||||||
|
$t->get_ok("//$librarian_userid:$password@/api/v1/jobs")
|
||||||
|
->status_is(200)->json_is( [ $job->to_api ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
$t->get_ok( "//$superlibrarian_userid:$password@/api/v1/jobs/"
|
||||||
|
. $job->id )->status_is(200)
|
||||||
|
->json_is( $job->to_api );
|
||||||
|
|
||||||
|
$t->get_ok( "//$librarian_userid:$password@/api/v1/jobs/"
|
||||||
|
. $job->id )->status_is(200)
|
||||||
|
->json_is( $job->to_api );
|
||||||
|
|
||||||
|
$job->borrowernumber( $superlibrarian->borrowernumber )->store;
|
||||||
|
$t->get_ok( "//$librarian_userid:$password@/api/v1/jobs/"
|
||||||
|
. $job->id )->status_is(403);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
$job->delete;
|
||||||
|
$t->get_ok( "//$superlibrarian_userid:$password@/api/v1/jobs/"
|
||||||
|
. $job->id )->status_is(404)
|
||||||
|
->json_is( '/error' => 'Object not found' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$schema->storage->txn_rollback;
|
|
@ -547,6 +547,9 @@ sub _gen_blob {
|
||||||
sub _gen_default_values {
|
sub _gen_default_values {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
return {
|
return {
|
||||||
|
BackgroundJob => {
|
||||||
|
context => '{}'
|
||||||
|
},
|
||||||
Borrower => {
|
Borrower => {
|
||||||
login_attempts => 0,
|
login_attempts => 0,
|
||||||
gonenoaddress => undef,
|
gonenoaddress => undef,
|
||||||
|
|
Loading…
Reference in a new issue