Skip to content

Commit a3120e0

Browse files
Merge pull request #64 from boostcampwm-2024/develop
1.1.1 버전 배포
2 parents c606baf + bc7a8f9 commit a3120e0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2370
-3241
lines changed

.github/workflows/auto-assign-merge.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ jobs:
6868
? context.payload.pull_request.requested_reviewers.map(r => r.login)
6969
: [];
7070
71-
const BE_reviewers = ['summersummerwhy', 'ezcolin2', 'Tolerblanc'];
72-
const FE_reviewers = ['yewonJin', 'djk01281'];
73-
const doc_reviewers = ['summersummerwhy', 'ezcolin2', 'Tolerblanc', 'yewonJin', 'djk01281'];
71+
const BE_reviewers = ['summersummerwhy', 'ezcolin2'];
72+
const FE_reviewers = ['baegyeong', 'pkh0106'];
73+
const doc_reviewers = ['summersummerwhy', 'ezcolin2', 'baegyeong', 'pkh0106'];
7474
7575
// Function to filter out assignees, PR author, and current reviewers
7676
const filterReviewers = (reviewers) => {

.github/workflows/cd-pipeline.yml

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
name: auto Workflow
1+
name: OctoDocs CD Pipeline
22

33
on:
44
push:
55
branches:
6-
- main
6+
- refactor-be-#47
77
workflow_dispatch:
88
jobs:
99
image-build-and-push:
@@ -27,28 +27,52 @@ jobs:
2727
username: ${{ secrets.DOCKER_USERNAME }}
2828
password: ${{ secrets.DOCKER_PASSWORD }}
2929

30+
# 패키지 이미지부터 빌드 후 푸시
31+
# 이 이미지를 base로 backend와 websocket 이미지를 만들기 때문
32+
# 만약 octodocs-modules 이미지가 존재하면 그 이미지를 base로 하고
33+
# 그렇지 않으면 node:20-alpine 버전 사용
34+
# 그리고 layer 수가 너무 많으면 node:20-alpine 버전 사용 (50개 이상)
35+
- name: package docker image build and push
36+
run: |
37+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://hub.docker.com/v2/repositories/summersummerwhy/octodocs-modules)
38+
if [ "$STATUS" -eq 404 ]; then
39+
echo "octodocs-modules not found"
40+
docker build -f ./services/module/Dockerfile.init -t summersummerwhy/octodocs-modules .
41+
42+
else
43+
echo "octodocs-modules found"
44+
docker build -f ./services/module/Dockerfile -t summersummerwhy/octodocs-modules .
45+
LAYERS=$(docker inspect --format '{{len .RootFS.Layers}}' summersummerwhy/octodocs-modules)
46+
if [ $LAYERS -gt 50 ]; then
47+
echo "too many layers"
48+
docker build -f ./services/module/Dockerfile.init -t summersummerwhy/octodocs-modules .
49+
fi
50+
fi
3051
# Docker 이미지 빌드
3152
- name: docker image build
3253
run: |
33-
docker build -f ./services/module/Dockerfile -t summersummerwhy/octodocs-modules .
34-
docker build -f ./services/backend/Dockerfile.prod -t summersummerwhy/octodocs-backend .
35-
docker build -f ./services/nginx/Dockerfile.prod -t summersummerwhy/octodocs-nginx .
36-
docker build -f ./services/websocket/Dockerfile.prod -t summersummerwhy/octodocs-websocket .
54+
docker build -f ./services/backend/Dockerfile.prod -t summersummerwhy/octodocs-backend . &
55+
docker build -f ./services/nginx/Dockerfile.prod -t summersummerwhy/octodocs-nginx . &
56+
docker build -f ./services/websocket/Dockerfile.prod -t summersummerwhy/octodocs-websocket . &
57+
wait
3758
3859
# Docker 이미지 푸시
3960
- name: docker image push
4061
run: |
41-
docker push summersummerwhy/octodocs-modules
42-
docker push summersummerwhy/octodocs-backend
43-
docker push summersummerwhy/octodocs-nginx
44-
docker push summersummerwhy/octodocs-websocket
62+
docker push summersummerwhy/octodocs-modules &
63+
docker push summersummerwhy/octodocs-backend &
64+
docker push summersummerwhy/octodocs-nginx &
65+
docker push summersummerwhy/octodocs-websocket &
66+
wait
4567
4668
deploy:
4769
needs: image-build-and-push
4870
runs-on: ubuntu-latest
4971

5072
steps:
51-
- name: deploy
73+
# octodocs-modules먼저 pull 한 뒤 나머지 이미지 pull
74+
# octodocs-modules를 base로 하는 이미지가 있기 때문
75+
- name: image-pulling
5276
env:
5377
REMOTE_HOST: ${{ secrets.REMOTE_SERVER_IP }}
5478
REMOTE_USER: ${{ secrets.REMOTE_SERVER_USER }}
@@ -61,6 +85,22 @@ jobs:
6185
6286
ssh -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST << 'EOF'
6387
cd /root/octodocs
64-
docker-compose -f compose.prod.yml down
88+
docker pull summersummerwhy/octodocs-modules
6589
docker-compose -f compose.prod.yml pull
90+
- name: deploy
91+
env:
92+
REMOTE_HOST: ${{ secrets.REMOTE_SERVER_IP }}
93+
REMOTE_USER: ${{ secrets.REMOTE_SERVER_USER }}
94+
run: |
95+
ssh -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST << 'EOF'
96+
cd /root/octodocs
97+
docker-compose -f compose.prod.yml down
6698
docker-compose -f compose.prod.yml up -d
99+
# 사용하지 않는 도커 리소스를 정리한다.
100+
- name: cleanup
101+
env:
102+
REMOTE_HOST: ${{ secrets.REMOTE_SERVER_IP }}
103+
REMOTE_USER: ${{ secrets.REMOTE_SERVER_USER }}
104+
run: |
105+
ssh -o StrictHostKeyChecking=no $REMOTE_USER@$REMOTE_HOST << 'EOF'
106+
docker system prune -f --all

.github/workflows/ci-pipeline.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ jobs:
7575
uses: actions/setup-node@v3
7676
with:
7777
node-version: "23"
78+
7879
# 루트 의존성 캐시 설정
7980
- name: Cache Yarn dependencies for root
8081
id: cache-deps
@@ -83,6 +84,12 @@ jobs:
8384
path: node_modules
8485
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
8586

87+
# 환경변수 build에 필요한 .env 생성
88+
- name: Set up .env file
89+
run: |
90+
cd /home/runner/work/refactor-web39-OctoDocs/refactor-web39-OctoDocs/apps/frontend
91+
echo "VITE_API_URL=${{ secrets.VITE_API_URL }}" >> .env
92+
8693
# 빌드 실행
8794
- name: Run build
8895
run: yarn build
@@ -100,11 +107,19 @@ jobs:
100107

101108
# 의존성 캐시 복원
102109
- name: Restore Yarn dependencies
110+
id: cache-deps
103111
uses: actions/cache@v3
104112
with:
105113
path: node_modules
106114
key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
107115

116+
# test에 필요한 의존성 설치
117+
- name: Install Jest dependencies from test package
118+
run: |
119+
cd /home/runner/work/refactor-web39-OctoDocs/refactor-web39-OctoDocs/apps/backend/test
120+
yarn install
121+
mv node_modules ../
122+
108123
# 테스트 실행
109124
- name: Run tests
110125
run: yarn test

apps/backend/src/app.module.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ import { ScheduleModule } from '@nestjs/schedule';
4040
database: configService.get('DB_NAME'),
4141
entities: [Node, Page, Edge, User, Workspace, Role],
4242
logging: process.env.NODE_ENV === 'development',
43-
// synchronize: process.env.NODE_ENV === 'development',
44-
synchronize: true,
43+
synchronize: process.env.NODE_ENV === 'development',
4544
}),
4645
}),
4746
NodeModule,

