add smart random

This commit is contained in:
2023-07-14 14:54:47 +02:00
parent 68942e904f
commit b14fb39009
25 changed files with 509 additions and 104 deletions

View File

@@ -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))
}
}

View File

@@ -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],

View File

@@ -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
}

View File

@@ -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)

View 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)
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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'

View File

@@ -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`)