44 lines
2.3 KiB
JavaScript
44 lines
2.3 KiB
JavaScript
import { board, walk, normal } from './sync.mjs';
|
|
import { checkTask, ref, taskFields, digest } from './digest.mjs';
|
|
import { outcome } from './vikunja.mjs';
|
|
import { createMarker, matchesCreate, createDigest } from '../../bus/src/broker.mjs';
|
|
|
|
// Host/adapter only. All tracker I/O here uses the read-only sync authority. A complete negative
|
|
// scan is evidence of uncertainty, never permission to issue a replacement create.
|
|
export async function reconcileCreate({ ctx, receipt }) {
|
|
const p = receipt.body;
|
|
const wait = () => {
|
|
ctx.broker.recordCreateWait({ receipt, code: 'task-create-uncertain' });
|
|
return { state: 'uncertain', code: 'task-create-uncertain' };
|
|
};
|
|
const committed = ctx.broker.taskCreateResult(receipt);
|
|
if (committed) return { state: 'committed', task_ref: committed };
|
|
try {
|
|
const view = ctx.tree?.get(p.project);
|
|
if (!view || createDigest(view) !== createDigest(p.view)) return wait();
|
|
// An id-keyed complete walk includes done tasks and cannot accept a truncated page set.
|
|
const tasks = await walk(ctx, '', {}, p.project);
|
|
const marker = createMarker(p);
|
|
// Count all occurrences of this operation, including edited suffixes and foreign creators.
|
|
// A duplicate with changed fields must not make an otherwise valid candidate unique.
|
|
const operation = `Mosaic operation: ${p.op}`;
|
|
const hits = tasks.filter(t => (t.description ?? '').split('\n').some(line => line === operation || line.startsWith(operation + ';')));
|
|
if (hits.length !== 1 || !hits[0].description.endsWith(marker)) return wait();
|
|
const r = await ctx.sync('GET', `/tasks/${hits[0].id}`);
|
|
if (outcome(r) !== 'ok') return wait();
|
|
const task = r.json;
|
|
checkTask(task);
|
|
if (task.id !== hits[0].id || task.project_id !== p.project) return wait();
|
|
const bucket = task.done ? view.done : (await board(ctx, p.project, view)).get(task.id)?.bucket;
|
|
const fields = taskFields(task, bucket);
|
|
if (!matchesCreate(p, fields, task.created_by?.id)) return wait();
|
|
const task_ref = ref(p.project, task.id);
|
|
ctx.broker.recordRecoveredCreate({ receipt, task_ref, snapshot: {
|
|
task_ref, updated: normal(task.updated), etag: r.etag ?? null, digest: digest(fields), fields, creator: task.created_by.id,
|
|
} });
|
|
return { state: 'committed', task_ref };
|
|
} catch {
|
|
return wait();
|
|
}
|
|
}
|