Filter by labels on alerts

This should be encoded in the URL and stored
there, and used as the "source of truth" for
parsing and propagating the filters.
This commit is contained in:
stuart nelson 2017-02-10 18:03:14 +01:00
parent 4d4791e0fb
commit 9126426eff
9 changed files with 199 additions and 99 deletions

View File

@ -1,19 +1,24 @@
module Alerts.Translator exposing (translator)
import Alerts.Types exposing (Msg(..), Alert, AlertsMsg, OutMsg(..))
import Utils.Types exposing (Filter)
type alias TranslationDictionary msg =
{ onInternalMessage : AlertsMsg -> msg
, onSilenceFromAlert : Alert -> msg
, onUpdateFilter : Filter -> String -> msg
}
translator : TranslationDictionary parentMsg -> Msg -> parentMsg
translator { onInternalMessage, onSilenceFromAlert } msg =
translator { onInternalMessage, onSilenceFromAlert, onUpdateFilter } msg =
case msg of
ForSelf internal ->
onInternalMessage internal
ForParent (SilenceFromAlert alert) ->
onSilenceFromAlert alert
ForParent (UpdateFilter filter string) ->
onUpdateFilter filter string

View File

@ -2,7 +2,7 @@ module Alerts.Types exposing (..)
import Http exposing (Error)
import ISO8601
import Utils.Types exposing (ApiData)
import Utils.Types exposing (ApiData, Filter)
type Route
@ -16,12 +16,14 @@ type Msg
type OutMsg
= SilenceFromAlert Alert
| UpdateFilter Filter String
type AlertsMsg
= AlertGroupsFetch (ApiData (List AlertGroup))
| FetchAlertGroups
| Noop
| FilterAlerts
type alias Block =

View File

@ -3,22 +3,154 @@ module Alerts.Update exposing (..)
import Alerts.Api as Api
import Alerts.Types exposing (..)
import Task
import Utils.Types exposing (ApiData, ApiResponse(..))
import Utils.Types exposing (ApiData, ApiResponse(..), Filter)
import Regex
update : AlertsMsg -> ApiData (List AlertGroup) -> ( ApiData (List AlertGroup), Cmd Msg )
update msg groups =
update : AlertsMsg -> ApiData (List AlertGroup) -> Filter -> ( ApiData (List AlertGroup), Filter, Cmd Msg )
update msg groups filter =
case msg of
AlertGroupsFetch alertGroups ->
( alertGroups, Cmd.none )
( alertGroups, filter, Cmd.none )
FetchAlertGroups ->
( groups, Api.getAlertGroups )
( groups, filter, Api.getAlertGroups )
Noop ->
( groups, Cmd.none )
( groups, filter, Cmd.none )
FilterAlerts ->
let
f =
case groups of
Success groups ->
let
replace =
(Regex.replace Regex.All (Regex.regex "{|}|\"|\\s") (\_ -> ""))
matches =
String.split "," (replace filter.text)
labels =
List.filterMap
(\m ->
let
label =
String.split "=" m
in
if List.length label == 2 then
Just ( Maybe.withDefault "" (List.head label), Maybe.withDefault "" (List.head <| List.reverse label) )
else
Nothing
)
matches
in
-- Instead of changing filter, change the query string and that then is parsed into the filter structure
{ filter | labels = labels }
_ ->
filter
in
( groups, f, Cmd.none )
urlUpdate : Route -> AlertsMsg
urlUpdate _ =
FetchAlertGroups
filterBy : (a -> Maybe a) -> List a -> List a
filterBy fn groups =
List.filterMap fn groups
filterByReceiver : Maybe String -> List AlertGroup -> List AlertGroup
filterByReceiver maybeReceiver groups =
case maybeReceiver of
Just receiver ->
filterBy (filterAlertGroup receiver) groups
Nothing ->
groups
filterAlertGroup : String -> AlertGroup -> Maybe AlertGroup
filterAlertGroup receiver alertGroup =
let
blocks =
List.filter (\b -> receiver == b.routeOpts.receiver) alertGroup.blocks
in
if not <| List.isEmpty blocks then
Just { alertGroup | blocks = blocks }
else
Nothing
filterBySilenced : Maybe Bool -> List AlertGroup -> List AlertGroup
filterBySilenced maybeShowSilenced groups =
case maybeShowSilenced of
Just showSilenced ->
groups
Nothing ->
filterBy filterAlertGroupSilenced groups
filterAlertsFromBlock : (Alert -> Bool) -> Block -> Maybe Block
filterAlertsFromBlock fn block =
let
alerts =
List.filter fn block.alerts
in
if not <| List.isEmpty alerts then
Just { block | alerts = alerts }
else
Nothing
filterAlertsByLabel : List ( String, String ) -> Block -> Maybe Block
filterAlertsByLabel labels block =
filterAlertsFromBlock
(\a ->
-- Check that all labels are present within the alert's label set.
List.all
(\l ->
List.member l a.labels
)
labels
)
block
filterAlertGroupLabels : List ( String, String ) -> AlertGroup -> Maybe AlertGroup
filterAlertGroupLabels labels alertGroup =
let
blocks =
List.filterMap (filterAlertsByLabel labels) alertGroup.blocks
in
if not <| List.isEmpty blocks then
Just { alertGroup | blocks = blocks }
else
Nothing
filterAlertGroupSilenced : AlertGroup -> Maybe AlertGroup
filterAlertGroupSilenced alertGroup =
let
blocks =
List.filterMap filterSilencedAlerts alertGroup.blocks
in
if not <| List.isEmpty blocks then
Just { alertGroup | blocks = blocks }
else
Nothing
filterSilencedAlerts : Block -> Maybe Block
filterSilencedAlerts block =
filterAlertsFromBlock (\a -> not a.silenced) block
filterByLabels : List ( String, String ) -> List AlertGroup -> List AlertGroup
filterByLabels labels groups =
filterBy (filterAlertGroupLabels labels) groups

