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#58/set fcm #99

Merged
merged 13 commits into from
Jun 7, 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
1 change: 1 addition & 0 deletions .github/workflows/server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
java-version: 17

- run : echo "${{ secrets.DEV_YML }}" > ./src/main/resources/application-dev.yml
- run : echo "${{ secrets.FIREBASE_KEY }}" > ./src/main/resources/firebase/firebase_key.json

- name: Log in to Docker Hub
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
Expand Down
2 changes: 2 additions & 0 deletions backend/memetory/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ out/
.vscode/

src/main/resources/application-*

src/main/resources/firebase/firebase_key.json
1 change: 1 addition & 0 deletions backend/memetory/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE' // Redis 라이브러리
implementation 'com.google.code.gson:gson:2.8.7'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'com.google.firebase:firebase-admin:9.2.0' // FCM

implementation 'com.auth0:java-jwt:4.2.1'
implementation 'org.springframework.boot:spring-boot-starter-security'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class MemeController implements MemeApi {
@Override
public ResponseEntity<HttpStatus> callBackMeme(@PathVariable Long memberId,
@RequestBody ShotStackCallBackRequest shotStackCallBackRequest) {
MemeServiceDto memeServiceDto = shotStackCallBackRequest.toServiceDtoFromMemeberId(memberId);
MemeServiceDto memeServiceDto = shotStackCallBackRequest.toServiceDtoFromMemberId(memberId);

memeService.registerMeme(memeServiceDto);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor
@Schema(description = "밈 생성 후 콜백 받는 포맷")
public class ShotStackCallBackRequest {

Expand All @@ -19,7 +21,7 @@ public class ShotStackCallBackRequest {
@Schema(description = "에러")
private String error;

public MemeServiceDto toServiceDtoFromMemeberId(Long memberId) {
public MemeServiceDto toServiceDtoFromMemberId(Long memberId) {
return MemeServiceDto.builder()
.memberId(memberId)
.s3Url(url)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.example.memetory.domain.meme.service;

import static com.example.memetory.global.firebase.FirebaseMessage.*;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -19,15 +21,19 @@
import com.example.memetory.domain.meme.exception.AccessDeniedMemeException;
import com.example.memetory.domain.meme.exception.NotFoundMemeException;
import com.example.memetory.domain.meme.repository.MemeRepository;
import com.example.memetory.global.firebase.service.FirebaseService;
import com.google.gson.Gson;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@RequiredArgsConstructor
@Slf4j
public class MemeService {
private final MemberService memberService;
private final MemeRepository memeRepository;
private final FirebaseService firebaseService;

@Value("${spring.ai-server.url}")
private String aiServerUrl;
Expand All @@ -39,6 +45,8 @@ public MemeResponse registerMeme(MemeServiceDto memeServiceDto) {

Meme savedMeme = memeRepository.save(meme);

firebaseService.sendMessage(MEME_CREATE_MESSAGE.toMessageWithFcmToken(member.getFcmToken()));

return MemeResponse.of(savedMeme);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.memetory.global.config;

import java.io.IOException;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;

@Configuration
public class FirebaseConfig {
@Value("${spring.firebase.path}")
private String firebasePath;

@Bean
public FirebaseApp initializeFirebase() {
try {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(
GoogleCredentials.fromStream(new ClassPathResource(firebasePath).getInputStream()))
.build();
if (FirebaseApp.getApps().isEmpty()) {
return FirebaseApp.initializeApp(options);
}
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
return FirebaseApp.getInstance();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.memetory.global.firebase;

import java.time.LocalDateTime;

import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum FirebaseMessage {
MEME_CREATE_MESSAGE("밈 생성 완료", "밈 생성이 완료되었습니다."),
;

private String title;
private String body;

public Message toMessageWithFcmToken(String fcmToken) {
return Message.builder()
.setToken(fcmToken)
.setNotification(
Notification.builder()
.setTitle(this.getTitle())
.setBody(this.getBody())
.build()
)
.putData("time", LocalDateTime.now().toString())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.example.memetory.global.firebase.service;

import java.time.LocalDateTime;

import org.springframework.stereotype.Service;

import com.example.memetory.global.firebase.FirebaseMessage;
import com.google.firebase.FirebaseApp;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
@RequiredArgsConstructor
public class FirebaseService {
private final FirebaseApp firebaseApp;

public void sendMessage(Message message) {
try {
String response = FirebaseMessaging.getInstance(firebaseApp).send(message);
log.info("Sent message: {}", response);
} catch (FirebaseMessagingException e) {
log.error("cannot send message by token. error info : {}", e.getMessage());
}
}

private Message buildMessage(FirebaseMessage message, String fcmToken) {
return Message.builder()
.setToken(fcmToken)
.setNotification(
Notification.builder()
.setTitle(message.getTitle())
.setBody(message.getBody())
.build()
)
.putData("time", LocalDateTime.now().toString())
.build();
}
}
3 changes: 3 additions & 0 deletions backend/memetory/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ spring:
google-url: "https://www.googleapis.com/oauth2/v2/userinfo"
kakao-url: "https://kapi.kakao.com/v2/user/me"

firebase:
path: firebase/firebase_key.json


springdoc:
swagger-ui:
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static LoginRequest GOOGLE_LOGIN_REQUEST() {
}

public static LoginRequest GOOGLE_LOGIN_REQUEST_OTHER_FCM_TOKE() {
return new LoginRequest("google login token", SocialType.GOOGLE, "fcmToken");
return new LoginRequest("google login token", SocialType.GOOGLE, "fcmToken5");
}

public static String JSON_GOOGLE_OAUTH2_RESPONSE() throws JsonProcessingException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public final static Member MEMBER() {
.nickname("junRain")
.socialType(SocialType.GOOGLE)
.socialId("123456789")
.fcmToken("fcmToken")
.build();
}

Expand All @@ -28,6 +29,7 @@ public final static Member OTHER_MEMBER() {
.nickname("goDDm")
.socialType(SocialType.GOOGLE)
.socialId("-2")
.fcmToken("fcmToken2")
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;

import com.example.memetory.domain.meme.dto.GenerateMemeListRequest;
import com.example.memetory.domain.meme.dto.MemePageResponse;
import com.example.memetory.domain.meme.dto.MemeResponse;
import com.example.memetory.domain.meme.dto.ShotStackCallBackRequest;
import com.example.memetory.domain.meme.entity.Meme;
import com.example.memetory.domain.meme.repository.MemeRepository;
import com.example.memetory.global.integration.BaseIntegrationTest;
Expand All @@ -26,6 +28,9 @@

@DisplayName("Meme 통합 테스트의 ")
public class MemeIntegrationTest extends BaseIntegrationTest {
@Value("${spring.firebase.token}")
private String fcmToken;

@Autowired
private MemeRepository memeRepository;

Expand Down Expand Up @@ -156,4 +161,34 @@ void Given_MemeId_When_findMemberMemePageResponse_Throw_NotFoundMemberMemeExcept
assertThat(result).isEqualTo(MEME_NOT_FOUND.getMessage());
}

@Test
@DisplayName("멤버 Id와 ShotStackCallBackRequest을 통한 밈 생성 성공")
public void Given_memberIdAndShotStackCallBackRequest_When_callBackMeme_Then_HttpStatus200() {

ShotStackCallBackRequest request = new ShotStackCallBackRequest("200", "s3Url", null);

member.updateFcmToken(fcmToken);
memberRepository.save(member);

// when
ExtractableResponse<Response> response =
given()
.log()
.all()
.auth().oauth2(accessToken)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(request)
.when()
.post("/meme/create/" + member.getId())
.then()
.log()
.all()
.extract();

Long memeCount = memeRepository.count();

// then
assertThat(memeCount).isEqualTo(1);
assertThat(200).isEqualTo(response.statusCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.example.memetory.domain.meme.exception.AccessDeniedMemeException;
import com.example.memetory.domain.meme.exception.NotFoundMemeException;
import com.example.memetory.domain.meme.repository.MemeRepository;
import com.example.memetory.global.firebase.service.FirebaseService;

@DisplayName("memeService 테스트의 ")
@ExtendWith(MockitoExtension.class)
Expand All @@ -42,6 +43,8 @@ public class MemeServiceTest {
private MemberService memberService;
@Mock
private MemeRepository memeRepository;
@Mock
private FirebaseService firebaseService;

private Member member;
private Meme meme;
Expand All @@ -62,6 +65,7 @@ void Given_MemesServiceDto_When_registerMeme_Then_Meme() {

given(memberService.findMemberFromId(memberId)).willReturn(member);
given(memeRepository.save(any(Meme.class))).willReturn(meme);
doNothing().when(firebaseService).sendMessage(any());

// when
MemeResponse result = memeService.registerMeme(memeServiceDto);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import static com.example.memetory.domain.member.MemberFixture.*;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
Expand Down Expand Up @@ -42,9 +44,12 @@ public void setUp() {
databaseCleanup.afterPropertiesSet();
}

databaseCleanup.execute();

member = memberRepository.save(MEMBER());
accessToken = jwtUtil.generateAccessToken(member.getEmail());
}

@AfterEach
public void afterEach() {
databaseCleanup.execute();
}
}
Loading