Skip to content

Instantly share code, notes, and snippets.

@dvekeman
Created August 2, 2019 17:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dvekeman/5f1cda64e8882fb94f8ede7d4852b09d to your computer and use it in GitHub Desktop.
Save dvekeman/5f1cda64e8882fb94f8ede7d4852b09d to your computer and use it in GitHub Desktop.
Original post
*original post*
I've come across the same problem in the past, thought about it a few times already but never came up with a proper solution.
Probably the approach by @rupert and @pdamoc are the best ones.
One other alternative would be to allow Main.elm peek into the Msg of the child pages and intercept some of them. I realize this is not the cleanest thing to do but it's the tradeof.
(note: this is just an approach I drafted out quickly as an example for this post and I haven't really tried it in a real application)
*Main.elm*
```
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
SomePageMsg (SomePage.AuthFailMsg failType) ->
case failType of
AuthMsg.Unauthenticated -> handleUnauthenticated model
AuthMsg.Unauthorized -> handleUnauthorized model
SomePageMsg somePageMsg ->
-- standard dispatching here: SomePage.update ...
```
In `SomePage.elm` I changed a few things
- Expose `Msg(..)` instead of `Msg` (not ideal but I'm not exposing the internal messages, see code below)
- Split up the Msg type into a plain message type and an http-with-authentication message type
```
module SomePage exposing (Model, Msg(..), init, update, view)
...
type Msg
= PlainMsg PlainMsg
| AuthFailMsg AuthMsg.AuthFailure -- AuthFailure will be reused across all child pages but basically it's Unauthenticated (401) or Unauthorized (403)
| AuthSuccessMsg AuthSuccessMsg
type PlainMsg = ... -- These are the regular messages FetchData, SetFieldValue, ...
type AuthSuccessMsg -- HTTP Result which succeeded authentication and authorization (but might still fail on something else)
= RequestPerformed (Result Http.Error BasicAuthResponse)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
PlainMsg submsg ->
...
-- Handled in Main.elm
AuthFailMsg _ ->
( model, Cmd.none )
-- 'Ok' processing
AuthSuccessMsg (RequestPerformed (Ok response)) ->
...
-- Any other HTTP error (Timeout, HTTP 500, ...)
-- (or fallback in case Main does not handle the message)
AuthSuccessMsg (RequestPerformed (Err error)) ->
...
```
It does mean that I need to wrap the messages in this page:
*SomePage.elm (C'ud)*
```
view : Model -> Html Msg
view model =
...
Html.button
[ Html.onClick (PlainMsg NoAuthRequest) ]
[ ... ]
-- Note the `withAuth`
someHttpGet : String -> Cmd Msg
someHttpGet url =
Http.get
{ url = url
, expect = Http.expectJson (withAuth RequestPerformed) basicAuthResponseDecoder
}
```
The `someHttpGet` function uses a helper `withAuth` to wrap the message
*SomePage.elm (C'ud)*
```
withAuth :
(Result Http.Error a -> AuthSuccessMsg)
-> Result Http.Error a
-> Msg
withAuth =
AuthMsg.withAuth AuthFailMsg AuthSuccessMsg
```
Which itself is a specific version of a more general `withAuth` function
*AuthMsg.elm*
```
module AuthMsg exposing (AuthFailure(..), withAuth)
{-| This function wraps a message with unauthenticated / unauthorized checks
-}
withAuth :
(AuthFailure -> msg) -- ^ The message constructor in case of failure (unauthenticated | unauthorized)
-> (authMsg -> msg) -- ^ The message constructor in case of success
-> (Result Http.Error a -> authMsg) -- ^ The sub-message constructor
-> Result Http.Error a -- ^ The HTTP result
-> msg
withAuth failMsg successMsg toAuthMsg result =
if isUnauthenticated result then
failMsg Unauthenticated
else if isUnauthorized result then
failMsg Unauthorized
else
successMsg <| toAuthMsg result
type AuthFailure = Unauthenticated | Unauthorized
```
Sorry for the lengthy post..
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment