11 files changed,
122 insertions(+),
68 deletions(-)
Author:
Smirnov Oleksandr
ss2316544@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2025-06-20 16:57:47 +0300
Parent:
9de58ad
jump to
M
web/src/Api.elm
@@ -1,46 +1,31 @@
-module Api exposing (HttpRequestDetails, Response(..), errorToFriendlyMessage) +module Api exposing (Error(..), Response(..), errorMessage) import Http import Json.Decode +type Error + = HttpError + { message : String + , reason : Http.Error + } + | JsonDecodeError + { message : String + , reason : Json.Decode.Error + } + + type Response value = Loading | Success value - | Failure Http.Error + | Failure Error -type alias HttpRequestDetails msg = - { endpoint : String - , method : String - , body : Http.Body - , decoder : Json.Decode.Decoder msg - , onHttpError : Http.Error -> msg - } +errorMessage : Error -> String +errorMessage error = + case error of + HttpError err -> + err.message - -errorToFriendlyMessage : Http.Error -> String -errorToFriendlyMessage httpError = - case httpError of - Http.BadUrl _ -> - "This page requested a bad URL" - - Http.Timeout -> - "Request took too long to respond" - - Http.NetworkError -> - "Could not connect to the API" - - Http.BadStatus code -> - case code of - 404 -> - "Not found" - - 401 -> - "Unauthorized" - - _ -> - "API returned an error code" - - Http.BadBody _ -> - "Unexpected response from API" + JsonDecodeError err -> + err.message
M
web/src/Api/Auth.elm
@@ -1,5 +1,6 @@
module Api.Auth exposing (refreshToken, signin, signup) +import Api import Data.Credentials as Credentials exposing (Credentials) import Effect exposing (Effect) import Http@@ -8,7 +9,7 @@ import Json.Encode as Encode
signin : - { onResponse : Result Http.Error Credentials -> msg + { onResponse : Result Api.Error Credentials -> msg , email : String , password : String }@@ -32,7 +33,7 @@ }
signup : - { onResponse : Result Http.Error () -> msg + { onResponse : Result Api.Error () -> msg , email : String , password : String }@@ -56,7 +57,7 @@ }
refreshToken : - { onResponse : Result Http.Error Credentials -> msg + { onResponse : Result Api.Error Credentials -> msg , refreshToken : String } -> Effect msg
M
web/src/Api/Me.elm
@@ -1,11 +1,12 @@
module Api.Me exposing (get) +import Api import Data.Me as Me exposing (Me) import Effect exposing (Effect) import Http -get : { onResponse : Result Http.Error Me -> msg } -> Effect msg +get : { onResponse : Result Api.Error Me -> msg } -> Effect msg get options = Effect.sendApiRequest { endpoint = "/api/v1/me"
A
web/src/Data/Error.elm
@@ -0,0 +1,13 @@
+module Data.Error exposing (Error, decode) + +import Json.Decode + + +type alias Error = + { message : String } + + +decode : Json.Decode.Decoder Error +decode = + Json.Decode.map Error + (Json.Decode.field "message" Json.Decode.string)
M
web/src/Effect.elm
@@ -28,10 +28,11 @@ @docs map, toCmd
-} -import Api exposing (HttpRequestDetails) +import Api import Auth.User import Browser.Navigation import Data.Credentials exposing (Credentials) +import Data.Error import Dict exposing (Dict) import Http import Json.Decode@@ -59,6 +60,15 @@ -- SHARED
| SendSharedMsg Shared.Msg.Msg | SendToLocalStorage { key : String, value : Json.Encode.Value } | SendApiRequest (HttpRequestDetails msg) + + +type alias HttpRequestDetails msg = + { endpoint : String + , method : String + , body : Http.Body + , decoder : Json.Decode.Decoder msg + , onHttpError : Api.Error -> msg + }@@ -161,27 +171,25 @@ { endpoint : String
, method : String , body : Http.Body , decoder : Json.Decode.Decoder value - , onResponse : Result Http.Error value -> msg + , onResponse : Result Api.Error value -> msg } -> Effect msg sendApiRequest opts = let - onHttpError : Http.Error -> msg - onHttpError httpError = - opts.onResponse (Err httpError) + onSuccess : value -> msg + onSuccess value = + opts.onResponse (Ok value) - decoder : Json.Decode.Decoder msg - decoder = - opts.decoder - |> Json.Decode.map Ok - |> Json.Decode.map opts.onResponse + onHttpError : Api.Error -> msg + onHttpError err = + opts.onResponse (Err err) in SendApiRequest { endpoint = opts.endpoint , method = opts.method , body = opts.body , onHttpError = onHttpError - , decoder = decoder + , decoder = Json.Decode.map onSuccess opts.decoder }@@ -326,7 +334,7 @@ , url = opts.endpoint
, headers = headers , body = opts.body , expect = - Http.expectJson + Http.expectStringResponse (\httpResult -> case httpResult of Ok msg ->@@ -335,7 +343,36 @@
Err err -> opts.onHttpError err ) - opts.decoder + (\resp -> fromHttpResponseToCustomError opts.decoder resp) , timeout = Just (1000 * 60) -- 60 second timeout , tracker = Nothing } + + +fromHttpResponseToCustomError : Json.Decode.Decoder msg -> Http.Response String -> Result Api.Error msg +fromHttpResponseToCustomError decoder response = + case response of + Http.GoodStatus_ _ body -> + case Json.Decode.decodeString decoder body of + Ok data -> + Ok data + + Err err -> + Err (Api.JsonDecodeError { message = "Something unexpected happened", reason = err }) + + Http.BadStatus_ { statusCode } body -> + case Json.Decode.decodeString Data.Error.decode body of + Ok err -> + Err (Api.HttpError { message = err.message, reason = Http.BadStatus statusCode }) + + Err err -> + Err (Api.JsonDecodeError { message = "Something unexpected happened", reason = err }) + + Http.BadUrl_ url -> + Err (Api.HttpError { message = "Unexpected URL format", reason = Http.BadUrl url }) + + Http.Timeout_ -> + Err (Api.HttpError { message = "Request timed out, please try again", reason = Http.Timeout }) + + Http.NetworkError_ -> + Err (Api.HttpError { message = "Could not connect, please try again", reason = Http.NetworkError })
M
web/src/Pages/Auth.elm
@@ -8,7 +8,6 @@ import Effect exposing (Effect)
import Html exposing (Html) import Html.Attributes as Attr import Html.Events -import Http import Layouts import Page exposing (Page) import Route exposing (Route)@@ -38,7 +37,7 @@ , password : String
, passwordAgain : String , isSubmittingForm : Bool , formVariant : Variant - , error : Maybe Http.Error + , error : Maybe Api.Error }@@ -71,8 +70,8 @@ type Msg
= UserUpdatedInput Field String | UserChangedFormVariant Variant | UserClickedSubmit - | ApiSignInResponded (Result Http.Error Credentials) - | ApiSignUpResponded (Result Http.Error ()) + | ApiSignInResponded (Result Api.Error Credentials) + | ApiSignUpResponded (Result Api.Error ()) type Field@@ -198,13 +197,13 @@ ]
) -viewError : Maybe Http.Error -> Html Msg +viewError : Maybe Api.Error -> Html Msg viewError maybeError = case maybeError of Just error -> Html.div [ Attr.class "box bad" ] [ Html.strong [ Attr.class "block titlebar" ] [ Html.text "Error" ] - , Html.text (Api.errorToFriendlyMessage error) + , Html.text (Api.errorMessage error) ] Nothing ->@@ -230,9 +229,7 @@ viewForgotPassword : Html Msg
viewForgotPassword = Html.div [] [ Html.a - [ Attr.href "/forgot-password" - , Attr.class "gray" - ] + [ Attr.href "/forgot-password" ] [ Html.text "Forgot password?" ] ]
M
web/src/Pages/Profile/Me.elm
@@ -6,7 +6,6 @@ import Auth
import Data.Me exposing (Me) import Effect exposing (Effect) import Html exposing (Html) -import Http import Layouts import Page exposing (Page) import Route exposing (Route)@@ -45,7 +44,7 @@ -- UPDATE
type Msg - = ApiMeResponded (Result Http.Error Me) + = ApiMeResponded (Result Api.Error Me) update : Msg -> Model -> ( Model, Effect Msg )@@ -88,7 +87,7 @@ Api.Success user ->
viewUserDetails shared user Api.Failure err -> - Html.text (Api.errorToFriendlyMessage err) + Html.text (Api.errorMessage err) viewUserDetails : Shared.Model -> Me -> Html Msg
M
web/tests/UnitTests/Data/Credentiala.elm
@@ -9,7 +9,7 @@
suite : Test suite = describe "Data.Credentials" - [ test "decode credentials" <| + [ test "decode" <| \_ -> """ {
A
web/tests/UnitTests/Data/Error.elm
@@ -0,0 +1,21 @@
+module UnitTests.Data.Error exposing (suite) + +import Data.Error +import Expect +import Json.Decode as Json +import Test exposing (Test, describe, test) + + +suite : Test +suite = + describe "Data.Error" + [ test "decode" <| + \_ -> + """ + { + "message": "some kind of an error" + } + """ + |> Json.decodeString Data.Error.decode + |> Expect.equal (Ok { message = "some kind of an error" }) + ]
M
web/tests/UnitTests/Data/Me.elm
@@ -9,7 +9,7 @@
suite : Test suite = describe "Data.Me" - [ test "decode credentials" <| + [ test "decode" <| \_ -> """ {