From fff0288659e889d7cb563a7197ab72ea98406e86 Mon Sep 17 00:00:00 2001 From: Andres Date: Mon, 7 Aug 2023 14:13:07 +0200 Subject: [PATCH] add duplicating random card prevention --- src/modules/auth/strategies/jwt.strategy.ts | 1 - src/modules/decks/decks.controller.ts | 27 +++++++++++++------ src/modules/decks/dto/get-random-card.dto.ts | 6 +++++ .../get-random-card-in-deck-use-case.ts | 21 +++++++++++++-- .../decks/use-cases/save-grade-use-case.ts | 6 ++--- 5 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 src/modules/decks/dto/get-random-card.dto.ts diff --git a/src/modules/auth/strategies/jwt.strategy.ts b/src/modules/auth/strategies/jwt.strategy.ts index 5f8f904..4275526 100644 --- a/src/modules/auth/strategies/jwt.strategy.ts +++ b/src/modules/auth/strategies/jwt.strategy.ts @@ -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 } diff --git a/src/modules/decks/decks.controller.ts b/src/modules/decks/decks.controller.ts index ace7907..b9c909d 100644 --- a/src/modules/decks/decks.controller.ts +++ b/src/modules/decks/decks.controller.ts @@ -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 { - return this.commandBus.execute(new GetRandomCardInDeckCommand(req.user.id, id)) + findRandomCardInDeck( + @Param('id') id: string, + @Req() req, + @Query() query: GetRandomCardDto + ): Promise { + 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 { - 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) + ) } } diff --git a/src/modules/decks/dto/get-random-card.dto.ts b/src/modules/decks/dto/get-random-card.dto.ts new file mode 100644 index 0000000..fc194dc --- /dev/null +++ b/src/modules/decks/dto/get-random-card.dto.ts @@ -0,0 +1,6 @@ +import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators' + +export class GetRandomCardDto { + @IsOptionalOrEmptyString() + previousCardId?: string +} diff --git a/src/modules/decks/use-cases/get-random-card-in-deck-use-case.ts b/src/modules/decks/use-cases/get-random-card-in-deck-use-case.ts index 53467f0..9aa2f70 100644 --- a/src/modules/decks/use-cases/get-random-card-in-deck-use-case.ts +++ b/src/modules/decks/use-cases/get-random-card-in-deck-use-case.ts @@ -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, + previousCardId: string + ): Promise { + 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 { constructor( - private readonly cardsRepository: CardsRepository, private readonly decksRepository: DecksRepository, private readonly gradesRepository: GradesRepository ) {} - async execute(command: SaveGradeCommand): Promise { + async execute(command: SaveGradeCommand) { const deck = await this.decksRepository.findDeckByCardId(command.args.cardId) if (!deck) @@ -33,7 +31,7 @@ export class SaveGradeHandler implements ICommandHandler { 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,