@@ -11,7 +11,6 @@ import Control.Concurrent.Extended (concurrentlyEIO, forConcurrentlyEIO)
1111import Control.Concurrent.STM qualified as STM
1212import Control.Lens hiding (contexts )
1313import Control.Monad.Memoize
14- import Data.Aeson.Ordered qualified as JO
1514import Data.HashMap.Strict qualified as HashMap
1615import Data.HashMap.Strict.InsOrd qualified as InsOrdHashMap
1716import Data.HashSet qualified as Set
@@ -23,6 +22,7 @@ import Hasura.Authentication.Role (RoleName, adminRoleName, mkRoleNameSafe)
2322import Hasura.Base.Error
2423import Hasura.Base.ErrorMessage
2524import Hasura.Base.ToErrorValue
25+ import Hasura.EncJSON
2626import Hasura.Function.Cache
2727import Hasura.GraphQL.ApolloFederation
2828import Hasura.GraphQL.Context
@@ -394,15 +394,20 @@ buildRoleContext sampledFeatureFlags options sources remotes actions customTypes
394394 (P. parserType <$> mutationParserFrontend)
395395 (P. parserType <$> subscriptionParser)
396396
397+ let queryRootFieldNamesFrontend = queryRootFieldNamesOf queryParserFrontend
398+ queryRootFieldNamesBackend = queryRootFieldNamesOf queryParserBackend
399+
397400 -- (since we're running this in parallel in caller, be strict)
398401 let ! frontendContext =
399402 GQLContext
400403 (finalizeParser queryParserFrontend)
404+ queryRootFieldNamesFrontend
401405 (finalizeParser <$> mutationParserFrontend)
402406 (finalizeParser <$> subscriptionParser)
403407 ! backendContext =
404408 GQLContext
405409 (finalizeParser queryParserBackend)
410+ queryRootFieldNamesBackend
406411 (finalizeParser <$> mutationParserBackend)
407412 (finalizeParser <$> subscriptionParser)
408413
@@ -527,14 +532,18 @@ buildRelayRoleContext options sources actions customTypes role expFeatures schem
527532 (P. parserType <$> mutationParserFrontend)
528533 (P. parserType <$> subscriptionParser)
529534
530- let frontendContext =
535+ let relayFrontendFieldNames = queryRootFieldNamesOf queryParserFrontend
536+ relayBackendFieldNames = queryRootFieldNamesOf queryParserBackend
537+ frontendContext =
531538 GQLContext
532539 (finalizeParser queryParserFrontend)
540+ relayFrontendFieldNames
533541 (finalizeParser <$> mutationParserFrontend)
534542 (finalizeParser <$> subscriptionParser)
535543 backendContext =
536544 GQLContext
537545 (finalizeParser queryParserBackend)
546+ relayBackendFieldNames
538547 (finalizeParser <$> mutationParserBackend)
539548 (finalizeParser <$> subscriptionParser)
540549
@@ -653,7 +662,8 @@ unauthenticatedContext options sources allRemotes expFeatures schemaSampledFeatu
653662 (P. parserType queryParser)
654663 (P. parserType <$> mutationParser)
655664 (P. parserType <$> subscriptionParser)
656- pure (GQLContext (finalizeParser queryParser) (finalizeParser <$> mutationParser) (finalizeParser <$> subscriptionParser), remoteErrors)
665+ let unauthFieldNames = queryRootFieldNamesOf queryParser
666+ pure (GQLContext (finalizeParser queryParser) unauthFieldNames (finalizeParser <$> mutationParser) (finalizeParser <$> subscriptionParser), remoteErrors)
657667
658668-------------------------------------------------------------------------------
659669-- Building parser fields
@@ -1024,13 +1034,14 @@ queryWithIntrospectionHelper basicQueryFP mutationP subscriptionP = do
10241034 -- Those two requirements cannot both be met when a service is mutations-only, and does not
10251035 -- provide any query. In such a case, to meet both of those, we introduce a placeholder query
10261036 -- in the schema.
1027- placeholderText = " There are no queries available to the current role. Either there are no sources or remote schemas configured, or the current role doesn't have the required permissions."
1028- placeholderField = NotNamespaced (RFRaw $ JO. String placeholderText) <$ P. selection_ Name. _no_queries_available (Just $ G. Description placeholderText) P. string
1037+ placeholderText = " There are no queries available to the current role. Either there are no sources or remote schemas configured, or the current role doesn't have the required permissions." :: Text
1038+ placeholderField = NotNamespaced (RFRaw . TypenameResult $ encJFromJValue placeholderText) <$ P. selection_ Name. _no_queries_available (Just $ G. Description placeholderText) P. string
10291039 fixedQueryFP = if null basicQueryFP then [placeholderField] else basicQueryFP
10301040 basicQueryP <- queryRootFromFields fixedQueryFP
10311041 let buildIntrospectionResponse printResponseFromSchema =
10321042 NotNamespaced
10331043 . RFRaw
1044+ . SchemaIntrospection
10341045 . printResponseFromSchema
10351046 <$> parseBuildIntrospectionSchema
10361047 (P. parserType basicQueryP)
@@ -1104,8 +1115,8 @@ customizeFields ::
11041115 (Functor f , MonadParse n ) =>
11051116 ResolvedSourceCustomization ->
11061117 MkTypename ->
1107- f [FieldParser n (RootField db remote action JO. Value )] ->
1108- f [FieldParser n (NamespacedField (RootField db remote action JO. Value ))]
1118+ f [FieldParser n (RootField db remote action RFRawPayload )] ->
1119+ f [FieldParser n (NamespacedField (RootField db remote action RFRawPayload ))]
11091120customizeFields ResolvedSourceCustomization {.. } =
11101121 fmap . customizeNamespace _rscRootNamespace (const typenameToRawRF)
11111122
@@ -1166,15 +1177,24 @@ queryRoot = Name._query_root
11661177finalizeParser :: Parser 'Output P. Parse a -> ParserFn a
11671178finalizeParser parser = P. toQErr . P. runParse . P. runParser parser
11681179
1180+ -- | Extract the top-level field names from a query root parser's output type,
1181+ -- for storage in 'GQLContext' and use in conflict detection ('checkConflictingNode').
1182+ -- Returns @[]@ if the type isn't a named object, which shouldn't happen for a
1183+ -- well-formed query root but is handled gracefully.
1184+ queryRootFieldNamesOf :: P. Parser 'Output n a -> [G. Name ]
1185+ queryRootFieldNamesOf p = case P. parserType p of
1186+ P. TNamed _ (P. Definition _ _ _ _ (P. TIObject oi)) -> map P. dName (P. oiFields oi)
1187+ _ -> []
1188+
11691189throwOnConflictingDefinitions :: (QErrM m ) => Either P. ConflictingDefinitions a -> m a
11701190throwOnConflictingDefinitions = either (throw500 . fromErrorMessage . toErrorValue) pure
11711191
11721192typenameToNamespacedRawRF ::
1173- P. ParsedSelection (NamespacedField (RootField db remote action JO. Value )) ->
1174- NamespacedField (RootField db remote action JO. Value )
1175- typenameToNamespacedRawRF = P. handleTypename $ NotNamespaced . RFRaw . JO. String . toTxt
1193+ P. ParsedSelection (NamespacedField (RootField db remote action RFRawPayload )) ->
1194+ NamespacedField (RootField db remote action RFRawPayload )
1195+ typenameToNamespacedRawRF = P. handleTypename $ NotNamespaced . RFRaw . TypenameResult . encJFromJValue . toTxt
11761196
11771197typenameToRawRF ::
1178- P. ParsedSelection (RootField db remote action JO. Value ) ->
1179- RootField db remote action JO. Value
1180- typenameToRawRF = P. handleTypename $ RFRaw . JO. String . toTxt
1198+ P. ParsedSelection (RootField db remote action RFRawPayload ) ->
1199+ RootField db remote action RFRawPayload
1200+ typenameToRawRF = P. handleTypename $ RFRaw . TypenameResult . encJFromJValue . toTxt
0 commit comments