add duplicating random card prevention

This commit is contained in:
2023-08-07 14:13:07 +02:00
parent 663a93cbb6
commit fff0288659
5 changed files with 46 additions and 15 deletions

View File

@@ -44,7 +44,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
if (req.cookies && 'accessToken' in req.cookies && req.cookies.accessToken.length > 0) {
return req.cookies.accessToken
}
console.log(req.headers)
return null
}

View File

@@ -34,18 +34,19 @@ import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
import { Card, PaginatedCards } from '../cards/entities/cards.entity'
import { DecksService } from './decks.service'
import { UpdateDeckDto, CreateDeckDto, GetAllDecksDto } from './dto'
import { CreateDeckDto, GetAllDecksDto, UpdateDeckDto } from './dto'
import { GetRandomCardDto } from './dto/get-random-card.dto'
import { Deck, DeckWithAuthor, PaginatedDecks } from './entities/deck.entity'
import {
CreateCardCommand,
CreateDeckCommand,
DeleteDeckByIdCommand,
GetAllCardsInDeckCommand,
GetAllDecksCommand,
GetDeckByIdCommand,
UpdateDeckCommand,
GetRandomCardInDeckCommand,
SaveGradeCommand,
CreateCardCommand,
UpdateDeckCommand,
} from './use-cases'
@ApiTags('Decks')
@@ -172,23 +173,33 @@ export class DecksController {
summary: 'Retrieve a random card',
})
@Get(':id/learn')
findRandomCardInDeck(@Param('id') id: string, @Req() req): Promise<Card> {
return this.commandBus.execute(new GetRandomCardInDeckCommand(req.user.id, id))
findRandomCardInDeck(
@Param('id') id: string,
@Req() req,
@Query() query: GetRandomCardDto
): Promise<Card> {
return this.commandBus.execute(
new GetRandomCardInDeckCommand(req.user.id, id, query.previousCardId)
)
}
@UseGuards(JwtAuthGuard)
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
@ApiNotFoundResponse({ description: 'Card not found' })
@HttpCode(HttpStatus.NO_CONTENT)
@HttpCode(HttpStatus.OK)
@ApiNoContentResponse({ description: 'Grade saved' })
@Post(':id/learn')
@ApiOperation({
description: 'Save the grade of a card',
summary: 'Save the grade of a card',
})
saveGrade(@Param('id') id: string, @Req() req, @Body() body: SaveGradeDto): Promise<void> {
return this.commandBus.execute(
async saveGrade(@Param('id') id: string, @Req() req, @Body() body: SaveGradeDto) {
const saved = await this.commandBus.execute(
new SaveGradeCommand(req.user.id, { cardId: body.cardId, grade: body.grade })
)
return await this.commandBus.execute(
new GetRandomCardInDeckCommand(req.user.id, saved.deckId, saved.id)
)
}
}

View File

@@ -0,0 +1,6 @@
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
export class GetRandomCardDto {
@IsOptionalOrEmptyString()
previousCardId?: string
}

View File

@@ -8,7 +8,11 @@ import { CardsRepository } from '../../cards/infrastructure/cards.repository'
import { DecksRepository } from '../infrastructure/decks.repository'
export class GetRandomCardInDeckCommand {
constructor(public readonly userId: string, public readonly deckId: string) {}
constructor(
public readonly userId: string,
public readonly deckId: string,
public readonly previousCardId: string
) {}
}
type CardWithGrade = Prisma.cardGetPayload<{ include: { grades: true } }>
@@ -41,6 +45,19 @@ export class GetRandomCardInDeckHandler implements ICommandHandler<GetRandomCard
return selectionPool[Math.floor(Math.random() * selectionPool.length)]
}
private async getNotDuplicateRandomCard(
cards: Array<CardWithGrade>,
previousCardId: string
): Promise<Card> {
const randomCard = await this.getSmartRandomCard(cards)
if (randomCard.id === previousCardId) {
return this.getNotDuplicateRandomCard(cards, previousCardId)
}
return randomCard
}
async execute(command: GetRandomCardInDeckCommand) {
const deck = await this.decksRepository.findDeckById(command.deckId)
@@ -54,7 +71,7 @@ export class GetRandomCardInDeckHandler implements ICommandHandler<GetRandomCard
command.userId,
command.deckId
)
const smartRandomCard = await this.getSmartRandomCard(cards)
const smartRandomCard = await this.getNotDuplicateRandomCard(cards, command.previousCardId)
return pick(smartRandomCard, [
'id',

View File

@@ -1,7 +1,6 @@
import { ForbiddenException, NotFoundException } from '@nestjs/common'
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
import { DecksRepository } from '../infrastructure/decks.repository'
import { GradesRepository } from '../infrastructure/grades.repository'
@@ -18,12 +17,11 @@ export class SaveGradeCommand {
@CommandHandler(SaveGradeCommand)
export class SaveGradeHandler implements ICommandHandler<SaveGradeCommand> {
constructor(
private readonly cardsRepository: CardsRepository,
private readonly decksRepository: DecksRepository,
private readonly gradesRepository: GradesRepository
) {}
async execute(command: SaveGradeCommand): Promise<void> {
async execute(command: SaveGradeCommand) {
const deck = await this.decksRepository.findDeckByCardId(command.args.cardId)
if (!deck)
@@ -33,7 +31,7 @@ export class SaveGradeHandler implements ICommandHandler<SaveGradeCommand> {
throw new ForbiddenException(`You can't save cards to a private deck that you don't own`)
}
await this.gradesRepository.createGrade({
return await this.gradesRepository.createGrade({
userId: command.userId,
grade: command.args.grade,
cardId: command.args.cardId,