mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-09-10 22:14:48 -05:00
feat(task): info, set and warning are now instance methods
Info, properties and warnings should be set on a specific/known task instead of relying on the async context. For instance, this permits updating the progress of a task from the code on one of its subtasks.
This commit is contained in:
@@ -3,6 +3,10 @@
|
||||
const assert = require('node:assert').strict
|
||||
const { AsyncLocalStorage } = require('node:async_hooks')
|
||||
|
||||
function alreadyEnded() {
|
||||
throw new Error('task has already ended')
|
||||
}
|
||||
|
||||
// define a read-only, non-enumerable, non-configurable property
|
||||
function define(object, property, value) {
|
||||
Object.defineProperty(object, property, { value })
|
||||
@@ -21,7 +25,7 @@ const asyncStorage = global[asyncStorageKey] ?? (global[asyncStorageKey] = new A
|
||||
|
||||
const getTask = () => asyncStorage.getStore()
|
||||
|
||||
exports.Task = class Task {
|
||||
class Task {
|
||||
static get abortSignal() {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
@@ -29,13 +33,6 @@ exports.Task = class Task {
|
||||
}
|
||||
}
|
||||
|
||||
static info(message, data) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('info', { data, message })
|
||||
}
|
||||
}
|
||||
|
||||
static run(...args) {
|
||||
let opts = args[0]
|
||||
let fn
|
||||
@@ -57,20 +54,6 @@ exports.Task = class Task {
|
||||
return new Task(opts).run(() => fn.apply(thisArg, args))
|
||||
}
|
||||
|
||||
static set(name, value) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('property', { name, value })
|
||||
}
|
||||
}
|
||||
|
||||
static warning(message, data) {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
task.#emit('warning', { data, message })
|
||||
}
|
||||
}
|
||||
|
||||
static wrap(opts, fn) {
|
||||
// compatibility with @decorateWith
|
||||
if (typeof fn !== 'function') {
|
||||
@@ -127,9 +110,7 @@ exports.Task = class Task {
|
||||
this.#emit('abortionRequested', { reason: signal.reason })
|
||||
|
||||
if (!this.#running) {
|
||||
const status = FAILURE
|
||||
this.#status = status
|
||||
this.#emit('end', { result: signal.reason, status })
|
||||
this.#end(FAILURE, signal.reason)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -140,12 +121,26 @@ exports.Task = class Task {
|
||||
}
|
||||
|
||||
#emit(type, data) {
|
||||
assert.equal(this.#startData, undefined, 'task has not started yet')
|
||||
|
||||
data.id = this.id
|
||||
data.timestamp = Date.now()
|
||||
data.type = type
|
||||
this.#onProgress(data)
|
||||
}
|
||||
|
||||
#end(status, result) {
|
||||
assert.equal(this.#status, PENDING)
|
||||
|
||||
this.#status = status
|
||||
this.#emit('end', { status, result })
|
||||
this.#onProgress = alreadyEnded
|
||||
}
|
||||
|
||||
info(message, data) {
|
||||
this.#emit('info', { data, message })
|
||||
}
|
||||
|
||||
#maybeStart() {
|
||||
const startData = this.#startData
|
||||
if (startData !== undefined) {
|
||||
@@ -157,8 +152,7 @@ exports.Task = class Task {
|
||||
async run(fn) {
|
||||
const result = await this.runInside(fn)
|
||||
if (this.status === PENDING) {
|
||||
this.#status = SUCCESS
|
||||
this.#emit('end', { status: SUCCESS, result })
|
||||
this.#end(SUCCESS, result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@@ -172,18 +166,27 @@ exports.Task = class Task {
|
||||
this.#maybeStart()
|
||||
|
||||
try {
|
||||
const result = await asyncStorage.run(this, fn)
|
||||
const result = await asyncStorage.run(this, fn, this)
|
||||
this.#running = false
|
||||
return result
|
||||
} catch (result) {
|
||||
const status = FAILURE
|
||||
|
||||
this.#status = status
|
||||
this.#emit('end', { status, result })
|
||||
this.#end(FAILURE, result)
|
||||
throw result
|
||||
}
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
assert.equal(this.status, PENDING)
|
||||
|
||||
this.#emit('property', { name, value })
|
||||
}
|
||||
|
||||
warning(message, data) {
|
||||
assert.equal(this.status, PENDING)
|
||||
|
||||
this.#emit('warning', { data, message })
|
||||
}
|
||||
|
||||
wrap(fn) {
|
||||
const task = this
|
||||
return function taskRun() {
|
||||
@@ -198,3 +201,15 @@ exports.Task = class Task {
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Task = Task
|
||||
|
||||
// setup static aliases
|
||||
for (const name of ['info', 'set', 'warning']) {
|
||||
const method = Task.prototype[name]
|
||||
Task[name] = function () {
|
||||
const task = getTask()
|
||||
if (task !== undefined) {
|
||||
return method.apply(task, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,31 @@ describe('Task', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#set()', function () {
|
||||
const name = 'progress'
|
||||
const value = 10
|
||||
|
||||
it('throws when the task is not started', function () {
|
||||
const task = createTask()
|
||||
assert.throws(() => task.set(name, value), { message: 'task has not started yet' })
|
||||
})
|
||||
|
||||
it(`emits an property message`, async function () {
|
||||
const task = createTask()
|
||||
await task.run(async () => {
|
||||
await Task.run(() => {
|
||||
task.set(name, value)
|
||||
|
||||
assertEvent(task, {
|
||||
name,
|
||||
type: 'property',
|
||||
value,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('.warning()', function () {
|
||||
it('does nothing when run outside a task', function () {
|
||||
Task.warning('foo')
|
||||
@@ -237,6 +262,30 @@ describe('Task', function () {
|
||||
})
|
||||
})
|
||||
|
||||
for (const type of ['info', 'warning']) {
|
||||
describe(`#${type}()`, function () {
|
||||
it('throws when the task is not started', function () {
|
||||
const task = createTask()
|
||||
assert.throws(() => task[type]('foo'), { message: 'task has not started yet' })
|
||||
})
|
||||
|
||||
it(`emits an ${type} message`, async function () {
|
||||
const task = createTask()
|
||||
await task.run(async () => {
|
||||
await Task.run(() => {
|
||||
task[type]('foo')
|
||||
|
||||
assertEvent(task, {
|
||||
data: undefined,
|
||||
message: 'foo',
|
||||
type,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('#id', function () {
|
||||
it('can be set', function () {
|
||||
const task = createTask()
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<!--packages-start-->
|
||||
|
||||
- @vates/fuse-vhd patch
|
||||
- @vates/task minor
|
||||
- @xen-orchestra/proxy-cli patch
|
||||
- @xen-orchestra/vmware-explorer patch
|
||||
- @xen-orchestra/web patch
|
||||
|
||||
Reference in New Issue
Block a user