mirror of
https://github.com/ershisan99/flashcards-api.git
synced 2025-12-17 12:33:22 +00:00
fix pagination and create pagination service
This commit is contained in:
@@ -9,19 +9,19 @@ generator client {
|
|||||||
previewFeatures = ["fullTextSearch", "fullTextIndex"]
|
previewFeatures = ["fullTextSearch", "fullTextIndex"]
|
||||||
}
|
}
|
||||||
|
|
||||||
model Verification {
|
model verification {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String @unique
|
userId String @unique
|
||||||
isEmailVerified Boolean @default(false)
|
isEmailVerified Boolean @default(false)
|
||||||
verificationToken String? @unique @default(uuid())
|
verificationToken String? @unique @default(uuid())
|
||||||
verificationTokenExpiry DateTime?
|
verificationTokenExpiry DateTime?
|
||||||
verificationEmailsSent Int @default(0)
|
verificationEmailsSent Int @default(0)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model User {
|
model user {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
email String @unique
|
email String @unique
|
||||||
password String
|
password String
|
||||||
@@ -34,51 +34,51 @@ model User {
|
|||||||
deleteTime Int?
|
deleteTime Int?
|
||||||
created DateTime @default(now())
|
created DateTime @default(now())
|
||||||
updated DateTime @updatedAt
|
updated DateTime @updatedAt
|
||||||
cards Card[]
|
cards card[]
|
||||||
decks Deck[]
|
decks deck[]
|
||||||
grades Grade[]
|
grades grade[]
|
||||||
generalChatMessages GeneralChatMessage[]
|
generalChatMessages GeneralChatMessage[]
|
||||||
verification Verification?
|
verification verification?
|
||||||
RevokedToken RevokedToken[]
|
revokedToken revokedToken[]
|
||||||
RefreshToken RefreshToken[]
|
RefreshToken refreshToken[]
|
||||||
ResetPassword ResetPassword?
|
resetPassword resetPassword?
|
||||||
|
|
||||||
@@fulltext([name, email])
|
@@fulltext([name, email])
|
||||||
}
|
}
|
||||||
|
|
||||||
model RevokedToken {
|
model revokedToken {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
token String @unique
|
token String @unique
|
||||||
revokedAt DateTime @default(now())
|
revokedAt DateTime @default(now())
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model RefreshToken {
|
model refreshToken {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
token String @unique @db.VarChar(255)
|
token String @unique @db.VarChar(255)
|
||||||
expiresAt DateTime
|
expiresAt DateTime
|
||||||
isRevoked Boolean @default(false)
|
isRevoked Boolean @default(false)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model ResetPassword {
|
model resetPassword {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String @unique
|
userId String @unique
|
||||||
resetPasswordToken String? @unique @default(uuid())
|
resetPasswordToken String? @unique @default(uuid())
|
||||||
resetPasswordTokenExpiry DateTime?
|
resetPasswordTokenExpiry DateTime?
|
||||||
resetPasswordEmailsSent Int @default(0)
|
resetPasswordEmailsSent Int @default(0)
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Card {
|
model card {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
deckId String
|
deckId String
|
||||||
userId String
|
userId String
|
||||||
@@ -96,15 +96,15 @@ model Card {
|
|||||||
moreId String?
|
moreId String?
|
||||||
created DateTime @default(now())
|
created DateTime @default(now())
|
||||||
updated DateTime @updatedAt
|
updated DateTime @updatedAt
|
||||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
author user @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
decks Deck @relation(fields: [deckId], references: [id], onDelete: Cascade)
|
decks deck @relation(fields: [deckId], references: [id], onDelete: Cascade)
|
||||||
grades Grade[]
|
grades grade[]
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@index([deckId])
|
@@index([deckId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Deck {
|
model deck {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
userId String
|
userId String
|
||||||
name String
|
name String
|
||||||
@@ -116,14 +116,15 @@ model Deck {
|
|||||||
isBlocked Boolean?
|
isBlocked Boolean?
|
||||||
created DateTime @default(now())
|
created DateTime @default(now())
|
||||||
updated DateTime @updatedAt
|
updated DateTime @updatedAt
|
||||||
user User @relation(fields: [userId], references: [id])
|
author user @relation(fields: [userId], references: [id])
|
||||||
Card Card[]
|
cardsCount Int @default(0)
|
||||||
Grade Grade[]
|
card card[]
|
||||||
|
grade grade[]
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|
||||||
model Grade {
|
model grade {
|
||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
deckId String
|
deckId String
|
||||||
cardId String
|
cardId String
|
||||||
@@ -133,9 +134,9 @@ model Grade {
|
|||||||
moreId String?
|
moreId String?
|
||||||
created DateTime @default(now())
|
created DateTime @default(now())
|
||||||
updated DateTime @updatedAt
|
updated DateTime @updatedAt
|
||||||
user User @relation(fields: [userId], references: [id])
|
user user @relation(fields: [userId], references: [id])
|
||||||
card Card @relation(fields: [cardId], references: [id])
|
card card @relation(fields: [cardId], references: [id])
|
||||||
decks Deck @relation(fields: [deckId], references: [id])
|
decks deck @relation(fields: [deckId], references: [id])
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@index([deckId])
|
@@index([deckId])
|
||||||
@@ -151,7 +152,7 @@ model GeneralChatMessage {
|
|||||||
message String
|
message String
|
||||||
created DateTime @default(now())
|
created DateTime @default(now())
|
||||||
updated DateTime @updatedAt
|
updated DateTime @updatedAt
|
||||||
user User @relation(fields: [userId], references: [id])
|
user user @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
|
|||||||
8
src/infrastructure/common/helpers/set-count-key.ts
Normal file
8
src/infrastructure/common/helpers/set-count-key.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { omit } from 'remeda'
|
||||||
|
|
||||||
|
export const setCountKey = <K extends string, L extends string>(key: K, newKey: L) => {
|
||||||
|
return <T extends Record<string, any>>(obj: T) => {
|
||||||
|
obj[newKey] = obj['_count'][key]
|
||||||
|
return omit(obj, ['_count']) as Omit<T, '_count'> & { [P in L]: number }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,5 +10,5 @@ export class PaginationDto {
|
|||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsNumber()
|
@IsNumber()
|
||||||
pageSize?: number
|
itemsPerPage?: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,43 @@
|
|||||||
|
import { isObject } from 'remeda'
|
||||||
|
|
||||||
export class Pagination {
|
export class Pagination {
|
||||||
static getPaginationData(query) {
|
static getPaginationData<T>(query: T) {
|
||||||
const page = typeof query.PageNumber === 'string' ? +query.PageNumber : 1
|
if (!isObject(query)) throw new Error('Pagination.getPaginationData: query is not an object')
|
||||||
const pageSize = typeof query.PageSize === 'string' ? +query.PageSize : 10
|
|
||||||
const searchNameTerm = typeof query.SearchNameTerm === 'string' ? query.SearchNameTerm : ''
|
const currentPage =
|
||||||
const searchEmailTerm = typeof query.SearchEmailTerm === 'string' ? query.SearchEmailTerm : ''
|
'currentPage' in query &&
|
||||||
return { page, pageSize, searchNameTerm, searchEmailTerm }
|
typeof query.currentPage === 'string' &&
|
||||||
|
!isNaN(Number(query.currentPage))
|
||||||
|
? +query.currentPage
|
||||||
|
: 1
|
||||||
|
const itemsPerPage =
|
||||||
|
'itemsPerPage' in query &&
|
||||||
|
typeof query.itemsPerPage === 'string' &&
|
||||||
|
!isNaN(Number(query.itemsPerPage))
|
||||||
|
? +query.itemsPerPage
|
||||||
|
: 10
|
||||||
|
return { currentPage, itemsPerPage, ...query }
|
||||||
|
}
|
||||||
|
|
||||||
|
static transformPaginationData<T>(
|
||||||
|
[count, items]: [number, T],
|
||||||
|
{
|
||||||
|
currentPage,
|
||||||
|
itemsPerPage,
|
||||||
|
}: {
|
||||||
|
currentPage: number
|
||||||
|
itemsPerPage: number
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
const totalPages = Math.ceil(count / itemsPerPage)
|
||||||
|
return {
|
||||||
|
pagination: {
|
||||||
|
totalPages,
|
||||||
|
currentPage,
|
||||||
|
itemsPerPage,
|
||||||
|
totalItems: count,
|
||||||
|
},
|
||||||
|
items,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { IsEmail, Length } from 'class-validator'
|
import { IsEmail, Length, IsOptional } from 'class-validator'
|
||||||
|
|
||||||
export class RegistrationDto {
|
export class RegistrationDto {
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
|
@IsOptional()
|
||||||
name: string
|
name: string
|
||||||
@Length(3, 30)
|
@Length(3, 30)
|
||||||
password: string
|
password: string
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||||
import { PrismaService } from '../../../prisma.service'
|
import { PrismaService } from '../../../prisma.service'
|
||||||
import { GetAllCardsInDeckDto } from '../dto/get-all-cards.dto'
|
import { GetAllCardsInDeckDto } from '../dto/get-all-cards.dto'
|
||||||
import {
|
|
||||||
DEFAULT_PAGE_NUMBER,
|
|
||||||
DEFAULT_PAGE_SIZE,
|
|
||||||
} from '../../../infrastructure/common/pagination/pagination.constants'
|
|
||||||
import { CreateCardDto } from '../dto/create-card.dto'
|
import { CreateCardDto } from '../dto/create-card.dto'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CardsRepository {
|
export class CardsRepository {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
private readonly logger = new Logger(CardsRepository.name)
|
private readonly logger = new Logger(CardsRepository.name)
|
||||||
|
|
||||||
async createCard(deckId: string, userId: string, card: CreateCardDto) {
|
async createCard(deckId: string, userId: string, card: CreateCardDto) {
|
||||||
try {
|
try {
|
||||||
return await this.prisma.card.create({
|
return await this.prisma.$transaction(async tx => {
|
||||||
|
const created = await tx.card.create({
|
||||||
data: {
|
data: {
|
||||||
user: {
|
author: {
|
||||||
connect: {
|
connect: {
|
||||||
id: userId,
|
id: userId,
|
||||||
},
|
},
|
||||||
@@ -25,9 +24,22 @@ export class CardsRepository {
|
|||||||
id: deckId,
|
id: deckId,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
...card,
|
...card,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
await tx.deck.update({
|
||||||
|
where: {
|
||||||
|
id: deckId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
cardsCount: {
|
||||||
|
increment: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return created
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(e?.message)
|
this.logger.error(e?.message)
|
||||||
throw new InternalServerErrorException(e?.message)
|
throw new InternalServerErrorException(e?.message)
|
||||||
@@ -36,12 +48,7 @@ export class CardsRepository {
|
|||||||
|
|
||||||
async findCardsByDeckId(
|
async findCardsByDeckId(
|
||||||
deckId: string,
|
deckId: string,
|
||||||
{
|
{ answer = undefined, question = undefined, currentPage, itemsPerPage }: GetAllCardsInDeckDto
|
||||||
answer = undefined,
|
|
||||||
question = undefined,
|
|
||||||
currentPage = DEFAULT_PAGE_NUMBER,
|
|
||||||
pageSize = DEFAULT_PAGE_SIZE,
|
|
||||||
}: GetAllCardsInDeckDto
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return await this.prisma.card.findMany({
|
return await this.prisma.card.findMany({
|
||||||
@@ -56,14 +63,15 @@ export class CardsRepository {
|
|||||||
contains: answer || undefined,
|
contains: answer || undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
skip: (currentPage - 1) * pageSize,
|
skip: (currentPage - 1) * itemsPerPage,
|
||||||
take: pageSize,
|
take: itemsPerPage,
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(e?.message)
|
this.logger.error(e?.message)
|
||||||
throw new InternalServerErrorException(e?.message)
|
throw new InternalServerErrorException(e?.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findDeckById(id: string) {
|
public async findDeckById(id: string) {
|
||||||
try {
|
try {
|
||||||
return await this.prisma.deck.findUnique({
|
return await this.prisma.deck.findUnique({
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ import {
|
|||||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'
|
||||||
import { GetAllDecksDto } from './dto/get-all-decks.dto'
|
import { GetAllDecksDto } from './dto/get-all-decks.dto'
|
||||||
import { GetAllCardsInDeckDto } from '../cards/dto/get-all-cards.dto'
|
import { GetAllCardsInDeckDto } from '../cards/dto/get-all-cards.dto'
|
||||||
import { CreateCardCommand } from './use-cases/create-card-use-case'
|
import { CreateCardCommand } from './use-cases'
|
||||||
import { CreateCardDto } from '../cards/dto/create-card.dto'
|
import { CreateCardDto } from '../cards/dto/create-card.dto'
|
||||||
|
import { Pagination } from '../../infrastructure/common/pagination/pagination.service'
|
||||||
|
|
||||||
@Controller('decks')
|
@Controller('decks')
|
||||||
export class DecksController {
|
export class DecksController {
|
||||||
@@ -43,7 +44,8 @@ export class DecksController {
|
|||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@Get()
|
@Get()
|
||||||
findAll(@Query() query: GetAllDecksDto, @Req() req) {
|
findAll(@Query() query: GetAllDecksDto, @Req() req) {
|
||||||
return this.commandBus.execute(new GetAllDecksCommand({ ...query, userId: req.user.id }))
|
const finalQuery = Pagination.getPaginationData(query)
|
||||||
|
return this.commandBus.execute(new GetAllDecksCommand({ ...finalQuery, userId: req.user.id }))
|
||||||
}
|
}
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
import { IsOptional, IsUUID, Length } from 'class-validator'
|
import { IsUUID } from 'class-validator'
|
||||||
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators/is-optional-or-empty-string'
|
import { IsOptionalOrEmptyString } from '../../../infrastructure/decorators/is-optional-or-empty-string'
|
||||||
import { PaginationDto } from '../../../infrastructure/common/pagination/pagination.dto'
|
import { PaginationDto } from '../../../infrastructure/common/pagination/pagination.dto'
|
||||||
|
|
||||||
export class GetAllDecksDto extends PaginationDto {
|
export class GetAllDecksDto extends PaginationDto {
|
||||||
@IsOptional()
|
@IsOptionalOrEmptyString()
|
||||||
@Length(3, 30)
|
minCardsCount?: string
|
||||||
|
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
|
maxCardsCount?: string
|
||||||
|
|
||||||
|
@IsOptionalOrEmptyString()
|
||||||
name?: string
|
name?: string
|
||||||
|
|
||||||
@IsOptionalOrEmptyString()
|
@IsOptionalOrEmptyString()
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
|
||||||
import { PrismaService } from '../../../prisma.service'
|
import { PrismaService } from '../../../prisma.service'
|
||||||
import { GetAllDecksDto } from '../dto/get-all-decks.dto'
|
import { GetAllDecksDto } from '../dto/get-all-decks.dto'
|
||||||
import {
|
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||||
DEFAULT_PAGE_NUMBER,
|
|
||||||
DEFAULT_PAGE_SIZE,
|
|
||||||
} from '../../../infrastructure/common/pagination/pagination.constants'
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DecksRepository {
|
export class DecksRepository {
|
||||||
constructor(private prisma: PrismaService) {}
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
private readonly logger = new Logger(DecksRepository.name)
|
private readonly logger = new Logger(DecksRepository.name)
|
||||||
|
|
||||||
async createDeck({
|
async createDeck({
|
||||||
name,
|
name,
|
||||||
userId,
|
userId,
|
||||||
@@ -24,7 +23,7 @@ export class DecksRepository {
|
|||||||
try {
|
try {
|
||||||
return await this.prisma.deck.create({
|
return await this.prisma.deck.create({
|
||||||
data: {
|
data: {
|
||||||
user: {
|
author: {
|
||||||
connect: {
|
connect: {
|
||||||
id: userId,
|
id: userId,
|
||||||
},
|
},
|
||||||
@@ -45,16 +44,22 @@ export class DecksRepository {
|
|||||||
name = undefined,
|
name = undefined,
|
||||||
authorId = undefined,
|
authorId = undefined,
|
||||||
userId,
|
userId,
|
||||||
currentPage = DEFAULT_PAGE_NUMBER,
|
currentPage,
|
||||||
pageSize = DEFAULT_PAGE_SIZE,
|
itemsPerPage,
|
||||||
|
minCardsCount,
|
||||||
|
maxCardsCount,
|
||||||
}: GetAllDecksDto) {
|
}: GetAllDecksDto) {
|
||||||
|
console.log({ name, authorId, userId, currentPage, itemsPerPage, minCardsCount, maxCardsCount })
|
||||||
try {
|
try {
|
||||||
return await this.prisma.deck.findMany({
|
const where = {
|
||||||
where: {
|
cardsCount: {
|
||||||
|
gte: Number(minCardsCount) ?? undefined,
|
||||||
|
lte: Number(maxCardsCount) ?? undefined,
|
||||||
|
},
|
||||||
name: {
|
name: {
|
||||||
contains: name,
|
contains: name,
|
||||||
},
|
},
|
||||||
user: {
|
author: {
|
||||||
id: authorId || undefined,
|
id: authorId || undefined,
|
||||||
},
|
},
|
||||||
OR: [
|
OR: [
|
||||||
@@ -72,15 +77,41 @@ export class DecksRepository {
|
|||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const [count, items, max] = await this.prisma.$transaction([
|
||||||
|
this.prisma.deck.count({
|
||||||
|
where,
|
||||||
|
}),
|
||||||
|
this.prisma.deck.findMany({
|
||||||
|
where,
|
||||||
|
orderBy: {
|
||||||
|
created: 'desc',
|
||||||
},
|
},
|
||||||
skip: (currentPage - 1) * pageSize,
|
include: {
|
||||||
take: pageSize,
|
author: {
|
||||||
})
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
skip: (currentPage - 1) * itemsPerPage,
|
||||||
|
take: itemsPerPage,
|
||||||
|
}),
|
||||||
|
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 }),
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(e?.message)
|
this.logger.error(e?.message)
|
||||||
throw new InternalServerErrorException(e?.message)
|
throw new InternalServerErrorException(e?.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async findDeckById(id: string) {
|
public async findDeckById(id: string) {
|
||||||
try {
|
try {
|
||||||
return await this.prisma.deck.findUnique({
|
return await this.prisma.deck.findUnique({
|
||||||
|
|||||||
@@ -22,8 +22,9 @@ export class UsersController {
|
|||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
async findAll(@Query() query) {
|
async findAll(@Query() query) {
|
||||||
const { page, pageSize, searchNameTerm, searchEmailTerm } = Pagination.getPaginationData(query)
|
const { page, pageSize } = Pagination.getPaginationData(query)
|
||||||
const users = await this.usersService.getUsers(page, pageSize, searchNameTerm, searchEmailTerm)
|
|
||||||
|
const users = await this.usersService.getUsers(page, pageSize, query.name, query.email)
|
||||||
if (!users) throw new NotFoundException('Users not found')
|
if (!users) throw new NotFoundException('Users not found')
|
||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Prisma } from '@prisma/client'
|
import { Prisma } from '@prisma/client'
|
||||||
|
|
||||||
export class User implements Prisma.UserUncheckedCreateInput {
|
export class User implements Prisma.userUncheckedCreateInput {
|
||||||
id: string
|
id: string
|
||||||
email: string
|
email: string
|
||||||
password: string
|
password: string
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ import {
|
|||||||
import { addHours } from 'date-fns'
|
import { addHours } from 'date-fns'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { PrismaService } from '../../../prisma.service'
|
import { PrismaService } from '../../../prisma.service'
|
||||||
import { pick } from 'remeda'
|
|
||||||
import { Prisma } from '@prisma/client'
|
import { Prisma } from '@prisma/client'
|
||||||
|
import { Pagination } from '../../../infrastructure/common/pagination/pagination.service'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersRepository {
|
export class UsersRepository {
|
||||||
@@ -30,31 +30,29 @@ export class UsersRepository {
|
|||||||
searchEmailTerm: string
|
searchEmailTerm: string
|
||||||
): Promise<EntityWithPaginationType<UserViewType>> {
|
): Promise<EntityWithPaginationType<UserViewType>> {
|
||||||
try {
|
try {
|
||||||
const where = {
|
const where: Prisma.userWhereInput = {
|
||||||
name: {
|
name: {
|
||||||
search: searchNameTerm || undefined,
|
contains: searchNameTerm || undefined,
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
search: searchEmailTerm || undefined,
|
contains: searchEmailTerm || undefined,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
const [totalItems, users] = await this.prisma.$transaction([
|
const res = await this.prisma.$transaction([
|
||||||
this.prisma.user.count({ where }),
|
this.prisma.user.count({ where }),
|
||||||
this.prisma.user.findMany({
|
this.prisma.user.findMany({
|
||||||
where,
|
where,
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
email: true,
|
||||||
|
isEmailVerified: true,
|
||||||
|
},
|
||||||
skip: (currentPage - 1) * itemsPerPage,
|
skip: (currentPage - 1) * itemsPerPage,
|
||||||
take: itemsPerPage,
|
take: itemsPerPage,
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
const totalPages = Math.ceil(totalItems / itemsPerPage)
|
return Pagination.transformPaginationData(res, { currentPage, itemsPerPage })
|
||||||
const usersView = users.map(u => pick(u, ['id', 'name', 'email', 'isEmailVerified']))
|
|
||||||
return {
|
|
||||||
totalPages,
|
|
||||||
currentPage,
|
|
||||||
itemsPerPage,
|
|
||||||
totalItems,
|
|
||||||
items: usersView,
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
if (e.code === 'P2025') {
|
if (e.code === 'P2025') {
|
||||||
@@ -124,7 +122,7 @@ export class UsersRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async findUserById(id: string, include?: Prisma.UserInclude) {
|
async findUserById(id: string, include?: Prisma.userInclude) {
|
||||||
try {
|
try {
|
||||||
const user = await this.prisma.user.findUnique({
|
const user = await this.prisma.user.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
@@ -134,7 +132,7 @@ export class UsersRepository {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return user as Prisma.UserGetPayload<{ include: typeof include }>
|
return user as Prisma.userGetPayload<{ include: typeof include }>
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
if (e.code === 'P2015') {
|
if (e.code === 'P2015') {
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ export class UsersService {
|
|||||||
|
|
||||||
private logger = new Logger(UsersService.name)
|
private logger = new Logger(UsersService.name)
|
||||||
|
|
||||||
async getUsers(page: number, pageSize: number, searchNameTerm: string, searchEmailTerm: string) {
|
async getUsers(page: number, pageSize: number, name: string, email: string) {
|
||||||
return await this.usersRepository.getUsers(page, pageSize, searchNameTerm, searchEmailTerm)
|
return await this.usersRepository.getUsers(page, pageSize, name, email)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUserById(id: string) {
|
async getUserById(id: string) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Prisma } from '@prisma/client'
|
import { Prisma } from '@prisma/client'
|
||||||
|
|
||||||
export type NewestLikesType = {
|
export type NewestLikesType = {
|
||||||
id: string
|
id: string
|
||||||
login: string
|
login: string
|
||||||
@@ -46,12 +47,13 @@ export type CommentType = {
|
|||||||
myStatus: string
|
myStatus: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EntityWithPaginationType<T> = {
|
export type EntityWithPaginationType<T> = {
|
||||||
|
pagination: {
|
||||||
totalPages: number
|
totalPages: number
|
||||||
currentPage: number
|
currentPage: number
|
||||||
itemsPerPage: number
|
itemsPerPage: number
|
||||||
totalItems: number
|
totalItems: number
|
||||||
|
}
|
||||||
items: T[]
|
items: T[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,22 +67,22 @@ export type ErrorMessageType = {
|
|||||||
field: string
|
field: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const userInclude: Prisma.UserInclude = {
|
const userInclude: Prisma.userInclude = {
|
||||||
verification: true,
|
verification: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VerificationWithUser = Prisma.VerificationGetPayload<{
|
export type VerificationWithUser = Prisma.verificationGetPayload<{
|
||||||
include: { user: true }
|
include: { user: true }
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type User = Prisma.UserGetPayload<{
|
export type User = Prisma.userGetPayload<{
|
||||||
include: typeof userInclude
|
include: typeof userInclude
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type CreateUserInput = Omit<Prisma.UserCreateInput & Prisma.VerificationCreateInput, 'user'>
|
export type CreateUserInput = Omit<Prisma.userCreateInput & Prisma.verificationCreateInput, 'user'>
|
||||||
|
|
||||||
export type UserType = {
|
export type UserType = {
|
||||||
accountData: Prisma.UserCreateInput
|
accountData: Prisma.userCreateInput
|
||||||
emailConfirmation: EmailConfirmationType
|
emailConfirmation: EmailConfirmationType
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,4 +141,5 @@ export enum LikeAction {
|
|||||||
Dislike = 'Dislike',
|
Dislike = 'Dislike',
|
||||||
None = 'None',
|
None = 'None',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LikeActionType = 'Like' | 'Dislike' | 'None'
|
export type LikeActionType = 'Like' | 'Dislike' | 'None'
|
||||||
|
|||||||
Reference in New Issue
Block a user