(input: CreateScheduleInput)
| 69 | const SCHEDULE_BATCH_SIZE = 100 |
| 70 | |
| 71 | const createOrUpdateSchedule = async (input: CreateScheduleInput): Promise<ScheduleRecord> => { |
| 72 | try { |
| 73 | const appServer = getRunningExpressApp() |
| 74 | const repo = appServer.AppDataSource.getRepository(ScheduleRecord) |
| 75 | |
| 76 | const validation = validateCronExpression(input.cronExpression, input.timezone ?? FALLBACK_TIMEZONE) |
| 77 | const cronExpression = validation.valid ? input.cronExpression : FALLBACK_CRON_EXPRESSION |
| 78 | const timezone = validation.valid ? input.timezone ?? FALLBACK_TIMEZONE : FALLBACK_TIMEZONE |
| 79 | |
| 80 | // Upsert: find existing record for this target + triggerType |
| 81 | const existing = await repo.findOne({ |
| 82 | where: { |
| 83 | targetId: input.targetId, |
| 84 | triggerType: input.triggerType, |
| 85 | workspaceId: input.workspaceId |
| 86 | } |
| 87 | }) |
| 88 | |
| 89 | if (existing) { |
| 90 | const updateSchedule = new ScheduleRecord() |
| 91 | const bodySchedule: ICommonObject = { |
| 92 | cronExpression, |
| 93 | timezone |
| 94 | } |
| 95 | if (input.enabled !== undefined) bodySchedule.enabled = input.enabled |
| 96 | if (input.scheduleInputMode !== undefined) bodySchedule.scheduleInputMode = input.scheduleInputMode |
| 97 | if (input.defaultInput !== undefined) bodySchedule.defaultInput = input.defaultInput |
| 98 | if (input.defaultForm !== undefined) bodySchedule.defaultForm = input.defaultForm |
| 99 | if (input.nodeId !== undefined) bodySchedule.nodeId = input.nodeId |
| 100 | bodySchedule.endDate = input.endDate ?? null |
| 101 | bodySchedule.nextRunAt = computeNextRunAt(cronExpression, timezone) ?? null |
| 102 | |
| 103 | // NOTE: Use assign + merge to update `endDate` and `nextRunAt` even if they are null |
| 104 | Object.assign(updateSchedule, bodySchedule) |
| 105 | const merged = repo.merge(existing, updateSchedule) |
| 106 | const saved = await repo.save(merged) |
| 107 | logger.debug(`[ScheduleService]: Updated schedule ${saved.id} for ${input.triggerType}:${input.targetId}`) |
| 108 | return saved |
| 109 | } |
| 110 | |
| 111 | const record = repo.create({ |
| 112 | triggerType: input.triggerType, |
| 113 | targetId: input.targetId, |
| 114 | nodeId: input.nodeId, |
| 115 | cronExpression: cronExpression, |
| 116 | timezone: timezone, |
| 117 | enabled: input.enabled !== undefined ? input.enabled : validation.valid, // default to enabled if valid, disabled if invalid |
| 118 | scheduleInputMode: input.scheduleInputMode, |
| 119 | defaultInput: input.defaultInput, |
| 120 | defaultForm: input.defaultForm, |
| 121 | endDate: input.endDate, |
| 122 | nextRunAt: computeNextRunAt(cronExpression, timezone) ?? undefined, |
| 123 | workspaceId: input.workspaceId |
| 124 | }) |
| 125 | |
| 126 | const saved = await repo.save(record) |
| 127 | logger.debug(`[ScheduleService]: Created schedule ${saved.id} for ${input.triggerType}:${input.targetId}`) |
| 128 | return saved |
nothing calls this directly
no test coverage detected