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

배포 버전 0.3.1 #95

Merged
merged 48 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a222d29
refactor: 유저 프로필의 생년월일, 이름 필드 제거
1o18z Nov 7, 2023
d39a693
test: 변경된 유저 프로필 정보에 따라 테스트 코드 변경
1o18z Nov 7, 2023
2101400
refactor: aws config 구조 변경
smartandhandsome Nov 6, 2023
c91291a
chore: 의존성 추가 및 AWS 의존성 최소한으로 변경
smartandhandsome Nov 6, 2023
8ae8b4c
chore: firebase 키 패스 추가
smartandhandsome Nov 6, 2023
be0825f
feat: 토큰 생성 및 업데이트 로직 구현
smartandhandsome Nov 6, 2023
7911433
feat: 파이어베이스 설정 구현
smartandhandsome Nov 6, 2023
11bec29
style: 패키지 구조 변경
smartandhandsome Nov 6, 2023
a3db7aa
feat: 알림 정보 구현
smartandhandsome Nov 6, 2023
a1c5b81
test: private 추가
smartandhandsome Nov 6, 2023
1e17420
fix: return 값 수정
smartandhandsome Nov 6, 2023
5c3c6b7
feat: 알림 거절 기능 추가
smartandhandsome Nov 6, 2023
4c89129
feat: 에러, 에러코드, 핸들러 추가
smartandhandsome Nov 6, 2023
4ed859a
test: 불필요한 코드 삭제
smartandhandsome Nov 7, 2023
3234d2a
test: 유저 알림 토큰 등록 및 알림 거부 테스트 구현
smartandhandsome Nov 7, 2023
d4f8ed8
test: 알림 픽스처 구현
smartandhandsome Nov 7, 2023
384dd1e
test: 유저 알림 토큰 등록 및 알림 거부 테스트 구현
smartandhandsome Nov 7, 2023
6b2031a
feat: 알림 보내기 구현
smartandhandsome Nov 7, 2023
78be753
test: 알림 보내기 테스트 구현
smartandhandsome Nov 7, 2023
b389407
refactor: code 변경
smartandhandsome Nov 7, 2023
81ef481
refactor: isSubscribedToNotification 추가
smartandhandsome Nov 7, 2023
4732a83
style: 포맷팅
smartandhandsome Nov 7, 2023
4067bb7
refactor: 예외처리 방식 변경
smartandhandsome Nov 7, 2023
6b12e18
refactor: 정적 팩토리 방식으로 변경
smartandhandsome Nov 7, 2023
0619375
chore: firebase key 설정 변경
smartandhandsome Nov 7, 2023
07fe336
refactor: 유저 프로필 기본값 지정 로직 수정
1o18z Nov 7, 2023
a0ca604
Merge branch 'dev' into refactor/#92
Nov 8, 2023
0812f0b
test: 변경된 유저 프로필 정보에 따라 UserFixture 수정
1o18z Nov 8, 2023
7c4b2c9
feat: 정적 팩토리 메서드 추가
Nov 8, 2023
ee69ced
feat: ChattingDto 작성
Nov 8, 2023
bce3ae7
feat: 채팅 내역을 DB에 저장하고, 채팅 응답을 만들어 반환하는 메서드 추가
Nov 8, 2023
086d0b5
test: chattingTest 작성
Nov 8, 2023
a265dbf
test: message() 수정
Nov 8, 2023
e2f7daa
refactor: 프로필 기본생성자 접근제어자 변경
1o18z Nov 8, 2023
37428b4
Merge pull request #93 from coffee-meet/refactor/#92
1o18z Nov 8, 2023
74ec237
Merge pull request #94 from coffee-meet/feat/#90
Nov 8, 2023
14488e0
feat: update cd script
Nov 8, 2023
a26cca7
feat: update yml
Nov 8, 2023
99fd7ff
feat: code formatting
Nov 8, 2023
7210ae2
feat: update cd script
Nov 8, 2023
eb7e988
chore: secret에 있는 키 파일 복사 로직
smartandhandsome Nov 8, 2023
02dc731
refactor: 위치 변경
smartandhandsome Nov 8, 2023
f906dc8
feat: 서브모듈 반영
smartandhandsome Nov 8, 2023
504a394
feat: update cd script
Nov 8, 2023
277c53a
refactor: 서브 모듈 위치 변경
smartandhandsome Nov 8, 2023
46c31d4
refactor: 경로 변경
smartandhandsome Nov 8, 2023
42ae90f
chore: 카피 삭제
smartandhandsome Nov 8, 2023
c8c04a4
feat: update cd script
Nov 8, 2023
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
5 changes: 4 additions & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ jobs:
test_and_deploy:
runs-on: ubuntu-latest
steps:
- name: 현재 작업중인 Repository 가져온다.
- name: 현재 작업중인 Repository를 서브 모듈과 함께 가져온다.
uses: actions/checkout@v3
with:
submodules: recursive
token: ${{ secrets.ACTION_TOKEN }}

