@alanszp/queue 9.2.4 → 9.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/errors/JobCannotBePromotedError.d.ts +10 -0
  2. package/dist/errors/JobCannotBePromotedError.js +25 -0
  3. package/dist/errors/JobCannotBePromotedError.js.map +1 -0
  4. package/dist/errors/JobNotFoundError.d.ts +8 -0
  5. package/dist/errors/JobNotFoundError.js +23 -0
  6. package/dist/errors/JobNotFoundError.js.map +1 -0
  7. package/dist/jobsManagement/commands/asyncCreationCommands.d.ts +7 -0
  8. package/dist/jobsManagement/commands/asyncCreationCommands.js +45 -0
  9. package/dist/jobsManagement/commands/asyncCreationCommands.js.map +1 -0
  10. package/dist/jobsManagement/commands/getCommands.d.ts +8 -0
  11. package/dist/jobsManagement/commands/getCommands.js +47 -0
  12. package/dist/jobsManagement/commands/getCommands.js.map +1 -0
  13. package/dist/jobsManagement/commands/workerHealthCommand.d.ts +2 -0
  14. package/dist/jobsManagement/commands/workerHealthCommand.js +10 -0
  15. package/dist/jobsManagement/commands/workerHealthCommand.js.map +1 -0
  16. package/dist/jobsManagement/index.d.ts +6 -0
  17. package/dist/jobsManagement/index.js +19 -0
  18. package/dist/jobsManagement/index.js.map +1 -0
  19. package/dist/jobsManagement/inputs/CreateAsyncJobInput.d.ts +11 -0
  20. package/dist/jobsManagement/inputs/CreateAsyncJobInput.js +58 -0
  21. package/dist/jobsManagement/inputs/CreateAsyncJobInput.js.map +1 -0
  22. package/dist/jobsManagement/inputs/GetJobInput.d.ts +6 -0
  23. package/dist/jobsManagement/inputs/GetJobInput.js +44 -0
  24. package/dist/jobsManagement/inputs/GetJobInput.js.map +1 -0
  25. package/dist/jobsManagement/inputs/SearchJobsInput.d.ts +14 -0
  26. package/dist/jobsManagement/inputs/SearchJobsInput.js +61 -0
  27. package/dist/jobsManagement/inputs/SearchJobsInput.js.map +1 -0
  28. package/dist/queue/createQueue.d.ts +1 -1
  29. package/dist/queue/queue.d.ts +15 -6
  30. package/dist/queue/queue.js +67 -1
  31. package/dist/queue/queue.js.map +1 -1
  32. package/dist/reporter/totalCount/ITotalCountReporter.d.ts +5 -0
  33. package/dist/reporter/totalCount/ITotalCountReporter.js +3 -0
  34. package/dist/reporter/totalCount/ITotalCountReporter.js.map +1 -0
  35. package/dist/reporter/totalCount/JobTotalCountReporter.d.ts +12 -0
  36. package/dist/reporter/totalCount/JobTotalCountReporter.js +29 -0
  37. package/dist/reporter/totalCount/JobTotalCountReporter.js.map +1 -0
  38. package/dist/reporter/totalCount/MockTotalCountReporter.d.ts +8 -0
  39. package/dist/reporter/totalCount/MockTotalCountReporter.js +21 -0
  40. package/dist/reporter/totalCount/MockTotalCountReporter.js.map +1 -0
  41. package/dist/types.d.ts +20 -1
  42. package/dist/types.js +22 -1
  43. package/dist/types.js.map +1 -1
  44. package/dist/worker/workerRepository.d.ts +2 -2
  45. package/dist/worker/workerRepository.js +7 -2
  46. package/dist/worker/workerRepository.js.map +1 -1
  47. package/package.json +5 -2
  48. package/src/errors/JobCannotBePromotedError.ts +32 -0
  49. package/src/errors/JobNotFoundError.ts +24 -0
  50. package/src/jobsManagement/commands/asyncCreationCommands.ts +58 -0
  51. package/src/jobsManagement/commands/getCommands.ts +57 -0
  52. package/src/jobsManagement/commands/workerHealthCommand.ts +10 -0
  53. package/src/jobsManagement/index.ts +6 -0
  54. package/src/jobsManagement/inputs/CreateAsyncJobInput.ts +37 -0
  55. package/src/jobsManagement/inputs/GetJobInput.ts +17 -0
  56. package/src/jobsManagement/inputs/SearchJobsInput.ts +45 -0
  57. package/src/queue/createQueue.ts +3 -3
  58. package/src/queue/queue.ts +101 -10
  59. package/src/reporter/totalCount/ITotalCountReporter.ts +5 -0
  60. package/src/reporter/totalCount/JobTotalCountReporter.ts +37 -0
  61. package/src/reporter/totalCount/MockTotalCountReporter.ts +20 -0
  62. package/src/types.ts +23 -0
  63. package/src/worker/workerRepository.ts +13 -5
