Remove __typename from inline fragments in allowlist normalization #10769
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description
This PR improves the consistency of the GraphQL query allowlist normalization by ensuring that __typename fields are also removed from inline fragments. Previously, normalization only handled regular field selections, which led to inconsistencies when queries included inline fragments with __typename.
Changelog
Component : server
Type: bugfix
Product: community-edition
Short Changelog
Fix allowlist normalization to properly remove
__typenamefrom inline fragmentsRelated Issues
N/A
Solution and Design
The issue was in the
filterSelSetfunction inHasura/RQL/Types/Allowlist.hs. The function was handlingSelectionFieldto recursively filter nested selection sets and remove__typename, but was not handlingSelectionInlineFragment.The fix adds a case for
SelectionInlineFragmentthat recursively applies the same filtering logic to the inline fragment's selection set, ensuring__typenamefields areremoved consistently across all selection types.
Steps to test and verify
__typenamefields in the inline fragmentsI also tested the logic with a script:
script
{-# LANGUAGE OverloadedStrings #-} module Main where import qualified Data.Text as T -- Simplified AST and normalization implementation for testing data ExecutableDocument = ExecutableDocument [ExecutableDefinition] deriving (Show, Eq) data ExecutableDefinition = ExecutableDefinitionOperation OperationDefinition deriving (Show, Eq) data OperationDefinition = OperationDefinitionTyped TypedOperationDefinition deriving (Show, Eq) data TypedOperationDefinition = TypedOperationDefinition { todName :: Maybe T.Text , todSelectionSet :: SelectionSet } deriving (Show, Eq) type SelectionSet = [Selection] data Selection = SelectionField Field | SelectionInlineFragment InlineFragment deriving (Show, Eq) data Field = Field { fName :: T.Text , fSelectionSet :: SelectionSet } deriving (Show, Eq) data InlineFragment = InlineFragment { ifTypeCondition :: Maybe T.Text , ifSelectionSet :: SelectionSet } deriving (Show, Eq) -- Manual AST construction for testing astWithTypename :: ExecutableDocument astWithTypename = ExecutableDocument [ ExecutableDefinitionOperation $ OperationDefinitionTyped $ TypedOperationDefinition { todName = Just "GetNotifications" , todSelectionSet = [ SelectionField $ Field { fName = "notifications" , fSelectionSet = [ SelectionField $ Field "id" [] , SelectionField $ Field "createdAt" [] , SelectionInlineFragment $ InlineFragment { ifTypeCondition = Just "CommentNotification" , ifSelectionSet = [ SelectionField $ Field "__typename" [] -- This should be removed , SelectionField $ Field "comment" [] , SelectionField $ Field "author" [] ] } , SelectionInlineFragment $ InlineFragment { ifTypeCondition = Just "LikeNotification" , ifSelectionSet = [ SelectionField $ Field "__typename" [] -- This should be removed , SelectionField $ Field "postId" [] , SelectionField $ Field "likedBy" [] ] } ] } ] } ] astWithoutTypename :: ExecutableDocument astWithoutTypename = ExecutableDocument [ ExecutableDefinitionOperation $ OperationDefinitionTyped $ TypedOperationDefinition { todName = Just "GetNotifications" , todSelectionSet = [ SelectionField $ Field { fName = "notifications" , fSelectionSet = [ SelectionField $ Field "id" [] , SelectionField $ Field "createdAt" [] , SelectionInlineFragment $ InlineFragment { ifTypeCondition = Just "CommentNotification" , ifSelectionSet = [ SelectionField $ Field "comment" [] , SelectionField $ Field "author" [] ] } , SelectionInlineFragment $ InlineFragment { ifTypeCondition = Just "LikeNotification" , ifSelectionSet = [ SelectionField $ Field "postId" [] , SelectionField $ Field "likedBy" [] ] } ] } ] } ] -- Normalization function (with the fix applied) normalizeQuery :: ExecutableDocument -> ExecutableDocument normalizeQuery (ExecutableDocument defs) = ExecutableDocument $ map normalizeExecDef defs where normalizeExecDef (ExecutableDefinitionOperation opDef) = ExecutableDefinitionOperation $ normalizeOpDef opDef normalizeOpDef (OperationDefinitionTyped typeOpDef) = OperationDefinitionTyped typeOpDef { todSelectionSet = filterSelSet (todSelectionSet typeOpDef) } filterSelSet :: SelectionSet -> SelectionSet filterSelSet = concatMap filterSel filterSel :: Selection -> SelectionSet filterSel (SelectionField f) = if fName f == "__typename" then [] -- Remove __typename fields else [SelectionField f { fSelectionSet = filterSelSet (fSelectionSet f) }] filterSel (SelectionInlineFragment inf) = -- The fix: recursively process inline fragment's selection set [SelectionInlineFragment inf { ifSelectionSet = filterSelSet (ifSelectionSet inf) }] -- Test function testNormalization :: IO () testNormalization = do putStrLn "=== Testing allowlist normalization fix for inline fragments ===" putStrLn "" putStrLn "Test case: GetNotifications query with union types" putStrLn "- CommentNotification and LikeNotification inline fragments" putStrLn "- Each fragment contains __typename field that should be removed" putStrLn "" let normalized1 = normalizeQuery astWithTypename let normalized2 = normalizeQuery astWithoutTypename putStrLn "=== Test Result ===" if normalized1 == normalized2 then do putStrLn "✓ Test passed: Queries normalize to the same result" putStrLn "✓ __typename fields in inline fragments are correctly removed" putStrLn "✓ Allowlist matching will work correctly" else do putStrLn "✗ Test failed: Normalized queries differ" putStrLn "✗ __typename fields in inline fragments were not properly removed" putStrLn "\nNormalized AST with __typename:" print normalized1 putStrLn "\nNormalized AST without __typename:" print normalized2 main :: IO () main = testNormalizationLimitations, known bugs & workarounds
None
Server checklist
Catalog upgrade
Does this PR change Hasura Catalog version?
Metadata
Does this PR add a new Metadata feature?
GraphQL
Breaking changes