Refactor type name customization

Source typename customization (hasura/graphql-engine@aac64f2c81) introduced a mechanism to change certain names in the GraphQL schema that is exposed. In particular it allows last-minute modification of:
1. the names of some types, and
2. the names of some root fields.

The above two items are assigned distinct customization algorithms, and at times both algorithms are in scope. So a need to distinguish them is needed.

In the original design, this was addressed by introducing a newtype wrapper `Typename` around GraphQL `Name`s, dedicated to the names of types. However, in the majority of the codebase, type names are also represented by `Name`. For this reason, it was unavoidable to allow for easy conversion. This was supported by a `HasName Typename` instance, as well as by publishing the constructors of `Typename`.

This means that the type safety that newtypes can add is lost. In particular, it is now very easy to confuse type name customization with root field name customization.

This refactors the above design by instead introducing newtypes around the customization operations:
```haskell
newtype MkTypename = MkTypename {runMkTypename :: Name -> Name}
  deriving (Semigroup, Monoid) via (Endo Name)

newtype MkRootFieldName = MkRootFieldName {runMkRootFieldName :: Name -> Name}
  deriving (Semigroup, Monoid) via (Endo Name)
```
The `Monoid` instance allows easy composition of customization operations, piggybacking off of the type of `Endo`maps.

This design allows safe co-existence of the two customization algorithms, while avoiding the syntactic overhead of packing and unpacking newtypes.

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2989
GitOrigin-RevId: da3a353a9b003ee40c8d0a1e02872e99d2edd3ca
This commit is contained in:
Auke Booij 2021-11-30 10:51:46 +01:00 committed by hasura-bot
parent 5bfce057c6
commit 29158900d8
19 changed files with 127 additions and 145 deletions

View File

@ -208,7 +208,7 @@ bqColumnParser columnType (G.Nullability isNullable) =
BigQuery.DatetimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DatetimeValue . BigQuery.Datetime <$> P.string BigQuery.DatetimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DatetimeValue . BigQuery.Datetime <$> P.string
BigQuery.GeographyScalarType -> pure $ possiblyNullable scalarType $ BigQuery.GeographyValue . BigQuery.Geography <$> P.string BigQuery.GeographyScalarType -> pure $ possiblyNullable scalarType $ BigQuery.GeographyValue . BigQuery.Geography <$> P.string
BigQuery.TimestampScalarType -> do BigQuery.TimestampScalarType -> do
let schemaType = P.Nullable . P.TNamed $ P.mkDefinition (P.Typename stringScalar) Nothing P.TIScalar let schemaType = P.Nullable . P.TNamed $ P.mkDefinition stringScalar Nothing P.TIScalar
pure $ pure $
possiblyNullable scalarType $ possiblyNullable scalarType $
Parser Parser
@ -291,7 +291,7 @@ bqComparisonExps = P.memoize 'comparisonExps $ \columnType -> do
typedParser <- columnParser columnType (G.Nullability False) typedParser <- columnParser columnType (G.Nullability False)
nullableTextParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability True) nullableTextParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability True)
-- textParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability False) -- textParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability False)
let name = P.Typename $ P.getName typedParser <> $$(G.litName "_BigQuery_comparison_exp") let name = P.getName typedParser <> $$(G.litName "_BigQuery_comparison_exp")
desc = desc =
G.Description $ G.Description $
"Boolean expression to compare columns of type " "Boolean expression to compare columns of type "

View File

