mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 17:31:56 +03:00
aac64f2c81
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/1616 GitOrigin-RevId: f7eefd2367929209aa77895ea585e96a99a78d47
101 lines
4.4 KiB
Markdown
101 lines
4.4 KiB
Markdown
# 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. |