module Hasura.RQL.DDL.QueryTags
  ( SetQueryTagsConfig,
    runSetQueryTagsConfig,
  )
where

import Control.Lens
import Data.Aeson
import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
import Data.Text.Extended (toTxt, (<<>))
import Hasura.Base.Error
import Hasura.EncJSON
import Hasura.Prelude
import Hasura.QueryTags.Types
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.BackendTag
import Hasura.RQL.Types.BackendType
import Hasura.RQL.Types.Common
import Hasura.RQL.Types.Metadata
import Hasura.RQL.Types.Metadata.Object
import Hasura.RQL.Types.SchemaCache.Build
import Hasura.SQL.AnyBackend qualified as AB

data SetQueryTagsConfig = SetQueryTagsConfig
  { SetQueryTagsConfig -> SourceName
_sqtSourceName :: SourceName,
    SetQueryTagsConfig -> QueryTagsConfig
_sqtConfig :: QueryTagsConfig
  }
  deriving stock ((forall x. SetQueryTagsConfig -> Rep SetQueryTagsConfig x)
-> (forall x. Rep SetQueryTagsConfig x -> SetQueryTagsConfig)
-> Generic SetQueryTagsConfig
forall x. Rep SetQueryTagsConfig x -> SetQueryTagsConfig
forall x. SetQueryTagsConfig -> Rep SetQueryTagsConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. SetQueryTagsConfig -> Rep SetQueryTagsConfig x
from :: forall x. SetQueryTagsConfig -> Rep SetQueryTagsConfig x
$cto :: forall x. Rep SetQueryTagsConfig x -> SetQueryTagsConfig
to :: forall x. Rep SetQueryTagsConfig x -> SetQueryTagsConfig
Generic)

instance ToJSON SetQueryTagsConfig where
  toJSON :: SetQueryTagsConfig -> Value
toJSON = Options -> SetQueryTagsConfig -> Value
forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
genericToJSON Options
hasuraJSON {omitNothingFields :: Bool
omitNothingFields = Bool
True}
  toEncoding :: SetQueryTagsConfig -> Encoding
toEncoding = Options -> SetQueryTagsConfig -> Encoding
forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
genericToEncoding Options
hasuraJSON {omitNothingFields :: Bool
omitNothingFields = Bool
True}

instance FromJSON SetQueryTagsConfig where
  parseJSON :: Value -> Parser SetQueryTagsConfig
parseJSON = String
-> (Object -> Parser SetQueryTagsConfig)
-> Value
-> Parser SetQueryTagsConfig
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"SetQueryTagsConfig" ((Object -> Parser SetQueryTagsConfig)
 -> Value -> Parser SetQueryTagsConfig)
-> (Object -> Parser SetQueryTagsConfig)
-> Value
-> Parser SetQueryTagsConfig
forall a b. (a -> b) -> a -> b
$ \Object
o -> do
    SourceName
sourceName <- Object
o Object -> Key -> Parser SourceName
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"source_name"
    QueryTagsConfig
queryTagsConfig <- Value -> Parser QueryTagsConfig
forall a. FromJSON a => Value -> Parser a
parseJSON (Value -> Parser QueryTagsConfig)
-> Value -> Parser QueryTagsConfig
forall a b. (a -> b) -> a -> b
$ Object -> Value
Object Object
o
    SetQueryTagsConfig -> Parser SetQueryTagsConfig
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (SetQueryTagsConfig -> Parser SetQueryTagsConfig)
-> SetQueryTagsConfig -> Parser SetQueryTagsConfig
forall a b. (a -> b) -> a -> b
$ SourceName -> QueryTagsConfig -> SetQueryTagsConfig
SetQueryTagsConfig SourceName
sourceName QueryTagsConfig
queryTagsConfig

runSetQueryTagsConfig ::
  (MonadError QErr m, MetadataM m, CacheRWM m) =>
  SetQueryTagsConfig ->
  m EncJSON
runSetQueryTagsConfig :: forall (m :: * -> *).
(MonadError QErr m, MetadataM m, CacheRWM m) =>
SetQueryTagsConfig -> m EncJSON
runSetQueryTagsConfig (SetQueryTagsConfig SourceName
sourceName QueryTagsConfig
queryTagsConfig) = do
  Metadata
