-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#
- Loading branch information
Showing
6 changed files
with
241 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { | ||
Body, | ||
Controller, | ||
DefaultValuePipe, | ||
Get, | ||
Param, | ||
ParseIntPipe, | ||
Post, | ||
Query, | ||
UseGuards, | ||
} from '@nestjs/common'; | ||
import { CollectionService } from './collection.service'; | ||
import { AuthGuard } from '@nestjs/passport'; | ||
import { ApiBearerAuth, ApiQuery } from '@nestjs/swagger'; | ||
import { GetUid } from '../decorators/get-uid.decorator'; | ||
import { CollectionDto } from './dto/response/collection.dto'; | ||
import { CreateCollectionDto } from './dto/request/create-collection.dto'; | ||
|
||
@Controller('collection') | ||
@UseGuards(AuthGuard('jwt')) | ||
@ApiBearerAuth() | ||
export class CollectionController { | ||
constructor(private readonly collectionService: CollectionService) {} | ||
|
||
@Get() | ||
@ApiQuery({ | ||
name: 'limit', | ||
required: false, | ||
type: Number, | ||
description: '한 번에 받을 태그의 개수. 최대 20개까지 가능. 기본값은 20.', | ||
}) | ||
@ApiQuery({ | ||
name: 'offset', | ||
required: false, | ||
type: Number, | ||
description: '몇 번째 태그부터 검색 결과에 포함할지. 기본값은 0.', | ||
}) | ||
getCollections( | ||
@GetUid() uid: string, | ||
@Query('limit', new DefaultValuePipe(20), ParseIntPipe) limit: number, | ||
@Query('offset', new DefaultValuePipe(0), ParseIntPipe) offset: number, | ||
): Promise<CollectionDto[]> { | ||
return this.collectionService.findAll(uid, limit, offset); | ||
} | ||
|
||
@Post() | ||
createCollection( | ||
@GetUid() uid: string, | ||
@Body() createCollectionDto: CreateCollectionDto, | ||
): Promise<CollectionDto> { | ||
return this.collectionService.create(uid, createCollectionDto); | ||
} | ||
|
||
@Get('find/:name') | ||
findByName( | ||
@GetUid() uid: string, | ||
@Param('name') name: string, | ||
): Promise<CollectionDto> { | ||
return this.collectionService.findOne(uid, name); | ||
} | ||
|
||
@Post('delete/:name') | ||
delete(@GetUid() uid: string, @Param('name') name: string): Promise<void> { | ||
return this.collectionService.deleteOne(uid, name); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { CollectionService } from './collection.service'; | ||
import { CollectionController } from './collection.controller'; | ||
import { UserModule } from '../user/user.module'; | ||
|
||
@Module({ | ||
imports: [UserModule], | ||
controllers: [CollectionController], | ||
providers: [CollectionService], | ||
}) | ||
export class CollectionModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { | ||
BadRequestException, | ||
Injectable, | ||
Logger, | ||
UnauthorizedException, | ||
} from '@nestjs/common'; | ||
import { PrismaService } from '../prisma/prisma.service'; | ||
import { CollectionDto } from './dto/response/collection.dto'; | ||
import { UserService } from '../user/user.service'; | ||
import { CreateCollectionDto } from './dto/request/create-collection.dto'; | ||
|
||
@Injectable() | ||
export class CollectionService { | ||
private readonly logger: Logger = new Logger(CollectionService.name); | ||
|
||
constructor( | ||
private readonly prisma: PrismaService, | ||
private readonly userService: UserService, | ||
) {} | ||
|
||
async findAll( | ||
uid: string, | ||
limit: number, | ||
offset: number, | ||
): Promise<CollectionDto[]> { | ||
limit = limit > 20 ? 20 : limit; | ||
const collections = await this.prisma.collection.findMany({ | ||
include: { | ||
_count: { select: { documents: true } }, | ||
}, | ||
where: { user: { uid } }, | ||
orderBy: [{ documents: { _count: 'desc' } }, { name: 'asc' }], | ||
take: limit, | ||
skip: offset, | ||
}); | ||
|
||
this.logger.log(`getTags: ${JSON.stringify(collections)}`); | ||
|
||
return collections.map((collection) => ({ | ||
name: collection.name, | ||
description: collection.description, | ||
count: collection._count.documents, | ||
})); | ||
} | ||
|
||
async create( | ||
uid: string, | ||
createCollectionDto: CreateCollectionDto, | ||
): Promise<CollectionDto> { | ||
const user = await this.userService.findByUid(uid); | ||
|
||
const documents = await this.prisma.document.findMany({ | ||
select: { id: true }, | ||
where: { | ||
userId: user.id, | ||
docId: { in: createCollectionDto.docIds ?? [] }, | ||
}, | ||
}); | ||
|
||
// TODO: CreateCollectionDto에 docIds가 너무 많을 경우 처리 | ||
|
||
try { | ||
const collection = await this.prisma.collection.create({ | ||
data: { | ||
name: createCollectionDto.name, | ||
description: createCollectionDto.description, | ||
user: { connect: { id: user.id } }, | ||
documents: { | ||
connect: documents.map((doc) => ({ id: doc.id })), | ||
}, | ||
}, | ||
}); | ||
|
||
return { | ||
name: collection.name, | ||
description: collection.description, | ||
count: documents.length, | ||
}; | ||
} catch (error) { | ||
if (error.code === 'P2002') { | ||
// "Unique constraint failed on the {constraint}" 에러 | ||
throw new BadRequestException('Collection already exists'); | ||
} | ||
} | ||
} | ||
|
||
async deleteOne(uid: string, name: string): Promise<void> { | ||
const user = await this.userService.findByUid(uid); | ||
const collection = await this.prisma.collection.findUnique({ | ||
where: { userId_name: { userId: user.id, name } }, | ||
include: { user: true }, | ||
}); | ||
|
||
if (!collection) { | ||
throw new BadRequestException('Collection does not exist'); | ||
} | ||
|
||
if (collection.user.uid !== uid) { | ||
throw new UnauthorizedException('Unauthorized'); | ||
} | ||
|
||
await this.prisma.collection.delete({ where: { id: collection.id } }); | ||
} | ||
|
||
async findOne(uid: string, name: string) { | ||
const user = await this.userService.findByUid(uid); | ||
const collection = await this.prisma.collection.findUnique({ | ||
where: { userId_name: { userId: user.id, name } }, | ||
include: { _count: { select: { documents: true } } }, | ||
}); | ||
|
||
if (!collection) { | ||
throw new BadRequestException('Collection does not exist'); | ||
} | ||
|
||
return { | ||
name: collection.name, | ||
description: collection.description, | ||
count: collection._count.documents, | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
|
||
export class CreateCollectionDto { | ||
@IsString() | ||
@IsNotEmpty() | ||
name: string; | ||
|
||
@IsString() | ||
description: string; | ||
|
||
@IsOptional() | ||
@IsString({ each: true }) | ||
@ApiProperty({ | ||
description: '컬렉션에 속한 문서의 uuid', | ||
required: false, | ||
}) | ||
docIds?: string[]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
|
||
export class CollectionDto { | ||
@ApiProperty({ | ||
description: '컬렉션 이름', | ||
example: '백엔드', | ||
}) | ||
name: string; | ||
|
||
@ApiProperty({ | ||
description: '컬렉션 설명', | ||
example: '백엔드 개발과 관련된 블로그 글 모음', | ||
}) | ||
description: string; | ||
|
||
@ApiProperty({ | ||
description: '컬렉션에 속한 문서의 개수', | ||
example: 1, | ||
}) | ||
count: number; | ||
} |