func (cs CalendarSubscription) Normalize() (*CalendarSubscription, error) {
	if cs.ID == "" {
		cs.ID = uuid.New().String()
	}
	err := validate.Many(
		validate.Range("ReminderMinutes", len(cs.Config.ReminderMinutes), 0, 15),
		validate.IDName("Name", cs.Name),
		validate.UUID("ID", cs.ID),
		validate.UUID("UserID", cs.UserID),
	)
	if err != nil {
		return nil, err
	}
	return &cs, nil
}
Normalize will validate and produce a normalized CalendarSubscription struct.
package calendarsubscription
import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"html/template"
	"strings"
	"time"
	"github.com/google/uuid"
	"github.com/pkg/errors"
	"github.com/target/goalert/oncall"
	"github.com/target/goalert/validation/validate"
	"github.com/target/goalert/version"
)
type CalendarSubscription struct {
	ID         string
	Name       string
	UserID     string
	ScheduleID string
	LastAccess time.Time
	Disabled   bool
	Config struct {
		ReminderMinutes []int
	}
	token string
}
type iCalRenderData struct {
	ApplicationName string
	Shifts          []oncall.Shift
	ReminderMinutes []int
	Version         string
	GeneratedAt     time.Time
	EventUIDs       []string
}
var iCalTemplate = template.Must(template.New("ical").Parse(strings.ReplaceAll(`BEGIN:VCALENDAR
PRODID:- 
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:PUBLISH
{{- $mins := .ReminderMinutes }}
{{- $genTime := .GeneratedAt }}
{{- $eventUIDs := .EventUIDs}}
{{- range $i, $s := .Shifts}}
BEGIN:VEVENT
UID:{{index $eventUIDs $i}}
SUMMARY:On-Call Shift{{if $s.Truncated}} Begins*
DESCRIPTION:The end time of this shift is unknown and will continue beyond what is displayed.
{{- end }}
DTSTAMP:{{$genTime.UTC.Format "20060102T150405Z"}}
DTSTART:{{.Start.UTC.Format "20060102T150405Z"}}
DTEND:{{.End.UTC.Format "20060102T150405Z"}}
{{- range $mins}}
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:REMINDER
TRIGGER:-PT{{.}}M
END:VALARM
{{- end}}
END:VEVENT
{{- end}}
END:VCALENDAR
`, "\n", "\r\n")))
func (cs CalendarSubscription) Token() string { return cs.token }