@alterior/tasks 3.5.3 → 3.5.6
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.
- package/package.json +6 -6
- package/src/index.ts +5 -0
- package/src/task-runner.ts +33 -0
- package/src/task-worker.ts +136 -0
- package/src/tasks.module.ts +99 -0
- package/src/tasks.ts +229 -0
- package/dist.esm/test.js +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alterior/tasks",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.6",
|
|
4
4
|
"description": "Flexible background task system",
|
|
5
5
|
"author": "The Alterior Project (https://github.com/alterior-mvc)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"docs": "typedoc ."
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@alterior/annotations": "^3.
|
|
43
|
+
"@alterior/annotations": "^3.5.6",
|
|
44
44
|
"@alterior/common": "^3.4.0",
|
|
45
|
-
"@alterior/di": "^3.5.
|
|
46
|
-
"@alterior/logging": "^3.5.
|
|
47
|
-
"@alterior/runtime": "^3.5.
|
|
45
|
+
"@alterior/di": "^3.5.6",
|
|
46
|
+
"@alterior/logging": "^3.5.6",
|
|
47
|
+
"@alterior/runtime": "^3.5.6",
|
|
48
48
|
"bull": "^3.18.0",
|
|
49
49
|
"kind-of": ">=6.0.3",
|
|
50
50
|
"tslib": "^2.3.1"
|
|
@@ -60,5 +60,5 @@
|
|
|
60
60
|
"swagger-editor-dist": "^3.9.0",
|
|
61
61
|
"wtfnode": "^0.7.0"
|
|
62
62
|
},
|
|
63
|
-
"gitHead": "
|
|
63
|
+
"gitHead": "220e169fc8c855dc0935620bdb5efb1e9a2b86a7"
|
|
64
64
|
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Injectable, Inject } from "@alterior/di";
|
|
2
|
+
import { QUEUE_OPTIONS, TaskJob, TaskAnnotation, TaskModuleOptionsRef, TaskModuleOptions, TaskWorkerRegistry, Constructor, Worker, RemoteWorker, RemoteService } from "./tasks";
|
|
3
|
+
import { InvalidOperationError, ArgumentError } from "@alterior/common";
|
|
4
|
+
|
|
5
|
+
@Injectable()
|
|
6
|
+
export class TaskRunner {
|
|
7
|
+
constructor(
|
|
8
|
+
private _registry : TaskWorkerRegistry
|
|
9
|
+
) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get registry() {
|
|
13
|
+
return this._registry;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Acquire a Remote for the given service where any calls to the remote will
|
|
18
|
+
* resolve to a QueueJob which can be further interacted with. Promise will
|
|
19
|
+
* resolve once the item has been successfully delivered to the event queue.
|
|
20
|
+
*/
|
|
21
|
+
worker<T extends Worker>(workerClass : Constructor<T>): RemoteWorker<T> {
|
|
22
|
+
return this.registry.get<T>(workerClass).async;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Acquire a Remote for the given service where any calls to the remote will await
|
|
27
|
+
* the full completion of the remote call and resolve to the return value of the
|
|
28
|
+
* remote function. If you only want to enqueue a task, use `worker()` instead.
|
|
29
|
+
*/
|
|
30
|
+
service<T extends Worker>(workerClass : Constructor<T>): RemoteService<T> {
|
|
31
|
+
return this.registry.get<T>(workerClass).sync;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { InvalidOperationError, ArgumentError, ArgumentNullError } from "@alterior/common";
|
|
2
|
+
import { Injector, Provider, ReflectiveInjector } from "@alterior/di";
|
|
3
|
+
import { TaskAnnotation, TaskJob, TaskModuleOptions, TaskQueueClient, Worker } from "./tasks";
|
|
4
|
+
import { ApplicationOptions } from "@alterior/runtime";
|
|
5
|
+
import { Type } from "@alterior/runtime";
|
|
6
|
+
import * as Queue from "bull";
|
|
7
|
+
import { Logger } from "@alterior/logging";
|
|
8
|
+
import * as util from 'util';
|
|
9
|
+
|
|
10
|
+
export interface TaskHandler {
|
|
11
|
+
worker : Worker;
|
|
12
|
+
handler : (methodName : string, args : any[]) => Promise<any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export class TaskWorker {
|
|
16
|
+
constructor(
|
|
17
|
+
private _injector : Injector,
|
|
18
|
+
private _client : TaskQueueClient,
|
|
19
|
+
private _options : TaskModuleOptions,
|
|
20
|
+
private _appOptions : ApplicationOptions,
|
|
21
|
+
private _logger : Logger
|
|
22
|
+
) {
|
|
23
|
+
if (!_injector)
|
|
24
|
+
throw new ArgumentNullError(`injector`);
|
|
25
|
+
|
|
26
|
+
if (!_options)
|
|
27
|
+
throw new ArgumentNullError(`options`);
|
|
28
|
+
|
|
29
|
+
if (!_appOptions)
|
|
30
|
+
throw new ArgumentNullError(`appOptions`);
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public get injector() {
|
|
35
|
+
return this._injector;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public get options() {
|
|
39
|
+
return this._options;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public get appOptions() {
|
|
43
|
+
return this._appOptions;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private _taskHandlers = {};
|
|
47
|
+
|
|
48
|
+
get queue() {
|
|
49
|
+
return this._client.queue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
stop() {
|
|
53
|
+
this.queue.close();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
start() {
|
|
57
|
+
this.queue.process(async (job : Queue.Job<TaskJob>, done) => {
|
|
58
|
+
let task = job.data;
|
|
59
|
+
|
|
60
|
+
if (!task || !task.id) {
|
|
61
|
+
console.log(`TaskWorker: Could not process invalid task:`);
|
|
62
|
+
console.dir(task, { depth: 3 });
|
|
63
|
+
console.log(`Associated job data:`);
|
|
64
|
+
console.dir(job, { depth: 3 });
|
|
65
|
+
|
|
66
|
+
await job.discard();
|
|
67
|
+
|
|
68
|
+
done(new Error(`Invalid job task`), null);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let handler : TaskHandler = this._taskHandlers[task.id];
|
|
72
|
+
|
|
73
|
+
if (!handler) {
|
|
74
|
+
console.error(`No handler for task ID '${task.id}'! Check that your worker class declares this task ID!`);
|
|
75
|
+
|
|
76
|
+
console.info(`Task was: `);
|
|
77
|
+
console.dir(task, { depth: 3 });
|
|
78
|
+
|
|
79
|
+
console.info(`Registered worker IDs: ${Object.keys(this._taskHandlers).join(', ')}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
await this._logger.withContext(
|
|
83
|
+
{ host: 'tasks', worker: handler.worker },
|
|
84
|
+
`TaskWorker | ${handler.worker.constructor.name}.${task.method}(${task.args.map(x => util.inspect(x, false, 2)).join(', ')})`,
|
|
85
|
+
async () => {
|
|
86
|
+
this._logger.info(`TaskWorker: ${task.method}() of worker ${handler.worker.constructor.name} (ID '${task.id}')`);
|
|
87
|
+
try {
|
|
88
|
+
let result = await handler.handler(task.method, task.args);
|
|
89
|
+
done(undefined, result);
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error(`Caught error while running task ${job.data.id}.${job.data.method || 'execute'}():`);
|
|
92
|
+
console.error(e);
|
|
93
|
+
|
|
94
|
+
done(e);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
registerHandler(name : string, handler : TaskHandler) {
|
|
102
|
+
this._taskHandlers[name] = handler;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
registerClasses(taskClasses : Function[]) {
|
|
106
|
+
|
|
107
|
+
let providers : Provider[] = taskClasses as Provider[];
|
|
108
|
+
let ownInjector = ReflectiveInjector.resolveAndCreate(providers, this.injector);
|
|
109
|
+
let allRoutes = [];
|
|
110
|
+
|
|
111
|
+
let tasks = taskClasses.map(taskClass => {
|
|
112
|
+
|
|
113
|
+
let id = taskClass.name;
|
|
114
|
+
let annotation = TaskAnnotation.getForClass(taskClass);
|
|
115
|
+
let instance : Worker = ownInjector.get(taskClass);
|
|
116
|
+
|
|
117
|
+
if (instance.name)
|
|
118
|
+
id = instance.name;
|
|
119
|
+
|
|
120
|
+
if (annotation && annotation.id)
|
|
121
|
+
id = annotation.id;
|
|
122
|
+
|
|
123
|
+
this.registerHandler(id, {
|
|
124
|
+
worker: instance,
|
|
125
|
+
handler: async (methodName, args) => {
|
|
126
|
+
let impl = instance.constructor.prototype[methodName];
|
|
127
|
+
|
|
128
|
+
if (typeof impl !== 'function' || methodName.startsWith('_'))
|
|
129
|
+
throw new InvalidOperationError(`Invalid task method ${methodName}`);
|
|
130
|
+
|
|
131
|
+
return await instance[methodName](...args);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Module, Injectable, Optional } from "@alterior/di";
|
|
2
|
+
import { OnInit, Application, RolesService, Constructor } from "@alterior/runtime";
|
|
3
|
+
import * as Queue from "bull";
|
|
4
|
+
import { TaskModuleOptions, TaskModuleOptionsRef, TaskQueueClient, TaskWorkerRegistry } from "./tasks";
|
|
5
|
+
import { TaskRunner } from "./task-runner";
|
|
6
|
+
import { TaskWorker } from "./task-worker";
|
|
7
|
+
import { Logger, LoggingModule } from "@alterior/logging";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Import this into your application module to run tasks enqueued by other
|
|
11
|
+
* services on a shared queue. The tasks which can be processed are specified
|
|
12
|
+
* in the `tasks` field of one or more modules.
|
|
13
|
+
*/
|
|
14
|
+
@Module({
|
|
15
|
+
providers: [
|
|
16
|
+
TaskQueueClient,
|
|
17
|
+
TaskWorkerRegistry,
|
|
18
|
+
TaskRunner
|
|
19
|
+
],
|
|
20
|
+
imports: [
|
|
21
|
+
LoggingModule
|
|
22
|
+
]
|
|
23
|
+
})
|
|
24
|
+
export class TasksModule implements OnInit {
|
|
25
|
+
constructor(
|
|
26
|
+
private app : Application,
|
|
27
|
+
private rolesService : RolesService,
|
|
28
|
+
private client : TaskQueueClient,
|
|
29
|
+
private workerRegistry : TaskWorkerRegistry,
|
|
30
|
+
private logger : Logger,
|
|
31
|
+
@Optional() private _options : TaskModuleOptionsRef
|
|
32
|
+
) {
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Used when importing this module from the root (app) module
|
|
38
|
+
* using the default configuration.
|
|
39
|
+
* Should be called only once in the application.
|
|
40
|
+
*/
|
|
41
|
+
public static forRoot() {
|
|
42
|
+
return this.configure({});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create a configured version of the WebServerModule that can be then
|
|
47
|
+
* be imported into an entry module (or feature module).
|
|
48
|
+
* @param options The options to use for the web server
|
|
49
|
+
*/
|
|
50
|
+
public static configure(options : TaskModuleOptions) {
|
|
51
|
+
return {
|
|
52
|
+
$module: TasksModule,
|
|
53
|
+
providers: [
|
|
54
|
+
{ provide: TaskModuleOptionsRef, useValue: new TaskModuleOptionsRef(options) }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
worker : TaskWorker;
|
|
60
|
+
|
|
61
|
+
get options(): TaskModuleOptions {
|
|
62
|
+
return this._options ? this._options.options : {} || {};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get tasks(): Function[] {
|
|
66
|
+
return [].concat(...this.app.runtime.definitions.map(x => x.metadata.tasks || []));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
altOnInit() {
|
|
70
|
+
|
|
71
|
+
this.workerRegistry.registerClasses(this.tasks);
|
|
72
|
+
|
|
73
|
+
this.worker = new TaskWorker(
|
|
74
|
+
this.app.runtime.injector,
|
|
75
|
+
this.client,
|
|
76
|
+
this.options,
|
|
77
|
+
this.app.options,
|
|
78
|
+
this.logger
|
|
79
|
+
);
|
|
80
|
+
this.worker.registerClasses(this.tasks);
|
|
81
|
+
|
|
82
|
+
let self = this;
|
|
83
|
+
|
|
84
|
+
this.rolesService.registerRole({
|
|
85
|
+
identifier: 'task-worker',
|
|
86
|
+
instance: this,
|
|
87
|
+
name: 'Task Worker',
|
|
88
|
+
summary: 'Pulls from the task queue and executes them using task classes registered in the module tree',
|
|
89
|
+
async start() {
|
|
90
|
+
self.worker.start();
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
async stop() {
|
|
94
|
+
self.worker.stop();
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
}
|
|
99
|
+
}
|
package/src/tasks.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { Annotation, MetadataName, AnnotationDecorator } from "@alterior/annotations";
|
|
2
|
+
import { Injectable, InjectionToken, Optional, Injector, Provider, ReflectiveInjector } from "@alterior/di";
|
|
3
|
+
import BullQueue from "bull";
|
|
4
|
+
|
|
5
|
+
export interface TaskModuleOptions {
|
|
6
|
+
queueName? : string;
|
|
7
|
+
queueOptions? : BullQueue.QueueOptions;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* This injectable allows configuration of the task system.
|
|
13
|
+
* Include a provider for the injection token `QUEUE_OPTIONS`
|
|
14
|
+
* which provides an instance of this class.
|
|
15
|
+
*
|
|
16
|
+
* For instance: `[ provide: QUEUE_OPTIONS, useValue: new TaskClientOptionsRef({ optionsHere }) ]`
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
@Injectable()
|
|
20
|
+
export class TaskModuleOptionsRef {
|
|
21
|
+
constructor(options : TaskModuleOptions) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public options : TaskModuleOptions;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TaskJob {
|
|
29
|
+
id : string;
|
|
30
|
+
method : string;
|
|
31
|
+
args : any[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const QUEUE_OPTIONS = new InjectionToken<BullQueue.QueueOptions>('QueueOptions');
|
|
35
|
+
|
|
36
|
+
@MetadataName('@alterior/tasks:Task')
|
|
37
|
+
export class TaskAnnotation extends Annotation {
|
|
38
|
+
constructor(readonly id? : string) {
|
|
39
|
+
super();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Task = TaskAnnotation.decorator();
|
|
44
|
+
|
|
45
|
+
export abstract class Worker {
|
|
46
|
+
abstract get name() : string;
|
|
47
|
+
get options() : JobOptions { return undefined; }
|
|
48
|
+
|
|
49
|
+
protected get currentJob() : QueueJob<TaskJob> {
|
|
50
|
+
return Zone.current.get('workerStateJob');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type RemoteService<T> = {
|
|
55
|
+
[P in keyof T]:
|
|
56
|
+
T[P] extends (...args: any[]) => any ?
|
|
57
|
+
// methods
|
|
58
|
+
(ReturnType<T[P]> extends Promise<any> ?
|
|
59
|
+
T[P] // dont modify methods that are already promises
|
|
60
|
+
: (...args : Parameters<T[P]>) => Promise<ReturnType<T[P]>>
|
|
61
|
+
)
|
|
62
|
+
// fields
|
|
63
|
+
: never
|
|
64
|
+
;
|
|
65
|
+
} & {
|
|
66
|
+
withOptions(options : JobOptions) : RemoteService<T>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export type RemoteWorker<T> = {
|
|
70
|
+
[P in keyof T]:
|
|
71
|
+
T[P] extends (...args: any[]) => any ?
|
|
72
|
+
// methods
|
|
73
|
+
(...args : Parameters<T[P]>) => Promise<QueueJob<TaskJob>>
|
|
74
|
+
|
|
75
|
+
// fields
|
|
76
|
+
: never
|
|
77
|
+
;
|
|
78
|
+
} & {
|
|
79
|
+
withOptions(options : JobOptions) : RemoteWorker<T>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export type QueueOptions = BullQueue.QueueOptions;
|
|
83
|
+
export type JobOptions = BullQueue.JobOptions;
|
|
84
|
+
export type QueueJob<T> = BullQueue.Job<T>;
|
|
85
|
+
export type Queue<T> = BullQueue.Queue<T>;
|
|
86
|
+
|
|
87
|
+
export interface Constructor<T> {
|
|
88
|
+
new (...args) : T;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type PromiseWrap<T> = T extends PromiseLike<any> ? T : Promise<T>;
|
|
92
|
+
|
|
93
|
+
export class TaskWorkerProxy {
|
|
94
|
+
private static create<T extends Worker>(handler : (key, ...args) => any): any {
|
|
95
|
+
return <RemoteWorker<T>> new Proxy({}, {
|
|
96
|
+
get(t, key : string, receiver) {
|
|
97
|
+
return (...args) => handler(key, ...args);
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static createAsync<T extends Worker>(queueClient : TaskQueueClient, id : string, options? : JobOptions): RemoteWorker<T> {
|
|
103
|
+
return this.create(
|
|
104
|
+
(method, ...args) => {
|
|
105
|
+
if (method === 'withOptions')
|
|
106
|
+
return TaskWorkerProxy.createAsync(queueClient, id, Object.assign({}, options, args[0]));
|
|
107
|
+
else
|
|
108
|
+
return queueClient.enqueue({ id, method, args }, options)
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static createSync<T extends Worker>(queueClient : TaskQueueClient, id : string, options? : JobOptions): RemoteService<T> {
|
|
114
|
+
return this.create(
|
|
115
|
+
(method, ...args) => {
|
|
116
|
+
if (method === 'withOptions')
|
|
117
|
+
return TaskWorkerProxy.createSync(queueClient, id, Object.assign({}, options, args[0]));
|
|
118
|
+
else
|
|
119
|
+
return queueClient.enqueue({ id, method, args }, options).then(v => v.finished);
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
interface TaskWorkerEntry<T extends Worker = any> {
|
|
126
|
+
type : Constructor<T>;
|
|
127
|
+
local : T;
|
|
128
|
+
async : RemoteWorker<T>;
|
|
129
|
+
sync : RemoteService<T>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@Injectable()
|
|
134
|
+
export class TaskQueueClient {
|
|
135
|
+
constructor(
|
|
136
|
+
@Optional()
|
|
137
|
+
private optionsRef : TaskModuleOptionsRef
|
|
138
|
+
) {
|
|
139
|
+
this._queue = new BullQueue(this.queueName, this.queueOptions);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
_queue : BullQueue.Queue;
|
|
143
|
+
|
|
144
|
+
get queue(): Queue<TaskJob> {
|
|
145
|
+
return this._queue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get the task client options. See
|
|
150
|
+
*/
|
|
151
|
+
get options(): TaskModuleOptions {
|
|
152
|
+
return (this.optionsRef ? this.optionsRef.options : undefined) || {};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get queueName(): string {
|
|
156
|
+
return this.options.queueName || 'alteriorTasks';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get queueOptions() {
|
|
160
|
+
let queueOptions = Object.assign({}, this.options.queueOptions);
|
|
161
|
+
|
|
162
|
+
if (!queueOptions.redis) {
|
|
163
|
+
queueOptions.redis = {
|
|
164
|
+
port: 6379,
|
|
165
|
+
host: '127.0.0.1',
|
|
166
|
+
db: 6
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return queueOptions;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Enqueue a new task. To handle the task on the worker side, register for it with `.process()`
|
|
175
|
+
*/
|
|
176
|
+
async enqueue(data : TaskJob, opts? : JobOptions): Promise<QueueJob<TaskJob>> {
|
|
177
|
+
return await this._queue.add(data, opts);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@Injectable()
|
|
182
|
+
export class TaskWorkerRegistry {
|
|
183
|
+
constructor(
|
|
184
|
+
private injector : Injector,
|
|
185
|
+
private client : TaskQueueClient
|
|
186
|
+
) {
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private _entries : { [name : string] : TaskWorkerEntry } = {};
|
|
191
|
+
|
|
192
|
+
private registerClass(injector : Injector, taskClass : Constructor<Worker>) {
|
|
193
|
+
let instance = <Worker> injector.get(taskClass);
|
|
194
|
+
let id = instance.name;
|
|
195
|
+
|
|
196
|
+
if (this._entries[id])
|
|
197
|
+
throw new Error(`Another worker is already registered with name '${id}'`);
|
|
198
|
+
|
|
199
|
+
this._entries[id] = {
|
|
200
|
+
type: <any> taskClass,
|
|
201
|
+
local: instance,
|
|
202
|
+
sync: TaskWorkerProxy.createSync(this.client, id, instance.options),
|
|
203
|
+
async: TaskWorkerProxy.createAsync(this.client, id, instance.options)
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get all() : TaskWorkerEntry[] {
|
|
208
|
+
return Object.values(this._entries);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get<T extends Worker>(cls : Constructor<T>): TaskWorkerEntry<T> {
|
|
212
|
+
let entry = this.all.find(x => x.type === cls);
|
|
213
|
+
|
|
214
|
+
if (!entry)
|
|
215
|
+
throw new Error(`Worker class ${cls.name} is not registered. Add it to the 'tasks' property of a module or call TaskWorkerRegistry.register(${cls.name})`);
|
|
216
|
+
|
|
217
|
+
return <TaskWorkerEntry<T>> entry;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
getByName(name : string) {
|
|
221
|
+
return this._entries[name];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
registerClasses(classes : Function[]) {
|
|
225
|
+
let taskClasses : Constructor<Worker>[] = classes as any;
|
|
226
|
+
let ownInjector = ReflectiveInjector.resolveAndCreate(taskClasses as Provider[], this.injector);
|
|
227
|
+
taskClasses.forEach(taskClass => this.registerClass(ownInjector, taskClass));
|
|
228
|
+
}
|
|
229
|
+
}
|