Переопределение значения корневого объекта типа 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 функция, которая может гипотетически измениться в будущем.

Другие вопросы по тегам