@ -275,7 +275,7 @@ msColumnParser columnType (G.Nullability isNullable) =
MSSQL.BitType -> pure $ ODBC.BoolValue <$> P.boolean MSSQL.BitType -> pure $ ODBC.BoolValue <$> P.boolean
_ -> do _ -> do
name <- MSSQL.mkMSSQLScalarTypeName scalarType name <- MSSQL.mkMSSQLScalarTypeName scalarType
let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition (P.Typename name) Nothing P.TIScalar let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition name Nothing P.TIScalar
pure $ pure $
Parser Parser
{ pType = schemaType, { pType = schemaType,
@ -358,7 +358,7 @@ msComparisonExps = P.memoize 'comparisonExps \columnType -> do
textListParser = fmap openValueOrigin <$> P.list textParser textListParser = fmap openValueOrigin <$> P.list textParser
-- field info -- field info
let name = P.Typename $ P.getName typedParser <> $$(G.litName "_MSSQL_comparison_exp") let name = P.getName typedParser <> $$(G.litName "_MSSQL_comparison_exp")
desc = desc =
G.Description $ G.Description $
"Boolean expression to compare columns of type " "Boolean expression to compare columns of type "

View File

@ -185,7 +185,7 @@ columnParser' columnType (G.Nullability isNullable) =
MySQL.Timestamp -> pure $ possiblyNullable scalarType $ MySQL.TimestampValue <$> P.string MySQL.Timestamp -> pure $ possiblyNullable scalarType $ MySQL.TimestampValue <$> P.string
_ -> do _ -> do
name <- MySQL.mkMySQLScalarTypeName scalarType name <- MySQL.mkMySQLScalarTypeName scalarType
let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition (P.Typename name) Nothing P.TIScalar let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition name Nothing P.TIScalar
pure $ pure $
Parser Parser
{ pType = schemaType, { pType = schemaType,
@ -261,7 +261,7 @@ comparisonExps' = P.memoize 'comparisonExps $ \columnType -> do
typedParser <- columnParser columnType (G.Nullability False) typedParser <- columnParser columnType (G.Nullability False)
nullableTextParser <- columnParser (ColumnScalar @'MySQL MySQL.VarChar) (G.Nullability True) nullableTextParser <- columnParser (ColumnScalar @'MySQL MySQL.VarChar) (G.Nullability True)
textParser <- columnParser (ColumnScalar @'MySQL MySQL.VarChar) (G.Nullability False) textParser <- columnParser (ColumnScalar @'MySQL MySQL.VarChar) (G.Nullability False)
let name = P.Typename $ P.getName typedParser <> $$(G.litName "_MySQL_comparison_exp") let name = P.getName typedParser <> $$(G.litName "_MySQL_comparison_exp")
desc = desc =
G.Description $ G.Description $
"Boolean expression to compare columns of type " "Boolean expression to compare columns of type "

View File

@ -213,7 +213,7 @@ columnParser columnType (G.Nullability isNullable) =
-- not accept strings. -- not accept strings.
-- --
-- TODO: introduce new dedicated scalars for Postgres column types. -- TODO: introduce new dedicated scalars for Postgres column types.
name <- P.Typename <$> mkScalarTypeName scalarType name <- mkScalarTypeName scalarType
let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition name Nothing P.TIScalar let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition name Nothing P.TIScalar
pure $ pure $
Parser Parser
@ -313,7 +313,7 @@ comparisonExps = P.memoize 'comparisonExps \columnType -> do
-- `ltxtquery` represents a full-text-search-like pattern for matching `ltree` values. -- `ltxtquery` represents a full-text-search-like pattern for matching `ltree` values.
ltxtqueryParser <- columnParser (ColumnScalar PGLtxtquery) (G.Nullability False) ltxtqueryParser <- columnParser (ColumnScalar PGLtxtquery) (G.Nullability False)
maybeCastParser <- castExp columnType maybeCastParser <- castExp columnType
let name = P.Typename $ P.getName typedParser <> $$(G.litName "_comparison_exp") let name = P.getName typedParser <> $$(G.litName "_comparison_exp")
desc = desc =
G.Description $ G.Description $
"Boolean expression to compare columns of type " "Boolean expression to compare columns of type "
@ -564,7 +564,7 @@ comparisonExps = P.memoize 'comparisonExps \columnType -> do
targetName <- mkScalarTypeName targetScalar targetName <- mkScalarTypeName targetScalar
targetOpExps <- comparisonExps $ ColumnScalar targetScalar targetOpExps <- comparisonExps $ ColumnScalar targetScalar
let field = P.fieldOptional targetName Nothing $ (targetScalar,) <$> targetOpExps let field = P.fieldOptional targetName Nothing $ (targetScalar,) <$> targetOpExps
pure $ P.object (P.Typename sourceName) Nothing $ M.fromList . maybeToList <$> field pure $ P.object sourceName Nothing $ M.fromList . maybeToList <$> field
geographyWithinDistanceInput :: geographyWithinDistanceInput ::
forall pgKind m n r. forall pgKind m n r.
@ -581,7 +581,7 @@ geographyWithinDistanceInput = do
booleanParser <- columnParser (ColumnScalar PGBoolean) (G.Nullability True) booleanParser <- columnParser (ColumnScalar PGBoolean) (G.Nullability True)
floatParser <- columnParser (ColumnScalar PGFloat) (G.Nullability False) floatParser <- columnParser (ColumnScalar PGFloat) (G.Nullability False)
pure $ pure $
P.object (P.Typename $$(G.litName "st_d_within_geography_input")) Nothing $ P.object $$(G.litName "st_d_within_geography_input") Nothing $
DWithinGeogOp <$> (mkParameter <$> P.field $$(G.litName "distance") Nothing floatParser) DWithinGeogOp <$> (mkParameter <$> P.field $$(G.litName "distance") Nothing floatParser)
<*> (mkParameter <$> P.field $$(G.litName "from") Nothing geographyParser) <*> (mkParameter <$> P.field $$(G.litName "from") Nothing geographyParser)
<*> (mkParameter <$> P.fieldWithDefault $$(G.litName "use_spheroid") Nothing (G.VBoolean True) booleanParser) <*> (mkParameter <$> P.fieldWithDefault $$(G.litName "use_spheroid") Nothing (G.VBoolean True) booleanParser)
@ -594,7 +594,7 @@ geometryWithinDistanceInput = do
geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False) geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False)
floatParser <- columnParser (ColumnScalar PGFloat) (G.Nullability False) floatParser <- columnParser (ColumnScalar PGFloat) (G.Nullability False)
pure $ pure $
P.object (P.Typename $$(G.litName "st_d_within_input")) Nothing $ P.object $$(G.litName "st_d_within_input") Nothing $
DWithinGeomOp <$> (mkParameter <$> P.field $$(G.litName "distance") Nothing floatParser) DWithinGeomOp <$> (mkParameter <$> P.field $$(G.litName "distance") Nothing floatParser)
<*> (mkParameter <$> P.field $$(G.litName "from") Nothing geometryParser) <*> (mkParameter <$> P.field $$(G.litName "from") Nothing geometryParser)
@ -606,7 +606,7 @@ intersectsNbandGeomInput = do
geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False) geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False)
integerParser <- columnParser (ColumnScalar PGInteger) (G.Nullability False) integerParser <- columnParser (ColumnScalar PGInteger) (G.Nullability False)
pure $ pure $
P.object (P.Typename $$(G.litName "st_intersects_nband_geom_input")) Nothing $ P.object $$(G.litName "st_intersects_nband_geom_input") Nothing $
STIntersectsNbandGeommin <$> (mkParameter <$> P.field $$(G.litName "nband") Nothing integerParser) STIntersectsNbandGeommin <$> (mkParameter <$> P.field $$(G.litName "nband") Nothing integerParser)
<*> (mkParameter <$> P.field $$(G.litName "geommin") Nothing geometryParser) <*> (mkParameter <$> P.field $$(G.litName "geommin") Nothing geometryParser)
@ -618,7 +618,7 @@ intersectsGeomNbandInput = do
geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False) geometryParser <- columnParser (ColumnScalar PGGeometry) (G.Nullability False)
integerParser <- columnParser (ColumnScalar PGInteger) (G.Nullability False) integerParser <- columnParser (ColumnScalar PGInteger) (G.Nullability False)
pure $ pure $
P.object (P.Typename $$(G.litName "st_intersects_geom_nband_input")) Nothing $ P.object $$(G.litName "st_intersects_geom_nband_input") Nothing $
STIntersectsGeomminNband STIntersectsGeomminNband
<$> (mkParameter <$> P.field $$(G.litName "geommin") Nothing geometryParser) <$> (mkParameter <$> P.field $$(G.litName "geommin") Nothing geometryParser)
<*> (fmap mkParameter <$> P.fieldOptional $$(G.litName "nband") Nothing integerParser) <*> (fmap mkParameter <$> P.fieldOptional $$(G.litName "nband") Nothing integerParser)

View File

@ -97,7 +97,7 @@ customizeNamespace (Just namespace) fromParsedSelection mkNamespaceTypename fiel
parser :: Parser 'Output n (NamespacedField a) parser :: Parser 'Output n (NamespacedField a)
parser = parser =
Namespaced . OMap.mapWithKey fromParsedSelection Namespaced . OMap.mapWithKey fromParsedSelection
<$> P.selectionSet (mkNamespaceTypename namespace) Nothing fieldParsers <$> P.selectionSet (runMkTypename mkNamespaceTypename namespace) Nothing fieldParsers
customizeNamespace Nothing _ _ fieldParsers = customizeNamespace Nothing _ _ fieldParsers =
-- No namespace so just wrap the field parser results in @NotNamespaced@. -- No namespace so just wrap the field parser results in @NotNamespaced@.
fmap NotNamespaced <$> fieldParsers fmap NotNamespaced <$> fieldParsers

View File

@ -255,7 +255,7 @@ fieldWithDefault name description defaultValue parser =
enum :: enum ::
MonadParse m => MonadParse m =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
NonEmpty (Definition EnumValueInfo, a) -> NonEmpty (Definition EnumValueInfo, a) ->
Parser 'Both m a Parser 'Both m a
@ -289,7 +289,7 @@ enum name description values =
-- the spec. -- the spec.
object :: object ::
MonadParse m => MonadParse m =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
InputFieldsParser m a -> InputFieldsParser m a ->
Parser 'Input m a Parser 'Input m a

View File

