Files
supermq/pkg/schedule/schedule.go
T
Arvindh 67180a55f7 NOISSUE - Update Errors (#374)
* update MG errors

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update MG errors

Signed-off-by: Arvindh <arvindh91@gmail.com>

* sync with supermq main

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update MG errors

Signed-off-by: Arvindh <arvindh91@gmail.com>

---------

Signed-off-by: Arvindh <arvindh91@gmail.com>
2025-12-31 16:57:06 +01:00

173 lines
3.7 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package schedule
import (
"encoding/json"
"time"
"github.com/absmach/supermq/pkg/errors"
)
const (
noneType = "none"
hourlyType = "hourly"
dailyType = "daily"
weeklyType = "weekly"
monthlyType = "monthly"
)
var (
ErrInvalidRecurringType = errors.NewRequestError("invalid recurring type")
ErrStartDateTimeInPast = errors.NewRequestError("start_datetime must be greater than or equal to current time")
)
// Type can be daily, weekly or monthly.
type Recurring uint
const (
None Recurring = iota
Hourly
Daily
Weekly
Monthly
)
func (rt Recurring) String() string {
switch rt {
case Hourly:
return hourlyType
case Daily:
return dailyType
case Weekly:
return weeklyType
case Monthly:
return monthlyType
default:
return noneType
}
}
func (rt Recurring) MarshalJSON() ([]byte, error) {
return json.Marshal(rt.String())
}
func (rt *Recurring) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
switch s {
case hourlyType:
*rt = Hourly
case dailyType:
*rt = Daily
case weeklyType:
*rt = Weekly
case monthlyType:
*rt = Monthly
case noneType:
*rt = None
default:
return ErrInvalidRecurringType
}
return nil
}
type Schedule struct {
StartDateTime time.Time `json:"start_datetime,omitempty"` // When the schedule becomes active
Time time.Time `json:"time,omitempty"` // Specific time for the rule to run
Recurring Recurring `json:"recurring,omitempty"` // None, Daily, Weekly, Monthly
RecurringPeriod uint `json:"recurring_period,omitempty"` // Controls how many intervals to skip between executions: 1 = every interval, 2 = every second interval, etc.
}
func (s Schedule) Validate() error {
if !s.StartDateTime.IsZero() {
now := time.Now().UTC()
if s.StartDateTime.Before(now) {
return ErrStartDateTimeInPast
}
}
return nil
}
func (s Schedule) MarshalJSON() ([]byte, error) {
type Alias Schedule
jTimes := struct {
StartDateTime *string `json:"start_datetime"`
Time string `json:"time"`
*Alias
}{
Time: s.Time.Format(time.RFC3339),
Alias: (*Alias)(&s),
}
if !s.StartDateTime.IsZero() {
formatted := s.StartDateTime.Format(time.RFC3339)
jTimes.StartDateTime = &formatted
}
return json.Marshal(jTimes)
}
func (s *Schedule) UnmarshalJSON(data []byte) error {
type Alias Schedule
temp := struct {
StartDateTime string `json:"start_datetime,omitempty"`
Time string `json:"time,omitempty"`
*Alias
}{
Alias: (*Alias)(s),
}
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
if temp.StartDateTime != "" {
startDateTime, err := time.Parse(time.RFC3339, temp.StartDateTime)
if err != nil {
return err
}
s.StartDateTime = startDateTime
}
if temp.Time != "" {
parsedTime, err := time.Parse(time.RFC3339, temp.Time)
if err != nil {
return err
}
s.Time = parsedTime
}
return nil
}
func (s Schedule) NextDue() time.Time {
switch s.Recurring {
case Hourly:
return s.Time.Add(time.Hour * time.Duration(s.RecurringPeriod))
case Daily:
return s.Time.AddDate(0, 0, int(s.RecurringPeriod))
case Weekly:
return s.Time.AddDate(0, 0, int(s.RecurringPeriod)*7)
case Monthly:
return s.Time.AddDate(0, int(s.RecurringPeriod), 0)
default:
return time.Time{}
}
}
// EventEncode converts a schedule.Schedule struct to map[string]any.
func (s Schedule) EventEncode() map[string]any {
m := map[string]any{
"recurring": s.Recurring.String(),
"recurring_period": s.RecurringPeriod,
}
if !s.StartDateTime.IsZero() {
m["start_datetime"] = s.StartDateTime.Format(time.RFC3339)
}
if !s.Time.IsZero() {
m["time"] = s.Time.Format(time.RFC3339)
}
return m
}