Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: 모든 날짜 조회수 통계 API 구현 #219

Merged
merged 6 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions server/src/statistic/statistic.api-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import { applyDecorators } from '@nestjs/common';
import {
ApiBadRequestResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';

export function ApiTodayStatistic() {
export function ApiStatistic(category: 'today' | 'all') {
const type = category === 'all' ? '전체' : '금일';
return applyDecorators(
ApiOperation({
summary: `${type} 게시글 조회수 통계 API`,
}),
ApiQuery({
name: 'limit',
required: false,
Expand Down Expand Up @@ -42,7 +47,7 @@ export function ApiTodayStatistic() {
},
},
example: {
message: '금일 조회수 통계 조회 완료',
message: `${type} 조회수 통계 조회 완료`,
data: [
{
id: 1,
Expand Down
17 changes: 15 additions & 2 deletions server/src/statistic/statistic.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { ApiTodayStatistic } from './statistic.api-docs';
import { ApiStatistic } from './statistic.api-docs';
import { StatisticService } from './statistic.service';
import { ApiResponse } from '../common/response/common.response';
import { ApiTags } from '@nestjs/swagger';
Expand All @@ -18,7 +18,7 @@ import { StatisticQueryDto } from './dto/statistic-query.dto';
export class StatisticController {
constructor(private readonly statisticService: StatisticService) {}

@ApiTodayStatistic()
@ApiStatistic('today')
@UseGuards(CookieAuthGuard)
@Get('today')
@UsePipes(
Expand All @@ -30,4 +30,17 @@ export class StatisticController {
const data = await this.statisticService.getTodayViewCount(queryObj.limit);
return ApiResponse.responseWithData('금일 조회수 통계 조회 완료', data);
}

@ApiStatistic('all')
@UseGuards(CookieAuthGuard)
@Get('all')
@UsePipes(
new ValidationPipe({
transform: true,
}),
)
async getAllStatistic(@Query() queryObj: StatisticQueryDto) {
const data = await this.statisticService.getAllViewCount(queryObj.limit);
return ApiResponse.responseWithData('전체 조회수 통계 조회 완료', data);
}
CodeVac513 marked this conversation as resolved.
Show resolved Hide resolved
}
11 changes: 11 additions & 0 deletions server/src/statistic/statistic.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,15 @@ export class StatisticService {

return result;
}

async getAllViewCount(limit: number) {
const ranking = await this.feedRepository.find({
select: ['id', 'title', 'viewCount'],
order: {
viewCount: 'DESC',
},
take: limit,
});
return ranking;
}
}
177 changes: 177 additions & 0 deletions server/test/statistic/all.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import * as request from 'supertest';
import { INestApplication } from '@nestjs/common';
import { TestingModule } from '@nestjs/testing';
import { DataSource } from 'typeorm';
import { Feed } from '../../src/feed/feed.entity';
import { RssAccept } from '../../src/rss/rss.entity';
import { RedisService } from '../../src/common/redis/redis.service';

describe('All view count statistic E2E Test : GET /api/statistic/all', () => {
let app: INestApplication;
beforeAll(async () => {
app = global.testApp;
const moduleFixture: TestingModule = global.testModuleFixture;
const dataSource = moduleFixture.get<DataSource>(DataSource);
const rssAcceptRepository = dataSource.getRepository(RssAccept);
const feedRepository = dataSource.getRepository(Feed);
const redisService = app.get(RedisService);
const [blog] = await Promise.all([
rssAcceptRepository.save({
id: 1,
name: 'test',
userName: 'test',
email: '[email protected]',
rssUrl: 'https://test.com/rss',
}),
redisService.redisClient.set('test1234', 'test'),
]);
await feedRepository.save([
{
id: 1,
createdAt: '2024-11-26 09:00:00',
title: 'test1',
path: 'test1',
viewCount: 5,
thumbnail: 'https://test.com/test.png',
blog: blog,
},
CodeVac513 marked this conversation as resolved.
Show resolved Hide resolved
{
id: 2,
createdAt: '2024-11-26 09:00:00',
title: 'test2',
path: 'test2',
viewCount: 4,
thumbnail: 'https://test.com/test.png',
blog: blog,
},
{
id: 3,
createdAt: '2024-11-26 09:00:00',
title: 'test3',
path: 'test3',
viewCount: 3,
thumbnail: 'https://test.com/test.png',
blog: blog,
},
{
id: 4,
createdAt: '2024-11-26 09:00:00',
title: 'test4',
path: 'test4',
viewCount: 2,
thumbnail: 'https://test.com/test.png',
blog: blog,
},
{
id: 5,
createdAt: '2024-11-26 09:00:00',
title: 'test5',
path: 'test5',
viewCount: 1,
thumbnail: 'https://test.com/test.png',
blog: blog,
},
]);
});

describe('관리자 권한이 없을 경우', () => {
it('관리자 쿠키가 없다.', async () => {
const response = await request(app.getHttpServer()).get(
'/api/statistic/all?limit=1.1',
);
expect(response.status).toBe(401);
expect(response.body.message).toBe('인증되지 않은 요청입니다.');
});
it('관리자 쿠키가 만료되었다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all?limit=1.1')
.set('Cookie', 'sessionId=test4321');
expect(response.status).toBe(401);
expect(response.body.message).toBe('인증되지 않은 요청입니다.');
});
});

describe('관리자 권한이 있을 경우', () => {
describe('limit 값을 올바르게 입력하지 않았을 경우', () => {
it('실수를 입력한다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all?limit=1.1')
.set('Cookie', 'sessionId=test1234');
expect(response.status).toBe(400);
expect(response.body.message).toBe('정수로 입력해주세요.');
});
it('문자열을 입력한다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all?limit=test')
.set('Cookie', 'sessionId=test1234');
expect(response.status).toBe(400);
expect(response.body.message).toBe('정수로 입력해주세요.');
});

it('음수를 입력한다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all?limit=-100')
.set('Cookie', 'sessionId=test1234');
expect(response.status).toBe(400);
expect(response.body).toStrictEqual({
message: 'limit 값은 1 이상이어야 합니다.',
});
});
});

describe('limit 값을 올바르게 입력했을 경우', () => {
it('값을 입력 하지 않는다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all')
.set('Cookie', 'sessionId=test1234');
expect(response.status).toBe(200);
expect(response.body).toStrictEqual({
message: '전체 조회수 통계 조회 완료',
data: [
{
id: 1,
title: 'test1',
viewCount: 5,
},
{
id: 2,
title: 'test2',
viewCount: 4,
},
{
id: 3,
title: 'test3',
viewCount: 3,
},
{
id: 4,
title: 'test4',
viewCount: 2,
},
{
id: 5,
title: 'test5',
viewCount: 1,
},
],
});
});
it('양수를 입력한다.', async () => {
const response = await request(app.getHttpServer())
.get('/api/statistic/all?limit=1')
.set('Cookie', 'sessionId=test1234');
expect(response.status).toBe(200);
expect(response.body).toStrictEqual({
message: '전체 조회수 통계 조회 완료',
data: [
{
id: 1,
title: 'test1',
viewCount: 5,
},
],
});
});
});
});
});