@@ -1,14 +1,27 @@
1
- import { merge } from "lodash";
1
+ import { capitalize, merge } from "lodash";
2
2
  import { Job, JobsOptions } from "bullmq";
3
+ import { ListResult } from "@alanszp/core";
3
4
  import { SharedContext } from "@alanszp/shared-context";
4
- import { ConnectionOptions, JobData, QueueOptions, RawQueue } from "../types";
5
+ import {
6
+ ConnectionOptions,
7
+ JobData,
8
+ JobReturnValue,
9
+ JobStateEnum,
10
+ JobTypeEnum,
11
+ QueueOptions,
12
+ RawQueue,
13
+ } from "../types";
14
+ import { JobNotFoundError } from "../errors/JobNotFoundError";
15
+ import { JobCannotBePromotedError } from "../errors/JobCannotBePromotedError";
16
+ import { ResultTypes } from "ioredis/built/utils/RedisCommander";
5
17
 
6
18
  const BULL_PREFIX = "b";
7
19
 
8
20
  const DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS = 60 * 60 * 24 * 30;
9
21
  const DEFAULT_COMPLETED_JOB_MAX_COUNT = 500;
22
+ const DEFAULT_FAILED_JOB_MAX_COUNT = 1000;
10
23
 
11
- export class Queue<JobType = JobData> {
24
+ export class Queue<Data = JobData, ReturnValue = JobReturnValue> {
12
25
  private _queue: RawQueue;
13
26
 
14
27
  private name: string;
@@ -26,7 +39,7 @@ export class Queue<JobType = JobData> {
26
39
 
27
40
  this.getSharedContext = getSharedContext;
28
41
 
29
- this._queue = new RawQueue<JobType>(name, {
42
+ this._queue = new RawQueue<Data>(name, {
30
43
  ...merge(
31
44
  {
32
45
  defaultJobOptions: {
@@ -34,6 +47,9 @@ export class Queue<JobType = JobData> {
34
47
  age: DEFAULT_COMPLETED_JOB_MAX_AGE_IN_SECONDS,
35
48
  count: DEFAULT_COMPLETED_JOB_MAX_COUNT,
36
49
  },
50
+ removeOnFail: {
51
+ count: DEFAULT_FAILED_JOB_MAX_COUNT,
52
+ },
37
53
  attempts: 3,
38
54
  backoff: {
39
55
  type: "exponential",
@@ -52,34 +68,109 @@ export class Queue<JobType = JobData> {
52
68
  return this.name;
53
69
  }
54
70
 
55
- async publishJob(job: JobType, opts?: JobsOptions): Promise<Job<JobType>> {
71
+ async publishJob(
72
+ job: Data,
73
+ opts?: JobsOptions
74
+ ): Promise<Job<Data, ReturnValue>> {
56
75
  const context = this.getSharedContext();
57
76
  const lid = context.getLifecycleId();
58
77
  const lch = context.getLifecycleChain();
59
78
  return this.queue.add(this.name, { ...job, lid, lch }, opts);
60
79
  }
61
80
 
62
- async publishBulkJob(jobDatas: JobType[]): Promise<Job<JobType>[]> {
81
+ async publishBulkJob(jobDatas: Data[]): Promise<Job<Data, ReturnValue>[]> {
63
82
  const jobs = jobDatas.map((data) => ({ name: this.name, data }));
64
83
  return this.queue.addBulk(jobs);
65
84
  }
66
85
 
67
86
  async publishBulkJobWithOptions(
68
- jobDefinitions: { jobData: JobType; opts: JobsOptions }[]
69
- ): Promise<void> {
87
+ jobDefinitions: { jobData: Data; opts: JobsOptions }[]
88
+ ): Promise<Job<Data, ReturnValue>[]> {
70
89
  const jobs = jobDefinitions.map(({ jobData: data, opts }) => ({
71
90
  name: this.name,
72
91
  data,
73
92
  opts,
74
93
  }));
75
- await this.queue.addBulk(jobs);
94
+ return this.queue.addBulk(jobs);
95
+ }
96
+
97
+ private pageToStartEnd(pageNumber: number, pageSize: number) {
98
+ return {
99
+ start: (pageNumber - 1) * pageSize,
100
+ end: pageNumber * pageSize - 1,
101
+ };
102
+ }
103
+
104
+ public async getJobsAndCountByStatus(
105
+ statuses: JobTypeEnum[],
106
+ page: number = 1,
107
+ pageSize: number = 50,
108
+ ascending: boolean = false
109
+ ): Promise<ListResult<Job<Data, ReturnValue>>> {
110
+ const { start, end } = this.pageToStartEnd(page, pageSize);
111
+ const [total, elements] = await Promise.all([
112
+ this.queue.getJobCountByTypes(...statuses),
113
+ this.queue.getJobs(statuses, start, end, ascending),
114
+ ]);
115
+
116
+ return {
117
+ total,
118
+ elements,
119
+ page,
120
+ pageSize,
121
+ };
122
+ }
123
+
124
+ public async getJobCountByStatus(
125
+ statuses: JobTypeEnum[],
126
+ page: number = 1,
127
+ pageSize: number = 50
128
+ ): Promise<number> {
129
+ return this.queue.getJobCountByTypes(...statuses);
130
+ }
131
+
132
+ public async getJobsByStatus(
133
+ statuses: JobTypeEnum[],
134
+ page: number = 1,
135
+ pageSize: number = 50,
136
+ ascending: boolean = false
137
+ ): Promise<Job<Data, ReturnValue>[]> {
138
+ const { start, end } = this.pageToStartEnd(page, pageSize);
139
+ return this.queue.getJobs(statuses, start, end, ascending);
140
+ }
141
+
142
+ public async deleteJob(jobId: string) {
143
+ this.queue.remove(jobId);
144
+ }
145
+
146
+ public async getJob(
147
+ jobId: string
148
+ ): Promise<Job<Data, ReturnValue> | undefined> {
149
+ return this.queue.getJob(jobId);
150
+ }
151
+
152
+ public async getJobOrFail(jobId: string): Promise<Job<Data, ReturnValue>> {
153
+ const job = await this.queue.getJob(jobId);
154
+ if (!job) throw new JobNotFoundError(jobId);
155
+ return job;
156
+ }
157
+
158
+ public async changeDelayToJob(jobId: string, delayMs: number) {
159
+ const job = await this.getJobOrFail(jobId);
160
+
161
+ const state = await job.getState();
162
+ if (state !== JobStateEnum.DELAYED) {
163
+ throw new JobCannotBePromotedError(jobId, state);
164
+ }
165
+
166
+ job.changeDelay(delayMs);
76
167
  }
77
168
 
78
169
  async close(): Promise<void> {
79
170
  await this.queue.close();
80
171
  }
81
172
 
82
- private get queue(): RawQueue<JobType> {
173
+ private get queue(): RawQueue<Data, ReturnValue> {
83
174
  return this._queue;
84
175
  }
85
176
  }
@@ -0,0 +1,5 @@
1
+ export interface ITotalCountReporter {
2
+ setTotal(total: number): void;
3
+ increment(n?: number): void;
4
+ reset(value?: number): void;
5
+ }
@@ -0,0 +1,37 @@
1
+ import { Job } from "../../types";
2
+ import { ITotalCountReporter } from "./ITotalCountReporter";
3
+
4
+ export class JobTotalCountReporter<JobInstance extends Job>
5
+ implements ITotalCountReporter
6
+ {
7
+ private total = 1;
8
+
9
+ private current = 0;
10
+
11
+ private job: JobInstance;
12
+
13
+ constructor(job: JobInstance) {
14
+ this.job = job;
15
+ }
16
+
17
+ setTotal(total: number): void {
18
+ this.total = total;
19
+ this.reset();
20
+ }
21
+
22
+ increment(n?: number): void {
23
+ this.current += n ?? 1;
24
+ this.report();
25
+ }
26
+
27
+ reset(value?: number): void {
28
+ this.current = value ?? 0;
29
+ this.report();
30
+ }
31
+
32
+ report() {
33
+ this.job
34
+ .updateProgress({ total: this.total, current: this.current })
35
+ .catch(() => {});
36
+ }
37
+ }
@@ -0,0 +1,20 @@
1
+ import { ITotalCountReporter } from "./ITotalCountReporter";
2
+
3
+ export class MockTotalCountReporter implements ITotalCountReporter {
4
+ private total = 1;
5
+
6
+ private progress = 0;
7
+
8
+ setTotal(total: number): void {
9
+ this.total = total;
10
+ this.reset();
11
+ }
12
+
13
+ increment(n?: number): void {
14
+ this.progress += n ?? 1;
15
+ }
16
+
17
+ reset(value?: number): void {
18
+ this.progress = value ?? 0;
19
+ }
20
+ }
package/src/types.ts CHANGED
@@ -7,9 +7,32 @@ export {
7
7
  WorkerOptions,
8
8
  Queue as RawQueue,
9
9
  QueueOptions,
10
+ JobState,
11
+ JobType,
10
12
  JobsOptions,
11
13
  } from "bullmq";
12
14
 
13
15
  export type JobData = unknown;
14
16
 
15
17
  export type JobReturnValue = unknown;
18
+
19
+ export enum JobStateEnum {
20
+ ACTIVE = "active",
21
+ DELAYED = "delayed",
22
+ WAITING = "waiting",
23
+ COMPLETED = "completed",
24
+ FAILED = "failed",
25
+ WAITING_CHILDREN = "waiting-children",
26
+ }
27
+
28
+ export enum JobTypeEnum {
29
+ ACTIVE = "active",
30
+ DELAYED = "delayed",
31
+ WAITING = "waiting",
32
+ COMPLETED = "completed",
33
+ FAILED = "failed",
34
+ WAITING_CHILDREN = "waiting-children",
35
+ PAUSED = "paused",
36
+ REPEAT = "repeat",
37
+ WAIT = "wait",
38
+ }
@@ -3,7 +3,7 @@ import { compact, isEmpty } from "lodash";
3
3
  import { ConnectionManager } from "../connectionManager";
4
4
  import { Worker, WorkerStatus } from "./worker";
5
5
 
6
- export type WokerStatusWithId = { status: WorkerStatus } & { id: string };
6
+ export type WorkerStatusWithId = { status: WorkerStatus } & { id: string };
7
7
  interface WorkerClass {
8
8
  new (): Worker;
9
9
  }
@@ -32,7 +32,10 @@ export class WorkerRepository {
32
32
  return this._instance;
33
33
  }
34
34
 
35
- public registerWorker(queueName: string, workerClass: WorkerClass): WorkerRepository {
35
+ public registerWorker(
36
+ queueName: string,
37
+ workerClass: WorkerClass
38
+ ): WorkerRepository {
36
39
  Object.assign(this.workers, { [queueName]: workerClass });
37
40
  return this;
38
41
  }
@@ -51,14 +54,19 @@ export class WorkerRepository {
51
54
  this._activeWorkers.push(worker);
52
55
  return worker;
53
56
  }
54
- this.getLogger().warn("worker_repository.invalid_queue_name", { queueName });
57
+ this.getLogger().warn("worker_repository.invalid_queue_name", {
58
+ queueName,
59
+ });
55
60
  return null;
56
61
  })
57
62
  );
58
63
  }
59
64
 
60
- getWorkerStatuses(): WokerStatusWithId[] {
61
- return this.activeWorkers.map((worker) => ({ id: worker.id, status: worker.status }));
65
+ getWorkerStatuses(): WorkerStatusWithId[] {
66
+ return this.activeWorkers.map((worker) => ({
67
+ id: worker.id,
68
+ status: worker.status,
69
+ }));
62
70
  }
63
71
 
64
72
  public getCloseConnections(): Promise<void>[] {