{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Types for storing user-defined-functions in metadata
module Hasura.Function.Metadata
  ( FunctionMetadata (..),
    fmComment,
    fmConfiguration,
    fmFunction,
    fmPermissions,
  )
where

import Autodocodec hiding (object, (.=))
import Autodocodec qualified as AC
import Control.Lens hiding (set, (.=))
import Data.Aeson.Types
import Data.Text qualified as T
import Hasura.Function.Cache
import Hasura.Prelude
import Hasura.RQL.Types.Backend
import Hasura.RQL.Types.BackendTag (backendPrefix)

data FunctionMetadata b = FunctionMetadata
  { forall (b :: BackendType). FunctionMetadata b -> FunctionName b
_fmFunction :: FunctionName b,
    forall (b :: BackendType). FunctionMetadata b -> FunctionConfig b
_fmConfiguration :: FunctionConfig b,
    forall (b :: BackendType).
FunctionMetadata b -> [FunctionPermissionInfo]
_fmPermissions :: [FunctionPermissionInfo],
    forall (b :: BackendType). FunctionMetadata b -> Maybe Text
_fmComment :: Maybe Text
  }
  deriving ((forall x. FunctionMetadata b -> Rep (FunctionMetadata b) x)
-> (forall x. Rep (FunctionMetadata b) x -> FunctionMetadata b)
-> Generic (FunctionMetadata b)
forall x. Rep (FunctionMetadata b) x -> FunctionMetadata b
forall x. FunctionMetadata b -> Rep (FunctionMetadata b) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (b :: BackendType) x.
Rep (FunctionMetadata b) x -> FunctionMetadata b
forall (b :: BackendType) x.
FunctionMetadata b -> Rep (FunctionMetadata b) x
$cfrom :: forall (b :: BackendType) x.
FunctionMetadata b -> Rep (FunctionMetadata b) x
from :: forall x. FunctionMetadata b -> Rep (FunctionMetadata b) x
$cto :: forall (b :: BackendType) x.
Rep (FunctionMetadata b) x -> FunctionMetadata b
to :: forall x. Rep (FunctionMetadata b) x -> FunctionMetadata b
Generic)

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

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

instance (Backend b) => ToJSON (FunctionMetadata b) where
  toJSON :: FunctionMetadata b -> Value
toJSON = Options -> FunctionMetadata b -> Value
forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
genericToJSON Options
hasuraJSON

$(makeLenses ''FunctionMetadata)

instance (Backend b) => FromJSON (FunctionMetadata b) where
  parseJSON :: Value -> Parser (FunctionMetadata b)
parseJSON = String
-> (Object -> Parser (FunctionMetadata b))
-> Value
-> Parser (FunctionMetadata b)
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"FunctionMetadata" ((Object -> Parser (FunctionMetadata b))
 -> Value -> Parser (FunctionMetadata b))
-> (Object -> Parser (FunctionMetadata b))
-> Value
-> Parser (FunctionMetadata b)
forall a b. (a -> b) -> a -> b
$ \Object
o ->
    FunctionName b
-> FunctionConfig b
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
forall (b :: BackendType).
FunctionName b
-> FunctionConfig b
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
FunctionMetadata
      (FunctionName b
 -> FunctionConfig b
 -> [FunctionPermissionInfo]
 -> Maybe Text
 -> FunctionMetadata b)
-> Parser (FunctionName b)
-> Parser
     (FunctionConfig b
      -> [FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o
      Object -> Key -> Parser (FunctionName b)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"function"
      Parser
  (FunctionConfig b
   -> [FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
-> Parser (FunctionConfig b)
-> Parser
     ([FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o
      Object -> Key -> Parser (Maybe (FunctionConfig b))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"configuration"
      Parser (Maybe (FunctionConfig b))
-> FunctionConfig b -> Parser (FunctionConfig b)
forall a. Parser (Maybe a) -> a -> Parser a
.!= FunctionConfig b
forall (b :: BackendType). FunctionConfig b
emptyFunctionConfig
      Parser
  ([FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
-> Parser [FunctionPermissionInfo]
-> Parser (Maybe Text -> FunctionMetadata b)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o
      Object -> Key -> Parser (Maybe [FunctionPermissionInfo])
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"permissions"
      Parser (Maybe [FunctionPermissionInfo])
-> [FunctionPermissionInfo] -> Parser [FunctionPermissionInfo]
forall a. Parser (Maybe a) -> a -> Parser a
.!= []
      Parser (Maybe Text -> FunctionMetadata b)
-> Parser (Maybe Text) -> Parser (FunctionMetadata b)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o
      Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"comment"

instance (Backend b) => HasCodec (FunctionMetadata b) where
  codec :: JSONCodec (FunctionMetadata b)
codec =
    Text
-> JSONCodec (FunctionMetadata b) -> JSONCodec (FunctionMetadata b)
forall input output.
Text -> ValueCodec input output -> ValueCodec input output
CommentCodec
      ( [Text] -> Text
T.unlines
          [ Text
"A custom SQL function to add to the GraphQL schema with configuration.",
            Text
"",
            Text
"https://hasura.io/docs/latest/graphql/core/api-reference/schema-metadata-api/custom-functions.html#args-syntax"
          ]
      )
      (JSONCodec (FunctionMetadata b) -> JSONCodec (FunctionMetadata b))
-> JSONCodec (FunctionMetadata b) -> JSONCodec (FunctionMetadata b)
forall a b. (a -> b) -> a -> b
$ Text
-> ObjectCodec (FunctionMetadata b) (FunctionMetadata b)
-> JSONCodec (FunctionMetadata 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
"FunctionMetadata")
      (ObjectCodec (FunctionMetadata b) (FunctionMetadata b)
 -> JSONCodec (FunctionMetadata b))
-> ObjectCodec (FunctionMetadata b) (FunctionMetadata b)
-> JSONCodec (FunctionMetadata b)
forall a b. (a -> b) -> a -> b
$ FunctionName b
-> FunctionConfig b
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
forall (b :: BackendType).
FunctionName b
-> FunctionConfig b
-> [FunctionPermissionInfo]
-> Maybe Text
-> FunctionMetadata b
FunctionMetadata
      (FunctionName b
 -> FunctionConfig b
 -> [FunctionPermissionInfo]
 -> Maybe Text
 -> FunctionMetadata b)
-> Codec Object (FunctionMetadata b) (FunctionName b)
-> Codec
     Object
     (FunctionMetadata b)
     (FunctionConfig b
      -> [FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Text -> Text -> ObjectCodec (FunctionName b) (FunctionName b)
forall output.
HasCodec output =>
Text -> Text -> ObjectCodec output output
requiredField Text
"function" Text
nameDoc
      ObjectCodec (FunctionName b) (FunctionName b)
-> (FunctionMetadata b -> FunctionName b)
-> Codec Object (FunctionMetadata b) (FunctionName b)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= FunctionMetadata b -> FunctionName b
forall (b :: BackendType). FunctionMetadata b -> FunctionName b
_fmFunction
        Codec
  Object
  (FunctionMetadata b)
  (FunctionConfig b
   -> [FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
-> Codec Object (FunctionMetadata b) (FunctionConfig b)
-> Codec
     Object
     (FunctionMetadata b)
     ([FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
forall a b.
Codec Object (FunctionMetadata b) (a -> b)
-> Codec Object (FunctionMetadata b) a
-> Codec Object (FunctionMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> FunctionConfig b
-> Text
-> ObjectCodec (FunctionConfig b) (FunctionConfig b)
forall output.
(Eq output, HasCodec output) =>
Text -> output -> Text -> ObjectCodec output output
optionalFieldWithOmittedDefault Text
"configuration" FunctionConfig b
forall (b :: BackendType). FunctionConfig b
emptyFunctionConfig Text
configDoc
      ObjectCodec (FunctionConfig b) (FunctionConfig b)
-> (FunctionMetadata b -> FunctionConfig b)
-> Codec Object (FunctionMetadata b) (FunctionConfig b)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= FunctionMetadata b -> FunctionConfig b
forall (b :: BackendType). FunctionMetadata b -> FunctionConfig b
_fmConfiguration
        Codec
  Object
  (FunctionMetadata b)
  ([FunctionPermissionInfo] -> Maybe Text -> FunctionMetadata b)
-> Codec Object (FunctionMetadata b) [FunctionPermissionInfo]
-> Codec
     Object (FunctionMetadata b) (Maybe Text -> FunctionMetadata b)
forall a b.
Codec Object (FunctionMetadata b) (a -> b)
-> Codec Object (FunctionMetadata b) a
-> Codec Object (FunctionMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text
-> [FunctionPermissionInfo]
-> ObjectCodec [FunctionPermissionInfo] [FunctionPermissionInfo]
forall output.
(Eq output, HasCodec output) =>
Text -> output -> ObjectCodec output output
optionalFieldWithOmittedDefault' Text
"permissions" []
      ObjectCodec [FunctionPermissionInfo] [FunctionPermissionInfo]
-> (FunctionMetadata b -> [FunctionPermissionInfo])
-> Codec Object (FunctionMetadata b) [FunctionPermissionInfo]
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= FunctionMetadata b -> [FunctionPermissionInfo]
forall (b :: BackendType).
FunctionMetadata b -> [FunctionPermissionInfo]
_fmPermissions
        Codec
  Object (FunctionMetadata b) (Maybe Text -> FunctionMetadata b)
-> Codec Object (FunctionMetadata b) (Maybe Text)
-> ObjectCodec (FunctionMetadata b) (FunctionMetadata b)
forall a b.
Codec Object (FunctionMetadata b) (a -> b)
-> Codec Object (FunctionMetadata b) a
-> Codec Object (FunctionMetadata b) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Text -> ObjectCodec (Maybe Text) (Maybe Text)
forall output.
HasCodec output =>
Text -> ObjectCodec (Maybe output) (Maybe output)
optionalField' Text
"comment"
      ObjectCodec (Maybe Text) (Maybe Text)
-> (FunctionMetadata b -> Maybe Text)
-> Codec Object (FunctionMetadata b) (Maybe Text)
forall oldInput output newInput.
ObjectCodec oldInput output
-> (newInput -> oldInput) -> ObjectCodec newInput output
AC..= FunctionMetadata b -> Maybe Text
forall (b :: BackendType). FunctionMetadata b -> Maybe Text
_fmComment
    where
      nameDoc :: Text
nameDoc = Text
"Name of the SQL function"
      configDoc :: Text
configDoc = Text
"Configuration for the SQL function"