115 lines
3.5 KiB
TypeScript
115 lines
3.5 KiB
TypeScript
import { v } from 'convex/values';
|
|
|
|
import type { Doc, Id } from './_generated/dataModel';
|
|
import { internalMutation, query } from './_generated/server';
|
|
import { getOwnedSpoon, getRequiredUserId } from './model';
|
|
|
|
const syncKind = v.union(
|
|
v.literal('scheduled_check'),
|
|
v.literal('manual_check'),
|
|
v.literal('upstream_update'),
|
|
v.literal('merge_attempt'),
|
|
v.literal('ai_review'),
|
|
);
|
|
|
|
const syncStatus = v.union(
|
|
v.literal('queued'),
|
|
v.literal('running'),
|
|
v.literal('clean'),
|
|
v.literal('conflict'),
|
|
v.literal('needs_review'),
|
|
v.literal('failed'),
|
|
v.literal('merged'),
|
|
);
|
|
|
|
const syncDecision = v.union(
|
|
v.literal('auto_synced'),
|
|
v.literal('thread_created'),
|
|
v.literal('ignored'),
|
|
v.literal('failed'),
|
|
);
|
|
|
|
export const listRecent = query({
|
|
args: { limit: v.optional(v.number()) },
|
|
handler: async (ctx, { limit }) => {
|
|
const ownerId = await getRequiredUserId(ctx);
|
|
const runs = await ctx.db
|
|
.query('syncRuns')
|
|
.withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
|
|
.order('desc')
|
|
.take(limit ?? 25);
|
|
return runs;
|
|
},
|
|
});
|
|
|
|
export const listForSpoon = query({
|
|
args: { spoonId: v.id('spoons'), limit: v.optional(v.number()) },
|
|
handler: async (ctx, { spoonId, limit }) => {
|
|
const ownerId = await getRequiredUserId(ctx);
|
|
await getOwnedSpoon(ctx, spoonId, ownerId);
|
|
return await ctx.db
|
|
.query('syncRuns')
|
|
.withIndex('by_spoon', (q) => q.eq('spoonId', spoonId))
|
|
.order('desc')
|
|
.take(limit ?? 25);
|
|
},
|
|
});
|
|
|
|
export const createInternal = internalMutation({
|
|
args: {
|
|
spoonId: v.id('spoons'),
|
|
ownerId: v.id('users'),
|
|
threadId: v.optional(v.id('threads')),
|
|
kind: syncKind,
|
|
status: syncStatus,
|
|
upstreamFrom: v.optional(v.string()),
|
|
upstreamTo: v.optional(v.string()),
|
|
summary: v.optional(v.string()),
|
|
aiAssessment: v.optional(v.string()),
|
|
mergeRequestUrl: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
decision: v.optional(syncDecision),
|
|
},
|
|
handler: async (ctx, args): Promise<Id<'syncRuns'>> => {
|
|
const now = Date.now();
|
|
return await ctx.db.insert('syncRuns', {
|
|
...args,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
});
|
|
},
|
|
});
|
|
|
|
export const patchInternal = internalMutation({
|
|
args: {
|
|
syncRunId: v.id('syncRuns'),
|
|
threadId: v.optional(v.id('threads')),
|
|
status: v.optional(syncStatus),
|
|
upstreamFrom: v.optional(v.string()),
|
|
upstreamTo: v.optional(v.string()),
|
|
summary: v.optional(v.string()),
|
|
aiAssessment: v.optional(v.string()),
|
|
mergeRequestUrl: v.optional(v.string()),
|
|
error: v.optional(v.string()),
|
|
decision: v.optional(syncDecision),
|
|
},
|
|
handler: async (ctx, args) => {
|
|
const patch: Partial<Doc<'syncRuns'>> = { updatedAt: Date.now() };
|
|
if (args.threadId !== undefined) patch.threadId = args.threadId;
|
|
if (args.status !== undefined) patch.status = args.status;
|
|
if (args.upstreamFrom !== undefined) patch.upstreamFrom = args.upstreamFrom;
|
|
if (args.upstreamTo !== undefined) patch.upstreamTo = args.upstreamTo;
|
|
if (args.summary !== undefined) patch.summary = args.summary;
|
|
if (args.aiAssessment !== undefined) {
|
|
patch.aiAssessment = args.aiAssessment;
|
|
}
|
|
if (args.mergeRequestUrl !== undefined) {
|
|
patch.mergeRequestUrl = args.mergeRequestUrl;
|
|
}
|
|
if (args.error !== undefined) patch.error = args.error;
|
|
if (args.decision !== undefined) patch.decision = args.decision;
|
|
await ctx.db.patch(args.syncRunId, patch);
|
|
return { success: true };
|
|
},
|
|
});
|