feat: add pagination, sorting and filtering
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsInt, IsOptional, IsString, Min } from 'class-validator';
|
||||
|
||||
export class PaginationQueryDTO {
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page?: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
limit?: number = 20;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
sortBy?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
order?: 'ASC' | 'DESC' = 'DESC';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { RequestTimeoutException } from '@nestjs/common';
|
||||
|
||||
export class AppTimeoutException extends RequestTimeoutException {
|
||||
constructor(cause?: unknown, description?: string) {
|
||||
super('common.errors.requestTimeout', {
|
||||
cause,
|
||||
description: description ?? 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import * as StringUtils from './string-utils';
|
||||
import * as PaginationUtils from './pagination-utils';
|
||||
|
||||
const Utils = {
|
||||
StringUtils,
|
||||
PaginationUtils,
|
||||
};
|
||||
|
||||
export default Utils;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { PaginationQueryDTO } from '../dtos/pagination-query.dto';
|
||||
|
||||
export const getPaginationOptions = (query: PaginationQueryDTO) => {
|
||||
const page = query.page ?? 1;
|
||||
const limit = query.limit ?? 20;
|
||||
|
||||
return {
|
||||
skip: (page - 1) * limit,
|
||||
take: limit,
|
||||
page,
|
||||
limit,
|
||||
};
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
||||
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { AdminAuthService } from './providers/admin-auth.service';
|
||||
import { AdminLoginDTO } from './dtos/admin-login.dto';
|
||||
import { Public } from '@/modules/auth/decorators/public.decorator';
|
||||
@@ -14,7 +14,7 @@ export class AdminAuthController {
|
||||
|
||||
@Public()
|
||||
@Post('login')
|
||||
@HttpCode(200)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public async login(@Body() adminLoginDto: AdminLoginDTO) {
|
||||
return await this.adminAuthService.login(adminLoginDto);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
import { Role } from '@/common/enums/roles.enum';
|
||||
import { Roles } from '@/modules/auth/decorators/roles.decorator';
|
||||
import { CreateTestDTO } from '@/modules/tests/dtos/create-test.dto';
|
||||
import { FindTestsDTO } from '@/modules/tests/dtos/find-tests.dto';
|
||||
import { UpdateTestDTO } from '@/modules/tests/dtos/update-test.dto';
|
||||
import { TestsService } from '@/modules/tests/providers/tests.service';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
|
||||
@Controller('admin/tests')
|
||||
@@ -29,8 +34,8 @@ export class AdminTestsController {
|
||||
}
|
||||
|
||||
@Get()
|
||||
public async getAllTests() {
|
||||
return await this.testsService.adminGetAll();
|
||||
public async getAllTests(@Query() query: FindTestsDTO) {
|
||||
return await this.testsService.adminGetAll(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@@ -45,4 +50,10 @@ export class AdminTestsController {
|
||||
) {
|
||||
return await this.testsService.updateOne(id, updateTestDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
public async deleteTestById(@Param('id', ParseIntPipe) id: number) {
|
||||
return await this.testsService.deleteOne(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
DELETE http://localhost:3000/admin/tests/1
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwNzQ5MTcwLCJleHAiOjE3ODA3NTI3NzAsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.apRhtVZgFMFcyutZGO7uzckscch9Jw42s0RCSZZv4K0
|
||||
@@ -1,5 +1,5 @@
|
||||
GET http://localhost:3000/admin/tests
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwMzk4MzI0LCJleHAiOjE3ODA0MDE5MjQsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.s3NAErSAjMTz2s27obas3TEi5Og2HPeLgWCoIyzmzII
|
||||
GET http://localhost:3000/admin/tests?title=test&isActive=true&accessType=paid&sortBy=title&order=DSC&page=2&limit=1
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwNzQ5MTcwLCJleHAiOjE3ODA3NTI3NzAsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.apRhtVZgFMFcyutZGO7uzckscch9Jw42s0RCSZZv4K0
|
||||
|
||||
GET http://localhost:3000/admin/tests/4
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwMzk4MzI0LCJleHAiOjE3ODA0MDE5MjQsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.s3NAErSAjMTz2s27obas3TEi5Og2HPeLgWCoIyzmzII
|
||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEwLCJwaG9uZSI6IjA5MzMzMDI2MzYzIiwiaWF0IjoxNzgwNzQ5MTcwLCJleHAiOjE3ODA3NTI3NzAsImF1ZCI6ImxvY2FsaG9zdCIsImlzcyI6ImxvY2FsaG9zdCJ9.apRhtVZgFMFcyutZGO7uzckscch9Jw42s0RCSZZv4K0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, HttpCode, Post } from '@nestjs/common';
|
||||
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { LoginDTO } from './dtos/login.dto';
|
||||
import { AuthService } from './providers/auth.service';
|
||||
import { VerifyOtpDTO } from './dtos/verify-otp.dto';
|
||||
@@ -16,21 +16,21 @@ export class AuthController {
|
||||
|
||||
@Public()
|
||||
@Post('login')
|
||||
@HttpCode(200)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public async logIn(@Body() loginDto: LoginDTO) {
|
||||
return await this.authService.logIn(loginDto);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('verify-otp')
|
||||
@HttpCode(200)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public async verifyOTP(@Body() verifyOtpDto: VerifyOtpDTO) {
|
||||
return await this.authService.verifyOTP(verifyOtpDto);
|
||||
}
|
||||
|
||||
@Public()
|
||||
@Post('refresh')
|
||||
@HttpCode(200)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
public async refreshToken(@Body() refreshTokenDto: RefreshTokenDTO) {
|
||||
return await this.authService.refreshToken(refreshTokenDto);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import {
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
RequestTimeoutException,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { HttpStatus, Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { LessThan, MoreThanOrEqual, Repository } from 'typeorm';
|
||||
import { OTP } from '../entities/otp.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { HashingProvider } from '@/common/modules/hashing/providers/hashing.provider';
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
|
||||
@Injectable()
|
||||
export class OtpService {
|
||||
@@ -73,10 +69,7 @@ export class OtpService {
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!otp) throw new UnauthorizedException('auth.errors.invalidOTP');
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { PaginationQueryDTO } from '@/common/dtos/pagination-query.dto';
|
||||
import {
|
||||
IsBoolean,
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
import { AccessType } from '../enums/access-types.enum';
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { TestSortField } from '../enums/test-sort-field.enum';
|
||||
|
||||
export class FindTestsDTO extends PartialType(PaginationQueryDTO) {
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
id?: number;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
title?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
price?: string;
|
||||
|
||||
@IsEnum(AccessType)
|
||||
@IsOptional()
|
||||
accessType?: AccessType;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsEnum(TestSortField)
|
||||
@IsOptional()
|
||||
sortBy?: TestSortField;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export enum TestSortField {
|
||||
ID = 'id',
|
||||
TITLE = 'title',
|
||||
DESCRIPTION = 'description',
|
||||
ACCESS_TYPE = 'accessType',
|
||||
PRICE = 'price',
|
||||
IS_ACTIVE = 'isActive',
|
||||
CREATED_AT = 'createdAt',
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { FindOptionsWhere, ILike } from 'typeorm';
|
||||
import { FindTestsDTO } from '../dtos/find-tests.dto';
|
||||
|
||||
export const filterTests = (query: FindTestsDTO) => {
|
||||
const where: FindOptionsWhere<any> = {};
|
||||
|
||||
if (query.id) {
|
||||
where.id = query.id;
|
||||
}
|
||||
|
||||
if (query.title) {
|
||||
where.title = ILike(`%${query.title}%`);
|
||||
}
|
||||
|
||||
if (query.description) {
|
||||
where.description = ILike(`%${query.description}%`);
|
||||
}
|
||||
|
||||
if (query.accessType !== undefined) {
|
||||
where.accessType = query.accessType;
|
||||
}
|
||||
|
||||
if (query.price !== undefined) {
|
||||
where.price = query.price;
|
||||
}
|
||||
|
||||
if (query.isActive !== undefined) {
|
||||
where.isActive = query.isActive;
|
||||
}
|
||||
|
||||
return where;
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { CreateTestDTO } from '../dtos/create-test.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Test } from '../entities/test.entity';
|
||||
import { Repository } from 'typeorm';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { AppResponse } from '@/common/responses';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
import { UpdateTestDTO } from '../dtos/update-test.dto';
|
||||
import { AccessType } from '../enums/access-types.enum';
|
||||
|
||||
@Injectable()
|
||||
export class TestsCommandProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Tests Repository
|
||||
*/
|
||||
@InjectRepository(Test)
|
||||
private readonly testRepository: Repository<Test>,
|
||||
|
||||
/**
|
||||
* Inject I18n Service
|
||||
*/
|
||||
private readonly i18nService: I18nService,
|
||||
) {}
|
||||
|
||||
public async createOne(createTestDto: CreateTestDTO) {
|
||||
const titleExists = await this.testRepository.findOne({
|
||||
where: {
|
||||
title: createTestDto.title,
|
||||
},
|
||||
});
|
||||
|
||||
if (titleExists) {
|
||||
throw new AppException('validation.duplicateField', HttpStatus.CONFLICT, {
|
||||
field: '$t(tests.fields.title)',
|
||||
});
|
||||
}
|
||||
|
||||
const newTest = this.testRepository.create(createTestDto);
|
||||
|
||||
try {
|
||||
await this.testRepository.save(newTest);
|
||||
return new AppResponse(
|
||||
newTest,
|
||||
this.i18nService.translate('tests.messages.createSuccess'),
|
||||
);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateOne(id: number, updateTestDto: UpdateTestDTO) {
|
||||
const test = await this.testRepository.findOneBy({ id });
|
||||
|
||||
if (!test) {
|
||||
throw new NotFoundException('tests.errors.testNotFound');
|
||||
}
|
||||
|
||||
const accessType = updateTestDto.accessType ?? test.accessType;
|
||||
const price = updateTestDto.price ?? test.price;
|
||||
|
||||
if (
|
||||
(accessType === AccessType.PAID && !price) ||
|
||||
(accessType !== AccessType.PAID && price)
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
this.i18nService.translate('tests.errors.priceAccessConstraint'),
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(test, updateTestDto);
|
||||
|
||||
try {
|
||||
return await this.testRepository.save(test);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteOne(id: number) {
|
||||
let test: Test | null = null;
|
||||
|
||||
try {
|
||||
test = await this.testRepository.findOneBy({ id });
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!test) throw new NotFoundException('tests.errors.testNotFound');
|
||||
|
||||
try {
|
||||
await this.testRepository.delete(test.id);
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { PaginatedResponse } from '@/common/responses';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
import { FindTestsDTO } from '../dtos/find-tests.dto';
|
||||
import { filterTests } from '../helpers/filter-tests.helper';
|
||||
import Utils from '@/common/utils';
|
||||
import { Test } from '../entities/test.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TestsQueryProvider {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Tests Repository
|
||||
*/
|
||||
@InjectRepository(Test)
|
||||
private readonly testRepository: Repository<Test>,
|
||||
) {}
|
||||
|
||||
public async findAllForAdmin(query: FindTestsDTO) {
|
||||
const filterOptions = filterTests(query);
|
||||
const { page, take, limit, skip } =
|
||||
Utils.PaginationUtils.getPaginationOptions(query);
|
||||
|
||||
try {
|
||||
const [tests, total] = await this.testRepository.findAndCount({
|
||||
where: filterOptions,
|
||||
skip,
|
||||
take,
|
||||
order: query.sortBy
|
||||
? {
|
||||
[query.sortBy]: query.order ?? 'ASC',
|
||||
}
|
||||
: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
return new PaginatedResponse(tests, {
|
||||
page,
|
||||
limit,
|
||||
total,
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async findAllForUser() {
|
||||
try {
|
||||
return await this.testRepository.find({
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
accessType: true,
|
||||
description: true,
|
||||
price: true,
|
||||
title: true,
|
||||
},
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async findOneForAdmin(id: number) {
|
||||
try {
|
||||
return await this.testRepository.findOneBy({ id });
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
public async findOneForUser(id: number) {
|
||||
let test: Test | null;
|
||||
|
||||
try {
|
||||
test = await this.testRepository.findOne({
|
||||
where: { id, isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
accessType: true,
|
||||
description: true,
|
||||
price: true,
|
||||
title: true,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
throw new NotFoundException('tests.errors.testNotFound');
|
||||
}
|
||||
|
||||
return test;
|
||||
}
|
||||
}
|
||||
@@ -1,163 +1,49 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
RequestTimeoutException,
|
||||
} from '@nestjs/common';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Test } from '../entities/test.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CreateTestDTO } from '../dtos/create-test.dto';
|
||||
import { AppException } from '@/common/exceptions/app.exception';
|
||||
import { AppResponse } from '@/common/responses';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { UpdateTestDTO } from '../dtos/update-test.dto';
|
||||
import { AccessType } from '../enums/access-types.enum';
|
||||
import { FindTestsDTO } from '../dtos/find-tests.dto';
|
||||
import { TestsQueryProvider } from './tests-query.provider';
|
||||
import { TestsCommandProvider } from './tests-command.provider';
|
||||
|
||||
@Injectable()
|
||||
export class TestsService {
|
||||
constructor(
|
||||
/**
|
||||
* Inject Tests Repository
|
||||
* Inject Tests Query Provider
|
||||
*/
|
||||
@InjectRepository(Test)
|
||||
private readonly testRepository: Repository<Test>,
|
||||
private readonly testsQueryProvider: TestsQueryProvider,
|
||||
|
||||
/**
|
||||
* Inject I18n Service
|
||||
* Inject Tests Command Provider
|
||||
*/
|
||||
private readonly i18nService: I18nService,
|
||||
private readonly testsCommandProvider: TestsCommandProvider,
|
||||
) {}
|
||||
|
||||
public async createOne(createTestDto: CreateTestDTO) {
|
||||
const titleExists = await this.testRepository.findOne({
|
||||
where: {
|
||||
title: createTestDto.title,
|
||||
},
|
||||
});
|
||||
|
||||
if (titleExists) {
|
||||
throw new AppException('validation.duplicateField', HttpStatus.CONFLICT, {
|
||||
field: '$t(tests.fields.title)',
|
||||
});
|
||||
return await this.testsCommandProvider.createOne(createTestDto);
|
||||
}
|
||||
|
||||
const newTest = this.testRepository.create(createTestDto);
|
||||
|
||||
try {
|
||||
await this.testRepository.save(newTest);
|
||||
return new AppResponse(
|
||||
newTest,
|
||||
this.i18nService.translate('tests.messages.createSuccess'),
|
||||
);
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async adminGetAll() {
|
||||
try {
|
||||
return await this.testRepository.find({ order: { createdAt: 'DESC' } });
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
public async adminGetAll(query: FindTestsDTO) {
|
||||
return await this.testsQueryProvider.findAllForAdmin(query);
|
||||
}
|
||||
|
||||
public async userGetAll() {
|
||||
try {
|
||||
return await this.testRepository.find({
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
accessType: true,
|
||||
description: true,
|
||||
price: true,
|
||||
title: true,
|
||||
},
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
return await this.testsQueryProvider.findAllForUser();
|
||||
}
|
||||
|
||||
public async adminGetOne(id: number) {
|
||||
try {
|
||||
return await this.testRepository.findOneBy({ id });
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
return await this.testsQueryProvider.findOneForAdmin(id);
|
||||
}
|
||||
|
||||
public async userGetOne(id: number) {
|
||||
let test: Test | null;
|
||||
|
||||
try {
|
||||
test = await this.testRepository.findOne({
|
||||
where: { id, isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
accessType: true,
|
||||
description: true,
|
||||
price: true,
|
||||
title: true,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
|
||||
if (!test) {
|
||||
throw new NotFoundException('tests.errors.testNotFound');
|
||||
}
|
||||
|
||||
return test;
|
||||
return await this.testsQueryProvider.findOneForUser(id);
|
||||
}
|
||||
|
||||
public async updateOne(id: number, updateTestDto: UpdateTestDTO) {
|
||||
const test = await this.testRepository.findOneBy({ id });
|
||||
|
||||
if (!test) {
|
||||
throw new NotFoundException('tests.errors.testNotFound');
|
||||
return await this.testsCommandProvider.updateOne(id, updateTestDto);
|
||||
}
|
||||
|
||||
const accessType = updateTestDto.accessType ?? test.accessType;
|
||||
const price = updateTestDto.price ?? test.price;
|
||||
|
||||
if (
|
||||
(accessType === AccessType.PAID && !price) ||
|
||||
(accessType !== AccessType.PAID && price)
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
this.i18nService.translate('tests.errors.priceAccessConstraint'),
|
||||
);
|
||||
}
|
||||
|
||||
Object.assign(test, updateTestDto);
|
||||
|
||||
try {
|
||||
return await this.testRepository.save(test);
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
}
|
||||
public async deleteOne(id: number) {
|
||||
return await this.testsCommandProvider.deleteOne(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,13 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Test } from './entities/test.entity';
|
||||
import { TestsService } from './providers/tests.service';
|
||||
import { TestsController } from './tests.controller';
|
||||
import { TestsQueryProvider } from './providers/tests-query.provider';
|
||||
import { TestsCommandProvider } from './providers/tests-command.provider';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Test])],
|
||||
controllers: [TestsController],
|
||||
providers: [TestsService],
|
||||
providers: [TestsService, TestsQueryProvider, TestsCommandProvider],
|
||||
exports: [TestsService],
|
||||
})
|
||||
export class TestsModule {}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { HashingProvider } from '@/common/modules/hashing/providers/hashing.provider';
|
||||
import { Injectable, RequestTimeoutException } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { I18nService } from 'nestjs-i18n';
|
||||
import { User } from '../entities/user.entity';
|
||||
import { UpdateProfileDTO } from '../dto/update-profile.dto';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { AppResponse } from '@/common/responses';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
|
||||
@Injectable()
|
||||
export class UserProfileProvider {
|
||||
@@ -59,10 +60,7 @@ export class UserProfileProvider {
|
||||
try {
|
||||
await this.userRepository.update(user.id, updateData);
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
RequestTimeoutException,
|
||||
} from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { Repository } from 'typeorm';
|
||||
import { User } from '../entities/user.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { UpdateProfileDTO } from '../dto/update-profile.dto';
|
||||
import { UserProfileProvider } from './user-profile.provider';
|
||||
import { AppTimeoutException } from '@/common/exceptions/app-timeout.exception';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@@ -29,10 +26,7 @@ export class UsersService {
|
||||
try {
|
||||
user = await this.userRepository.findOneById(id);
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
@@ -46,10 +40,7 @@ export class UsersService {
|
||||
try {
|
||||
return await this.userRepository.findOneBy({ phone });
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,10 +50,7 @@ export class UsersService {
|
||||
try {
|
||||
await this.userRepository.save(newUser);
|
||||
} catch (err) {
|
||||
throw new RequestTimeoutException('common.errors.requestTimeout', {
|
||||
cause: err,
|
||||
description: 'Error connecting to the database',
|
||||
});
|
||||
throw new AppTimeoutException(err);
|
||||
}
|
||||
|
||||
return newUser;
|
||||
|
||||
Reference in New Issue
Block a user