mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 12:31:52 +03:00
3a79fdbfcc
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6870 GitOrigin-RevId: e55ec0a39c68e960665d59f6c1824f5ba115f84d
134 lines
5.5 KiB
Haskell
134 lines
5.5 KiB
Haskell
{-# LANGUAGE PolyKinds #-}
|
|
|
|
-- | We validate 'TransformFn' terms inside 'RequestTransform' before
|
|
-- dispatching Metadata actions in 'runMetadataQueryV1M'. Validation
|
|
-- follows the same HKD pattern from 'applyRequestTransform' but using
|
|
-- 'btraverseC' to call 'validate' from the 'Transform' class on all
|
|
-- the HKD fields.
|
|
module Hasura.RQL.DDL.Webhook.Transform.Validation
|
|
( Unvalidated (..),
|
|
Unvalidated1 (..),
|
|
unUnvalidate,
|
|
unUnvalidate1,
|
|
validateRequestTransform,
|
|
validateTransforms,
|
|
)
|
|
where
|
|
|
|
import Control.Lens (Lens', LensLike, lens, traverseOf)
|
|
import Data.Aeson (FromJSON, ToJSON)
|
|
import Data.Aeson qualified as J
|
|
import Data.Functor.Barbie (FunctorB (bmap), btraverseC)
|
|
import Data.Functor.Compose (Compose (..))
|
|
import Data.Kind
|
|
import Data.Validation (Validation, toEither)
|
|
import Hasura.Base.Error (Code (..), QErr (..), QErrExtra (..), err400)
|
|
import Hasura.EncJSON (EncJSON)
|
|
import Hasura.Prelude
|
|
import Hasura.RQL.DDL.Webhook.Transform
|
|
import Hasura.RQL.DDL.Webhook.Transform.Body (validateBodyTransformFn)
|
|
import Hasura.RQL.DDL.Webhook.Transform.Class
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
type Tuple1 a b = Compose ((,) a) b
|
|
|
|
type OptionalTuple1 a b = WithOptional (Tuple1 a b)
|
|
|
|
-- | A variation on 'RequestTransformFn' where 'TransformFn' is tupled
|
|
-- with 'TemplatingEngine'. This is necessary to validate the 'TransformFn'.
|
|
--
|
|
-- TODO: In the future we most likely want to embed the
|
|
-- 'TemplatingEngine' in the 'TransformFn' or the
|
|
-- 'Template'/'UnwrappedTemplate', in which case we would not need
|
|
-- this alias for validation.
|
|
type ValidationFields = RequestFields (OptionalTuple1 TemplatingEngine TransformFn)
|
|
|
|
-- TODO(SOLOMON): Add lens law unit tests
|
|
|
|
-- | A lens for zipping our defunctionalized transform with the
|
|
-- 'TemplatingEngine' for validation.
|
|
transformFns :: Lens' RequestTransform ValidationFields
|
|
transformFns = lens getter setter
|
|
where
|
|
getter :: RequestTransform -> ValidationFields
|
|
getter RequestTransform {..} =
|
|
bmap (WithOptional . fmap (Compose . (templateEngine,)) . getOptional) requestFields
|
|
|
|
setter :: RequestTransform -> ValidationFields -> RequestTransform
|
|
setter rt requestFields' =
|
|
rt {requestFields = bmap (WithOptional . fmap (snd . getCompose) . getOptional) requestFields'}
|
|
|
|
-- | Validate all 'TransformFn a' fields in the 'RequestTransform'.
|
|
validateRequestTransform ::
|
|
MonadError QErr m =>
|
|
RequestTransform ->
|
|
m RequestTransform
|
|
validateRequestTransform reqTransform =
|
|
liftEither $ mapLeft mkRequestQErr $ toEither $ transformFns (btraverseC @Transform validate') reqTransform
|
|
where
|
|
validate' ::
|
|
(Transform a) =>
|
|
OptionalTuple1 TemplatingEngine TransformFn a ->
|
|
Validation TransformErrorBundle (OptionalTuple1 TemplatingEngine TransformFn a)
|
|
validate' = \case
|
|
fn@(WithOptional (Just (Compose (engine, transformFn)))) ->
|
|
fn <$ validate engine transformFn
|
|
fn -> pure fn
|
|
|
|
mkRequestQErr :: TransformErrorBundle -> QErr
|
|
mkRequestQErr errBundle = (err400 ValidationFailed errMsg) {qeInternal = Just $ ExtraInternal internalErr}
|
|
where
|
|
internalErr = J.toJSON errBundle
|
|
errMsg = "request transform validation failed"
|
|
|
|
-------------------------------------------------------------------------------
|
|
|
|
-- | Used to annotate that a 'RequestTransform', or some record
|
|
-- containing a 'RequestTransform' has not yet been validated.
|
|
newtype Unvalidated a = Unvalidated {_unUnvalidate :: a}
|
|
deriving newtype (FromJSON, ToJSON)
|
|
|
|
-- | A lens for focusing through 'Unvalidated' in 'validateTransforms'.
|
|
unUnvalidate :: Lens' (Unvalidated a) a
|
|
unUnvalidate = lens _unUnvalidate (\_ a -> Unvalidated a)
|
|
|
|
-- | Used to annotate that a higher kinded type containing a
|
|
-- 'RequestTransform' has not yet been validated.
|
|
--
|
|
-- This is needed specifically for 'CreateEventTriggerQuery' and any
|
|
-- other type that is paramterized by a 'BackendType'.
|
|
newtype Unvalidated1 (f :: k -> Type) (a :: k) = Unvalidated1 {_unUnvalidate1 :: f a}
|
|
deriving newtype (FromJSON, ToJSON)
|
|
|
|
-- | A lens for focusing through 'Unvalidated1' in 'validateTransforms'.
|
|
unUnvalidate1 :: Lens' (Unvalidated1 f a) (f a)
|
|
unUnvalidate1 = lens _unUnvalidate1 (\_ a -> Unvalidated1 a)
|
|
|
|
-- | Used to focus into a records in 'RQLMetadataV1' and validate any
|
|
-- 'RequestTransform' terms present.
|
|
validateTransforms ::
|
|
(MonadError QErr m) =>
|
|
LensLike m api api RequestTransform RequestTransform ->
|
|
LensLike m api api MetadataResponseTransform MetadataResponseTransform ->
|
|
(api -> m EncJSON) ->
|
|
api ->
|
|
m EncJSON
|
|
validateTransforms focusRequestTransform focusResponseTransform f q =
|
|
(validateResponseTransformTemplate q) >>= validateRequestTransformTemplate >>= f
|
|
where
|
|
validateRequestTransformTemplate = traverseOf focusRequestTransform validateRequestTransform
|
|
validateResponseTransformTemplate = traverseOf focusResponseTransform validateResponseTransform
|
|
|
|
validateResponseTransform :: (MonadError QErr m) => MetadataResponseTransform -> m MetadataResponseTransform
|
|
validateResponseTransform mrt@MetadataResponseTransform {..} =
|
|
case mrtBodyTransform of
|
|
Just bodyTransform -> liftEither $ mapLeft mkResponseQErr $ toEither $ mrt <$ validateBodyTransformFn mrtTemplatingEngine bodyTransform
|
|
Nothing -> pure mrt
|
|
where
|
|
mkResponseQErr :: TransformErrorBundle -> QErr
|
|
mkResponseQErr errBundle = (err400 ValidationFailed errMsg) {qeInternal = Just $ ExtraInternal internalErr}
|
|
where
|
|
internalErr = J.toJSON errBundle
|
|
errMsg = "response transform validation failed"
|