@ -122,7 +122,7 @@ multiple parser = parser {pType = Nullable $ TList $ pType parser}
-- | A variant of 'selectionSetObject' which doesn't implement any interfaces -- | A variant of 'selectionSetObject' which doesn't implement any interfaces
selectionSet :: selectionSet ::
MonadParse m => MonadParse m =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
[FieldParser m a] -> [FieldParser m a] ->
Parser 'Output m (OMap.InsOrdHashMap Name (ParsedSelection a)) Parser 'Output m (OMap.InsOrdHashMap Name (ParsedSelection a))
@ -130,7 +130,7 @@ selectionSet name desc fields = selectionSetObject name desc fields []
safeSelectionSet :: safeSelectionSet ::
(MonadError QErr n, MonadParse m) => (MonadError QErr n, MonadParse m) =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
[FieldParser m a] -> [FieldParser m a] ->
n (Parser 'Output m (OMap.InsOrdHashMap Name (ParsedSelection a))) n (Parser 'Output m (OMap.InsOrdHashMap Name (ParsedSelection a)))
@ -147,7 +147,7 @@ safeSelectionSet name desc fields
-- See also Note [Selectability of tables]. -- See also Note [Selectability of tables].
selectionSetObject :: selectionSetObject ::
MonadParse m => MonadParse m =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
-- | Fields of this object, including any fields that are required from the -- | Fields of this object, including any fields that are required from the
-- interfaces that it implements. Note that we can't derive those fields from -- interfaces that it implements. Note that we can't derive those fields from
@ -208,7 +208,7 @@ selectionSetObject name description parsers implementsInterfaces =
selectionSetInterface :: selectionSetInterface ::
(MonadParse n, Traversable t) => (MonadParse n, Traversable t) =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
-- | Fields defined in this interface -- | Fields defined in this interface
[FieldParser n a] -> [FieldParser n a] ->
@ -239,7 +239,7 @@ selectionSetInterface name description fields objectImplementations =
selectionSetUnion :: selectionSetUnion ::
(MonadParse n, Traversable t) => (MonadParse n, Traversable t) =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
-- | The member object types. -- | The member object types.
t (Parser 'Output n b) -> t (Parser 'Output n b) ->

View File

@ -87,8 +87,8 @@ uuid = mkScalar name Nothing \case
name = $$(litName "uuid") name = $$(litName "uuid")
json, jsonb :: MonadParse m => Parser 'Both m A.Value json, jsonb :: MonadParse m => Parser 'Both m A.Value
json = jsonScalar (Typename $$(litName "json")) Nothing json = jsonScalar $$(litName "json") Nothing
jsonb = jsonScalar (Typename $$(litName "jsonb")) Nothing jsonb = jsonScalar $$(litName "jsonb") Nothing
-- | Additional validation on integers. We do keep the same type name in the schema for backwards -- | Additional validation on integers. We do keep the same type name in the schema for backwards
-- compatibility. -- compatibility.
@ -130,7 +130,7 @@ bigInt = mkScalar intScalar Nothing \case
-- explicit use of the Parser constructor. -- explicit use of the Parser constructor.
unsafeRawScalar :: unsafeRawScalar ::
MonadParse n => MonadParse n =>
Typename -> Name ->
Maybe Description -> Maybe Description ->
Parser 'Both n (InputValue Variable) Parser 'Both n (InputValue Variable)
unsafeRawScalar name description = unsafeRawScalar name description =
@ -141,7 +141,7 @@ unsafeRawScalar name description =
-- | Creates a parser that transforms its input into a JSON value. 'valueToJSON' -- | Creates a parser that transforms its input into a JSON value. 'valueToJSON'
-- does properly unpack variables. -- does properly unpack variables.
jsonScalar :: MonadParse m => Typename -> Maybe Description -> Parser 'Both m A.Value jsonScalar :: MonadParse m => Name -> Maybe Description -> Parser 'Both m A.Value
jsonScalar name description = jsonScalar name description =
Parser Parser
{ pType = schemaType, { pType = schemaType,
@ -165,7 +165,7 @@ mkScalar name description parser =
pParser = peelVariable (toGraphQLType schemaType) >=> parser pParser = peelVariable (toGraphQLType schemaType) >=> parser
} }
where where
schemaType = NonNullable $ TNamed $ mkDefinition (Typename name) description TIScalar schemaType = NonNullable $ TNamed $ mkDefinition name description TIScalar
convertWith :: convertWith ::
MonadParse m => MonadParse m =>

View File

