Data Connectors API 400 error response - GDC-619

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6839
GitOrigin-RevId: 813ea5e976ff41754e7500abf6bcd0c8b70c960e
This commit is contained in:
Lyndon Maydwell 2022-11-14 15:25:01 +10:00 committed by hasura-bot
parent 6470eeb7f6
commit 6f9f44a441
4 changed files with 30 additions and 19 deletions

View File

@ -1513,7 +1513,7 @@ However, this endpoint can also be used to check whether the ability of the agen
### Reporting Errors ### Reporting Errors
Any non-200 response code from an Agent (except for the `/health` endpoint) will be interpreted as an error. These should be handled gracefully by `graphql-engine` but provide limited details to users. If you wish to return structured error information to users you can return a status of `500` from the `/capabilities`, `/schema`, and `/query` endpoints with the following JSON format: Any non-200 response code from an Agent (except for the `/health` endpoint) will be interpreted as an error. These should be handled gracefully by `graphql-engine` but provide limited details to users. If you wish to return structured error information to users you can return a status of `500`, or `400` from the `/capabilities`, `/schema`, and `/query` endpoints with the following JSON format:
``` ```
{ {

View File

@ -25,6 +25,7 @@ where
import Control.Arrow (left) import Control.Arrow (left)
import Data.ByteString.Lazy as BL import Data.ByteString.Lazy as BL
import Data.Data (Proxy (..)) import Data.Data (Proxy (..))
import Data.Foldable (for_)
import Data.List.NonEmpty (NonEmpty ((:|))) import Data.List.NonEmpty (NonEmpty ((:|)))
import Data.OpenApi (OpenApi) import Data.OpenApi (OpenApi)
import Data.Text (Text) import Data.Text (Text)
@ -47,12 +48,14 @@ capabilitiesCase :: a -> (CapabilitiesResponse -> a) -> (ErrorResponse -> a) ->
capabilitiesCase defaultAction capabilitiesAction errorAction union = do capabilitiesCase defaultAction capabilitiesAction errorAction union = do
let capabilitiesM = matchUnion @CapabilitiesResponse union let capabilitiesM = matchUnion @CapabilitiesResponse union
let errorM = matchUnion @ErrorResponse union let errorM = matchUnion @ErrorResponse union
case (capabilitiesM, errorM) of let errorM400 = matchUnion @ErrorResponse400 union
(Just c, Nothing) -> capabilitiesAction c case (capabilitiesM, errorM, errorM400) of
(Nothing, Just e) -> errorAction e (Nothing, Nothing, Nothing) -> defaultAction
_ -> defaultAction -- Note, this could technically include the (Just _, Just _) scenario which is not possible. (Just c, _, _) -> capabilitiesAction c
(_, Just e, _) -> errorAction e
(_, _, Just (WithStatus e)) -> errorAction e
type CapabilitiesResponses = '[V0.CapabilitiesResponse, V0.ErrorResponse] type CapabilitiesResponses = '[V0.CapabilitiesResponse, V0.ErrorResponse, V0.ErrorResponse400]
type CapabilitiesApi = type CapabilitiesApi =
"capabilities" "capabilities"
@ -60,16 +63,19 @@ type CapabilitiesApi =
-- | This function defines a central place to ensure that all cases are covered for schema and error responses. -- | This function defines a central place to ensure that all cases are covered for schema and error responses.
-- When additional responses are added to the Union, this should be updated to ensure that all responses have been considered. -- When additional responses are added to the Union, this should be updated to ensure that all responses have been considered.
schemaCase :: Monad m => m a -> (SchemaResponse -> m a) -> (ErrorResponse -> m a) -> Union SchemaResponses -> m a schemaCase :: a -> (SchemaResponse -> a) -> (ErrorResponse -> a) -> Union SchemaResponses -> a
schemaCase defaultAction schemaAction errorAction union = do schemaCase defaultAction schemaAction errorAction union = do
let schemaM = matchUnion @SchemaResponse union let schemaM = matchUnion @SchemaResponse union
let errorM = matchUnion @ErrorResponse union let errorM = matchUnion @ErrorResponse union
case (schemaM, errorM) of let errorM400 = matchUnion @ErrorResponse400 union
(Just c, Nothing) -> schemaAction c case (schemaM, errorM, errorM400) of
(Nothing, Just e) -> errorAction e -- Note, this could technically include the ...Just _, Just _... scenario, but won't occurr due to matchUnion
_ -> defaultAction -- Note, this could technically include the (Just _, Just _) scenario which is not possible. (Nothing, Nothing, Nothing) -> defaultAction
(Just x, _, _) -> schemaAction x
(_, Just x, _) -> errorAction x
(_, _, Just (WithStatus x)) -> errorAction x
type SchemaResponses = '[V0.SchemaResponse, V0.ErrorResponse] type SchemaResponses = '[V0.SchemaResponse, V0.ErrorResponse, V0.ErrorResponse400]
type SchemaApi = type SchemaApi =
"schema" "schema"
@ -79,16 +85,18 @@ type SchemaApi =
-- | This function defines a central place to ensure that all cases are covered for query and error responses. -- | This function defines a central place to ensure that all cases are covered for query and error responses.
-- When additional responses are added to the Union, this should be updated to ensure that all responses have been considered. -- When additional responses are added to the Union, this should be updated to ensure that all responses have been considered.
queryCase :: Monad m => m a -> (QueryResponse -> m a) -> (ErrorResponse -> m a) -> Union QueryResponses -> m a queryCase :: a -> (QueryResponse -> a) -> (ErrorResponse -> a) -> Union QueryResponses -> a
queryCase defaultAction queryAction errorAction union = do queryCase defaultAction queryAction errorAction union = do
let queryM = matchUnion @QueryResponse union let queryM = matchUnion @QueryResponse union
let errorM = matchUnion @ErrorResponse union let errorM = matchUnion @ErrorResponse union
case (queryM, errorM) of let errorM400 = matchUnion @ErrorResponse400 union
(Just c, Nothing) -> queryAction c case (queryM, errorM, errorM400) of
(Nothing, Just e) -> errorAction e (Nothing, Nothing, Nothing) -> defaultAction
_ -> defaultAction -- Note, this could technically include the (Just _, Just _) scenario which is not possible. (Just c, _, _) -> queryAction c
(_, Just e, _) -> errorAction e
(_, _, Just (WithStatus e)) -> errorAction e
type QueryResponses = '[V0.QueryResponse, V0.ErrorResponse] type QueryResponses = '[V0.QueryResponse, V0.ErrorResponse, V0.ErrorResponse400]
type QueryApi = type QueryApi =
"query" "query"

View File

@ -3,6 +3,7 @@
module Hasura.Backends.DataConnector.API.V0.ErrorResponse module Hasura.Backends.DataConnector.API.V0.ErrorResponse
( ErrorResponse (..), ( ErrorResponse (..),
ErrorResponse400,
ErrorResponseType (..), ErrorResponseType (..),
errorResponseJsonText, errorResponseJsonText,
errorResponseSummary, errorResponseSummary,
@ -54,6 +55,8 @@ data ErrorResponse = ErrorResponse
instance HasStatus ErrorResponse where instance HasStatus ErrorResponse where
type StatusOf ErrorResponse = 500 type StatusOf ErrorResponse = 500
type ErrorResponse400 = Servant.WithStatus 400 ErrorResponse
{-# HLINT ignore "Use tshow" #-} {-# HLINT ignore "Use tshow" #-}
errorResponseSummary :: ErrorResponse -> Text errorResponseSummary :: ErrorResponse -> Text
errorResponseSummary ErrorResponse {..} = pack (show _crType) <> ": " <> _crMessage errorResponseSummary ErrorResponse {..} = pack (show _crType) <> ": " <> _crMessage

View File

@ -435,7 +435,7 @@ runGetTableInfo GetTableInfo {..} = do
pure $ EncJSON.encJFromJValue table pure $ EncJSON.encJFromJValue table
backend -> Error.throw500 ("Schema fetching is not supported for '" <> Text.E.toTxt backend <> "'") backend -> Error.throw500 ("Schema fetching is not supported for '" <> Text.E.toTxt backend <> "'")
schemaGuard :: MonadError QErr m => Union '[API.SchemaResponse, API.ErrorResponse] -> m API.SchemaResponse schemaGuard :: MonadError QErr m => Union API.SchemaResponses -> m API.SchemaResponse
schemaGuard = schemaCase defaultAction pure errorAction schemaGuard = schemaCase defaultAction pure errorAction
where where
defaultAction = throw400 DataConnectorError "Error resolving source schema" defaultAction = throw400 DataConnectorError "Error resolving source schema"