module Hasura.RQL.DDL.SourceKinds
  ( -- * List Source Kinds
    ListSourceKinds (..),
    runListSourceKinds,

    -- * Source Kind Info
    SourceKindInfo (..),
    SourceType (..),
  )
where

--------------------------------------------------------------------------------

import Data.Aeson (FromJSON, ToJSON, (.:), (.=))
import Data.Aeson qualified as Aeson
import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
import Data.Text.Extended (ToTxt (..))
import Data.Text.NonEmpty qualified as NE.Text
import Hasura.Backends.DataConnector.Adapter.Types qualified as DC.Types
import Hasura.EncJSON (EncJSON)
import Hasura.EncJSON qualified as EncJSON
import Hasura.Prelude
import Hasura.RQL.Types.Metadata qualified as Metadata
import Hasura.SQL.Backend qualified as Backend
import Hasura.SQL.BackendMap qualified as BackendMap

--------------------------------------------------------------------------------

data ListSourceKinds = ListSourceKinds

instance FromJSON ListSourceKinds where
  parseJSON :: Value -> Parser ListSourceKinds
parseJSON = String
-> (Object -> Parser ListSourceKinds)
-> Value
-> Parser ListSourceKinds
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject String
"ListSourceKinds" (Parser ListSourceKinds -> Object -> Parser ListSourceKinds
forall a b. a -> b -> a
const (Parser ListSourceKinds -> Object -> Parser ListSourceKinds)
-> Parser ListSourceKinds -> Object -> Parser ListSourceKinds
forall a b. (a -> b) -> a -> b
$ ListSourceKinds -> Parser ListSourceKinds
forall (f :: * -> *) a. Applicative f => a -> f a
pure ListSourceKinds
ListSourceKinds)

instance ToJSON ListSourceKinds where
  toJSON :: ListSourceKinds -> Value
toJSON ListSourceKinds
ListSourceKinds = [Pair] -> Value
Aeson.object []

--------------------------------------------------------------------------------

data SourceKindInfo = SourceKindInfo
  { SourceKindInfo -> Text
_skiSourceKind :: Text,
    SourceKindInfo -> SourceType
_skiBuiltin :: SourceType
  }

instance FromJSON SourceKindInfo where
  parseJSON :: Value -> Parser SourceKindInfo
parseJSON = String
-> (Object -> Parser SourceKindInfo)
-> Value
-> Parser SourceKindInfo
forall a. String -> (Object -> Parser a) -> Value -> Parser a
Aeson.withObject String
"SourceKindInfo" \Object
o -> do
    Text
_skiSourceKind <- Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"kind"
    SourceType
_skiBuiltin <- Object
o Object -> Key -> Parser SourceType
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"builtin"
    SourceKindInfo -> Parser SourceKindInfo
forall (f :: * -> *) a. Applicative f => a -> f a
pure SourceKindInfo :: Text -> SourceType -> SourceKindInfo
SourceKindInfo {Text
SourceType
_skiBuiltin :: SourceType
_skiSourceKind :: Text
_skiBuiltin :: SourceType
_skiSourceKind :: Text
..}

instance ToJSON SourceKindInfo where
  toJSON :: SourceKindInfo -> Value
toJSON SourceKindInfo {Text
SourceType
_skiBuiltin :: SourceType
_skiSourceKind :: Text
_skiBuiltin :: SourceKindInfo -> SourceType
_skiSourceKind :: SourceKindInfo -> Text
..} = [Pair] -> Value
Aeson.object [Key
"kind" Key -> Text -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Text
_skiSourceKind, Key
"builtin" Key -> SourceType -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= SourceType
_skiBuiltin]

data SourceType = Builtin | Agent

instance FromJSON SourceType where
  parseJSON :: Value -> Parser SourceType
parseJSON = String -> (Bool -> Parser SourceType) -> Value -> Parser SourceType
forall a. String -> (Bool -> Parser a) -> Value -> Parser a
Aeson.withBool String
"source type" \case
    Bool
True -> SourceType -> Parser SourceType
forall (f :: * -> *) a. Applicative f => a -> f a
pure SourceType
Builtin
    Bool
False -> SourceType -> Parser SourceType
forall (f :: * -> *) a. Applicative f => a -> f a
pure SourceType
Agent

instance ToJSON SourceType where
  toJSON :: SourceType -> Value
toJSON SourceType
Builtin = Bool -> Value
Aeson.Bool Bool
True
  toJSON SourceType
Agent = Bool -> Value
Aeson.Bool Bool
False

--------------------------------------------------------------------------------

