Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/resources/function/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function toDeployPayloadItem(fn: FunctionWithCode) {
name: fn.name,
entry: fn.entry,
files: fn.files,
automations: fn.automations,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/core/resources/function/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export async function pushFunctions(
functions: BackendFunction[]
): Promise<DeployFunctionsResponse> {
if (functions.length === 0) {
return { deployed: [], deleted: [], errors: null };
return { deployed: [], deleted: [], skipped: [], errors: null };
}

const functionsWithCode = await Promise.all(functions.map(loadFunctionCode));
Expand Down
77 changes: 66 additions & 11 deletions src/core/resources/function/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,83 @@ const FunctionFileSchema = z.object({
content: z.string(),
});

// Base fields shared by all automation types
const AutomationBaseSchema = z.object({
name: z.string().min(1, "Automation name cannot be empty"),
description: z.string().nullable().optional(),
function_args: z.record(z.string(), z.unknown()).nullable().optional(),
is_active: z.boolean().optional().default(true),
});

// One-time scheduled automation
const ScheduledOneTimeSchema = AutomationBaseSchema.extend({
type: z.literal("scheduled"),
schedule_mode: z.literal("one-time"),
one_time_date: z
.string()
.min(1, "One-time date is required for one-time schedules"),
});

// Recurring cron scheduled automation
const ScheduledCronSchema = AutomationBaseSchema.extend({
type: z.literal("scheduled"),
schedule_mode: z.literal("recurring"),
schedule_type: z.literal("cron"),
cron_expression: z
.string()
.min(1, "Cron expression is required for cron schedules"),
ends_type: z.enum(["never", "on", "after"]).optional().default("never"),
ends_on_date: z.string().nullable().optional(),
ends_after_count: z.number().int().positive().nullable().optional(),
});

// Recurring simple scheduled automation
const ScheduledSimpleSchema = AutomationBaseSchema.extend({
type: z.literal("scheduled"),
schedule_mode: z.literal("recurring"),
schedule_type: z.literal("simple"),
repeat_unit: z.enum(["minutes", "hours", "days", "weeks", "months"]),
repeat_interval: z.number().int().positive().optional(),
start_time: z.string().nullable().optional(),
repeat_on_days: z.array(z.number().int().min(0).max(6)).nullable().optional(),
repeat_on_day_of_month: z.number().int().min(1).max(31).nullable().optional(),
ends_type: z.enum(["never", "on", "after"]).optional().default("never"),
ends_on_date: z.string().nullable().optional(),
ends_after_count: z.number().int().positive().nullable().optional(),
});

// Entity event automation
const EntityAutomationSchema = AutomationBaseSchema.extend({
type: z.literal("entity"),
entity_name: z.string().min(1, "Entity name cannot be empty"),
event_types: z
.array(z.enum(["create", "update", "delete"]))
.min(1, "At least one event type is required"),
});

// Union of all automation types
const AutomationSchema = z.union([
ScheduledOneTimeSchema,
ScheduledCronSchema,
ScheduledSimpleSchema,
EntityAutomationSchema,
]);

export const FunctionConfigSchema = z.object({
name: FunctionNameSchema,
entry: z.string().min(1, "Entry point cannot be empty"),
automations: z.array(AutomationSchema).optional(),
});

export const BackendFunctionSchema = FunctionConfigSchema.extend({
const BackendFunctionSchema = FunctionConfigSchema.extend({
entryPath: z.string().min(1, "Entry path cannot be empty"),
filePaths: z.array(z.string()).min(1, "Function must have at least one file"),
});

export const FunctionDeploySchema = z.object({
name: FunctionNameSchema,
entry: z.string().min(1),
files: z
.array(FunctionFileSchema)
.min(1, "Function must have at least one file"),
});

export const DeployFunctionsResponseSchema = z.object({
deployed: z.array(z.string()),
deleted: z.array(z.string()),
skipped: z.array(z.string()).optional().nullable(),
errors: z
.array(z.object({ name: z.string(), message: z.string() }))
.nullable(),
Expand All @@ -40,11 +96,10 @@ export const DeployFunctionsResponseSchema = z.object({
export type FunctionConfig = z.infer<typeof FunctionConfigSchema>;
export type BackendFunction = z.infer<typeof BackendFunctionSchema>;
export type FunctionFile = z.infer<typeof FunctionFileSchema>;
export type FunctionDeploy = z.infer<typeof FunctionDeploySchema>;
export type DeployFunctionsResponse = z.infer<
typeof DeployFunctionsResponseSchema
>;

export type FunctionWithCode = Omit<BackendFunction, "files"> & {
export type FunctionWithCode = Omit<BackendFunction, "filePaths"> & {
files: FunctionFile[];
};
Loading