apps/backend/src/auth/auth.controller.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ describe('AuthController', () => {
2929
refreshAccessToken: jest.fn(() => 'mockedAccessToken'),
3030
},
3131
},
32+
{
33+
provide: JwtAuthGuard,
34+
useValue: {},
35+
},
3236
],
3337
})
3438
.overrideGuard(JwtAuthGuard)

apps/backend/src/edge/edge.controller.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,24 @@ describe('EdgeController', () => {
5151
});
5252

5353
describe('deleteEdge', () => {
54-
it('id에 해당하는 엣지를 찾아 삭제한다.', async () => {
55-
const id = 2;
54+
it('fromNode, toNode에 해당하는 엣지를 찾아 삭제한다.', async () => {
5655
const expectedResponse = {
5756
message: EdgeResponseMessage.EDGE_DELETED,
5857
};
5958

60-
const result = await controller.deleteEdge(id);
59+
jest.spyOn(edgeService, 'deleteEdge').mockResolvedValue(undefined);
60+
const result = await controller.deleteEdge(1, 3);
6161

62-
expect(edgeService.deleteEdge).toHaveBeenCalledWith(id);
62+
expect(edgeService.deleteEdge).toHaveBeenCalledWith(1, 3);
6363
expect(result).toEqual(expectedResponse);
6464
});
6565

66-
it('id에 해당하는 엣지가 존재하지 않으면 NodeNotFoundException을 throw한다.', async () => {
66+
it('fromNode, toNode에 해당하는 엣지가 존재하지 않으면 NodeNotFoundException을 throw한다.', async () => {
6767
jest
6868
.spyOn(edgeService, 'deleteEdge')
6969
.mockRejectedValue(new EdgeNotFoundException());
7070

71-
await expect(controller.deleteEdge(1)).rejects.toThrow(
71+
await expect(controller.deleteEdge(1, 3)).rejects.toThrow(
7272
EdgeNotFoundException,
7373
);
7474
});

apps/backend/src/edge/edge.controller.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ export class EdgeController {
3939

4040
@ApiResponse({ type: MessageResponseDto })
4141
@ApiOperation({ summary: '엣지를 삭제합니다.' })
42-
@Delete('/:id')
42+
@Delete('/:fromNode/:toNode') // URL 경로에서 fromNode와 toNode를 추출
4343
@HttpCode(HttpStatus.OK)
4444
async deleteEdge(
45-
@Param('id', ParseIntPipe) id: number,
46-
): Promise<{ message: string }> {
47-
await this.edgeService.deleteEdge(id);
45+
@Param('fromNode', ParseIntPipe) fromNode: number,
46+
@Param('toNode', ParseIntPipe) toNode: number,
47+
) {
48+
await this.edgeService.deleteEdge(fromNode, toNode);
4849

4950
return {
5051
message: EdgeResponseMessage.EDGE_DELETED,

apps/backend/src/edge/edge.service.spec.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ describe('EdgeService', () => {
2525
useValue: {
2626
create: jest.fn(),
2727
save: jest.fn(),
28-
delete: jest.fn(),
28+
remove: jest.fn(),
2929
findOneBy: jest.fn(),
30+
findOne: jest.fn(),
3031
findEdgesByWorkspace: jest.fn(),
3132
},
3233
},
@@ -35,6 +36,7 @@ describe('EdgeService', () => {
3536
useValue: {
3637
save: jest.fn(),
3738
findOneBy: jest.fn(),
39+
findOne: jest.fn(),
3840
},
3941
},
4042
{
@@ -86,41 +88,41 @@ describe('EdgeService', () => {
8688
id: 1,
8789
fromNode: fromNode,
8890
toNode: toNode,
91+
workspace: null,
8992
} as Edge;
9093

91-
jest
92-
.spyOn(nodeRepository, 'findOneBy')
93-
.mockResolvedValueOnce(fromNode) // 첫 번째 호출: fromNode
94-
.mockResolvedValueOnce(toNode); // 두 번째 호출: toNode
94+
jest.spyOn(nodeRepository, 'findOne').mockResolvedValueOnce(fromNode); // 첫 번째 호출: fromNode
95+
jest.spyOn(nodeRepository, 'findOneBy').mockResolvedValueOnce(toNode); // 두 번째 호출: toNode
9596
jest.spyOn(edgeRepository, 'save').mockResolvedValue(edge);
9697

9798
const result = await service.createEdge(dto);
9899

99100
expect(result).toEqual(edge);
100101
expect(edgeRepository.save).toHaveBeenCalledTimes(1);
101-
expect(nodeRepository.findOneBy).toHaveBeenCalledTimes(2);
102+
expect(nodeRepository.findOne).toHaveBeenCalledTimes(1);
103+
expect(nodeRepository.findOneBy).toHaveBeenCalledTimes(1);
102104
});
103105
});
104106

105107
describe('deleteEdge', () => {
106108
it('엣지를 성공적으로 삭제한다.', async () => {
107109
jest
108-
.spyOn(edgeRepository, 'delete')
110+
.spyOn(edgeRepository, 'remove')
109111
.mockResolvedValue({ affected: true } as any);
110-
jest.spyOn(edgeRepository, 'findOneBy').mockResolvedValue(new Edge());
112+
jest.spyOn(edgeRepository, 'findOne').mockResolvedValue(new Edge());
111113

112-
await service.deleteEdge(1);
114+
await service.deleteEdge(1, 3);
113115

114-
expect(edgeRepository.delete).toHaveBeenCalledWith(1);
116+
expect(edgeRepository.remove).toHaveBeenCalledTimes(1);
115117
});
116118

117119
it('삭제할 엣지가 존재하지 않으면 EdgeNotFoundException을 throw한다.', async () => {
118-
jest
119-
.spyOn(edgeRepository, 'delete')
120-
.mockResolvedValue({ affected: false } as any);
121-
await expect(service.deleteEdge(1)).rejects.toThrow(
120+
jest.spyOn(edgeRepository, 'findOne').mockResolvedValue(undefined);
121+
122+
await expect(service.deleteEdge(1, 3)).rejects.toThrow(
122123
EdgeNotFoundException,
123124
);
125+
expect(edgeRepository.remove).toHaveBeenCalledTimes(0);
124126
});
125127
});
126128

apps/backend/src/edge/edge.service.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,37 +19,43 @@ export class EdgeService {
1919
const { fromNode, toNode } = dto;
2020

2121
// 출발 노드를 조회한다.
22-
const existingFromNode = await this.nodeRepository.findOneBy({
23-
id: fromNode,
22+
const existingFromNode = await this.nodeRepository.findOne({
23+
where: { id: fromNode },
24+
relations: ['workspace'], // workspace 관계를 포함
2425
});
26+
2527
// 도착 노드를 조회한다.
2628
const existingToNode = await this.nodeRepository.findOneBy({ id: toNode });
2729

2830
// 엣지를 생성한다.
2931
return await this.edgeRepository.save({
3032
fromNode: existingFromNode,
3133
toNode: existingToNode,
34+
workspace: existingFromNode.workspace,
3235
});
3336
}
3437

35-
async deleteEdge(id: number): Promise<void> {
36-
// 엣지를 삭제한다
37-
const deleteResult = await this.edgeRepository.delete(id);
38+
async deleteEdge(fromNode: number, toNode: number): Promise<void> {
39+
// fromNode와 toNode로 매치되는 엣지를 검색
40+
41+
// 출발 노드를 조회한다.
42+
const existingFromNode = await this.nodeRepository.findOneBy({
43+
id: fromNode,
44+
});
45+
// 도착 노드를 조회한다.
46+
const existingToNode = await this.nodeRepository.findOneBy({ id: toNode });
47+
48+
const edge = await this.edgeRepository.findOne({
49+
where: { fromNode: existingFromNode, toNode: existingToNode },
50+
});
3851

39-
// 삭제된 엣지가 없으면 노드를 찾지 못한 것
40-
if (!deleteResult.affected) {
52+
// 엣지가 없으면 예외를 발생시킴
53+
if (!edge) {
4154
throw new EdgeNotFoundException();
4255
}
43-
}
4456

45-
async findEdgeByFromNodeAndToNode(fromNodeId: number, toNodeId: number) {
46-
return this.edgeRepository.findOne({
47-
where: {
48-
fromNode: { id: fromNodeId },
49-
toNode: { id: toNodeId },
50-
},
51-
relations: ['fromNode', 'toNode'],
52-
});
57+
// 엣지를 삭제
58+
await this.edgeRepository.remove(edge);
5359
}
5460

5561
async findEdgesByWorkspace(workspaceId: string): Promise<Edge[]> {

apps/backend/src/node/dtos/coordinateResponse.dto.ts

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)