agentSourceKinds :: (Metadata.MetadataM m) => m [SourceKindInfo]
agentSourceKinds :: m [SourceKindInfo]
agentSourceKinds = do
  Maybe (BackendConfigWrapper 'DataConnector)
agentsM <- forall (b :: BackendType) (i :: BackendType -> *).
HasTag b =>
BackendMap i -> Maybe (i b)
forall (i :: BackendType -> *).
HasTag 'DataConnector =>
BackendMap i -> Maybe (i 'DataConnector)
BackendMap.lookup @'Backend.DataConnector (BackendMap BackendConfigWrapper
 -> Maybe (BackendConfigWrapper 'DataConnector))
-> (Metadata -> BackendMap BackendConfigWrapper)
-> Metadata
-> Maybe (BackendConfigWrapper 'DataConnector)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Metadata -> BackendMap BackendConfigWrapper
Metadata._metaBackendConfigs (Metadata -> Maybe (BackendConfigWrapper 'DataConnector))
-> m Metadata -> m (Maybe (BackendConfigWrapper 'DataConnector))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m Metadata
forall (m :: * -> *). MetadataM m => m Metadata
Metadata.getMetadata
  case Maybe (BackendConfigWrapper 'DataConnector)
agentsM of
    Maybe (BackendConfigWrapper 'DataConnector)
Nothing -> [SourceKindInfo] -> m [SourceKindInfo]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
    Just (Metadata.BackendConfigWrapper BackendConfig 'DataConnector
agents) ->
      [SourceKindInfo] -> m [SourceKindInfo]
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([SourceKindInfo] -> m [SourceKindInfo])
-> [SourceKindInfo] -> m [SourceKindInfo]
forall a b. (a -> b) -> a -> b
$ (DataConnectorName -> SourceKindInfo)
-> [DataConnectorName] -> [SourceKindInfo]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap DataConnectorName -> SourceKindInfo
mkAgentSource ([DataConnectorName] -> [SourceKindInfo])
-> [DataConnectorName] -> [SourceKindInfo]
forall a b. (a -> b) -> a -> b
$ InsOrdHashMap DataConnectorName DataConnectorOptions
-> [DataConnectorName]
forall k v. InsOrdHashMap k v -> [k]
InsOrdHashMap.keys InsOrdHashMap DataConnectorName DataConnectorOptions
BackendConfig 'DataConnector
agents

mkAgentSource :: DC.Types.DataConnectorName -> SourceKindInfo
mkAgentSource :: DataConnectorName -> SourceKindInfo
mkAgentSource (DC.Types.DataConnectorName NonEmptyText
name) =
  SourceKindInfo :: Text -> SourceType -> SourceKindInfo
SourceKindInfo {_skiSourceKind :: Text
_skiSourceKind = NonEmptyText -> Text
NE.Text.unNonEmptyText NonEmptyText
name, _skiBuiltin :: SourceType
_skiBuiltin = SourceType
Agent}

mkNativeSource :: Backend.BackendType -> Maybe SourceKindInfo
mkNativeSource :: BackendType -> Maybe SourceKindInfo
mkNativeSource = \case
  BackendType
Backend.DataConnector -> Maybe SourceKindInfo
forall a. Maybe a
Nothing
  BackendType
b -> SourceKindInfo -> Maybe SourceKindInfo
forall a. a -> Maybe a
Just (SourceKindInfo -> Maybe SourceKindInfo)
-> SourceKindInfo -> Maybe SourceKindInfo
forall a b. (a -> b) -> a -> b
$ SourceKindInfo :: Text -> SourceType -> SourceKindInfo
SourceKindInfo {_skiSourceKind :: Text
_skiSourceKind = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe (BackendType -> Text
forall a. ToTxt a => a -> Text
toTxt BackendType
b) (BackendType -> Maybe Text
Backend.backendShortName BackendType
b), _skiBuiltin :: SourceType
_skiBuiltin = SourceType
Builtin}

runListSourceKinds :: Metadata.MetadataM m => ListSourceKinds -> m EncJSON
runListSourceKinds :: ListSourceKinds -> m EncJSON
runListSourceKinds ListSourceKinds
ListSourceKinds = do
  let builtins :: [SourceKindInfo]
builtins = (BackendType -> Maybe SourceKindInfo)
-> [BackendType] -> [SourceKindInfo]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe BackendType -> Maybe SourceKindInfo
mkNativeSource ([BackendType] -> [SourceKindInfo])
-> [BackendType] -> [SourceKindInfo]
forall a b. (a -> b) -> a -> b
$ (BackendType -> Bool) -> [BackendType] -> [BackendType]
forall a. (a -> Bool) -> [a] -> [a]
filter (BackendType -> BackendType -> Bool
forall a. Eq a => a -> a -> Bool
/= BackendType
Backend.DataConnector) [BackendType]
Backend.supportedBackends
  [SourceKindInfo]
agents <- m [SourceKindInfo]
forall (m :: * -> *). MetadataM m => m [SourceKindInfo]
agentSourceKinds

  EncJSON -> m EncJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure (EncJSON -> m EncJSON) -> EncJSON -> m EncJSON
forall a b. (a -> b) -> a -> b
$ Value -> EncJSON
forall a. ToJSON a => a -> EncJSON
EncJSON.encJFromJValue (Value -> EncJSON) -> Value -> EncJSON
forall a b. (a -> b) -> a -> b
$ [Pair] -> Value
Aeson.object [Key
"sources" Key -> [SourceKindInfo] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= ([SourceKindInfo]
builtins [SourceKindInfo] -> [SourceKindInfo] -> [SourceKindInfo]
forall a. Semigroup a => a -> a -> a
<> [SourceKindInfo]
agents)]