const std = @import("std"); const Allocator = std.mem.Allocator; const backend = @import("backend.zig"); const log = @import("../logging.zig"); pub const DeploymentScheduleRow = struct { id: []const u8, created: []const u8, updated: []const u8, deployment_id: []const u8, schedule: []const u8, active: bool, max_scheduled_runs: ?i64, parameters: []const u8, slug: ?[]const u8, }; const Col = struct { const id: usize = 0; const created: usize = 1; const updated: usize = 2; const deployment_id: usize = 3; const schedule: usize = 4; const active: usize = 5; const max_scheduled_runs: usize = 6; const parameters: usize = 7; const slug: usize = 8; }; const select_cols = "id, created, updated, deployment_id, schedule, active, max_scheduled_runs, parameters, slug"; fn rowFromResult(alloc: Allocator, r: anytype) !DeploymentScheduleRow { const max_runs = r.textOrNull(Col.max_scheduled_runs); return DeploymentScheduleRow{ .id = try alloc.dupe(u8, r.text(Col.id)), .created = try alloc.dupe(u8, r.text(Col.created)), .updated = try alloc.dupe(u8, r.text(Col.updated)), .deployment_id = try alloc.dupe(u8, r.text(Col.deployment_id)), .schedule = try alloc.dupe(u8, r.text(Col.schedule)), .active = r.int(Col.active) != 0, .max_scheduled_runs = if (max_runs != null) r.bigint(Col.max_scheduled_runs) else null, .parameters = try alloc.dupe(u8, r.text(Col.parameters)), .slug = if (r.textOrNull(Col.slug)) |s| try alloc.dupe(u8, s) else null, }; } pub fn getById(alloc: Allocator, id: []const u8) !?DeploymentScheduleRow { var r = backend.db.row( "SELECT " ++ select_cols ++ " FROM deployment_schedule WHERE id = ?", .{id}, ) catch return null; if (r) |*row| { defer row.deinit(); return try rowFromResult(alloc, row); } return null; } pub fn listByDeployment(alloc: Allocator, deployment_id: []const u8) ![]DeploymentScheduleRow { var results = std.ArrayListUnmanaged(DeploymentScheduleRow){}; errdefer results.deinit(alloc); var rows = backend.db.query( "SELECT " ++ select_cols ++ " FROM deployment_schedule WHERE deployment_id = ? ORDER BY created ASC", .{deployment_id}, ) catch |err| { log.err("database", "list deployment_schedules error: {}", .{err}); return err; }; defer rows.deinit(); while (rows.next()) |r| { try results.append(alloc, try rowFromResult(alloc, &r)); } return results.toOwnedSlice(alloc); } pub const InsertParams = struct { active: bool = true, max_scheduled_runs: ?i64 = null, parameters: []const u8 = "{}", slug: ?[]const u8 = null, }; pub fn insert( id: []const u8, deployment_id: []const u8, schedule: []const u8, created: []const u8, params: InsertParams, ) !void { backend.db.exec( \\INSERT INTO deployment_schedule (id, created, updated, deployment_id, schedule, active, max_scheduled_runs, parameters, slug) \\VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) , .{ id, created, created, deployment_id, schedule, @as(i32, if (params.active) 1 else 0), params.max_scheduled_runs, params.parameters, params.slug, }) catch |err| { log.err("database", "insert deployment_schedule error: {}", .{err}); return err; }; } pub const UpdateParams = struct { schedule: ?[]const u8 = null, active: ?bool = null, max_scheduled_runs: ?i64 = null, parameters: ?[]const u8 = null, slug: ?[]const u8 = null, }; pub fn updateById(id: []const u8, updated: []const u8, params: UpdateParams) !bool { const affected = backend.db.execWithRowCount( \\UPDATE deployment_schedule SET \\ schedule = COALESCE(?, schedule), \\ active = COALESCE(?, active), \\ max_scheduled_runs = COALESCE(?, max_scheduled_runs), \\ parameters = COALESCE(?, parameters), \\ slug = COALESCE(?, slug), \\ updated = ? \\WHERE id = ? , .{ params.schedule, if (params.active) |a| @as(?i32, if (a) 1 else 0) else null, params.max_scheduled_runs, params.parameters, params.slug, updated, id, }) catch |err| { log.err("database", "update deployment_schedule error: {}", .{err}); return err; }; return affected > 0; } pub fn deleteById(id: []const u8) !bool { const affected = backend.db.execWithRowCount( "DELETE FROM deployment_schedule WHERE id = ?", .{id}, ) catch |err| { log.err("database", "delete deployment_schedule error: {}", .{err}); return err; }; return affected > 0; } pub fn deleteByDeployment(deployment_id: []const u8) !usize { const affected = backend.db.execWithRowCount( "DELETE FROM deployment_schedule WHERE deployment_id = ?", .{deployment_id}, ) catch |err| { log.err("database", "delete deployment_schedules error: {}", .{err}); return err; }; return @intCast(affected); } /// List all active schedules (for scheduler service) pub fn listActive(alloc: Allocator, limit: usize) ![]DeploymentScheduleRow { var results = std.ArrayListUnmanaged(DeploymentScheduleRow){}; errdefer results.deinit(alloc); var rows = backend.db.query( "SELECT " ++ select_cols ++ " FROM deployment_schedule WHERE active = 1 ORDER BY created ASC LIMIT ?", .{@as(i64, @intCast(limit))}, ) catch |err| { log.err("database", "list active schedules error: {}", .{err}); return err; }; defer rows.deinit(); while (rows.next()) |r| { try results.append(alloc, try rowFromResult(alloc, &r)); } return results.toOwnedSlice(alloc); }