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
2022-06-08 18:31:28 +03:00
import Data.Aeson.Key qualified as K
import Data.Aeson.KeyMap qualified as KM
2022-04-14 05:06:07 +03:00
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-07-01 15:20:07 +03:00
import Data.Sequence qualified as Seq
2022-07-19 11:41:27 +03:00
import Data.Sequence.NonEmpty qualified as NESeq
2022-05-05 08:18:43 +03:00
import Data.Text qualified as Text
2022-06-02 05:06:45 +03:00
import Data.Text.Extended ( toTxt , ( <<> ) , ( <>> ) )
2022-05-02 08:03:12 +03:00
import Hasura.Backends.DataConnector.API qualified as API
2022-07-19 04:51:42 +03:00
import Hasura.Backends.DataConnector.Adapter.ConfigTransform ( transformConnSourceConfig )
import Hasura.Backends.DataConnector.Adapter.Types ( ConnSourceConfig ( ConnSourceConfig ) )
2022-05-02 08:03:12 +03:00
import Hasura.Backends.DataConnector.Adapter.Types qualified as DC
2022-07-11 11:04:30 +03:00
import Hasura.Backends.DataConnector.Agent.Client ( AgentClientContext ( .. ) , runAgentClientT )
2022-06-02 05:06:45 +03:00
import Hasura.Backends.DataConnector.IR.Column qualified as IR . C
2022-05-02 08:03:12 +03:00
import Hasura.Backends.DataConnector.IR.Name qualified as IR . N
import Hasura.Backends.DataConnector.IR.Scalar.Type qualified as IR . S . T
2022-06-02 05:06:45 +03:00
import Hasura.Backends.DataConnector.IR.Scalar.Value qualified as IR . S . V
2022-05-02 08:03:12 +03:00
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 ( .. ) )
2022-06-02 05:06:45 +03:00
import Hasura.Base.Error ( Code ( .. ) , QErr , decodeValue , throw400 , throw500 , withPathK )
2022-07-11 11:04:30 +03:00
import Hasura.Logging ( Hasura , Logger )
2022-02-25 19:08:18 +03:00
import Hasura.Prelude
2022-06-02 05:06:45 +03:00
import Hasura.RQL.IR.BoolExp ( OpExpG ( .. ) , PartialSQLExp ( .. ) , RootOrCurrent ( .. ) , RootOrCurrentColumn ( .. ) )
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-06-02 05:06:45 +03:00
import Hasura.RQL.Types.SchemaCache qualified as SchemaCache
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-07-11 11:04:30 +03:00
import Hasura.Tracing ( 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-07-11 11:04:30 +03:00
import Servant.Client.Core.HasClient ( ( // ) )
import Servant.Client.Generic ( genericClient )
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 =>
2022-07-11 11:04:30 +03:00
Logger Hasura ->
2022-04-14 05:06:07 +03:00
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-07-19 04:51:42 +03:00
resolveSourceConfig' logger sourceName csc @ ConnSourceConfig { template , value = originalConfig } ( DataConnectorKind dataConnectorName ) backendConfig env = 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-07-19 04:51:42 +03:00
transformedConfig <- transformConnSourceConfig csc [ ( " $session " , J . object [] ) , ( " $env " , J . toJSON env ) ] env
2022-04-14 05:06:07 +03:00
manager <- liftIO $ HTTP . newManager HTTP . defaultManagerSettings
2022-06-03 11:06:31 +03:00
-- TODO: capabilities applies to all sources for an agent.
-- We should be able to call it once per agent and store it in the SchemaCache
2022-07-11 11:04:30 +03:00
API . CapabilitiesResponse { .. } <-
runTraceTWithReporter noReporter " capabilities "
. flip runAgentClientT ( AgentClientContext logger _dcoUri manager )
$ genericClient // API . _capabilities
2022-07-19 04:51:42 +03:00
validateConfiguration sourceName dataConnectorName crConfigSchemaResponse transformedConfig
2022-07-11 11:04:30 +03:00
schemaResponse <-
runTraceTWithReporter noReporter " resolve source "
. flip runAgentClientT ( AgentClientContext logger _dcoUri manager )
2022-07-19 04:51:42 +03:00
$ ( genericClient // API . _schema ) ( toTxt sourceName ) transformedConfig
2022-06-03 11:06:31 +03:00
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-07-19 04:51:42 +03:00
_scConfig = originalConfig ,
_scTemplate = template ,
2022-06-03 11:06:31 +03:00
_scCapabilities = crCapabilities ,
2022-04-29 05:13:13 +03:00
_scSchema = schemaResponse ,
2022-06-10 06:59:00 +03:00
_scManager = manager ,
_scDataConnectorName = dataConnectorName
2022-04-14 05:06:07 +03:00
}
2022-05-05 08:18:43 +03:00
validateConfiguration ::
2022-07-11 11:04:30 +03:00
MonadError QErr m =>
2022-05-05 08:18:43 +03:00
SourceName ->
DC . DataConnectorName ->
2022-07-11 11:04:30 +03:00
API . ConfigSchemaResponse ->
2022-05-05 08:18:43 +03:00
API . Config ->
2022-07-11 11:04:30 +03:00
m ()
validateConfiguration sourceName dataConnectorName configSchema config = do
let errors = API . validateConfigAgainstConfigSchema configSchema config
2022-05-27 16:33:38 +03:00
unless ( null errors ) $
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 )
2022-05-05 08:18:43 +03:00
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
2022-07-01 15:20:07 +03:00
let primaryKeyColumns = Seq . fromList $ coerce <$> fromMaybe [] dtiPrimaryKey
2022-04-14 05:06:07 +03:00
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-07-19 11:41:27 +03:00
_ptmiPrimaryKey = RQL . T . T . PrimaryKey ( RQL . T . T . Constraint () ( OID 0 ) ) <$> NESeq . nonEmptySeq primaryKeyColumns ,
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 ,
Move, document, and prune action types and custom types types.
### Description
This PR is a first step in a series of cleanups of action relationships. This first step does not contain any behavioral change, and it simply reorganizes / prunes / rearranges / documents the code. Mainly:
- it divides some files in RQL.Types between metadata types, schema cache types, execution types;
- it renames some types for consistency;
- it minimizes exports and prunes unnecessary types;
- it moves some types in places where they make more sense;
- it replaces uses of `DMap BackendTag` with `BackendMap`.
Most of the "movement" within files re-organizes declarations in a "top-down" fashion, by moving all TH splices to the end of the file, which avoids order or declarations mattering.
### Optional list types
One main type change this PR makes is a replacement of variant list types in `CustomTypes.hs`; we had `Maybe [a]`, or sometimes `Maybe (NonEmpty a)`. This PR harmonizes all of them to `[a]`, as most of the code would use them as such, by doing `fromMaybe []` or `maybe [] toList`.
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/4613
GitOrigin-RevId: bc624e10df587eba862ff27a5e8021b32d0d78a2
2022-06-07 18:43:34 +03:00
_rsScalars = mempty
2022-04-14 05:06:07 +03:00
}
-- | This is needed to get permissions to work
parseBoolExpOperations' ::
forall m v .
2022-06-02 05:06:45 +03:00
( MonadError QErr m , SchemaCache . TableCoreInfoRM 'DataConnector 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-06-02 05:06:45 +03:00
parseBoolExpOperations' rhsParser rootTable fieldInfoMap columnRef value =
withPathK ( toTxt columnRef ) $ parseOperations value
2022-04-14 05:06:07 +03:00
where
2022-06-02 05:06:45 +03:00
columnType :: RQL . T . C . ColumnType 'DataConnector
columnType = RQL . T . C . columnReferenceType columnRef
2022-04-14 05:06:07 +03:00
parseWithTy ty = rhsParser ( CollectableTypeScalar ty )
2022-06-02 05:06:45 +03:00
parseOperations :: J . Value -> m [ OpExpG 'DataConnector v ]
parseOperations = \ case
2022-06-08 18:31:28 +03:00
J . Object o -> traverse ( parseOperation . first K . toText ) $ KM . toList o
2022-04-14 05:06:07 +03:00
v -> pure . AEQ False <$> parseWithTy columnType v
2022-06-02 05:06:45 +03:00
parseOperation :: ( Text , J . Value ) -> m ( OpExpG 'DataConnector v )
parseOperation ( opStr , val ) = withPathK opStr $
2022-04-14 05:06:07 +03:00
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
2022-06-02 05:06:45 +03:00
" $in " -> parseIn
2022-04-28 04:51:58 +03:00
" _nin " -> parseNin
2022-06-02 05:06:45 +03:00
" $nin " -> parseNin
" _is_null " -> parseIsNull
" $is_null " -> parseIsNull
" _ceq " -> parseCeq
" $ceq " -> parseCeq
" _cneq " -> parseCne
" $cneq " -> parseCne
" _cgt " -> parseCgt
" $cgt " -> parseCgt
" _clt " -> parseClt
" $clt " -> parseClt
" _cgte " -> parseCgte
" $cgte " -> parseCgte
" _clte " -> parseClte
" $clte " -> parseClte
2022-04-14 05:06:07 +03:00
-- "_like" -> parseLike
2022-06-02 05:06:45 +03:00
-- "$like" -> parseLike
2022-04-14 05:06:07 +03:00
--
-- "_nlike" -> parseNlike
2022-06-02 05:06:45 +03:00
-- "$nlike" -> parseNlike
--
-- "_cast" -> parseCast
-- "$cast" -> parseCast
2022-04-14 05:06:07 +03:00
x -> throw400 UnexpectedPayload $ " Unknown operator : " <> x
where
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-06-02 05:06:45 +03:00
parseIn = AIN <$> parseManyWithType columnType
parseNin = ANIN <$> parseManyWithType columnType
2022-04-14 05:06:07 +03:00
parseGt = AGT <$> parseOne
parseLt = ALT <$> parseOne
parseGte = AGTE <$> parseOne
parseLte = ALTE <$> parseOne
2022-06-02 05:06:45 +03:00
parseIsNull = bool ANISNOTNULL ANISNULL <$> decodeValue val
parseCeq = CEQ <$> decodeAndValidateRhsCol val
parseCne = CNE <$> decodeAndValidateRhsCol val
parseCgt = CGT <$> decodeAndValidateRhsCol val
parseClt = CLT <$> decodeAndValidateRhsCol val
parseCgte = CGTE <$> decodeAndValidateRhsCol val
parseClte = CLTE <$> decodeAndValidateRhsCol val
decodeAndValidateRhsCol :: J . Value -> m ( RootOrCurrentColumn 'DataConnector )
decodeAndValidateRhsCol v = case v of
J . String _ -> go IsCurrent fieldInfoMap v
J . Array path -> case toList path of
[] -> throw400 Unexpected " path cannot be empty "
[ col ] -> go IsCurrent fieldInfoMap col
[ J . String " $ " , col ] -> do
rootTableInfo <-
SchemaCache . lookupTableCoreInfo rootTable
>>= flip onNothing ( throw500 $ " unexpected: " <> rootTable <<> " doesn't exist " )
go IsRoot ( RQL . T . T . _tciFieldInfoMap rootTableInfo ) col
_ -> throw400 NotSupported " Relationship references are not supported in column comparison RHS "
_ -> throw400 Unexpected " a boolean expression JSON must be either a string or an array "
where
go rootInfo fieldInfoMap' columnValue = do
colName <- decodeValue columnValue
colInfo <- validateRhsColumn fieldInfoMap' colName
pure $ RootOrCurrentColumn rootInfo colInfo
validateRhsColumn :: RQL . T . T . FieldInfoMap ( RQL . T . T . FieldInfo 'DataConnector ) -> IR . C . Name -> m IR . C . Name
validateRhsColumn fieldInfoMap' rhsCol = do
rhsType <- RQL . T . T . askColumnType fieldInfoMap' rhsCol " column operators can only compare table columns "
when ( columnType /= rhsType ) $
throw400 UnexpectedPayload $
" incompatible column types: "
<> columnRef <<> " has type "
<> columnType <<> " , but "
<> rhsCol <<> " has type " <>> rhsType
pure rhsCol
2022-04-14 05:06:07 +03:00
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-06-02 05:06:45 +03:00
PSESQLExp . IR . S . V . ValueLiteral <$> 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