Skip to content

Commit 80d0fde

Browse files
authored
Merge pull request #20 from reflex-frp/aa/better-xhr-error
Provide more informative xhr errors
2 parents 45dcf24 + 19c9138 commit 80d0fde

File tree

5 files changed

+41
-18
lines changed

5 files changed

+41
-18
lines changed

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Revision history for reflex-gadt-api
22

3+
## 0.3.0.0
4+
5+
* *Breaking*: switch to a more useful xhr error response type. To retain the current behavior, you must convert `Either XhrResponse` to `Either Text`. See `Readme.lhs` for an example using `xhrErrorToText`.
6+
37
## 0.2.2.3
48

59
* Support GHC 9.12

Readme.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,17 @@ The `Event` of responses comes, in this case, from a function that will take the
150150
```haskell
151151

152152
> responses <- case endpoint of
153-
> Left xhr -> performXhrRequests xhr (requests :: Event t (RequesterData CatApi))
153+
> Left xhr -> do
154+
> r <- performXhrRequests xhr (requests :: Event t (RequesterData CatApi))
155+
> performEvent $ ffor r $ traverseRequesterData $ \x ->
156+
> pure $ mapLeft xhrErrorToText x
154157
> Right ws -> performWebSocketRequests ws (requests :: Event t (RequesterData CatApi))
155158
> pure ()
156159
> where
160+
> mapLeft f = \case
161+
> Right a -> Right a
162+
> Left x -> Left $ f x
163+
>
157164

158165
```
159166

example/.obelisk/impl/github.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"repo": "obelisk",
44
"branch": "develop",
55
"private": false,
6-
"rev": "3b618ec6e42af4a6fffe87e8b424387ca06c767f",
7-
"sha256": "17cvazh0fcb74hrqsh8zh0siyr4sjm39dkrmdj07c0ng8v4ri82w"
6+
"rev": "d420659bf7b81094921519f1f9243f7f11cc3fc2",
7+
"sha256": "1irwjc783cr8s52vcfsz21zh17p61qp6kabaxn2qpkqfy7n2g71c"
88
}

reflex-gadt-api.cabal

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
cabal-version: >=1.10
1+
cabal-version: 3.0
22
name: reflex-gadt-api
3-
version: 0.2.2.3
3+
version: 0.3.0.0
44
synopsis: Interact with a GADT API in your reflex-dom application.
55
description:
66
This package is designed to be used in full-stack Haskell applications where the API is defined as a GADT and the frontend is using reflex-dom.
77

88
bug-reports: https://github.com/reflex-frp/reflex-gadt-api/issues
9-
license: BSD3
9+
license: BSD-3-Clause
1010
license-file: LICENSE
1111
author: Obsidian Systems
1212
maintainer: [email protected]
@@ -17,15 +17,14 @@ extra-source-files:
1717
ChangeLog.md
1818
Readme.md
1919

20-
tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.7 || ==9.0.2 || ==9.8.2 || ==9.10.1 || ==9.12.1
20+
tested-with: GHC ==8.10.7 || ==9.10 || ==9.12
2121

2222
library
2323
hs-source-dirs: src, .
2424
build-depends:
2525
aeson >=1.4.4 && <2.3
2626
, aeson-gadt-th >=0.2.4 && <0.3
2727
, base >=4.12 && <4.22
28-
, bytestring >=0.10.8 && <0.13
2928
, constraints-extras >=0.3.0 && <0.5
3029
, containers >=0.6 && <0.8
3130
, data-default >=0.6 && <0.9

src/Reflex/Dom/GadtApi/XHR.hs

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE DeriveGeneric #-}
12
{-# LANGUAGE FlexibleContexts #-}
23
{-# LANGUAGE GADTs #-}
34
{-# LANGUAGE OverloadedStrings #-}
@@ -9,11 +10,10 @@ module Reflex.Dom.GadtApi.XHR where
910
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
1011
import Control.Monad.IO.Class (MonadIO, liftIO)
1112
import Data.Aeson
12-
import qualified Data.ByteString.Lazy as LBS
1313
import Data.Constraint.Extras (Has, has)
1414
import Data.Functor (void)
1515
import Data.Text (Text)
16-
import qualified Data.Text.Encoding as T
16+
import GHC.Generics
1717
import Language.Javascript.JSaddle (MonadJSM)
1818
import Language.Javascript.JSaddle.Monad (runJSM, askJSM)
1919
import Reflex.Dom.Core
@@ -38,13 +38,28 @@ performXhrRequests
3838
)
3939
=> ApiEndpoint
4040
-> Event t (RequesterData api)
41-
-> m (Event t (RequesterData (Either Text)))
41+
-> m (Event t (RequesterData (Either XhrError)))
4242
performXhrRequests apiUrl req = fmap switchPromptlyDyn $ prerender (pure never) $ do
4343
performEventAsync $ ffor req $ \r yield -> do
4444
ctx <- askJSM
4545
void $ liftIO $ forkIO $ flip runJSM ctx $
4646
liftIO . yield =<< apiRequestXhr apiUrl r
4747

48+
data XhrError = XhrError
49+
{ _xhrError_request :: XhrRequest Text
50+
, _xhrError_response :: XhrResponse
51+
}
52+
deriving (Generic)
53+
54+
xhrErrorToText :: XhrError -> Text
55+
xhrErrorToText e =
56+
let
57+
status = _xhrResponse_statusText . _xhrError_response $ e
58+
rsp = _xhrResponse_responseText . _xhrError_response $ e
59+
in status <> case rsp of
60+
Nothing -> ""
61+
Just r -> ": " <> r
62+
4863
-- | Encodes an API request as JSON and issues an 'XhrRequest',
4964
-- and attempts to decode the response.
5065
apiRequestXhr
@@ -56,21 +71,19 @@ apiRequestXhr
5671
)
5772
=> ApiEndpoint
5873
-> RequesterData api
59-
-> m (RequesterData (Either Text))
74+
-> m (RequesterData (Either XhrError))
6075
apiRequestXhr apiUrl = traverseRequesterData $ \x ->
6176
has @FromJSON @api x $ mkRequest x
6277
where
6378
mkRequest
6479
:: (MonadJSM m, FromJSON b)
6580
=> api b
66-
-> m (Either Text b)
81+
-> m (Either XhrError b)
6782
mkRequest req = do
6883
response <- liftIO newEmptyMVar
69-
_ <- newXMLHttpRequest (postJson apiUrl req) $
70-
liftIO . putMVar response
84+
let request = postJson apiUrl req
85+
_ <- newXMLHttpRequest request $ liftIO . putMVar response
7186
xhrResp <- liftIO $ takeMVar response
7287
case decodeXhrResponse xhrResp of
73-
Nothing -> pure $ Left $
74-
"Response could not be decoded for request: " <>
75-
T.decodeUtf8 (LBS.toStrict $ encode req)
88+
Nothing -> pure $ Left $ XhrError request xhrResp
7689
Just r -> pure $ Right r

0 commit comments

Comments
 (0)