Skip to content

Commit

Permalink
[MOA-487] 외부 API 호출 실패시 디스코드에 알림 (#108)
Browse files Browse the repository at this point in the history
* MOA-487: 외부 API 호출 실패시 디스코드에 알림

* MOA-487: 서브모듈 업데이트

* MOA-487: oauth api 호출 실패시에도 웹훅 알림
  • Loading branch information
shin-mallang authored Mar 1, 2024
1 parent ed2043e commit 9dea6a9
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 17 deletions.
2 changes: 1 addition & 1 deletion MoA-Backend-Submodule
18 changes: 15 additions & 3 deletions core/src/main/java/moa/client/discord/DiscordWebHookClient.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
package moa.client.discord;

import static moa.global.config.async.AsyncConfig.VIRTUAL_THREAD_EXECUTOR;

import lombok.RequiredArgsConstructor;
import moa.client.discord.dto.DiscordSendMessageRequest;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
@Async(VIRTUAL_THREAD_EXECUTOR)
public class DiscordWebHookClient {

private final DiscordWebHookProperty discordWebHookProperty;
private final DiscordWebHookApiClient webHookApiClient;

public void sendContent(String content) {
public void sendToInquiryChannel(String content) {
webHookApiClient.sendMessage(
discordWebHookProperty.inquiryChannel().webhookId(),
discordWebHookProperty.inquiryChannel().webhookToken(),
new DiscordSendMessageRequest(content)
);
}

public void sendToErrorChannel(String content) {
webHookApiClient.sendMessage(
discordWebHookProperty.webhookId(),
discordWebHookProperty.webhookToken(),
discordWebHookProperty.errorChannel().webhookId(),
discordWebHookProperty.errorChannel().webhookToken(),
new DiscordSendMessageRequest(content)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@

@ConfigurationProperties(prefix = "discord")
public record DiscordWebHookProperty(
String webhookId,
String webhookToken
WebhookProperty inquiryChannel,
WebhookProperty errorChannel
) {
public record WebhookProperty(
String webhookId,
String webhookToken
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.global.http.HttpInterfaceUtil;
import org.springframework.context.annotation.Bean;
Expand All @@ -17,12 +18,15 @@
@RequiredArgsConstructor
public class AppleClientConfig {

private final DiscordWebHookClient discordWebHookClient;

@Bean
public AppleApiClient appleApiClient() {
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
String responseData = new String(response.getBody().readAllBytes());
log.error("Apple API ERROR {}", responseData);
discordWebHookClient.sendToErrorChannel("Apple API ERROR \n ->" + responseData);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(responseData)
.setStatus(HttpStatus.valueOf(response.getStatusCode().value())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.global.http.HttpInterfaceUtil;
import org.springframework.context.annotation.Bean;
Expand All @@ -18,12 +19,15 @@
@RequiredArgsConstructor
public class KakaoClientConfig {

private final DiscordWebHookClient discordWebHookClient;

@Bean
public KakaoApiClient kakaoApiClient() {
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
String responseData = new String(response.getBody().readAllBytes());
log.error("Kakao API ERROR {}", responseData);
discordWebHookClient.sendToErrorChannel("Kakao API ERROR \n ->" + responseData);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(responseData)
.setStatus(HttpStatus.valueOf(response.getStatusCode().value())));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.global.http.HttpInterfaceUtil;
import org.springframework.context.annotation.Bean;
Expand All @@ -18,12 +19,15 @@
@RequiredArgsConstructor
public class NaverClientConfig {

private final DiscordWebHookClient discordWebHookClient;

@Bean
public NaverApiClient naverApiClient() {
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
String responseData = new String(response.getBody().readAllBytes());
log.error("Naver API ERROR {}", responseData);
discordWebHookClient.sendToErrorChannel("Naver API ERROR \n ->" + responseData);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(responseData)
.setStatus(HttpStatus.valueOf(response.getStatusCode().value())));
Expand Down
4 changes: 4 additions & 0 deletions core/src/main/java/moa/client/sms/NHNClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand All @@ -17,12 +18,15 @@
@RequiredArgsConstructor
public class NHNClientConfig {

private final DiscordWebHookClient discordWebHookClient;

@Bean
public NHNApiClient nhnApiClient() {
RestClient restClient = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
String responseData = new String(response.getBody().readAllBytes());
log.error("NHN SMS API ERROR {}", responseData);
discordWebHookClient.sendToErrorChannel("NHN SMS API ERROR \n ->" + responseData);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(responseData)
.setStatus(HttpStatus.valueOf(response.getStatusCode().value())));
Expand Down
9 changes: 6 additions & 3 deletions core/src/main/java/moa/client/sms/SmsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.sms.NHNSendSmsRequest.RecipientRequest;
import org.springframework.stereotype.Service;

Expand All @@ -13,6 +14,7 @@ public class SmsClient {

private final NHNApiClient client;
private final NHNSmsConfig config;
private final DiscordWebHookClient discordWebHookClient;

public void send(String message, String phoneNumber) {
NHNSendSmsRequest request = new NHNSendSmsRequest(
Expand All @@ -22,11 +24,12 @@ public void send(String message, String phoneNumber) {
);

NHNSendSmsResponse response = client.sendSms(config.appKey(), config.secretKey(), request);
if (!response.header().isSuccessful()) {
if (response.header().isSuccessful()) {
log.info("NHN 문자 발송 성공 {}", response);
} else {
log.error("NHN 문자 발송 API 에서 문제 발생 {}", response);
discordWebHookClient.sendToErrorChannel("NHN 문자 발송 API 에서 문제 발생 \n ->" + response);
throw new RuntimeException("NHN 문자 발송 API 에서 문제 발생. Detail: %s".formatted(response.toString()));
} else {
log.info("NHN 문자 발송 성공 {}", response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.global.http.HttpInterfaceUtil;
import org.springframework.context.annotation.Bean;
Expand All @@ -20,12 +21,15 @@
@RequiredArgsConstructor
public class TossPaymentClientConfig {

private final DiscordWebHookClient discordWebHookClient;

@Bean
public TossApiClient tossApiClient() {
RestClient build = RestClient.builder()
.defaultStatusHandler(HttpStatusCode::isError, (request, response) -> {
String responseData = new String(response.getBody().readAllBytes());
log.error("Toss API ERROR {}", responseData);
discordWebHookClient.sendToErrorChannel("Toss API ERROR \n ->" + responseData);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(responseData)
.setStatus(HttpStatus.valueOf(response.getStatusCode().value())));
Expand Down
8 changes: 7 additions & 1 deletion core/src/main/java/moa/client/wincube/WincubeClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.annotation.Nullable;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.client.wincube.auth.WincubeAuthClient;
import moa.client.wincube.dto.WincubeCancelCouponResponse;
Expand All @@ -26,6 +27,7 @@ public class WincubeClient {
private final WincubeProperty wincubeProperty;
private final WincubeAuthClient authClient;
private final WincubeApiClient client;
private final DiscordWebHookClient discordWebHookClient;

public WincubeProductResponse getProductList() {
String authToken = authClient.getAuthToken();
Expand Down Expand Up @@ -63,6 +65,8 @@ private void validateIssueCoupon(WincubeIssueCouponResponse response) {
if (response.isSuccess()) {
log.info("윈큐브 쿠폰 발행 완료 {}", response);
} else {
log.error("윈큐브 쿠폰 발행 실패 {}", response);
discordWebHookClient.sendToErrorChannel("윈큐브 쿠폰 발행 실패 \n ->" + response);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION.withDetail(
"윈큐브 쿠폰 발행 실패: " + response
));
Expand All @@ -79,8 +83,10 @@ public void cancelCoupon(String transactionId) {
);
log.info("윈큐브 쿠폰 취소 API 호출 완료.");
if (!response.isSuccess()) {
log.error("윈큐브 쿠폰 취소 실패 {}", response);
discordWebHookClient.sendToErrorChannel("윈큐브 쿠폰 취소 실패 \n ->" + response);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION.withDetail(
"윈큐브 쿠폰 취소 실해: " + response
"윈큐브 쿠폰 취소 실패: " + response
));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import moa.client.discord.DiscordWebHookClient;
import moa.client.exception.ExternalApiException;
import moa.client.wincube.auth.request.WincubeAuthResultCode;
import moa.client.wincube.auth.request.WincubeIssueAuthCodeRequest;
Expand All @@ -25,6 +26,7 @@ public class WincubeAuthClient {

private static final int AES_IV_BYTE = 16;

private final DiscordWebHookClient discordWebHookClient;
private final ObjectMapper objectMapper;
private final WincubeAuthProperty wincubeProperty;
private final WincubeAuthApiClient wincubeAuthApiClient;
Expand Down Expand Up @@ -63,7 +65,8 @@ private void validateResponseIsSuccess(String response) {
log.info("윈큐브 Auth Code 응답 검증");
var code = readValue(response, WincubeAuthResultCode.class);
if (code.resultCode() >= 400) {
log.error("Wincube AUTH API error {}", response);
log.error("Wincube AUTH CODE API ERROR {}", response);
discordWebHookClient.sendToErrorChannel("Wincube AUTH CODE API ERROR \n ->" + response);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(response)
.setStatus(HttpStatus.valueOf(code.resultCode())));
Expand Down Expand Up @@ -105,6 +108,7 @@ private String getAuthToken(String codeId, String aesIv) {
private void validateTokenResponse(WincubeIssueAuthTokenResponse response, String aesIv) {
if (response.resultCode() != 200) {
log.error("Wincube AUTH TOKEN API ERROR {}", response);
discordWebHookClient.sendToErrorChannel("Wincube AUTH TOKEN API ERROR \n ->" + response);
throw new ExternalApiException(EXTERNAL_API_EXCEPTION
.withDetail(response.message())
.setStatus(HttpStatus.valueOf(response.resultCode())));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package moa.cs.application;

import static moa.global.config.async.AsyncConfig.VIRTUAL_THREAD_EXECUTOR;
import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT;

import lombok.RequiredArgsConstructor;
import moa.client.discord.DiscordWebHookClient;
import moa.cs.domain.PersonalInquiry;
import moa.cs.domain.PersonalInquiryCreateEvent;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.event.TransactionalEventListener;

@Service
@RequiredArgsConstructor
@Async(VIRTUAL_THREAD_EXECUTOR)
public class PersonalInquiryEventHandler {

private static final String MESSAGE_FORMAT = """
Expand All @@ -28,7 +25,7 @@ public class PersonalInquiryEventHandler {
@TransactionalEventListener(value = PersonalInquiryCreateEvent.class, phase = AFTER_COMMIT)
public void push(PersonalInquiryCreateEvent event) {
PersonalInquiry inquiry = event.inquiry();
discordWebHookClient.sendContent(
discordWebHookClient.sendToInquiryChannel(
MESSAGE_FORMAT
.formatted(
inquiry.getCategory(),
Expand Down
8 changes: 6 additions & 2 deletions core/src/main/resources/application-core-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,9 @@ wincube:
rsaPublicKey: ""

discord:
webhookId: "id"
webhookToken: "token"
inquiry-channel:
webhookId: "id"
webhookToken: "token"
error-channel:
webhookId: "id"
webhookToken: "token"

0 comments on commit 9dea6a9

Please sign in to comment.