mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-27 05:09:26 +00:00
add auth docs, add eslint import/order
This commit is contained in:
@@ -13,9 +13,16 @@ import {
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common'
|
||||
import { CommandBus } from '@nestjs/cqrs'
|
||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||
import { ApiTags } from '@nestjs/swagger'
|
||||
|
||||
import { Pagination } from '../../infrastructure/common/pagination/pagination.service'
|
||||
import { JwtAuthGuard } from '../auth/guards'
|
||||
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
|
||||
|
||||
import { DecksService } from './decks.service'
|
||||
import { UpdateDeckDto, CreateDeckDto, GetAllDecksDto } from './dto'
|
||||
import { CommandBus } from '@nestjs/cqrs'
|
||||
import {
|
||||
CreateDeckCommand,
|
||||
DeleteDeckByIdCommand,
|
||||
@@ -27,11 +34,8 @@ import {
|
||||
SaveGradeCommand,
|
||||
CreateCardCommand,
|
||||
} from './use-cases'
|
||||
import { JwtAuthGuard } from '../auth/guards'
|
||||
import { CreateCardDto, GetAllCardsInDeckDto } from '../cards/dto'
|
||||
import { Pagination } from '../../infrastructure/common/pagination/pagination.service'
|
||||
import { FileFieldsInterceptor } from '@nestjs/platform-express'
|
||||
|
||||
@ApiTags('Decks')
|
||||
@Controller('decks')
|
||||
export class DecksController {
|
||||
constructor(private readonly decksService: DecksService, private commandBus: CommandBus) {}
|
||||
@@ -48,6 +52,7 @@ export class DecksController {
|
||||
@Body() createDeckDto: CreateDeckDto
|
||||
) {
|
||||
const userId = req.user.id
|
||||
|
||||
return this.commandBus.execute(
|
||||
new CreateDeckCommand({ ...createDeckDto, userId: userId }, files?.cover?.[0])
|
||||
)
|
||||
@@ -57,6 +62,7 @@ export class DecksController {
|
||||
@Get()
|
||||
findAll(@Query() query: GetAllDecksDto, @Req() req) {
|
||||
const finalQuery = Pagination.getPaginationData(query)
|
||||
|
||||
return this.commandBus.execute(new GetAllDecksCommand({ ...finalQuery, userId: req.user.id }))
|
||||
}
|
||||
|
||||
@@ -70,6 +76,7 @@ export class DecksController {
|
||||
@Get(':id/cards')
|
||||
findCardsInDeck(@Param('id') id: string, @Req() req, @Query() query: GetAllCardsInDeckDto) {
|
||||
const finalQuery = Pagination.getPaginationData(query)
|
||||
|
||||
return this.commandBus.execute(new GetAllCardsInDeckCommand(req.user.id, id, finalQuery))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { Module } from '@nestjs/common'
|
||||
import { DecksService } from './decks.service'
|
||||
import { DecksController } from './decks.controller'
|
||||
import { CqrsModule } from '@nestjs/cqrs'
|
||||
|
||||
import { FileUploadService } from '../../infrastructure/file-upload-service/file-upload.service'
|
||||
import { CardsRepository } from '../cards/infrastructure/cards.repository'
|
||||
|
||||
import { DecksController } from './decks.controller'
|
||||
import { DecksService } from './decks.service'
|
||||
import { DecksRepository } from './infrastructure/decks.repository'
|
||||
import { GradesRepository } from './infrastructure/grades.repository'
|
||||
import {
|
||||
CreateDeckHandler,
|
||||
DeleteDeckByIdHandler,
|
||||
@@ -13,10 +19,6 @@ import {
|
||||
SaveGradeHandler,
|
||||
GetRandomCardInDeckHandler,
|
||||
} from './use-cases'
|
||||
import { DecksRepository } from './infrastructure/decks.repository'
|
||||
import { CardsRepository } from '../cards/infrastructure/cards.repository'
|
||||
import { GradesRepository } from './infrastructure/grades.repository'
|
||||
import { FileUploadService } from '../../infrastructure/file-upload-service/file-upload.service'
|
||||
|
||||
const commandHandlers = [
|
||||
CreateDeckHandler,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IsBoolean, IsOptional, Length } from 'class-validator'
|
||||
import { Transform } from 'class-transformer'
|
||||
import { IsBoolean, IsOptional, Length } from 'class-validator'
|
||||
|
||||
export class CreateDeckDto {
|
||||
@Length(3, 30)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { IsUUID } from 'class-validator'
|
||||
import { IsOptionalOrEmptyString, IsOrderBy } from '../../../infrastructure/decorators'
|
||||
|
||||
import { PaginationDto } from '../../../infrastructure/common/pagination/pagination.dto'
|
||||
import { IsOptionalOrEmptyString, IsOrderBy } from '../../../infrastructure/decorators'
|
||||
|
||||
export class GetAllDecksDto extends PaginationDto {
|
||||
@IsOptionalOrEmptyString()
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { PartialType } from '@nestjs/mapped-types'
|
||||
import { CreateDeckDto } from './create-deck.dto'
|
||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
|
||||
import { IsBoolean } from 'class-validator'
|
||||
|
||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators'
|
||||
|
||||
import { CreateDeckDto } from './create-deck.dto'
|
||||
|
||||
export class UpdateDeckDto extends PartialType(CreateDeckDto) {
|
||||
@IsOptionalOrEmptyString()
|
||||
name: string
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||
|
||||
import { createPrismaOrderBy } from '../../../infrastructure/common/helpers/get-order-by-object'
|
||||
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||
import { PrismaService } from '../../../prisma.service'
|
||||
import { GetAllDecksDto } from '../dto'
|
||||
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||
import { createPrismaOrderBy } from '../../../infrastructure/common/helpers/get-order-by-object'
|
||||
|
||||
@Injectable()
|
||||
export class DecksRepository {
|
||||
@@ -103,6 +104,7 @@ export class DecksRepository {
|
||||
this.prisma
|
||||
.$queryRaw`SELECT MAX(card_count) as maxCardsCount FROM (SELECT COUNT(*) as card_count FROM card GROUP BY deckId) AS card_counts;`,
|
||||
])
|
||||
|
||||
return {
|
||||
maxCardsCount: Number(max[0].maxCardsCount),
|
||||
...Pagination.transformPaginationData([count, items], { currentPage, itemsPerPage }),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||
|
||||
import { PrismaService } from '../../../prisma.service'
|
||||
|
||||
@Injectable()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { FileUploadService } from '../../../infrastructure/file-upload-service/file-upload.service'
|
||||
import { CreateCardDto } from '../../cards/dto'
|
||||
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
|
||||
import { FileUploadService } from '../../../infrastructure/file-upload-service/file-upload.service'
|
||||
|
||||
export class CreateCardCommand {
|
||||
constructor(
|
||||
@@ -34,6 +35,7 @@ export class CreateCardHandler implements ICommandHandler<CreateCardCommand> {
|
||||
)
|
||||
|
||||
const result = await Promise.all([addQuestionImagePromise, addAnswerImagePromise])
|
||||
|
||||
questionImg = result[0].fileUrl
|
||||
answerImg = result[1].fileUrl
|
||||
} else if (command.answerImg) {
|
||||
@@ -42,6 +44,7 @@ export class CreateCardHandler implements ICommandHandler<CreateCardCommand> {
|
||||
command.answerImg?.originalname
|
||||
)
|
||||
const result = await addAnswerImagePromise
|
||||
|
||||
answerImg = result.fileUrl
|
||||
} else if (command.questionImg) {
|
||||
const addQuestionImagePromise = this.fileUploadService.uploadFile(
|
||||
@@ -49,6 +52,7 @@ export class CreateCardHandler implements ICommandHandler<CreateCardCommand> {
|
||||
command.questionImg?.originalname
|
||||
)
|
||||
const result = await addQuestionImagePromise
|
||||
|
||||
questionImg = result.fileUrl
|
||||
}
|
||||
if (command.card.questionImg === '') {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { FileUploadService } from '../../../infrastructure/file-upload-service/file-upload.service'
|
||||
import { CreateDeckDto } from '../dto'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
import { FileUploadService } from '../../../infrastructure/file-upload-service/file-upload.service'
|
||||
|
||||
export class CreateDeckCommand {
|
||||
constructor(public readonly deck: CreateDeckDto, public readonly cover: Express.Multer.File) {}
|
||||
@@ -22,6 +23,7 @@ export class CreateDeckHandler implements ICommandHandler<CreateDeckCommand> {
|
||||
command.cover.buffer,
|
||||
command.cover.originalname
|
||||
)
|
||||
|
||||
cover = result.fileUrl
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common'
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class DeleteDeckByIdCommand {
|
||||
constructor(public readonly id: string, public readonly userId: string) {}
|
||||
@@ -12,10 +13,12 @@ export class DeleteDeckByIdHandler implements ICommandHandler<DeleteDeckByIdComm
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
|
||||
import { GetAllCardsInDeckDto } from '../../cards/dto'
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common'
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { GetAllCardsInDeckDto } from '../../cards/dto'
|
||||
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetAllCardsInDeckCommand {
|
||||
@@ -21,6 +22,7 @@ export class GetAllCardsInDeckHandler implements ICommandHandler<GetAllCardsInDe
|
||||
|
||||
async execute(command: GetAllCardsInDeckCommand) {
|
||||
const deck = await this.decksRepository.findDeckById(command.deckId)
|
||||
|
||||
if (!deck) throw new NotFoundException(`Deck with id ${command.deckId} not found`)
|
||||
|
||||
if (deck.userId !== command.userId && deck.isPrivate) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
import { GetAllDecksDto } from '../dto'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetAllDecksCommand {
|
||||
constructor(public readonly params: GetAllDecksDto) {}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class GetDeckByIdCommand {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
|
||||
import { ForbiddenException, NotFoundException } from '@nestjs/common'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { pick } from 'remeda'
|
||||
|
||||
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) {}
|
||||
}
|
||||
@@ -20,6 +21,7 @@ export class GetRandomCardInDeckHandler implements ICommandHandler<GetRandomCard
|
||||
|
||||
private async getSmartRandomCard(cards: Array<CardWithGrade>) {
|
||||
const selectionPool: Array<CardWithGrade> = []
|
||||
|
||||
cards.forEach(card => {
|
||||
// Calculate the average grade for the card
|
||||
const averageGrade =
|
||||
@@ -40,6 +42,7 @@ export class GetRandomCardInDeckHandler implements ICommandHandler<GetRandomCard
|
||||
|
||||
async execute(command: GetRandomCardInDeckCommand) {
|
||||
const deck = await this.decksRepository.findDeckById(command.deckId)
|
||||
|
||||
if (!deck) throw new NotFoundException(`Deck with id ${command.deckId} not found`)
|
||||
|
||||
if (deck.userId !== command.userId && deck.isPrivate) {
|
||||
@@ -51,6 +54,7 @@ export class GetRandomCardInDeckHandler implements ICommandHandler<GetRandomCard
|
||||
command.deckId
|
||||
)
|
||||
const smartRandomCard = await this.getSmartRandomCard(cards)
|
||||
|
||||
return pick(smartRandomCard, ['id', 'question', 'answer', 'deckId'])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { CardsRepository } from '../../cards/infrastructure/cards.repository'
|
||||
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'
|
||||
|
||||
@@ -24,6 +25,7 @@ export class SaveGradeHandler implements ICommandHandler<SaveGradeCommand> {
|
||||
|
||||
async execute(command: SaveGradeCommand) {
|
||||
const deck = await this.decksRepository.findDeckByCardId(command.args.cardId)
|
||||
|
||||
if (!deck)
|
||||
throw new NotFoundException(`Deck containing card with id ${command.args.cardId} not found`)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
import { UpdateDeckDto } from '../dto'
|
||||
import { BadRequestException, NotFoundException } from '@nestjs/common'
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
|
||||
|
||||
import { FileUploadService } from '../../../infrastructure/file-upload-service/file-upload.service'
|
||||
import { UpdateDeckDto } from '../dto'
|
||||
import { DecksRepository } from '../infrastructure/decks.repository'
|
||||
|
||||
export class UpdateDeckCommand {
|
||||
constructor(
|
||||
@@ -22,6 +23,7 @@ export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
|
||||
|
||||
async execute(command: UpdateDeckCommand) {
|
||||
const deck = await this.deckRepository.findDeckById(command.deckId)
|
||||
|
||||
if (!deck) {
|
||||
throw new NotFoundException(`Deck with id ${command.deckId} not found`)
|
||||
}
|
||||
@@ -36,10 +38,12 @@ export class UpdateDeckHandler implements ICommandHandler<UpdateDeckCommand> {
|
||||
command.cover.buffer,
|
||||
command.cover.originalname
|
||||
)
|
||||
|
||||
cover = result.fileUrl
|
||||
} else if (command.deck.cover === '') {
|
||||
cover = null
|
||||
}
|
||||
|
||||
return await this.deckRepository.updateDeckById(command.deckId, { ...command.deck, cover })
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user