graphql-compose / graphql-compose/graphql-compose-mongoose
Allow differing discriminator key values and discriminator model names
- Dominant language
- TypeScript
- Stars
- 706
- Forks
- 98
- PR merge metrics
- No merged PRs in 30d
Description
### Description
At the moment, `graphql-compose-mongoose` generates unexpected schema when **the value used for a discriminator key** is _not the same_ as the **model name of the discriminator**.
If using the two-argument version of Mongoose's [Model#discriminator()](https://mongoosejs.com/docs/api/model.html#model_Model.discriminator) function, the GraphQL schema generated works as expected.
However, if using the three-argument version of that function and suppling a _different_ model name for the discriminator than the string used as the value of the discriminator key, we encounter some odd behavior.
### Example Case
Extending from the docs:
```js
const DKey = 'type';
const typeValuesToModels = {
person: 'PersonCharacter',
droid: 'DroidCharacter',
};
// DEFINE BASE SCHEMA
const CharacterSchema = new mongoose.Schema({
type: {
type: String,
require: true,
enum: (Object.keys(typeValuesToModels): Array),
description: 'Character type Droid or Person',
},
name: String,
height: Number,
mass: Number,
films: [String],
});
// DEFINE DISCRIMINATOR SCHEMAS
const DroidSchema = new mongoose.Schema({
makeDate: String,
primaryFunction: [String],
});
const PersonSchema = new mongoose.Schema({
gender: String,
hairColor: String,
starships: [String],
});
// set discriminator Key
CharacterSchema.set('discriminatorKey', DKey);
// create base Model
const CharacterModel = mongoose.model('Character', CharacterSchema);
// create mongoose discriminator models
const DroidModel = CharacterModel.discriminator(typeValuesToModels.droid, DroidSchema, 'droid');
const PersonModel = CharacterModel.discriminator(typeValuesToModels.person, PersonSchema, 'person');
```
^^ Key difference is in the last two lines of the above.
With this setup, I would expect that `graphql-compose-mongoose` would generate a schema that includes an type `EnumDKeyCharacterType` with the values `person` and `droid`, because these represent the string values saved in the document's `type` field that Mongoose uses to identify which discriminator model to use.
Instead, this setup will generate an `EnumDKeyCharacterType` with the values `DroidCharacter` and `PersonCharacter` (i.e. the model names for the discriminators), which prevents reading/writing the `type` field because the enum values used by Mongoose do not match the `EnumDKeyCharacterType` values used by GraphQL.
What this means in practice is that queries which include the discriminator key field throw errors.
### Errors
For example, this query:
```graphql
{
droidCharacterOne {
_id
name
type
__typename
}
}
```
Returns this response:
```json
{
"errors": [
{
"message": "Expected a value of type \"EnumDKeyCharacterType\" but received: \"droid\"",
"locations": [
{
"line": 5,
"column": 5
}
],
"path": [
"droidCharacterOne",
"type"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Expected a value of type \"EnumDKeyCharacterType\" but received: \"droid\"",
" at completeLeafValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:638:11)",
" at completeValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:579:12)",
" at completeValueCatchingError (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:495:19)",
" at resolveField (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:435:10)",
" at executeFields (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:275:18)",
" at collectAndExecuteSubfields (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:713:10)",
" at completeObjectValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:703:10)",
" at completeValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:591:12)",
" at /Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:492:16",
" at ",
" at process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
}
}
],
"data": {
"droidCharacterOne": {
"_id": "5ad7d3c623a9b5d7aec16c5c",
"name": "R2D2",
"type": null,
"__typename": "DroidCharacter"
}
}
}
```
Also, queries on the base model generate errors as well:
```graphql
{
characterOne {
_id
name
type
__typename
}
}
```
Returns:
```json
{
"errors": [
{
"message": "Cannot find ObjectTypeComposer with name droid",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"characterOne"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"Error: Cannot find ObjectTypeComposer with name droid",
" at SchemaComposer.getOTC (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql-compose/lib/SchemaComposer.js:463:11)",
" at resolveType (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql-compose-mongoose/node8/discriminators/DiscriminatorTypeComposer.js:111:40)",
" at completeAbstractValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:652:21)",
" at completeValue (/Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:585:12)",
" at /Users/tannerwelsh/Code/Swayable/swaypi/node_modules/graphql/execution/execute.js:492:16",
" at ",
" at process._tickDomainCallback (internal/process/next_tick.js:228:7)"
]
}
}
}
],
"data": {
"characterOne": null
}
}
```
If we modify the above to _remove_ the `type` field from the response, we don't get any errors but the `__typename` provided just uses the base model type, not the specific discriminator type for this object.
```graphql
{
characterOne {
_id
name
__typename
}
}
```
```json
{
"data": {
"droidCharacterOne": {
"_id": "5ad7d3c623a9b5d7aec16c5c",
"name": "R2D2",
"__typename": "Character"
}
}
}
```
---
### Proposed Change
It would be great if this plugin could handle discriminators that have a non-equal model name and value used by the discriminator key, mirroring the functionality of Mongoose.
To achieve this, I suspect changes would need to be made to these functions:
- https://github.com/graphql-compose/graphql-compose-mongoose/blob/master/src/discriminators/DiscriminatorTypeComposer.js#L48
- https://github.com/graphql-compose/graphql-compose-mongoose/blob/master/src/discriminators/DiscriminatorTypeComposer.js#L152
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.