简体   繁体   English

Nest 无法解析 AuthService (AuthRepository, ?) 的依赖项

[英]Nest can't resolve dependencies of the AuthService (AuthRepository, ?)

I'm trying to implement JWT into my project.我正在尝试将 JWT 实施到我的项目中。 I've followed the steps as outline in https://www.npmjs.com/package/@nestjs/jwt#usage我已按照https://www.npmjs.com/package/@nestjs/jwt#usage 中的概述步骤操作

auth.module.ts auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { JwtModule } from '@nestjs/jwt';
import { AuthRepository } from './auth.repository';

@Module({
    imports: [
        JwtModule.register({ secret: process.env.JWT_SECRET || 'ABCDE12345' }),
        TypeOrmModule.forFeature([AuthRepository]),
    ],
    exports: [TypeOrmModule, AuthService],
    providers: [AuthService],
    controllers: [AuthController],
})
export class AuthModule {}

auth.service.ts auth.service.ts

import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthEntity } from './auth.entity';
import { LoginDTO } from './dto/login.dto';
import * as bcrypt from 'bcrypt';
import { JwtService } from '@nestjs/jwt';
import crypto from 'crypto';
import { AuthRepository } from './auth.repository';

// export interface FindWhereData {
//     readonly email: string;
//     readonly password: string;
// }

export interface LoginResponse {
    readonly token: string;
    readonly refresh_token: string;
}

@Injectable()
export class AuthService {
    constructor(
        @InjectRepository(AuthRepository)
        private authRepository: AuthRepository,
        private jwtService: JwtService
    ) {}

    /**
     * Login user service
     *
     * @param doc
     */
    public async login(doc: LoginDTO): Promise<LoginResponse> {
        // verify user email
        const user = await this.authRepository.findOne({ email: doc.email });

        if (!user) {
            throw new NotFoundException('Could not find user');
        }
        // verify password
        const passwordsMatch = await this.passwordsAreEqual(doc.password, user.password);
        if (!passwordsMatch) {
            throw new UnauthorizedException('Incorrect login credentials');
        }

        // generate JWT
        const token = await this.jwtService.signAsync({ id: user.id });

        // create the refresh token
        const refreshToken = crypto.randomBytes(256).toString('hex');

        // store the refresh token

        return {
            token: token,
            refresh_token: refreshToken,
        };
    }

    /**
     * Generate a hashed password
     *
     * @param password
     */
    public async hashPassword(password: string): Promise<string> {
        const salt = await bcrypt.genSalt();

        return await bcrypt.hash(password, salt);
    }

    /**
     * Compare password against an encrypted string
     *
     * @param password
     * @param encryptedPassword
     */
    public async passwordsAreEqual(password: string, encryptedPassword: string): Promise<boolean> {
        return await bcrypt.compare(password, encryptedPassword);
    }

    /**
     * Find a record by column attribute and value
     *
     * @param queryObject
     *
     */
    public async findWhere(queryObject): Promise<AuthEntity> {
        const authEntity = await this.authRepository.findOne({ where: queryObject });

        if (!authEntity) {
            return null;
        }

        return authEntity;
    }

    public async findOne(id: string): Promise<AuthEntity> {
        return this.authRepository.findOne(id);
    }
}

auth.repository.ts auth.repository.ts

import { EntityRepository, Repository } from "typeorm";
import { AuthEntity } from "./auth.entity";

@EntityRepository(AuthEntity)
export class AuthRepository extends Repository<AuthEntity> {}

app.module.ts app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { RouterModule } from 'nest-router';
import { routes } from './routes';
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
import { AuthModule } from './auth/auth.module';

@Module({
    imports: [
        RouterModule.forRoutes(routes),
        ConfigModule.forRoot({
            load: [configuration],
        }),
        TypeOrmModule.forRoot({
            type: 'postgres',
            host: process.env.POSTGRES_HOST || 'localhost',
            port: 5432,
            username: process.env.POSTGRES_USERNAME || 'postgres',
            password: process.env.POSTGRES_PASSWORD || 'password',
            database: process.env.POSTGRES_DATABASE || 'service-auth',
            autoLoadEntities: true,
            synchronize: true,
        }),
        AuthModule,
    ],
    controllers: [AppController],
    providers: [AppService],
})
export class AppModule {
    constructor(private readonly connection: Connection) {
        console.log('connection status: ' + connection.isConnected);
    }
}

auth.service.spec.ts auth.service.spec.ts

import { JwtModule, JwtService } from '@nestjs/jwt';
import { Test, TestingModule } from '@nestjs/testing';
import { AuthEntity } from './auth.entity';
import { AuthRepository } from './auth.repository';
import { AuthService } from './auth.service';

describe('AuthService', () => {
    let authService: AuthService;
    let authRepository: AuthRepository;
    const mockAuthRepository = () => ({
        login: jest.fn(),
    });

    beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
            providers: [
                AuthService,
                // JwtModule,
                {
                    provide: getRepositoryToken(AuthRepository),
                    useFactory: mockAuthRepository,
                },
                {
                    provide: JwtService,
                    useValue: {
                        signAsync: jest.fn(() => 'token'),
                    }
                }
            ]
        }).compile();

        authService = await module.get<AuthService>(AuthService);
        authRepository = await module.get<AuthRepository>(AuthRepository);
    });

    /**
     * Test that the service is defined
     */
    it('should be defined', () => {
        expect(authService).toBeDefined();
    });
});

When I run npm run test I get the following error message:当我运行npm run test我收到以下错误消息:

FAIL  src/auth/auth.service.spec.ts
  ● AuthService › should be defined

    Nest can't resolve dependencies of the AuthService (AuthRepository, ?). Please make sure that the argument JwtService at index [1] is available in the RootTestModule context.

    Potential solutions:
    - If JwtService is a provider, is it part of the current RootTestModule?
    - If JwtService is exported from a separate @Module, is that module imported within RootTestModule?
      @Module({
        imports: [ /* the Module containing JwtService */ ]
      })

I know the error is probably pretty clear to seasoned Node/Nest developer but I cannot figure out what the RootTestModule is and how to get JwtModule imported.我知道这个错误对于经验丰富的 Node/Nest 开发人员来说可能很清楚,但我无法弄清楚 RootTestModule 是什么以及如何导入 JwtModule。

I believe I have followed the setup correctly but adding this JwtModule to the AuthService is causing the service to be undefined in my unit tests.我相信我已经正确地遵循了设置,但是将此 JwtModule 添加到 AuthService 会导致我的单元测试中未定义该服务。

Repo https://github.com/marcuschristiansen/nestjs-auth回购https://github.com/marcuschristiansen/nestjs-auth

You should be adding a custom provider for the JwtService so that you can mock it.您应该为JwtService添加一个自定义提供程序,以便您可以模拟它。 A custom provider could look like自定义提供程序可能看起来像

{
  provide: JwtService,
  useValue: {
    signAsync: jest.fn(() => 'token'),
  }
}

To tell Nest to inject an object that has a signAsync() method that when called returns the string 'token' so that it will always be the same in your tests.告诉 Nest 注入一个具有signAsync()方法的对象,该方法在调用时返回字符串'token'以便它在您的测试中始终相同。

This object goes in the providers array, just like the AuthRepository mock这个对象进入providers数组,就像AuthRepository模拟

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

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