Добавление преобразователя полей для второго вызова api с помощью type-graphql и RESTDataSource

#graphql #apollo-server #typegraphql

#graphql #apollo-сервер #typegraphql

Вопрос:

Мы хотим, чтобы приведенный ниже запрос разрешал truck поля на основе truckId возвращенных:

введите описание изображения здесь

Сущности

 // RosterEntity.ts
import { Truck } from '@sap-truck-roster/entity/Truck'
import { Field, ID, ObjectType } from 'type-graphql'

@ObjectType()
export class Roster {
  @Field(() => Truck, { nullable: true })
  truck: Truck
  
  @Field(() => ID)
  readonly truckId: string
}
 
 // TruckEntity.ts
import { Field, ID, ObjectType } from 'type-graphql'

@ObjectType()
export class Truck {
  @Field(() => ID)
  readonly id: string

  @Field()
  readonly description: string
}
 

Преобразователи

 // TruckResolver.ts
import { Resolver, Arg, Query, Ctx, Field, ObjectType, createUnionType,} from 'type-graphql'
import { Truck } from '@sap-truck-roster/entity/Truck'
import { plainToClass } from 'class-transformer'
import { Context } from '@shared/typings'
import { ApiError } from '@shared/graphql'

@ObjectType()
class TruckArray {
  @Field(() => [Truck], { nullable: true })
  data?: [Truck]
}

const TruckQueryResultUnion = createUnionType({
  name: 'TruckQueryResult',
  // types: () => [[Truck], ApiError] as const,
  // an array is not supported by the Graphql spec in unions
  types: () => [TruckArray, ApiError] as const,
})

@Resolver(() => Truck)
export class TruckResolver {
  @Query(() => TruckQueryResultUnion)
  async truck(
    @Ctx() ctx: Context,
    @Arg('id', { nullable: true }) id?: string,
    @Arg('country', { nullable: true }) country?: string
  ): Promise<typeof TruckQueryResultUnion> {
      const response = await ctx.dataSources.sapTruckRosterAPI.getTruck({ id, country })

      return plainToClass(TruckArray, { data: response.data, })
  }
}
 
 // RosterResolver.ts
import { Resolver, Arg, Query, Ctx, Field, ObjectType, createUnionType,} from 'type-graphql'
import { Roster } from '@sap-truck-roster/entity/Roster'
import { plainToClass } from 'class-transformer'
import { Context } from '@shared/typings'
import { ApiError } from '@shared/graphql'
import { Truck } from '@sap-truck-roster/entity/Truck'

@ObjectType()
class RosterArray {
  @Field(() => [Roster], { nullable: true })
  data?: [Roster]
}

const RosterQueryResultUnion = createUnionType({
  name: 'RosterQueryResult',
  // types: () => [[Roster], ApiError] as const,
  // an array is not supported by the Graphql spec in unions
  types: () => [RosterArray, ApiError] as const,
})

@Resolver(() => Roster)
export class RosterResolver {
  @Query(() => RosterQueryResultUnion)
  async roster(
    @Ctx() ctx: Context,
    @Arg('date', () => String) date: string,
    @Arg('truckId', { nullable: true }) truckId?: string,
    @Arg('driverId', { nullable: true }) driverId?: string
  ): Promise<typeof RosterQueryResultUnion> {
      const response = await ctx.dataSources.sapTruckRosterAPI.getRoster({ date, driverId, truckId })

      // @FieldResolver(() => Truck)
      // truck(@Root() truck: Truck) {
      //   return await ctx.dataSources.sapTruckRosterAPI.getTruck({
      //     id: response.truckId
      //   })
      // }
      
     return plainToClass(RosterArray, { data: response.data })
  }
}
 

Как вы можете видеть в RosterResolver.ts , мы пытались использовать response.truckId для запроса другой конечной точки в API, но это не удается. Каков правильный способ заполнения truck поля данными?

Это возвращает в консоль правильные данные, но не сопоставляет их с roster as, поскольку мы получаем null значения для всех truck свойств:

   // RosterResolver.ts

  @FieldResolver(() => Truck, { nullable: true })
  async truck(@Root() roster: Roster, @Ctx() ctx: Context) {
    console.log('roster.truckId: ', roster.truckId)

    const response = await ctx.dataSources.sapTruckRosterAPI.getTruck({
      id: roster.truckId,
    })

    console.log('response: ', response);
    
    return response
  }
 

введите описание изображения здесь

введите описание изображения здесь

Ответ №1:

Что-то, что вы можете сделать, это наследование преобразователя. https://github.com/MichalLytek/type-graphql/blob/master/docs/resolvers.md#field-resolvers

 @Resolver(() => Roster)
export class RosterResolver {

  ....

  @FieldResolver()
  truck(@Root() roster: Roster) {
    return ctx.dataSources.sapTruckRosterAPI.getTruck({
      id: roster.truckId
    });
  }
 

Комментарии:

1. Спасибо за совет, но я попробовал это, и он завершается с ошибкой «Не удается вернуть значение null для ненулевого поля Truck.id .». Даже когда делаешь это @FieldResolver(() => Truck, { nullable: true })

2. Когда я пытаюсь @FieldResolver(() => Roster, { nullable: true }) , я получаю сообщение об ошибке «Невозможно запросить поле «id» для типа «Реестр»