import { Knex } from 'knex'; export type WorkflowScheduleStateStatus = | 'scheduled' | 'paused' | 'disabled' | 'completed' | 'failed'; export type WorkflowScheduleDayTypeFilter = 'any' | 'business' | 'non_business'; export type WorkflowScheduleStateRecord = { id: string; // uuid Citus distribution column. The legacy `tenant_id` column is being phased // out (dropped in the cleanup migration) and is not referenced here. tenant: string; workflow_id: string; workflow_version: number; name: string; trigger_type: 'schedule' | 'recurring'; day_type_filter: WorkflowScheduleDayTypeFilter; business_hours_schedule_id?: string | null; run_at?: string | null; cron?: string | null; timezone?: string | null; payload_json?: Record | unknown[] | null; enabled: boolean; status: WorkflowScheduleStateStatus; job_id?: string | null; runner_schedule_id?: string | null; last_fire_at?: string | null; next_fire_at?: string | null; last_run_status?: string | null; last_error?: string | null; last_fire_key?: string | null; created_at: string; updated_at: string; }; const serializeJsonForPgJsonColumn = (value: unknown): unknown => ( Array.isArray(value) ? JSON.stringify(value) : value ); const normalizeWorkflowScheduleWrite = ( data: Partial ): Partial => { const out: Partial = { ...data }; if ('payload_json' in out) { out.payload_json = serializeJsonForPgJsonColumn(out.payload_json) as WorkflowScheduleStateRecord['payload_json']; } return out; }; const WorkflowScheduleStateModel = { create: async ( knex: Knex, data: Partial ): Promise => { const normalized = normalizeWorkflowScheduleWrite(data); const [record] = await knex('tenant_workflow_schedule') .insert({ ...normalized, created_at: new Date().toISOString(), updated_at: new Date().toISOString() }) .returning('*'); return record; }, update: async ( knex: Knex, scheduleId: string, data: Partial, tenant?: string | null ): Promise => { const normalized = normalizeWorkflowScheduleWrite(data); const query = knex('tenant_workflow_schedule').where({ id: scheduleId }); if (tenant) query.andWhere({ tenant }); const [record] = await query .update({ ...normalized, updated_at: new Date().toISOString() }) .returning('*'); return record; }, getById: async (knex: Knex, scheduleId: string, tenant?: string | null): Promise => { const query = knex('tenant_workflow_schedule').where({ id: scheduleId }); if (tenant) query.andWhere({ tenant }); const record = await query.first(); return record ?? null; }, getByWorkflowId: async (knex: Knex, workflowId: string, tenant?: string | null): Promise => { const query = knex('tenant_workflow_schedule').where({ workflow_id: workflowId }); if (tenant) query.andWhere({ tenant }); const record = await query.orderBy('created_at', 'asc').first(); return record ?? null; }, listByWorkflowId: async (knex: Knex, workflowId: string, tenant?: string | null): Promise => { const query = knex('tenant_workflow_schedule').where({ workflow_id: workflowId }); if (tenant) query.andWhere({ tenant }); return query.orderBy('created_at', 'asc'); }, listByWorkflowIds: async (knex: Knex, workflowIds: string[], tenant?: string | null): Promise => { if (!workflowIds.length) return []; const query = knex('tenant_workflow_schedule').whereIn('workflow_id', workflowIds); if (tenant) query.andWhere({ tenant }); return query.orderBy('created_at', 'asc'); }, listByTenantId: async (knex: Knex, tenantId: string): Promise => knex('tenant_workflow_schedule') .where({ tenant: tenantId }) .orderBy('created_at', 'asc'), list: async (knex: Knex): Promise => knex('tenant_workflow_schedule').select('*'), deleteById: async (knex: Knex, scheduleId: string, tenant?: string | null): Promise => { const query = knex('tenant_workflow_schedule').where({ id: scheduleId }); if (tenant) query.andWhere({ tenant }); return query.del(); }, deleteByWorkflowId: async (knex: Knex, workflowId: string, tenant?: string | null): Promise => { const query = knex('tenant_workflow_schedule').where({ workflow_id: workflowId }); if (tenant) query.andWhere({ tenant }); return query.del(); } }; export default WorkflowScheduleStateModel;