{-# LANGUAGE UndecidableInstances #-}

-- | Metadata representation of a native query in the metadata,
--   as well as a parser and prettyprinter for the query code.
module Hasura.NativeQuery.Metadata
  ( NativeQueryName (..),
    NativeQueryMetadata (..),
    ArgumentName (..),
    InterpolatedItem (..),
    InterpolatedQuery (..),
    parseInterpolatedQuery,
    module Hasura.NativeQuery.Types,
  )
where

import Autodocodec
import Autodocodec qualified as AC
import Data.Aeson (FromJSON, ToJSON)
import Data.HashMap.Strict.InsOrd.Autodocodec (sortedElemsCodec)
import Data.Text.Extended qualified as T
import Hasura.LogicalModelResolver.Metadata (LogicalModelIdentifier)
import Hasura.NativeQuery.InterpolatedQuery
import Hasura.NativeQuery.Types (NativeQueryName (..), NullableScalarType (..))
import Hasura.Prelude hiding (first)
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.BackendTag (backendPrefix)
import Hasura.RQL.Types.BackendType
import Hasura.RQL.Types.Common (RelName)
import Hasura.RQL.Types.Relationships.Local (RelDef (..), RelManualNativeQueryConfig (..))

-- | copy pasta'd from Hasura.RQL.Types.Metadata.Common, forgive me Padre i did
-- not have the heart for the Real Fix.
type Relationships = InsOrdHashMap RelName

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

-- | The representation of native queries within the metadata structure.
data NativeQueryMetadata (b :: BackendType) = NativeQueryMetadata
  { forall (b :: BackendType). NativeQueryMetadata b -> NativeQueryName
_nqmRootFieldName :: NativeQueryName,
    forall (b :: BackendType).
NativeQueryMetadata b -> InterpolatedQuery ArgumentName
_nqmCode :: InterpolatedQuery ArgumentName,
    forall (b :: BackendType).
NativeQueryMetadata b -> LogicalModelIdentifier b
_nqmReturns :: LogicalModelIdentifier b,
    forall (b :: BackendType).
NativeQueryMetadata b
-> HashMap ArgumentName (NullableScalarType b)
_nqmArguments :: HashMap ArgumentName (NullableScalarType b),
    forall (b :: BackendType).
NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
_nqmArrayRelationships :: Relationships (RelDef (RelManualNativeQueryConfig b)),
    forall (b :: BackendType).
NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
_nqmObjectRelationships :: Relationships (RelDef (RelManualNativeQueryConfig b)),
    forall (b :: BackendType). NativeQueryMetadata b -> Maybe Text
_nqmDescription :: Maybe Text
  }
  deriving ((forall x. NativeQueryMetadata b -> Rep (NativeQueryMetadata b) x)
-> (forall x.
    Rep (NativeQueryMetadata b) x -> NativeQueryMetadata b)
-> Generic (NativeQueryMetadata b)
forall x. Rep (NativeQueryMetadata b) x -> NativeQueryMetadata b
forall x. NativeQueryMetadata b -> Rep (NativeQueryMetadata b) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (b :: BackendType) x.
Rep (NativeQueryMetadata b) x -> NativeQueryMetadata b
forall (b :: BackendType) x.
NativeQueryMetadata b -> Rep (NativeQueryMetadata b) x
$cfrom :: forall (b :: BackendType) x.
NativeQueryMetadata b -> Rep (NativeQueryMetadata b) x
from :: forall x. NativeQueryMetadata b -> Rep (NativeQueryMetadata b) x
$cto :: forall (b :: BackendType) x.
Rep (NativeQueryMetadata b) x -> NativeQueryMetadata b
to :: forall x. Rep (NativeQueryMetadata b) x -> NativeQueryMetadata b
Generic)

deriving instance (Backend b) => Eq (NativeQueryMetadata b)

deriving instance (Backend b) => Show (NativeQueryMetadata b)

instance (Backend b) => HasCodec (NativeQueryMetadata b) where
  codec :: JSONCodec (NativeQueryMetadata b)
codec =
    Text
-> JSONCodec (NativeQueryMetadata b)
-> JSONCodec (NativeQueryMetadata b)
forall input output.
Text -> ValueCodec input output -> ValueCodec input output
CommentCodec
      (Text
"A native query as represented in metadata.")
      (JSONCodec (NativeQueryMetadata b)
 -> JSONCodec (NativeQueryMetadata b))
-> JSONCodec (NativeQueryMetadata b)
-> JSONCodec (NativeQueryMetadata b)
forall a b. (a -> b) -> a -> b
$ Text
-> ObjectCodec (NativeQueryMetadata b) (NativeQueryMetadata b)
-> JSONCodec (NativeQueryMetadata b)
forall input output.
Text -> ObjectCodec input output -> ValueCodec input output
AC.object (forall (b :: BackendType). HasTag b => Text
backendPrefix @b Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"NativeQueryMetadata")
      (ObjectCodec (NativeQueryMetadata b) (NativeQueryMetadata b)
 -> JSONCodec (NativeQueryMetadata b))
-> ObjectCodec (NativeQueryMetadata b) (NativeQueryMetadata b)
-> JSONCodec (NativeQueryMetadata b)
forall a b. (a -> b) -> a -> b
$ NativeQueryName
-> InterpolatedQuery ArgumentName
-> LogicalModelIdentifier b
-> HashMap ArgumentName (NullableScalarType b)
-> Relationships (RelDef (RelManualNativeQueryConfig b))
-> Relationships (RelDef (RelManualNativeQueryConfig b))
-> Maybe Text
-> NativeQueryMetadata b
forall (b :: BackendType).
NativeQueryName
-> InterpolatedQuery ArgumentName
-> LogicalModelIdentifier b
-> HashMap ArgumentName (NullableScalarType b)
-> Relationships (RelDef (RelManualNativeQueryConfig b))
-> Relationships (RelDef (RelManualNativeQueryConfig b))
-> Maybe Text
-> NativeQueryMetadata b
NativeQueryMetadata
      (NativeQueryName
 -> InterpolatedQuery ArgumentName
 -> LogicalModelIdentifier b
 -> HashMap ArgumentName (NullableScalarType b)
 -> Relationships (RelDef (RelManualNativeQueryConfig b))
 -> Relationships (RelDef (RelManualNativeQueryConfig b))
 -> Maybe Text
 -> NativeQueryMetadata b)
-> Codec Object (NativeQueryMetadata b) NativeQueryName
-> Codec
     Object
     (NativeQueryMetadata b)
     (InterpolatedQuery ArgumentName
      -> LogicalModelIdentifier b
      -> HashMap ArgumentName (NullableScalarType b)
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Maybe Text
      -> NativeQueryMetadata b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> ObjectCodec NativeQueryName NativeQueryName
forall output.
HasCodec output =>
Text -> Text -> ObjectCodec output output
requiredField Text
"root_field_name" Text
fieldNameDoc
      ObjectCodec NativeQueryName NativeQueryName
-> (NativeQueryMetadata b -> NativeQueryName)
-> Codec Object (NativeQueryMetadata b) NativeQueryName
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b -> NativeQueryName
forall (b :: BackendType). NativeQueryMetadata b -> NativeQueryName
_nqmRootFieldName
        Codec
  Object
  (NativeQueryMetadata b)
  (InterpolatedQuery ArgumentName
   -> LogicalModelIdentifier b
   -> HashMap ArgumentName (NullableScalarType b)
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Maybe Text
   -> NativeQueryMetadata b)
-> Codec
     Object (NativeQueryMetadata b) (InterpolatedQuery ArgumentName)
-> Codec
     Object
     (NativeQueryMetadata b)
     (LogicalModelIdentifier b
      -> HashMap ArgumentName (NullableScalarType b)
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Maybe Text
      -> NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> Text
-> ObjectCodec
     (InterpolatedQuery ArgumentName) (InterpolatedQuery ArgumentName)
forall output.
HasCodec output =>
Text -> Text -> ObjectCodec output output
requiredField Text
"code" Text
sqlDoc
      ObjectCodec
  (InterpolatedQuery ArgumentName) (InterpolatedQuery ArgumentName)
-> (NativeQueryMetadata b -> InterpolatedQuery ArgumentName)
-> Codec
     Object (NativeQueryMetadata b) (InterpolatedQuery ArgumentName)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b -> InterpolatedQuery ArgumentName
forall (b :: BackendType).
NativeQueryMetadata b -> InterpolatedQuery ArgumentName
_nqmCode
        Codec
  Object
  (NativeQueryMetadata b)
  (LogicalModelIdentifier b
   -> HashMap ArgumentName (NullableScalarType b)
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Maybe Text
   -> NativeQueryMetadata b)
-> Codec Object (NativeQueryMetadata b) (LogicalModelIdentifier b)
-> Codec
     Object
     (NativeQueryMetadata b)
     (HashMap ArgumentName (NullableScalarType b)
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Maybe Text
      -> NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> Text
-> ObjectCodec
     (LogicalModelIdentifier b) (LogicalModelIdentifier b)
forall output.
HasCodec output =>
Text -> Text -> ObjectCodec output output
requiredField Text
"returns" Text
returnsDoc
      ObjectCodec (LogicalModelIdentifier b) (LogicalModelIdentifier b)
-> (NativeQueryMetadata b -> LogicalModelIdentifier b)
-> Codec Object (NativeQueryMetadata b) (LogicalModelIdentifier b)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b -> LogicalModelIdentifier b
forall (b :: BackendType).
NativeQueryMetadata b -> LogicalModelIdentifier b
_nqmReturns
        Codec
  Object
  (NativeQueryMetadata b)
  (HashMap ArgumentName (NullableScalarType b)
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Maybe Text
   -> NativeQueryMetadata b)
-> Codec
     Object
     (NativeQueryMetadata b)
     (HashMap ArgumentName (NullableScalarType b))
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Maybe Text
      -> NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> HashMap ArgumentName (NullableScalarType b)
-> Text
-> ObjectCodec
     (HashMap ArgumentName (NullableScalarType b))
     (HashMap ArgumentName (NullableScalarType b))
forall output.
HasCodec output =>
Text -> output -> Text -> ObjectCodec output output
optionalFieldWithDefault Text
"arguments" HashMap ArgumentName (NullableScalarType b)
forall a. Monoid a => a
mempty Text
argumentDoc
      ObjectCodec
  (HashMap ArgumentName (NullableScalarType b))
  (HashMap ArgumentName (NullableScalarType b))
-> (NativeQueryMetadata b
    -> HashMap ArgumentName (NullableScalarType b))
-> Codec
     Object
     (NativeQueryMetadata b)
     (HashMap ArgumentName (NullableScalarType b))
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b
-> HashMap ArgumentName (NullableScalarType b)
forall (b :: BackendType).
NativeQueryMetadata b
-> HashMap ArgumentName (NullableScalarType b)
_nqmArguments
        Codec
  Object
  (NativeQueryMetadata b)
  (Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Maybe Text
   -> NativeQueryMetadata b)
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b))
      -> Maybe Text -> NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> (RelDef (RelManualNativeQueryConfig b) -> RelName)
-> ObjectCodec
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
forall a k.
(HasCodec a, Eq a, Hashable k, Ord k, ToTxt k) =>
Text
-> (a -> k) -> ObjectCodec (InsOrdHashMap k a) (InsOrdHashMap k a)
optSortedList Text
"array_relationships" RelDef (RelManualNativeQueryConfig b) -> RelName
forall a. RelDef a -> RelName
_rdName
      ObjectCodec
  (Relationships (RelDef (RelManualNativeQueryConfig b)))
  (Relationships (RelDef (RelManualNativeQueryConfig b)))
-> (NativeQueryMetadata b
    -> Relationships (RelDef (RelManualNativeQueryConfig b)))
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
forall (b :: BackendType).
NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
_nqmArrayRelationships
        Codec
  Object
  (NativeQueryMetadata b)
  (Relationships (RelDef (RelManualNativeQueryConfig b))
   -> Maybe Text -> NativeQueryMetadata b)
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
-> Codec
     Object
     (NativeQueryMetadata b)
     (Maybe Text -> NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> (RelDef (RelManualNativeQueryConfig b) -> RelName)
-> ObjectCodec
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
forall a k.
(HasCodec a, Eq a, Hashable k, Ord k, ToTxt k) =>
Text
-> (a -> k) -> ObjectCodec (InsOrdHashMap k a) (InsOrdHashMap k a)
optSortedList Text
"object_relationships" RelDef (RelManualNativeQueryConfig b) -> RelName
forall a. RelDef a -> RelName
_rdName
      ObjectCodec
  (Relationships (RelDef (RelManualNativeQueryConfig b)))
  (Relationships (RelDef (RelManualNativeQueryConfig b)))
-> (NativeQueryMetadata b
    -> Relationships (RelDef (RelManualNativeQueryConfig b)))
-> Codec
     Object
     (NativeQueryMetadata b)
     (Relationships (RelDef (RelManualNativeQueryConfig b)))
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
forall (b :: BackendType).
NativeQueryMetadata b
-> Relationships (RelDef (RelManualNativeQueryConfig b))
_nqmObjectRelationships
        Codec
  Object
  (NativeQueryMetadata b)
  (Maybe Text -> NativeQueryMetadata b)
-> Codec Object (NativeQueryMetadata b) (Maybe Text)
-> ObjectCodec (NativeQueryMetadata b) (NativeQueryMetadata b)
forall a b.
Codec Object (NativeQueryMetadata b) (a -> b)
-> Codec Object (NativeQueryMetadata b) a
-> Codec Object (NativeQueryMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> Text -> ObjectCodec (Maybe Text) (Maybe Text)
forall output.
HasCodec output =>
Text -> Text -> ObjectCodec (Maybe output) (Maybe output)
optionalField Text
"description" Text
descriptionDoc
      ObjectCodec (Maybe Text) (Maybe Text)
-> (NativeQueryMetadata b -> Maybe Text)
-> Codec Object (NativeQueryMetadata b) (Maybe Text)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= NativeQueryMetadata b -> Maybe Text
forall (b :: BackendType). NativeQueryMetadata b -> Maybe Text
_nqmDescription
    where
      fieldNameDoc :: Text
fieldNameDoc = Text
"Root field name for the native query"
      sqlDoc :: Text
sqlDoc = Text
"Native code expression (SQL) to run"
      argumentDoc :: Text
argumentDoc = Text
"Free variables in the expression and their types"
      returnsDoc :: Text
returnsDoc = Text
"Return type (table) of the expression"
      descriptionDoc :: Text
descriptionDoc = Text
"A description of the native query which appears in the graphql schema"

      optSortedList ::
        (HasCodec a, Eq a, Hashable k, Ord k, T.ToTxt k) =>
        Text ->
        (a -> k) ->
        ObjectCodec (InsOrdHashMap k a) (InsOrdHashMap k a)
      optSortedList :: forall a k.
(HasCodec a, Eq a, Hashable k, Ord k, ToTxt k) =>
Text
-> (a -> k) -> ObjectCodec (InsOrdHashMap k a) (InsOrdHashMap k a)
optSortedList Text
name a -> k
keyForElem =
        Text
-> JSONCodec (InsOrdHashMap k a)
-> InsOrdHashMap k a
-> ObjectCodec (InsOrdHashMap k a) (InsOrdHashMap k a)
forall output.
Eq output =>
Text -> JSONCodec output -> output -> ObjectCodec output output
AC.optionalFieldWithOmittedDefaultWith' Text
name ((a -> k) -> JSONCodec (InsOrdHashMap k a)
forall a k.
(HasCodec a, Hashable k, Ord k, ToTxt k) =>
(a -> k) -> JSONCodec (InsOrdHashMap k a)
sortedElemsCodec a -> k
keyForElem) InsOrdHashMap k a
forall a. Monoid a => a
mempty

deriving via
  (Autodocodec (NativeQueryMetadata b))
  instance
    (Backend b) => (FromJSON (NativeQueryMetadata b))

deriving via
  (Autodocodec (NativeQueryMetadata b))
  instance
    (Backend b) => (ToJSON (NativeQueryMetadata b))