oldMetadata <- m Metadata
forall (m :: * -> *). MetadataM m => m Metadata
getMetadata
  case SourceName
-> InsOrdHashMap SourceName BackendSourceMetadata
-> Maybe BackendSourceMetadata
forall k v. (Eq k, Hashable k) => k -> InsOrdHashMap k v -> Maybe v
InsOrdHashMap.lookup SourceName
sourceName (Metadata -> InsOrdHashMap SourceName BackendSourceMetadata
_metaSources Metadata
oldMetadata) of
    Maybe BackendSourceMetadata
Nothing -> Code -> Text -> m EncJSON
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotExists (Text -> m EncJSON) -> Text -> m EncJSON
forall a b. (a -> b) -> a -> b
$ Text
"source with name " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> SourceName
sourceName SourceName -> Text -> Text
forall t. ToTxt t => t -> Text -> Text
<<> Text
" does not exist"
    Just BackendSourceMetadata
exists -> do
      let backendType :: BackendType
backendType = BackendSourceMetadata -> BackendType
getBackendType BackendSourceMetadata
exists
      case BackendType
backendType of
        Postgres PostgresKind
_ -> AnyBackend SourceMetadata -> Maybe QueryTagsConfig -> m EncJSON
setQueryTagsConfigInMetadata (BackendSourceMetadata -> AnyBackend SourceMetadata
unBackendSourceMetadata BackendSourceMetadata
exists) (QueryTagsConfig -> Maybe QueryTagsConfig
forall a. a -> Maybe a
Just QueryTagsConfig
queryTagsConfig)
        BackendType
_ -> BackendType -> m EncJSON
forall {m :: * -> *} {a} {a}.
(MonadError QErr m, ToTxt a) =>
a -> m a
queryTagsNotSupported BackendType
backendType
  where
    getBackendType :: BackendSourceMetadata -> BackendType
    getBackendType :: BackendSourceMetadata -> BackendType
getBackendType BackendSourceMetadata
exists =
      forall (c :: BackendType -> Constraint) (i :: BackendType -> *) r.
AllBackendsSatisfy c =>
AnyBackend i -> (forall (b :: BackendType). c b => i b -> r) -> r
AB.dispatchAnyBackend @Backend (BackendSourceMetadata -> AnyBackend SourceMetadata
unBackendSourceMetadata BackendSourceMetadata
exists) ((forall (b :: BackendType).
  Backend b =>
  SourceMetadata b -> BackendType)
 -> BackendType)
-> (forall (b :: BackendType).
    Backend b =>
    SourceMetadata b -> BackendType)
-> BackendType
forall a b. (a -> b) -> a -> b
$ \(SourceMetadata b
_sourceMetadata :: SourceMetadata b) ->
        BackendTag b -> BackendType
forall (b :: BackendType). BackendTag b -> BackendType
reify (BackendTag b -> BackendType) -> BackendTag b -> BackendType
forall a b. (a -> b) -> a -> b
$ forall (b :: BackendType). HasTag b => BackendTag b
backendTag @b

    setQueryTagsConfigInMetadata :: AnyBackend SourceMetadata -> Maybe QueryTagsConfig -> m EncJSON
setQueryTagsConfigInMetadata AnyBackend SourceMetadata
exists Maybe QueryTagsConfig
qtConfig = do
      let metadataModifier :: MetadataModifier
metadataModifier = AnyBackend SourceMetadata
-> Maybe QueryTagsConfig -> MetadataModifier
queryTagsMetadataModifier AnyBackend SourceMetadata
exists Maybe QueryTagsConfig
qtConfig
      MetadataObjId -> MetadataModifier -> m ()
forall (m :: * -> *).
(QErrM m, CacheRWM m, MetadataM m) =>
MetadataObjId -> MetadataModifier -> m ()
buildSchemaCacheFor (SourceName -> MetadataObjId
MOSource SourceName
sourceName) MetadataModifier
metadataModifier
      EncJSON -> m EncJSON
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return EncJSON
successMsg

    queryTagsNotSupported :: a -> m a
