I'm trying to make a POST request and am getting this error:
{
"msg": "Unable to read undefined properties (reading 'securityService')"
}
Also, when I'm trying to console.log(this.securityService), I get the same error.
security.controller:
/* eslint-disable no-console */
import { IRouter, Request, Response } from 'express';
import { StatusCodes } from 'http-status-codes';
import SecurityService from './security.service';
class SecurityController {
private securityService: SecurityService;
constructor(private router: IRouter) {
this.router = router;
this.securityService = new SecurityService();
}
public async sendVerificationCode(req: Request, res: Response) {
try {
console.log(this.securityService);
const { receiver } = req.query;
const user = await this.securityService.getUser(String(receiver));
console.log(user);
if (!user) {
return res.status(StatusCodes.CONFLICT).json({ msg: "User with this phone number doesn't exist" });
}
const id = await this.securityService.sendSms;
return res.status(StatusCodes.OK).json({ id });
} catch (error) {
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({ msg: error.message });
}
}
}
export default SecurityController;
security.service:
/* eslint-disable no-console */
import { getRepository } from 'typeorm';
import VerificationEntity from '../entities/verification.entity';
class SecurityService {
public async getUser(receiver: string) {
console.log('Check 2');
return getRepository(VerificationEntity).findOne({ mobilePhone: receiver });
}
public async sendSms(body: { receiver: string }) {
const verificationCode = process.env.VERIFICATION_CODE;
await getRepository(VerificationEntity).insert({ mobilePhone: body.receiver, verificationCode });
const { id } = await getRepository(VerificationEntity).findOne();
console.log('Check 3');
return getRepository(VerificationEntity).findOne({ id: id as string });
}
}
export default SecurityService;
So I'm trying to throw my 403 Forbidden error via the HttpException module imported from nest #nestjs/common from a microservice to a gateway, but nest keeps throwing it as a 500 Internal Server error.
The JSON.stringify of the generated error is:
{"response":"User already exists","status":403,"message":"User already exists","name":"HttpException"}
The error displayed in the gateway is:
[Nest] 4747 - 11/06/2022, 18:01:58 ERROR [ExceptionsHandler] Internal server error
[Nest] 4747 - 11/06/2022, 18:01:58 ERROR [ExceptionsHandler] undefined
Any idea whats up with that?
Here's the service that throws an error:
import { Model } from 'mongoose';
import {
Injectable,
Inject,
ForbiddenException,
HttpException,
HttpStatus,
} from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { User, UserDocument } from './schemas/user.schema';
#Injectable()
export class AccountManagementService {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
async createUser(user: any): Promise<any> {
const existingUser = await this.userModel.find({
$or: [{ username: user.username }, { email: user.email }],
});
if (existingUser) {
throw new HttpException('User already exists', HttpStatus.FORBIDDEN);
}
const createdUser = new this.userModel(user);
return createdUser.save();
}
}
And here's the controller:
import { Controller, Post } from '#nestjs/common';
import { MessagePattern } from '#nestjs/microservices';
import { AccountManagementService } from './account-management.service';
#Controller()
export class AccountManagementController {
constructor(
private accountManagementService: AccountManagementService,
) {}
#MessagePattern({ cmd: 'createUser' })
async createUser(userData: any): Promise<any> {
return this.accountManagementService
.createUser(userData)
}
}
So what i basically did was handle the errors thrown from the service in the controllers try and catch block, so any error caught from the service gets thrown with the right status code and description
user.controller.ts
import { Controller, Post, Body, HttpCode, HttpStatus } from '#nestjs/common';
import { AuthService } from './auth.service';
import { AuthDto, SigninDto } from './dto';
#Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
#HttpCode(HttpStatus.OK)
#Post('signin')
async signin(#Body() dto: SigninDto) {
try {
return this.authService.signin(dto);
} catch (error) {
throw error;
}
}
#Post('signup')
async signup(#Body() dto: AuthDto) {
try {
return this.authService.signup(dto);
} catch (error) {
throw error;
}
}
}
Then this is my signup function in the user service
async signup(dto: AuthDto): Promise<any> {
const account = await this.userModel.findOne({
phoneNumber: dto.phoneNumber,
});
if (account) {
throw new ForbiddenException(
'An account with this phoneNumber already exists',
);
}
const SALT = 10;
const hashedPassword = await bcrypt.hash(dto.password, SALT);
const newUser = await this.userService.create({
name: dto.name,
password: hashedPassword,
phoneNumber: dto.phoneNumber,
});
return newUser;
}
I am using axios for the request of my own api, everything works correctly except the DELETE request, I looked for information about the error but I have not found the solution. when I send the request to the server I get this error: "xhr.js:210 DELETE http://localhost:3000/posts/62575cb61cb27c6417732193 403 (Forbidden)".
I put this line of code in Package.Json to avoid problems with CORS:
"proxy": "http://localhost:8080/api"
This would be my api, for the operation I pass the post id by url and the user id:
(I tried it in postman and it works without any problem)
router.delete("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (post.userId === req.body.userId) {
await post.deleteOne();
res.status(200).json("the post has been deleted");
} else {
res.status(403).json("you can delete only your post");
}
} catch (err) {
res.status(500).json(err);
}
});
and this is where I consume my api:
const Post = ({ date, _id,userId, description }) => {
const handleDelete = async () => {
try {
await axios.delete('posts/' + _id, { userId: currentUser._id })
} catch (error) {
console.log(error)
}
}
return(
<div onClick={handleDelete }>
//component content
</div>
)
}
export default Post
I solved it, sending a data object to my api (inside that object I put the user id):
const handleDelete = async () => {
try {
await axios.delete('/posts/' + _id, {
data: {
userId: currentUser._id
}
}
)
} catch (error) {
console.log(error)
}
}
When I update the status field in destroy then update created_at field date.
this is my controller code
async destroy({ params, request, response }) {
try {
const Applicationdata = await Application.find(params.id)
if (Applicationdata) {
Applicationdata.status = !Applicationdata.status
await Applicationdata.save()
return response.ok(Config.get('HttpResponse.OK'), {
Applicationdata
})
} else {
return response.fail(
Config.get('HttpResponse.NOT_FOUND'),
Antl.formatMessage('messages.recordNotFound')
)
}
} catch (error) {
console.log(error)
return response.fail(
Config.get('HttpResponse.INTERNAL_SERVER_ERROR'),
Antl.formatMessage('messages.serverError')
)
}
}
it's my first time making questions on stackoverflow :).
So I'm developing a test / learning application to learn how to use NestJS and Vue.
I am was currently trying to implement several server-side unit tests (using Jest). When trying to create a testingmodule from my UsersService I get the error below on running "npm test users.service.spec"
Nest can't resolve dependencies of the UsersService (?,
ConfigService). Please make sure that the argument at index [0] is
available in the _RootTestModule context.
at Injector.lookupComponentInExports (../node_modules/#nestjs/core/injector/injector.js:183:19)
I am not sure if I am incorrectly instantiating the test model, or misconceived the injection of config.service, the code itself works, but may be implemented incorrectly.
Does anyone have any ideas how to solve this problem?
Git link: https://github.com/Lindul/FuelTracker_NestJs_MySql_Vue.git
users.service.spec.ts
import { Test } from '#nestjs/testing';
import { UsersService } from './users.service';
import { UserDto } from './dto/user.dto';
import { UserLoginRegRequestDto } from './dto/user-login-reg-request.dto';
import { ConfigService } from '../shared/config/config.service';
describe('User Service Tests', () => {
let loginTeste: UserLoginRegRequestDto = {
login: 'LoginJestTeste',
password: 'PassJestTeste',
}
const userMock: UserDto = {
id: 1,
login: 'hugombsantos',
password: 'wefewkfnwekfnwekjnf',
isActive: true,
needChangePass: false,
userType: 0,
};
const dataFindAllMock: UserDto[] = [{
id: 1,
login: 'hugombsantos',
password: 'wefewkfnwekfnwekjnf',
isActive: true,
needChangePass: false,
userType: 0,
}, {
id: 2,
login: 'user2',
password: 'sdgsdgdsgsdgsgsdg',
isActive: true,
needChangePass: false,
userType: 0,
}];
let service: UsersService;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
ConfigService,
UsersService,
],
}).compile();
service = module.get<UsersService>(UsersService);
});
it('UsersService está defenido', () => {
expect(service).toBeDefined();
});
});
user.service.ts
import { Injectable, Inject, HttpException, HttpStatus } from '#nestjs/common';
import { Users } from '../models/users.schema';
import { UserDto } from './dto/user.dto';
import { UserLoginRegRequestDto } from './dto/user-login-reg-request.dto';
import { UserLoginResponseDto } from './dto/user-login-response.dto';
import { sign } from 'jsonwebtoken';
import { ConfigService } from '../shared/config/config.service';
import { JwtPayload } from './auth/jwt-payload.model';
import { ErroMessageDto } from '../shared/config/dto/erro-message.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import bcrypt = require('bcrypt-nodejs');
const SALT_FACTOR = 8;
#Injectable()
export class UsersService {
constructor(
#Inject('UsersRepository') private usersRepository: typeof Users,
private readonly configService: ConfigService,
) { }
/* istanbul ignore next */
async findAll(): Promise<UserDto[]> {
const users = await this.usersRepository.findAll<Users>();
return users.map(user => {
return new UserDto(user);
});
}
/* istanbul ignore next */
async findUser(id: number): Promise<Users> {
const user = await this.usersRepository.findOne<Users>({
where: { id },
});
return user;
}
/* istanbul ignore next */
async findUserformLogin(login: string): Promise<UserDto> {
return await this.usersRepository.findOne<Users>({
where: { login },
});
}
/* istanbul ignore next */
async findUserforLogin(id: number, login: string): Promise<Users> {
return await this.usersRepository.findOne<Users>({
where: { id, login },
});
}
/* istanbul ignore next */
async creatUserOnDb(createUser: UserLoginRegRequestDto): Promise<Users> {
try {
const user = new Users();
user.login = createUser.login;
user.password = await this.hashPass(createUser.password);
return await user.save();
} catch (err) {
if (err.original.code === 'ER_DUP_ENTRY') {
throw new HttpException(
new ErroMessageDto(
HttpStatus.CONFLICT,
`Login '${err.errors[0].value}' already exists`),
HttpStatus.CONFLICT,
);
}
throw new HttpException(
new ErroMessageDto(
HttpStatus.INTERNAL_SERVER_ERROR,
err),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
/* istanbul ignore next */
async updateUserOnDb(user: Users): Promise<Users> {
try {
return await user.save();
} catch (err) {
if (err.original.code === 'ER_DUP_ENTRY') {
throw new HttpException(
new ErroMessageDto(
HttpStatus.CONFLICT,
`Login '${err.errors[0].value}' already exists`),
HttpStatus.CONFLICT,
);
}
throw new HttpException(
new ErroMessageDto(
HttpStatus.INTERNAL_SERVER_ERROR,
err),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
/* istanbul ignore next */
async delete(id: number): Promise<UserDto> {
const user = await this.findUser(+id);
await user.destroy();
return new UserDto(user);
}
async login(
userLoginRequestDto: UserLoginRegRequestDto,
): Promise<UserLoginResponseDto> {
const login = userLoginRequestDto.login;
const password = userLoginRequestDto.password;
const user = await this.findUserformLogin(login);
if (!user) {
throw new HttpException(
new ErroMessageDto(
HttpStatus.UNAUTHORIZED,
'Invalid login or password.'),
HttpStatus.UNAUTHORIZED,
);
}
const isMatch = await this.comparePass(password, user.password);
if (!isMatch) {
throw new HttpException(
new ErroMessageDto(
HttpStatus.UNAUTHORIZED,
'Invalid login or password.'),
HttpStatus.UNAUTHORIZED,
);
}
const token = await this.signToken(user);
return new UserLoginResponseDto(token);
}
async create(createUser: UserLoginRegRequestDto): Promise<UserLoginResponseDto> {
const userData = await this.creatUserOnDb(createUser);
// when registering then log user in automatically by returning a token
const token = await this.signToken(userData);
return new UserLoginResponseDto(token);
}
async updateUser(id: number | string, updateUserDto: UpdateUserDto): Promise<UserLoginResponseDto> {
const user = await this.findUser(+id);
if (!user) {
throw new HttpException(
new ErroMessageDto(
HttpStatus.NOT_FOUND,
'User not found.'),
HttpStatus.NOT_FOUND,
);
}
user.login = updateUserDto.login || user.login;
user.userType = updateUserDto.userType || user.userType;
user.needChangePass = false;
const isMatch = await this.comparePass(updateUserDto.password, user.password);
if (updateUserDto.password && !isMatch) {
user.password = await this.hashPass(updateUserDto.password);
} else {
user.password = user.password;
}
const userData = await this.updateUserOnDb(user);
const token = await this.signToken(userData);
return new UserLoginResponseDto(token);
}
async signToken(user: UserDto): Promise<string> {
const payload: JwtPayload = {
id: user.id,
login: user.login,
};
const token = sign(payload, this.configService.jwtConfig.jwtPrivateKey,
{
expiresIn: this.configService.jwtConfig.valideTime,
});
return token;
}
async hashPass(passToHash: string): Promise<string> {
const saltValue = await bcrypt.genSaltSync(SALT_FACTOR);
return await bcrypt.hashSync(passToHash, saltValue);
}
async comparePass(passToCheck: string, correntPass: string) {
return await bcrypt.compareSync(passToCheck, correntPass);
}
}
users.modules.ts
import { Module } from '#nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import {userProviders} from './users.providers';
import { DatabaseModule } from '../database/database.module';
import { JwtStrategy } from './auth/jwt-strategy';
import { ConfigService } from '../shared/config/config.service';
#Module({
imports: [DatabaseModule, ConfigService],
controllers: [UsersController],
providers: [UsersService,
...userProviders, JwtStrategy],
exports: [UsersService],
})
export class UsersModule {}
config.service.ts
import { Injectable } from '#nestjs/common';
import { JwtConfig } from './interfaces/jwt-config.interface';
import { config } from '../../../config/config.JwT';
import { config as dbConfigData } from '../../../config/config.db';
import { SequelizeOrmConfig } from './interfaces/sequelize-orm-config.interface';
#Injectable()
export class ConfigService {
get sequelizeOrmConfig(): SequelizeOrmConfig {
return dbConfigData.databaseOpt;
}
get jwtConfig(): JwtConfig {
return {
jwtPrivateKey: config.jwtPrivateKey,
valideTime: config.valideTime,
};
}
}
You need to provide some value for Nest to inject for your UserRepository. It looks like you are using the TypeORM #InjectRepository() decorator, so in your testing module you'll need to add something like this under your providers
{
provide: getRepositoryToken('UsersRepository'),
useValue: MyMockUserRepo,
}
So that Nest can know what value to inject for that string. MyMockUserRepo would be your mock repository functionality, so that you aren't making actual database calls to your backend