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

时间:2020-05-09 18:06:10

标签: javascript nestjs typeorm

实际学习NestJs并面对一个节省类型orOneToMany关系的问题。 假设我有两个模块ProjectsModule @ PlansModule

在计划和项目实体之间存在一个一对多关系

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

尝试此操作时,向我的计划控制器发送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导入PlansModule:

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

很长的帖子,很抱歉,请尝试详尽。

1 个答案:

答案 0 :(得分:0)

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

我已经简化了实现(对于Interceptor),您可能需要对其进行一些调整以适应您的确切需求。

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)。 / p>

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

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

您还可以选择向请求中添加/添加内容,例如已验证的.user或标头中的值。 (如果要通过Request对象将projectId发送到Controller中,可能会很有用。)