mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
Moving kriti function references into a single module for coordination of availability
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5095 GitOrigin-RevId: 8394c33f0182baad53306c6efc3ab720957b7339
This commit is contained in:
parent
b326fd978a
commit
6821b90910
@ -199,7 +199,7 @@ Please submit any feedback you may have for this feature at https://github.com/h
|
|||||||
### Bug fixes and improvements
|
### Bug fixes and improvements
|
||||||
|
|
||||||
- server: fix bug where hasura SQL trigger was not dropped when MSSQL source is dropped
|
- server: fix bug where hasura SQL trigger was not dropped when MSSQL source is dropped
|
||||||
- server: Kriti `basicFunctions` now available for REST Connectors
|
- server: Kriti `basicFunctions` now available for REST Connectors and Webhook Transforms
|
||||||
- server: use `root_field_namespace` as prefix for remote schema (fixes #8438)
|
- server: use `root_field_namespace` as prefix for remote schema (fixes #8438)
|
||||||
- server: allow all argument types for BigQuery routines
|
- server: allow all argument types for BigQuery routines
|
||||||
- server: fix prefix/suffix behaviour for `graphql-default` naming convention (fixes #8544)
|
- server: fix prefix/suffix behaviour for `graphql-default` naming convention (fixes #8544)
|
||||||
|
@ -395,6 +395,7 @@ library
|
|||||||
, Control.Monad.Unique
|
, Control.Monad.Unique
|
||||||
, Data.Aeson.Extended
|
, Data.Aeson.Extended
|
||||||
, Data.Aeson.KeyMap.Extended
|
, Data.Aeson.KeyMap.Extended
|
||||||
|
, Data.Aeson.Kriti.Functions
|
||||||
, Data.Environment
|
, Data.Environment
|
||||||
, Data.HashMap.Strict.Extended
|
, Data.HashMap.Strict.Extended
|
||||||
, Data.HashMap.Strict.Multi
|
, Data.HashMap.Strict.Multi
|
||||||
|
65
server/src-lib/Data/Aeson/Kriti/Functions.hs
Normal file
65
server/src-lib/Data/Aeson/Kriti/Functions.hs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
-- | Module of reusable functions for Kriti transforms.
|
||||||
|
--
|
||||||
|
-- NOTE: This defines an alternative `runKritiWith` that includes the basicFunctions by default.
|
||||||
|
-- You should probably invoke Kriti through this module rather than directly in order to
|
||||||
|
-- make updating the functions available only require touching this module.
|
||||||
|
--
|
||||||
|
-- TODO: This should be added to the documentation and referenced in (for-example) REST Connectors once
|
||||||
|
-- the documentation refactor project is complete.
|
||||||
|
module Data.Aeson.Kriti.Functions (runKriti, runKritiWith, basicFunctions, environmentFunctions, sessionFunctions) where
|
||||||
|
|
||||||
|
import Control.Arrow (left)
|
||||||
|
import Data.Aeson qualified as J
|
||||||
|
import Data.Environment qualified as Env
|
||||||
|
import Data.HashMap.Strict qualified as M
|
||||||
|
import Data.Text qualified as T
|
||||||
|
import Hasura.Prelude
|
||||||
|
import Hasura.Session (SessionVariables, getSessionVariableValue, mkSessionVariable)
|
||||||
|
import Kriti qualified
|
||||||
|
import Kriti.CustomFunctions qualified as Kriti
|
||||||
|
import Kriti.Error (SerializeError (serialize), SerializedError)
|
||||||
|
import Kriti.Error qualified as Kriti
|
||||||
|
|
||||||
|
type KritiFunc = J.Value -> Either Kriti.CustomFunctionError J.Value
|
||||||
|
|
||||||
|
-- | `Data.Aeson.Kriti.Functions.runKriti` attaches the basicFunctions by default
|
||||||
|
-- NOTE: The error type is SerializedError due to KritiError not currently being exported
|
||||||
|
runKriti :: Text -> [(Text, J.Value)] -> Either SerializedError J.Value
|
||||||
|
runKriti t m = left serialize $ Kriti.runKritiWith t m basicFunctions
|
||||||
|
|
||||||
|
-- | `Data.Aeson.Kriti.Functions.runKritiWith` attaches the basicFunctions by default.
|
||||||
|
runKritiWith :: Text -> [(Text, J.Value)] -> HashMap Text KritiFunc -> Either SerializedError J.Value
|
||||||
|
runKritiWith t m f = left serialize $ Kriti.runKritiWith t m (basicFunctions <> f)
|
||||||
|
|
||||||
|
-- | Re-Export of the Kriti 'stdlib'
|
||||||
|
basicFunctions :: M.HashMap Text KritiFunc
|
||||||
|
basicFunctions = Kriti.basicFuncMap
|
||||||
|
|
||||||
|
-- | Functions that interact with environment variables
|
||||||
|
environmentFunctions :: Env.Environment -> M.HashMap Text KritiFunc
|
||||||
|
environmentFunctions env =
|
||||||
|
M.fromList
|
||||||
|
[ ("getEnvironmentVariable", getEnvVar)
|
||||||
|
]
|
||||||
|
where
|
||||||
|
getEnvVar :: J.Value -> Either Kriti.CustomFunctionError J.Value
|
||||||
|
getEnvVar = \case
|
||||||
|
J.Null -> Right $ J.Null
|
||||||
|
J.String k -> Right $ J.toJSON $ Env.lookupEnv env (T.unpack k)
|
||||||
|
_ -> Left $ Kriti.CustomFunctionError "Environment variable name should be a string"
|
||||||
|
|
||||||
|
-- | Functions that interact with HGE session during requests
|
||||||
|
sessionFunctions :: Maybe SessionVariables -> M.HashMap Text KritiFunc
|
||||||
|
sessionFunctions sessionVars = M.singleton "getSessionVariable" getSessionVar
|
||||||
|
where
|
||||||
|
-- Returns Null if session-variables aren't passed in
|
||||||
|
-- Throws an error if session variable isn't found. Perhaps a version that returns null would also be useful.
|
||||||
|
-- Lookups are case-insensitive
|
||||||
|
getSessionVar :: J.Value -> Either Kriti.CustomFunctionError J.Value
|
||||||
|
getSessionVar = \case
|
||||||
|
J.Null -> Right $ J.Null
|
||||||
|
J.String txt ->
|
||||||
|
case sessionVars >>= getSessionVariableValue (mkSessionVariable txt) of
|
||||||
|
Just x -> Right $ J.String x
|
||||||
|
Nothing -> Left . Kriti.CustomFunctionError $ "Session variable \"" <> txt <> "\" not found"
|
||||||
|
_ -> Left $ Kriti.CustomFunctionError "Session variable name should be a string"
|
@ -7,6 +7,7 @@ where
|
|||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
import Data.Aeson qualified as J
|
import Data.Aeson qualified as J
|
||||||
|
import Data.Aeson.Kriti.Functions qualified as KFunc
|
||||||
import Data.Environment qualified as Env
|
import Data.Environment qualified as Env
|
||||||
import Data.HashMap.Strict qualified as M
|
import Data.HashMap.Strict qualified as M
|
||||||
import Data.Text qualified as T
|
import Data.Text qualified as T
|
||||||
@ -14,8 +15,6 @@ import Hasura.Backends.DataConnector.API qualified as API
|
|||||||
import Hasura.Backends.DataConnector.Adapter.Types (ConnSourceConfig (ConnSourceConfig, template, value), SourceConfig (..))
|
import Hasura.Backends.DataConnector.Adapter.Types (ConnSourceConfig (ConnSourceConfig, template, value), SourceConfig (..))
|
||||||
import Hasura.Base.Error (Code (NotSupported), QErr, throw400)
|
import Hasura.Base.Error (Code (NotSupported), QErr, throw400)
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
import Kriti qualified
|
|
||||||
import Kriti.CustomFunctions qualified as Kriti
|
|
||||||
import Kriti.Error qualified as Kriti
|
import Kriti.Error qualified as Kriti
|
||||||
|
|
||||||
transformConfig :: (MonadError QErr m) => API.Config -> Maybe Text -> [(T.Text, J.Value)] -> Env.Environment -> m API.Config
|
transformConfig :: (MonadError QErr m) => API.Config -> Maybe Text -> [(T.Text, J.Value)] -> Env.Environment -> m API.Config
|
||||||
@ -23,8 +22,8 @@ transformConfig config maybeTemplate scope env = do
|
|||||||
case maybeTemplate of
|
case maybeTemplate of
|
||||||
Nothing -> pure config
|
Nothing -> pure config
|
||||||
(Just t) ->
|
(Just t) ->
|
||||||
case Kriti.runKritiWith t (("$config", J.toJSON config) : scope) (additionalFunctions env) of
|
case KFunc.runKritiWith t (("$config", J.toJSON config) : scope) (additionalFunctions env) of
|
||||||
Left e -> throw400 NotSupported $ "transformConfig: Kriti template transform failed - " <> tshow (Kriti.serialize e)
|
Left e -> throw400 NotSupported $ "transformConfig: Kriti template transform failed - " <> tshow e
|
||||||
Right (J.Object r) -> pure $ API.Config r
|
Right (J.Object r) -> pure $ API.Config r
|
||||||
Right o -> throw400 NotSupported $ "transformConfig: Kriti did not decode into Object - " <> tshow o
|
Right o -> throw400 NotSupported $ "transformConfig: Kriti did not decode into Object - " <> tshow o
|
||||||
|
|
||||||
@ -37,10 +36,4 @@ transformConnSourceConfig :: (MonadError QErr m) => ConnSourceConfig -> [(T.Text
|
|||||||
transformConnSourceConfig ConnSourceConfig {value, template} scope env = transformConfig value template scope env
|
transformConnSourceConfig ConnSourceConfig {value, template} scope env = transformConfig value template scope env
|
||||||
|
|
||||||
additionalFunctions :: Env.Environment -> M.HashMap T.Text (J.Value -> Either Kriti.CustomFunctionError J.Value)
|
additionalFunctions :: Env.Environment -> M.HashMap T.Text (J.Value -> Either Kriti.CustomFunctionError J.Value)
|
||||||
additionalFunctions env = M.singleton "env" getEnv <> Kriti.basicFuncMap
|
additionalFunctions env = KFunc.environmentFunctions env
|
||||||
where
|
|
||||||
getEnv :: J.Value -> Either Kriti.CustomFunctionError J.Value
|
|
||||||
getEnv x = case x of
|
|
||||||
J.Null -> Right $ J.Null
|
|
||||||
J.String k -> Right $ J.toJSON $ Env.lookupEnv env (T.unpack k)
|
|
||||||
_ -> Left $ Kriti.CustomFunctionError "Environment variable name should be a string"
|
|
||||||
|
@ -55,13 +55,13 @@ where
|
|||||||
import Control.Lens (Lens', lens, set, traverseOf, view)
|
import Control.Lens (Lens', lens, set, traverseOf, view)
|
||||||
import Data.Aeson (FromJSON, ToJSON)
|
import Data.Aeson (FromJSON, ToJSON)
|
||||||
import Data.Aeson.Extended qualified as J
|
import Data.Aeson.Extended qualified as J
|
||||||
|
import Data.Aeson.Kriti.Functions qualified as KFunc
|
||||||
import Data.Bifunctor (first)
|
import Data.Bifunctor (first)
|
||||||
import Data.ByteString.Lazy qualified as BL
|
import Data.ByteString.Lazy qualified as BL
|
||||||
import Data.CaseInsensitive qualified as CI
|
import Data.CaseInsensitive qualified as CI
|
||||||
import Data.Coerce (Coercible)
|
import Data.Coerce (Coercible)
|
||||||
import Data.Functor.Barbie (AllBF, ApplicativeB, ConstraintsB, FunctorB, TraversableB)
|
import Data.Functor.Barbie (AllBF, ApplicativeB, ConstraintsB, FunctorB, TraversableB)
|
||||||
import Data.Functor.Barbie qualified as B
|
import Data.Functor.Barbie qualified as B
|
||||||
import Data.HashMap.Strict qualified as M
|
|
||||||
import Data.Text.Encoding qualified as TE
|
import Data.Text.Encoding qualified as TE
|
||||||
import Data.Validation qualified as V
|
import Data.Validation qualified as V
|
||||||
import Hasura.Incremental (Cacheable)
|
import Hasura.Incremental (Cacheable)
|
||||||
@ -73,9 +73,7 @@ import Hasura.RQL.DDL.Webhook.Transform.Headers (Headers (..), HeadersTransformF
|
|||||||
import Hasura.RQL.DDL.Webhook.Transform.Method
|
import Hasura.RQL.DDL.Webhook.Transform.Method
|
||||||
import Hasura.RQL.DDL.Webhook.Transform.QueryParams
|
import Hasura.RQL.DDL.Webhook.Transform.QueryParams
|
||||||
import Hasura.RQL.DDL.Webhook.Transform.Url
|
import Hasura.RQL.DDL.Webhook.Transform.Url
|
||||||
import Hasura.Session (SessionVariables, getSessionVariableValue, mkSessionVariable)
|
import Hasura.Session (SessionVariables)
|
||||||
import Kriti qualified (runKriti)
|
|
||||||
import Kriti.Error qualified as Kriti (CustomFunctionError (CustomFunctionError), serialize)
|
|
||||||
import Network.HTTP.Client.Transformable qualified as HTTP
|
import Network.HTTP.Client.Transformable qualified as HTTP
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
@ -376,18 +374,8 @@ buildRespTransformCtx reqCtx sessionVars engine respBody =
|
|||||||
{ responseTransformBody = fromMaybe J.Null $ J.decode @J.Value respBody,
|
{ responseTransformBody = fromMaybe J.Null $ J.decode @J.Value respBody,
|
||||||
responseTransformReqCtx = J.toJSON reqCtx,
|
responseTransformReqCtx = J.toJSON reqCtx,
|
||||||
responseTransformEngine = engine,
|
responseTransformEngine = engine,
|
||||||
responseTransformFunctions = M.singleton "getSessionVariable" getSessionVar
|
responseTransformFunctions = KFunc.sessionFunctions sessionVars
|
||||||
}
|
}
|
||||||
where
|
|
||||||
getSessionVar :: J.Value -> Either Kriti.CustomFunctionError J.Value
|
|
||||||
getSessionVar inp = case inp of
|
|
||||||
J.String txt ->
|
|
||||||
case sessionVarValue of
|
|
||||||
Just x -> Right $ J.String x
|
|
||||||
Nothing -> Left . Kriti.CustomFunctionError $ "Session variable \"" <> txt <> "\" not found"
|
|
||||||
where
|
|
||||||
sessionVarValue = sessionVars >>= getSessionVariableValue (mkSessionVariable txt)
|
|
||||||
_ -> Left $ Kriti.CustomFunctionError "Session variable name should be a string"
|
|
||||||
|
|
||||||
-- | Construct a Template Transformation function for Responses
|
-- | Construct a Template Transformation function for Responses
|
||||||
--
|
--
|
||||||
@ -403,7 +391,7 @@ mkRespTemplateTransform _ Body.Remove _ = pure J.Null
|
|||||||
mkRespTemplateTransform engine (Body.ModifyAsJSON (Template template)) ResponseTransformCtx {..} =
|
mkRespTemplateTransform engine (Body.ModifyAsJSON (Template template)) ResponseTransformCtx {..} =
|
||||||
let context = [("$body", responseTransformBody), ("$request", responseTransformReqCtx)]
|
let context = [("$body", responseTransformBody), ("$request", responseTransformReqCtx)]
|
||||||
in case engine of
|
in case engine of
|
||||||
Kriti -> first (TransformErrorBundle . pure . J.toJSON . Kriti.serialize) $ Kriti.runKriti template context
|
Kriti -> first (TransformErrorBundle . pure . J.toJSON) $ KFunc.runKriti template context
|
||||||
mkRespTemplateTransform engine (Body.ModifyAsFormURLEncoded formTemplates) context =
|
mkRespTemplateTransform engine (Body.ModifyAsFormURLEncoded formTemplates) context =
|
||||||
case engine of
|
case engine of
|
||||||
Kriti -> do
|
Kriti -> do
|
||||||
|
@ -43,6 +43,7 @@ import Control.Arrow (left)
|
|||||||
import Control.Lens (bimap, view)
|
import Control.Lens (bimap, view)
|
||||||
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
|
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
|
||||||
import Data.Aeson qualified as J
|
import Data.Aeson qualified as J
|
||||||
|
import Data.Aeson.Kriti.Functions as KFunc
|
||||||
import Data.Binary.Builder (toLazyByteString)
|
import Data.Binary.Builder (toLazyByteString)
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.ByteString.Builder.Scientific (scientificBuilder)
|
import Data.ByteString.Builder.Scientific (scientificBuilder)
|
||||||
@ -54,9 +55,7 @@ import Data.Text.Encoding qualified as TE
|
|||||||
import Data.Validation (Validation, fromEither)
|
import Data.Validation (Validation, fromEither)
|
||||||
import Hasura.Incremental (Cacheable)
|
import Hasura.Incremental (Cacheable)
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
import Hasura.Session (SessionVariables, getSessionVariableValue, mkSessionVariable)
|
import Hasura.Session (SessionVariables)
|
||||||
import Kriti (runKritiWith)
|
|
||||||
import Kriti.CustomFunctions qualified as Kriti (basicFuncMap)
|
|
||||||
import Kriti.Error qualified as Kriti (CustomFunctionError (..), serialize)
|
import Kriti.Error qualified as Kriti (CustomFunctionError (..), serialize)
|
||||||
import Kriti.Parser qualified as Kriti (parser)
|
import Kriti.Parser qualified as Kriti (parser)
|
||||||
import Network.HTTP.Client.Transformable qualified as HTTP
|
import Network.HTTP.Client.Transformable qualified as HTTP
|
||||||
@ -166,25 +165,14 @@ mkReqTransformCtx url sessionVars rtcEngine reqData =
|
|||||||
view HTTP.queryParams reqData & fmap \(key, val) ->
|
view HTTP.queryParams reqData & fmap \(key, val) ->
|
||||||
(TE.decodeUtf8 key, fmap TE.decodeUtf8 val)
|
(TE.decodeUtf8 key, fmap TE.decodeUtf8 val)
|
||||||
in Just $ J.toJSON queryParams
|
in Just $ J.toJSON queryParams
|
||||||
rtcFunctions = M.singleton "getSessionVariable" getSessionVar
|
|
||||||
in RequestTransformCtx
|
in RequestTransformCtx
|
||||||
{ rtcBaseUrl,
|
{ rtcBaseUrl,
|
||||||
rtcBody,
|
rtcBody,
|
||||||
rtcSessionVariables,
|
rtcSessionVariables,
|
||||||
rtcQueryParams,
|
rtcQueryParams,
|
||||||
rtcEngine,
|
rtcEngine,
|
||||||
rtcFunctions = rtcFunctions <> Kriti.basicFuncMap
|
rtcFunctions = KFunc.sessionFunctions sessionVars
|
||||||
}
|
}
|
||||||
where
|
|
||||||
getSessionVar :: J.Value -> Either Kriti.CustomFunctionError J.Value
|
|
||||||
getSessionVar inp = case inp of
|
|
||||||
J.String txt ->
|
|
||||||
case sessionVarValue of
|
|
||||||
Just x -> Right $ J.String x
|
|
||||||
Nothing -> Left . Kriti.CustomFunctionError $ "Session variable \"" <> txt <> "\" not found"
|
|
||||||
where
|
|
||||||
sessionVarValue = sessionVars >>= getSessionVariableValue (mkSessionVariable txt)
|
|
||||||
_ -> Left $ Kriti.CustomFunctionError "Session variable name should be a string"
|
|
||||||
|
|
||||||
-- | Common context that is made available to all response transformations.
|
-- | Common context that is made available to all response transformations.
|
||||||
data ResponseTransformCtx = ResponseTransformCtx
|
data ResponseTransformCtx = ResponseTransformCtx
|
||||||
@ -257,9 +245,9 @@ runRequestTemplateTransform template RequestTransformCtx {rtcEngine = Kriti, ..}
|
|||||||
[ ("$query_params",) <$> rtcQueryParams,
|
[ ("$query_params",) <$> rtcQueryParams,
|
||||||
("$base_url",) <$> rtcBaseUrl
|
("$base_url",) <$> rtcBaseUrl
|
||||||
]
|
]
|
||||||
eResult = runKritiWith (unTemplate $ template) context rtcFunctions
|
eResult = KFunc.runKritiWith (unTemplate $ template) context rtcFunctions
|
||||||
in eResult & left \kritiErr ->
|
in eResult & left \kritiErr ->
|
||||||
let renderedErr = J.toJSON $ Kriti.serialize kritiErr
|
let renderedErr = J.toJSON kritiErr
|
||||||
in TransformErrorBundle [renderedErr]
|
in TransformErrorBundle [renderedErr]
|
||||||
|
|
||||||
-- TODO: Should this live in 'Hasura.RQL.DDL.Webhook.Transform.Validation'?
|
-- TODO: Should this live in 'Hasura.RQL.DDL.Webhook.Transform.Validation'?
|
||||||
@ -291,9 +279,9 @@ runResponseTemplateTransform ::
|
|||||||
Either TransformErrorBundle J.Value
|
Either TransformErrorBundle J.Value
|
||||||
runResponseTemplateTransform template ResponseTransformCtx {responseTransformEngine = Kriti, ..} =
|
runResponseTemplateTransform template ResponseTransformCtx {responseTransformEngine = Kriti, ..} =
|
||||||
let context = [("$body", responseTransformBody), ("$request", responseTransformReqCtx)]
|
let context = [("$body", responseTransformBody), ("$request", responseTransformReqCtx)]
|
||||||
eResult = runKritiWith (unTemplate $ template) context responseTransformFunctions
|
eResult = KFunc.runKritiWith (unTemplate $ template) context responseTransformFunctions
|
||||||
in eResult & left \kritiErr ->
|
in eResult & left \kritiErr ->
|
||||||
let renderedErr = J.toJSON $ Kriti.serialize kritiErr
|
let renderedErr = J.toJSON kritiErr
|
||||||
in TransformErrorBundle [renderedErr]
|
in TransformErrorBundle [renderedErr]
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user