Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## main / (unreleased)

* [ENHANCEMENT] ui: The silence form no longer requires a creator and a comment, matching the API. #5471

## 0.34.0 / 2026-08-16

* [CHANGE] notify: The `reason` label on `alertmanager_notifications_failed_total` now distinguishes `authError` (HTTP 401/403) and `rateLimited` (HTTP 429) from the generic `clientError`. Dashboards/alerts matching `reason="clientError"` for these codes must be updated. #5332
Expand Down
10 changes: 0 additions & 10 deletions ui/app/src/Utils/FormValidation.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Utils.FormValidation exposing
( ValidatedField
, ValidationState(..)
, initialField
, stringNotEmpty
, updateValue
, validate
)
Expand Down Expand Up @@ -45,12 +44,3 @@ updateValue value field =
validate : (String -> Result String a) -> ValidatedField -> ValidatedField
validate validator field =
{ field | validationState = fromResult (validator field.value) }


stringNotEmpty : String -> Result String String
stringNotEmpty string =
if String.isEmpty (String.trim string) then
Err "Should not be empty"

else
Ok string
19 changes: 8 additions & 11 deletions ui/app/src/Views/SilenceForm/Types.elm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import Utils.FormValidation
( ValidatedField
, ValidationState(..)
, initialField
, stringNotEmpty
, validate
)
import Utils.Types exposing (ApiData(..))
Expand Down Expand Up @@ -90,9 +89,7 @@ type SilenceFormFieldMsg
| UpdateDuration String
| ValidateTime
| UpdateCreatedBy String
| ValidateCreatedBy
| UpdateComment String
| ValidateComment
| UpdateTimesFromPicker
| OpenDateTimePicker
| CloseDateTimePicker
Expand All @@ -118,13 +115,15 @@ initSilenceForm key firstDayOfWeek =