@ -6,8 +6,7 @@ module Hasura.GraphQL.Parser.Schema
type (<:) (..), type (<:) (..),
-- * Types -- * Types
Typename (..), MkTypename (..),
MkTypename,
mkTypename, mkTypename,
withTypenameCustomization, withTypenameCustomization,
Type (..), Type (..),
@ -32,7 +31,6 @@ module Hasura.GraphQL.Parser.Schema
UnionInfo (..), UnionInfo (..),
-- * Definitions -- * Definitions
DefinitionName,
Definition (..), Definition (..),
mkDefinition, mkDefinition,
addDefinitionUnique, addDefinitionUnique,
@ -63,6 +61,7 @@ import Data.HashMap.Strict.Extended qualified as Map
import Data.HashSet qualified as Set import Data.HashSet qualified as Set
import Data.Hashable (Hashable (..)) import Data.Hashable (Hashable (..))
import Data.List.NonEmpty qualified as NE import Data.List.NonEmpty qualified as NE
import Data.Monoid
import Data.Text qualified as T import Data.Text qualified as T
import Data.Text.Extended import Data.Text.Extended
import Hasura.Incremental (Cacheable) import Hasura.Incremental (Cacheable)
@ -82,25 +81,20 @@ class HasName a where
instance HasName Name where instance HasName Name where
getName = id getName = id
-- | newtype wrapper to allow us to distinguish type names from other GraphQL names. -- | Type name customization
newtype Typename = Typename {unTypename :: Name} deriving (Eq, Ord, Show, HasName, J.ToJSON) newtype MkTypename = MkTypename {runMkTypename :: Name -> Name}
deriving (Semigroup, Monoid) via (Endo Name)
instance ToTxt Typename where -- | Inject a new @MkTypename@ customization function into the environment.
toTxt = toTxt . getName
-- | Function to turn a @Name@ into a @Typename@ while possibly applying some customizations.
type MkTypename = Name -> Typename
-- | Inject a new @Typename@ customization function into the environment.
-- This can be used by schema-building code (with @MonadBuildSchema@ constraint) to ensure -- This can be used by schema-building code (with @MonadBuildSchema@ constraint) to ensure
-- the correct typename customizations are applied. -- the correct type name customizations are applied.
withTypenameCustomization :: forall m r a. (MonadReader r m, Has MkTypename r) => MkTypename -> m a -> m a withTypenameCustomization :: forall m r a. (MonadReader r m, Has MkTypename r) => MkTypename -> m a -> m a
withTypenameCustomization = local . set hasLens withTypenameCustomization = local . set hasLens
-- | Apply the typename customization function from the current environment. -- | Apply the type name customization function from the current environment.
mkTypename :: (MonadReader r m, Has MkTypename r) => Name -> m Typename mkTypename :: (MonadReader r m, Has MkTypename r) => Name -> m Name
mkTypename name = mkTypename name =
($ name) <$> asks getter ($ name) . runMkTypename <$> asks getter
-- | GraphQL types are divided into two classes: input types and output types. -- | GraphQL types are divided into two classes: input types and output types.
-- The GraphQL spec does not use the word “kind” to describe these classes, but -- The GraphQL spec does not use the word “kind” to describe these classes, but
@ -566,15 +560,6 @@ data SomeTypeInfo = forall k. SomeTypeInfo (TypeInfo k)
instance Eq SomeTypeInfo where instance Eq SomeTypeInfo where
SomeTypeInfo a == SomeTypeInfo b = eqTypeInfo a b SomeTypeInfo a == SomeTypeInfo b = eqTypeInfo a b
type family DefinitionName a where
DefinitionName (TypeInfo k) = Typename
DefinitionName SomeTypeInfo = Typename
DefinitionName InterfaceInfo = Typename
DefinitionName ObjectInfo = Typename
DefinitionName InputFieldInfo = Name
DefinitionName FieldInfo = Name
DefinitionName EnumValueInfo = Name
data Definition a = Definition data Definition a = Definition
{ dName :: Name, { dName :: Name,
-- | A unique identifier used to break cycles in mutually-recursive type -- | A unique identifier used to break cycles in mutually-recursive type
@ -594,8 +579,8 @@ instance Hashable a => Hashable (Definition a) where
hashWithSalt salt Definition {..} = hashWithSalt salt Definition {..} =
salt `hashWithSalt` dName `hashWithSalt` dInfo salt `hashWithSalt` dName `hashWithSalt` dInfo
mkDefinition :: HasName (DefinitionName a) => DefinitionName a -> Maybe Description -> a -> Definition a mkDefinition :: Name -> Maybe Description -> a -> Definition a
mkDefinition name description info = Definition (getName name) Nothing description info mkDefinition name description info = Definition name Nothing description info
instance Eq a => Eq (Definition a) where instance Eq a => Eq (Definition a) where
(==) = eq1 (==) = eq1

View File

@ -81,8 +81,8 @@ validateSchemaCustomizationsConsistent remoteSchemaCustomizer (RemoteSchemaIntro
G.TypeDefinitionInterface G.InterfaceTypeDefinition {..} -> G.TypeDefinitionInterface G.InterfaceTypeDefinition {..} ->
for_ _itdPossibleTypes $ \typeName -> for_ _itdPossibleTypes $ \typeName ->
for_ _itdFieldsDefinition $ \G.FieldDefinition {..} -> do for_ _itdFieldsDefinition $ \G.FieldDefinition {..} -> do
let interfaceCustomizedFieldName = customizeFieldName _itdName _fldName let interfaceCustomizedFieldName = runCustomizeRemoteFieldName customizeFieldName _itdName _fldName
typeCustomizedFieldName = customizeFieldName typeName _fldName typeCustomizedFieldName = runCustomizeRemoteFieldName customizeFieldName typeName _fldName
when (interfaceCustomizedFieldName /= typeCustomizedFieldName) $ when (interfaceCustomizedFieldName /= typeCustomizedFieldName) $
throwRemoteSchema $ throwRemoteSchema $
"Remote schema customization inconsistency: field name mapping for field " "Remote schema customization inconsistency: field name mapping for field "
@ -109,11 +109,11 @@ validateSchemaCustomizationsDistinct remoteSchemaCustomizer (RemoteSchemaIntrosp
traverse_ validateFieldMappingsAreDistinct typeDefinitions traverse_ validateFieldMappingsAreDistinct typeDefinitions
where where
customizeTypeName = remoteSchemaCustomizeTypeName remoteSchemaCustomizer customizeTypeName = remoteSchemaCustomizeTypeName remoteSchemaCustomizer
customizeFieldName = remoteSchemaCustomizeFieldName remoteSchemaCustomizer customizeFieldName = runCustomizeRemoteFieldName (remoteSchemaCustomizeFieldName remoteSchemaCustomizer)
validateTypeMappingsAreDistinct :: m () validateTypeMappingsAreDistinct :: m ()
validateTypeMappingsAreDistinct = do validateTypeMappingsAreDistinct = do
let dups = duplicates $ (customizeTypeName . typeDefinitionName) <$> typeDefinitions let dups = duplicates $ (runMkTypename customizeTypeName . typeDefinitionName) <$> typeDefinitions
unless (Set.null dups) $ unless (Set.null dups) $
throwRemoteSchema $ throwRemoteSchema $
"Type name mappings are not distinct; the following types appear more than once: " "Type name mappings are not distinct; the following types appear more than once: "

View File

@ -246,15 +246,15 @@ buildRoleContext
(,,) (,,)
<$> customizeFields <$> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_query"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_query")))
(buildQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig)
<*> customizeFields <*> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_mutation_frontend"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_mutation_frontend")))
(buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig)
<*> customizeFields <*> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_mutation_backend"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_mutation_backend")))
(buildMutationFields Backend sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildMutationFields Backend sourceName sourceConfig validTables validFunctions queryTagsConfig)
buildRelayRoleContext :: buildRelayRoleContext ::
@ -331,15 +331,15 @@ buildRelayRoleContext
(,,) (,,)
<$> customizeFields <$> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_query"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_query")))
(buildRelayQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildRelayQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig)
<*> customizeFields <*> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_mutation_frontend"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_mutation_frontend")))
(buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig)
<*> customizeFields <*> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_mutation_backend"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_mutation_backend")))
(buildMutationFields Backend sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildMutationFields Backend sourceName sourceConfig validTables validFunctions queryTagsConfig)
buildFullestDBSchema :: buildFullestDBSchema ::
@ -387,11 +387,11 @@ buildFullestDBSchema queryContext sources allActionInfos nonObjectCustomTypes =
(,) (,)
<$> customizeFields <$> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_query"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_query")))
(buildQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildQueryFields sourceName sourceConfig validTables validFunctions queryTagsConfig)
<*> customizeFields <*> customizeFields
sourceCustomization sourceCustomization
(mkTypename . (<> $$(G.litName "_mutation_frontend"))) (mkTypename <> P.MkTypename (<> $$(G.litName "_mutation_frontend")))
(buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig) (buildMutationFields Frontend sourceName sourceConfig validTables validFunctions queryTagsConfig)
-- The `unauthenticatedContext` is used when the user queries the graphql-engine -- The `unauthenticatedContext` is used when the user queries the graphql-engine
@ -785,14 +785,14 @@ mkRootField sourceName sourceConfig queryTagsConfig inj =
takeExposedAs :: FunctionExposedAs -> FunctionCache b -> FunctionCache b takeExposedAs :: FunctionExposedAs -> FunctionCache b -> FunctionCache b
takeExposedAs x = Map.filter ((== x) . _fiExposedAs) takeExposedAs x = Map.filter ((== x) . _fiExposedAs)
subscriptionRoot :: P.Typename subscriptionRoot :: G.Name
subscriptionRoot = P.Typename $$(G.litName "subscription_root") subscriptionRoot = $$(G.litName "subscription_root")
mutationRoot :: P.Typename mutationRoot :: G.Name
mutationRoot = P.Typename $$(G.litName "mutation_root") mutationRoot = $$(G.litName "mutation_root")
queryRoot :: P.Typename queryRoot :: G.Name
queryRoot = P.Typename $$(G.litName "query_root") queryRoot = $$(G.litName "query_root")
finalizeParser :: Parser 'Output (P.ParseT Identity) a -> ParserFn a finalizeParser :: Parser 'Output (P.ParseT Identity) a -> ParserFn a
finalizeParser parser = runIdentity . P.runParseT . P.runParser parser finalizeParser parser = runIdentity . P.runParseT . P.runParser parser
@ -821,7 +821,7 @@ runMonadSchema ::
ConcreteSchemaT m a -> ConcreteSchemaT m a ->
m a m a
runMonadSchema roleName queryContext pgSources m = runMonadSchema roleName queryContext pgSources m =
P.runSchemaT m `runReaderT` (roleName, pgSources, queryContext, P.Typename, id, const id) P.runSchemaT m `runReaderT` (roleName, pgSources, queryContext, mempty, mempty, mempty)
-- | Whether the request is sent with `x-hasura-use-backend-only-permissions` set to `true`. -- | Whether the request is sent with `x-hasura-use-backend-only-permissions` set to `true`.
data Scenario = Backend | Frontend deriving (Enum, Show, Eq) data Scenario = Backend | Frontend deriving (Enum, Show, Eq)

