# Source Customization See issue [#6974](https://github.com/hasura/graphql-engine/issues/6974). When adding multiple remote database sources there is a possibility of getting name conflicts if two sources have tables or functions with the same name. This can be worked around for individual tables by adding a `custom_name` and/or `custom_root_fields`, but it would be better to have source-level customization options to avoid conflicts. ## Spec Add an optional `customization` object to the `*_add_source` API: ```yaml customization: root_fields: namespace: "something" prefix: some_prefix suffix: some_suffix type_names: prefix: some_prefix suffix: some_suffix ``` All fields within the `customization` object are optional. - `root_fields` provides options for avoiding conflicts in root field names between sources. - If `namespace` is given then all of the source's root fields will be nested under a new namespace root field. - If `prefix` and/or `suffix` are given then they will be applied to the root field names generated for the source. - `type_names` provides options for avoiding conflicts in type names generated for a source. - If `prefix` and/or `suffix` are given then they will be applied to the type names generated for the source. - Prefix and suffix only affect types generated specifically for a source. They do not affect builtin scalar types (`Int`, `Float`, etc) or types starting with `__`. ## Implementation approach ### API and metadata changes New data type `SourceCustomization` to be added to `AddSource` (for API) and `SourceInfo` (for metadata) to represent the customization options described above. ### Type name customization Type name customizations are applied by passing a customization function into the parser builders and applying it wherever we generate a type name. We add new type definitions: ```haskell newtype Typename = Typename {unTypename :: Name} deriving (Eq, Ord, Show, HasName, J.ToJSON) type MkTypename = Name -> Typename withTypenameCustomization :: forall m r a. (MonadReader r m, Has MkTypename r) => MkTypename -> m a -> m a ``` and add `Has MkTypename r` constraint to `MonadBuildSchema` to allow us to pass the typename customization function through the schema building code and insert customizations for sources where appropriate. ### Remote source relationships To allow type name customization in remote source relationships, we add the `SourceTypeCustomization` for the remote source to `RemoteSourceRelationshipInfo`. This allows us to call `withTypenameCustomization` when building the remote source schema. ### Relay Node type The Relay `Node` interface type is generated separately from the other query parsers. We need to make sure type name customizations are applied here too. ### Root field name customization Similar to type name customizations, we add new type definitions: ```haskell type MkRootFieldName = Name -> Name withRootFieldNameCustomization :: forall m r a. (MonadReader r m, Has MkRootFieldName r) => MkRootFieldName -> m a -> m a ``` and add `Has MkRootFieldName r` constraint to `MonadBuildSchema` to allow us to apss the root field name customization function through to places where root field names are constructed. ### Namespaces The collection of root fields is represented in many places using the type `InsOrdHashMap Name a` where `Name` is the GraphQL name of a root field and `a` is whatever data we need associated with that field, e.g. the `QueryRootField` or `ExecutionStep`. To represent that root fields can now optionally have a namespace, we introduce the new data type ```haskell data RootFieldAlias = RootFieldAlias { _rfaNamespace :: !(Maybe G.Name), _rfaAlias :: !G.Name } deriving (Show, Eq, Generic) ``` and the type alias ```haskell type RootFieldMap = InsOrdHashMap RootFieldAlias ``` `RootFieldMap a` will now be used instead of `InsOrdHashMap Name a` where appropriate. ### Subscriptions JSON result objects for subscriptions are generated by the individual database backends (currently Postgres and MSSQL support subcriptions). If the subscription source has a namespace then we need to wrap the response from the database in an object with the namespace field before sending it to the websocket. ### Tech debt We previously implemented namespaces for remote schemas before the more general `RootFieldAlias` field was added. Now that we have `RootFieldAlias` we should use this for remote schema namespaces too.