mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-11-10 10:29:12 +03:00
server: improve handling of HTTP client errors for remote schema calls
https://github.com/hasura/graphql-engine-mono/pull/1719 GitOrigin-RevId: 5ff671bcff5f0559b9821f8359ebee4129f4b664
This commit is contained in:
parent
80161e4208
commit
ae6d624441
@ -8,6 +8,7 @@
|
|||||||
- server: Add global limit to BigQuery via the `global_select_limit`
|
- server: Add global limit to BigQuery via the `global_select_limit`
|
||||||
field in the connection configuration
|
field in the connection configuration
|
||||||
- server: include action and event names in log output
|
- server: include action and event names in log output
|
||||||
|
- server: log all HTTP errors in remote schema calls as `remote-schema-error` with details
|
||||||
|
|
||||||
## v2.0.1
|
## v2.0.1
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import Control.Lens ((^.))
|
|||||||
import Control.Monad.Unique
|
import Control.Monad.Unique
|
||||||
import Data.Aeson ((.:), (.:?))
|
import Data.Aeson ((.:), (.:?))
|
||||||
import Data.FileEmbed (makeRelativeToProject)
|
import Data.FileEmbed (makeRelativeToProject)
|
||||||
|
import Network.URI (URI)
|
||||||
|
|
||||||
import qualified Hasura.GraphQL.Parser.Monad as P
|
import qualified Hasura.GraphQL.Parser.Monad as P
|
||||||
|
|
||||||
@ -62,7 +63,7 @@ fetchRemoteSchema env manager schemaName schemaInfo@(RemoteSchemaInfo url header
|
|||||||
let hdrsWithDefaults = addDefaultHeaders headers
|
let hdrsWithDefaults = addDefaultHeaders headers
|
||||||
|
|
||||||
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
||||||
initReq <- onLeft initReqE throwHttpErr
|
initReq <- onLeft initReqE (throwRemoteSchemaHttp url)
|
||||||
let req = initReq
|
let req = initReq
|
||||||
{ HTTP.method = "POST"
|
{ HTTP.method = "POST"
|
||||||
, HTTP.requestHeaders = hdrsWithDefaults
|
, HTTP.requestHeaders = hdrsWithDefaults
|
||||||
@ -70,7 +71,7 @@ fetchRemoteSchema env manager schemaName schemaInfo@(RemoteSchemaInfo url header
|
|||||||
, HTTP.responseTimeout = HTTP.responseTimeoutMicro (timeout * 1000000)
|
, HTTP.responseTimeout = HTTP.responseTimeoutMicro (timeout * 1000000)
|
||||||
}
|
}
|
||||||
res <- liftIO $ try $ HTTP.httpLbs req manager
|
res <- liftIO $ try $ HTTP.httpLbs req manager
|
||||||
resp <- onLeft res throwHttpErr
|
resp <- onLeft res (throwRemoteSchemaHttp url)
|
||||||
|
|
||||||
let respData = resp ^. Wreq.responseBody
|
let respData = resp ^. Wreq.responseBody
|
||||||
statusCode = resp ^. Wreq.responseStatus . Wreq.statusCode
|
statusCode = resp ^. Wreq.responseStatus . Wreq.statusCode
|
||||||
@ -78,7 +79,7 @@ fetchRemoteSchema env manager schemaName schemaInfo@(RemoteSchemaInfo url header
|
|||||||
|
|
||||||
-- Parse the JSON into flat GraphQL type AST
|
-- Parse the JSON into flat GraphQL type AST
|
||||||
(FromIntrospection introspectRes) :: (FromIntrospection IntrospectionResult) <-
|
(FromIntrospection introspectRes) :: (FromIntrospection IntrospectionResult) <-
|
||||||
onLeft (J.eitherDecode respData) (remoteSchemaErr . T.pack)
|
onLeft (J.eitherDecode respData) (throwRemoteSchema . T.pack)
|
||||||
|
|
||||||
-- Check that the parsed GraphQL type info is valid by running the schema generation
|
-- Check that the parsed GraphQL type info is valid by running the schema generation
|
||||||
(queryParsers, mutationParsers, subscriptionParsers) <-
|
(queryParsers, mutationParsers, subscriptionParsers) <-
|
||||||
@ -92,24 +93,9 @@ fetchRemoteSchema env manager schemaName schemaInfo@(RemoteSchemaInfo url header
|
|||||||
-- is called by simple encoding the result to JSON.
|
-- is called by simple encoding the result to JSON.
|
||||||
return $ RemoteSchemaCtx schemaName introspectRes schemaInfo respData parsedIntrospection mempty
|
return $ RemoteSchemaCtx schemaName introspectRes schemaInfo respData parsedIntrospection mempty
|
||||||
where
|
where
|
||||||
remoteSchemaErr :: Text -> m a
|
throwNon200 st = throwRemoteSchemaWithInternal (non200Msg st) . decodeNon200Resp
|
||||||
remoteSchemaErr = throw400 RemoteSchemaError
|
non200Msg st = T.pack $ "introspection query to " <> show url
|
||||||
|
|
||||||
throwHttpErr :: HTTP.HttpException -> m a
|
|
||||||
throwHttpErr = throwWithInternal httpExceptMsg . httpExceptToJSON
|
|
||||||
|
|
||||||
throwNon200 st = throwWithInternal (non200Msg st) . decodeNon200Resp
|
|
||||||
|
|
||||||
throwWithInternal msg v =
|
|
||||||
let err = err400 RemoteSchemaError $ T.pack msg
|
|
||||||
in throwError err{qeInternal = Just $ J.toJSON v}
|
|
||||||
|
|
||||||
httpExceptMsg =
|
|
||||||
"HTTP exception occurred while sending the request to " <> show url
|
|
||||||
|
|
||||||
non200Msg st = "introspection query to " <> show url
|
|
||||||
<> " has responded with " <> show st <> " status code"
|
<> " has responded with " <> show st <> " status code"
|
||||||
|
|
||||||
decodeNon200Resp bs = case J.eitherDecode bs of
|
decodeNon200Resp bs = case J.eitherDecode bs of
|
||||||
Right a -> J.object ["response" J..= (a :: J.Value)]
|
Right a -> J.object ["response" J..= (a :: J.Value)]
|
||||||
Left _ -> J.object ["raw_body" J..= bsToTxt (BL.toStrict bs)]
|
Left _ -> J.object ["raw_body" J..= bsToTxt (BL.toStrict bs)]
|
||||||
@ -348,7 +334,7 @@ execRemoteGQ env manager userInfo reqHdrs rsi gqlReq@GQLReq{..} = do
|
|||||||
headers = Map.toList $ foldr Map.union Map.empty hdrMaps
|
headers = Map.toList $ foldr Map.union Map.empty hdrMaps
|
||||||
finalHeaders = addDefaultHeaders headers
|
finalHeaders = addDefaultHeaders headers
|
||||||
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
initReqE <- liftIO $ try $ HTTP.parseRequest (show url)
|
||||||
initReq <- onLeft initReqE httpThrow
|
initReq <- onLeft initReqE (throwRemoteSchemaHttp url)
|
||||||
let req = initReq
|
let req = initReq
|
||||||
{ HTTP.method = "POST"
|
{ HTTP.method = "POST"
|
||||||
, HTTP.requestHeaders = finalHeaders
|
, HTTP.requestHeaders = finalHeaders
|
||||||
@ -357,13 +343,30 @@ execRemoteGQ env manager userInfo reqHdrs rsi gqlReq@GQLReq{..} = do
|
|||||||
}
|
}
|
||||||
Tracing.tracedHttpRequest req \req' -> do
|
Tracing.tracedHttpRequest req \req' -> do
|
||||||
(time, res) <- withElapsedTime $ liftIO $ try $ HTTP.httpLbs req' manager
|
(time, res) <- withElapsedTime $ liftIO $ try $ HTTP.httpLbs req' manager
|
||||||
resp <- onLeft res httpThrow
|
resp <- onLeft res (throwRemoteSchemaHttp url)
|
||||||
pure (time, mkSetCookieHeaders resp, resp ^. Wreq.responseBody)
|
pure (time, mkSetCookieHeaders resp, resp ^. Wreq.responseBody)
|
||||||
where
|
where
|
||||||
RemoteSchemaInfo url hdrConf fwdClientHdrs timeout = rsi
|
RemoteSchemaInfo url hdrConf fwdClientHdrs timeout = rsi
|
||||||
httpThrow :: (MonadError QErr m) => HTTP.HttpException -> m a
|
|
||||||
httpThrow = \case
|
|
||||||
HTTP.HttpExceptionRequest _req content -> throw500 $ tshow content
|
|
||||||
HTTP.InvalidUrlException _url reason -> throw500 $ tshow reason
|
|
||||||
|
|
||||||
userInfoToHdrs = sessionVariablesToHeaders $ _uiSession userInfo
|
userInfoToHdrs = sessionVariablesToHeaders $ _uiSession userInfo
|
||||||
|
|
||||||
|
throwRemoteSchema
|
||||||
|
:: QErrM m
|
||||||
|
=> Text -> m a
|
||||||
|
throwRemoteSchema = throw400 RemoteSchemaError
|
||||||
|
|
||||||
|
throwRemoteSchemaWithInternal
|
||||||
|
:: (QErrM m, J.ToJSON a)
|
||||||
|
=> Text -> a -> m b
|
||||||
|
throwRemoteSchemaWithInternal msg v =
|
||||||
|
let err = err400 RemoteSchemaError msg
|
||||||
|
in throwError err{qeInternal = Just $ J.toJSON v}
|
||||||
|
|
||||||
|
throwRemoteSchemaHttp
|
||||||
|
:: QErrM m
|
||||||
|
=> URI -> HTTP.HttpException -> m a
|
||||||
|
throwRemoteSchemaHttp url =
|
||||||
|
throwRemoteSchemaWithInternal (T.pack httpExceptMsg) . httpExceptToJSON
|
||||||
|
where
|
||||||
|
httpExceptMsg =
|
||||||
|
"HTTP exception occurred while sending the request to " <> show url
|
||||||
|
@ -3,8 +3,22 @@ url: /v1/graphql
|
|||||||
status: 200
|
status: 200
|
||||||
response:
|
response:
|
||||||
errors:
|
errors:
|
||||||
- message: ResponseTimeout
|
- message: HTTP exception occurred while sending the request to http://localhost:5000/hello-graphql
|
||||||
extensions: {'path': '$', 'code': 'unexpected'}
|
extensions:
|
||||||
|
code: remote-schema-error
|
||||||
|
path: $
|
||||||
|
internal:
|
||||||
|
message: ResponseTimeout
|
||||||
|
request:
|
||||||
|
proxy:
|
||||||
|
secure: false
|
||||||
|
path: /hello-graphql
|
||||||
|
responseTimeout: ResponseTimeoutMicro 5000000
|
||||||
|
method: POST
|
||||||
|
host: localhost
|
||||||
|
requestVersion: HTTP/1.1
|
||||||
|
redirectCount: '10'
|
||||||
|
port: '5000'
|
||||||
query:
|
query:
|
||||||
query: |
|
query: |
|
||||||
query {
|
query {
|
||||||
|
Loading…
Reference in New Issue
Block a user