mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-18 05:09:29 +00:00
add smart random
This commit is contained in:
@@ -2,7 +2,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Req, UseGuards } from '@ne
|
||||
import { CardsService } from './cards.service'
|
||||
import { UpdateCardDto } from './dto/update-card.dto'
|
||||
import { CommandBus } from '@nestjs/cqrs'
|
||||
import { DeleteDeckByIdCommand, GetDeckByIdCommand, UpdateDeckCommand } from './use-cases'
|
||||
import { DeleteCardByIdCommand, GetDeckByIdCommand, UpdateDeckCommand } from './use-cases'
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'
|
||||
|
||||
@Controller('cards')
|
||||
@@ -24,6 +24,6 @@ export class CardsController {
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Delete(':id')
|
||||
remove(@Param('id') id: string, @Req() req) {
|
||||
return this.commandBus.execute(new DeleteDeckByIdCommand(id, req.user.id))
|
||||
return this.commandBus.execute(new DeleteCardByIdCommand(id, req.user.id))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { Module } from '@nestjs/common'
|
||||
import { CardsService } from './cards.service'
|
||||
import { CardsController } from './cards.controller'
|
||||
import { CqrsModule } from '@nestjs/cqrs'
|
||||
import { DeleteDeckByIdHandler, GetDeckByIdHandler, UpdateDeckHandler } from './use-cases'
|
||||
import { DeleteCardByIdHandler, GetDeckByIdHandler, UpdateDeckHandler } from './use-cases'
|
||||
import { CardsRepository } from './infrastructure/cards.repository'
|
||||
|
||||
const commandHandlers = [GetDeckByIdHandler, DeleteDeckByIdHandler, UpdateDeckHandler]
|
||||
const commandHandlers = [GetDeckByIdHandler, DeleteCardByIdHandler, UpdateDeckHandler]
|
||||
|
||||
@Module({
|
||||
imports: [CqrsModule],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Length } from 'class-validator'
|
||||
import { PaginationDto } from '../../../infrastructure/common/pagination/pagination.dto'
|
||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators/is-optional-or-empty-string'
|
||||
import { IsOrderBy } from '../../../infrastructure/decorators/is-order-by-constraint'
|
||||
|
||||
export class GetAllCardsInDeckDto extends PaginationDto {
|
||||
@IsOptionalOrEmptyString()
|
||||
@@ -10,4 +11,7 @@ export class GetAllCardsInDeckDto extends PaginationDto {
|
||||
@IsOptionalOrEmptyString()
|
||||
@Length(1, 30)
|
||||
answer?: string
|
||||
|
||||
@IsOrderBy()
|
||||
orderBy?: string | null
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common
|
||||
import { PrismaService } from '../../../prisma.service'
|
||||
import { GetAllCardsInDeckDto } from '../dto/get-all-cards.dto'
|
||||
import { CreateCardDto } from '../dto/create-card.dto'
|
||||
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||
import { createPrismaOrderBy } from '../../../infrastructure/common/helpers/get-order-by-object'
|
||||
|
||||
@Injectable()
|
||||
export class CardsRepository {
|
||||
@@ -48,23 +50,56 @@ export class CardsRepository {
|
||||
|
||||
async findCardsByDeckId(
|
||||
deckId: string,
|
||||
{ answer = undefined, question = undefined, currentPage, itemsPerPage }: GetAllCardsInDeckDto
|
||||
{
|
||||
answer = undefined,
|
||||
question = undefined,
|
||||
currentPage,
|
||||
itemsPerPage,
|
||||
orderBy,
|
||||
}: GetAllCardsInDeckDto
|
||||
) {
|
||||
try {
|
||||
return await this.prisma.card.findMany({
|
||||
const where = {
|
||||
decks: {
|
||||
id: deckId,
|
||||
},
|
||||
question: {
|
||||
contains: question || undefined,
|
||||
},
|
||||
answer: {
|
||||
contains: answer || undefined,
|
||||
},
|
||||
}
|
||||
const result = await this.prisma.$transaction([
|
||||
this.prisma.card.count({ where }),
|
||||
this.prisma.card.findMany({
|
||||
orderBy: createPrismaOrderBy(orderBy) || { updated: 'desc' },
|
||||
where,
|
||||
skip: (currentPage - 1) * itemsPerPage,
|
||||
take: itemsPerPage,
|
||||
}),
|
||||
])
|
||||
return Pagination.transformPaginationData(result, { currentPage, itemsPerPage })
|
||||
} catch (e) {
|
||||
this.logger.error(e?.message)
|
||||
throw new InternalServerErrorException(e?.message)
|
||||
}
|
||||
}
|
||||
|
||||
async findCardsByDeckIdWithGrade(userId: string, deckId: string) {
|
||||
try {
|
||||
return this.prisma.card.findMany({
|
||||
where: {
|
||||
decks: {
|
||||
id: deckId,
|
||||
},
|
||||
question: {
|
||||
contains: question || undefined,
|
||||
},
|
||||
answer: {
|
||||
contains: answer || undefined,
|
||||
deckId,
|
||||
},
|
||||
include: {
|
||||
grades: {
|
||||
where: {
|
||||
userId,
|
||||
deckId,
|
||||
},
|
||||
},
|
||||
},
|
||||
skip: (currentPage - 1) * itemsPerPage,
|
||||
take: itemsPerPage,
|
||||
})
|
||||
} catch (e) {
|
||||
this.logger.error(e?.message)
|
||||
@@ -72,9 +107,9 @@ export class CardsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public async findDeckById(id: string) {
|
||||
public async findCardById(id: string) {
|
||||
try {
|
||||
return await this.prisma.deck.findUnique({
|
||||
return await this.prisma.card.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
@@ -85,12 +120,25 @@ export class CardsRepository {
|
||||
}
|
||||
}
|
||||
|
||||
public async deleteDeckById(id: string) {
|
||||
public async deleteCardById(id: string) {
|
||||
try {
|
||||
return await this.prisma.deck.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
return await this.prisma.$transaction(async tx => {
|
||||
const deleted = await tx.card.delete({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
await tx.deck.update({
|
||||
where: {
|
||||
id: deleted.deckId,
|
||||
},
|
||||
data: {
|
||||
cardsCount: {
|
||||
decrement: 1,
|
||||
},
|
||||
},
|
||||
})
|
||||
return deleted
|
||||
})
|
||||
} catch (e) {
|
||||
this.logger.error(e?.message)
|
||||
|
||||
21
src/modules/cards/use-cases/delete-card-by-id-use-case.ts
Normal file
21
src/modules/cards/use-cases/delete-card-by-id-use-case.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { CardsRepository } from '../infrastructure/cards.repository'
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common'
|
||||
|
||||
export class DeleteCardByIdCommand {
|
||||
constructor(public readonly id: string, public readonly userId: string) {}
|
||||
}
|
||||
|
||||
@CommandHandler(DeleteCardByIdCommand)
|
||||
export class DeleteCardByIdHandler implements ICommandHandler<DeleteCardByIdCommand> {
|
||||
constructor(private readonly cardsRepository: CardsRepository) {}
|
||||
|
||||
async execute(command: DeleteCardByIdCommand) {
|
||||
const card = await this.cardsRepository.findCardById(command.id)
|
||||
if (!card) throw new NotFoundException(`Card with id ${command.id} not found`)
|
||||
if (card.userId !== command.userId) {
|
||||
throw new BadRequestException(`You can't delete a card that you don't own`)
|
||||
}
|
||||
return await this.cardsRepository.deleteCardById(command.id)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { CardsRepository } from '../infrastructure/cards.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: CardsRepository) {}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,6 @@ export class GetDeckByIdHandler implements ICommandHandler<GetDeckByIdCommand> {
|
||||
constructor(private readonly deckRepository: CardsRepository) {}
|
||||
|
||||
async execute(command: GetDeckByIdCommand) {
|
||||
return await this.deckRepository.findDeckById(command.id)
|
||||
return await this.deckRepository.findCardById(command.id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from './get-deck-by-id-use-case'
|
||||
export * from './delete-deck-by-id-use-case'
|
||||
export * from './delete-card-by-id-use-case'
|
||||
export * from './update-deck-use-case'
|
||||
|
||||
@@ -16,7 +16,7 @@ export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
|
||||
constructor(private readonly deckRepository: CardsRepository) {}
|
||||
|
||||
async execute(command: UpdateDeckCommand) {
|
||||
const deck = await this.deckRepository.findDeckById(command.deckId)
|
||||
const deck = await this.deckRepository.findCardById(command.deckId)
|
||||
|
||||
if (!deck) throw new NotFoundException(`Deck with id ${command.deckId} not found`)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user