feat: add batched recipe import queue
This commit is contained in:
@@ -60,6 +60,107 @@ export class FakeRecipeRepository {
|
||||
}
|
||||
}
|
||||
|
||||
export class FakeImportJobRepository {
|
||||
constructor() {
|
||||
this.jobs = new Map();
|
||||
this.nextId = 1;
|
||||
}
|
||||
|
||||
async enqueue({ ownerGoogleSub, url, platform, sourceId }) {
|
||||
const existing = [...this.jobs.values()].find((job) => (
|
||||
job.ownerGoogleSub === ownerGoogleSub
|
||||
&& job.platform === platform
|
||||
&& job.sourceId === sourceId
|
||||
));
|
||||
if (existing) return { created: false, job: existing };
|
||||
|
||||
const now = new Date();
|
||||
const job = {
|
||||
_id: `job-${this.nextId}`,
|
||||
ownerGoogleSub,
|
||||
url,
|
||||
platform,
|
||||
sourceId,
|
||||
status: 'pending',
|
||||
attempts: 0,
|
||||
error: null,
|
||||
recipeId: null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
lockedAt: null,
|
||||
};
|
||||
this.nextId += 1;
|
||||
this.jobs.set(job._id, job);
|
||||
return { created: true, job };
|
||||
}
|
||||
|
||||
async listVisible(ownerGoogleSub) {
|
||||
return [...this.jobs.values()].filter((job) => (
|
||||
job.ownerGoogleSub === ownerGoogleSub
|
||||
&& ['pending', 'processing', 'failed'].includes(job.status)
|
||||
));
|
||||
}
|
||||
|
||||
async recoverStale(ownerGoogleSub, staleBefore) {
|
||||
for (const job of this.jobs.values()) {
|
||||
if (
|
||||
job.ownerGoogleSub === ownerGoogleSub
|
||||
&& job.status === 'processing'
|
||||
&& job.lockedAt < staleBefore
|
||||
) {
|
||||
Object.assign(job, { status: 'pending', startedAt: null, lockedAt: null });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async claimNext(ownerGoogleSub) {
|
||||
const job = [...this.jobs.values()].find((candidate) => (
|
||||
candidate.ownerGoogleSub === ownerGoogleSub && candidate.status === 'pending'
|
||||
));
|
||||
if (!job) return null;
|
||||
const now = new Date();
|
||||
Object.assign(job, {
|
||||
status: 'processing',
|
||||
error: null,
|
||||
attempts: job.attempts + 1,
|
||||
startedAt: now,
|
||||
lockedAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
return job;
|
||||
}
|
||||
|
||||
async markCompleted(jobId, recipeId) {
|
||||
const job = this.jobs.get(jobId);
|
||||
Object.assign(job, {
|
||||
status: 'completed',
|
||||
recipeId,
|
||||
error: null,
|
||||
completedAt: new Date(),
|
||||
lockedAt: null,
|
||||
});
|
||||
}
|
||||
|
||||
async markFailed(jobId, error) {
|
||||
Object.assign(this.jobs.get(jobId), { status: 'failed', error, lockedAt: null });
|
||||
}
|
||||
|
||||
async retry(ownerGoogleSub, jobId) {
|
||||
const job = this.jobs.get(jobId);
|
||||
if (job?.ownerGoogleSub !== ownerGoogleSub || job.status !== 'failed') return null;
|
||||
Object.assign(job, {
|
||||
status: 'pending',
|
||||
error: null,
|
||||
startedAt: null,
|
||||
lockedAt: null,
|
||||
updatedAt: new Date(),
|
||||
});
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
export class FakeGroupRepository {
|
||||
constructor(groups = []) {
|
||||
this.groups = groups;
|
||||
|
||||
Reference in New Issue
Block a user