forked from codesquad-members-2023/second-hand-max
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: fcm 설정을 위한 configuration 등록 * feat: FCM token 발급 및 저장 기능 구현 * style: 불필요한 로직 제거 * test: FCM 토큰 저장 테스트코드 작성 * test: FCM 토큰 저장 RESTDocs 테스트코드 작성 * test: FCM 토큰 저장 인수 테스트코드 작성
- Loading branch information
Showing
18 changed files
with
382 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,3 +36,6 @@ replay_pid* | |
application-db.yml | ||
application-oauth.yml | ||
application-secret.yml | ||
|
||
# FCM | ||
firebase-admin-sdk.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
src/main/java/kr/codesquad/secondhand/application/firebase/FcmTokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package kr.codesquad.secondhand.application.firebase; | ||
|
||
import com.google.auth.oauth2.AccessToken; | ||
import com.google.auth.oauth2.GoogleCredentials; | ||
import kr.codesquad.secondhand.application.redis.RedisService; | ||
import kr.codesquad.secondhand.exception.ErrorCode; | ||
import kr.codesquad.secondhand.exception.InternalServerException; | ||
import kr.codesquad.secondhand.infrastructure.properties.FcmProperties; | ||
import kr.codesquad.secondhand.presentation.dto.fcm.FcmTokenIssueResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
|
||
@RequiredArgsConstructor | ||
@Service | ||
public class FcmTokenService { | ||
|
||
private static final String FCM_TOKEN_PREFIX = "fcm_token:"; | ||
|
||
private final FcmProperties fcmProperties; | ||
private final RedisService redisService; | ||
|
||
public void updateToken(String token, Long memberId) { | ||
redisService.set(FCM_TOKEN_PREFIX + memberId, token, fcmProperties.getExpirationMillis()); | ||
} | ||
|
||
public FcmTokenIssueResponse issueToken() { | ||
try (FileInputStream serviceAccount = new FileInputStream(fcmProperties.getPrivateKeyPath())) { | ||
GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccount) | ||
.createScoped(fcmProperties.getScopes()); | ||
|
||
AccessToken accessToken = credentials.refreshAccessToken(); | ||
|
||
return new FcmTokenIssueResponse(accessToken.getTokenValue()); | ||
} catch (IOException e) { | ||
throw new InternalServerException(ErrorCode.FIREBASE_CONFIG_ERROR, "FCM 토큰 발급에 실패했습니다."); | ||
} | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/kr/codesquad/secondhand/config/FcmConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package kr.codesquad.secondhand.config; | ||
|
||
import com.google.auth.oauth2.GoogleCredentials; | ||
import com.google.firebase.FirebaseApp; | ||
import com.google.firebase.FirebaseOptions; | ||
import com.google.firebase.messaging.FirebaseMessaging; | ||
import kr.codesquad.secondhand.exception.ErrorCode; | ||
import kr.codesquad.secondhand.exception.InternalServerException; | ||
import kr.codesquad.secondhand.infrastructure.properties.FcmProperties; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Profile; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
@RequiredArgsConstructor | ||
@Profile("!test") | ||
@Configuration | ||
public class FcmConfig { | ||
|
||
private final FcmProperties fcmProperties; | ||
|
||
@Bean | ||
public FirebaseMessaging firebaseMessaging() { | ||
List<FirebaseApp> apps = FirebaseApp.getApps(); | ||
|
||
try (FileInputStream refreshToken = new FileInputStream(fcmProperties.getPrivateKeyPath())) { | ||
return apps.stream() | ||
.filter(app -> app.getName().equals(FirebaseApp.DEFAULT_APP_NAME)) | ||
.map(FirebaseMessaging::getInstance) | ||
.findFirst() | ||
.orElseGet(() -> createFirebaseMessaging(refreshToken)); | ||
} catch (IOException e) { | ||
throw new InternalServerException(ErrorCode.FIREBASE_CONFIG_ERROR, "Firebase 설정 파일을 읽어올 수 없습니다."); | ||
} | ||
} | ||
|
||
private FirebaseMessaging createFirebaseMessaging(FileInputStream refreshToken) { | ||
try { | ||
FirebaseOptions options = FirebaseOptions.builder() | ||
.setCredentials(GoogleCredentials.fromStream(refreshToken)) | ||
.build(); | ||
|
||
return FirebaseMessaging.getInstance(FirebaseApp.initializeApp(options)); | ||
} catch (IOException e) { | ||
throw new InternalServerException(ErrorCode.FIREBASE_CONFIG_ERROR, "Firebase 설정 파일을 읽어올 수 없습니다."); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
src/main/java/kr/codesquad/secondhand/infrastructure/properties/FcmProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package kr.codesquad.secondhand.infrastructure.properties; | ||
|
||
import lombok.Getter; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.boot.context.properties.ConstructorBinding; | ||
|
||
@Getter | ||
@ConfigurationProperties("fcm") | ||
public class FcmProperties { | ||
|
||
private static final String MESSAGING_SCOPE = "https://www.googleapis.com/auth/firebase.messaging"; | ||
|
||
private final String privateKeyPath; | ||
private final long expirationMillis; | ||
private final String[] scopes; | ||
|
||
@ConstructorBinding | ||
public FcmProperties(String privateKeyPath, long expirationMillis) { | ||
this.privateKeyPath = privateKeyPath; | ||
this.expirationMillis = expirationMillis; | ||
this.scopes = new String[]{MESSAGING_SCOPE}; | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
src/main/java/kr/codesquad/secondhand/presentation/FcmController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package kr.codesquad.secondhand.presentation; | ||
|
||
import kr.codesquad.secondhand.application.firebase.FcmTokenService; | ||
import kr.codesquad.secondhand.presentation.dto.fcm.FcmTokenIssueResponse; | ||
import kr.codesquad.secondhand.presentation.dto.fcm.FcmTokenUpdateRequest; | ||
import kr.codesquad.secondhand.presentation.support.Auth; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
import javax.validation.Valid; | ||
|
||
@RequiredArgsConstructor | ||
@RequestMapping("/api/fcm-token") | ||
@RestController | ||
public class FcmController { | ||
|
||
private final FcmTokenService fcmTokenService; | ||
|
||
@PatchMapping | ||
public void updateToken(@Valid @RequestBody FcmTokenUpdateRequest request, | ||
@Auth Long memberId) { | ||
fcmTokenService.updateToken(request.getToken(), memberId); | ||
} | ||
|
||
@ResponseStatus(HttpStatus.CREATED) | ||
@PostMapping | ||
public FcmTokenIssueResponse issueToken() { | ||
return fcmTokenService.issueToken(); | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
src/main/java/kr/codesquad/secondhand/presentation/dto/fcm/FcmTokenIssueResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package kr.codesquad.secondhand.presentation.dto.fcm; | ||
|
||
import lombok.Getter; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Getter | ||
@RequiredArgsConstructor | ||
public class FcmTokenIssueResponse { | ||
|
||
private final String token; | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/kr/codesquad/secondhand/presentation/dto/fcm/FcmTokenUpdateRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package kr.codesquad.secondhand.presentation.dto.fcm; | ||
|
||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import javax.validation.constraints.NotEmpty; | ||
|
||
@Getter | ||
@NoArgsConstructor | ||
public class FcmTokenUpdateRequest { | ||
|
||
@NotEmpty(message = "토큰은 필수 입력 값입니다.") | ||
private String token; | ||
} |
Submodule secret
updated
from ef25c6 to a02f7c
37 changes: 37 additions & 0 deletions
37
src/test/java/kr/codesquad/secondhand/acceptance/FcmAcceptanceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package kr.codesquad.secondhand.acceptance; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import io.restassured.RestAssured; | ||
import kr.codesquad.secondhand.domain.member.Member; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.MediaType; | ||
import java.util.Map; | ||
|
||
public class FcmAcceptanceTest extends AcceptanceTestSupport { | ||
|
||
@DisplayName("FCM 토큰을 저장하는데 성공한다.") | ||
@Test | ||
void saveFcmToken() { | ||
// given | ||
Member member = signup(); | ||
String tokenValue = "testTokenValue"; | ||
|
||
var request = RestAssured | ||
.given().log().all() | ||
.header(HttpHeaders.AUTHORIZATION, "Bearer " + jwtProvider.createAccessToken(member.getId())) | ||
.contentType(MediaType.APPLICATION_JSON_VALUE) | ||
.body(Map.of("token", tokenValue)); | ||
|
||
// when | ||
var response = request | ||
.patch("/api/fcm-token") | ||
.then().log().all() | ||
.extract(); | ||
|
||
// then | ||
assertThat(response.statusCode()).isEqualTo(200); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/test/java/kr/codesquad/secondhand/application/firebase/FcmTokenServiceTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package kr.codesquad.secondhand.application.firebase; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import kr.codesquad.secondhand.application.ApplicationTestSupport; | ||
import kr.codesquad.secondhand.domain.member.Member; | ||
import kr.codesquad.secondhand.fixture.FixtureFactory; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
|
||
class FcmTokenServiceTest extends ApplicationTestSupport { | ||
|
||
@Autowired | ||
private FcmTokenService fcmTokenService; | ||
|
||
@Autowired | ||
private RedisTemplate<String, Object> redisTemplate; | ||
|
||
@DisplayName("토큰을 저장하는데 성공한다.") | ||
@Test | ||
void givenTokenValue_whenUpdateToken_thenSuccess() { | ||
// given | ||
Member member = supportRepository.save(FixtureFactory.createMember()); | ||
String tokenValue = "testTokenValue"; | ||
|
||
// when | ||
fcmTokenService.updateToken(tokenValue, member.getId()); | ||
|
||
// then | ||
String token = (String) redisTemplate.opsForValue().get("fcm_token:" + member.getId()); | ||
assertThat(token).isNotBlank(); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
src/test/java/kr/codesquad/secondhand/config/FcmTestConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package kr.codesquad.secondhand.config; | ||
|
||
import com.google.firebase.FirebaseApp; | ||
import com.google.firebase.FirebaseOptions; | ||
import com.google.firebase.messaging.FirebaseMessaging; | ||
import kr.codesquad.secondhand.infrastructure.fcm.MockGoogleCredentials; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.test.context.ActiveProfiles; | ||
import java.util.List; | ||
|
||
@ActiveProfiles("test") | ||
@Configuration | ||
public class FcmTestConfig { | ||
|
||
@Bean | ||
public FirebaseMessaging firebaseMessaging() { | ||
List<FirebaseApp> apps = FirebaseApp.getApps(); | ||
|
||
return apps.stream() | ||
.filter(app -> app.getName().equals(FirebaseApp.DEFAULT_APP_NAME)) | ||
.map(FirebaseMessaging::getInstance) | ||
.findFirst() | ||
.orElseGet(this::createFirebaseMessaging); | ||
} | ||
|
||
private FirebaseMessaging createFirebaseMessaging() { | ||
FirebaseOptions options = FirebaseOptions.builder() | ||
.setCredentials(new MockGoogleCredentials("test-token")) | ||
.setProjectId("test-project-id") | ||
.build(); | ||
|
||
return FirebaseMessaging.getInstance(FirebaseApp.initializeApp(options)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.