diff --git a/api/api.go b/api/api.go index e372c8ea..5e468c56 100644 --- a/api/api.go +++ b/api/api.go @@ -15,6 +15,7 @@ package api import ( "encoding/json" + "errors" "fmt" "net/http" "regexp" @@ -540,6 +541,27 @@ func (api *API) setSilence(w http.ResponseWriter, r *http.Request) { }, nil) return } + + // This is an API only validation, it cannot be done internally + // because the expired silence is semantically important. + // But one should not be able to create expired silences, that + // won't have any use. + if sil.Expired() { + api.respondError(w, apiError{ + typ: errorBadData, + err: errors.New("start time must not be equal to end time"), + }, nil) + return + } + + if sil.EndsAt.Before(time.Now()) { + api.respondError(w, apiError{ + typ: errorBadData, + err: errors.New("end time can't be in the past"), + }, nil) + return + } + psil, err := silenceToProto(&sil) if err != nil { api.respondError(w, apiError{ diff --git a/types/types.go b/types/types.go index ce964102..29202ecf 100644 --- a/types/types.go +++ b/types/types.go @@ -356,6 +356,12 @@ type Silence struct { Status SilenceStatus `json:"status"` } +// Expired return if the silence is expired +// meaning that both StartsAt and EndsAt are equal +func (s *Silence) Expired() bool { + return s.StartsAt.Equal(s.EndsAt) +} + type SilenceStatus struct { State SilenceState `json:"state"` } diff --git a/types/types_test.go b/types/types_test.go index ae143743..9953b8c0 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -82,3 +82,15 @@ func TestCalcSilenceState(t *testing.T) { expected = CalcSilenceState(pastStartTime, pastEndTime) require.Equal(t, SilenceStateExpired, expected) } + +func TestSilenceExpired(t *testing.T) { + now := time.Now() + silence := Silence{StartsAt: now, EndsAt: now} + require.True(t, silence.Expired()) + + silence = Silence{StartsAt: now.Add(time.Hour), EndsAt: now.Add(time.Hour)} + require.True(t, silence.Expired()) + + silence = Silence{StartsAt: now, EndsAt: now.Add(time.Hour)} + require.False(t, silence.Expired()) +}