View File

@ -2,12 +2,41 @@ module Alerts.Views exposing (view)
import Alerts.Types exposing (Alert, AlertGroup, Block, Route(..))
import Alerts.Types exposing (AlertsMsg(..), Msg(..), OutMsg(..))
import Alerts.Update exposing (filterBySilenced, filterByReceiver, filterByLabels)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Utils.Date
import Utils.Types exposing (Filter)
import Utils.Views exposing (..)
view : Route -> List AlertGroup -> Filter -> Html Msg
view route alertGroups filter =
let
groups =
case route of
Receiver maybeReceiver maybeShowSilenced ->
filterByReceiver maybeReceiver alertGroups
|> filterBySilenced maybeShowSilenced
|> filterByLabels filter.labels
in
if List.isEmpty groups then
div [] [ text "no alerts found found" ]
else
div []
[ Html.map ForParent (textField "Filter" filter.text (UpdateFilter filter))
, a [ class "f6 link br2 ba ph3 pv2 mr2 dib blue", onClick (ForSelf FilterAlerts) ] [ text "Filter Alerts" ]
, ul
[ classList
[ ( "list", True )
, ( "pa0", True )
]
]
(List.map alertGroupView groups)
]
alertGroupView : AlertGroup -> Html Msg
alertGroupView alertGroup =
li [ class "pa3 pa4-ns bb b--black-10" ]
@ -54,84 +83,3 @@ alertHeader ( key, value ) =
b [ class "db f4 mr2 dark-red dib" ] [ text value ]
else
listButton "ph1 pv1" ( key, value )
view : Route -> List AlertGroup -> Html Msg
view route alertGroups =
let
groups =
case route of
Receiver maybeReceiver maybeShowSilenced ->
filterBySilenced maybeShowSilenced <| filterByReceiver maybeReceiver alertGroups
in
if List.isEmpty groups then
div [] [ text "no alerts found found" ]
else
ul
[ classList
[ ( "list", True )
, ( "pa0", True )
]
]
(List.map alertGroupView groups)
filterBy : (a -> Maybe a) -> List a -> List a
filterBy fn groups =
List.filterMap fn groups
filterByReceiver : Maybe String -> List AlertGroup -> List AlertGroup
filterByReceiver maybeReceiver groups =
case maybeReceiver of
Just receiver ->
filterBy (filterAlertGroup receiver) groups
Nothing ->
groups
filterAlertGroup : String -> AlertGroup -> Maybe AlertGroup
filterAlertGroup receiver alertGroup =
let
blocks =
List.filter (\b -> receiver == b.routeOpts.receiver) alertGroup.blocks
in
if not <| List.isEmpty blocks then
Just { alertGroup | blocks = blocks }
else
Nothing
filterBySilenced : Maybe Bool -> List AlertGroup -> List AlertGroup
filterBySilenced maybeShowSilenced groups =
case maybeShowSilenced of
Just showSilenced ->
groups
Nothing ->
filterBy filterAlertGroupSilenced groups
filterAlertGroupSilenced : AlertGroup -> Maybe AlertGroup
filterAlertGroupSilenced alertGroup =
let
blocks =
List.filterMap filterSilencedAlerts alertGroup.blocks
in
if not <| List.isEmpty blocks then
Just { alertGroup | blocks = blocks }
else
Nothing
filterSilencedAlerts : Block -> Maybe Block
filterSilencedAlerts block =
let
alerts =
List.filter (\a -> not a.silenced) block.alerts
in
if not <| List.isEmpty alerts then
Just { block | alerts = alerts }
else
Nothing

