From 040d6896a3cd5622e78cccdedd8cce2afcf49a31 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Sun, 13 Dec 2020 19:27:25 +0100 Subject: [PATCH] add display of logs matching any state --- .../src/app/+admin/system/jobs/job.service.ts | 11 +++++-- .../+admin/system/jobs/jobs.component.html | 33 +++++++++++++------ .../app/+admin/system/jobs/jobs.component.ts | 16 +++++++-- server/controllers/api/jobs.ts | 23 +++++++++++-- server/helpers/custom-validators/jobs.ts | 1 + server/lib/job-queue/job-queue.ts | 13 +++++--- server/middlewares/validators/jobs.ts | 18 ++++++++-- support/doc/api/openapi.yaml | 24 +++++++++++++- 8 files changed, 113 insertions(+), 26 deletions(-) diff --git a/client/src/app/+admin/system/jobs/job.service.ts b/client/src/app/+admin/system/jobs/job.service.ts index 1ac50f050..4b4a8914f 100644 --- a/client/src/app/+admin/system/jobs/job.service.ts +++ b/client/src/app/+admin/system/jobs/job.service.ts @@ -19,13 +19,20 @@ export class JobService { private restExtractor: RestExtractor ) {} - getJobs (jobState: JobStateClient, jobType: JobTypeClient, pagination: RestPagination, sort: SortMeta): Observable> { + getJobs (options: { + jobState?: JobStateClient, + jobType: JobTypeClient, + pagination: RestPagination, + sort: SortMeta + }): Observable> { + const { jobState, jobType, pagination, sort } = options + let params = new HttpParams() params = this.restService.addRestGetParams(params, pagination, sort) if (jobType !== 'all') params = params.append('jobType', jobType) - return this.authHttp.get>(JobService.BASE_JOB_URL + '/' + jobState, { params }) + return this.authHttp.get>(JobService.BASE_JOB_URL + `/${jobState ? jobState : ''}`, { params }) .pipe( map(res => { return this.restExtractor.convertResultListDateToHuman(res, [ 'createdAt', 'processedOn', 'finishedOn' ]) diff --git a/client/src/app/+admin/system/jobs/jobs.component.html b/client/src/app/+admin/system/jobs/jobs.component.html index e06156a9e..2d60e7b9e 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.html +++ b/client/src/app/+admin/system/jobs/jobs.component.html @@ -17,6 +17,9 @@ [clearable]="false" [searchable]="false" > + + any + {{ state }} @@ -37,27 +40,31 @@ ID Type + State Created - + - {{ job.id }} - {{ job.type }} - {{ job.createdAt | date: 'short' }} + {{ job.id }} + {{ job.type }} + + {{ job.state }} + + {{ job.createdAt | date: 'short' }} - +
{{ [
           'Job: ' + job.id,
           'Type: ' + job.type,
@@ -67,12 +74,12 @@
       
     
     
-      
+      
         
{{ job.data }}
- +
{{ job.error }}
@@ -80,11 +87,17 @@ - +
- No {{ jobState }} jobs found. - No {{ jobType }} jobs found that are {{ jobState }}. + + No jobs found. + No {{ jobType }} jobs found. + + + No {{ jobState }} jobs found. + No {{ jobType }} jobs found that are {{ jobState }}. +
diff --git a/client/src/app/+admin/system/jobs/jobs.component.ts b/client/src/app/+admin/system/jobs/jobs.component.ts index f8e12d1b6..b7f18067b 100644 --- a/client/src/app/+admin/system/jobs/jobs.component.ts +++ b/client/src/app/+admin/system/jobs/jobs.component.ts @@ -16,7 +16,7 @@ export class JobsComponent extends RestTable implements OnInit { private static LOCAL_STORAGE_STATE = 'jobs-list-state' private static LOCAL_STORAGE_TYPE = 'jobs-list-type' - jobState: JobStateClient = 'waiting' + jobState?: JobStateClient | 'all' jobStates: JobStateClient[] = [ 'active', 'completed', 'failed', 'waiting', 'delayed' ] jobType: JobTypeClient = 'all' @@ -73,6 +73,10 @@ export class JobsComponent extends RestTable implements OnInit { } } + getColspan () { + return this.jobState === 'all' ? 5 : 4 + } + onJobStateOrTypeChanged () { this.pagination.start = 0 @@ -81,8 +85,16 @@ export class JobsComponent extends RestTable implements OnInit { } protected loadData () { + let jobState = this.jobState as JobState + if (this.jobState === 'all') jobState = null + this.jobsService - .getJobs(this.jobState, this.jobType, this.pagination, this.sort) + .getJobs({ + jobState, + jobType: this.jobType, + pagination: this.pagination, + sort: this.sort + }) .subscribe( resultList => { this.jobs = resultList.data diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts index ed6c94533..1131a44d6 100644 --- a/server/controllers/api/jobs.ts +++ b/server/controllers/api/jobs.ts @@ -12,11 +12,23 @@ import { setDefaultSort } from '../../middlewares' import { paginationValidator } from '../../middlewares/validators' -import { listJobsValidator } from '../../middlewares/validators/jobs' +import { listJobsStateValidator, listJobsValidator } from '../../middlewares/validators/jobs' import { isArray } from '../../helpers/custom-validators/misc' +import { jobStates } from '@server/helpers/custom-validators/jobs' const jobsRouter = express.Router() +jobsRouter.get('/', + authenticate, + ensureUserHasRight(UserRight.MANAGE_JOBS), + paginationValidator, + jobsSortValidator, + setDefaultSort, + setDefaultPagination, + listJobsValidator, + asyncMiddleware(listJobs) +) + jobsRouter.get('/:state', authenticate, ensureUserHasRight(UserRight.MANAGE_JOBS), @@ -25,6 +37,7 @@ jobsRouter.get('/:state', setDefaultSort, setDefaultPagination, listJobsValidator, + listJobsStateValidator, asyncMiddleware(listJobs) ) @@ -37,7 +50,7 @@ export { // --------------------------------------------------------------------------- async function listJobs (req: express.Request, res: express.Response) { - const state = req.params.state as JobState + const state = req.params.state as JobState || jobStates const asc = req.query.sort === 'createdAt' const jobType = req.query.jobType @@ -52,7 +65,11 @@ async function listJobs (req: express.Request, res: express.Response) { const result: ResultList = { total, - data: jobs.map(j => formatJob(j, state)) + data: Array.isArray(state) + ? await Promise.all( + jobs.map(async j => formatJob(j, await j.getState() as JobState)) + ) + : jobs.map(j => formatJob(j, state)) } return res.json(result) } diff --git a/server/helpers/custom-validators/jobs.ts b/server/helpers/custom-validators/jobs.ts index dd33e85a3..72dc73ee4 100644 --- a/server/helpers/custom-validators/jobs.ts +++ b/server/helpers/custom-validators/jobs.ts @@ -15,6 +15,7 @@ function isValidJobType (value: any) { // --------------------------------------------------------------------------- export { + jobStates, isValidJobState, isValidJobType } diff --git a/server/lib/job-queue/job-queue.ts b/server/lib/job-queue/job-queue.ts index 8d97434ac..49f06584d 100644 --- a/server/lib/job-queue/job-queue.ts +++ b/server/lib/job-queue/job-queue.ts @@ -154,13 +154,13 @@ class JobQueue { } async listForApi (options: { - state: JobState + state: JobState | JobState[] start: number count: number asc?: boolean jobType: JobType }): Promise { - const { state, start, count, asc, jobType } = options + const { state = Array.isArray(options.state) ? options.state : [ options.state ], start, count, asc, jobType } = options let results: Bull.Job[] = [] const filteredJobTypes = this.filterJobTypes(jobType) @@ -172,7 +172,7 @@ class JobQueue { continue } - const jobs = await queue.getJobs([ state ], 0, start + count, asc) + const jobs = await queue.getJobs(state as Bull.JobStatus[], 0, start + count, asc) results = results.concat(jobs) } @@ -188,7 +188,8 @@ class JobQueue { return results.slice(start, start + count) } - async count (state: JobState, jobType?: JobType): Promise { + async count (state: JobState | JobState[], jobType?: JobType): Promise { + const states = Array.isArray(state) ? state : [ state ] let total = 0 const filteredJobTypes = this.filterJobTypes(jobType) @@ -202,7 +203,9 @@ class JobQueue { const counts = await queue.getJobCounts() - total += counts[state] + for (const s of states) { + total += counts[s] + } } return total diff --git a/server/middlewares/validators/jobs.ts b/server/middlewares/validators/jobs.ts index b57615dbc..0fc183c1a 100644 --- a/server/middlewares/validators/jobs.ts +++ b/server/middlewares/validators/jobs.ts @@ -5,8 +5,6 @@ import { logger } from '../../helpers/logger' import { areValidationErrors } from './utils' const listJobsValidator = [ - param('state') - .custom(isValidJobState).not().isEmpty().withMessage('Should have a valid job state'), query('jobType') .optional() .custom(isValidJobType).withMessage('Should have a valid job state'), @@ -20,8 +18,22 @@ const listJobsValidator = [ } ] +const listJobsStateValidator = [ + param('state') + .custom(isValidJobState).not().isEmpty().withMessage('Should have a valid job state'), + + (req: express.Request, res: express.Response, next: express.NextFunction) => { + logger.debug('Checking listJobsValidator parameters.', { parameters: req.params }) + + if (areValidationErrors(req, res)) return + + return next() + } +] + // --------------------------------------------------------------------------- export { - listJobsValidator + listJobsValidator, + listJobsStateValidator } diff --git a/support/doc/api/openapi.yaml b/support/doc/api/openapi.yaml index ba420b4a9..c06bffb0a 100644 --- a/support/doc/api/openapi.yaml +++ b/support/doc/api/openapi.yaml @@ -356,15 +356,17 @@ paths: - name: state in: path required: true - description: The state of the job + description: The state of the job ('' for for no filter) schema: type: string enum: + - '' - active - completed - failed - waiting - delayed + - $ref: '#/components/parameters/jobType' - $ref: '#/components/parameters/start' - $ref: '#/components/parameters/count' - $ref: '#/components/parameters/sort' @@ -3780,6 +3782,26 @@ components: schema: type: string example: peertube-plugin-auth-ldap + jobType: + name: jobType + in: query + required: false + description: job type + schema: + type: string + enum: + - activitypub-follow + - activitypub-http-broadcast + - activitypub-http-fetcher + - activitypub-http-unicast + - email + - video-transcoding + - video-file-import + - video-import + - videos-views + - activitypub-refresher + - video-redundancy + - video-live-ending securitySchemes: OAuth2: description: >