Adds better error handler

This commit is contained in:
Mike Solomon 2023-06-19 14:48:46 +03:00
parent a46a03592e
commit 018f8c702b
3 changed files with 115 additions and 17 deletions

10
package-lock.json generated
View File

@ -11,6 +11,7 @@
"license": "ISC",
"devDependencies": {
"purescript": "^0.14.1",
"purs-tidy": "^0.10.0",
"spago": "^0.20.3",
"xhr2": "^0.2.1"
}
@ -1119,6 +1120,15 @@
"node": ">=8.3.0"
}
},
"node_modules/purs-tidy": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/purs-tidy/-/purs-tidy-0.10.0.tgz",
"integrity": "sha512-ULbJfBHRngczYwcOzugytRiNy+Guy2VXoAG1jMtGaSFEaUZk1lZkKU11t8jIZTrDdOrkgqlWCZLi3mfcHvdimA==",
"dev": true,
"bin": {
"purs-tidy": "bin/index.js"
}
},
"node_modules/qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",

View File

@ -10,6 +10,7 @@
"license": "ISC",
"devDependencies": {
"purescript": "^0.14.1",
"purs-tidy": "^0.10.0",
"spago": "^0.20.3",
"xhr2": "^0.2.1"
}

View File

@ -6,9 +6,10 @@ import Affjax as AX
import Affjax.RequestBody as RequestBody
import Affjax.RequestHeader (RequestHeader(..))
import Affjax.ResponseFormat as ResponseFormat
import Control.Monad.Error.Class (class MonadError, class MonadThrow)
import Control.Monad.Error.Class (class MonadThrow)
import Data.Either (Either(..))
import Data.HTTP.Method (Method(..))
import Data.List.Types (NonEmptyList)
import Data.Maybe (Maybe(..))
import Data.MediaType (MediaType(..))
import Data.String as String
@ -17,35 +18,113 @@ import Effect.Aff (Aff, error, throwError)
import Effect.Aff.Class (class MonadAff, liftAff)
import Effect.Class (liftEffect)
import Effect.Class.Console as Log
import Foreign (ForeignError)
import Simple.JSON as JSON
import Type.Proxy (Proxy(..))
data GraphQL
data Gql (operation :: GraphQL)
= Gql
data Gql (operation :: GraphQL) = Gql
class GraphQLReqRes (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type) | operation -> gql i o
class
GraphQLReqRes
(operation :: GraphQL)
(gql :: Symbol)
(i :: Row Type)
(o :: Row Type)
| operation -> gql i o
type Endpoint = String
type NetworkErrorInfo =
{ endpoint :: Endpoint
, requestHeaders :: Array RequestHeader
, requestBody :: String
, error :: AX.Error
}
type GraphQLClient' (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type) (m :: Type -> Type) (e :: Type) = GraphQLReqRes operation gql i o => IsSymbol gql => JSON.WriteForeign { | i } => JSON.ReadForeign { | o } => MonadAff m => MonadThrow e m => MonadError e m => (AX.Error -> e) -> (AX.Response String -> e) ->Gql operation -> Record i -> m { | o }
type ParsingErrorInfo =
{ endpoint :: Endpoint
, requestHeaders :: Array RequestHeader
, requestBody :: String
, response :: AX.Response String
, jsonParsingError :: NonEmptyList ForeignError
}
type GraphQLClient = forall (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type) (m :: Type -> Type) (e :: Type). GraphQLClient' operation gql i o m e
type GraphQLClient'
(operation :: GraphQL)
(gql :: Symbol)
(i :: Row Type)
(o :: Row Type)
(m :: Type -> Type)
(e :: Type) =
GraphQLReqRes operation gql i o
=> IsSymbol gql
=> JSON.WriteForeign { | i }
=> JSON.ReadForeign { | o }
=> MonadAff m
=> MonadThrow e m
=> (NetworkErrorInfo -> e)
-> (ParsingErrorInfo -> e)
-> Gql operation
-> Record i
-> m { | o }
graphQL :: Endpoint -> Array RequestHeader -> (forall (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type). GraphQLReqRes operation gql i o => IsSymbol gql => JSON.WriteForeign { | i } => JSON.ReadForeign { | o } => Gql operation -> Record i -> Aff { | o })
graphQL endpoint headers = graphQL' endpoint headers (error <<< AX.printError) (error <<< _.body)
type GraphQLClient =
forall (operation :: GraphQL)
(gql :: Symbol) (i :: Row Type) (o :: Row Type) (m :: Type -> Type) (e :: Type)
. GraphQLClient' operation gql i o m e
graphQL
:: Endpoint
-> Array RequestHeader
-> ( forall (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type)
. GraphQLReqRes operation gql i o
=> IsSymbol gql
=> JSON.WriteForeign { | i }
=> JSON.ReadForeign { | o }
=> Gql operation
-> Record i
-> Aff { | o }
)
graphQL endpoint headers = graphQL'
endpoint
headers
(error <<< AX.printError <<< _.error)
(error <<< _.body <<< _.response)
graphQL' :: Endpoint -> Array RequestHeader -> GraphQLClient
graphQL' endpoint headers = go
graphQL' endpoint headers' = go
where
go :: forall (operation :: GraphQL) (gql :: Symbol) (i :: Row Type) (o :: Row Type) (m :: Type -> Type) (e :: Type). GraphQLReqRes operation gql i o => IsSymbol gql => JSON.WriteForeign { | i } => JSON.ReadForeign { | o } => MonadAff m => MonadThrow e m => MonadError e m => (AX.Error -> e) -> (AX.Response String -> e) ->Gql operation -> Record i -> m { | o }
requestHeaders = headers' <> [ ContentType $ MediaType "application/json" ]
go
:: forall (operation :: GraphQL) (gql :: Symbol)
(i :: Row Type) (o :: Row Type) (m :: Type -> Type) (e :: Type)
. GraphQLReqRes operation gql i o
=> IsSymbol gql
=> JSON.WriteForeign
{ | i }
=> JSON.ReadForeign { | o }
=> MonadAff m
=> MonadThrow e m
=> (NetworkErrorInfo -> e)
-> (ParsingErrorInfo -> e)
-> Gql operation
-> Record i
-> m { | o }
go networkErrorF readErrorF _ variables = do
let
input =
{ variables
, query: String.replaceAll (String.Pattern "\n") (String.Replacement " ") (String.replaceAll (String.Pattern "\r\n") (String.Replacement " ") (reflectSymbol (Proxy :: Proxy gql)))
, query: String.replaceAll (String.Pattern "\n")
(String.Replacement " ")
( String.replaceAll
(String.Pattern "\r\n")
(String.Replacement " ")
(reflectSymbol (Proxy :: Proxy gql))
)
}
requestBody = JSON.writeJSON input
res <-
liftAff $ AX.request
( AX.defaultRequest
@ -53,15 +132,23 @@ graphQL' endpoint headers = go
, method = Left POST
, responseFormat = ResponseFormat.string
, content =
Just
(RequestBody.string (JSON.writeJSON input))
, headers = headers <> [ContentType $ MediaType "application/json"]
Just
(RequestBody.string requestBody)
, headers = requestHeaders
}
)
case res of
Left err -> do
Left error -> do
liftEffect $ Log.info "Request did not go through"
throwError (networkErrorF err)
throwError (networkErrorF { endpoint, requestHeaders, error, requestBody })
Right response -> case (JSON.readJSON response.body) of
Left _ -> throwError (readErrorF response)
Left jsonParsingError -> throwError
( readErrorF
{ endpoint
, requestHeaders
, jsonParsingError
, requestBody
, response
}
)
Right ({ data: d } :: { data :: { | o } }) -> pure d