- name: CD 프로세스를 최적화 하기 위해 Gradle 정보를 캐싱한다.
uses: actions/cache@v3
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
.env

### STS ###
.apt_generated
Expand Down Expand Up @@ -36,3 +35,7 @@ out/

### VS Code ###
.vscode/

### Secret ###
firebase-service-key.json
.env
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "src/main/resources/secret"]
path = src/main/resources/secret
url = https://github.com/coffee-meet/secret.git
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ dependencies {
swaggerUI 'org.webjars:swagger-ui:4.11.1'

/* Cloud */
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.581'
implementation 'com.google.firebase:firebase-admin:9.1.1'

/* WebSocket */
implementation 'org.springframework.boot:spring-boot-starter-websocket'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public enum CertificationErrorCode implements ErrorCode {

@Override
public String code() {
return null;
return errorCode;
}

@Override
public String message() {
return null;
return message;
}

}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package coffeemeet.server.chatting.current.presentation;

import coffeemeet.server.chatting.current.presentation.dto.ChatStomp;
import coffeemeet.server.chatting.current.presentation.dto.ChatStomp.Response;
import coffeemeet.server.chatting.current.service.ChattingMessageService;
import coffeemeet.server.chatting.current.service.dto.ChattingDto;
import jakarta.validation.Valid;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -42,10 +41,10 @@ public void onDisconnect(SessionDisconnectEvent event) {
@MessageMapping("/chatting/messages")
public void message(@Valid ChatStomp.Request request, SimpMessageHeaderAccessor accessor) {
Long userId = sessions.get(accessor.getSessionId());
chattingMessageService.createChattingMessage(request.roomId(), request.content(), userId);
log.info("채팅 = {}", request.content());
ChattingDto.Response response = chattingMessageService.chatting(request.roomId(),
request.content(), userId);
simpMessageSendingOperations.convertAndSend("/sub/chatting/rooms/" + request.roomId(),
new Response("유명한", request.content(), LocalDateTime.now()));
ChatStomp.Response.from(response));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package coffeemeet.server.chatting.current.presentation.dto;

import coffeemeet.server.chatting.current.service.dto.ChattingDto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
Expand All @@ -21,6 +22,14 @@ record Response(
LocalDateTime createdAt
) implements ChatStomp {

public static ChatStomp.Response from(ChattingDto.Response response) {
return new ChatStomp.Response(
response.nickname(),
response.content(),
response.createdAt()
);
}

}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package coffeemeet.server.chatting.current.service;

import coffeemeet.server.chatting.current.domain.ChattingMessage;
import coffeemeet.server.chatting.current.domain.ChattingRoom;
import coffeemeet.server.chatting.current.implement.ChattingMessageCommand;
import coffeemeet.server.chatting.current.implement.ChattingRoomQuery;
import coffeemeet.server.chatting.current.service.dto.ChattingDto;
import coffeemeet.server.user.domain.User;
import coffeemeet.server.user.implement.UserQuery;
import lombok.RequiredArgsConstructor;
Expand All @@ -16,10 +18,12 @@ public class ChattingMessageService {
private final ChattingRoomQuery chattingRoomQuery;
private final UserQuery userQuery;

public void createChattingMessage(Long roomId, String content, Long userId) {
public ChattingDto.Response chatting(Long roomId, String content, Long userId) {
User user = userQuery.getUserById(userId);
ChattingRoom chattingRoom = chattingRoomQuery.getChattingRoomById(roomId);
chattingMessageCommand.saveChattingMessage(content, chattingRoom, user);
ChattingMessage chattingMessage = chattingMessageCommand.saveChattingMessage(content,
chattingRoom, user);
return ChattingDto.Response.of(user, chattingMessage);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package coffeemeet.server.chatting.current.service.dto;

import coffeemeet.server.chatting.current.domain.ChattingMessage;
import coffeemeet.server.user.domain.User;
import java.time.LocalDateTime;

public sealed interface ChattingDto permits ChattingDto.Response {

record Response(
String nickname,
String content,
LocalDateTime createdAt
) implements ChattingDto {

public static Response of(User user, ChattingMessage chattingMessage) {
return new Response(
user.getProfile().getNickname(),
chattingMessage.getMessage(),
chattingMessage.getCreatedAt()
);
}

}

}
11 changes: 8 additions & 3 deletions src/main/java/coffeemeet/server/common/config/AWSConfig.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package coffeemeet.server.common.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
Expand All @@ -25,12 +26,16 @@ public AWSConfig(
}

@Bean
public AmazonS3 amazonS3Client() {
BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
public AWSCredentials awsCredentials() {
return new BasicAWSCredentials(accessKey, secretKey);
}

@Bean
public AmazonS3 amazonS3Client(AWSCredentials awsCredentials) {
return AmazonS3ClientBuilder
.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package coffeemeet.server.common.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import java.io.IOException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class FirebaseConfig {

@Bean
FirebaseApp firebaseApp() throws IOException {
String path = "secret/firebase-service-key.json";
ClassPathResource resource = new ClassPathResource(path);

FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(resource.getInputStream())).build();

return FirebaseApp.initializeApp(options);
}

@Bean
FirebaseMessaging firebaseMessaging(FirebaseApp firebaseApp) {
return FirebaseMessaging.getInstance(firebaseApp);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
public enum GlobalErrorCode implements ErrorCode {

VALIDATION_ERROR("G000", "유효하지 않은 입력입니다."),
INVALID_S3_URL("G004", "유효하지 않은 s3 url 입니다."),
STOMP_ACCESSOR_NOT_FOUND("G004", "웹소켓 연결을 할 수 없습니다."),
INTERNAL_SERVER_ERROR("G050", "예상치 못한 오류입니다.");
INVALID_FCM_TOKEN("G004", "유효하지 않은 FCM토큰입니다."),
INVALID_S3_URL("G004", "유효하지 않은 s3 URL 입니다."),
STOMP_ACCESSOR_NOT_FOUND("G005", "웹소켓 연결을 할 수 없습니다."),
INTERNAL_SERVER_ERROR("G050", "예상치 못한 오류입니다."),
PUSH_NOTIFICATION_SEND_FAILURE("G050", "푸시 알림 전송에 실패했습니다.");

private final String code;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package coffeemeet.server.common.execption;

import lombok.Getter;

@Getter
public class NotificationFailException extends CoffeeMeetException {

private final ErrorCode errorCode;

public NotificationFailException(ErrorCode errorCode, String message) {
super(message);
this.errorCode = errorCode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package coffeemeet.server.common.implement;

import static coffeemeet.server.common.execption.GlobalErrorCode.INVALID_FCM_TOKEN;
import static coffeemeet.server.common.execption.GlobalErrorCode.PUSH_NOTIFICATION_SEND_FAILURE;
import static com.google.firebase.messaging.MessagingErrorCode.INVALID_ARGUMENT;
import static com.google.firebase.messaging.MessagingErrorCode.UNREGISTERED;

import coffeemeet.server.common.execption.InvalidInputException;
import coffeemeet.server.common.execption.NotificationFailException;
import coffeemeet.server.user.domain.NotificationInfo;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.MessagingErrorCode;
import com.google.firebase.messaging.MulticastMessage;
import com.google.firebase.messaging.Notification;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class FCMNotificationSender {

private static final String INVALID_FCM_TOKEN_MESSAGE = "사용된 토큰(%s)이 유효하지 않습니다.";
private static final String PUSH_NOTIFICATION_SEND_FAILURE_MESSAGE = "푸시 알림 전송에 실패했습니다. 토큰: %s";
private final FirebaseMessaging firebaseMessaging;

public void sendNotificationByToken(NotificationInfo notificationInfo, String content) {
if (!notificationInfo.isSubscribedToNotification()) {
return;
}

Notification notification = createNotification(content);
Message message = Message.builder().setToken(notificationInfo.getToken())
.setNotification(notification)
.build();

try {
firebaseMessaging.send(message);
} catch (FirebaseMessagingException e) {
handleFirebaseMessagingException(e, notificationInfo.getToken());
}
}

public void sendNotificationByTokens(Set<NotificationInfo> notificationInfos, String content) {
notificationInfos.removeIf(notificationInfo -> !notificationInfo.isSubscribedToNotification());

Set<String> tokens = notificationInfos.stream().map(
NotificationInfo::getToken
).collect(Collectors.toUnmodifiableSet());

Notification notification = createNotification(content);
MulticastMessage message = MulticastMessage.builder().addAllTokens(tokens)
.setNotification(notification).build();

try {
firebaseMessaging.sendMulticast(message);
} catch (FirebaseMessagingException e) {
handleFirebaseMessagingException(e, String.join(", ", tokens)); //
}
}

private void handleFirebaseMessagingException(FirebaseMessagingException e, String token) {
MessagingErrorCode messagingErrorCode = e.getMessagingErrorCode();

if (messagingErrorCode == UNREGISTERED || messagingErrorCode == INVALID_ARGUMENT) {
throw new InvalidInputException(INVALID_FCM_TOKEN,
String.format(INVALID_FCM_TOKEN_MESSAGE, token));
}
throw new NotificationFailException(PUSH_NOTIFICATION_SEND_FAILURE,
String.format(PUSH_NOTIFICATION_SEND_FAILURE_MESSAGE, token));
}

private Notification createNotification(String content) {
return Notification.builder().setTitle("커피밋").setBody(content).build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,20 @@
import coffeemeet.server.user.domain.OAuthProvider;

public record OAuthMemberDetail(
String name,
String profileImage,
String birthYear,
String birthDay,
String email,
OAuthProvider oAuthProvider,
String oAuthProviderId
) {

public static OAuthMemberDetail of(
String name,
String profileImage,
String birthYear,
String birthDay,
String email,
OAuthProvider oAuthProvider,
String oAuthProviderId
) {
return new OAuthMemberDetail(
name,
profileImage,
birthYear,
birthDay,
email,
oAuthProvider,
oAuthProviderId
Expand Down
Loading