queryTagsNotSupported a
backendType = Code -> Text -> m a
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
NotSupported (Text -> m a) -> Text -> m a
forall a b. (a -> b) -> a -> b
$ a -> Text
forall a. ToTxt a => a -> Text
toTxt a
backendType Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" sources do not support query-tags yet"

    queryTagsMetadataModifier :: AnyBackend SourceMetadata
-> Maybe QueryTagsConfig -> MetadataModifier
queryTagsMetadataModifier AnyBackend SourceMetadata
exists Maybe QueryTagsConfig
qtConfig =
      forall (c :: BackendType -> Constraint) (i :: BackendType -> *) r.
AllBackendsSatisfy c =>
AnyBackend i -> (forall (b :: BackendType). c b => i b -> r) -> r
AB.dispatchAnyBackend @Backend AnyBackend SourceMetadata
exists ((forall (b :: BackendType).
  Backend b =>
  SourceMetadata b -> MetadataModifier)
 -> MetadataModifier)
-> (forall (b :: BackendType).
    Backend b =>
    SourceMetadata b -> MetadataModifier)
-> MetadataModifier
forall a b. (a -> b) -> a -> b
$ \(SourceMetadata b
_sourceMetadata :: SourceMetadata b) ->
        (Metadata -> Metadata) -> MetadataModifier
MetadataModifier ((Metadata -> Metadata) -> MetadataModifier)
-> (Metadata -> Metadata) -> MetadataModifier
forall a b. (a -> b) -> a -> b
$ (InsOrdHashMap SourceName BackendSourceMetadata
 -> Identity (InsOrdHashMap SourceName BackendSourceMetadata))
-> Metadata -> Identity Metadata
Lens' Metadata (InsOrdHashMap SourceName BackendSourceMetadata)
metaSources ((InsOrdHashMap SourceName BackendSourceMetadata
  -> Identity (InsOrdHashMap SourceName BackendSourceMetadata))
 -> Metadata -> Identity Metadata)
-> ((Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
    -> InsOrdHashMap SourceName BackendSourceMetadata
    -> Identity (InsOrdHashMap SourceName BackendSourceMetadata))
-> (Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
-> Metadata
-> Identity Metadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Index (InsOrdHashMap SourceName BackendSourceMetadata)
-> Traversal'
     (InsOrdHashMap SourceName BackendSourceMetadata)
     (IxValue (InsOrdHashMap SourceName BackendSourceMetadata))
forall m. Ixed m => Index m -> Traversal' m (IxValue m)
ix Index (InsOrdHashMap SourceName BackendSourceMetadata)
SourceName
sourceName ((BackendSourceMetadata -> Identity BackendSourceMetadata)
 -> InsOrdHashMap SourceName BackendSourceMetadata
 -> Identity (InsOrdHashMap SourceName BackendSourceMetadata))
-> ((Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
    -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> (Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
-> InsOrdHashMap SourceName BackendSourceMetadata
-> Identity (InsOrdHashMap SourceName BackendSourceMetadata)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (b :: BackendType).
Backend b =>
Prism' BackendSourceMetadata (SourceMetadata b)
toSourceMetadata @b ((SourceMetadata b -> Identity (SourceMetadata b))
 -> BackendSourceMetadata -> Identity BackendSourceMetadata)
-> ((Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
    -> SourceMetadata b -> Identity (SourceMetadata b))
-> (Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
-> BackendSourceMetadata
-> Identity BackendSourceMetadata
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
-> SourceMetadata b -> Identity (SourceMetadata b)
forall (b :: BackendType) (f :: * -> *).
Functor f =>
(Maybe QueryTagsConfig -> f (Maybe QueryTagsConfig))
-> SourceMetadata b -> f (SourceMetadata b)
smQueryTags ((Maybe QueryTagsConfig -> Identity (Maybe QueryTagsConfig))
 -> Metadata -> Identity Metadata)
-> Maybe QueryTagsConfig -> Metadata -> Metadata
forall s t a b. ASetter s t a b -> b -> s -> t
.~ Maybe QueryTagsConfig
qtConfig