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.Metadata ( ) where
2022-02-25 19:08:18 +03:00
2022-04-14 05:06:07 +03:00
import Data.Aeson qualified as J
import Data.Environment ( Environment )
import Data.HashMap.Strict qualified as Map
2022-04-29 05:13:13 +03:00
import Data.HashMap.Strict.InsOrd qualified as OMap
2022-04-14 05:06:07 +03:00
import Data.Sequence.NonEmpty qualified as NESeq
2022-05-05 08:18:43 +03:00
import Data.Text qualified as Text
import Data.Text.Extended ( toTxt , ( <<> ) )
2022-05-02 08:03:12 +03:00
import Hasura.Backends.DataConnector.API qualified as API
import Hasura.Backends.DataConnector.Adapter.Types qualified as DC
import Hasura.Backends.DataConnector.Agent.Client qualified as Agent . Client
import Hasura.Backends.DataConnector.IR.Expression qualified as IR . E
import Hasura.Backends.DataConnector.IR.Name qualified as IR . N
import Hasura.Backends.DataConnector.IR.Scalar.Type qualified as IR . S . T
import Hasura.Backends.DataConnector.IR.Table qualified as IR . T
2022-04-14 05:06:07 +03:00
import Hasura.Backends.Postgres.SQL.Types ( PGDescription ( .. ) )
import Hasura.Base.Error ( Code ( .. ) , QErr , throw400 , withPathK )
2022-02-25 19:08:18 +03:00
import Hasura.Prelude
2022-04-14 05:06:07 +03:00
import Hasura.RQL.IR.BoolExp ( OpExpG ( .. ) , PartialSQLExp ( .. ) )
2022-04-28 04:51:58 +03:00
import Hasura.RQL.Types.Column qualified as RQL . T . C
2022-04-14 05:06:07 +03:00
import Hasura.RQL.Types.Common ( OID ( .. ) , SourceName )
2022-05-05 16:43:50 +03:00
import Hasura.RQL.Types.EventTrigger ( RecreateEventTriggers ( RETDoNothing ) )
2022-04-14 05:06:07 +03:00
import Hasura.RQL.Types.Metadata ( SourceMetadata ( .. ) )
2022-02-25 19:08:18 +03:00
import Hasura.RQL.Types.Metadata.Backend ( BackendMetadata ( .. ) )
2022-04-14 05:06:07 +03:00
import Hasura.RQL.Types.Source ( ResolvedSource ( .. ) )
import Hasura.RQL.Types.SourceCustomization ( SourceTypeCustomization )
2022-04-28 04:51:58 +03:00
import Hasura.RQL.Types.Table qualified as RQL . T . T
2022-04-29 05:13:13 +03:00
import Hasura.SQL.Backend ( BackendSourceKind ( .. ) , BackendType ( .. ) )
2022-04-14 05:06:07 +03:00
import Hasura.SQL.Types ( CollectableType ( .. ) )
2022-04-28 04:51:58 +03:00
import Hasura.Server.Utils qualified as HSU
2022-04-14 05:06:07 +03:00
import Hasura.Session ( SessionVariable , mkSessionVariable )
2022-05-05 08:18:43 +03:00
import Hasura.Tracing ( TraceT , noReporter , runTraceTWithReporter )
2022-04-14 05:06:07 +03:00
import Language.GraphQL.Draft.Syntax qualified as GQL
import Network.HTTP.Client qualified as HTTP
2022-05-05 08:18:43 +03:00
import Servant.Client ( AsClientT )
2022-04-14 05:06:07 +03:00
import Witch qualified
2022-02-25 19:08:18 +03:00
2022-05-02 08:03:12 +03:00
instance BackendMetadata 'DataConnector where
2022-05-05 16:43:50 +03:00
prepareCatalog = const $ pure RETDoNothing
2022-04-14 05:06:07 +03:00
resolveSourceConfig = resolveSourceConfig'
resolveDatabaseMetadata = resolveDatabaseMetadata'
parseBoolExpOperations = parseBoolExpOperations'
parseCollectableType = parseCollectableType'
2022-05-02 08:03:12 +03:00
buildComputedFieldInfo = error " buildComputedFieldInfo: not implemented for the Data Connector backend. "
fetchAndValidateEnumValues = error " fetchAndValidateEnumValues: not implemented for the Data Connector backend. "
buildFunctionInfo = error " buildFunctionInfo: not implemented for the Data Connector backend. "
updateColumnInEventTrigger = error " updateColumnInEventTrigger: not implemented for the Data Connector backend. "
2022-05-11 09:14:25 +03:00
postDropSourceHook _sourceConfig = pure ()
2022-05-25 13:24:41 +03:00
buildComputedFieldBooleanExp _ _ _ _ _ _ =
error " buildComputedFieldBooleanExp: not implemented for the Data Connector backend. "
2022-04-14 05:06:07 +03:00
resolveSourceConfig' ::
MonadIO m =>
SourceName ->
2022-05-02 08:03:12 +03:00
DC . ConnSourceConfig ->
BackendSourceKind 'DataConnector ->
DC . DataConnectorBackendConfig ->
2022-04-14 05:06:07 +03:00
Environment ->
2022-05-02 08:03:12 +03:00
m ( Either QErr DC . SourceConfig )
2022-05-05 08:18:43 +03:00
resolveSourceConfig' sourceName ( DC . ConnSourceConfig config ) ( DataConnectorKind dataConnectorName ) backendConfig _ = runExceptT do
2022-05-02 08:03:12 +03:00
DC . DataConnectorOptions { .. } <-
2022-04-29 05:13:13 +03:00
OMap . lookup dataConnectorName backendConfig
` onNothing ` throw400 DataConnectorError ( " Data connector named " <> toTxt dataConnectorName <> " was not found in the data connector backend config " )
2022-04-14 05:06:07 +03:00
manager <- liftIO $ HTTP . newManager HTTP . defaultManagerSettings
2022-05-05 08:18:43 +03:00
routes @ API . Routes { .. } <- liftIO $ Agent . Client . client manager _dcoUri
schemaResponse <- runTraceTWithReporter noReporter " resolve source " $ do
validateConfiguration routes sourceName dataConnectorName config
_schema config
2022-04-14 05:06:07 +03:00
pure
2022-05-02 08:03:12 +03:00
DC . SourceConfig
2022-04-29 05:13:13 +03:00
{ _scEndpoint = _dcoUri ,
2022-05-02 02:01:11 +03:00
_scConfig = config ,
2022-04-29 05:13:13 +03:00
_scSchema = schemaResponse ,
_scManager = manager
2022-04-14 05:06:07 +03:00
}
2022-05-05 08:18:43 +03:00
validateConfiguration ::
MonadIO m =>
API . Routes ( AsClientT ( TraceT ( ExceptT QErr m ) ) ) ->
SourceName ->
DC . DataConnectorName ->
API . Config ->
TraceT ( ExceptT QErr m ) ()
validateConfiguration API . Routes { .. } sourceName dataConnectorName config = do
configSchemaResponse <- _configSchema
let errors = API . validateConfigAgainstConfigSchema configSchemaResponse config
2022-05-27 15:27:18 +03:00
if not $ null errors
2022-05-05 08:18:43 +03:00
then
let errorsText = Text . unlines ( ( " - " <> ) . Text . pack <$> errors )
in throw400
DataConnectorError
( " Configuration for source " <> sourceName <<> " is not valid based on the configuration schema declared by the " <> dataConnectorName <<> " data connector agent. Errors: \ n " <> errorsText )
else pure ()
2022-04-14 05:06:07 +03:00
resolveDatabaseMetadata' ::
Applicative m =>
2022-05-02 08:03:12 +03:00
SourceMetadata 'DataConnector ->
DC . SourceConfig ->
2022-04-14 05:06:07 +03:00
SourceTypeCustomization ->
2022-05-02 08:03:12 +03:00
m ( Either QErr ( ResolvedSource 'DataConnector ) )
resolveDatabaseMetadata' _ sc @ ( DC . SourceConfig { _scSchema = API . SchemaResponse { .. } } ) customization =
2022-04-14 05:06:07 +03:00
let tables = Map . fromList $ do
API . TableInfo { .. } <- srTables
let meta =
2022-04-28 04:51:58 +03:00
RQL . T . T . DBTableMetadata
2022-04-14 05:06:07 +03:00
{ _ptmiOid = OID 0 ,
_ptmiColumns = do
API . ColumnInfo { .. } <- dtiColumns
pure $
2022-04-28 04:51:58 +03:00
RQL . T . C . RawColumnInfo
2022-04-14 05:06:07 +03:00
{ rciName = Witch . from dciName ,
rciPosition = 1 ,
rciType = Witch . from dciType ,
rciIsNullable = dciNullable ,
rciDescription = fmap GQL . Description dciDescription ,
-- TODO: Add Column Mutability to the 'TableInfo'
2022-04-28 04:51:58 +03:00
rciMutability = RQL . T . C . ColumnMutability False False
2022-04-14 05:06:07 +03:00
} ,
2022-04-28 04:51:58 +03:00
_ptmiPrimaryKey = dtiPrimaryKey <&> \ key -> RQL . T . T . PrimaryKey ( RQL . T . T . Constraint () ( OID 0 ) ) ( NESeq . singleton ( coerce key ) ) ,
2022-04-14 05:06:07 +03:00
_ptmiUniqueConstraints = mempty ,
_ptmiForeignKeys = mempty ,
2022-04-28 04:51:58 +03:00
_ptmiViewInfo = Just $ RQL . T . T . ViewInfo False False False ,
2022-04-14 05:06:07 +03:00
_ptmiDescription = fmap PGDescription dtiDescription ,
_ptmiExtraTableMetadata = ()
}
pure ( coerce dtiName , meta )
in pure $
pure $
ResolvedSource
{ _rsConfig = sc ,
_rsCustomization = customization ,
_rsTables = tables ,
_rsFunctions = mempty ,
_rsPgScalars = mempty
}
-- | This is needed to get permissions to work
parseBoolExpOperations' ::
forall m v .
MonadError QErr m =>
2022-05-02 08:03:12 +03:00
RQL . T . C . ValueParser 'DataConnector m v ->
2022-04-28 04:51:58 +03:00
IR . T . Name ->
2022-05-02 08:03:12 +03:00
RQL . T . T . FieldInfoMap ( RQL . T . T . FieldInfo 'DataConnector ) ->
RQL . T . C . ColumnReference 'DataConnector ->
2022-04-14 05:06:07 +03:00
J . Value ->
2022-05-02 08:03:12 +03:00
m [ OpExpG 'DataConnector v ]
2022-04-14 05:06:07 +03:00
parseBoolExpOperations' rhsParser _table _fields columnRef value =
2022-04-28 04:51:58 +03:00
withPathK ( toTxt columnRef ) $ parseOperations ( RQL . T . C . columnReferenceType columnRef ) value
2022-04-14 05:06:07 +03:00
where
parseWithTy ty = rhsParser ( CollectableTypeScalar ty )
2022-05-02 08:03:12 +03:00
parseOperations :: RQL . T . C . ColumnType 'DataConnector -> J . Value -> m [ OpExpG 'DataConnector v ]
2022-04-14 05:06:07 +03:00
parseOperations columnType = \ case
2022-04-28 04:51:58 +03:00
J . Object o -> traverse ( parseOperation columnType ) $ Map . toList o
2022-04-14 05:06:07 +03:00
v -> pure . AEQ False <$> parseWithTy columnType v
2022-05-02 08:03:12 +03:00
parseOperation :: RQL . T . C . ColumnType 'DataConnector -> ( Text , J . Value ) -> m ( OpExpG 'DataConnector v )
2022-04-14 05:06:07 +03:00
parseOperation columnType ( opStr , val ) = withPathK opStr $
case opStr of
" _eq " -> parseEq
" $eq " -> parseEq
" _neq " -> parseNeq
" $neq " -> parseNeq
" _gt " -> parseGt
" $gt " -> parseGt
" _lt " -> parseLt
" $lt " -> parseLt
" _gte " -> parseGte
" $gte " -> parseGte
" _lte " -> parseLte
" $lte " -> parseLte
2022-04-28 04:51:58 +03:00
" $in " -> parseIn
" _in " -> parseIn
" $nin " -> parseNin
" _nin " -> parseNin
2022-04-14 05:06:07 +03:00
-- "$like" -> parseLike
-- "_like" -> parseLike
--
-- "$nlike" -> parseNlike
-- "_nlike" -> parseNlike
x -> throw400 UnexpectedPayload $ " Unknown operator : " <> x
where
2022-04-28 04:51:58 +03:00
colTy = RQL . T . C . columnReferenceType columnRef
2022-04-14 05:06:07 +03:00
parseOne = parseWithTy columnType val
2022-04-28 04:51:58 +03:00
parseManyWithType ty = rhsParser ( CollectableTypeArray ty ) val
2022-04-14 05:06:07 +03:00
parseEq = AEQ False <$> parseOne
parseNeq = ANE False <$> parseOne
2022-04-28 04:51:58 +03:00
parseIn = AIN <$> parseManyWithType colTy
parseNin = ANIN <$> parseManyWithType colTy
2022-04-14 05:06:07 +03:00
parseGt = AGT <$> parseOne
parseLt = ALT <$> parseOne
parseGte = AGTE <$> parseOne
parseLte = ALTE <$> parseOne
parseCollectableType' ::
MonadError QErr m =>
2022-05-02 08:03:12 +03:00
CollectableType ( RQL . T . C . ColumnType 'DataConnector ) ->
2022-04-14 05:06:07 +03:00
J . Value ->
2022-05-02 08:03:12 +03:00
m ( PartialSQLExp 'DataConnector )
2022-04-14 05:06:07 +03:00
parseCollectableType' collectableType = \ case
J . String t
2022-04-28 04:51:58 +03:00
| HSU . isSessionVariable t -> pure $ mkTypedSessionVar collectableType $ mkSessionVariable t
| HSU . isReqUserId t -> pure $ mkTypedSessionVar collectableType HSU . userIdHeader
2022-04-14 05:06:07 +03:00
val -> case collectableType of
CollectableTypeScalar scalarType ->
2022-04-28 04:51:58 +03:00
PSESQLExp . IR . E . Literal <$> RQL . T . C . parseScalarValueColumnType scalarType val
2022-04-14 05:06:07 +03:00
CollectableTypeArray _ ->
2022-05-02 08:03:12 +03:00
throw400 NotSupported " Array types are not supported by the Data Connector backend "
2022-04-14 05:06:07 +03:00
mkTypedSessionVar ::
2022-05-02 08:03:12 +03:00
CollectableType ( RQL . T . C . ColumnType 'DataConnector ) ->
2022-04-14 05:06:07 +03:00
SessionVariable ->
2022-05-02 08:03:12 +03:00
PartialSQLExp 'DataConnector
2022-04-14 05:06:07 +03:00
mkTypedSessionVar columnType =
PSESessVar ( columnTypeToScalarType <$> columnType )
2022-05-02 08:03:12 +03:00
columnTypeToScalarType :: RQL . T . C . ColumnType 'DataConnector -> IR . S . T . Type
2022-04-14 05:06:07 +03:00
columnTypeToScalarType = \ case
2022-04-28 04:51:58 +03:00
RQL . T . C . ColumnScalar scalarType -> scalarType
-- NOTE: This should be unreachable:
RQL . T . C . ColumnEnumReference _ -> IR . S . T . String