Support default grouping

Signed-off-by: Andrey Kuzmin <andrey.kuzmin@soundcloud.com>
This commit is contained in:
Andrey Kuzmin 2019-04-27 12:06:26 +02:00
parent 1df9de3f28
commit 9ddadb8c77
11 changed files with 145 additions and 31 deletions

View File

@ -1,5 +1,6 @@
module Alerts.Api exposing (fetchAlerts, fetchReceivers)
module Alerts.Api exposing (fetchAlertGroups, fetchAlerts, fetchReceivers)
import Data.AlertGroup exposing (AlertGroup)
import Data.GettableAlert exposing (GettableAlert)
import Data.Receiver exposing (Receiver)
import Json.Decode
@ -18,6 +19,15 @@ fetchReceivers apiUrl =
)
fetchAlertGroups : String -> Filter -> Cmd (ApiData (List AlertGroup))
fetchAlertGroups apiUrl filter =
let
url =
String.join "/" [ apiUrl, "alerts", "groups" ++ generateQueryString filter ]
in
Utils.Api.send (Utils.Api.get url (Json.Decode.list Data.AlertGroup.decoder))
fetchAlerts : String -> Filter -> Cmd (ApiData (List GettableAlert))
fetchAlerts apiUrl filter =
let

View File

@ -22,6 +22,7 @@ import Url exposing (percentEncode)
type alias Filter =
{ text : Maybe String
, group : Maybe String
, customGrouping : Bool
, receiver : Maybe String
, showSilenced : Maybe Bool
, showInhibited : Maybe Bool
@ -32,6 +33,7 @@ nullFilter : Filter
nullFilter =
{ text = Nothing
, group = Nothing
, customGrouping = False
, receiver = Nothing
, showSilenced = Nothing
, showInhibited = Nothing
@ -44,7 +46,7 @@ generateQueryParam name =
generateQueryString : Filter -> String
generateQueryString { receiver, showSilenced, showInhibited, text, group } =
generateQueryString { receiver, customGrouping, showSilenced, showInhibited, text, group } =
let
parts =
[ ( "silenced", Maybe.withDefault False showSilenced |> boolToString |> Just )
@ -52,6 +54,7 @@ generateQueryString { receiver, showSilenced, showInhibited, text, group } =
, ( "filter", emptyToNothing text )
, ( "receiver", emptyToNothing receiver )
, ( "group", group )
, ( "customGrouping", boolToMaybeString customGrouping )
]
|> List.filterMap (\( a, b ) -> generateQueryParam a b)
in
@ -64,6 +67,15 @@ generateQueryString { receiver, showSilenced, showInhibited, text, group } =
""
boolToMaybeString : Bool -> Maybe String
boolToMaybeString b =
if b then
Just "true"
else
Nothing
boolToString : Bool -> String
boolToString b =
if b then

View File

@ -5,8 +5,13 @@ import Url.Parser.Query as Query
import Utils.Filter exposing (Filter, MatchOperator(..), parseMatcher)
boolParam : String -> Query.Parser (Maybe Bool)
boolParam : String -> Query.Parser Bool
boolParam name =
Query.custom name (List.head >> (/=) Nothing)
maybeBoolParam : String -> Query.Parser (Maybe Bool)
maybeBoolParam name =
Query.custom name
(List.head >> Maybe.map (String.toLower >> (/=) "false"))
@ -16,7 +21,8 @@ alertsParser =
s "alerts"
<?> Query.string "filter"
<?> Query.string "group"
<?> boolParam "customGrouping"
<?> Query.string "receiver"
<?> boolParam "silenced"
<?> boolParam "inhibited"
<?> maybeBoolParam "silenced"
<?> maybeBoolParam "inhibited"
|> map Filter

View File

@ -1,6 +1,12 @@
module Views.AlertList.Types exposing (AlertListMsg(..), Model, Tab(..), initAlertList)
module Views.AlertList.Types exposing
( AlertListMsg(..)
, Model
, Tab(..)
, initAlertList
)
import Browser.Navigation exposing (Key)
import Data.AlertGroup exposing (AlertGroup)
import Data.GettableAlert exposing (GettableAlert)
import Utils.Types exposing (ApiData(..))
import Views.FilterBar.Types as FilterBar
@ -10,6 +16,7 @@ import Views.ReceiverBar.Types as ReceiverBar
type AlertListMsg
= AlertsFetched (ApiData (List GettableAlert))
| AlertGroupsFetched (ApiData (List AlertGroup))
| FetchAlerts
| MsgForReceiverBar ReceiverBar.Msg
| MsgForFilterBar FilterBar.Msg
@ -27,6 +34,7 @@ type Tab
type alias Model =
{ alerts : ApiData (List GettableAlert)
, alertGroups : ApiData (List AlertGroup)
, receiverBar : ReceiverBar.Model
, groupBar : GroupBar.Model
, filterBar : FilterBar.Model
@ -39,6 +47,7 @@ type alias Model =
initAlertList : Key -> Model
initAlertList key =
{ alerts = Initial
, alertGroups = Initial
, receiverBar = ReceiverBar.initReceiverBar key
, groupBar = GroupBar.initGroupBar key
, filterBar = FilterBar.initFilterBar key

View File

@ -14,21 +14,26 @@ import Views.ReceiverBar.Updates as ReceiverBar
update : AlertListMsg -> Model -> Filter -> String -> String -> ( Model, Cmd Types.Msg )
update msg ({ groupBar, filterBar, receiverBar } as model) filter apiUrl basePath =
update msg ({ groupBar, alerts, filterBar, receiverBar, alertGroups } as model) filter apiUrl basePath =
let
alertsUrl =
basePath ++ "#/alerts"
in
case msg of
AlertGroupsFetched listOfAlertGroups ->
( { model | alertGroups = listOfAlertGroups }
, Cmd.none
)
AlertsFetched listOfAlerts ->
( { model
| alerts = listOfAlerts
, groupBar =
case listOfAlerts of
Success alerts ->
Success ungroupedAlerts ->
{ groupBar
| list =
List.concatMap (.labels >> Dict.toList) alerts
List.concatMap (.labels >> Dict.toList) ungroupedAlerts
|> List.map Tuple.first
|> Set.fromList
}
@ -47,9 +52,29 @@ update msg ({ groupBar, filterBar, receiverBar } as model) filter apiUrl basePat
newFilterBar =
FilterBar.setMatchers filter filterBar
in
( { model | alerts = Loading, filterBar = newFilterBar, groupBar = newGroupBar, activeId = Nothing }
( { model
| alerts =
if filter.customGrouping then
Loading
else
alerts
, alertGroups =
if filter.customGrouping then
alertGroups
else
Loading
, filterBar = newFilterBar
, groupBar = newGroupBar
, activeId = Nothing
}
, Cmd.batch
[ Api.fetchAlerts apiUrl filter |> Cmd.map (AlertsFetched >> MsgForAlertList)
[ if filter.customGrouping then
Api.fetchAlerts apiUrl filter |> Cmd.map (AlertsFetched >> MsgForAlertList)
else
Api.fetchAlertGroups apiUrl filter |> Cmd.map (AlertGroupsFetched >> MsgForAlertList)
, ReceiverBar.fetchReceivers apiUrl |> Cmd.map (MsgForReceiverBar >> MsgForAlertList)
]
)

View File

@ -1,5 +1,6 @@
module Views.AlertList.Views exposing (view)
import Data.AlertGroup exposing (AlertGroup)
import Data.GettableAlert exposing (GettableAlert)
import Dict exposing (Dict)
import Html exposing (..)
@ -35,15 +36,24 @@ renderCheckbox textLabel maybeShowSilenced toggleMsg =
]
groupTabName : Bool -> Html msg
groupTabName customGrouping =
if customGrouping then
text "Group (custom)"
else
text "Group"
view : Model -> Filter -> Html Msg
view { alerts, groupBar, filterBar, receiverBar, tab, activeId } filter =
view { alerts, alertGroups, groupBar, filterBar, receiverBar, tab, activeId } filter =
div []
[ div
[ class "card mb-5" ]
[ div [ class "card-header" ]
[ ul [ class "nav nav-tabs card-header-tabs" ]
[ Utils.Views.tab FilterTab tab (SetTab >> MsgForAlertList) [ text "Filter" ]
, Utils.Views.tab GroupTab tab (SetTab >> MsgForAlertList) [ text "Group" ]
, Utils.Views.tab GroupTab tab (SetTab >> MsgForAlertList) [ groupTabName filter.customGrouping ]
, receiverBar
|> ReceiverBar.view filter.receiver
|> Html.map (MsgForReceiverBar >> MsgForAlertList)
@ -57,10 +67,14 @@ view { alerts, groupBar, filterBar, receiverBar, tab, activeId } filter =
Html.map (MsgForFilterBar >> MsgForAlertList) (FilterBar.view filterBar)
GroupTab ->
Html.map (MsgForGroupBar >> MsgForAlertList) (GroupBar.view groupBar)
Html.map (MsgForGroupBar >> MsgForAlertList) (GroupBar.view groupBar filter.customGrouping)
]
]
, Utils.Views.apiData (customAlertGroups activeId groupBar) alerts
, if filter.customGrouping then
Utils.Views.apiData (customAlertGroups activeId groupBar) alerts
else
Utils.Views.apiData (defaultAlertGroups activeId) alertGroups
]
@ -85,6 +99,22 @@ customAlertGroups activeId { fields } ungroupedAlerts =
)
defaultAlertGroups : Maybe String -> List AlertGroup -> Html Msg
defaultAlertGroups activeId groups =
case groups of
[] ->
Utils.Views.error "No alert groups found"
_ ->
div []
(List.map
(\{ labels, alerts } ->
alertGroup activeId (Dict.toList labels) alerts
)
groups
)
alertGroup : Maybe String -> Labels -> List GettableAlert -> Html Msg
alertGroup activeId labels alerts =
div []

View File

@ -25,6 +25,7 @@ type Msg
| Focus Bool
| ResultsHovered Bool
| UpdateFieldText String
| CustomGrouping Bool
| Noop

View File

@ -12,6 +12,14 @@ import Views.GroupBar.Types exposing (Model, Msg(..))
update : String -> Filter -> Msg -> Model -> ( Model, Cmd Msg )
update url filter msg model =
case msg of
CustomGrouping customGrouping ->
( model
, Cmd.batch
[ Navigation.pushUrl model.key (url ++ generateQueryString { filter | customGrouping = customGrouping })
, Dom.focus "group-by-field" |> Task.attempt (always Noop)
]
)
AddField emptyFieldText text ->
immediatelyFilter url
filter

View File

@ -6,11 +6,12 @@ import Html.Events exposing (keyCode, on, onBlur, onClick, onFocus, onInput, onM
import Set
import Utils.Keyboard exposing (keys, onKeyDown, onKeyUp)
import Utils.List
import Utils.Views
import Views.GroupBar.Types exposing (Model, Msg(..))
view : Model -> Html Msg
view ({ list, fieldText, fields } as model) =
view : Model -> Bool -> Html Msg
view ({ list, fieldText, fields } as model) customGrouping =
let
isDisabled =
not (Set.member fieldText list) || List.member fieldText fields
@ -24,20 +25,31 @@ view ({ list, fieldText, fields } as model) =
else
"has-success"
checkbox =
div [ class "mb-3" ]
[ Utils.Views.checkbox "Enable custom grouping" customGrouping CustomGrouping ]
in
div
[ class "row no-gutters align-items-start" ]
(List.map viewField fields
++ [ div
[ class ("col " ++ className)
, style "min-width" "200px"
]
[ textInputField isDisabled model
, exampleField fields
, autoCompleteResults model
]
]
)
if customGrouping then
div []
[ checkbox
, div
[ class "row no-gutters align-items-start" ]
(List.map viewField fields
++ [ div
[ class ("col " ++ className)
, style "min-width" "200px"
]
[ textInputField isDisabled model
, exampleField fields
, autoCompleteResults model
]
]
)
]
else
checkbox
exampleField : List String -> Html Msg

View File

@ -9,6 +9,6 @@ silenceListParser : Parser (Filter -> a) a
silenceListParser =
map
(\t ->
Filter t Nothing Nothing Nothing Nothing
Filter t Nothing False Nothing Nothing Nothing
)
(s "silences" <?> Query.string "filter")

View File

@ -101,5 +101,6 @@ updateFilter maybeFilter =
, showSilenced = Nothing
, showInhibited = Nothing
, group = Nothing
, customGrouping = False
, text = maybeFilter
}