diff --git a/src/modules/auth/dto/min-max-cards.dto.ts b/src/modules/auth/dto/min-max-cards.dto.ts new file mode 100644 index 0000000..6c71725 --- /dev/null +++ b/src/modules/auth/dto/min-max-cards.dto.ts @@ -0,0 +1,29 @@ +import { ApiHideProperty } from '@nestjs/swagger' + +import { ApiSchema } from '../../../infrastructure/common/helpers/api-schema' +import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators' +import { IsUUIDOrCaller } from '../../../infrastructure/decorators/is-uuid-or-caller' + +@ApiSchema({ name: 'MinMaxCardsRequestArgs' }) +export class GetMinMaxCardsDto { + /** Search by deck name */ + @IsOptionalOrEmptyString() + name?: string + + /** Filter by deck authorId + * If ~caller is passed, it will be replaced with the current user's id + */ + @IsOptionalOrEmptyString() + @IsUUIDOrCaller() + authorId?: string + + /** Decks favorited by user + * If ~caller is passed, it will be replaced with the current user's id + * */ + @IsOptionalOrEmptyString() + @IsUUIDOrCaller() + favoritedBy?: string + + @ApiHideProperty() + userId?: string +} diff --git a/src/modules/decks/decks.controller.ts b/src/modules/decks/decks.controller.ts index 2ff2771..c4a66fc 100644 --- a/src/modules/decks/decks.controller.ts +++ b/src/modules/decks/decks.controller.ts @@ -31,6 +31,7 @@ import { import { Pagination } from '../../infrastructure/common/pagination/pagination.service' import { SaveGradeDto } from '../auth/dto' +import { GetMinMaxCardsDto } from '../auth/dto/min-max-cards.dto' import { JwtAuthGuard } from '../auth/guards' import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto' import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/cards.entity' @@ -110,8 +111,13 @@ export class DecksController { @UseGuards(JwtAuthGuard) @Version('2') @Get('min-max-cards') - findMinMaxCards(): Promise { - return this.commandBus.execute(new GetMinMaxCardsUseCaseCommand()) + findMinMaxCards(@Query() query: GetMinMaxCardsDto, @Req() req): Promise { + return this.commandBus.execute( + new GetMinMaxCardsUseCaseCommand({ + ...query, + userId: req.user.id, + }) + ) } @ApiConsumes('multipart/form-data') diff --git a/src/modules/decks/infrastructure/decks.repository.ts b/src/modules/decks/infrastructure/decks.repository.ts index 06551f9..22d2370 100644 --- a/src/modules/decks/infrastructure/decks.repository.ts +++ b/src/modules/decks/infrastructure/decks.repository.ts @@ -57,9 +57,61 @@ export class DecksRepository { } } - async findMinMaxCards(): Promise<{ min: number; max: number }> { - const result = await this.prisma - .$queryRaw`SELECT MAX(card_count) as "maxCardsCount", MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count FROM flashcards.deck LEFT JOIN "flashcards"."card" ON deck.id = card."deckId" GROUP BY deck.id) AS card_counts;` + async findMinMaxCards({ + name, + authorId, + favoritedBy, + userId, + }: { + name?: string + authorId?: string + favoritedBy?: string + userId?: string + }): Promise<{ min: number; max: number }> { + const conditions = [] + + if (name) conditions.push(`deck.name ILIKE ('%' || ? || '%')`) + if (authorId) conditions.push(`deck."userId" = ?`) + if (userId) + conditions.push( + `(deck."isPrivate" = FALSE OR (deck."isPrivate" = TRUE AND deck."userId" = ?))` + ) + if (favoritedBy) conditions.push(`fd."userId" = ?`) + const deckQueryParams = [ + userId, + ...(name ? [name] : []), + ...(authorId ? [authorId] : []), + ...(userId ? [userId] : []), + ...(favoritedBy ? [favoritedBy] : []), + ] + + console.log(conditions, deckQueryParams) + const query = ` + SELECT MAX(card_count) as "maxCardsCount", + MIN(card_count) as "minCardsCount" FROM (SELECT deck.id, COUNT(card.id) as card_count + FROM flashcards.deck + LEFT JOIN flashcards."favoriteDeck" AS fd ON deck."id" = fd."deckId" AND fd."userId" = $1 + LEFT JOIN + "flashcards"."card" ON deck.id = card."deckId" + ${ + conditions.length + ? `WHERE ${conditions + .map( + (condition, index) => + `${condition.replace( + '?', + `$${index + 2}` + )}` + ) + .join(' AND ')}` + : '' + } + GROUP BY deck.id + ) AS card_counts; +` + + console.log(query) + const result = await this.prisma.$queryRawUnsafe(query, ...deckQueryParams) return { max: Number(result[0].maxCardsCount), diff --git a/src/modules/decks/use-cases/get-all-decks-use-case-v1.ts b/src/modules/decks/use-cases/get-all-decks-use-case-v1.ts index 255718e..e0b0108 100644 --- a/src/modules/decks/use-cases/get-all-decks-use-case-v1.ts +++ b/src/modules/decks/use-cases/get-all-decks-use-case-v1.ts @@ -14,7 +14,7 @@ export class GetAllDecksV1Handler implements ICommandHandler { const decks = await this.deckRepository.findAllDecks(command.params) - const minMax = await this.deckRepository.findMinMaxCards() + const minMax = await this.deckRepository.findMinMaxCards({}) return { ...decks, maxCardsCount: minMax.max } } diff --git a/src/modules/decks/use-cases/get-min-max-cards-use-case.ts b/src/modules/decks/use-cases/get-min-max-cards-use-case.ts index 6756dd1..4bce297 100644 --- a/src/modules/decks/use-cases/get-min-max-cards-use-case.ts +++ b/src/modules/decks/use-cases/get-min-max-cards-use-case.ts @@ -1,17 +1,18 @@ import { CommandHandler, ICommandHandler } from '@nestjs/cqrs' +import { ResendVerificationEmailDto } from '../../auth/dto' import { MinMaxCards } from '../entities/min-max-cards.entity' import { DecksRepository } from '../infrastructure/decks.repository' export class GetMinMaxCardsUseCaseCommand { - constructor() {} + constructor(public readonly args: ResendVerificationEmailDto) {} } @CommandHandler(GetMinMaxCardsUseCaseCommand) export class GetMinMaxCardsUseCaseHandler implements ICommandHandler { constructor(private readonly deckRepository: DecksRepository) {} - async execute(): Promise { - return await this.deckRepository.findMinMaxCards() + async execute(command: GetMinMaxCardsUseCaseCommand): Promise { + return await this.deckRepository.findMinMaxCards(command.args) } }