mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-16 20:59:26 +00:00
feat: add get /decks v2 which does not return maxCardsCount. Add /decks/min-max-cards to use instead. /v1/decks is now deprecated
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Logger } from '@nestjs/common'
|
||||
import { Logger, VersioningType } from '@nestjs/common'
|
||||
import { NestFactory } from '@nestjs/core'
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'
|
||||
import * as cookieParser from 'cookie-parser'
|
||||
@@ -16,7 +16,10 @@ async function bootstrap() {
|
||||
})
|
||||
app.use(cookieParser())
|
||||
|
||||
app.setGlobalPrefix('v1')
|
||||
app.enableVersioning({
|
||||
type: VersioningType.URI,
|
||||
defaultVersion: '1',
|
||||
})
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Flashcards')
|
||||
.setDescription('Flashcards API')
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
UploadedFiles,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
Version,
|
||||
} from '@nestjs/common'
|
||||
import { CommandBus } from '@nestjs/cqrs'
|
||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||
@@ -35,14 +36,17 @@ import { Card, CardWithGrade, PaginatedCardsWithGrade } from '../cards/entities/
|
||||
|
||||
import { CreateDeckDto, GetAllDecksDto, UpdateDeckDto } from './dto'
|
||||
import { GetRandomCardDto } from './dto/get-random-card.dto'
|
||||
import { Deck, DeckWithAuthor, PaginatedDecks } from './entities/deck.entity'
|
||||
import { Deck, DeckWithAuthor, PaginatedDecksWithMaxCardsCount } from './entities/deck.entity'
|
||||
import { MinMaxCards } from './entities/min-max-cards.entity'
|
||||
import {
|
||||
CreateCardCommand,
|
||||
CreateDeckCommand,
|
||||
DeleteDeckByIdCommand,
|
||||
GetAllCardsInDeckCommand,
|
||||
GetAllDecksCommand,
|
||||
GetAllDecksV1Command,
|
||||
GetAllDecksV2Command,
|
||||
GetDeckByIdCommand,
|
||||
GetMinMaxCardsUseCaseCommand,
|
||||
GetRandomCardInDeckCommand,
|
||||
SaveGradeCommand,
|
||||
UpdateDeckCommand,
|
||||
@@ -54,14 +58,43 @@ export class DecksController {
|
||||
constructor(private commandBus: CommandBus) {}
|
||||
|
||||
@HttpCode(HttpStatus.PARTIAL_CONTENT)
|
||||
@ApiOperation({ description: 'Retrieve paginated decks list.', summary: 'Paginated decks list' })
|
||||
@ApiOperation({
|
||||
description: 'Deprecated. Use v2 in combination with /min-max-cards request',
|
||||
summary: 'Paginated decks list',
|
||||
deprecated: true,
|
||||
})
|
||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Get()
|
||||
findAll(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecks> {
|
||||
findAllV1(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||
const finalQuery = Pagination.getPaginationData(query)
|
||||
|
||||
return this.commandBus.execute(new GetAllDecksCommand({ ...finalQuery, userId: req.user.id }))
|
||||
return this.commandBus.execute(new GetAllDecksV1Command({ ...finalQuery, userId: req.user.id }))
|
||||
}
|
||||
|
||||
@HttpCode(HttpStatus.PARTIAL_CONTENT)
|
||||
@ApiOperation({ description: 'Retrieve paginated decks list.', summary: 'Paginated decks list' })
|
||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Version('2')
|
||||
@Get()
|
||||
findAllV2(@Query() query: GetAllDecksDto, @Req() req): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||
const finalQuery = Pagination.getPaginationData(query)
|
||||
|
||||
return this.commandBus.execute(new GetAllDecksV2Command({ ...finalQuery, userId: req.user.id }))
|
||||
}
|
||||
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
description: 'Retrieve the minimum and maximum amount of cards in a deck.',
|
||||
summary: 'Minimum and maximum amount of cards in a deck',
|
||||
})
|
||||
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Version('2')
|
||||
@Get('min-max-cards')
|
||||
findMinMaxCards(): Promise<MinMaxCards> {
|
||||
return this.commandBus.execute(new GetMinMaxCardsUseCaseCommand())
|
||||
}
|
||||
|
||||
@ApiConsumes('multipart/form-data')
|
||||
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
CreateDeckHandler,
|
||||
DeleteDeckByIdHandler,
|
||||
GetDeckByIdHandler,
|
||||
GetAllDecksHandler,
|
||||
GetAllDecksV1Handler,
|
||||
GetMinMaxCardsUseCaseHandler,
|
||||
GetAllDecksV2Handler,
|
||||
UpdateDeckHandler,
|
||||
GetAllCardsInDeckHandler,
|
||||
CreateCardHandler,
|
||||
@@ -22,10 +24,12 @@ import {
|
||||
|
||||
const commandHandlers = [
|
||||
CreateDeckHandler,
|
||||
GetAllDecksHandler,
|
||||
GetAllDecksV1Handler,
|
||||
GetAllDecksV2Handler,
|
||||
GetDeckByIdHandler,
|
||||
GetRandomCardInDeckHandler,
|
||||
DeleteDeckByIdHandler,
|
||||
GetMinMaxCardsUseCaseHandler,
|
||||
UpdateDeckHandler,
|
||||
GetAllCardsInDeckHandler,
|
||||
CreateCardHandler,
|
||||
|
||||
@@ -20,8 +20,13 @@ export class DeckAuthor {
|
||||
name: string
|
||||
}
|
||||
|
||||
export class PaginatedDecks {
|
||||
export class PaginatedDecksWithMaxCardsCount {
|
||||
items: DeckWithAuthor[]
|
||||
pagination: Pagination
|
||||
maxCardsCount: number
|
||||
}
|
||||
|
||||
export class PaginatedDecks {
|
||||
items: DeckWithAuthor[]
|
||||
pagination: Pagination
|
||||
}
|
||||
|
||||
4
src/modules/decks/entities/min-max-cards.entity.ts
Normal file
4
src/modules/decks/entities/min-max-cards.entity.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export class MinMaxCards {
|
||||
max: number
|
||||
min: number
|
||||
}
|
||||
@@ -57,6 +57,16 @@ 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 deck LEFT JOIN card ON deck.id = card.deckId GROUP BY deck.id) AS card_counts;`
|
||||
|
||||
return {
|
||||
max: Number(result[0].maxCardsCount),
|
||||
min: Number(result[0].minCardsCount),
|
||||
}
|
||||
}
|
||||
|
||||
async findAllDecks({
|
||||
name = undefined,
|
||||
authorId = undefined,
|
||||
@@ -190,8 +200,6 @@ export class DecksRepository {
|
||||
['authorId', 'authorName']
|
||||
)
|
||||
})
|
||||
const max = await this.prisma
|
||||
.$queryRaw`SELECT MAX(card_count) as maxCardsCount FROM (SELECT COUNT(*) as card_count FROM card GROUP BY deckId) AS card_counts;`
|
||||
|
||||
// Return the result with pagination data
|
||||
return {
|
||||
@@ -202,7 +210,6 @@ export class DecksRepository {
|
||||
itemsPerPage,
|
||||
totalPages: Math.ceil(total / itemsPerPage),
|
||||
},
|
||||
maxCardsCount: Number(max[0].maxCardsCount),
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(e?.message)
|
||||
|
||||
21
src/modules/decks/use-cases/get-all-decks-use-case-v1.ts
Normal file
21
src/modules/decks/use-cases/get-all-decks-use-case-v1.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { GetAllDecksDto } from '../dto'
|
||||
import { PaginatedDecksWithMaxCardsCount } from '../entities/deck.entity'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetAllDecksV1Command {
|
||||
constructor(public readonly params: GetAllDecksDto) {}
|
||||
}
|
||||
|
||||
@CommandHandler(GetAllDecksV1Command)
|
||||
export class GetAllDecksV1Handler implements ICommandHandler<GetAllDecksV1Command> {
|
||||
constructor(private readonly deckRepository: DecksRepository) {}
|
||||
|
||||
async execute(command: GetAllDecksV1Command): Promise<PaginatedDecksWithMaxCardsCount> {
|
||||
const decks = await this.deckRepository.findAllDecks(command.params)
|
||||
const minMax = await this.deckRepository.findMinMaxCards()
|
||||
|
||||
return { ...decks, maxCardsCount: minMax.max }
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,15 @@ import { GetAllDecksDto } from '../dto'
|
||||
import { PaginatedDecks } from '../entities/deck.entity'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetAllDecksCommand {
|
||||
export class GetAllDecksV2Command {
|
||||
constructor(public readonly params: GetAllDecksDto) {}
|
||||
}
|
||||
|
||||
@CommandHandler(GetAllDecksCommand)
|
||||
export class GetAllDecksHandler implements ICommandHandler<GetAllDecksCommand> {
|
||||
@CommandHandler(GetAllDecksV2Command)
|
||||
export class GetAllDecksV2Handler implements ICommandHandler<GetAllDecksV2Command> {
|
||||
constructor(private readonly deckRepository: DecksRepository) {}
|
||||
|
||||
async execute(command: GetAllDecksCommand): Promise<PaginatedDecks> {
|
||||
async execute(command: GetAllDecksV2Command): Promise<PaginatedDecks> {
|
||||
return await this.deckRepository.findAllDecks(command.params)
|
||||
}
|
||||
}
|
||||
17
src/modules/decks/use-cases/get-min-max-cards-use-case.ts
Normal file
17
src/modules/decks/use-cases/get-min-max-cards-use-case.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { MinMaxCards } from '../entities/min-max-cards.entity'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetMinMaxCardsUseCaseCommand {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@CommandHandler(GetMinMaxCardsUseCaseCommand)
|
||||
export class GetMinMaxCardsUseCaseHandler implements ICommandHandler<GetMinMaxCardsUseCaseCommand> {
|
||||
constructor(private readonly deckRepository: DecksRepository) {}
|
||||
|
||||
async execute(): Promise<MinMaxCards> {
|
||||
return await this.deckRepository.findMinMaxCards()
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@ export * from './create-card-use-case'
|
||||
export * from './create-deck-use-case'
|
||||
export * from './delete-deck-by-id-use-case'
|
||||
export * from './get-all-cards-in-deck-use-case'
|
||||
export * from './get-all-decks-use-case'
|
||||
export * from './get-all-decks-use-case-v1'
|
||||
export * from './get-all-decks-use-case-v2'
|
||||
export * from './get-deck-by-id-use-case'
|
||||
export * from './get-random-card-in-deck-use-case'
|
||||
export * from './save-grade-use-case'
|
||||
export * from './update-deck-use-case'
|
||||
export * from './get-min-max-cards-use-case'
|
||||
|
||||
Reference in New Issue
Block a user