繁体   English   中英

NestJs 使用自定义存储库中另一个模块的服务

[英]NestJs using a service from another Module in a Custom Repository

实际学习 NestJs 并面临保存 typeorm OneToMany 关系的问题。 假设我有两个模块 ProjectsModule @ PlansModule

在计划和项目实体之间存在 OneToMany 关系

@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;
  }

我的问题是当我尝试保存新计划时。 我的 PlansService 像这样调用 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;
  }

在向我的计划 controller 发送 POST 请求时,尝试这样做会引发此错误:

TypeError: this.projectsService.getProjectById is not a function

并尝试

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

给我吗

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

我想我没有正确使用项目服务,但我不明白我可能在哪里犯了错误。

在模块方面,我在他的模块中导出 ProjectsService:

exports: [ProjectsService]

并将完整的 ProjectsModule 导入 PlansModule:

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

抱歉,这篇文章很长,试图详尽无遗。

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,这将您的服务、数据访问、日志记录、验证等解耦。

我已经简化了实现(对于拦截器),您可能需要稍微调整它以适应您的确切需要。

yarn run v1.22.4
$ jest
ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0.0 <
26.0.0). Please do not report issues in ts-jest if you are using unsupported versions.
ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0.0 <
26.0.0). Please do not report issues in ts-jest if you are using unsupported versions.
ts-jest[versions] (WARN) Version 24.9.0 of jest installed has not been tested with ts-jest. If you're experiencing issues, consider using a supported version (>=25.0.0 <
26.0.0). Please do not report issues in ts-jest if you are using unsupported versions.
 PASS  src/auth/user.repository.spec.ts
 PASS  src/projects/projects.service.spec.ts
 PASS  src/auth/jwt.strategy.spec.ts
 PASS  src/auth/user.entity.spec.ts

Test Suites: 4 passed, 4 total
Tests:       18 passed, 18 total
Snapshots:   0 total
Time:        3.774s, estimated 4s
Ran all test suites.
Done in 4.58s.

我没有花太多时间审查您的测试,但所做的更改并没有对单元测试造成任何重大更改(对于 e2e 不能这么说,个人不要使用 Cucumber.js)。

这个答案的重点不是为您提供所需的代码,而是您可以用来解决紧密耦合组件的抽象。

您还可以使用拦截器来验证请求,检查project是否存在,检查它是否存在,如果不存在错误则中止。 再次将您的错误处理与您的控制器/服务/其他内容分离。

您还可以选择在请求中提取/添加内容,例如经过身份验证的.user或来自 header 的值。 (如果您想通过 Request 对象将 projectId 发送到 Controller 可能会很有用)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM