Documentation
¶
Index ¶
- Constants
- Variables
- func CIDRMiddleware(allowlist, trustedProxies []string) echo.MiddlewareFunc
- func ParseTime(timeStr string) time.Time
- type EmptyRequest
- type Job
- type JobContext
- type JobIDParam
- type JobListResponse
- type JobMetadata
- type JobTriggerData
- type JobTriggerResponse
- type ScheduleConfiguration
- type ScheduleType
- type SchedulerModule
- func (m *SchedulerModule) DailyAt(jobID string, job any, localTime time.Time) error
- func (m *SchedulerModule) DeclareMessaging(_ *messaging.Declarations)
- func (m *SchedulerModule) FixedRate(jobID string, job any, interval time.Duration) error
- func (m *SchedulerModule) HourlyAt(jobID string, job any, minute int) error
- func (m *SchedulerModule) Init(deps *app.ModuleDeps) error
- func (m *SchedulerModule) MonthlyAt(jobID string, job any, dayOfMonth int, localTime time.Time) error
- func (m *SchedulerModule) Name() string
- func (m *SchedulerModule) RegisterRoutes(hr *server.HandlerRegistry, r server.RouteRegistrar)
- func (m *SchedulerModule) Shutdown() error
- func (m *SchedulerModule) WeeklyAt(jobID string, job any, dayOfWeek time.Weekday, localTime time.Time) error
- type ValidationError
Constants ¶
const ( StatusSuccess = "success" StatusFailure = "failure" )
Variables ¶
var ( // ErrInvalidScheduleType indicates an unknown or unsupported schedule type ErrInvalidScheduleType = errors.New("scheduler: invalid schedule type") // ErrInvalidInterval indicates an invalid interval for fixed-rate schedules ErrInvalidInterval = errors.New("scheduler: invalid interval") // ErrInvalidTimeRange indicates a time value (hour, minute, day) is out of valid range ErrInvalidTimeRange = errors.New("scheduler: invalid time range") )
Sentinel errors for testability and error type checking
Functions ¶
func CIDRMiddleware ¶
func CIDRMiddleware(allowlist, trustedProxies []string) echo.MiddlewareFunc
CIDRMiddleware creates middleware that restricts access based on CIDR allowlist. Per clarification #2: Empty allowlist = localhost-only access (127.0.0.1, ::1). Non-empty allowlist = restrict to matching IP ranges only.
trustedProxies: CIDR ranges of reverse proxies that can be trusted to provide X-Forwarded-For/X-Real-IP headers. If empty, proxy headers are IGNORED to prevent spoofing.
func ParseTime ¶
ParseTime parses a time string in "HH:MM" format (24-hour) and returns a time.Time. This is a convenience function intended for use in application initialization code when registering scheduled jobs.
The function panics if the input format is invalid, following the "fail fast" principle from the GoBricks constitution - configuration errors should be caught at startup, not runtime.
Example:
scheduler.DailyAt("cleanup-job", &CleanupJob{}, scheduler.ParseTime("03:00")) // 3:00 AM
scheduler.WeeklyAt("report-job", &ReportJob{}, time.Monday, scheduler.ParseTime("09:30")) // Monday at 9:30 AM
Format: "HH:MM" where HH is 00-23 and MM is 00-59
Panics on invalid input (wrong format, invalid hour/minute values).
Types ¶
type Job ¶
type Job interface {
// Execute performs the job's work using the provided context.
// Return error to mark execution as failed (will be logged and recorded in metrics per FR-020).
// Panic will be recovered, logged with stack trace, and marked as failed per FR-021.
// Jobs SHOULD respect context cancellation (check ctx.Done()) for graceful shutdown per FR-024.
Execute(ctx JobContext) error
}
Job represents a unit of work to be executed on a schedule. Implementations must be thread-safe as different job instances may run concurrently (though the scheduler prevents overlapping executions of the SAME job per FR-026).
Example:
type CleanupJob struct{}
func (j *CleanupJob) Execute(ctx JobContext) error {
ctx.Logger().Info().Str("jobID", ctx.JobID()).Msg("Starting cleanup")
rows, err := ctx.DB().Query(ctx, "DELETE FROM temp_data WHERE created_at < NOW() - INTERVAL '24 hours'")
if err != nil {
return fmt.Errorf("cleanup failed: %w", err)
}
defer rows.Close()
return nil
}
type JobContext ¶
type JobContext interface {
context.Context // Embed stdlib context for cancellation, deadlines, values, and trace context
// JobID returns the unique identifier for this job (as registered via JobRegistrar)
JobID() string
// TriggerType returns how this job was triggered: "scheduled" or "manual"
// "scheduled" = automatic execution based on schedule
// "manual" = triggered via POST /_sys/job/:jobId
TriggerType() string
// Logger returns the framework logger with job-specific fields pre-populated
// (jobID, trigger type). Use this for all job logging per FR-020.
Logger() logger.Logger
// DB returns the database interface (may be nil if not configured in ModuleDeps)
DB() types.Interface
// Messaging returns the messaging client (may be nil if not configured in ModuleDeps)
Messaging() messaging.Client
// Config returns the application configuration
Config() *config.Config
}
JobContext provides access to framework dependencies and execution metadata during job execution. Embeds context.Context for cancellation, deadlines, and trace context.
JobContext mirrors the HTTP handler context pattern per Constitution VII (UX Consistency). OpenTelemetry trace context is automatically propagated to DB, Messaging calls per FR-019.
type JobIDParam ¶
type JobIDParam struct {
JobID string `param:"jobId" validate:"required"`
}
JobIDParam captures the jobId path parameter
type JobListResponse ¶
type JobListResponse struct {
Data []*JobMetadata `json:"data"`
Meta map[string]interface{} `json:"meta"`
}
JobListResponse represents the response for GET /_sys/job using standard GoBricks envelope
type JobMetadata ¶
type JobMetadata struct {
// JobID is the unique identifier provided during registration
JobID string `json:"jobId"`
// ScheduleType identifies the scheduling pattern (fixed-rate, daily, weekly, hourly, monthly)
ScheduleType string `json:"scheduleType"`
// CronExpression is a cron-style representation of the schedule (e.g., "0 3 * * *" for daily at 3 AM)
// Generated from ScheduleConfiguration per data-model.md
CronExpression string `json:"cronExpression"`
// HumanReadable is a user-friendly description (e.g., "Every Monday at 2:00 AM", "Every 30 minutes")
HumanReadable string `json:"humanReadable"`
// NextExecutionTime is when the job will execute next (nil if not yet scheduled)
NextExecutionTime *time.Time `json:"nextExecutionTime,omitempty"`
// LastExecutionTime is when the job last executed (nil if never run)
LastExecutionTime *time.Time `json:"lastExecutionTime,omitempty"`
// LastExecutionStatus is the status of the last execution: "success" or "failure" (nil if never run)
// Note: skipped triggers do not update last execution status.
LastExecutionStatus string `json:"lastExecutionStatus,omitempty"`
// TotalExecutions is the total number of times the job has been triggered (scheduled + manual)
TotalExecutions int64 `json:"totalExecutions"`
// SuccessCount is the number of executions that completed without error
SuccessCount int64 `json:"successCount"`
// FailureCount is the number of executions that returned error or panicked
FailureCount int64 `json:"failureCount"`
// SkippedCount is the number of triggers skipped due to already-running instance (overlapping prevention per FR-026)
SkippedCount int64 `json:"skippedCount"`
// contains filtered or unexported fields
}
JobMetadata contains information about a registered job for system API responses. Thread-safe access is managed by jobEntry mutex.
Exposed via GET /_sys/job endpoint per FR-009, FR-010.
type JobTriggerData ¶
type JobTriggerData struct {
JobID string `json:"jobId"`
Trigger string `json:"trigger"`
Message string `json:"message"`
}
JobTriggerData contains the trigger response data
type JobTriggerResponse ¶
type JobTriggerResponse struct {
Data JobTriggerData `json:"data"`
Meta map[string]interface{} `json:"meta"`
}
JobTriggerResponse represents the response for POST /_sys/job/:jobId using standard GoBricks envelope
type ScheduleConfiguration ¶
type ScheduleConfiguration struct {
Type ScheduleType
// FixedRate fields
Interval time.Duration // Used when Type == ScheduleTypeFixedRate
// Time-based fields (daily, weekly, monthly, hourly)
Hour int // 0-23 (used for daily, weekly, monthly)
Minute int // 0-59 (used for all time-based schedules)
// WeeklyAt field
DayOfWeek time.Weekday // Sunday-Saturday
// MonthlyAt field
DayOfMonth int // 1-31
// Timezone (nil = system local time per ASSUME-001)
// For MVP, this is always nil (local time).
// Future enhancement: allow explicit timezone specification.
Timezone *time.Location
}
ScheduleConfiguration holds the configuration details for a scheduled job
func (*ScheduleConfiguration) ToCronExpression ¶
func (c *ScheduleConfiguration) ToCronExpression() string
ToCronExpression converts the schedule configuration to a cron-style expression. Note: Fixed-rate schedules don't have a true cron equivalent, so we use "@every" notation.
func (*ScheduleConfiguration) ToHumanReadable ¶
func (c *ScheduleConfiguration) ToHumanReadable() string
ToHumanReadable converts the schedule configuration to a user-friendly description.
type ScheduleType ¶
type ScheduleType string
ScheduleType identifies the scheduling pattern
const ( // ScheduleTypeFixedRate represents jobs that execute every N duration ScheduleTypeFixedRate ScheduleType = "fixed-rate" // ScheduleTypeDaily represents jobs that execute once per day at a specific time ScheduleTypeDaily ScheduleType = "daily" // ScheduleTypeWeekly represents jobs that execute once per week on a specific day and time ScheduleTypeWeekly ScheduleType = "weekly" // ScheduleTypeHourly represents jobs that execute once per hour at a specific minute ScheduleTypeHourly ScheduleType = "hourly" // ScheduleTypeMonthly represents jobs that execute once per month on a specific day and time ScheduleTypeMonthly ScheduleType = "monthly" )
type SchedulerModule ¶
type SchedulerModule struct {
// contains filtered or unexported fields
}
SchedulerModule implements the GoBricks Module interface for job scheduling. It provides lazy initialization per FR-016: scheduler created only when first job is registered.
Example usage:
func (m *MyModule) Init(deps *app.ModuleDeps) error {
return deps.Scheduler.DailyAt("cleanup-job", &CleanupJob{}, mustParseTime("03:00"))
}
func NewSchedulerModule ¶
func NewSchedulerModule() *SchedulerModule
NewSchedulerModule creates a new SchedulerModule instance. Per FR-016: The scheduler itself is lazy-initialized on first job registration.
func (*SchedulerModule) DeclareMessaging ¶
func (m *SchedulerModule) DeclareMessaging(_ *messaging.Declarations)
DeclareMessaging implements app.Module Scheduler does not declare any messaging exchanges/queues
func (*SchedulerModule) HourlyAt ¶
func (m *SchedulerModule) HourlyAt(jobID string, job any, minute int) error
HourlyAt implements JobRegistrar per FR-006
func (*SchedulerModule) Init ¶
func (m *SchedulerModule) Init(deps *app.ModuleDeps) error
Init implements app.Module Stores dependencies and makes the module available as a JobRegistrar via deps.
func (*SchedulerModule) MonthlyAt ¶
func (m *SchedulerModule) MonthlyAt(jobID string, job any, dayOfMonth int, localTime time.Time) error
MonthlyAt implements JobRegistrar per FR-007
func (*SchedulerModule) RegisterRoutes ¶
func (m *SchedulerModule) RegisterRoutes(hr *server.HandlerRegistry, r server.RouteRegistrar)
RegisterRoutes implements app.Module Registers system API routes for job listing and manual triggering
func (*SchedulerModule) Shutdown ¶
func (m *SchedulerModule) Shutdown() error
Shutdown implements app.Module Gracefully shuts down the scheduler per FR-013, FR-014, FR-015.
type ValidationError ¶
type ValidationError struct {
Field string // The field that failed validation (e.g., "hour", "minute", "interval")
Message string // Description of what's wrong (e.g., "must be 0-23")
Action string // Optional: Actionable guidance (e.g., "Choose a value between 0 and 23")
Err error // Optional: Wrapped sentinel error for errors.Is checking
}
ValidationError represents a validation error during job registration or schedule validation. Per Constitution VII: Error messages follow format "scheduler: <field> <message>. <action>"
func NewInvalidValueError ¶
func NewInvalidValueError(field string, value any, expected string) *ValidationError
NewInvalidValueError creates a ValidationError for invalid values
func NewRangeError ¶
func NewRangeError(field string, minVal, maxVal, actual any) *ValidationError
NewRangeError creates a ValidationError for values outside valid ranges
func NewValidationError ¶
func NewValidationError(field, message, action string) *ValidationError
NewValidationError creates a new ValidationError with the specified field, message, and action
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
Unwrap returns the wrapped error for errors.Is/As support