85 lines
3.2 KiB
JavaScript
85 lines
3.2 KiB
JavaScript
function createBatchFindWork({
|
|
capacity,
|
|
claim,
|
|
queue,
|
|
timeBudget = () => ({ capacity_minutes: null, planned_minutes: 0 }),
|
|
persistEstimate = () => {},
|
|
onProgress = () => {},
|
|
}) {
|
|
let request = null;
|
|
|
|
function result(status, selected, available, queued = [], failed = []) {
|
|
return { status, selected, available, queued, failed };
|
|
}
|
|
|
|
function key(item) {
|
|
return String(item.repository || '') + '#' + String(item.number || '');
|
|
}
|
|
|
|
function run(items, estimates = {}) {
|
|
if (request) return request;
|
|
const selected = Array.isArray(items) ? items.slice() : [];
|
|
const available = Math.max(0, Number(capacity()) || 0);
|
|
if (selected.length > available) {
|
|
const outcome = result('full', selected.length, available);
|
|
onProgress(outcome);
|
|
return Promise.resolve(outcome);
|
|
}
|
|
const budget = timeBudget() || {};
|
|
if (Number.isInteger(budget.capacity_minutes) && budget.capacity_minutes > 0) {
|
|
const remaining = Math.max(0, budget.capacity_minutes - (Number(budget.planned_minutes) || 0));
|
|
const invalid = selected.map(key).filter(id =>
|
|
!Number.isInteger(estimates[id]) || estimates[id] <= 0
|
|
);
|
|
const requested = selected.reduce((sum, item) => sum +
|
|
(Number.isInteger(estimates[key(item)]) && estimates[key(item)] > 0 ? estimates[key(item)] : 0), 0);
|
|
if (invalid.length) {
|
|
const outcome = { ...result('estimates-required', selected.length, available),
|
|
remaining_minutes: remaining, requested_minutes: requested, invalid };
|
|
onProgress(outcome);
|
|
return Promise.resolve(outcome);
|
|
}
|
|
if (requested > remaining) {
|
|
const outcome = { ...result('over-budget', selected.length, available),
|
|
remaining_minutes: remaining, requested_minutes: requested, over_minutes: requested - remaining };
|
|
onProgress(outcome);
|
|
return Promise.resolve(outcome);
|
|
}
|
|
}
|
|
request = (async () => {
|
|
const queued = [];
|
|
const failed = [];
|
|
for (let index = 0; index < selected.length; index += 1) {
|
|
const item = selected[index];
|
|
try {
|
|
const confirmed = await claim(item);
|
|
const queueResult = await queue(confirmed);
|
|
if (queueResult === 'queued' || queueResult === 'exists') {
|
|
queued.push(key(item));
|
|
if (Number.isInteger(estimates[key(item)]) && estimates[key(item)] > 0) {
|
|
persistEstimate(confirmed, estimates[key(item)]);
|
|
}
|
|
} else {
|
|
failed.push({
|
|
key: key(item),
|
|
reason: 'assigned but Today sync is unavailable',
|
|
assigned: true,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
failed.push({ key: key(item), reason: error?.message || 'assignment failed' });
|
|
}
|
|
onProgress({ status: 'running', processed: index + 1, selected: selected.length });
|
|
}
|
|
const outcome = result('complete', selected.length, available, queued, failed);
|
|
onProgress({ ...outcome, processed: selected.length });
|
|
return outcome;
|
|
})().finally(() => { request = null; });
|
|
return request;
|
|
}
|
|
|
|
return { run };
|
|
}
|
|
|
|
if (typeof module !== 'undefined' && module.exports) module.exports = createBatchFindWork;
|