javascript - NestJs 使用自定义存储库中另一个模块的服务
问题描述
实际学习 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;
}
在向我的计划控制器发送 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)。
这个答案的重点不是为您提供所需的代码,而是您可以用来解决紧密耦合组件的抽象。
您还可以使用拦截器来验证请求,检查 aproject
是否存在,检查它是否存在,如果不存在错误则中止。再次将您的错误处理与您的控制器/服务/其他内容分离。
您还可以选择在请求中提取/添加内容,例如经过.user
身份验证的内容或标头中的值。(如果您想通过 Request 对象将 projectId 发送到控制器中,这可能很有用)。
推荐阅读
- c# - 以不同的方法使用全局控件
- sql - 通过某些列 id 选择主表和关系表
- javascript - 为什么 fetch 返回 promise 未决?
- javascript - Angular/Firebase:如何将用户响应放在前端模型中
- spring - 带有排序的spring mongoDB聚合不起作用
- react-native - 应用程序在 Android 中关闭并重新打开后,嵌套堆栈导航器重置为 initialRoute(无需滑动)
- benchmarkdotnet - 如何测量字符串实习?
- sql - BigQuery 标准 SQL 中的 typedef 等效项
- node.js - Jenkins 参数在命令行中无法识别
- autodesk-forge - MarkupsCore Extension 自定义代码后无法与 Autodesk View 交互