@adonisjs/queue 0.2.0 → 0.2.2
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/build/commands/queue_work.js +8 -0
- package/build/providers/queue_provider.js +2 -12
- package/build/src/utils.d.ts +14 -0
- package/build/src/utils.js +29 -0
- package/build/stubs/make/job/main.stub +2 -2
- package/build/stubs/migration.stub +2 -2
- package/build/tests/utils.spec.d.ts +1 -0
- package/build/tests/utils.spec.js +148 -0
- package/package.json +5 -5
|
@@ -13,6 +13,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
13
13
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
14
14
|
};
|
|
15
15
|
import { flags, BaseCommand } from '@adonisjs/core/ace';
|
|
16
|
+
import { resolveAdapters } from '../src/utils.js';
|
|
16
17
|
export default class QueueWork extends BaseCommand {
|
|
17
18
|
static commandName = 'queue:work';
|
|
18
19
|
static description = 'Start processing jobs from the queue';
|
|
@@ -30,10 +31,17 @@ export default class QueueWork extends BaseCommand {
|
|
|
30
31
|
*/
|
|
31
32
|
const router = await this.app.container.make('router');
|
|
32
33
|
router.commit();
|
|
34
|
+
const resolvedAdapters = await resolveAdapters(config, this.app);
|
|
33
35
|
const queues = this.queue ? this.queue.split(',').map((q) => q.trim()) : ['default'];
|
|
34
36
|
this.logger.info(`Starting worker for queues: ${queues.join(', ')}`);
|
|
37
|
+
const jobFactory = config.jobFactory ??
|
|
38
|
+
(async (JobClass) => {
|
|
39
|
+
return this.app.container.make(JobClass);
|
|
40
|
+
});
|
|
35
41
|
const worker = new Worker({
|
|
36
42
|
...config,
|
|
43
|
+
adapters: resolvedAdapters,
|
|
44
|
+
jobFactory,
|
|
37
45
|
...(this.concurrency && { concurrency: this.concurrency }),
|
|
38
46
|
});
|
|
39
47
|
await worker.start(queues);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* file that was distributed with this source code.
|
|
8
8
|
*/
|
|
9
9
|
import '../src/types/extended.js';
|
|
10
|
+
import { resolveAdapters } from '../src/utils.js';
|
|
10
11
|
export default class QueueProvider {
|
|
11
12
|
app;
|
|
12
13
|
constructor(app) {
|
|
@@ -16,18 +17,7 @@ export default class QueueProvider {
|
|
|
16
17
|
this.app.container.singleton('queue.manager', async () => {
|
|
17
18
|
const { QueueManager } = await import('@boringnode/queue');
|
|
18
19
|
const config = this.app.config.get('queue');
|
|
19
|
-
|
|
20
|
-
* Resolve adapter factories from config providers
|
|
21
|
-
*/
|
|
22
|
-
const resolvedAdapters = {};
|
|
23
|
-
for (const [name, adapterConfig] of Object.entries(config.adapters)) {
|
|
24
|
-
if (typeof adapterConfig === 'function') {
|
|
25
|
-
resolvedAdapters[name] = adapterConfig;
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
resolvedAdapters[name] = await adapterConfig.resolver(this.app);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
20
|
+
const resolvedAdapters = await resolveAdapters(config, this.app);
|
|
31
21
|
/**
|
|
32
22
|
* Inject jobFactory if not already defined.
|
|
33
23
|
* This enables automatic dependency injection for job classes.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ApplicationService } from '@adonisjs/core/types';
|
|
2
|
+
import type { QueueConfig } from './types/main.js';
|
|
3
|
+
type AdapterFactory = () => any;
|
|
4
|
+
/**
|
|
5
|
+
* Resolve adapter factories from config providers.
|
|
6
|
+
*
|
|
7
|
+
* Adapters in the config can be either:
|
|
8
|
+
* - Direct factory functions
|
|
9
|
+
* - ConfigProvider objects that need to be resolved
|
|
10
|
+
*
|
|
11
|
+
* This function normalizes them all to factory functions.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveAdapters(config: QueueConfig, app: ApplicationService): Promise<Record<string, AdapterFactory>>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @adonisjs/queue
|
|
3
|
+
*
|
|
4
|
+
* (c) AdonisJS
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Resolve adapter factories from config providers.
|
|
11
|
+
*
|
|
12
|
+
* Adapters in the config can be either:
|
|
13
|
+
* - Direct factory functions
|
|
14
|
+
* - ConfigProvider objects that need to be resolved
|
|
15
|
+
*
|
|
16
|
+
* This function normalizes them all to factory functions.
|
|
17
|
+
*/
|
|
18
|
+
export async function resolveAdapters(config, app) {
|
|
19
|
+
const resolvedAdapters = {};
|
|
20
|
+
for (const [name, adapterConfig] of Object.entries(config.adapters)) {
|
|
21
|
+
if (typeof adapterConfig === 'function') {
|
|
22
|
+
resolvedAdapters[name] = adapterConfig;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
resolvedAdapters[name] = await adapterConfig.resolver(app);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return resolvedAdapters;
|
|
29
|
+
}
|
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
})
|
|
7
7
|
}}}
|
|
8
8
|
import { Job } from '@adonisjs/queue'
|
|
9
|
-
import type {
|
|
9
|
+
import type { JobOptions } from '@adonisjs/queue/types'
|
|
10
10
|
|
|
11
11
|
interface {{ jobName }}Payload {
|
|
12
12
|
// Define your payload type here
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export default class {{ jobName }} extends Job<{{ jobName }}Payload> {
|
|
16
|
-
static options:
|
|
16
|
+
static options: JobOptions = {
|
|
17
17
|
queue: 'default',
|
|
18
18
|
maxRetries: 3,
|
|
19
19
|
}
|
|
@@ -13,7 +13,7 @@ export default class extends BaseSchema {
|
|
|
13
13
|
this.schema.createTable('queue_jobs', (table) => {
|
|
14
14
|
table.string('id', 255).notNullable()
|
|
15
15
|
table.string('queue', 255).notNullable()
|
|
16
|
-
table.
|
|
16
|
+
table.enu('status', ['pending', 'active', 'delayed']).notNullable()
|
|
17
17
|
table.text('data').notNullable()
|
|
18
18
|
table.bigint('score').unsigned().nullable()
|
|
19
19
|
table.string('worker_id', 255).nullable()
|
|
@@ -41,7 +41,7 @@ export default class extends BaseSchema {
|
|
|
41
41
|
table.integer('run_count').unsigned().notNullable().defaultTo(0)
|
|
42
42
|
table.timestamp('next_run_at').nullable()
|
|
43
43
|
table.timestamp('last_run_at').nullable()
|
|
44
|
-
table.timestamp('created_at').notNullable()
|
|
44
|
+
table.timestamp('created_at').notNullable().defaultTo(this.#connection.fn.now())
|
|
45
45
|
table.index(['status', 'next_run_at'])
|
|
46
46
|
})
|
|
47
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @adonisjs/queue
|
|
3
|
+
*
|
|
4
|
+
* (c) AdonisJS
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
10
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
11
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
12
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return path;
|
|
16
|
+
};
|
|
17
|
+
import { test } from '@japa/runner';
|
|
18
|
+
import { IgnitorFactory } from '@adonisjs/core/factories';
|
|
19
|
+
import { defineConfig, drivers } from '../index.js';
|
|
20
|
+
import { resolveAdapters } from '../src/utils.js';
|
|
21
|
+
const BASE_URL = new URL('./tmp/', import.meta.url);
|
|
22
|
+
test.group('resolveAdapters', () => {
|
|
23
|
+
test('should resolve config providers to adapter factories', async ({ assert }) => {
|
|
24
|
+
const ignitor = new IgnitorFactory()
|
|
25
|
+
.withCoreProviders()
|
|
26
|
+
.withCoreConfig()
|
|
27
|
+
.merge({
|
|
28
|
+
config: {
|
|
29
|
+
queue: defineConfig({
|
|
30
|
+
default: 'sync',
|
|
31
|
+
adapters: {
|
|
32
|
+
sync: drivers.sync(),
|
|
33
|
+
},
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
rcFileContents: {
|
|
37
|
+
providers: [() => import('../providers/queue_provider.js')],
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
.create(BASE_URL, {
|
|
41
|
+
importer: (filePath) => {
|
|
42
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
43
|
+
return import(__rewriteRelativeImportExtension(new URL(filePath, BASE_URL).href));
|
|
44
|
+
}
|
|
45
|
+
return import(__rewriteRelativeImportExtension(filePath));
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
const app = ignitor.createApp('console');
|
|
49
|
+
await app.init().then(() => app.boot());
|
|
50
|
+
const config = app.config.get('queue');
|
|
51
|
+
/**
|
|
52
|
+
* Before resolution, the adapter is a ConfigProvider (object with resolver method)
|
|
53
|
+
*/
|
|
54
|
+
assert.isObject(config.adapters.sync);
|
|
55
|
+
assert.isFunction(config.adapters.sync.resolver);
|
|
56
|
+
/**
|
|
57
|
+
* After resolution, the adapter should be a factory function
|
|
58
|
+
*/
|
|
59
|
+
const resolvedAdapters = await resolveAdapters(config, app);
|
|
60
|
+
assert.isFunction(resolvedAdapters.sync);
|
|
61
|
+
await app.terminate();
|
|
62
|
+
});
|
|
63
|
+
test('should pass through direct factory functions unchanged', async ({ assert }) => {
|
|
64
|
+
const directFactory = () => ({
|
|
65
|
+
pushOn: async () => { },
|
|
66
|
+
pushLaterOn: async () => { },
|
|
67
|
+
pop: async () => null,
|
|
68
|
+
acknowledge: async () => { },
|
|
69
|
+
fail: async () => { },
|
|
70
|
+
getFailedJobs: async () => [],
|
|
71
|
+
removeFailedJob: async () => { },
|
|
72
|
+
clearFailedJobs: async () => { },
|
|
73
|
+
destroy: async () => { },
|
|
74
|
+
});
|
|
75
|
+
const ignitor = new IgnitorFactory()
|
|
76
|
+
.withCoreProviders()
|
|
77
|
+
.withCoreConfig()
|
|
78
|
+
.merge({
|
|
79
|
+
config: {
|
|
80
|
+
queue: defineConfig({
|
|
81
|
+
default: 'custom',
|
|
82
|
+
adapters: {
|
|
83
|
+
custom: directFactory,
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
},
|
|
87
|
+
rcFileContents: {
|
|
88
|
+
providers: [() => import('../providers/queue_provider.js')],
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
.create(BASE_URL, {
|
|
92
|
+
importer: (filePath) => {
|
|
93
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
94
|
+
return import(__rewriteRelativeImportExtension(new URL(filePath, BASE_URL).href));
|
|
95
|
+
}
|
|
96
|
+
return import(__rewriteRelativeImportExtension(filePath));
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
const app = ignitor.createApp('console');
|
|
100
|
+
await app.init().then(() => app.boot());
|
|
101
|
+
const config = app.config.get('queue');
|
|
102
|
+
const resolvedAdapters = await resolveAdapters(config, app);
|
|
103
|
+
assert.strictEqual(resolvedAdapters.custom, directFactory);
|
|
104
|
+
await app.terminate();
|
|
105
|
+
});
|
|
106
|
+
test('worker should accept resolved adapters without throwing', async ({ assert }) => {
|
|
107
|
+
const ignitor = new IgnitorFactory()
|
|
108
|
+
.withCoreProviders()
|
|
109
|
+
.withCoreConfig()
|
|
110
|
+
.merge({
|
|
111
|
+
config: {
|
|
112
|
+
queue: defineConfig({
|
|
113
|
+
default: 'sync',
|
|
114
|
+
adapters: {
|
|
115
|
+
sync: drivers.sync(),
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
},
|
|
119
|
+
rcFileContents: {
|
|
120
|
+
providers: [() => import('../providers/queue_provider.js')],
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
.create(BASE_URL, {
|
|
124
|
+
importer: (filePath) => {
|
|
125
|
+
if (filePath.startsWith('./') || filePath.startsWith('../')) {
|
|
126
|
+
return import(__rewriteRelativeImportExtension(new URL(filePath, BASE_URL).href));
|
|
127
|
+
}
|
|
128
|
+
return import(__rewriteRelativeImportExtension(filePath));
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
const app = ignitor.createApp('console');
|
|
132
|
+
await app.init().then(() => app.boot());
|
|
133
|
+
const config = app.config.get('queue');
|
|
134
|
+
const resolvedAdapters = await resolveAdapters(config, app);
|
|
135
|
+
/**
|
|
136
|
+
* Creating a Worker with resolved adapters should not throw
|
|
137
|
+
* "Adapter must be a factory function" error
|
|
138
|
+
*/
|
|
139
|
+
const { Worker } = await import('@boringnode/queue');
|
|
140
|
+
assert.doesNotThrow(() => {
|
|
141
|
+
new Worker({
|
|
142
|
+
...config,
|
|
143
|
+
adapters: resolvedAdapters,
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
await app.terminate();
|
|
147
|
+
});
|
|
148
|
+
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonisjs/queue",
|
|
3
3
|
"description": "Queue system for AdonisJS powered by @boringnode/queue",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.11.1"
|
|
7
7
|
},
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"typescript": "^5.8.2"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@adonisjs/assembler": "^7.0.0 || ^8.0.0",
|
|
65
|
-
"@adonisjs/core": "^6.2.0 || ^7.0.0",
|
|
66
|
-
"@adonisjs/lucid": "^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
67
|
-
"@adonisjs/redis": "^8.0.0 || ^9.0.0 || ^10.0.0"
|
|
64
|
+
"@adonisjs/assembler": "^7.0.0 || ^8.0.0 || ^8.0.0-next",
|
|
65
|
+
"@adonisjs/core": "^6.2.0 || ^7.0.0 || ^7.0.0-next",
|
|
66
|
+
"@adonisjs/lucid": "^20.0.0 || ^21.0.0 || ^22.0.0 || ^22.0.0-next",
|
|
67
|
+
"@adonisjs/redis": "^8.0.0 || ^9.0.0 || ^10.0.0 || ^10.0.0-next"
|
|
68
68
|
},
|
|
69
69
|
"peerDependenciesMeta": {
|
|
70
70
|
"@adonisjs/lucid": {
|