2022-02-25 19:08:18 +03:00
|
|
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
|
|
|
2022-05-02 08:03:12 +03:00
|
|
|
module Hasura.Backends.DataConnector.Adapter.Execute
|
2023-01-17 03:33:56 +03:00
|
|
|
( DataConnectorPreparedQuery (..),
|
|
|
|
encodePreparedQueryToJsonText,
|
2022-02-25 19:08:18 +03:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-04-08 09:48:37 +03:00
|
|
|
import Data.Aeson qualified as J
|
2023-01-17 03:33:56 +03:00
|
|
|
import Data.ByteString.Lazy qualified as BL
|
2022-07-19 04:51:42 +03:00
|
|
|
import Data.Environment qualified as Env
|
2023-01-17 03:33:56 +03:00
|
|
|
import Data.Text.Encoding qualified as TE
|
2022-06-02 08:22:44 +03:00
|
|
|
import Data.Text.Extended (toTxt)
|
2022-05-02 08:03:12 +03:00
|
|
|
import Hasura.Backends.DataConnector.API qualified as API
|
2022-10-11 03:25:07 +03:00
|
|
|
import Hasura.Backends.DataConnector.API.V0.ErrorResponse (ErrorResponse (..))
|
2022-07-19 04:51:42 +03:00
|
|
|
import Hasura.Backends.DataConnector.Adapter.ConfigTransform (transformSourceConfig)
|
2022-07-20 08:20:49 +03:00
|
|
|
import Hasura.Backends.DataConnector.Adapter.Types (SourceConfig (..))
|
2022-07-19 04:51:42 +03:00
|
|
|
import Hasura.Backends.DataConnector.Agent.Client (AgentClientT)
|
2023-01-17 03:33:56 +03:00
|
|
|
import Hasura.Backends.DataConnector.Plan.Common qualified as DC
|
|
|
|
import Hasura.Backends.DataConnector.Plan.MutationPlan qualified as DC
|
|
|
|
import Hasura.Backends.DataConnector.Plan.QueryPlan qualified as DC
|
2022-10-11 03:25:07 +03:00
|
|
|
import Hasura.Base.Error (Code (..), QErr, throw400, throw400WithDetail, throw500)
|
2022-07-20 08:20:49 +03:00
|
|
|
import Hasura.EncJSON (EncJSON, encJFromBuilder, encJFromJValue)
|
2022-04-08 09:48:37 +03:00
|
|
|
import Hasura.GraphQL.Execute.Backend (BackendExecute (..), DBStepInfo (..), ExplainPlan (..))
|
|
|
|
import Hasura.GraphQL.Namespace qualified as GQL
|
2022-02-25 19:08:18 +03:00
|
|
|
import Hasura.Prelude
|
2022-06-02 08:22:44 +03:00
|
|
|
import Hasura.RQL.Types.Common qualified as RQL
|
2022-04-08 09:48:37 +03:00
|
|
|
import Hasura.SQL.AnyBackend (mkAnyBackend)
|
2022-05-02 08:03:12 +03:00
|
|
|
import Hasura.SQL.Backend (BackendType (DataConnector))
|
2022-04-08 09:48:37 +03:00
|
|
|
import Hasura.Session
|
2022-07-11 11:04:30 +03:00
|
|
|
import Hasura.Tracing (MonadTrace)
|
2022-02-25 19:08:18 +03:00
|
|
|
import Hasura.Tracing qualified as Tracing
|
2022-07-22 12:46:25 +03:00
|
|
|
import Servant.Client.Core.HasClient ((//))
|
2022-07-11 11:04:30 +03:00
|
|
|
import Servant.Client.Generic (genericClient)
|
2023-01-17 03:33:56 +03:00
|
|
|
|
|
|
|
data DataConnectorPreparedQuery
|
|
|
|
= QueryRequest API.QueryRequest
|
|
|
|
| MutationRequest API.MutationRequest
|
|
|
|
|
|
|
|
encodePreparedQueryToJsonText :: DataConnectorPreparedQuery -> Text
|
|
|
|
encodePreparedQueryToJsonText = \case
|
|
|
|
QueryRequest req -> encodeToJsonText req
|
|
|
|
MutationRequest req -> encodeToJsonText req
|
|
|
|
|
|
|
|
encodeToJsonText :: J.ToJSON a => a -> Text
|
|
|
|
encodeToJsonText =
|
|
|
|
TE.decodeUtf8 . BL.toStrict . J.encode
|
2022-02-25 19:08:18 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-05-02 08:03:12 +03:00
|
|
|
instance BackendExecute 'DataConnector where
|
2023-01-17 03:33:56 +03:00
|
|
|
type PreparedQuery 'DataConnector = DataConnectorPreparedQuery
|
2022-05-02 08:03:12 +03:00
|
|
|
type MultiplexedQuery 'DataConnector = Void
|
2022-07-11 11:04:30 +03:00
|
|
|
type ExecutionMonad 'DataConnector = AgentClientT (Tracing.TraceT (ExceptT QErr IO))
|
2022-02-25 19:08:18 +03:00
|
|
|
|
2023-01-25 10:12:53 +03:00
|
|
|
mkDBQueryPlan UserInfo {..} env sourceName sourceConfig ir _headers _gName = do
|
2023-01-17 03:33:56 +03:00
|
|
|
queryPlan@DC.Plan {..} <- DC.mkQueryPlan _uiSession sourceConfig ir
|
2022-07-19 04:51:42 +03:00
|
|
|
transformedSourceConfig <- transformSourceConfig sourceConfig [("$session", J.toJSON _uiSession), ("$env", J.toJSON env)] env
|
2022-04-08 09:48:37 +03:00
|
|
|
pure
|
|
|
|
DBStepInfo
|
|
|
|
{ dbsiSourceName = sourceName,
|
2022-07-19 04:51:42 +03:00
|
|
|
dbsiSourceConfig = transformedSourceConfig,
|
2023-01-17 03:33:56 +03:00
|
|
|
dbsiPreparedQuery = Just $ QueryRequest _pRequest,
|
2023-01-25 10:12:53 +03:00
|
|
|
dbsiAction = buildQueryAction sourceName transformedSourceConfig queryPlan,
|
|
|
|
dbsiResolvedConnectionTemplate = ()
|
2022-04-08 09:48:37 +03:00
|
|
|
}
|
|
|
|
|
2023-01-25 10:12:53 +03:00
|
|
|
mkDBQueryExplain fieldName UserInfo {..} sourceName sourceConfig ir _headers _gName = do
|
2023-01-17 03:33:56 +03:00
|
|
|
queryPlan@DC.Plan {..} <- DC.mkQueryPlan _uiSession sourceConfig ir
|
2022-07-19 04:51:42 +03:00
|
|
|
transformedSourceConfig <- transformSourceConfig sourceConfig [("$session", J.toJSON _uiSession), ("$env", J.object [])] Env.emptyEnvironment
|
2022-04-08 09:48:37 +03:00
|
|
|
pure $
|
2022-05-02 08:03:12 +03:00
|
|
|
mkAnyBackend @'DataConnector
|
2022-04-08 09:48:37 +03:00
|
|
|
DBStepInfo
|
|
|
|
{ dbsiSourceName = sourceName,
|
2022-07-19 04:51:42 +03:00
|
|
|
dbsiSourceConfig = transformedSourceConfig,
|
2023-01-17 03:33:56 +03:00
|
|
|
dbsiPreparedQuery = Just $ QueryRequest _pRequest,
|
2023-01-25 10:12:53 +03:00
|
|
|
dbsiAction = buildExplainAction fieldName sourceName transformedSourceConfig queryPlan,
|
|
|
|
dbsiResolvedConnectionTemplate = ()
|
2022-04-08 09:48:37 +03:00
|
|
|
}
|
2023-01-25 10:12:53 +03:00
|
|
|
mkDBMutationPlan UserInfo {..} env _stringifyNum sourceName sourceConfig mutationDB _headers _gName = do
|
2023-01-17 03:33:56 +03:00
|
|
|
mutationPlan@DC.Plan {..} <- DC.mkMutationPlan _uiSession mutationDB
|
|
|
|
transformedSourceConfig <- transformSourceConfig sourceConfig [("$session", J.toJSON _uiSession), ("$env", J.toJSON env)] env
|
|
|
|
pure
|
|
|
|
DBStepInfo
|
|
|
|
{ dbsiSourceName = sourceName,
|
|
|
|
dbsiSourceConfig = transformedSourceConfig,
|
|
|
|
dbsiPreparedQuery = Just $ MutationRequest _pRequest,
|
2023-01-25 10:12:53 +03:00
|
|
|
dbsiAction = buildMutationAction sourceName transformedSourceConfig mutationPlan,
|
|
|
|
dbsiResolvedConnectionTemplate = ()
|
2023-01-17 03:33:56 +03:00
|
|
|
}
|
2023-01-25 10:12:53 +03:00
|
|
|
mkLiveQuerySubscriptionPlan _ _ _ _ _ _ _ =
|
2022-05-02 08:03:12 +03:00
|
|
|
throw400 NotSupported "mkLiveQuerySubscriptionPlan: not implemented for the Data Connector backend."
|
2023-01-25 10:12:53 +03:00
|
|
|
mkDBStreamingSubscriptionPlan _ _ _ _ _ _ =
|
2022-05-02 08:03:12 +03:00
|
|
|
throw400 NotSupported "mkLiveQuerySubscriptionPlan: not implemented for the Data Connector backend."
|
2023-01-25 10:12:53 +03:00
|
|
|
mkDBRemoteRelationshipPlan _ _ _ _ _ _ _ _ _ _ =
|
2022-05-02 08:03:12 +03:00
|
|
|
throw500 "mkDBRemoteRelationshipPlan: not implemented for the Data Connector backend."
|
2022-03-21 13:39:49 +03:00
|
|
|
mkSubscriptionExplain _ =
|
2022-05-02 08:03:12 +03:00
|
|
|
throw400 NotSupported "mkSubscriptionExplain: not implemented for the Data Connector backend."
|
2022-04-08 09:48:37 +03:00
|
|
|
|
2023-01-17 03:33:56 +03:00
|
|
|
buildQueryAction :: (MonadIO m, MonadTrace m, MonadError QErr m) => RQL.SourceName -> SourceConfig -> DC.Plan API.QueryRequest API.QueryResponse -> AgentClientT m EncJSON
|
|
|
|
buildQueryAction sourceName SourceConfig {..} DC.Plan {..} = do
|
2022-04-28 04:51:58 +03:00
|
|
|
-- NOTE: Should this check occur during query construction in 'mkPlan'?
|
2023-01-17 03:33:56 +03:00
|
|
|
when (DC.queryHasRelations _pRequest && isNothing (API._cRelationships _scCapabilities)) $
|
2022-04-08 09:48:37 +03:00
|
|
|
throw400 NotSupported "Agents must provide their own dataloader."
|
2022-10-11 03:25:07 +03:00
|
|
|
|
2023-01-17 03:33:56 +03:00
|
|
|
queryResponse <- queryGuard =<< (genericClient // API._query) (toTxt sourceName) _scConfig _pRequest
|
|
|
|
reshapedResponse <- _pResponseReshaper queryResponse
|
2022-07-22 12:46:25 +03:00
|
|
|
pure . encJFromBuilder $ J.fromEncoding reshapedResponse
|
2022-10-11 03:25:07 +03:00
|
|
|
where
|
2023-01-17 03:33:56 +03:00
|
|
|
errorAction e = throw400WithDetail DataConnectorError (API.errorResponseSummary e) (_crDetails e)
|
|
|
|
defaultAction = throw400 DataConnectorError "Unexpected data connector query response - Unexpected Type"
|
|
|
|
queryGuard = API.queryCase defaultAction pure errorAction
|
2022-08-29 06:38:24 +03:00
|
|
|
|
|
|
|
-- Delegates the generation to the Agent's /explain endpoint if it has that capability,
|
|
|
|
-- otherwise, returns the IR sent to the agent.
|
2023-01-17 03:33:56 +03:00
|
|
|
buildExplainAction :: (MonadIO m, MonadTrace m, MonadError QErr m) => GQL.RootFieldAlias -> RQL.SourceName -> SourceConfig -> DC.Plan API.QueryRequest API.QueryResponse -> AgentClientT m EncJSON
|
|
|
|
buildExplainAction fieldName sourceName SourceConfig {..} DC.Plan {..} =
|
2022-09-21 08:11:53 +03:00
|
|
|
case API._cExplain _scCapabilities of
|
2023-01-17 03:33:56 +03:00
|
|
|
Nothing -> pure . encJFromJValue . toExplainPlan fieldName $ _pRequest
|
2022-08-29 06:38:24 +03:00
|
|
|
Just API.ExplainCapabilities -> do
|
2023-01-17 03:33:56 +03:00
|
|
|
explainResponse <- (genericClient // API._explain) (toTxt sourceName) _scConfig _pRequest
|
2022-08-29 06:38:24 +03:00
|
|
|
pure . encJFromJValue $
|
|
|
|
ExplainPlan
|
|
|
|
fieldName
|
|
|
|
(Just (API._erQuery explainResponse))
|
|
|
|
(Just (API._erLines explainResponse))
|
|
|
|
|
2022-09-20 09:18:46 +03:00
|
|
|
toExplainPlan :: GQL.RootFieldAlias -> API.QueryRequest -> ExplainPlan
|
2022-08-29 06:38:24 +03:00
|
|
|
toExplainPlan fieldName queryRequest =
|
2023-01-17 03:33:56 +03:00
|
|
|
ExplainPlan fieldName (Just "") (Just [encodeToJsonText queryRequest])
|
|
|
|
|
|
|
|
buildMutationAction :: (MonadIO m, MonadTrace m, MonadError QErr m) => RQL.SourceName -> SourceConfig -> DC.Plan API.MutationRequest API.MutationResponse -> AgentClientT m EncJSON
|
|
|
|
buildMutationAction sourceName SourceConfig {..} DC.Plan {..} = do
|
|
|
|
queryResponse <- mutationGuard =<< (genericClient // API._mutation) (toTxt sourceName) _scConfig _pRequest
|
|
|
|
reshapedResponse <- _pResponseReshaper queryResponse
|
|
|
|
pure . encJFromBuilder $ J.fromEncoding reshapedResponse
|
|
|
|
where
|
|
|
|
errorAction e = throw400WithDetail DataConnectorError (API.errorResponseSummary e) (_crDetails e)
|
|
|
|
defaultAction = throw400 DataConnectorError "Unexpected data connector mutations response - Unexpected Type"
|
|
|
|
mutationGuard = API.mutationCase defaultAction pure errorAction
|