View File

@ -318,7 +318,7 @@ actionInputArguments nonObjectTypeMap arguments = do
`onNothing` throw500 "object type for a field found in custom input object type" `onNothing` throw500 "object type for a field found in custom input object type"
(fieldName,) <$> argumentParser fieldName fieldDesc fieldType nonObjectFieldType (fieldName,) <$> argumentParser fieldName fieldDesc fieldType nonObjectFieldType
pure $ pure $
P.object (P.Typename objectName) objectDesc $ P.object objectName objectDesc $
J.Object <$> inputFieldsToObject inputFieldsParsers J.Object <$> inputFieldsToObject inputFieldsParsers
mkArgumentInputFieldParser :: mkArgumentInputFieldParser ::
@ -358,9 +358,9 @@ customScalarParser = \case
| _stdName == floatScalar -> J.toJSON <$> P.float | _stdName == floatScalar -> J.toJSON <$> P.float
| _stdName == stringScalar -> J.toJSON <$> P.string | _stdName == stringScalar -> J.toJSON <$> P.string
| _stdName == boolScalar -> J.toJSON <$> P.boolean | _stdName == boolScalar -> J.toJSON <$> P.boolean
| otherwise -> P.jsonScalar (P.Typename _stdName) _stdDescription | otherwise -> P.jsonScalar _stdName _stdDescription
ASTReusedScalar name pgScalarType -> ASTReusedScalar name pgScalarType ->
let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition (P.Typename name) Nothing P.TIScalar let schemaType = P.NonNullable $ P.TNamed $ P.mkDefinition name Nothing P.TIScalar
in P.Parser in P.Parser
{ pType = schemaType, { pType = schemaType,
pParser = pParser =
@ -385,4 +385,4 @@ customEnumParser (EnumTypeDefinition typeName description enumValues) =
valueName valueName
(_evdDescription enumValue) (_evdDescription enumValue)
P.EnumValueInfo P.EnumValueInfo
in P.enum (P.Typename enumName) description enumValueDefinitions in P.enum enumName description enumValueDefinitions

View File

@ -312,7 +312,7 @@ typeField =
_ -> J.Null _ -> J.Null
in applyPrinter in applyPrinter
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__Type")) $$(G.litName "__Type")
Nothing Nothing
[ kind, [ kind,
name, name,
@ -360,7 +360,7 @@ inputValue =
_ -> J.Null _ -> J.Null
in applyPrinter in applyPrinter
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__InputValue")) $$(G.litName "__InputValue")
Nothing Nothing
[ name, [ name,
description, description,
@ -400,7 +400,7 @@ enumValue =
$> const J.Null $> const J.Null
in applyPrinter in applyPrinter
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__EnumValue")) $$(G.litName "__EnumValue")
Nothing Nothing
[ name, [ name,
description, description,
@ -426,7 +426,7 @@ typeKind ::
Parser 'Both n () Parser 'Both n ()
typeKind = typeKind =
P.enum P.enum
(P.Typename $$(G.litName "__TypeKind")) $$(G.litName "__TypeKind")
Nothing Nothing
( NE.fromList ( NE.fromList
[ mkDefinition $$(G.litName "ENUM"), [ mkDefinition $$(G.litName "ENUM"),
@ -486,7 +486,7 @@ fieldField =
$> const J.Null $> const J.Null
in applyPrinter in applyPrinter
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__Field")) $$(G.litName "__Field")
Nothing Nothing
[ name, [ name,
description, description,
@ -533,7 +533,7 @@ directiveSet =
$> const J.Null $> const J.Null
in applyPrinter in applyPrinter
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__Directive")) $$(G.litName "__Directive")
Nothing Nothing
[ name, [ name,
description, description,
@ -605,7 +605,7 @@ schemaSet fakeSchema =
return $ J.array $ map printer $ sDirectives fakeSchema return $ J.array $ map printer $ sDirectives fakeSchema
in selectionSetToJSON . fmap (P.handleTypename nameAsJSON) in selectionSetToJSON . fmap (P.handleTypename nameAsJSON)
<$> P.selectionSet <$> P.selectionSet
(P.Typename $$(G.litName "__Schema")) $$(G.litName "__Schema")
Nothing Nothing
[ description, [ description,
types, types,

View File

@ -15,7 +15,6 @@ import Hasura.GraphQL.Parser
) )
import Hasura.GraphQL.Parser qualified as P import Hasura.GraphQL.Parser qualified as P
import Hasura.GraphQL.Parser.Class import Hasura.GraphQL.Parser.Class
import Hasura.GraphQL.Parser.Schema (Typename (..))
import Hasura.GraphQL.Schema.Backend import Hasura.GraphQL.Schema.Backend
import Hasura.GraphQL.Schema.Common import Hasura.GraphQL.Schema.Common
import Hasura.GraphQL.Schema.Table import Hasura.GraphQL.Schema.Table
@ -176,7 +175,7 @@ orderByAggregation sourceName tableInfo selectPermissions = memoizeOn 'orderByAg
InputFieldsParser n (Maybe [IR.OrderByItemG b (IR.AnnotatedAggregateOrderBy b)]) InputFieldsParser n (Maybe [IR.OrderByItemG b (IR.AnnotatedAggregateOrderBy b)])
parseOperator mkTypename operator tableGQLName columns = parseOperator mkTypename operator tableGQLName columns =
let opText = G.unName operator let opText = G.unName operator
objectName = mkTypename $ tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_order_by") objectName = P.runMkTypename mkTypename $ tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_order_by")
objectDesc = Just $ G.Description $ "order by " <> opText <> "() on columns of table " <>> tableName objectDesc = Just $ G.Description $ "order by " <> opText <> "() on columns of table " <>> tableName
in P.fieldOptional operator Nothing (P.object objectName objectDesc columns) in P.fieldOptional operator Nothing (P.object objectName objectDesc columns)
`mapField` map (\(col, info) -> mkOrderByItemG (IR.AAOOp opText col) info) `mapField` map (\(col, info) -> mkOrderByItemG (IR.AAOOp opText col) info)
@ -186,7 +185,7 @@ orderByOperator ::
(BackendSchema b, MonadParse n) => (BackendSchema b, MonadParse n) =>
Parser 'Both n (Maybe (BasicOrderType b, NullsOrderType b)) Parser 'Both n (Maybe (BasicOrderType b, NullsOrderType b))
orderByOperator = orderByOperator =
P.nullable $ P.enum (Typename $$(G.litName "order_by")) (Just "column ordering options") $ orderByOperators @b P.nullable $ P.enum $$(G.litName "order_by") (Just "column ordering options") $ orderByOperators @b
mkOrderByItemG :: forall b a. a -> (BasicOrderType b, NullsOrderType b) -> IR.OrderByItemG b a mkOrderByItemG :: forall b a. a -> (BasicOrderType b, NullsOrderType b) -> IR.OrderByItemG b a
mkOrderByItemG column (orderType, nullsOrder) = mkOrderByItemG column (orderType, nullsOrder) =

View File

@ -349,7 +349,7 @@ remoteFieldScalarParser customizeTypename (G.ScalarTypeDefinition description na
_ -> pure (Altered False, QueryVariable <$> v) _ -> pure (Altered False, QueryVariable <$> v)
} }
where where
customizedTypename = customizeTypename name customizedTypename = runMkTypename customizeTypename name
schemaType = NonNullable $ TNamed $ mkDefinition customizedTypename description TIScalar schemaType = NonNullable $ TNamed $ mkDefinition customizedTypename description TIScalar
gType = toGraphQLType schemaType gType = toGraphQLType schemaType
@ -368,7 +368,7 @@ remoteFieldEnumParser customizeTypename (G.EnumTypeDefinition desc name _directi
( mkDefinition (G.unEnumValue enumName) enumDesc P.EnumValueInfo, ( mkDefinition (G.unEnumValue enumName) enumDesc P.EnumValueInfo,
G.VEnum enumName G.VEnum enumName
) )
in fmap (Altered False,) $ P.enum (customizeTypename name) desc $ NE.fromList enumValDefns in fmap (Altered False,) $ P.enum (runMkTypename customizeTypename name) desc $ NE.fromList enumValDefns
-- | remoteInputObjectParser returns an input parser for a given 'G.InputObjectTypeDefinition' -- | remoteInputObjectParser returns an input parser for a given 'G.InputObjectTypeDefinition'
-- --
@ -897,7 +897,7 @@ remoteField sdoc parentTypeName fieldName description argsDefn typeDefn = do
argsParser <- argumentsParser argsDefn sdoc argsParser <- argumentsParser argsDefn sdoc
customizeTypename <- asks getter customizeTypename <- asks getter
customizeFieldName <- asks getter customizeFieldName <- asks getter
let customizedFieldName = customizeFieldName parentTypeName fieldName let customizedFieldName = runCustomizeRemoteFieldName customizeFieldName parentTypeName fieldName
case typeDefn of case typeDefn of
G.TypeDefinitionObject objTypeDefn -> do G.TypeDefinitionObject objTypeDefn -> do
remoteSchemaObjFields <- remoteSchemaObject sdoc objTypeDefn remoteSchemaObjFields <- remoteSchemaObject sdoc objTypeDefn
@ -975,15 +975,12 @@ customizeRemoteNamespace remoteSchemaInfo@RemoteSchemaInfo {..} rootTypeName fie
-- In P.selectionSet we lose the resultCustomizer from __typename fields so we need to put it back -- In P.selectionSet we lose the resultCustomizer from __typename fields so we need to put it back
let resultCustomizer = modifyFieldByName alias $ customizeTypeNameString $ _rscCustomizeTypeName rsCustomizer let resultCustomizer = modifyFieldByName alias $ customizeTypeNameString $ _rscCustomizeTypeName rsCustomizer
in RemoteFieldG remoteSchemaInfo resultCustomizer $ G.Field (Just alias) $$(G.litName "__typename") mempty mempty mempty in RemoteFieldG remoteSchemaInfo resultCustomizer $ G.Field (Just alias) $$(G.litName "__typename") mempty mempty mempty
mkNamespaceTypename = Typename . const (remoteSchemaCustomizeTypeName rsCustomizer rootTypeName) mkNamespaceTypename = MkTypename $ const $ runMkTypename (remoteSchemaCustomizeTypeName rsCustomizer) rootTypeName
type MonadBuildRemoteSchema r m n = (MonadSchema n m, MonadError QErr m, MonadReader r m, Has MkTypename r, Has CustomizeRemoteFieldName r) type MonadBuildRemoteSchema r m n = (MonadSchema n m, MonadError QErr m, MonadReader r m, Has MkTypename r, Has CustomizeRemoteFieldName r)
runMonadBuildRemoteSchema :: Monad m => SchemaT n (ReaderT (MkTypename, CustomizeRemoteFieldName) m) a -> m a runMonadBuildRemoteSchema :: Monad m => SchemaT n (ReaderT (MkTypename, CustomizeRemoteFieldName) m) a -> m a
runMonadBuildRemoteSchema m = flip runReaderT (Typename, idFieldCustomizer) $ runSchemaT m runMonadBuildRemoteSchema m = flip runReaderT (mempty, mempty) $ runSchemaT m
where
idFieldCustomizer :: CustomizeRemoteFieldName
idFieldCustomizer = const id
withRemoteSchemaCustomization :: withRemoteSchemaCustomization ::
forall m r a. forall m r a.
@ -992,5 +989,5 @@ withRemoteSchemaCustomization ::
m a -> m a ->
m a m a
withRemoteSchemaCustomization remoteSchemaCustomizer = withRemoteSchemaCustomization remoteSchemaCustomizer =
withTypenameCustomization (Typename . remoteSchemaCustomizeTypeName remoteSchemaCustomizer) withTypenameCustomization (remoteSchemaCustomizeTypeName remoteSchemaCustomizer)
. withRemoteFieldNameCustomization (remoteSchemaCustomizeFieldName remoteSchemaCustomizer) . withRemoteFieldNameCustomization (remoteSchemaCustomizeFieldName remoteSchemaCustomizer)

View File

@ -504,7 +504,7 @@ tableConnectionSelectionSet sourceName tableInfo selectPermissions = memoizeOn '
hasPreviousPageField hasPreviousPageField
] ]
in P.nonNullableParser $ in P.nonNullableParser $
P.selectionSet (P.Typename $$(G.litName "PageInfo")) Nothing allFields P.selectionSet $$(G.litName "PageInfo") Nothing allFields
<&> parsedSelectionsToFields IR.PageInfoTypename <&> parsedSelectionsToFields IR.PageInfoTypename
tableEdgesSelectionSet :: tableEdgesSelectionSet ::
@ -1037,7 +1037,7 @@ tableAggregationFields sourceName tableInfo selectPermissions = memoizeOn 'table
FieldParser n (IR.AggregateField b) FieldParser n (IR.AggregateField b)
parseAggOperator mkTypename operator tableGQLName columns = parseAggOperator mkTypename operator tableGQLName columns =
let opText = G.unName operator let opText = G.unName operator
setName = mkTypename $ tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_fields") setName = P.runMkTypename mkTypename $ tableGQLName <> $$(G.litName "_") <> operator <> $$(G.litName "_fields")
setDesc = Just $ G.Description $ "aggregate " <> opText <> " on columns" setDesc = Just $ G.Description $ "aggregate " <> opText <> " on columns"
subselectionParser = subselectionParser =
P.selectionSet setName setDesc columns P.selectionSet setName setDesc columns
@ -1674,7 +1674,7 @@ nodePG = memoizeOn 'nodePG () do
pure $ (source,sourceConfig,selectPermissions,tablePkeyColumns,) <$> annotatedFieldsParser pure $ (source,sourceConfig,selectPermissions,tablePkeyColumns,) <$> annotatedFieldsParser
pure $ pure $
P.selectionSetInterface P.selectionSetInterface
(P.Typename $$(G.litName "Node")) $$(G.litName "Node")
(Just nodeInterfaceDescription) (Just nodeInterfaceDescription)
[idField] [idField]
tables tables

View File

@ -55,12 +55,13 @@ import Data.Text.Extended
import Data.Text.NonEmpty import Data.Text.NonEmpty
import Database.PG.Query qualified as Q import Database.PG.Query qualified as Q
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.GraphQL.Parser.Schema (Variable) import Hasura.GraphQL.Parser.Schema
import Hasura.Incremental (Cacheable) import Hasura.Incremental (Cacheable)
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.DDL.Headers (HeaderConf (..)) import Hasura.RQL.DDL.Headers (HeaderConf (..))
import Hasura.RQL.Types.Common import Hasura.RQL.Types.Common
import Hasura.RQL.Types.ResultCustomization import Hasura.RQL.Types.ResultCustomization
import Hasura.RQL.Types.SourceCustomization
import Hasura.Session import Hasura.Session
import Language.GraphQL.Draft.Printer qualified as G import Language.GraphQL.Draft.Printer qualified as G
import Language.GraphQL.Draft.Syntax qualified as G import Language.GraphQL.Draft.Syntax qualified as G
@ -191,12 +192,12 @@ instance Hashable RemoteSchemaCustomizer
$(J.deriveJSON hasuraJSON ''RemoteSchemaCustomizer) $(J.deriveJSON hasuraJSON ''RemoteSchemaCustomizer)
remoteSchemaCustomizeTypeName :: RemoteSchemaCustomizer -> G.Name -> G.Name remoteSchemaCustomizeTypeName :: RemoteSchemaCustomizer -> MkTypename
remoteSchemaCustomizeTypeName RemoteSchemaCustomizer {..} typeName = remoteSchemaCustomizeTypeName RemoteSchemaCustomizer {..} = MkTypename $ \typeName ->
Map.lookupDefault typeName typeName _rscCustomizeTypeName Map.lookupDefault typeName typeName _rscCustomizeTypeName
remoteSchemaCustomizeFieldName :: RemoteSchemaCustomizer -> G.Name -> G.Name -> G.Name remoteSchemaCustomizeFieldName :: RemoteSchemaCustomizer -> CustomizeRemoteFieldName
remoteSchemaCustomizeFieldName RemoteSchemaCustomizer {..} typeName fieldName = remoteSchemaCustomizeFieldName RemoteSchemaCustomizer {..} = CustomizeRemoteFieldName $ \typeName fieldName ->
Map.lookup typeName _rscCustomizeFieldName >>= Map.lookup fieldName & fromMaybe fieldName Map.lookup typeName _rscCustomizeFieldName >>= Map.lookup fieldName & fromMaybe fieldName
hasTypeOrFieldCustomizations :: RemoteSchemaCustomizer -> Bool hasTypeOrFieldCustomizations :: RemoteSchemaCustomizer -> Bool

View File

@ -18,59 +18,53 @@ import Language.GraphQL.Draft.Syntax qualified as G
-- | Mapping that can be provided to a ResultCustomizer -- | Mapping that can be provided to a ResultCustomizer
-- to map top-level field aliases that were not available at field parse time. -- to map top-level field aliases that were not available at field parse time.
-- E.g. for aliases created in the remote server query for remote joins. -- E.g. for aliases created in the remote server query for remote joins.
newtype AliasMapping = AliasMapping {unAliasMapping :: Endo G.Name} newtype AliasMapping = AliasMapping {unAliasMapping :: G.Name -> G.Name}
deriving (Semigroup, Monoid) deriving (Semigroup, Monoid) via (Endo G.Name)
-- | AliasMapping that maps a single field name to an alias -- | AliasMapping that maps a single field name to an alias
singletonAliasMapping :: G.Name -> G.Name -> AliasMapping singletonAliasMapping :: G.Name -> G.Name -> AliasMapping
singletonAliasMapping fieldName alias = AliasMapping $ singletonAliasMapping fieldName alias = AliasMapping $ \fieldName' ->
Endo $ \fieldName' -> if fieldName == fieldName'
if fieldName == fieldName' then alias else fieldName' then alias
else fieldName'
-- | Function to modify JSON values returned from the remote server -- | Function to modify JSON values returned from the remote server
-- e.g. to map values of __typename fields to customized type names. -- e.g. to map values of __typename fields to customized type names.
-- The customizer uses Maybe to allow short-circuiting subtrees -- The customizer uses Maybe to allow short-circuiting subtrees
-- where no customizations are needed. -- where no customizations are needed.
newtype ResultCustomizer = ResultCustomizer {unResultCustomizer :: Maybe (AliasMapping -> Endo JO.Value)} newtype ResultCustomizer = ResultCustomizer {unResultCustomizer :: AliasMapping -> JO.Value -> JO.Value}
deriving (Semigroup, Monoid) deriving (Semigroup, Monoid) via (AliasMapping -> Endo JO.Value)
-- | Apply a ResultCustomizer to a JSON value -- | Apply a ResultCustomizer to a JSON value
applyResultCustomizer :: ResultCustomizer -> JO.Value -> JO.Value applyResultCustomizer :: ResultCustomizer -> JO.Value -> JO.Value
applyResultCustomizer = maybe id (appEndo . ($ mempty)) . unResultCustomizer applyResultCustomizer = ($ mempty) . unResultCustomizer
-- | Apply an AliasMapping to a ResultCustomizer. -- | Apply an AliasMapping to a ResultCustomizer.
applyAliasMapping :: AliasMapping -> ResultCustomizer -> ResultCustomizer applyAliasMapping :: AliasMapping -> ResultCustomizer -> ResultCustomizer
applyAliasMapping aliasMapping (ResultCustomizer m) = applyAliasMapping aliasMapping (ResultCustomizer m) =
ResultCustomizer $ ResultCustomizer $ m . (<> aliasMapping)
m <&> \g aliasMapping' -> g $ aliasMapping' <> aliasMapping
-- | Take a ResultCustomizer for a JSON subtree, and a fieldName, -- | Take a ResultCustomizer for a JSON subtree, and a fieldName,
-- and produce a ResultCustomizer for a parent object or array of objects -- and produce a ResultCustomizer for a parent object or array of objects
-- that applies the subtree customizer to the subtree at the given fieldName. -- that applies the subtree customizer to the subtree at the given fieldName.
modifyFieldByName :: G.Name -> ResultCustomizer -> ResultCustomizer modifyFieldByName :: G.Name -> ResultCustomizer -> ResultCustomizer
modifyFieldByName fieldName (ResultCustomizer m) = modifyFieldByName fieldName ResultCustomizer {..} =
ResultCustomizer $ ResultCustomizer $ \AliasMapping {..} ->
m <&> \g aliasMapping -> let applyCustomizer = unResultCustomizer mempty
Endo $ modifyFieldByName' = \case
let Endo f = g mempty -- AliasMapping is only applied to the top level so use mempty for nested customizers JO.Object o -> JO.Object $ JO.adjust applyCustomizer (G.unName $ unAliasMapping fieldName) o
modifyFieldByName' = \case JO.Array a -> JO.Array $ modifyFieldByName' <$> a
JO.Object o -> JO.Object $ JO.adjust f (G.unName $ (appEndo $ unAliasMapping aliasMapping) fieldName) o v -> v
JO.Array a -> JO.Array $ modifyFieldByName' <$> a in modifyFieldByName'
v -> v
in modifyFieldByName'
-- | Create a RemoteResultCustomizer that applies the typeNameMap -- | Create a RemoteResultCustomizer that applies the typeNameMap
-- to a JSON string value, e.g. for use in customizing a __typename field value. -- to a JSON string value, e.g. for use in customizing a __typename field value.
customizeTypeNameString :: HashMap G.Name G.Name -> ResultCustomizer customizeTypeNameString :: HashMap G.Name G.Name -> ResultCustomizer
customizeTypeNameString typeNameMap | Map.null typeNameMap = mempty
customizeTypeNameString typeNameMap = customizeTypeNameString typeNameMap =
if Map.null typeNameMap ResultCustomizer $ \_aliasMapping -> \case
then mempty JO.String t -> JO.String $ G.unName $ customizeTypeName $ G.unsafeMkName t
else ResultCustomizer $ where
Just $ customizeTypeName :: G.Name -> G.Name
const $ customizeTypeName typeName = Map.lookupDefault typeName typeName typeNameMap
Endo $ \case v -> v
JO.String t -> JO.String $ G.unName $ customizeTypeName $ G.unsafeMkName t
v -> v
where
customizeTypeName :: G.Name -> G.Name
customizeTypeName typeName = Map.lookupDefault typeName typeName typeNameMap

View File

@ -1,7 +1,8 @@
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
module Hasura.RQL.Types.SourceCustomization module Hasura.RQL.Types.SourceCustomization
( SourceTypeCustomization, ( MkTypename (..),
SourceTypeCustomization,
RootFieldsCustomization (..), RootFieldsCustomization (..),
mkCustomizedTypename, mkCustomizedTypename,
emptySourceCustomization, emptySourceCustomization,
@ -11,7 +12,7 @@ module Hasura.RQL.Types.SourceCustomization
SourceCustomization (..), SourceCustomization (..),
withSourceCustomization, withSourceCustomization,
MkRootFieldName, MkRootFieldName,
CustomizeRemoteFieldName, CustomizeRemoteFieldName (..),
withRemoteFieldNameCustomization, withRemoteFieldNameCustomization,
) )
where where
@ -19,6 +20,7 @@ where
import Control.Lens import Control.Lens
import Data.Aeson.Extended import Data.Aeson.Extended
import Data.Has import Data.Has
import Data.Monoid
import Hasura.GraphQL.Parser.Schema import Hasura.GraphQL.Parser.Schema
import Hasura.Incremental.Internal.Dependency (Cacheable) import Hasura.Incremental.Internal.Dependency (Cacheable)
import Hasura.Prelude import Hasura.Prelude
@ -60,15 +62,15 @@ instance FromJSON SourceTypeCustomization where
emptySourceTypeCustomization :: SourceTypeCustomization emptySourceTypeCustomization :: SourceTypeCustomization
emptySourceTypeCustomization = SourceTypeCustomization Nothing Nothing emptySourceTypeCustomization = SourceTypeCustomization Nothing Nothing
mkCustomizedTypename :: Maybe SourceTypeCustomization -> G.Name -> Typename mkCustomizedTypename :: Maybe SourceTypeCustomization -> MkTypename
mkCustomizedTypename Nothing = Typename mkCustomizedTypename Nothing = mempty
mkCustomizedTypename (Just SourceTypeCustomization {..}) = mkCustomizedTypename (Just SourceTypeCustomization {..}) =
Typename . applyPrefixSuffix _stcPrefix _stcSuffix MkTypename (applyPrefixSuffix _stcPrefix _stcSuffix)
mkCustomizedFieldName :: Maybe RootFieldsCustomization -> G.Name -> G.Name mkCustomizedFieldName :: Maybe RootFieldsCustomization -> MkRootFieldName
mkCustomizedFieldName Nothing = id mkCustomizedFieldName Nothing = mempty
mkCustomizedFieldName (Just RootFieldsCustomization {..}) = mkCustomizedFieldName (Just RootFieldsCustomization {..}) =
applyPrefixSuffix _rootfcPrefix _rootfcSuffix MkRootFieldName (applyPrefixSuffix _rootfcPrefix _rootfcSuffix)
applyPrefixSuffix :: Maybe G.Name -> Maybe G.Name -> G.Name -> G.Name applyPrefixSuffix :: Maybe G.Name -> Maybe G.Name -> G.Name -> G.Name
applyPrefixSuffix Nothing Nothing name = name applyPrefixSuffix Nothing Nothing name = name
@ -100,7 +102,8 @@ getSourceTypeCustomization :: SourceCustomization -> SourceTypeCustomization
getSourceTypeCustomization = fromMaybe emptySourceTypeCustomization . _scTypeNames getSourceTypeCustomization = fromMaybe emptySourceTypeCustomization . _scTypeNames
-- | Function to apply root field name customizations. -- | Function to apply root field name customizations.
type MkRootFieldName = G.Name -> G.Name newtype MkRootFieldName = MkRootFieldName {runMkRootFieldName :: G.Name -> G.Name}
deriving (Semigroup, Monoid) via (Endo G.Name)
-- | Inject a new root field name customization function into the environment. -- | Inject a new root field name customization function into the environment.
-- This can be used by schema-building code (with @MonadBuildSchema@ constraint) to ensure -- This can be used by schema-building code (with @MonadBuildSchema@ constraint) to ensure
@ -111,7 +114,7 @@ withRootFieldNameCustomization = local . set hasLens
-- | Apply the root field name customization function from the current environment. -- | Apply the root field name customization function from the current environment.
mkRootFieldName :: (MonadReader r m, Has MkRootFieldName r) => G.Name -> m G.Name mkRootFieldName :: (MonadReader r m, Has MkRootFieldName r) => G.Name -> m G.Name
mkRootFieldName name = mkRootFieldName name =
($ name) <$> asks getter ($ name) . runMkRootFieldName <$> asks getter
-- | Inject typename and root field name customizations from @SourceCustomization@ into -- | Inject typename and root field name customizations from @SourceCustomization@ into
-- the environment. -- the environment.
@ -125,7 +128,10 @@ withSourceCustomization SourceCustomization {..} =
withTypenameCustomization (mkCustomizedTypename _scTypeNames) withTypenameCustomization (mkCustomizedTypename _scTypeNames)
. withRootFieldNameCustomization (mkCustomizedFieldName _scRootFields) . withRootFieldNameCustomization (mkCustomizedFieldName _scRootFields)
type CustomizeRemoteFieldName = G.Name -> G.Name -> G.Name newtype CustomizeRemoteFieldName = CustomizeRemoteFieldName
{ runCustomizeRemoteFieldName :: G.Name -> G.Name -> G.Name
}
deriving (Semigroup, Monoid) via (G.Name -> Endo G.Name)
withRemoteFieldNameCustomization :: forall m r a. (MonadReader r m, Has CustomizeRemoteFieldName r) => CustomizeRemoteFieldName -> m a -> m a withRemoteFieldNameCustomization :: forall m r a. (MonadReader r m, Has CustomizeRemoteFieldName r) => CustomizeRemoteFieldName -> m a -> m a
withRemoteFieldNameCustomization = local . set hasLens withRemoteFieldNameCustomization = local . set hasLens