Переопределение значения корневого объекта типа GraphQL Union / Interface - PullRequest
0 голосов
/ 11 января 2019

У меня есть сервис 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 Ответ

0 голосов
/ 11 января 2019

Держу пари, что такой сценарий обычно решается путем преобразования данных до того, как они достигнут логики __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, которая может гипотетически измениться в будущем.

...