Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved Validation #827

Merged
merged 14 commits into from
May 28, 2017
Merged

Improved Validation #827

merged 14 commits into from
May 28, 2017

Conversation

w0rm
Copy link
Member

@w0rm w0rm commented May 24, 2017

  1. Extracted affected alerts out of the silence type
  2. Unified ApiData
  3. Added separate validation messages for each field to validate on blur
  4. Changed Update/Preview messages so they create the silence from the form or trigger validation errors
  5. If there was an error from the backend when saving a silence, it will be displayed

I thought of changing the form styles in this PR, but I think it's too much.

@@ -8,7 +8,7 @@ import Types
( Route(..)
, Msg
( NavigateToSilenceList
, NavigateToSilence
, NavigateToSilenceView
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big thing: Why adding View to NavigateToSilence msg? We don't do it with the other ones.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I renamed Silence to SilenceView, which represents a page. I didn't want to confuse it with a Silence type.

@@ -54,7 +54,7 @@ init location =
_ ->
nullFilter
in
update (urlUpdate location) (Model initSilenceList Loading initSilenceForm initAlertList route filter initStatusModel)
update (urlUpdate location) (Model initSilenceList initSilenceView initSilenceForm initAlertList route filter initStatusModel)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

= Initial
| Loading
| Failure e
| Failure String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having a string instead of a type variable forces us to drop information, doesn't it? Can't we keep the type variable here and convert an error to a string in the view functions?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where we require this information. I need to create the error myself, when e.g. validation fails. It is easier to create it from String, rather than manually create Http.Error, I don't think this is even possible.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can always change the Failure String to be a Failure of our own union type if we need to extract additional information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

( initialField
, ValidationState(..)
, ValidatedField
, validate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I smell a library forming here

}
)
(stringNotEmpty comment.value)
(List.foldr appendMatcher (Ok []) matchers)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I don't specify any matchers and create a silence, I get redirected to the /silences page without any error messages in the UI. The API call fails with silence invalid: at least one matcher required. Should we enforce having at least one matcher in this PR or in an upcoming one? Sorry, I missed this requirement in my last changes as well.

Copy link
Member Author

@w0rm w0rm May 24, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot to remove the redirect. Now it should display the error from the backend. It is not pretty, but I think we should address this later.

screen shot 2017-05-24 at 18 08 37

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely good enough for now. Thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I've got an idea. We should just hide the delete button, when there is only one matcher left. Will do it in another pull request after this is merged.

)
agg
validate : (String -> Result String a) -> ValidatedField -> ValidatedField
validate validate field =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we do validate validator field = instead? A little confusing to see the same term twice.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed

validatedMatcherToMatcher matcher =
Result.map2 (\n v -> Matcher n v matcher.isRegex) matcher.name matcher.value
|> Result.mapError second
stringNotEmpty : String -> Result String String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the long run we should trim the string as well so a space is not valid either.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added String.trim

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks.

import Time


type ApiResponse e a
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I see it correctly we get rid of ApiResponse? 👍 for removing an extra layer of complexity.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had no idea of why both were there. It was pretty confusing

@mxinden
Copy link
Member

mxinden commented May 25, 2017

Related to #798.

@mxinden mxinden mentioned this pull request May 25, 2017
Copy link
Member

@mxinden mxinden left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. Thanks @w0rm

@@ -27,8 +28,8 @@ view model =
error msg


silence2 : Silence -> Html Msg
silence2 silence =
silence2 : ApiData (List Alert) -> Silence -> Html SilenceViewMsg
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a random question: Is there a particular reason we called this silence2?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe because silence is taken. I guess it's a question to @stuartnelson3

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed it to viewSilence

Copy link
Contributor

@stuartnelson3 stuartnelson3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks awesome!

One thing I noticed: When deleting all matchers and then "previewing" a silence, it returns all available alerts (since we aren't filtering by anything).

I think it would make sense to disable the preview button if len(matchers) == 0.

import Utils.Filter exposing (Filter)


type alias Model =
{ silenceList : SilenceList.Model
, silence : ApiData Silence
, silenceView : SilenceView.Model
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm happy to see the final part of our top-level model refactored into its own model

@@ -30,10 +29,6 @@ type alias Label =
( String, String )


type alias ApiData a =
ApiResponse Http.Error a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I originally brought this in from reading the RemoteData blog post. I think the other had a generic type, and then made a specific type alias for dealing with http requests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since that thing was a library, it had to be more generic. We don't need more than String for error at the moment. Changing this to smth else later won't be a problem.

Ok inputValue ->
validatedField : (List (Attribute msg) -> List (Html msg) -> Html msg) -> String -> String -> (String -> msg) -> msg -> ValidatedField -> Html msg
validatedField htmlField labelText classes inputMsg blurMsg field =
case field.validationState of
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three different cases in this function differ only by the css classes has-success, has-danger, or the absence of these two. Would it be possible to just write the HTML once and switch on the css class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stuartnelson3 another class is applied to the input field (form-control-success or form-control-danger), plus there is an error message in the error case. I think this is fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mk

]
div []
[ p [] [ text <| "Error: " ++ err ]
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for moving this logic out of the view

( initialField
, ValidationState(..)
, ValidatedField
, validate
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I smell a library forming here

validateForm : SilenceForm -> SilenceForm
validateForm { id, createdBy, comment, startsAt, endsAt, duration, matchers } =
{ id = id
, createdBy = validate stringNotEmpty createdBy
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mmm love that validate function

, form : SilenceForm
{ form : SilenceForm
, silenceId : ApiData String
, alerts : ApiData (List Alert)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like having alerts exist independently on this and the silence view model, +1

case silence of
Success id ->
( model, Navigation.newUrl ("/#/silences/" ++ id) )
SilenceCreate silenceId ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can definitely re-think these names I came up with. SilenceCreate and CreateSilence could definitely be better.

(note for the future)

@w0rm
Copy link
Member Author

w0rm commented May 28, 2017

@stuartnelson3 I made it impossible to delete the last matcher, but accidentally committed True instead of using showDeleteButton. Thanks, fixed now!

Copy link
Contributor

@stuartnelson3 stuartnelson3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@stuartnelson3 stuartnelson3 merged commit 562f995 into master May 28, 2017
@stuartnelson3 stuartnelson3 deleted the validation branch May 28, 2017 10:53
hh pushed a commit to ii/alertmanager that referenced this pull request Feb 28, 2018
hh pushed a commit to ii/alertmanager that referenced this pull request Mar 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants