2022-07-19 04:51:42 +03:00
module Hasura.Backends.DataConnector.Adapter.ConfigTransform
( transformSourceConfig ,
2023-01-12 02:11:56 +03:00
transformConnSourceConfigUnsafe ,
2022-07-19 04:51:42 +03:00
transformConnSourceConfig ,
2022-12-02 11:01:06 +03:00
validateConfiguration ,
getConfigSchemaResponse ,
2022-07-19 04:51:42 +03:00
)
where
--------------------------------------------------------------------------------
import Data.Aeson qualified as J
2022-07-21 10:05:46 +03:00
import Data.Aeson.Kriti.Functions qualified as KFunc
2022-07-19 04:51:42 +03:00
import Data.Environment qualified as Env
2022-12-02 11:01:06 +03:00
import Data.HashMap.Strict qualified as HashMap
import Data.Text qualified as Text
import Data.Text.Extended qualified as Text
2023-01-12 02:11:56 +03:00
import Hasura.Backends.DataConnector.API ( ConfigSchemaResponse )
2022-07-19 04:51:42 +03:00
import Hasura.Backends.DataConnector.API qualified as API
2022-07-20 08:20:49 +03:00
import Hasura.Backends.DataConnector.Adapter.Types ( ConnSourceConfig ( ConnSourceConfig , template , value ) , SourceConfig ( .. ) )
2022-12-02 11:01:06 +03:00
import Hasura.Backends.DataConnector.Adapter.Types qualified as DC
import Hasura.Base.Error ( Code ( DataConnectorError , NotSupported ) , QErr , throw400 )
2022-07-19 04:51:42 +03:00
import Hasura.Prelude
2022-12-02 11:01:06 +03:00
import Hasura.RQL.Types.Common as Common
import Hasura.RQL.Types.SchemaCache
import Hasura.SQL.Backend qualified as Backend
2022-07-19 04:51:42 +03:00
import Kriti.Error qualified as Kriti
2022-12-02 11:01:06 +03:00
--------------------------------------------------------------------------------
transformConfig :: ( MonadError QErr m ) => API . Config -> Maybe Text -> [ ( Text , J . Value ) ] -> Env . Environment -> m API . Config
2022-07-19 04:51:42 +03:00
transformConfig config maybeTemplate scope env = do
case maybeTemplate of
Nothing -> pure config
( Just t ) ->
2022-07-21 10:05:46 +03:00
case KFunc . runKritiWith t ( ( " $config " , J . toJSON config ) : scope ) ( additionalFunctions env ) of
Left e -> throw400 NotSupported $ " transformConfig: Kriti template transform failed - " <> tshow e
2022-07-19 04:51:42 +03:00
Right ( J . Object r ) -> pure $ API . Config r
Right o -> throw400 NotSupported $ " transformConfig: Kriti did not decode into Object - " <> tshow o
2022-12-02 11:01:06 +03:00
transformSourceConfig :: ( MonadError QErr m ) => SourceConfig -> [ ( Text , J . Value ) ] -> Env . Environment -> m SourceConfig
2022-07-20 08:20:49 +03:00
transformSourceConfig sc @ SourceConfig { _scConfig , _scTemplate } scope env = do
2022-07-19 04:51:42 +03:00
transformedConfig <- transformConfig _scConfig _scTemplate scope env
2022-07-20 08:20:49 +03:00
pure sc { _scConfig = transformedConfig }
2022-07-19 04:51:42 +03:00
2023-01-12 02:11:56 +03:00
-- | Apply a transformation to a 'ConnSourceConfig' without validating the result.
transformConnSourceConfigUnsafe :: ( MonadError QErr m ) => ConnSourceConfig -> [ ( Text , J . Value ) ] -> Env . Environment -> m API . Config
transformConnSourceConfigUnsafe ConnSourceConfig { value , template } scope env = transformConfig value template scope env
-- | Apply a transformation to a 'ConnSourceConfig' and validate the result.
transformConnSourceConfig ::
( MonadError QErr m ) =>
DC . DataConnectorName ->
Common . SourceName ->
ConfigSchemaResponse ->
ConnSourceConfig ->
[ ( Text , J . Value ) ] ->
Env . Environment ->
m API . Config
transformConnSourceConfig dcName sourceName configSchemaResponse connSourceConfig scope env = do
transformedConfig <- transformConnSourceConfigUnsafe connSourceConfig scope env
validateConfiguration sourceName dcName configSchemaResponse transformedConfig
pure transformedConfig
2022-07-19 04:51:42 +03:00
2022-12-02 11:01:06 +03:00
--------------------------------------------------------------------------------
-- | Given a 'DC.DataConnectorName' fetch the associated
-- 'DC.DataConnectorInfo' from the SchemaCache.
getDataConnectorInfo' :: CacheRM m => DC . DataConnectorName -> m ( Maybe DC . DataConnectorInfo )
getDataConnectorInfo' dataConnectorName = do
bmap <- getBackendInfo @ 'Backend . DataConnector
pure $ bmap >>= HashMap . lookup dataConnectorName
-- | Given a 'DC.DataConnectorName' fetch the associated
-- 'DC.DataConnectorInfo' from the SchemaCache. Lookup failures are
-- pushed into 'MonadError QErr m'.
getDataConnectorInfo :: ( CacheRM m , MonadError QErr m ) => DC . DataConnectorName -> m DC . DataConnectorInfo
getDataConnectorInfo dataConnectorName = do
onNothingM ( getDataConnectorInfo' dataConnectorName ) $
throw400 DataConnectorError ( " Data connector named " <> Text . dquote dataConnectorName <> " was not found in the data connector backend info " )
-- | Given a 'DC.DataConnectorName' fetch the associated
-- 'API.ConfigSchemaResponse' from the SchemaCache. Lookup failures
-- are pushed into 'MonadError QErr m'.
getConfigSchemaResponse :: ( CacheRM m , MonadError QErr m ) => DC . DataConnectorName -> m API . ConfigSchemaResponse
getConfigSchemaResponse = fmap DC . _dciConfigSchemaResponse . getDataConnectorInfo
--------------------------------------------------------------------------------
validateConfiguration ::
MonadError QErr m =>
Common . SourceName ->
DC . DataConnectorName ->
API . ConfigSchemaResponse ->
API . Config ->
m ()
validateConfiguration sourceName dataConnectorName configSchema config = do
let errors = API . validateConfigAgainstConfigSchema configSchema config
unless ( null errors ) $
let errorsText = Text . unlines ( ( " - " <> ) . Text . pack <$> errors )
in throw400
DataConnectorError
( " Configuration for source " <> Text . dquote sourceName <> " is not valid based on the configuration schema declared by the " <> Text . dquote dataConnectorName <> " data connector agent. Errors: \ n " <> errorsText )
additionalFunctions :: Env . Environment -> HashMap Text ( J . Value -> Either Kriti . CustomFunctionError J . Value )
2022-07-21 10:05:46 +03:00
additionalFunctions env = KFunc . environmentFunctions env