From 25dfc582a85e5a8c3bcb89ee3a2be8725ff25999 Mon Sep 17 00:00:00 2001 From: Felix Mosheev <9304194+felixmosh@users.noreply.github.com> Date: Tue, 27 Apr 2021 11:52:03 +0300 Subject: [PATCH] Split bull & bullMq types to be generic, closes #202 --- bullAdapter.d.ts | 1 + bullAdapter.js | 1 + bullMQAdapter.d.ts | 1 + bullMQAdapter.js | 2 + package.json | 6 +-- src/@types/app.ts | 79 +++++++++++++++--------------- src/index.ts | 14 ++---- src/queueAdapters/base.ts | 12 ++--- src/routes/queues.ts | 9 ++-- src/ui/services/Api.ts | 22 ++------- tests/api/public-interface.spec.ts | 2 - yarn.lock | 8 +-- 12 files changed, 71 insertions(+), 86 deletions(-) create mode 100644 bullAdapter.d.ts create mode 100644 bullAdapter.js create mode 100644 bullMQAdapter.d.ts create mode 100644 bullMQAdapter.js diff --git a/bullAdapter.d.ts b/bullAdapter.d.ts new file mode 100644 index 00000000..c3aa2d36 --- /dev/null +++ b/bullAdapter.d.ts @@ -0,0 +1 @@ +export { BullAdapter } from './dist/queueAdapters/bull' diff --git a/bullAdapter.js b/bullAdapter.js new file mode 100644 index 00000000..d81e887a --- /dev/null +++ b/bullAdapter.js @@ -0,0 +1 @@ +module.exports = require('./dist/queueAdapters/bull') diff --git a/bullMQAdapter.d.ts b/bullMQAdapter.d.ts new file mode 100644 index 00000000..114a12e2 --- /dev/null +++ b/bullMQAdapter.d.ts @@ -0,0 +1 @@ +export { BullMQAdapter } from './dist/queueAdapters/bullMQ' diff --git a/bullMQAdapter.js b/bullMQAdapter.js new file mode 100644 index 00000000..31b9ef5d --- /dev/null +++ b/bullMQAdapter.js @@ -0,0 +1,2 @@ +/// +module.exports = require('./dist/queueAdapters/bullMQ') diff --git a/package.json b/package.json index 3fd5a8df..7478a7c6 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,6 @@ "Felix Mosheev" ], "main": "dist/index.js", - "types": "dist/index.d.ts", "license": "MIT", "repository": { "type": "git", @@ -25,7 +24,8 @@ }, "files": [ "dist/**/*", - "static/**/*" + "static/**/*", + "*Adapter.{js,d.ts}" ], "scripts": { "prepublishOnly": "yarn build", @@ -84,7 +84,7 @@ "auto-changelog": "^2.2.1", "babel-loader": "^8.2.2", "bull": "^3.21.1", - "bullmq": "1.19.3", + "bullmq": "^1.21.0", "css-loader": "^5.1.3", "css-minimizer-webpack-plugin": "^2.0.0", "dockest": "2.1.0", diff --git a/src/@types/app.ts b/src/@types/app.ts index d510e866..446c3d65 100644 --- a/src/@types/app.ts +++ b/src/@types/app.ts @@ -1,6 +1,4 @@ -import { Job, JobOptions } from 'bull' -import { Job as JobMq, JobsOptions } from 'bullmq' -import * as Redis from 'ioredis' +import { BaseAdapter } from '../queueAdapters/base' import { Status } from '../ui/components/constants' export type JobCleanStatus = @@ -14,41 +12,44 @@ export type JobStatus = Status export type JobCounts = Record -export interface QueueAdapter { - readonly readOnlyMode: boolean - - getClient(): Promise - - getName(): string - - getJob(id: string): Promise +export interface QueueAdapterOptions { + readOnlyMode: boolean +} - getJobs( - jobStatuses: JobStatus[], - start?: number, - end?: number, - ): Promise<(Job | JobMq)[]> +export type BullBoardQueues = Map - getJobCounts(...jobStatuses: JobStatus[]): Promise +export interface QueueJob { + opts: { + delay?: number | undefined + } - clean(queueStatus: JobCleanStatus, graceTimeMs: number): Promise + promote(): Promise - setFormatter( - field: 'data' | 'returnValue', - formatter: (data: any) => any, - ): void + remove(): Promise - format(field: 'data' | 'returnValue', data: any): any + retry(): Promise - getJobLogs(jobId: string): Promise + toJSON(): QueueJobJson } -export interface QueueAdapterOptions { - readOnlyMode: boolean +export interface QueueJobJson { + // add properties as needed from real Bull/BullMQ jobs + id?: string | undefined | number | null + name: string + // eslint-disable-next-line @typescript-eslint/ban-types + progress: number | object + attemptsMade: number + finishedOn?: number | null + processedOn?: number | null + timestamp: number + failedReason: string + stacktrace: string[] | null + data: any + returnvalue: any + opts: any + parentKey?: string } -export type BullBoardQueues = Map - export interface ValidMetrics { total_system_memory: string redis_version: string @@ -59,19 +60,19 @@ export interface ValidMetrics { } export interface AppJob { - id: string | number | undefined - timestamp: number | null - processedOn?: number | null - finishedOn?: number | null - progress: JobMq['progress'] - attempts: JobMq['attemptsMade'] - failedReason: JobMq['failedReason'] + id: QueueJobJson['id'] + name: QueueJobJson['name'] + timestamp: QueueJobJson['timestamp'] + processedOn?: QueueJobJson['processedOn'] + finishedOn?: QueueJobJson['finishedOn'] + progress: QueueJobJson['progress'] + attempts: QueueJobJson['attemptsMade'] + failedReason: QueueJobJson['failedReason'] stacktrace: string[] - opts: JobsOptions | JobOptions - data: JobMq['data'] - name: JobMq['name'] delay: number | undefined - returnValue: string | Record | null + opts: QueueJobJson['opts'] + data: QueueJobJson['data'] + returnValue: QueueJobJson['returnvalue'] } export interface AppQueue { diff --git a/src/index.ts b/src/index.ts index 6bf082df..d81af99c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,8 @@ import { RequestHandler, } from 'express-serve-static-core' import path from 'path' -import { BullBoardQueues, QueueAdapter } from './@types/app' +import { BullBoardQueues } from './@types/app' +import { BaseAdapter } from './queueAdapters/base' import { cleanAll } from './routes/cleanAll' import { cleanJob } from './routes/cleanJob' import { errorHandler } from './routes/errorHandler' @@ -17,10 +18,7 @@ import { queuesHandler } from './routes/queues' import { retryAll } from './routes/retryAll' import { retryJob } from './routes/retryJob' -export { BullMQAdapter } from './queueAdapters/bullMQ' -export { BullAdapter } from './queueAdapters/bull' - -const bullBoardQueues: BullBoardQueues = new Map() +const bullBoardQueues: BullBoardQueues = new Map() const wrapAsync = ( fn: RequestHandler, @@ -45,7 +43,7 @@ router.get('/api/queues/:queueName/:id/logs', wrapAsync(jobLogs)) router.put('/api/queues/:queueName/clean/:queueStatus', wrapAsync(cleanAll)) router.use(errorHandler) -export const setQueues = (bullQueues: ReadonlyArray): void => { +export const setQueues = (bullQueues: ReadonlyArray): void => { bullQueues.forEach((queue) => { const name = queue.getName() @@ -53,9 +51,7 @@ export const setQueues = (bullQueues: ReadonlyArray): void => { }) } -export const replaceQueues = ( - bullQueues: ReadonlyArray, -): void => { +export const replaceQueues = (bullQueues: ReadonlyArray): void => { const queuesToPersist: string[] = bullQueues.map((queue) => queue.getName()) bullBoardQueues.forEach((_queue, name) => { diff --git a/src/queueAdapters/base.ts b/src/queueAdapters/base.ts index df574f63..23b33023 100644 --- a/src/queueAdapters/base.ts +++ b/src/queueAdapters/base.ts @@ -1,15 +1,13 @@ -import { Job } from 'bull' -import { Job as JobMq } from 'bullmq' +import * as Redis from 'ioredis' import { JobCleanStatus, JobCounts, JobStatus, - QueueAdapter, QueueAdapterOptions, + QueueJob, } from '../@types/app' -import * as Redis from 'ioredis' -export abstract class BaseAdapter implements QueueAdapter { +export abstract class BaseAdapter { public readonly readOnlyMode: boolean private formatters: Record any> = {} @@ -36,7 +34,7 @@ export abstract class BaseAdapter implements QueueAdapter { graceTimeMs: number, ): Promise - public abstract getJob(id: string): Promise + public abstract getJob(id: string): Promise public abstract getJobCounts(...jobStatuses: JobStatus[]): Promise @@ -44,7 +42,7 @@ export abstract class BaseAdapter implements QueueAdapter { jobStatuses: JobStatus[], start?: number, end?: number, - ): Promise<(Job | JobMq)[]> + ): Promise public abstract getJobLogs(id: string): Promise diff --git a/src/routes/queues.ts b/src/routes/queues.ts index 8d038ccc..4220e048 100644 --- a/src/routes/queues.ts +++ b/src/routes/queues.ts @@ -1,11 +1,10 @@ -import { Job } from 'bull' -import { Job as JobMq } from 'bullmq' import { Request, RequestHandler, Response } from 'express-serve-static-core' import { parse as parseRedisInfo } from 'redis-info' import * as api from '../@types/api' import * as app from '../@types/app' -import { BullBoardQueues, JobStatus, QueueAdapter } from '../@types/app' +import { BullBoardQueues, JobStatus, QueueJob } from '../@types/app' +import { BaseAdapter } from '../queueAdapters/base' import { Status } from '../ui/components/constants' type MetricName = keyof app.ValidMetrics @@ -18,7 +17,7 @@ const metrics: MetricName[] = [ 'blocked_clients', ] -const getStats = async (queue: QueueAdapter): Promise => { +const getStats = async (queue: BaseAdapter): Promise => { const redisClient = await queue.getClient() const redisInfoRaw = await redisClient.info() const redisInfo = parseRedisInfo(redisInfoRaw) @@ -37,7 +36,7 @@ const getStats = async (queue: QueueAdapter): Promise => { return validMetrics } -const formatJob = (job: Job | JobMq, queue: QueueAdapter): app.AppJob => { +const formatJob = (job: QueueJob, queue: BaseAdapter): app.AppJob => { const jobProps = job.toJSON() return { diff --git a/src/ui/services/Api.ts b/src/ui/services/Api.ts index 4470b068..d642e16e 100644 --- a/src/ui/services/Api.ts +++ b/src/ui/services/Api.ts @@ -1,7 +1,7 @@ import Axios, { AxiosInstance, AxiosResponse } from 'axios' import { toast } from 'react-toastify' import { GetQueues } from '../../@types/api' -import { SelectedStatuses } from '../../@types/app' +import { AppJob, SelectedStatuses } from '../../@types/app' export class Api { private axios: AxiosInstance @@ -41,37 +41,25 @@ export class Api { ) } - public cleanJob( - queueName: string, - jobId: string | number | undefined, - ): Promise { + public cleanJob(queueName: string, jobId: AppJob['id']): Promise { return this.axios.put( `/queues/${encodeURIComponent(queueName)}/${jobId}/clean`, ) } - public retryJob( - queueName: string, - jobId: string | number | undefined, - ): Promise { + public retryJob(queueName: string, jobId: AppJob['id']): Promise { return this.axios.put( `/queues/${encodeURIComponent(queueName)}/${jobId}/retry`, ) } - public promoteJob( - queueName: string, - jobId: string | number | undefined, - ): Promise { + public promoteJob(queueName: string, jobId: AppJob['id']): Promise { return this.axios.put( `/queues/${encodeURIComponent(queueName)}/${jobId}/promote`, ) } - public getJobLogs( - queueName: string, - jobId: string | number | undefined, - ): Promise { + public getJobLogs(queueName: string, jobId: AppJob['id']): Promise { return this.axios.get( `/queues/${encodeURIComponent(queueName)}/${jobId}/logs`, ) diff --git a/tests/api/public-interface.spec.ts b/tests/api/public-interface.spec.ts index c1ab9343..422360c3 100644 --- a/tests/api/public-interface.spec.ts +++ b/tests/api/public-interface.spec.ts @@ -4,8 +4,6 @@ describe('lib public interface', () => { it('should save the interface', () => { expect(bullBoard).toMatchInlineSnapshot(` Object { - "BullAdapter": [Function], - "BullMQAdapter": [Function], "replaceQueues": [Function], "router": [Function], "setQueues": [Function], diff --git a/yarn.lock b/yarn.lock index 67175b0a..71337ec4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3222,10 +3222,10 @@ bull@^3.21.1: util.promisify "^1.0.1" uuid "^8.3.0" -bullmq@1.19.3: - version "1.19.3" - resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-1.19.3.tgz#efa7b21b67b9293aa727254ded989c5f3bee4ba0" - integrity sha512-iuZHTU+OL7oqfjiLYA6P3auzFnLar3OTjTPs0ByTrqrUaXc+XD607HxHB4olCdP/UNO9V2ZVlhYeucwMgXCbzQ== +bullmq@^1.21.0: + version "1.21.0" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-1.21.0.tgz#fac7a8a7a141094f6936443bd050aa1078e801c2" + integrity sha512-27Mu+aO+5B1bpWhUnu0qMrwhUrWsn1PpPzm2Mq4qx2DEWqMOx00uPcX1sSv9ZTE4b2jqqeIBb5fXr+OzTGnsIg== dependencies: "@types/ioredis" "^4.22.2" cron-parser "^2.7.3"