diff --git a/.github/workflows/backend-dev-deploy.yml b/.github/workflows/backend-dev-deploy.yml
index f17fa1c93..fdacb2c46 100644
--- a/.github/workflows/backend-dev-deploy.yml
+++ b/.github/workflows/backend-dev-deploy.yml
@@ -21,6 +21,8 @@ jobs:
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+ outputs:
+ current_timestamp: ${{ steps.timestamp.outputs.timestamp }}
steps:
- name: workflow_dispatch에서 지정한 branch로 checkout
@@ -29,6 +31,13 @@ jobs:
token: ${{ secrets.SUBMODULE_TOKEN }}
submodules: true
+ - name: unix 타임스탬프 얻기
+ id: timestamp
+ run: echo "::set-output name=timestamp::$(date +%s)"
+
+ - name: unix 타임스탬프 확인하기
+ run: echo ${{ steps.timestamp.outputs.timestamp }}
+
- name: JDK 11로 설정
uses: actions/setup-java@v3
with:
@@ -65,7 +74,7 @@ jobs:
file: backend/emm-sale/Dockerfile-dev
platforms: linux/arm64/v8
push: true
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/kerdy-dev:latest
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/kerdy-dev:${{ steps.timestamp.outputs.timestamp }}
deploy:
needs: build
@@ -73,9 +82,8 @@ jobs:
runs-on: [ self-hosted, label-dev ]
steps:
- name: 도커 실행
- run: |
- docker stop kerdy && docker rm kerdy && docker rmi -f ${{ secrets.DOCKERHUB_USERNAME }}/kerdy-dev
- docker run -d -p 8080:8080 --name kerdy -e TZ=Asia/Seoul --network host ${{ secrets.DOCKERHUB_USERNAME }}/kerdy-dev
+ run:
+ sudo /home/ubuntu/deploy.sh ${{ secrets.DOCKERHUB_USERNAME }} ${{ needs.build.outputs.current_timestamp }}
- name: 슬랙 메시지 보내기
uses: 8398a7/action-slack@v3
diff --git a/.github/workflows/backend-prod-deploy.yml b/.github/workflows/backend-prod-deploy.yml
index 1239600c9..384e98bb8 100644
--- a/.github/workflows/backend-prod-deploy.yml
+++ b/.github/workflows/backend-prod-deploy.yml
@@ -21,7 +21,8 @@ jobs:
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
-
+ outputs:
+ current_timestamp: ${{ steps.timestamp.outputs.timestamp }}
steps:
- name: workflow_dispatch에서 지정한 branch로 checkout
uses: actions/checkout@v3
@@ -29,6 +30,13 @@ jobs:
token: ${{ secrets.SUBMODULE_TOKEN }}
submodules: true
+ - name: unix 타임스탬프 얻기
+ id: timestamp
+ run: echo "::set-output name=timestamp::$(date +%s)"
+
+ - name: unix 타임스탬프 확인하기
+ run: echo ${{ steps.timestamp.outputs.timestamp }}
+
- name: firebase key 생성
run: |
echo "${{ secrets.FIREBASE_KEY }}" > firebase-kerdy.json
@@ -72,7 +80,7 @@ jobs:
file: backend/emm-sale/Dockerfile-prod
platforms: linux/arm64/v8
push: true
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/kerdy:latest
+ tags: ${{ secrets.DOCKERHUB_USERNAME }}/kerdy:${{ steps.timestamp.outputs.timestamp }}
deploy:
needs: build
@@ -80,9 +88,8 @@ jobs:
runs-on: [ self-hosted, label-prod ]
steps:
- name: 도커 실행
- run: |
- docker stop kerdy && docker rm kerdy && docker rmi -f ${{ secrets.DOCKERHUB_USERNAME }}/kerdy
- docker run -d -p 8080:8080 -v /home/ubuntu/logs/:/logs/ -e TZ=Asia/Seoul --name kerdy ${{ secrets.DOCKERHUB_USERNAME }}/kerdy
+ run:
+ sudo /home/ubuntu/deploy.sh ${{ secrets.DOCKERHUB_USERNAME }} ${{ needs.build.outputs.current_timestamp }}
- name: 슬랙 메시지 보내기
uses: 8398a7/action-slack@v3
diff --git a/README.md b/README.md
index 20b1f6481..dbc81da17 100644
--- a/README.md
+++ b/README.md
@@ -1,43 +1,121 @@
# 📄 서비스 소개
-### 여러분은 IT 컨퍼런스나 해커톤, 대회에 참여할 때 주로 어떤 목적을 가지고 참여하시나요?
+## 이런 적 있지 않나요?
-아마도 유익한 강연을 듣기 위해서일 수도 있고, 같은 관심사를 가진 동료 개발자들과 교류하려는 의도일 수도 있습니다. 하지만 이러한 행사에 참여하기 위해 동료를 찾거나 행사의 질에 대한 리뷰를 알아보려면 많은 노력이 필요했을 겁니다. 이런 고민을 해결하고자 **커디(KerDy)**가 탄생했습니다.
+### **평소 IT 행사 정보를 찾아보기 어렵지 않으셨나요?**
-커디는 개발자들의 특별한 인연을 만들어낼 수 있는 모바일 앱 서비스로써, 행사에 참여하려는 동료를 찾거나 행사의 리뷰를 확인하고 싶을 때 직접적인 도움을 줍니다. 커디의 주요 기능은 다음과 같습니다.
+
+
+
+
+IT 업계의 능력있는 사람들이 모여 지식을 공유하고, 네트워킹하며 지식의 뿌리를 넓힐 수 있는 컨퍼런스
+자신의 역량을 시험하고 경험을 넓힐 수 있는 귀중한 기회인 공모전과 대회
+여러분은 컨퍼런스나 대회를 찾을 때 어떻게 찾으시나요?
+
+어쩌다가 생각나서 행사 모음 사이트를 검색하거나 지인에게 건너 듣진 않으신가요?
+
+이런 IT 행사 정보를 간편하게 한 데 모아볼 수 있다면 좋지 않을까요?
+
+
+
+### **여러분의 관심사와 맞지 않는 행사가 너무 많지 않나요?**
+
+
+
+
+
+사실, 이런 행사 정보를 모아둔 사이트는 이미 여럿 존재하고 있습니다.
+
+하지만 막상 사이트를 둘러보면, 수많은 행사 중 자신의 관심 분야와 맞는 행사의 개수는 손에 꼽습니다.
+
+> _백엔드, 프론트엔드, 안드로이드, 정보보안, AI, 빅데이터…_
+
+그렇다고 사이트에 매일 접속해서 자신이 관심있는 분야의 행사가 올라왔는지 확인하는 것은 귀찮고 번거로운 작업입니다.
+
+그냥 자신이 관심있는 분야의 행사 정보만 골라 볼 수 있다면 좋지 않을까요?
+
+
+
+### **행사를 함께할 친구를 찾기 힘들지 않았나요?**
+
+
+
+
+
+가고 싶은 행사가 생겼어도, 컨퍼런스를 가려고 하니 막상 컨퍼런스를 혼자 가기가 두렵진 않으셨나요?
+
+혹은 팀으로 참여하는 대회 팀원을 구하는 데 어려움을 겪은 적이 있지 않았나요?
+
+하나의 플랫폼에서 행사에 함께 참여할 사람을 구할 수 있다면 좋지 않을까요?
+
+그런 여러분을 위해 Kerdy 서비스를 출시하게 되었습니다!
+
+
## ✨ 핵심 기능
-**1. 오프라인 네트워킹 활성화**
+### 1. 행사 필터링 & 스크랩
+
+| ![](https://velog.velcdn.com/images/kerdy-official/post/10c013bf-0e44-4a5b-8e4b-fb494e8dcbd0/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/0fb37480-65bc-40aa-b4b0-b0ecb8befd13/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/02c7c3d0-2dde-4ea1-a3b4-8664773805ff/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
+
+원하는 행사, 쉽게 필터링하고 찾아보세요!
+
+커디는 관심 분야별로 행사를 필터링할 수 있습니다.
+
+만약 관심있는 행사가 있다면 스크랩 해보시는건 어떠신가요?
+
+관심있는 행사는 스크랩해서 언제든지 다시 확인해보세요!
+
+
+
+### 2. 관심 태그를 설정해 알람을 받아보세요!
+
+| ![](https://velog.velcdn.com/images/kerdy-official/post/c82091e5-b2ae-4ff0-9457-cdcb560229ca/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/c681ce12-73ea-4c77-b540-6c8223116a85/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
+
+
+관심 태그 설정을 통해 원하는 분야의 행사 알림을 쉽게 받아볼 수 있어요.
+
+
+
+| ![](https://velog.velcdn.com/images/kerdy-official/post/cd47070e-5fda-41b6-91d6-2985b87b234b/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/39b28b61-624b-4857-81ee-e3c55d5bc348/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
+
+알림 보관함을 통해 나에게 온 알림을 모아볼 수 있어요!
-커디를 통해 원하는 행사에 함께 참여하고 싶은 동료를 쉽게 찾을 수 있습니다. 요청을 보내고 상대방이 수락하면, 카카오톡 오픈 프로필로 연결되어 행사 참여에 관한 대화를 이어나갈 수 있습니다.
+
-**2. IT 행사 정보 제공**
+### 3. 행사에 같이가요!
-커디에서는 다양한 IT 행사의 정보를 한눈에 볼 수 있습니다. 관심 있는 행사의 태그를 설정하면 관련 행사 정보의 업데이트 알림을 받을 수 있습니다.
+| ![](https://velog.velcdn.com/images/kerdy-official/post/439361f4-076a-4b0c-b32e-1f236e90874a/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/eb500827-3deb-40d8-a020-44542f443140/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
-**3. 커뮤니티**
+
+상대방의 활동 이력과 관심사를 확인하고 마음이 맞으면 `함께하기 요청` 을 시도해보세요.
+
+
-행사에 관한 후기나 의견을 자유롭게 공유할 수 있는 댓글 커뮤니티 기능을 제공합니다. 행사에 참여한 후기나 궁금한 점을 나누어보세요.
+| ![](https://velog.velcdn.com/images/kerdy-official/post/0431e0bb-40f5-404a-b560-7c96309e1965/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/0a8de7ee-99d6-4cbb-9282-bf9b073c15f2/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
-## 🌈 기대 효과
+약간의 채팅을 통해 상대방이 어떤 사람인지 알아보고, 만나고자 하는 시간과 장소를 정해보세요!
-그럼 커디를 통해 어떤 변화를 가져올 수 있을까요?
+간단한 소통만으로 `행사에 대한 두려움` 을 조금은 덜어냈네요!
-첫째, **행사 참여의 장벽을 낮춥니다.** 주변에 같은 관심사를 가진 동료가 없어도 커디를 통해 쉽게 동료를 찾을 수 있습니다.
+### 4. 행사 게시판을 통해 의견을 공유하세요!
-둘째, **행사의 질을 향상시킵니다.** 다양한 참여자들의 리뷰와 의견을 통해 더 나은 행사를 기획하고 개선할 수 있습니다.
+| ![](https://velog.velcdn.com/images/kerdy-official/post/7b0a19da-8adf-4995-8183-ede78039657f/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/1594c6c2-4085-45cf-977f-fa1b4f893d5e/image.png) | ![](https://velog.velcdn.com/images/kerdy-official/post/dd995906-1ac5-4142-a978-4a67dd6f94b7/image.png) |
+| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
-셋째, **활발한 행사 참여를 도모합니다.** 개발자들이 관심 있어 할 IT 행사 정보를 한 데 모아 제공함으로써, 많은 개발자들이 유익한 경험을 할 수 있도록 토대를 마련합니다.
+커디의 행사 상세정보 페이지는 더욱 풍부한 상호작용과 소통을 위해 게시판 기능을 제공하고 있습니다.
-커디는 단순한 정보 제공 서비스가 아닙니다. 사용자들 간의 연결과 커뮤니케이션을 중심으로 한 서비스로써 개발자 커뮤니티의 활성화에 기여하고자 합니다.
+이 기능을 통해 참가자 간의 소통을 쉽게 할 수 있고, 다양한 정보를 공유하고 토론할 수 있어요.
-지금 바로 커디를 통해 특별한 인연과 유익한 행사 정보를 얻어보세요!
+게시판 탭에서 행사에 대한 후기, QNA 등 자유롭게 이야기를 나눠 보세요!
-# 설치 URL
-[Google PlayStore 설치 URL](https://play.google.com/store/apps/details?id=com.emmsale&hl=ko-KR)
+# 팀원 소개
-# 팀 구성
### Android
diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/ActivityApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/ActivityApiTest.java
index 8706deef9..6d1d83fe4 100644
--- a/backend/emm-sale/src/documentTest/java/com/emmsale/ActivityApiTest.java
+++ b/backend/emm-sale/src/documentTest/java/com/emmsale/ActivityApiTest.java
@@ -12,7 +12,6 @@
import com.emmsale.activity.api.ActivityApi;
import com.emmsale.activity.application.dto.ActivityAddRequest;
import com.emmsale.activity.application.dto.ActivityResponse;
-import com.emmsale.activity.application.dto.ActivityResponses;
import com.emmsale.activity.domain.ActivityType;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
@@ -36,42 +35,26 @@ class ActivityApiTest extends MockMvcTestHelper {
void findAll() throws Exception {
// given
final ResponseFieldsSnippet responseFields = PayloadDocumentation.responseFields(
- PayloadDocumentation.fieldWithPath("[].activityType").type(JsonFieldType.STRING)
- .description("activity 분류"),
- PayloadDocumentation.fieldWithPath("[].activityResponses[].id").type(JsonFieldType.NUMBER)
- .description("activity id"),
- PayloadDocumentation.fieldWithPath("[].activityResponses[].name").type(JsonFieldType.STRING)
- .description("activity 이름")
+ fieldWithPath("[].id").type(JsonFieldType.NUMBER).description("activity id"),
+ fieldWithPath("[].activityType").type(JsonFieldType.STRING).description("activity 분류"),
+ fieldWithPath("[].name").type(JsonFieldType.STRING).description("activity 이름")
);
- final List activityResponses = List.of(
- new ActivityResponses("동아리",
- List.of(
- new ActivityResponse(1L, "YAPP"),
- new ActivityResponse(2L, "DND"),
- new ActivityResponse(3L, "nexters")
- )),
- new ActivityResponses("컨퍼런스",
- List.of(
- new ActivityResponse(4L, "인프콘")
- )),
- new ActivityResponses("교육",
- List.of(
- new ActivityResponse(5L, "우아한테크코스")
- )),
- new ActivityResponses("직무",
- List.of(
- new ActivityResponse(6L, "Backend")
- ))
+ final List expected = List.of(
+ new ActivityResponse(1L, "동아리", "YAPP"),
+ new ActivityResponse(2L, "동아리", "DND"),
+ new ActivityResponse(3L, "동아리", "nexters"),
+ new ActivityResponse(4L, "컨퍼런스", "인프콘"),
+ new ActivityResponse(5L, "교육", "우아한테크코스"),
+ new ActivityResponse(6L, "직무", "Backend")
);
- Mockito.when(activityQueryService.findAll()).thenReturn(activityResponses);
+ Mockito.when(activityQueryService.findAll()).thenReturn(expected);
// when & then
-
mockMvc.perform(MockMvcRequestBuilders.get("/activities"))
- .andExpect(MockMvcResultMatchers.status().isOk())
- .andDo(MockMvcRestDocumentation.document("find-all-activities", responseFields));
+ .andExpect(MockMvcResultMatchers.status().isOk())
+ .andDo(MockMvcRestDocumentation.document("find-all-activities", responseFields));
}
@Test
@@ -79,25 +62,29 @@ void findAll() throws Exception {
void addTag() throws Exception {
//given
final RequestFieldsSnippet requestFields = requestFields(
- fieldWithPath("activityType").type(JsonFieldType.STRING).description("활동 유형"),
- fieldWithPath("name").type(JsonFieldType.STRING).description("활동 이름")
+ fieldWithPath("activityType").type(JsonFieldType.STRING).description("활동 유형"),
+ fieldWithPath("name").type(JsonFieldType.STRING).description("활동 이름")
);
final ActivityAddRequest request = new ActivityAddRequest(ActivityType.CLUB, "DND");
- final ActivityResponse response = new ActivityResponse(3L, "DND");
+ final ActivityResponse response = new ActivityResponse(3L,
+ ActivityType.CLUB.getValue(),
+ "DND"
+ );
when(activityCommandService.addActivity(any(ActivityAddRequest.class))).thenReturn(response);
final ResponseFieldsSnippet responseFields = responseFields(
- fieldWithPath("id").type(JsonFieldType.NUMBER).description("활동 식별자"),
- fieldWithPath("name").type(JsonFieldType.STRING).description("활동 이름")
+ fieldWithPath("id").type(JsonFieldType.NUMBER).description("활동 식별자"),
+ fieldWithPath("activityType").type(JsonFieldType.STRING).description("활동 종류"),
+ fieldWithPath("name").type(JsonFieldType.STRING).description("활동 이름")
);
//when & then
mockMvc.perform(post("/activities")
- .contentType(MediaType.APPLICATION_JSON)
- .content(objectMapper.writeValueAsString(request)))
- .andExpect(status().isCreated())
- .andDo(document("add-activity", requestFields, responseFields));
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(objectMapper.writeValueAsString(request)))
+ .andExpect(status().isCreated())
+ .andDo(document("add-activity", requestFields, responseFields));
}
}
diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java
index 95786f384..b9ea40906 100644
--- a/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java
+++ b/backend/emm-sale/src/documentTest/java/com/emmsale/EventApiTest.java
@@ -23,7 +23,6 @@
import com.emmsale.tag.TagFixture;
import com.emmsale.tag.application.dto.TagRequest;
import java.nio.charset.StandardCharsets;
-import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
@@ -109,8 +108,8 @@ void findEvent() throws Exception {
void findEvents() throws Exception {
// given
final RequestParametersSnippet requestParameters = requestParameters(
- RequestDocumentation.parameterWithName("category")
- .description("행사 카테고리(CONFERENCE, COMPETITION)"),
+ RequestDocumentation.parameterWithName("category").optional()
+ .description("행사 카테고리(CONFERENCE, COMPETITION)(option)"),
RequestDocumentation.parameterWithName("start_date")
.description("필터링하려는 기간의 시작일(yyyy:mm:dd)(option)")
.optional(),
@@ -171,7 +170,7 @@ void findEvents() throws Exception {
);
Mockito.when(eventService.findEvents(any(EventType.class),
- any(LocalDate.class), eq("2023-07-01"),
+ any(LocalDateTime.class), eq("2023-07-01"),
eq("2023-07-31"),
eq(null), any(), eq("컨퍼"))).thenReturn(eventResponses);
@@ -223,7 +222,7 @@ void updateEventTest() throws Exception {
request.getLocation(),
tags.stream().map(TagRequest::getName).collect(Collectors.toList()),
"image1.jpg", request.getType().toString(),
- List.of("imageUrl1", "imageUrl2"), "행사기관", "유료","온라인");
+ List.of("imageUrl1", "imageUrl2"), "행사기관", "유료", "온라인");
Mockito.when(eventService.updateEvent(eq(eventId), any(EventDetailRequest.class), any()))
.thenReturn(response);
@@ -325,7 +324,7 @@ void addEventTest() throws Exception {
request.getLocation(),
tags.stream().map(TagRequest::getName).collect(Collectors.toList()),
"image1.jpg", request.getType().toString(),
- List.of("imageUrl1", "imageUrl2"), "행사기관", "무료","오프라인");
+ List.of("imageUrl1", "imageUrl2"), "행사기관", "무료", "오프라인");
Mockito.when(eventService.addEvent(any(EventDetailRequest.class), any()))
.thenReturn(response);
diff --git a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java
index 0875c101c..ea596c11d 100644
--- a/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java
+++ b/backend/emm-sale/src/documentTest/java/com/emmsale/MemberApiTest.java
@@ -22,6 +22,7 @@
import com.emmsale.member.application.dto.MemberActivityInitialRequest;
import com.emmsale.member.application.dto.MemberActivityResponse;
import com.emmsale.member.application.dto.MemberActivityResponses;
+import com.emmsale.member.application.dto.MemberImageResponse;
import com.emmsale.member.application.dto.MemberProfileResponse;
import com.emmsale.member.application.dto.OpenProfileUrlRequest;
import com.emmsale.member.domain.Member;
@@ -266,14 +267,14 @@ void deleteMemberTest() throws Exception {
@DisplayName("멤버 프로필을 변경할 수 있다.")
void updateProfile() throws Exception {
//given
- final String imageUrl = "http://imageUrl.png";
+ final MemberImageResponse memberImageResponse = new MemberImageResponse("http://imageUrl.png");
final Long memberId = 1L;
final String accessToken = "access_token";
final MockMultipartHttpServletRequestBuilder builder = createUpdateProfileBuilder(memberId);
when(memberUpdateService.updateMemberProfile
(any(MultipartFile.class), anyLong(), any(Member.class)))
- .thenReturn(imageUrl);
+ .thenReturn(memberImageResponse);
//when
mockMvc.perform(builder
diff --git a/backend/emm-sale/src/main/java/com/emmsale/activity/api/ActivityApi.java b/backend/emm-sale/src/main/java/com/emmsale/activity/api/ActivityApi.java
index c1c60dc2c..ed2e74e79 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/activity/api/ActivityApi.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/activity/api/ActivityApi.java
@@ -4,7 +4,6 @@
import com.emmsale.activity.application.ActivityQueryService;
import com.emmsale.activity.application.dto.ActivityAddRequest;
import com.emmsale.activity.application.dto.ActivityResponse;
-import com.emmsale.activity.application.dto.ActivityResponses;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
@@ -24,14 +23,15 @@ public class ActivityApi {
private final ActivityCommandService activityCommandService;
@GetMapping
- public ResponseEntity> findAll() {
+ public ResponseEntity> findAll() {
return ResponseEntity.ok(activityQueryService.findAll());
}
@PostMapping
public ResponseEntity create(
- @RequestBody final ActivityAddRequest request) {
+ @RequestBody final ActivityAddRequest request
+ ) {
return ResponseEntity.status(HttpStatus.CREATED)
- .body(activityCommandService.addActivity(request));
+ .body(activityCommandService.addActivity(request));
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/activity/application/ActivityQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/activity/application/ActivityQueryService.java
index 2ab6c9989..22ae2eff7 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/activity/application/ActivityQueryService.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/activity/application/ActivityQueryService.java
@@ -1,18 +1,10 @@
package com.emmsale.activity.application;
-import static java.util.Comparator.comparing;
-import static java.util.stream.Collectors.groupingBy;
-import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toUnmodifiableList;
import com.emmsale.activity.application.dto.ActivityResponse;
-import com.emmsale.activity.application.dto.ActivityResponses;
-import com.emmsale.activity.domain.Activity;
import com.emmsale.activity.domain.ActivityRepository;
-import com.emmsale.activity.domain.ActivityType;
-import java.util.ArrayList;
-import java.util.EnumMap;
import java.util.List;
-import java.util.Map.Entry;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -24,25 +16,10 @@ public class ActivityQueryService {
private final ActivityRepository activityRepository;
- public List findAll() {
- final EnumMap> groupByActivityType = activityRepository.findAll()
- .stream()
- .sorted(comparing(activity -> activity.getName().toLowerCase()))
- .collect(
- groupingBy(Activity::getActivityType, () -> new EnumMap<>(ActivityType.class), toList())
- );
-
- final List responses = new ArrayList<>();
-
- for (final Entry> entry : groupByActivityType.entrySet()) {
- final List activityResponse = entry.getValue()
- .stream()
- .map(it -> new ActivityResponse(it.getId(), it.getName()))
- .collect(toList());
-
- responses.add(new ActivityResponses(entry.getKey().getValue(), activityResponse));
- }
-
- return responses;
+ public List findAll() {
+ return activityRepository.findAll()
+ .stream()
+ .map(ActivityResponse::from)
+ .collect(toUnmodifiableList());
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java b/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java
index 39af1dcf9..e101a9421 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponse.java
@@ -1,20 +1,32 @@
package com.emmsale.activity.application.dto;
import com.emmsale.activity.domain.Activity;
-import lombok.Getter;
+import lombok.RequiredArgsConstructor;
-@Getter
+@RequiredArgsConstructor
public class ActivityResponse {
private final Long id;
+ private final String activityType;
private final String name;
- public ActivityResponse(final Long id, final String name) {
- this.id = id;
- this.name = name;
+ public static ActivityResponse from(final Activity activity) {
+ return new ActivityResponse(
+ activity.getId(),
+ activity.getActivityType().getValue(),
+ activity.getName()
+ );
}
- public static ActivityResponse from(final Activity activity) {
- return new ActivityResponse(activity.getId(), activity.getName());
+ public Long getId() {
+ return id;
+ }
+
+ public String getActivityType() {
+ return activityType;
+ }
+
+ public String getName() {
+ return name;
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponses.java b/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponses.java
deleted file mode 100644
index e7adc1a9c..000000000
--- a/backend/emm-sale/src/main/java/com/emmsale/activity/application/dto/ActivityResponses.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.emmsale.activity.application.dto;
-
-import java.util.List;
-import lombok.Getter;
-
-@Getter
-public class ActivityResponses {
-
- private final String activityType;
- private final List activityResponses;
-
- public ActivityResponses(final String activityType, final List activityResponses) {
- this.activityType = activityType;
- this.activityResponses = activityResponses;
- }
-}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/comment/application/CommentCommandService.java b/backend/emm-sale/src/main/java/com/emmsale/comment/application/CommentCommandService.java
index 6433a234a..4e90d4d0d 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/comment/application/CommentCommandService.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/comment/application/CommentCommandService.java
@@ -50,11 +50,19 @@ public CommentResponse create(
final Comment savedComment = commentRepository.save(comment);
- eventPublisher.publish(savedComment, member);
+ publishEvent(savedComment, feed);
return CommentResponse.from(savedComment);
}
+ private void publishEvent(final Comment comment, final Feed feed) {
+ if (comment.getParent().isPresent()) {
+ eventPublisher.publish(comment);
+ return;
+ }
+ eventPublisher.publish(comment, feed.getWriter());
+ }
+
private Comment findSavedComment(final Long commentId) {
return commentRepository.findById(commentId)
.orElseThrow(() -> new CommentException(NOT_FOUND_COMMENT));
diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java
index ddfffd5fe..5ecd44ee0 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/event/api/EventApi.java
@@ -7,6 +7,7 @@
import com.emmsale.event.domain.EventStatus;
import com.emmsale.event.domain.EventType;
import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.List;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
@@ -39,14 +40,14 @@ public ResponseEntity findEventById(@PathVariable final Lon
@GetMapping
public ResponseEntity> findEvents(
- @RequestParam final EventType category,
+ @RequestParam(required = false) final EventType category,
@RequestParam(name = "start_date", required = false) final String startDate,
@RequestParam(name = "end_date", required = false) final String endDate,
@RequestParam(required = false) final List tags,
@RequestParam(required = false) final List statuses,
@RequestParam(required = false) final String keyword) {
return ResponseEntity.ok(
- eventService.findEvents(category, LocalDate.now(), startDate, endDate, tags, statuses,
+ eventService.findEvents(category, LocalDateTime.now(), startDate, endDate, tags, statuses,
keyword));
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java
index 1395d849c..8c67d38e1 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/event/application/EventService.java
@@ -92,10 +92,10 @@ private List extractInformationImages(final List imageUrls) {
@Transactional(readOnly = true)
public List findEvents(final EventType category,
- final LocalDate nowDate, final String startDate, final String endDate,
+ final LocalDateTime nowDateTime, final String startDate, final String endDate,
final List tagNames, final List statuses, final String keyword) {
- Specification spec = Specification.where(filterByCategory(category));
-
+ Specification spec = (root, query, criteriaBuilder) -> null;
+ spec = filterByCategoryIfExist(category, spec);
spec = filterByTagIfExist(tagNames, spec);
spec = filterByDateIfExist(startDate, endDate, spec);
spec = filterByKeywordIfExist(keyword, spec);
@@ -103,11 +103,23 @@ public List findEvents(final EventType category,
final List events = eventRepository.findAll(spec);
final EnumMap> eventsForEventStatus
- = groupByEventStatus(nowDate, events);
+ = groupByEventStatus(nowDateTime, events);
return filterByStatuses(statuses, eventsForEventStatus, makeImageUrlPerEventId(events));
}
+ private Specification filterByCategoryIfExist(final EventType category,
+ Specification spec) {
+ if (isExistCategory(category)) {
+ spec = spec.and(filterByCategory(category));
+ }
+ return spec;
+ }
+
+ private boolean isExistCategory(final EventType category) {
+ return category != null;
+ }
+
private Specification filterByTagIfExist(final List tagNames,
Specification spec) {
if (isExistTagNames(tagNames)) {
@@ -198,12 +210,12 @@ private boolean isExistKeyword(final String keyword) {
return keyword != null && !keyword.isBlank();
}
- private EnumMap> groupByEventStatus(final LocalDate nowDate,
+ private EnumMap> groupByEventStatus(final LocalDateTime nowDateTime,
final List events) {
return events.stream()
.sorted(comparing(event -> event.getEventPeriod().getStartDate()))
.collect(
- groupingBy(event -> event.getEventPeriod().calculateEventStatus(nowDate),
+ groupingBy(event -> event.getEventPeriod().calculateEventStatus(nowDateTime),
() -> new EnumMap<>(EventStatus.class), toList())
);
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventPeriod.java b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventPeriod.java
index 72f76076a..79acf04e7 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventPeriod.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/event/domain/EventPeriod.java
@@ -56,30 +56,33 @@ private void validateApplyDateTimes(final LocalDateTime applyStartDateTime,
}
}
- public EventStatus calculateEventStatus(final LocalDate now) {
- if (now.isBefore(startDate.toLocalDate())) {
+ public EventStatus calculateEventStatus(final LocalDateTime now) {
+ if (now.isBefore(startDate)) {
return EventStatus.UPCOMING;
}
- if (now.isAfter(endDate.toLocalDate())) {
+ if (now.isAfter(endDate)) {
return EventStatus.ENDED;
}
return EventStatus.IN_PROGRESS;
}
+ @Deprecated
public int calculateRemainingDays(final LocalDate today) {
return java.time.Period.between(today, startDate.toLocalDate()).getDays();
}
- public EventStatus calculateEventApplyStatus(final LocalDate now) {
- if (now.isBefore(applyStartDate.toLocalDate())) {
+ @Deprecated
+ public EventStatus calculateEventApplyStatus(final LocalDateTime now) {
+ if (now.isBefore(applyStartDate)) {
return EventStatus.UPCOMING;
}
- if (now.isAfter(applyEndDate.toLocalDate())) {
+ if (now.isAfter(applyEndDate)) {
return EventStatus.ENDED;
}
return EventStatus.IN_PROGRESS;
}
+ @Deprecated
public int calculateApplyRemainingDays(final LocalDate today) {
return java.time.Period.between(today, applyStartDate.toLocalDate()).getDays();
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/CommentNotificationEvent.java b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/CommentNotificationEvent.java
index 7ab31dd44..41e71db9b 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/CommentNotificationEvent.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/CommentNotificationEvent.java
@@ -47,4 +47,20 @@ public static CommentNotificationEvent of(final Comment comment, final Comment t
trigger.getParentIdOrSelfId()
);
}
+
+ public static CommentNotificationEvent of(final Comment comment, final Member receiver) {
+ final Member sender = comment.getMember();
+
+ return new CommentNotificationEvent(
+ receiver.getId(),
+ comment.getId(),
+ LocalDateTime.now(),
+ UPDATE_NOTIFICATION_COMMENT_TYPE,
+ comment.getContent(),
+ sender.getName(),
+ sender.getImageUrl(),
+ comment.getFeed().getId(),
+ comment.getParentIdOrSelfId()
+ );
+ }
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/EventPublisher.java b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/EventPublisher.java
index 741117877..2c78eb6d7 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/event_publisher/EventPublisher.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/event_publisher/EventPublisher.java
@@ -25,9 +25,11 @@ public class EventPublisher {
private final MemberRepository memberRepository;
private final CommentRepository commentRepository;
- public void publish(final Comment trigger, final Member loginMember) {
+ public void publish(final Comment trigger) {
+ final Member sender = trigger.getMember();
+
final Set notificationCommentCandidates = trigger.getParent()
- .map(parent -> findRelatedCommentsExcludingLoginMember(loginMember, parent))
+ .map(parent -> findRelatedCommentsExcludingLoginMember(sender, parent))
.orElse(Collections.emptySet());
notificationCommentCandidates.stream()
@@ -35,6 +37,12 @@ public void publish(final Comment trigger, final Member loginMember) {
.forEach(applicationEventPublisher::publishEvent);
}
+ public void publish(final Comment comment, final Member receiver) {
+ if (comment.isNotOwner(receiver.getId())) {
+ applicationEventPublisher.publishEvent(CommentNotificationEvent.of(comment, receiver));
+ }
+ }
+
private Set findRelatedCommentsExcludingLoginMember(
final Member loginMember,
final Comment parent
diff --git a/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java b/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java
index c42ad3bf5..3ceb06e5f 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/image/application/S3Client.java
@@ -27,10 +27,16 @@ public class S3Client {
private final String bucket;
private final AmazonS3 amazonS3;
+ private final String cloudFrontPrefix;
- public S3Client(@Value("${cloud.aws.s3.bucket}") final String bucket, final AmazonS3 amazonS3) {
+ public S3Client(
+ @Value("${cloud.aws.s3.bucket}") final String bucket,
+ @Value("${cloud.aws.cloudfront-prefix}") final String cloudFrontPrefix,
+ final AmazonS3 amazonS3
+ ) {
this.bucket = bucket;
this.amazonS3 = amazonS3;
+ this.cloudFrontPrefix = cloudFrontPrefix;
}
public List uploadImages(final List multipartFiles) {
@@ -84,10 +90,10 @@ public void deleteImages(final List fileNames) {
}
public String convertImageUrl(final String imageName) {
- return String.join(URL_DELIMITER, bucket, imageName);
+ return String.join(URL_DELIMITER, cloudFrontPrefix, imageName);
}
public String convertImageName(final String imageUrl) {
- return imageUrl.split(bucket + URL_DELIMITER, 2)[1];
+ return imageUrl.split(cloudFrontPrefix + URL_DELIMITER, 2)[1];
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java
index 87e3243ad..15b465d2d 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/member/api/MemberApi.java
@@ -7,6 +7,7 @@
import com.emmsale.member.application.dto.MemberActivityAddRequest;
import com.emmsale.member.application.dto.MemberActivityInitialRequest;
import com.emmsale.member.application.dto.MemberActivityResponses;
+import com.emmsale.member.application.dto.MemberImageResponse;
import com.emmsale.member.application.dto.MemberProfileResponse;
import com.emmsale.member.application.dto.OpenProfileUrlRequest;
import com.emmsale.member.domain.Member;
@@ -23,6 +24,7 @@
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
@@ -99,12 +101,13 @@ public ResponseEntity deleteMember(
}
@PatchMapping("/members/{memberId}/profile")
- public ResponseEntity updateProfile(
+ public ResponseEntity updateProfile(
@PathVariable final Long memberId,
- final MultipartFile image,
+ @RequestPart final MultipartFile image,
final Member member
) {
- final String imageUrl = memberUpdateService.updateMemberProfile(image, memberId, member);
- return ResponseEntity.ok(imageUrl);
+ final MemberImageResponse memberImageResponse
+ = memberUpdateService.updateMemberProfile(image, memberId, member);
+ return ResponseEntity.ok(memberImageResponse);
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java
index 603ca02cb..5668771c6 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/MemberUpdateService.java
@@ -2,6 +2,8 @@
import com.emmsale.image.application.S3Client;
import com.emmsale.member.application.dto.DescriptionRequest;
+import com.emmsale.member.application.dto.MemberImageResponse;
+import com.emmsale.member.application.dto.MemberProfileResponse;
import com.emmsale.member.application.dto.OpenProfileUrlRequest;
import com.emmsale.member.domain.Member;
import com.emmsale.member.domain.MemberRepository;
@@ -48,7 +50,7 @@ public void deleteMember(final Member member, final Long memberId) {
memberRepository.deleteById(memberId);
}
- public String updateMemberProfile(
+ public MemberImageResponse updateMemberProfile(
final MultipartFile image,
final Long memberId,
final Member member
@@ -66,6 +68,6 @@ public String updateMemberProfile(
final String imageUrl = s3Client.convertImageUrl(imageName);
member.updateProfile(imageUrl);
- return imageUrl;
+ return new MemberImageResponse(imageUrl);
}
}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberImageResponse.java b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberImageResponse.java
new file mode 100644
index 000000000..4a6ccce1b
--- /dev/null
+++ b/backend/emm-sale/src/main/java/com/emmsale/member/application/dto/MemberImageResponse.java
@@ -0,0 +1,11 @@
+package com.emmsale.member.application.dto;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@RequiredArgsConstructor
+@Getter
+public class MemberImageResponse {
+
+ private final String imageUrl;
+}
diff --git a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java
index 0cf5a3f78..9aa878962 100644
--- a/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java
+++ b/backend/emm-sale/src/main/java/com/emmsale/scrap/application/ScrapQueryService.java
@@ -10,7 +10,7 @@
import com.emmsale.member.domain.Member;
import com.emmsale.scrap.domain.Scrap;
import com.emmsale.scrap.domain.ScrapRepository;
-import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -35,7 +35,8 @@ public List findAllScraps(final Member member) {
.collect(Collectors.toList());
final Map> eventGroupByStatus = scrappedEvents.stream()
- .collect(groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDate.now())));
+ .collect(
+ groupingBy(event -> event.getEventPeriod().calculateEventStatus(LocalDateTime.now())));
return EventResponse.mergeEventResponses(eventGroupByStatus,
makeImageUrlPerEventId(scrappedEvents));
diff --git a/backend/emm-sale/src/main/resources/application.yml b/backend/emm-sale/src/main/resources/application.yml
index 85c40f7fa..652a9a99e 100644
--- a/backend/emm-sale/src/main/resources/application.yml
+++ b/backend/emm-sale/src/main/resources/application.yml
@@ -63,6 +63,7 @@ cloud:
static: ap-northeast-2
stack:
auto: false
+ cloudfront-prefix: empty-url
---
@@ -79,4 +80,3 @@ spring:
import: classpath:kerdy-submodule/application-prod.yml
activate:
on-profile: prod
-
diff --git a/backend/emm-sale/src/main/resources/kerdy-submodule b/backend/emm-sale/src/main/resources/kerdy-submodule
index 31641131c..201498016 160000
--- a/backend/emm-sale/src/main/resources/kerdy-submodule
+++ b/backend/emm-sale/src/main/resources/kerdy-submodule
@@ -1 +1 @@
-Subproject commit 31641131cf5c6c28654418fd8ba2f110140a3e68
+Subproject commit 201498016c6f27f34972c92cf1699252e294033b
diff --git a/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityCommandServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityCommandServiceTest.java
index c702b26d9..b71f3117b 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityCommandServiceTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityCommandServiceTest.java
@@ -25,15 +25,20 @@ void findActivity() {
//given
final String activityName = "DDD";
final ActivityAddRequest request = new ActivityAddRequest(ActivityType.CLUB, activityName);
- final ActivityResponse expected = new ActivityResponse(7L, activityName);
+ final ActivityResponse expected = new ActivityResponse(
+ 7L,
+ ActivityType.CLUB.getValue(),
+ activityName
+ );
//when
- final ActivityResponse actual = activityCommandService.addActivity(request);
+ final ActivityResponse actual
+ = activityCommandService.addActivity(request);
//then
assertThat(actual)
- .usingRecursiveComparison()
- .isEqualTo(expected);
+ .usingRecursiveComparison()
+ .isEqualTo(expected);
}
@Test
@@ -49,7 +54,7 @@ void findActivity_duplicate_fail() {
//then
assertThatThrownBy(actual)
- .isInstanceOf(ActivityException.class)
- .hasMessage(ActivityExceptionType.ALEADY_EXIST_ACTIVITY.errorMessage());
+ .isInstanceOf(ActivityException.class)
+ .hasMessage(ActivityExceptionType.ALEADY_EXIST_ACTIVITY.errorMessage());
}
}
diff --git a/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityQueryServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityQueryServiceTest.java
index c262de4b0..291ba7887 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityQueryServiceTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/activity/application/ActivityQueryServiceTest.java
@@ -1,14 +1,10 @@
package com.emmsale.activity.application;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertAll;
import com.emmsale.activity.application.dto.ActivityResponse;
-import com.emmsale.activity.application.dto.ActivityResponses;
-import com.emmsale.activity.domain.ActivityType;
import com.emmsale.helper.ServiceIntegrationTestHelper;
import java.util.List;
-import java.util.stream.Collectors;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,40 +14,25 @@ class ActivityQueryServiceTest extends ServiceIntegrationTestHelper {
@Autowired
private ActivityQueryService activityQueryService;
-
@Test
@DisplayName("존재하고 있는 Activity를 전체 조회할 수 있다.")
- void findAll() throws Exception {
+ void findAll() {
//given
- final List expectedActivities = List.of(
- ActivityType.CLUB.getValue(),
- ActivityType.CONFERENCE.getValue(),
- ActivityType.JOB.getValue(),
- ActivityType.EDUCATION.getValue()
- );
-
- final List expectedActivityNames = List.of(
- "YAPP", "DND",
- "nexters", "인프콘",
- "우아한테크코스", "Backend"
+ final List expected = List.of(
+ new ActivityResponse(1L, "동아리", "YAPP"),
+ new ActivityResponse(2L, "동아리", "DND"),
+ new ActivityResponse(3L, "동아리", "nexters"),
+ new ActivityResponse(4L, "컨퍼런스", "인프콘"),
+ new ActivityResponse(5L, "교육", "우아한테크코스"),
+ new ActivityResponse(6L, "직무", "Backend")
);
//when
- List activityResponses = activityQueryService.findAll();
+ final List actual = activityQueryService.findAll();
//then
- final List actualActivityNames = activityResponses.stream()
- .flatMap(it -> it.getActivityResponses().stream())
- .map(ActivityResponse::getName)
- .collect(Collectors.toList());
-
- assertAll(
- () -> assertThat(activityResponses).hasSize(4),
- () -> assertThat(activityResponses)
- .extracting("activityType")
- .containsExactlyInAnyOrderElementsOf(expectedActivities),
- () -> assertThat(expectedActivityNames)
- .containsExactlyInAnyOrderElementsOf(actualActivityNames)
- );
+ assertThat(actual)
+ .usingRecursiveComparison()
+ .isEqualTo(expected);
}
}
diff --git a/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceEventIntegrationTest.java b/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceEventIntegrationTest.java
index ca257f068..5ca68d53b 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceEventIntegrationTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/comment/application/CommentCommandServiceEventIntegrationTest.java
@@ -98,4 +98,42 @@ void test_publish_comment_error_firebase() throws Exception {
() -> assertEquals(1, notificationRepository.findAll().size())
);
}
+
+ @Test
+ @DisplayName("publish(Comment, Member) : 피드에 최상위 부모 댓글이 달리면 피드의 작성자에게 알림이 발송된다.")
+ void test_publish_comment_to_feed_writer() {
+ //given
+ final CommentAddRequest 부모_댓글 = new CommentAddRequest("내용1", feed.getId(), null);
+
+ doNothing().when(firebaseCloudMessageClient).sendMessageTo(any(Notification.class), anyLong());
+
+ //when
+ commentCommandService.create(부모_댓글, 댓글_작성자1);
+
+ //then
+ assertAll(
+ () -> verify(firebaseCloudMessageClient, times(1))
+ .sendMessageTo(any(Notification.class), anyLong()),
+ () -> assertEquals(1, notificationRepository.findAll().size())
+ );
+ }
+
+ @Test
+ @DisplayName("publish(Comment, Member) : 피드에 최상위 부모 댓글 작성자와 피드 작성자가 동일할 경우 알림을 발행하지 않는다.")
+ void test_do_not_publish_if_comment_writer_equals_feed_writer() {
+ //given
+ final CommentAddRequest 부모_댓글 = new CommentAddRequest("내용1", feed.getId(), null);
+
+ doNothing().when(firebaseCloudMessageClient).sendMessageTo(any(Notification.class), anyLong());
+
+ //when
+ commentCommandService.create(부모_댓글, feed.getWriter());
+
+ //then
+ assertAll(
+ () -> verify(firebaseCloudMessageClient, times(0))
+ .sendMessageTo(any(Notification.class), anyLong()),
+ () -> assertEquals(0, notificationRepository.findAll().size())
+ );
+ }
}
diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java
index 57d0e030a..5ba3f7257 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/event/application/EventServiceTest.java
@@ -90,8 +90,7 @@ class EventServiceTest extends ServiceIntegrationTestHelper {
private static final EventResponse 구름톤 = new EventResponse(null, "구름톤", null, null, null, null,
List.of(), null, EventMode.ONLINE.getValue(), PaymentType.PAID.getValue());
-
- private static final LocalDate TODAY = LocalDate.of(2023, 7, 21);
+ private static final LocalDateTime TODAY = LocalDateTime.of(2023, 7, 21, 0, 0);
@Autowired
private EventService eventService;
@Autowired
@@ -242,6 +241,24 @@ void fail_EventNotFoundException() {
@DisplayName("findEvents() : 행사 목록 조회")
class findEvents {
+ @Test
+ @DisplayName("2023년 7월 21일에 행사를 조회하면, 모든 행사 목록을 조회할 수 있다.")
+ void findEvents_all() {
+ // given
+ final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스,
+ 안드로이드_컨퍼런스, AI_아이디어_공모전);
+
+ // when
+ final List actualEvents = eventService.findEvents(null, TODAY,
+ null, null, null, null, null);
+
+ // then
+ assertThat(actualEvents)
+ .usingRecursiveComparison()
+ .comparingOnlyFields("name", "status", "applyStatus", "imageUrl")
+ .isEqualTo(expectedEvents);
+ }
+
@Test
@DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회하면, 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.")
void findEvents_CONFERENCE() {
@@ -521,11 +538,11 @@ class SearchEvent {
@DisplayName("2023년 7월 21일에 컨퍼런스 행사를 조회할 때, 검색어가 공백인 경우 해당 카테고리에 해당하는 모든 행사 목록을 조회할 수 있다.")
void findEvents_blank_search(final String keyword) {
// given
- final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, AI_컨퍼런스, 모바일_컨퍼런스,
- 안드로이드_컨퍼런스);
+ final List expectedEvents = List.of(인프콘_2023, 웹_컨퍼런스, 구름톤, AI_컨퍼런스, 모바일_컨퍼런스,
+ 안드로이드_컨퍼런스, AI_아이디어_공모전);
// when
- final List actualEvents = eventService.findEvents(EventType.CONFERENCE,
+ final List actualEvents = eventService.findEvents(null,
TODAY,
null, null, null, null, keyword);
@@ -545,7 +562,7 @@ void findEvents_search() {
안드로이드_컨퍼런스);
// when
- final List actualEvents = eventService.findEvents(EventType.CONFERENCE,
+ final List actualEvents = eventService.findEvents(null,
TODAY, null, null, null, null, keyword);
// then
@@ -563,7 +580,7 @@ void findEvents_multiple_tokens_search() {
final List expectedEvents = List.of(모바일_컨퍼런스);
// when
- final List actualEvents = eventService.findEvents(EventType.CONFERENCE,
+ final List actualEvents = eventService.findEvents(null,
TODAY, null, null, null, null, keyword);
// then
@@ -581,7 +598,7 @@ void findEvents_status_filter_and_search() {
final List expectedEvents = List.of(인프콘_2023);
// when
- final List actualEvents = eventService.findEvents(EventType.CONFERENCE,
+ final List actualEvents = eventService.findEvents(null,
TODAY,
null, null, null, List.of(IN_PROGRESS), keyword);
diff --git a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java
index 7c5053ac0..1a60329d6 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/event/domain/EventTest.java
@@ -31,10 +31,12 @@
class EventTest {
@ParameterizedTest
- @CsvSource(value = {"2023-03-01,UPCOMING", "2023-07-25,IN_PROGRESS",
- "2023-08-01,ENDED"}, delimiter = ',')
+ @CsvSource(value = {"2023-03-01T00:00:00,UPCOMING", "2023-07-25T00:00:00,IN_PROGRESS",
+ "2023-08-01T00:00:00,ENDED", "2023-07-22T12:00:00,IN_PROGRESS",
+ "2023-07-22T12:00:01,IN_PROGRESS", "2023-07-30T12:00:00,IN_PROGRESS",
+ "2023-07-30T12:00:01,ENDED"}, delimiter = ',')
@DisplayName("현재 날짜가 주어지면 행사의 진행 상태를 계산한다.")
- void calculateEventStatus(LocalDate input, EventStatus expected) {
+ void calculateEventStatus(LocalDateTime input, EventStatus expected) {
// given, when
EventStatus actual = EventFixture.AI_컨퍼런스().getEventPeriod().calculateEventStatus(input);
@@ -273,6 +275,7 @@ void updateEvent_fail_SUBSCRIPTION_START_AFTER_EVENT_START() {
exception.exceptionType());
}
+ @Deprecated
@Test
@DisplayName("현재날짜로부터 남은 날짜를 계산할 수 있다.")
void calculateRemainingDay() {
@@ -288,6 +291,7 @@ void calculateRemainingDay() {
.isEqualTo(5);
}
+ @Deprecated
@Test
@DisplayName("현재날짜로부터 신청 시작일까지 남은 날짜를 계산할 수 있다.")
void calculateApplyRemainingDay() {
diff --git a/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java b/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java
index 01622e6d1..5cb32927c 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/event_publisher/EventPublisherTest.java
@@ -187,7 +187,7 @@ void test_publish_comment_parent_children() throws Exception {
Comment.createChild(feed, 부모_댓글, 로그인_사용자, "내용4"));
//when
- eventPublisher.publish(알림_트리거_댓글, 로그인_사용자);
+ eventPublisher.publish(알림_트리거_댓글);
//then
verify(applicationEventPublisher, times(2))
@@ -212,7 +212,7 @@ void test_publish_comment_parent_children_not_notification() throws Exception {
);
//when
- eventPublisher.publish(알림_트리거_댓글, 댓글_작성자1);
+ eventPublisher.publish(알림_트리거_댓글);
//then
verify(applicationEventPublisher, times(0))
@@ -231,7 +231,7 @@ void test_publish_root_comment_no_notification() throws Exception {
);
//when
- eventPublisher.publish(알림_트리거_댓글, 댓글_작성자1);
+ eventPublisher.publish(알림_트리거_댓글);
//then
verify(applicationEventPublisher, times(0))
@@ -266,7 +266,7 @@ void test_publish_comment_not_notification_deletedComment() throws Exception {
);
//when
- eventPublisher.publish(알림_트리거_댓글, 로그인_사용자);
+ eventPublisher.publish(알림_트리거_댓글);
//then
verify(applicationEventPublisher, times(1))
@@ -301,7 +301,7 @@ void test_publish_comment_not_notification_deletedComment2() throws Exception {
);
//when
- eventPublisher.publish(알림_트리거_댓글, 로그인_사용자);
+ eventPublisher.publish(알림_트리거_댓글);
//then
verify(applicationEventPublisher, times(2))
@@ -337,4 +337,41 @@ void publishMessageEvent() {
.usingRecursiveComparison()
.isEqualTo(expectedEvent);
}
+
+ @Test
+ @DisplayName("publish(Comment, Member) : 피드에 ROOT 댓글이 작성되면 피드의 작성자에게 알림이 발송된다.")
+ void test_publish_to_feed_writer_when_root_comment_created() throws Exception {
+ //given
+ final Member 댓글_작성자1 = memberRepository.findById(1L).get();
+ eventRepository.save(event);
+ feedRepository.save(feed);
+ final Comment 알림_트리거_댓글 = commentRepository.save(
+ Comment.createRoot(feed, 댓글_작성자1, "내용1")
+ );
+
+ //when
+ eventPublisher.publish(알림_트리거_댓글, feed.getWriter());
+
+ //then
+ verify(applicationEventPublisher, times(1))
+ .publishEvent(any(NotificationEvent.class));
+ }
+
+ @Test
+ @DisplayName("publish(Comment, Member) : 피드 작성자와 피드의 ROOT 댓글 작성자가 동일할 경우 알림을 발송하지 않는다.")
+ void test_do_not_publish_notification_if_comment_writer_equals_feed_writer() throws Exception {
+ //given
+ eventRepository.save(event);
+ feedRepository.save(feed);
+ final Comment 알림_트리거_댓글 = commentRepository.save(
+ Comment.createRoot(feed, feed.getWriter(), "내용1")
+ );
+
+ //when
+ eventPublisher.publish(알림_트리거_댓글, feed.getWriter());
+
+ //then
+ verify(applicationEventPublisher, times(0))
+ .publishEvent(any(EventNotificationEvent.class));
+ }
}
diff --git a/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java b/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java
index cf4a8c7d4..81d3ec22b 100644
--- a/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java
+++ b/backend/emm-sale/src/test/java/com/emmsale/image/application/S3ClientTest.java
@@ -27,6 +27,7 @@
class S3ClientTest {
private static final String TEST_BUCKET = "Test";
+ private static final String CLOUD_FRONT_PREFIX = "cloudFrontPrefix";
private S3Client s3Client;
private AmazonS3 mockingAmazonS3;
@@ -34,7 +35,7 @@ class S3ClientTest {
@BeforeEach
void setUp() {
mockingAmazonS3 = mock(AmazonS3.class);
- s3Client = new S3Client(TEST_BUCKET, mockingAmazonS3);
+ s3Client = new S3Client(TEST_BUCKET, CLOUD_FRONT_PREFIX, mockingAmazonS3);
}
@Test
@@ -92,7 +93,7 @@ void deleteImages_success() {
@DisplayName("convertImageUrl(): 이미지 이름을 imageUrl로 바꾼다.")
void convertImageUrl() {
final String imageName = "image.png";
- final String expected = "Test/image.png";
+ final String expected = CLOUD_FRONT_PREFIX + "/image.png";
final String actual = s3Client.convertImageUrl(imageName);
@@ -103,7 +104,7 @@ void convertImageUrl() {
@Test
@DisplayName("convertImageName(): 이미지 이름을 imageName로 바꾼다.")
void convertImageName() {
- final String imageUrl = "Test/image.png";
+ final String imageUrl = CLOUD_FRONT_PREFIX + "/image.png";
final String expected = "image.png";
final String actual = s3Client.convertImageName(imageUrl);