toSilence : FilterBar.Model -> SilenceForm -> Maybe PostableSilence
toSilence filterBar { id, comment, createdBy, startsAt, endsAt, annotations } =
Result.map5
(\nonEmptyMatchers nonEmptyComment nonEmptyCreatedBy parsedStartsAt parsedEndsAt ->
-- The API allows an empty creator and comment, so they are not
-- validated here (see https://github.com/prometheus/alertmanager/issues/2998).
Result.map3
(\nonEmptyMatchers parsedStartsAt parsedEndsAt ->
{ nullSilence
| id = id
, comment = nonEmptyComment
, comment = comment.value
, matchers = nonEmptyMatchers
, createdBy = nonEmptyCreatedBy
, createdBy = createdBy.value
, startsAt = parsedStartsAt
, endsAt = parsedEndsAt
, annotations =
Expand All @@ -136,8 +135,6 @@ toSilence filterBar { id, comment, createdBy, startsAt, endsAt, annotations } =
}
)
(validMatchers filterBar)
(stringNotEmpty comment.value)
(stringNotEmpty createdBy.value)
(timeFromString startsAt.value)
(parseEndsAt startsAt.value endsAt.value)
|> Result.toMaybe
Expand Down Expand Up @@ -229,8 +226,8 @@ fromSilence { id, createdBy, comment, startsAt, endsAt, annotations } firstDayOf
validateForm : SilenceForm -> SilenceForm
validateForm { id, createdBy, comment, startsAt, endsAt, duration, dateTimePicker, annotations, annotationText } =
{ id = id
, createdBy = validate stringNotEmpty createdBy
, comment = validate stringNotEmpty comment
, createdBy = createdBy
, comment = comment
, startsAt = validate timeFromString startsAt
, endsAt = validate (parseEndsAt startsAt.value) endsAt
, duration = validate parseDuration duration
Expand Down
8 changes: 1 addition & 7 deletions ui/app/src/Views/SilenceForm/Updates.elm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Utils.Date exposing (timeFromString)
import Utils.DateTimePicker.Types exposing (initFromStartAndEndTime)
import Utils.DateTimePicker.Updates as DateTimePickerUpdates
import Utils.Filter exposing (silencePreviewFilter)
import Utils.FormValidation exposing (initialField, stringNotEmpty, updateValue, validate)
import Utils.FormValidation exposing (initialField, updateValue, validate)
import Utils.Types exposing (ApiData(..))
import Views.FilterBar.Types as FilterBar
import Views.FilterBar.Updates as FilterBar
Expand Down Expand Up @@ -119,15 +119,9 @@ updateForm msg form =
UpdateCreatedBy createdBy ->
{ form | createdBy = updateValue createdBy form.createdBy }

ValidateCreatedBy ->
{ form | createdBy = validate stringNotEmpty form.createdBy }

UpdateComment comment ->
{ form | comment = updateValue comment form.comment }

ValidateComment ->
{ form | comment = validate stringNotEmpty form.comment }

UpdateTimesFromPicker ->
let
( startsAt, endsAt, duration ) =
Expand Down
8 changes: 4 additions & 4 deletions ui/app/src/Views/SilenceForm/Views.elm
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ view maybeId silenceFormGetParams defaultCreator { form, filterBar, filterBarVal
, timeInput form.startsAt form.endsAt form.duration
, matchersInput filterBarValid filterBar
, validatedField input
"Creator"
"Creator (Optional)"
inputSectionPadding
(UpdateCreatedBy >> UpdateField)
(ValidateCreatedBy |> UpdateField)
(Noop |> UpdateField)
form.createdBy
, validatedTextareaField
"Comment"
"Comment (Optional)"
inputSectionPadding
(UpdateComment >> UpdateField)
(ValidateComment |> UpdateField)
(Noop |> UpdateField)
form.comment
, annotationsInput annotationsValid form
, div [ class inputSectionPadding ]
Expand Down
106 changes: 105 additions & 1 deletion ui/app/tests/SilenceForm.elm
Original file line number Diff line number Diff line change
@@ -1,10 +1,114 @@
module SilenceForm exposing (parseAnnotation)
module SilenceForm exposing (parseAnnotation, toSilence, validateForm)

import Expect
import Test exposing (..)
import Time
import Utils.Date
import Utils.DateTimePicker.Types exposing (initDateTimePicker)
import Utils.DateTimePicker.Utils exposing (FirstDayOfWeek(..))
import Utils.Filter
import Utils.FormValidation exposing (ValidationState(..), initialField)
import Views.FilterBar.Types as FilterBar
import Views.SilenceForm.Types


silenceForm : String -> String -> Views.SilenceForm.Types.SilenceForm
silenceForm createdBy comment =
let
startsAt =
Utils.Date.timeToString (Time.millisToPosix 1000000000000)

endsAt =
Utils.Date.timeToString (Time.millisToPosix 1000003600000)
in
{ id = Nothing
, createdBy = initialField createdBy
, comment = initialField comment
, startsAt = initialField startsAt
, endsAt = initialField endsAt
, duration = initialField "1h"
, dateTimePicker = initDateTimePicker Monday
, viewDateTimePicker = False
, annotations = []
, annotationText = ""
}


matcherFilterBar : List Utils.Filter.Matcher -> FilterBar.Model
matcherFilterBar matchers =
FilterBar.initFilterBar matchers


toSilence : Test
toSilence =
describe "toSilence"
[ test "accepts an empty creator and comment" <|
\() ->
Expect.notEqual Nothing
(Views.SilenceForm.Types.toSilence
(matcherFilterBar [ { key = "alertname", op = Utils.Filter.Eq, value = "ExampleAlert" } ])
(silenceForm "" "")
)
, test "accepts a creator and comment" <|
\() ->
Expect.notEqual Nothing
(Views.SilenceForm.Types.toSilence
(matcherFilterBar [ { key = "alertname", op = Utils.Filter.Eq, value = "ExampleAlert" } ])
(silenceForm "alice" "maintenance window")
)
, test "still requires at least one matcher" <|
\() ->
Expect.equal Nothing
(Views.SilenceForm.Types.toSilence
(matcherFilterBar [])
(silenceForm "" "")
)
]


validateForm : Test
validateForm =
describe "validateForm"
[ test "does not flag an empty creator and comment" <|
\() ->
let
form =
Views.SilenceForm.Types.validateForm (silenceForm "" "")

isInvalid state =
case state of
Invalid _ ->
True

_ ->
False
in
Expect.equal ( False, False )
( isInvalid form.createdBy.validationState
, isInvalid form.comment.validationState
)
, test "still flags invalid start and end times" <|
\() ->
let
baseForm =
silenceForm "" ""

form =
Views.SilenceForm.Types.validateForm
{ baseForm | startsAt = initialField "not-a-time" }

isInvalid state =
case state of
Invalid _ ->
True

_ ->
False
in
Expect.equal True (isInvalid form.startsAt.validationState)
]


parseAnnotation : Test
parseAnnotation =
describe "parseAnnotation"
Expand Down
Loading