refactor(tests): Remove use of unsafeCoerce

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5682
GitOrigin-RevId: 78ac1482977f427148e7675e45ff9c515db02b68
This commit is contained in:
Philip Lykke Carlsen 2022-09-07 11:20:53 +00:00 committed by hasura-bot
parent bf91655c30
commit 8bc34e12e3
2 changed files with 61 additions and 22 deletions

View File

@ -34,7 +34,12 @@
-- performed by "Hasura.GraphQL.Execute.Resolve", but for fragment definitions -- performed by "Hasura.GraphQL.Execute.Resolve", but for fragment definitions
-- rather than variables. -- rather than variables.
module Hasura.GraphQL.Execute.Inline module Hasura.GraphQL.Execute.Inline
( inlineSelectionSet, ( InlineMT,
InlineM,
inlineSelectionSet,
inlineField,
runInlineMT,
runInlineM,
) )
where where
@ -74,6 +79,34 @@ type MonadInline m =
MonadState InlineState m MonadState InlineState m
) )
type InlineMT m a = MonadError QErr m => (StateT InlineState (ReaderT InlineEnv m)) a
type InlineM a = InlineMT (Except QErr) a
{-# INLINE runInlineMT #-}
runInlineMT ::
forall m a.
(MonadError QErr m) =>
HashMap Name FragmentDefinition ->
InlineMT m a ->
m a
runInlineMT uniqueFragmentDefinitions =
flip
runReaderT
InlineEnv
{ _ieFragmentDefinitions = uniqueFragmentDefinitions,
_ieFragmentStack = []
}
. flip evalStateT InlineState {_isFragmentCache = mempty}
{-# INLINE runInlineM #-}
runInlineM ::
forall a.
HashMap Name FragmentDefinition ->
InlineM a ->
Either QErr a
runInlineM fragments = runExcept . runInlineMT fragments
-- | Inlines all fragment spreads in a 'SelectionSet'; see the module -- | Inlines all fragment spreads in a 'SelectionSet'; see the module
-- documentation for "Hasura.GraphQL.Execute.Inline" for details. -- documentation for "Hasura.GraphQL.Execute.Inline" for details.
inlineSelectionSet :: inlineSelectionSet ::
@ -106,6 +139,8 @@ inlineSelectionSet fragmentDefinitions selectionSet = do
Set.toList $ Set.toList $
Set.difference definedFragmentNames usedFragmentNames Set.difference definedFragmentNames usedFragmentNames
) )
-- The below code is a manual inlining of 'runInlineMT', as appearently the
-- inlining optimization does not trigger, even with the INLINE pragma.
traverse inlineSelection selectionSet traverse inlineSelection selectionSet
& flip evalStateT InlineState {_isFragmentCache = mempty} & flip evalStateT InlineState {_isFragmentCache = mempty}
& flip & flip
@ -128,11 +163,8 @@ inlineSelection ::
MonadInline m => MonadInline m =>
Selection FragmentSpread Name -> Selection FragmentSpread Name ->
m (Selection NoFragments Name) m (Selection NoFragments Name)
inlineSelection (SelectionField field@Field {_fSelectionSet}) = inlineSelection (SelectionField field) =
withPathK "selectionSet" $ withPathK "selectionSet" $ SelectionField <$> inlineField field
withPathK (unName $ _fName field) $ do
selectionSet <- traverse inlineSelection _fSelectionSet
pure $! SelectionField field {_fSelectionSet = selectionSet}
inlineSelection (SelectionFragmentSpread spread) = inlineSelection (SelectionFragmentSpread spread) =
withPathK "selectionSet" $ withPathK "selectionSet" $
SelectionInlineFragment <$> inlineFragmentSpread spread SelectionInlineFragment <$> inlineFragmentSpread spread
@ -140,6 +172,12 @@ inlineSelection (SelectionInlineFragment fragment@InlineFragment {_ifSelectionSe
selectionSet <- traverse inlineSelection _ifSelectionSet selectionSet <- traverse inlineSelection _ifSelectionSet
pure $! SelectionInlineFragment fragment {_ifSelectionSet = selectionSet} pure $! SelectionInlineFragment fragment {_ifSelectionSet = selectionSet}
{-# INLINE inlineField #-}
inlineField :: MonadInline m => Field FragmentSpread Name -> m (Field NoFragments Name)
inlineField field@(Field {_fSelectionSet}) = withPathK (unName $ _fName field) $ do
selectionSet <- traverse inlineSelection _fSelectionSet
pure $! field {_fSelectionSet = selectionSet}
inlineFragmentSpread :: inlineFragmentSpread ::
MonadInline m => MonadInline m =>
FragmentSpread Name -> FragmentSpread Name ->

View File

@ -1,8 +1,11 @@
-- | QuasiQuoter for parsing GraphQL fields in tests. See 'field' for details. -- | QuasiQuoter for parsing GraphQL fields in tests. See 'field' for details.
module Test.Parser.Field (field) where module Test.Parser.Field (field) where
import Control.Monad.Trans.Except
import Data.Attoparsec.Text qualified as Parser import Data.Attoparsec.Text qualified as Parser
import Data.Text qualified as T import Data.Text qualified as T
import Hasura.Base.Error (showQErr)
import Hasura.GraphQL.Execute.Inline (inlineField, runInlineM)
import Hasura.GraphQL.Parser.Variable import Hasura.GraphQL.Parser.Variable
import Hasura.Prelude import Hasura.Prelude
import Language.GraphQL.Draft.Parser qualified as GraphQL import Language.GraphQL.Draft.Parser qualified as GraphQL
@ -10,11 +13,8 @@ import Language.GraphQL.Draft.Syntax qualified as GraphQL
import Language.Haskell.TH.Lib (ExpQ) import Language.Haskell.TH.Lib (ExpQ)
import Language.Haskell.TH.Quote import Language.Haskell.TH.Quote
import Language.Haskell.TH.Syntax qualified as TH import Language.Haskell.TH.Syntax qualified as TH
import Unsafe.Coerce (unsafeCoerce)
-- | Warning: we are currently using unsafe coercions to convert the field. This -- | Quasi-Quoter for GraphQL fields.
-- seems to work for now, but beware.
--
-- Example usage: -- Example usage:
-- > [GQL.field| -- > [GQL.field|
-- > update_artist( -- > update_artist(
@ -27,21 +27,22 @@ import Unsafe.Coerce (unsafeCoerce)
field :: QuasiQuoter field :: QuasiQuoter
field = field =
QuasiQuoter QuasiQuoter
{ quoteExp = evalFieldGQL, { quoteExp = fieldExp,
quotePat = \_ -> fail "invalid", quotePat = \_ -> fail "invalid",
quoteType = \_ -> fail "invalid", quoteType = \_ -> fail "invalid",
quoteDec = \_ -> fail "invalid" quoteDec = \_ -> fail "invalid"
} }
evalFieldGQL :: String -> ExpQ
evalFieldGQL = either fail TH.lift . go
where where
-- Note: @skipSpace@ is used here to allow trailing whitespace in the QQ. fieldExp :: String -> ExpQ
go :: String -> Either String (GraphQL.Field GraphQL.NoFragments Variable) fieldExp input = do
go = either fail TH.lift $
fmap fixField runExcept $ do
. Parser.parseOnly (Parser.skipSpace *> GraphQL.field @GraphQL.Name) parsed <- hoistEither $ Parser.parseOnly (Parser.skipSpace *> GraphQL.field @GraphQL.Name) . T.pack $ input
. T.pack fixField parsed
fixField :: GraphQL.Field GraphQL.FragmentSpread GraphQL.Name -> GraphQL.Field GraphQL.NoFragments Variable -- A parsed field can contain both fragments and variables.
fixField = unsafeCoerce -- We support neither yet.
fixField :: GraphQL.Field GraphQL.FragmentSpread GraphQL.Name -> Except String (GraphQL.Field GraphQL.NoFragments Variable)
fixField f = do
x <- except $ mapLeft (T.unpack . showQErr) $ runInlineM mempty . inlineField $ f
traverse (throwE . ("Variables are not supported in tests yet: " ++) . show) x