Вложить Js с помощью службы из другого модуля в настраиваемом репозитории - PullRequest
0 голосов
/ 09 мая 2020

Learning Nest Js на самом деле и столкнулся с проблемой сохранения отношения типа OneToMany. Допустим, у меня есть два модуля ProjectsModule @ PlanModule

Существует связь OneToMany между сущностями Plan и Project

@Entity()
export class Project extends BaseEntity {

  @PrimaryGeneratedColumn('uuid')
  id: string;
  ...
  @OneToMany(type => Plan, plan => plan.project, { eager: true })
  plans: Plan[];
}
@Entity()
export class Plan extends BaseEntity {

  @PrimaryGeneratedColumn('uuid')
  id: string;
  ...
  @ManyToOne(type => Project, project => project.plans, { eager: false } )
  project: Project;

  @Column()
  projectId: string;
}

В ProjectsModule у меня есть ProjectsService с этим методом:

  async getProjectById(
    id: string,
    user: User
  ): Promise<Project> {
    const found = await this.projectRepository.findOne({ where: { id, ownerId: user.id } });

    if(!found) {
      throw new NotFoundException(`Project with ID "${id}" not found`)
    }

    return found;
  }

Моя проблема - когда я пытаюсь сохранить новый План. My PlanService вызывает PlanRepository вот так

 async createPlan(
    createPlanDto: CreatePlanDto,
    user: User
  ): Promise<Plan> {
    return this.planRepository.createPlan(createPlanDto, user);
  }

А в PlanRepository:


  constructor(
    @Inject(ProjectsService)
    private projectsService: ProjectsService
  ) {
    super();
  }

async createPlan(
    createPlanDto: CreatePlanDto,
    user: User
  ): Promise<Plan> {
    const { title, description, project } = createPlanDto;
    const plan = new Plan();

    const projectFound = await this.projectsService.getProjectById(project, user)

    plan.title = title;
    plan.description = description;
    plan.status = PlanStatus.ENABLED;
    plan.owner = user;
    plan.project = project;

    try {
      await plan.save();
    } catch (error) {
      this.logger.error(`Failed to create a Plan for user "${user.email}". Data: ${JSON.stringify(createPlanDto)}`, error.stack);
      throw new InternalServerErrorException();
    }
    delete plan.owner;
    return plan;
  }

Попытка этого выдает эту ошибку при отправке запроса POST контроллеру моего плана:

TypeError: this.projectsService.getProjectById is not a function

И попробовав

console.log('service', this.projectsService)

дайте мне

service EntityManager {
  repositories: [],
  plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer {},
  connection: Connection {

Думаю, я неправильно использую projectsService, но я не понимаю, где я мог бы сделать ошибка.

На стороне модуля я экспортирую ProjectsService в его модуле:

exports: [ProjectsService]

И импортирую полный модуль ProjectsModule в модуль планов:

imports: [
    TypeOrmModule.forFeature([PlanRepository]),
    AuthModule,
    ProjectsModule
  ],

Извините для длинного поста, пытаюсь быть исчерпывающим.

1 Ответ

0 голосов
/ 09 мая 2020
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from '../auth/user.entity';
import { PlanRepository } from './plan.repository';
import { GetPlanFilterDto } from './dto/get-plan-filter.dto';
import { Plan } from './plan.entity';
import { CreatePlanDto } from './dto/create-plan.dto';

@Injectable()
export class PlansService {
  constructor(
    @InjectRepository(PlanRepository)
    private planRepository: PlanRepository,
  ) {}

  async getPlans(filterDto: GetPlanFilterDto, user: User): Promise<Plan[]> {
    return this.planRepository.find({ ...filterDto, ownerId: user.id });
  }

  async getPlanById(id: string, user: User): Promise<Plan> {
    return this.planRepository.findOne({
      where: { id, ownerId: user.id },
    });
  }

  async createPlan(createPlanDto: CreatePlanDto, user: User): Promise<Plan> {
    const { project, ...data } = createPlanDto;

    return this.planRepository
      .create({
        projectId: project,
        ownerId: user.id,
        ...data,
      })
      .save();
  }
}

Этот PlanService только использует внутренние методы репозитория, если вы входите в систему в случае ошибки, ExceptionFilter будет подходящим вариантом для этого: https://docs.nestjs.com/exception-filters .

Вместо проверки, был ли план найден, вы можете использовать перехватчик:

import {
  CallHandler,
  ExecutionContext,
  Injectable,
  NestInterceptor,
  NotFoundException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class PlanNotFoundInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      map(plan => {
        if (!plan) {
          throw new NotFoundException("plan couldn't be found");
        }


        return plan;
      }),
    );
  }
}

Затем на вашем getById (Контроллере) используйте @UseInterceptor, это отделяет ваш сервис, доступ к данным, ведение журнала, проверка и т. д. c ..

Я упростил реализацию (для перехватчика), вам может потребоваться немного изменить его в соответствии с вашими конкретными потребностями. 1016 *

Я не тратил много времени на просмотр ваших тестов, но внесенные изменения не внесли никаких критических изменений в модульные тесты (не могу сказать то же самое для e2e, лично не используйте Cucumber. js).

Суть этого ответа не в том, чтобы предоставить вам необходимый код, а в абстракциях, которые вы можете использовать для решения тесно связанных компонентов.

Вы также можете использовать Interceptor для подтвердите запрос, че ck, если присутствует project, проверить, существует ли он, в противном случае прервать выполнение с ошибкой. Снова отделите обработку ошибок от вашего контроллера / службы / чего угодно.

У вас также есть возможность извлекать / добавлять элементы в запрос, например, .user, который аутентифицирован, или значение из заголовка. (Может быть полезно, если вы хотите отправить projectId в контроллер через объект Request).

...