mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-17 05:09:26 +00:00
add decks crud
This commit is contained in:
@@ -105,26 +105,20 @@ model Card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Deck {
|
model Deck {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
userName String
|
name String
|
||||||
name String
|
isPrivate Boolean @default(false)
|
||||||
private Boolean
|
shots Int @default(0)
|
||||||
path String
|
cover String?
|
||||||
grade Int
|
rating Int @default(0)
|
||||||
shots Int
|
isDeleted Boolean?
|
||||||
cardsCount Int
|
isBlocked Boolean?
|
||||||
deckCover String?
|
created DateTime @default(now())
|
||||||
type String
|
updated DateTime @updatedAt
|
||||||
rating Int
|
user User @relation(fields: [userId], references: [id])
|
||||||
moreId String?
|
Card Card[]
|
||||||
isDeleted Boolean?
|
Grade Grade[]
|
||||||
isBlocked Boolean?
|
|
||||||
created DateTime @default(now())
|
|
||||||
updated DateTime @updatedAt
|
|
||||||
user User @relation(fields: [userId], references: [id])
|
|
||||||
Card Card[] // One-to-many relation
|
|
||||||
Grade Grade[]
|
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { MailerModule } from '@nestjs-modules/mailer'
|
|||||||
import * as process from 'process'
|
import * as process from 'process'
|
||||||
import { JwtRefreshStrategy } from './modules/auth/strategies/jwt-refresh.strategy'
|
import { JwtRefreshStrategy } from './modules/auth/strategies/jwt-refresh.strategy'
|
||||||
import { CqrsModule } from '@nestjs/cqrs'
|
import { CqrsModule } from '@nestjs/cqrs'
|
||||||
|
import { DecksModule } from './modules/decks/decks.module'
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -15,6 +16,7 @@ import { CqrsModule } from '@nestjs/cqrs'
|
|||||||
ConfigModule,
|
ConfigModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
AuthModule,
|
AuthModule,
|
||||||
|
DecksModule,
|
||||||
PrismaModule,
|
PrismaModule,
|
||||||
|
|
||||||
MailerModule.forRoot({
|
MailerModule.forRoot({
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const DEFAULT_PAGE_SIZE = 10
|
||||||
|
export const DEFAULT_PAGE_NUMBER = 1
|
||||||
14
src/infrastructure/common/pagination/pagination.dto.ts
Normal file
14
src/infrastructure/common/pagination/pagination.dto.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { IsNumber, IsOptional } from 'class-validator'
|
||||||
|
import { Type } from 'class-transformer'
|
||||||
|
|
||||||
|
export class PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsNumber()
|
||||||
|
currentPage?: number
|
||||||
|
|
||||||
|
@Type(() => Number)
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
pageSize?: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { ValidateIf, ValidationOptions } from 'class-validator'
|
||||||
|
|
||||||
|
export function IsOptionalOrEmptyString(validationOptions?: ValidationOptions) {
|
||||||
|
return ValidateIf((obj, value) => {
|
||||||
|
return value !== null && value !== undefined && value !== ''
|
||||||
|
}, validationOptions)
|
||||||
|
}
|
||||||
18
src/main.ts
18
src/main.ts
@@ -1,9 +1,10 @@
|
|||||||
import { NestFactory } from '@nestjs/core'
|
import { NestFactory } from '@nestjs/core'
|
||||||
import { AppModule } from './app.module'
|
import { AppModule } from './app.module'
|
||||||
import { BadRequestException, Logger, ValidationPipe } from '@nestjs/common'
|
import { Logger } from '@nestjs/common'
|
||||||
import { HttpExceptionFilter } from './exception.filter'
|
import { HttpExceptionFilter } from './exception.filter'
|
||||||
import * as cookieParser from 'cookie-parser'
|
import * as cookieParser from 'cookie-parser'
|
||||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
||||||
|
import { pipesSetup } from './settings/pipes-setup'
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule)
|
const app = await NestFactory.create(AppModule)
|
||||||
@@ -15,18 +16,7 @@ async function bootstrap() {
|
|||||||
.build()
|
.build()
|
||||||
const document = SwaggerModule.createDocument(app, config)
|
const document = SwaggerModule.createDocument(app, config)
|
||||||
SwaggerModule.setup('docs', app, document)
|
SwaggerModule.setup('docs', app, document)
|
||||||
app.useGlobalPipes(
|
pipesSetup(app)
|
||||||
new ValidationPipe({
|
|
||||||
stopAtFirstError: false,
|
|
||||||
exceptionFactory: errors => {
|
|
||||||
const customErrors = errors.map(e => {
|
|
||||||
const firstError = JSON.stringify(e.constraints)
|
|
||||||
return { field: e.property, message: firstError }
|
|
||||||
})
|
|
||||||
throw new BadRequestException(customErrors)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
app.useGlobalFilters(new HttpExceptionFilter())
|
app.useGlobalFilters(new HttpExceptionFilter())
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
await app.listen(process.env.PORT || 3000)
|
await app.listen(process.env.PORT || 3000)
|
||||||
|
|||||||
60
src/modules/decks/decks.controller.ts
Normal file
60
src/modules/decks/decks.controller.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
Req,
|
||||||
|
Request,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common'
|
||||||
|
import { DecksService } from './decks.service'
|
||||||
|
import { CreateDeckDto } from './dto/create-deck.dto'
|
||||||
|
import { UpdateDeckDto } from './dto/update-deck.dto'
|
||||||
|
import { CommandBus } from '@nestjs/cqrs'
|
||||||
|
import { CreateDeckCommand } from './use-cases'
|
||||||
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'
|
||||||
|
import { GetAllDecksCommand } from './use-cases/get-all-decks-use-case'
|
||||||
|
import { GetAllDecksDto } from './dto/get-all-decks.dto'
|
||||||
|
import { GetDeckByIdCommand } from './use-cases/get-deck-by-id-use-case'
|
||||||
|
import { DeleteDeckByIdCommand } from './use-cases/delete-deck-by-id-use-case'
|
||||||
|
import { UpdateDeckCommand } from './use-cases/update-deck-use-case'
|
||||||
|
|
||||||
|
@Controller('decks')
|
||||||
|
export class DecksController {
|
||||||
|
constructor(private readonly decksService: DecksService, private commandBus: CommandBus) {}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Post()
|
||||||
|
create(@Request() req, @Body() createDeckDto: CreateDeckDto) {
|
||||||
|
const userId = req.user.id
|
||||||
|
return this.commandBus.execute(new CreateDeckCommand({ ...createDeckDto, userId: userId }))
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get()
|
||||||
|
findAll(@Query() query: GetAllDecksDto, @Req() req) {
|
||||||
|
return this.commandBus.execute(new GetAllDecksCommand({ ...query, userId: req.user.id }))
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Get(':id')
|
||||||
|
findOne(@Param('id') id: string) {
|
||||||
|
return this.commandBus.execute(new GetDeckByIdCommand(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Patch(':id')
|
||||||
|
update(@Param('id') id: string, @Body() updateDeckDto: UpdateDeckDto, @Req() req) {
|
||||||
|
return this.commandBus.execute(new UpdateDeckCommand(id, updateDeckDto, req.user.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
@Delete(':id')
|
||||||
|
remove(@Param('id') id: string, @Req() req) {
|
||||||
|
return this.commandBus.execute(new DeleteDeckByIdCommand(id, req.user.id))
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/modules/decks/decks.module.ts
Normal file
26
src/modules/decks/decks.module.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { DecksService } from './decks.service'
|
||||||
|
import { DecksController } from './decks.controller'
|
||||||
|
import { CqrsModule } from '@nestjs/cqrs'
|
||||||
|
import { CreateDeckHandler } from './use-cases'
|
||||||
|
import { DecksRepository } from './infrastructure/decks.repository'
|
||||||
|
import { GetAllDecksHandler } from './use-cases/get-all-decks-use-case'
|
||||||
|
import { GetDeckByIdHandler } from './use-cases/get-deck-by-id-use-case'
|
||||||
|
import { DeleteDeckByIdHandler } from './use-cases/delete-deck-by-id-use-case'
|
||||||
|
import { UpdateDeckHandler } from './use-cases/update-deck-use-case'
|
||||||
|
|
||||||
|
const commandHandlers = [
|
||||||
|
CreateDeckHandler,
|
||||||
|
GetAllDecksHandler,
|
||||||
|
GetDeckByIdHandler,
|
||||||
|
DeleteDeckByIdHandler,
|
||||||
|
UpdateDeckHandler,
|
||||||
|
]
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [CqrsModule],
|
||||||
|
controllers: [DecksController],
|
||||||
|
providers: [DecksService, DecksRepository, ...commandHandlers],
|
||||||
|
exports: [CqrsModule],
|
||||||
|
})
|
||||||
|
export class DecksModule {}
|
||||||
4
src/modules/decks/decks.service.ts
Normal file
4
src/modules/decks/decks.service.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { Injectable } from '@nestjs/common'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DecksService {}
|
||||||
16
src/modules/decks/dto/create-deck.dto.ts
Normal file
16
src/modules/decks/dto/create-deck.dto.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { IsBoolean, IsOptional, IsString, Length } from 'class-validator'
|
||||||
|
|
||||||
|
export class CreateDeckDto {
|
||||||
|
@Length(3, 30)
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsString()
|
||||||
|
cover?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsBoolean()
|
||||||
|
isPrivate?: boolean
|
||||||
|
|
||||||
|
userId: string
|
||||||
|
}
|
||||||
15
src/modules/decks/dto/get-all-decks.dto.ts
Normal file
15
src/modules/decks/dto/get-all-decks.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { IsOptional, IsUUID, Length } from 'class-validator'
|
||||||
|
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators/is-optional-or-empty-string'
|
||||||
|
import { PaginationDto } from '../../../infrastructure/common/pagination/pagination.dto'
|
||||||
|
|
||||||
|
export class GetAllDecksDto extends PaginationDto {
|
||||||
|
@IsOptional()
|
||||||
|
@Length(3, 30)
|
||||||
|
name?: string
|
||||||
|
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
|
@IsUUID(4)
|
||||||
|
authorId?: string
|
||||||
|
|
||||||
|
userId: string
|
||||||
|
}
|
||||||
16
src/modules/decks/dto/update-deck.dto.ts
Normal file
16
src/modules/decks/dto/update-deck.dto.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { PartialType } from '@nestjs/mapped-types'
|
||||||
|
import { CreateDeckDto } from './create-deck.dto'
|
||||||
|
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators/is-optional-or-empty-string'
|
||||||
|
import { IsBoolean } from 'class-validator'
|
||||||
|
|
||||||
|
export class UpdateDeckDto extends PartialType(CreateDeckDto) {
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
|
name: string
|
||||||
|
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
|
@IsBoolean()
|
||||||
|
isPrivate: boolean
|
||||||
|
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
|
cover: string
|
||||||
|
}
|
||||||
1
src/modules/decks/entities/deck.entity.ts
Normal file
1
src/modules/decks/entities/deck.entity.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export class Deck {}
|
||||||
126
src/modules/decks/infrastructure/decks.repository.ts
Normal file
126
src/modules/decks/infrastructure/decks.repository.ts
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||||
|
import { PrismaService } from '../../../prisma.service'
|
||||||
|
import { GetAllDecksDto } from '../dto/get-all-decks.dto'
|
||||||
|
import {
|
||||||
|
DEFAULT_PAGE_NUMBER,
|
||||||
|
DEFAULT_PAGE_SIZE,
|
||||||
|
} from '../../../infrastructure/common/pagination/pagination.constants'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DecksRepository {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
private readonly logger = new Logger(DecksRepository.name)
|
||||||
|
async createDeck({
|
||||||
|
name,
|
||||||
|
userId,
|
||||||
|
cover,
|
||||||
|
isPrivate,
|
||||||
|
}: {
|
||||||
|
name: string
|
||||||
|
userId: string
|
||||||
|
cover?: string
|
||||||
|
isPrivate?: boolean
|
||||||
|
}) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.deck.create({
|
||||||
|
data: {
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
name,
|
||||||
|
cover,
|
||||||
|
isPrivate,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e?.message)
|
||||||
|
throw new InternalServerErrorException(e?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async findAllDecks({
|
||||||
|
name = undefined,
|
||||||
|
authorId = undefined,
|
||||||
|
userId,
|
||||||
|
currentPage = DEFAULT_PAGE_NUMBER,
|
||||||
|
pageSize = DEFAULT_PAGE_SIZE,
|
||||||
|
}: GetAllDecksDto) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.deck.findMany({
|
||||||
|
where: {
|
||||||
|
name: {
|
||||||
|
contains: name,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
id: authorId || undefined,
|
||||||
|
},
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
isPrivate: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
userId: userId,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
isPrivate: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
skip: (currentPage - 1) * pageSize,
|
||||||
|
take: pageSize,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e?.message)
|
||||||
|
throw new InternalServerErrorException(e?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public async findDeckById(id: string) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.deck.findUnique({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e?.message)
|
||||||
|
throw new InternalServerErrorException(e?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteDeckById(id: string) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.deck.delete({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e?.message)
|
||||||
|
throw new InternalServerErrorException(e?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateDeckById(
|
||||||
|
id: string,
|
||||||
|
data: { name?: string; cover?: string; isPrivate?: boolean }
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return await this.prisma.deck.update({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data,
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(e?.message)
|
||||||
|
throw new InternalServerErrorException(e?.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/modules/decks/use-cases/create-deck-use-case.ts
Normal file
16
src/modules/decks/use-cases/create-deck-use-case.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
import { CreateDeckDto } from '../dto/create-deck.dto'
|
||||||
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
|
||||||
|
export class CreateDeckCommand {
|
||||||
|
constructor(public readonly deck: CreateDeckDto) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler(CreateDeckCommand)
|
||||||
|
export class CreateDeckHandler implements ICommandHandler<CreateDeckCommand> {
|
||||||
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
|
async execute(command: CreateDeckCommand) {
|
||||||
|
return await this.deckRepository.createDeck(command.deck)
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/modules/decks/use-cases/delete-deck-by-id-use-case.ts
Normal file
21
src/modules/decks/use-cases/delete-deck-by-id-use-case.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
import { BadRequestException, NotFoundException } from '@nestjs/common'
|
||||||
|
|
||||||
|
export class DeleteDeckByIdCommand {
|
||||||
|
constructor(public readonly id: string, public readonly userId: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler(DeleteDeckByIdCommand)
|
||||||
|
export class DeleteDeckByIdHandler implements ICommandHandler<DeleteDeckByIdCommand> {
|
||||||
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
|
async execute(command: DeleteDeckByIdCommand) {
|
||||||
|
const deck = await this.deckRepository.findDeckById(command.id)
|
||||||
|
if (!deck) throw new NotFoundException(`Deck with id ${command.id} not found`)
|
||||||
|
if (deck.userId !== command.userId) {
|
||||||
|
throw new BadRequestException(`You can't delete a deck that you don't own`)
|
||||||
|
}
|
||||||
|
return await this.deckRepository.deleteDeckById(command.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/modules/decks/use-cases/get-all-decks-use-case.ts
Normal file
16
src/modules/decks/use-cases/get-all-decks-use-case.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
import { GetAllDecksDto } from '../dto/get-all-decks.dto'
|
||||||
|
|
||||||
|
export class GetAllDecksCommand {
|
||||||
|
constructor(public readonly params: GetAllDecksDto) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler(GetAllDecksCommand)
|
||||||
|
export class GetAllDecksHandler implements ICommandHandler<GetAllDecksCommand> {
|
||||||
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
|
async execute(command: GetAllDecksCommand) {
|
||||||
|
return await this.deckRepository.findAllDecks(command.params)
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/modules/decks/use-cases/get-deck-by-id-use-case.ts
Normal file
15
src/modules/decks/use-cases/get-deck-by-id-use-case.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
|
||||||
|
export class GetDeckByIdCommand {
|
||||||
|
constructor(public readonly id: string) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler(GetDeckByIdCommand)
|
||||||
|
export class GetDeckByIdHandler implements ICommandHandler<GetDeckByIdCommand> {
|
||||||
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
|
async execute(command: GetDeckByIdCommand) {
|
||||||
|
return await this.deckRepository.findDeckById(command.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/modules/decks/use-cases/index.ts
Normal file
1
src/modules/decks/use-cases/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './create-deck-use-case'
|
||||||
27
src/modules/decks/use-cases/update-deck-use-case.ts
Normal file
27
src/modules/decks/use-cases/update-deck-use-case.ts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||||
|
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||||
|
import { UpdateDeckDto } from '../dto/update-deck.dto'
|
||||||
|
import { BadRequestException, NotFoundException } from '@nestjs/common'
|
||||||
|
|
||||||
|
export class UpdateDeckCommand {
|
||||||
|
constructor(
|
||||||
|
public readonly deckId: string,
|
||||||
|
public readonly deck: UpdateDeckDto,
|
||||||
|
public readonly userId: string
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@CommandHandler(UpdateDeckCommand)
|
||||||
|
export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
|
||||||
|
constructor(private readonly deckRepository: DecksRepository) {}
|
||||||
|
|
||||||
|
async execute(command: UpdateDeckCommand) {
|
||||||
|
const deck = await this.deckRepository.findDeckById(command.deckId)
|
||||||
|
if (!deck) throw new NotFoundException(`Deck with id ${command.deckId} not found`)
|
||||||
|
if (deck.userId !== command.userId) {
|
||||||
|
throw new BadRequestException(`You can't change a deck that you don't own`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.deckRepository.updateDeckById(command.deckId, command.deck)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
} from '@nestjs/common'
|
} from '@nestjs/common'
|
||||||
import { UsersService } from '../services/users.service'
|
import { UsersService } from '../services/users.service'
|
||||||
import { CreateUserDto } from '../dto/create-user.dto'
|
import { CreateUserDto } from '../dto/create-user.dto'
|
||||||
import { Pagination } from '../../../infrastructure/common/pagination.service'
|
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||||
import { BaseAuthGuard } from '../../auth/guards/base-auth.guard'
|
import { BaseAuthGuard } from '../../auth/guards/base-auth.guard'
|
||||||
import { CommandBus } from '@nestjs/cqrs'
|
import { CommandBus } from '@nestjs/cqrs'
|
||||||
import { CreateUserCommand } from '../../auth/use-cases'
|
import { CreateUserCommand } from '../../auth/use-cases'
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export function pipesSetup(app: INestApplication) {
|
|||||||
new ValidationPipe({
|
new ValidationPipe({
|
||||||
whitelist: true,
|
whitelist: true,
|
||||||
transform: true,
|
transform: true,
|
||||||
|
forbidNonWhitelisted: true,
|
||||||
|
|
||||||
stopAtFirstError: true,
|
stopAtFirstError: true,
|
||||||
exceptionFactory: (errors: ValidationError[]) => {
|
exceptionFactory: (errors: ValidationError[]) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user