Skip to content
Open
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
63 changes: 63 additions & 0 deletions push-notification-api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
plugins {
id "org.ec4j.editorconfig" version "0.0.3"
id "checkstyle"
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

compileJava.options.encoding = "UTF-8"
compileTestJava.options.encoding = "UTF-8"

idea {
module {
outputDir file("build/classes/main")
testOutputDir file("build/classes/test")
}
}

configurations {
compileOnly {
extendsFrom annotationProcessor
}
}

ext {
FIREBASE_ADMIN_VERSION = "6.8.1"
OKHTTP_VERSION = "4.2.2"
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.google.firebase:firebase-admin:${FIREBASE_ADMIN_VERSION}")
implementation("com.squareup.okhttp3:okhttp:${OKHTTP_VERSION}")

compileOnly("org.projectlombok:lombok")
annotationProcessor("org.projectlombok:lombok")

testImplementation("org.springframework.boot:spring-boot-starter-test")

testCompileOnly("org.projectlombok:lombok")
testAnnotationProcessor("org.projectlombok:lombok")
}

test {
useJUnitPlatform()
}

editorconfig {
excludes = ["build"]
}

check.dependsOn editorconfigCheck

checkstyle {
configFile = file("${project.rootDir}/rule/naver-checkstyle-rules.xml")
configProperties = [config_loc: ".."]
toolVersion = "8.24"
ignoreFailures = false
maxErrors = 0
maxWarnings = 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.bluebird.pipit.fcm.controller;

import com.bluebird.pipit.fcm.dto.PushNotificationRequest;
import com.bluebird.pipit.fcm.service.PushNotificationService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class PushNotificationController {
private final PushNotificationService pushNotificationService;

public void sendMessage(@RequestBody PushNotificationRequest request) {
pushNotificationService.sendMessageTo(request.getTargetToken(), request.getTitle(), request.getBody());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.bluebird.pipit.fcm.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Builder
@Getter
@AllArgsConstructor
Comment on lines +8 to +9
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 두개는 @Value 하나로 관리할 수 있을것 같습니다

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그리고 Value 사용하게되면 필드에 명시한 private 도 생략가능할것 같습니다.
(default 로 private final 데이터가 유지되므로)

public class PushNotificationMessage {
private boolean validate_only;
private Message message;

@Builder
@Getter
@AllArgsConstructor
public static class Message {
private Notification notification;
private String token;
}

@Builder
@Getter
@AllArgsConstructor
public static class Notification {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationPushNotificationMessage 가 포함하고 있는게 맞나요??
포함관계라면 PushNotificationMessage 내부에서 필드 추가해줘야할것 같습니다.

Notification notification;

아니라면 Notification 은 다른 클래스로 빼주는게 자연스러울것 같아요

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NotificationMessage의 필드로 사용되는 클래스입니다!
Message 클래스 내부에 포함되도록 변경하면 될까요?

private String title;
private String body;
private String image;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.bluebird.pipit.fcm.dto;

import lombok.Value;

@Value
public class PushNotificationRequest {
String targetToken;
String title;
String body;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.bluebird.pipit.fcm.service;

import com.bluebird.pipit.fcm.domain.PushNotificationMessage;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.GoogleCredentials;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.http.HttpHeaders;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class PushNotificationService {
private static final String FIREBASE_CONFIG_PATH = "firebase/firebase_service_key.json"; // TODO : add firebase project service key file
private static final String API_URL = "https://fcm.googleapis.com/v1/projects/{firebase-project-id}/messages:send";
private static final String OAUTH_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
Comment on lines +21 to +23
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

급한건 아니지만 이런것들은 yml 설정파일에 넣어주는 편이 좋을것 같습니다


private final ObjectMapper objectMapper;


private String getAccessToken() {
GoogleCredentials googleCredentials;
try {
googleCredentials = GoogleCredentials
.fromStream(new ClassPathResource(FIREBASE_CONFIG_PATH).getInputStream())
.createScoped(List.of(OAUTH_SCOPE));
} catch (IOException e) {
throw new RuntimeException("Failed to open Firebase Configuration File.", e);
}

try {
googleCredentials.refreshIfExpired();
} catch (IOException e) {
log.error("Failed to refresh Google Credentials.", e);
}

return googleCredentials.getAccessToken().getTokenValue();
}

private String makeMessage(String targetToken, String title, String body) throws JsonProcessingException {
PushNotificationMessage message = PushNotificationMessage.builder()
.message(
PushNotificationMessage.Message.builder()
.token(targetToken)
.notification(
PushNotificationMessage.Notification.builder()
.title(title)
.body(body)
.image(null)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 null 은 임시방편인가요?? 아님 항상 null 인건가요?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

임시방편으로 null 값을 넣어놨습니다!

.build()
).build())
.validate_only(false)
.build();

return objectMapper.writeValueAsString(message);

}

public void sendMessageTo(String targetToken, String title, String body) {
String message;
try {
message = makeMessage(targetToken, title, body);
} catch (JsonProcessingException e) {
throw new RuntimeException("Failed to make Message.", e);
}

OkHttpClient client = new OkHttpClient();
RequestBody requestBody = RequestBody.create(message, MediaType.get("application/json; charset=utf-8"));

Request request = new Request.Builder()
.url(API_URL)
.post(requestBody)
.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken())
.build();

try {
client.newCall(request).execute();
} catch (IOException e) {
log.error("Failed to send request.", e);
}
}



}
2 changes: 2 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ rootProject.name = 'pipit'
include 'pipit-api'
include 'authentication-api'
include 'support'
include 'push-notification-api'