Skip to content

Commit 26e1221

Browse files
authored
게임 방 리스트 조회 (#40)
* feat: 게임방 목록 조회 API * feat: 게임방 응답에 현재 구성원 수 추가 player를 member로 명칭하도록 속성명 변경
1 parent be4e054 commit 26e1221

24 files changed

+377
-4
lines changed

jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export default {
2424
setupFilesAfterEnv: [
2525
'<rootDir>/test/after-env-setup.ts',
2626
'jest-extended/all',
27+
'jest-sorted',
2728
],
2829
globalSetup: '<rootDir>/test/global-setup.ts',
2930
globalTeardown: '<rootDir>/test/global-teardown.ts',

package-lock.json

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"globals": "^16.0.0",
8080
"jest": "^29.7.0",
8181
"jest-extended": "^6.0.0",
82+
"jest-sorted": "^1.0.15",
8283
"prettier": "^3.4.2",
8384
"prisma": "6.4.0",
8485
"rosie": "^2.1.1",

src/common/base/base.dto.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { ApiProperty } from '@nestjs/swagger';
22

3+
import { ISort } from '@common/base/base.repository';
4+
5+
export class SortDto implements ISort {
6+
field: string;
7+
8+
direction: 'desc' | 'asc';
9+
}
10+
311
export class BaseResponseDto {
412
constructor(props: BaseResponseDto) {
513
this.id = props.id;

src/common/base/base.repository.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { IBaseMapper } from '@common/base/base.mapper';
33

44
import { PrismaService } from '@shared/prisma/prisma.service';
55

6+
export interface ISort<Field extends string = string> {
7+
field: Field;
8+
direction: 'desc' | 'asc';
9+
}
10+
611
export interface ICursorPaginated<T> {
712
cursor?: string;
813
data: T[];
@@ -96,4 +101,22 @@ export abstract class BaseRepository<
96101
},
97102
});
98103
}
104+
105+
protected toOrderBy(sort?: ISort[]): Record<string, any> | undefined {
106+
if (sort === undefined || sort.length === 0) {
107+
return;
108+
}
109+
110+
return sort.reduce((acc, cur) => {
111+
const { field, direction } = cur;
112+
113+
if (field === 'createdAt') {
114+
acc['id'] = direction;
115+
} else {
116+
acc[field] = direction;
117+
}
118+
119+
return acc;
120+
}, {});
121+
}
99122
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { applyDecorators } from '@nestjs/common';
2+
import { ApiProperty, ApiPropertyOptions } from '@nestjs/swagger';
3+
4+
export const ApiSort = (
5+
options: ApiPropertyOptions & { allowFields: ReadonlySet<string> },
6+
) => {
7+
return applyDecorators(
8+
ApiProperty({
9+
description: `
10+
정렬 쿼리. 다중 정렬은 콤마(,)로 구분.
11+
허용되지 않은 정렬 필드 및 방향은 무시합니다.
12+
형식: field:asc | field:desc
13+
허용 필드: ${Array.from(options.allowFields).join(',')},
14+
예: sort=-title:asc,createdAt:asc
15+
`,
16+
default: 'createdAt:asc',
17+
example: 'createdAt:asc',
18+
required: false as any,
19+
type: String,
20+
...options,
21+
}),
22+
);
23+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { parseSort } from '@common/transformer/parse-sort.transformer';
2+
3+
describe(parseSort, () => {
4+
const ALLOW_FIELDS = new Set(['id', 'createdAt']);
5+
6+
it('여러 필드로 정렬할 수 있다.', () => {
7+
expect(parseSort('id:asc,createdAt:desc', ALLOW_FIELDS)).toEqual([
8+
{
9+
field: 'id',
10+
direction: 'asc',
11+
},
12+
{
13+
field: 'createdAt',
14+
direction: 'desc',
15+
},
16+
]);
17+
});
18+
19+
it('허용되지 않은 필드는 무시한다.', () => {
20+
expect(parseSort('qwe:desc', ALLOW_FIELDS)).toEqual([]);
21+
});
22+
23+
it('허용되지 않은 정렬 방향은 무시한다.', () => {
24+
expect(parseSort('id:qwe', ALLOW_FIELDS)).toEqual([]);
25+
});
26+
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { applyDecorators } from '@nestjs/common';
2+
3+
import { Transform, TransformFnParams } from 'class-transformer';
4+
5+
import { SortDto } from '@common/base/base.dto';
6+
7+
export const parseSort = (
8+
value: unknown,
9+
allowFields: ReadonlySet<string>,
10+
): SortDto[] | undefined => {
11+
if (value === undefined) {
12+
return;
13+
}
14+
if (typeof value !== 'string') {
15+
return;
16+
}
17+
18+
const parsedOrders = value.split(',');
19+
20+
const orders = parsedOrders
21+
.map((parsedOrder) => {
22+
const [field, direction] = parsedOrder.split(':');
23+
24+
return {
25+
field,
26+
direction,
27+
};
28+
})
29+
.filter(({ field, direction }) => {
30+
if (allowFields.has(field) === false) {
31+
return false;
32+
}
33+
if (direction !== 'desc' && direction !== 'asc') {
34+
return false;
35+
}
36+
return true;
37+
})
38+
.map(({ field, direction }) => {
39+
const sortDto = new SortDto();
40+
41+
sortDto.field = field;
42+
sortDto.direction = direction as SortDto['direction'];
43+
44+
return sortDto;
45+
});
46+
47+
return orders;
48+
};
49+
50+
export const ParseSort = (allowFields: ReadonlySet<string>) => {
51+
return applyDecorators(
52+
Transform(({ value }: TransformFnParams): SortDto[] | undefined => {
53+
return parseSort(value, allowFields);
54+
}),
55+
);
56+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { GameRoomDtoAssembler } from '@module/game-room/assemblers/game-room-dto.assembler';
2+
import { GameRoomCollectionDto } from '@module/game-room/dto/game-room-collection.dto';
3+
import { GameRoom } from '@module/game-room/entities/game-room.entity';
4+
5+
export class GameRoomCollectionDtoAssembler {
6+
static convertToDto(domainObject: GameRoom[]): GameRoomCollectionDto {
7+
const dto = new GameRoomCollectionDto();
8+
9+
dto.data = domainObject.map(GameRoomDtoAssembler.convertToDto);
10+
11+
return dto;
12+
}
13+
}

src/modules/game-room/assemblers/game-room-dto.assembler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ export class GameRoomDtoAssembler {
1212
dto.hostId = gameRoom.props.hostId;
1313
dto.status = gameRoom.props.status;
1414
dto.title = gameRoom.props.title;
15-
dto.maxPlayersCount = gameRoom.props.maxMembersCount;
15+
dto.maxMembersCount = gameRoom.props.maxMembersCount;
16+
dto.currentMembersCount = gameRoom.props.currentMembersCount;
1617

1718
return dto;
1819
}

0 commit comments

Comments
 (0)