View File

@ -30,7 +30,8 @@ init location =
route =
Parsing.urlParser location
in
update (urlUpdate location) (Model Loading Loading Loading route)
-- Need to parse out the filter text with the url parser
update (urlUpdate location) (Model Loading Loading Loading route ({ text = "", labels = [] }))
update : Msg -> Model -> ( Model, Cmd Msg )
@ -55,17 +56,17 @@ update msg model =
alertsMsg =
(Alerts.Update.urlUpdate alertsRoute)
( alertGroups, alertCmd ) =
Alerts.Update.update alertsMsg model.alertGroups
( alertGroups, filter, alertCmd ) =
Alerts.Update.update alertsMsg model.alertGroups model.filter
in
( { model | alertGroups = alertGroups, route = AlertsRoute alertsRoute }, Cmd.map alertTranslator alertCmd )
( { model | alertGroups = alertGroups, filter = filter, route = AlertsRoute alertsRoute }, Cmd.map alertTranslator alertCmd )
Alerts alertsMsg ->
let
( alertGroups, alertCmd ) =
Alerts.Update.update alertsMsg model.alertGroups
( alertGroups, filter, alertCmd ) =
Alerts.Update.update alertsMsg model.alertGroups model.filter
in
( { model | alertGroups = alertGroups }, Cmd.map alertTranslator alertCmd )
( { model | alertGroups = alertGroups, filter = filter }, Cmd.map alertTranslator alertCmd )
Silences silencesMsg ->
let
@ -92,6 +93,9 @@ update msg model =
RedirectAlerts ->
( model, Navigation.newUrl "/#/alerts" )
UpdateFilter filter text ->
( { model | filter = { filter | text = text } }, Cmd.none )
Noop ->
( model, Cmd.none )

View File

@ -4,7 +4,7 @@ import Alerts.Types
import Alerts.Translator
import Silences.Types
import Silences.Translator
import Types exposing (Msg(Alerts, CreateSilenceFromAlert, Silences))
import Types exposing (Msg(Alerts, CreateSilenceFromAlert, Silences, UpdateFilter))
alertTranslator : Alerts.Types.Msg -> Msg
@ -12,6 +12,7 @@ alertTranslator =
Alerts.Translator.translator
{ onInternalMessage = Alerts
, onSilenceFromAlert = CreateSilenceFromAlert
, onUpdateFilter = UpdateFilter
}

View File

@ -7,7 +7,7 @@ import Silences.Types exposing (SilencesMsg, Silence)
import Http exposing (Error)
import ISO8601
import Time
import Utils.Types exposing (ApiData)
import Utils.Types exposing (ApiData, Filter)
-- Internal Imports
@ -19,6 +19,7 @@ type alias Model =
, silence : ApiData Silence
, alertGroups : ApiData (List AlertGroup)
, route : Route
, filter : Filter
}
@ -30,6 +31,7 @@ type Msg
| NewSilence
| EditSilence Int
| CreateSilenceFromAlert Alert
| UpdateFilter Filter String
| NavigateToAlerts Alerts.Types.Route
| Alerts AlertsMsg
| Silences SilencesMsg

View File

@ -10,6 +10,12 @@ type ApiResponse e a
| Success a
type alias Filter =
{ text : String
, labels : List ( String, String )
}
type alias ApiData a =
ApiResponse Http.Error a

View File

@ -22,7 +22,7 @@ view model =
AlertsRoute route ->
case model.alertGroups of
Success alertGroups ->
Html.map alertTranslator (Alerts.Views.view route alertGroups)
Html.map alertTranslator (Alerts.Views.view route alertGroups model.filter)
Loading ->
loading