Переопределение значения корневого объекта типа GraphQL Union/Interface
У меня есть сервис Apollo GraphQL, который делегирует внутренний сервис gRPC. Эта служба имеет конечную точку, которая возвращает сообщение, содержащее oneof
, который я сопоставляю с Union
в GraphQL.
Это просто, но при внедрении распознавателей в нем задействовано немало примеров. Предположим, у меня есть следующее определение сообщения protobuf:
message MyUnionMessage {
oneof value {
UnionType1 type1 = 1;
UnionType1 type2 = 3;
UnionType1 type3 = 4;
}
}
message UnionType1 {<type 1 props>}
message UnionType2 {<type 2 props>}
message UnionType3 {<type 3 props>}
Моя соответствующая схема GraphQL выглядит примерно так:
union MyUnionType = UnionType1 | UnionType2 | UnionType3
type UnionType1 {<type 1 props>}
type UnionType1 {<type 2 props>}
type UnionType1 {<type 3 props>}
В привязке javascript для gRPC, MyUnionMessage
Объект будет иметь два свойства: value
которая является строкой, указывающей, какой тип значения содержится, и свойством, названным для типа. Итак, если бы я имел MyUnionMessage
содержащий UnionType2
Например, объект будет выглядеть так:
{
value: 'type2',
type2: {...}
}
Это хорошо для реализации __resolveType
, поскольку я могу сделать простой switch
по значению в value
, но тогда я должен написать решатель для всех полей всех конкретных типов.
То, что я ищу, так это чтобы я мог что-то вроде этого:
resolvers = {
MyUnionType: {
__resolveType(obj) {
switch(obj.value) {
case 'type1': return 'UnionType1';
case 'type2': return 'UnionType2';
case 'type3': return 'UnionType3';
default: return null;
},
__resolveValue(obj) {
return obj[obj.value];
},
},
};
По сути, я хочу написать "преобразователь" на уровне универсального типа объединения (или интерфейса), который преобразует объект перед его передачей в конкретный преобразователь.
Это возможно?
1 ответ
Держу пари, что такой сценарий обычно решается путем преобразования данных до того, как они попадут в __resolveType
логика. Например, скажем, у вас был Query
поле, которое возвратило список MyUnionType
, Ваш распознаватель для этого поля может выглядеть примерно так:
function resolve (arr) {
return arr.map(obj => {
return {
...obj[obj.value]
type: obj.value // or whatever field name that won't cause a collision
}
})
}
Ты тогда switch
на type
Внутри __resolveType
и ты в порядке. Конечно, это означает, что если у вас есть несколько полей, которые возвращают MyUnionType
вы захотите извлечь эту логику в служебную функцию, которая может использоваться каждым распознавателем.
Я не думаю, что на самом деле нет способа сделать то, что вы пытаетесь сделать с помощью существующего API. Конечно, вы могли бы сделать что-то вроде этого:
const getUnionType(obj) {
switch(obj.value) {
case 'type1': return 'UnionType1';
case 'type2': return 'UnionType2';
case 'type3': return 'UnionType3';
default: {
throw new Error(`Unrecognized type ${obj.value}`)
}
}
}
const resolvers = {
MyUnionType: {
__resolveType(obj) {
const type = getUnionType(obj)
Object.assign(obj, obj[obj.value])
return type
},
},
};
Это работает, но имейте в виду, что это немного хрупко, так как предполагает resolveType
всегда получит то же корневое значение, что и resolve
функция, которая может гипотетически измениться в будущем.