add auth docs, add eslint import/order

This commit is contained in:
andres
2023-07-16 19:44:58 +02:00
parent 0f3e89900a
commit aa7ece41a9
74 changed files with 1152 additions and 164 deletions

View File

@@ -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))
}

View File

@@ -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,

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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 }),

View File

@@ -1,4 +1,5 @@
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
import { PrismaService } from '../../../prisma.service'
@Injectable()

View File

@@ -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 === '') {

View File

@@ -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
}

View File

@@ -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)
}
}

View File

@@ -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) {

View File

@@ -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) {}

View File

@@ -1,4 +1,5 @@
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'
import { DecksRepository } from '../infrastructure/decks.repository'
export class GetDeckByIdCommand {

View File

@@ -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'])
}
}

View File

@@ -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`)

View File

@@ -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 })
}
}