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
-- rather than variables.
module Hasura.GraphQL.Execute.Inline
( inlineSelectionSet,
( InlineMT,
InlineM,
inlineSelectionSet,
inlineField,
runInlineMT,
runInlineM,
)
where
@ -74,6 +79,34 @@ type MonadInline 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
-- documentation for "Hasura.GraphQL.Execute.Inline" for details.
inlineSelectionSet ::
@ -106,6 +139,8 @@ inlineSelectionSet fragmentDefinitions selectionSet = do
Set.toList $
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
& flip evalStateT InlineState {_isFragmentCache = mempty}
& flip
@ -128,11 +163,8 @@ inlineSelection ::
MonadInline m =>
Selection FragmentSpread Name ->
m (Selection NoFragments Name)
inlineSelection (SelectionField field@Field {_fSelectionSet}) =
withPathK "selectionSet" $
withPathK (unName $ _fName field) $ do
selectionSet <- traverse inlineSelection _fSelectionSet
pure $! SelectionField field {_fSelectionSet = selectionSet}
inlineSelection (SelectionField field) =
withPathK "selectionSet" $ SelectionField <$> inlineField field
inlineSelection (SelectionFragmentSpread spread) =
withPathK "selectionSet" $
SelectionInlineFragment <$> inlineFragmentSpread spread
@ -140,6 +172,12 @@ inlineSelection (SelectionInlineFragment fragment@InlineFragment {_ifSelectionSe
selectionSet <- traverse inlineSelection _ifSelectionSet
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 ::
MonadInline m =>
FragmentSpread Name ->

View File

@ -1,8 +1,11 @@
-- | QuasiQuoter for parsing GraphQL fields in tests. See 'field' for details.
module Test.Parser.Field (field) where
import Control.Monad.Trans.Except
import Data.Attoparsec.Text qualified as Parser
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.Prelude
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.Quote
import Language.Haskell.TH.Syntax qualified as TH
import Unsafe.Coerce (unsafeCoerce)
-- | Warning: we are currently using unsafe coercions to convert the field. This
-- seems to work for now, but beware.
--
-- | Quasi-Quoter for GraphQL fields.
-- Example usage:
-- > [GQL.field|
-- > update_artist(
@ -27,21 +27,22 @@ import Unsafe.Coerce (unsafeCoerce)
field :: QuasiQuoter
field =
QuasiQuoter
{ quoteExp = evalFieldGQL,
{ quoteExp = fieldExp,
quotePat = \_ -> fail "invalid",
quoteType = \_ -> fail "invalid",
quoteDec = \_ -> fail "invalid"
}
evalFieldGQL :: String -> ExpQ
evalFieldGQL = either fail TH.lift . go
where
-- Note: @skipSpace@ is used here to allow trailing whitespace in the QQ.
go :: String -> Either String (GraphQL.Field GraphQL.NoFragments Variable)
go =
fmap fixField
. Parser.parseOnly (Parser.skipSpace *> GraphQL.field @GraphQL.Name)
. T.pack
fieldExp :: String -> ExpQ
fieldExp input = do
either fail TH.lift $
runExcept $ do
parsed <- hoistEither $ Parser.parseOnly (Parser.skipSpace *> GraphQL.field @GraphQL.Name) . T.pack $ input
fixField parsed
fixField :: GraphQL.Field GraphQL.FragmentSpread GraphQL.Name -> GraphQL.Field GraphQL.NoFragments Variable
fixField = unsafeCoerce
-- A parsed field can contain both fragments and variables.
-- 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