From 58648bf955af5e54905caf19ddcc2aaf4543219a Mon Sep 17 00:00:00 2001 From: hodoon Date: Wed, 12 Feb 2025 11:36:50 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20google=20API=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++ .../google/controller/GoogleController.java | 37 +++++++++++ .../google/service/GoogleApiService.java | 63 +++++++++++++++++++ .../resources/application-credentials.yml | 3 + 4 files changed, 107 insertions(+) create mode 100644 src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java create mode 100644 src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java diff --git a/build.gradle b/build.gradle index 603bf06..d2c1c8e 100644 --- a/build.gradle +++ b/build.gradle @@ -49,6 +49,10 @@ dependencies { testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'com.google.apis:google-api-services-sheets:v4-rev516-1.23.0' + implementation 'com.google.auth:google-auth-library-oauth2-http:0.20.0' + + implementation 'io.jsonwebtoken:jjwt-api:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' diff --git a/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java b/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java new file mode 100644 index 0000000..a1577a5 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java @@ -0,0 +1,37 @@ +package dmu.dasom.api.domain.google.controller; + +import dmu.dasom.api.domain.google.service.GoogleApiService; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; + +@RestController +@RequestMapping("/google") +@RequiredArgsConstructor +public class GoogleController { + + private final GoogleApiService googleApiService; + + private static final String SPREADSHEET_ID = "1Vpu6_2raNvJN_GGg7aGBmzV4cXG1rCoizHl9v7kbG2o"; + private static final String RANGE = "A1"; + + @PostMapping("/write") + public ResponseEntity writeToSheet(@RequestParam String word){ + try{ + List> values = List.of(Collections.singletonList(word)); + + googleApiService.writeToSheet(SPREADSHEET_ID, RANGE, values); + return ResponseEntity.ok("Data written successfully to the spreadsheet" + word); + } catch (Exception e){ + e.printStackTrace(); + return ResponseEntity.internalServerError().body("Failed to write data to the spreadsheet" + e.getMessage()); + } + } +} diff --git a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java new file mode 100644 index 0000000..3b39c61 --- /dev/null +++ b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java @@ -0,0 +1,63 @@ +package dmu.dasom.api.domain.google.service; + +import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.json.jackson2.JacksonFactory; +import com.google.api.client.util.Value; +import com.google.api.services.sheets.v4.Sheets; +import com.google.api.services.sheets.v4.model.UpdateValuesResponse; +import com.google.api.services.sheets.v4.model.ValueRange; +import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.GoogleCredentials; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Collections; +import java.util.List; + +@Service +public class GoogleApiService { + + private static final Logger logger = LoggerFactory.getLogger(GoogleApiService.class); + private static final String APPLICATION_NAME = "Recruit Form"; + private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); + @Value("${google.credentials.file.path}") + private String credentialsFilePath; + private Sheets sheetsService; + + // 해당 메소드는 sheets의 인스턴스를 얻는데 사용 + private Sheets getSheetsService() throws IOException, GeneralSecurityException{ + if(sheetsService == null){ + GoogleCredentials credentials = GoogleCredentials + .fromStream(new ClassPathResource(credentialsFilePath).getInputStream()) + .createScoped(Collections.singletonList("https://www.googleapis.com/auth/spreadsheets")); + sheetsService = new Sheets.Builder(GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, new HttpCredentialsAdapter(credentials)) + .setApplicationName(APPLICATION_NAME) + .build(); + } + return sheetsService; + } + + public void writeToSheet(String spreadsheetId, String range, List> values) { + try { + Sheets service = getSheetsService(); + ValueRange body = new ValueRange().setValues(values); + UpdateValuesResponse result = service.spreadsheets().values() + .update(spreadsheetId, range, body) + .setValueInputOption("USER_ENTERED") + .execute(); + logger.info("Updated rows: {}", result.getUpdatedRows()); + } catch (IOException e) { + logger.error("Failed to write data to the spreadsheet", e); + throw new RuntimeException("Failed to write data to the spreadsheet: " + e.getMessage(), e); + } catch (GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/resources/application-credentials.yml b/src/main/resources/application-credentials.yml index b63ae1a..669fbcf 100644 --- a/src/main/resources/application-credentials.yml +++ b/src/main/resources/application-credentials.yml @@ -19,3 +19,6 @@ jwt: secret: ${JWT_SECRET} access-token-expiration: ${JWT_ACCESS_TOKEN_EXPIRATION} refresh-token-expiration: ${JWT_REFRESH_TOKEN_EXPIRATION} +google: + credentials: + path: ${GOOGLE_CREDENTIALS_PATH} \ No newline at end of file From 31f5121d0ecca593dac38a9b018d855ce5d7b6e4 Mon Sep 17 00:00:00 2001 From: hodoon Date: Wed, 12 Feb 2025 13:58:35 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20google=20API=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dasom/api/domain/common/exception/ErrorCode.java | 3 ++- .../domain/google/controller/GoogleController.java | 7 ++++--- .../api/domain/google/service/GoogleApiService.java | 11 +++++++---- src/main/resources/application-credentials.yml | 4 +++- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java b/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java index ac0a036..472da76 100644 --- a/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java +++ b/src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java @@ -16,7 +16,8 @@ public enum ErrorCode { ARGUMENT_NOT_VALID(400, "C007", "요청한 값이 올바르지 않습니다."), TOKEN_NOT_VALID(400, "C008", "토큰이 올바르지 않습니다."), INTERNAL_SERVER_ERROR(500, "C009", "서버에 문제가 발생하였습니다."), - NOT_FOUND(404, "C010", "해당 리소스를 찾을 수 없습니다.") + NOT_FOUND(404, "C010", "해당 리소스를 찾을 수 없습니다."), + WRITE_FAIL(400, "C011", "데이터를 쓰는데 실패하였습니다.") ; private final int status; diff --git a/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java b/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java index a1577a5..984ed66 100644 --- a/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java +++ b/src/main/java/dmu/dasom/api/domain/google/controller/GoogleController.java @@ -2,7 +2,7 @@ import dmu.dasom.api.domain.google.service.GoogleApiService; import lombok.RequiredArgsConstructor; -import lombok.Value; +import org.springframework.beans.factory.annotation.Value; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -18,8 +18,9 @@ public class GoogleController { private final GoogleApiService googleApiService; + @Value("${google.spreadsheet.id}") + private String spreadsheetId; - private static final String SPREADSHEET_ID = "1Vpu6_2raNvJN_GGg7aGBmzV4cXG1rCoizHl9v7kbG2o"; private static final String RANGE = "A1"; @PostMapping("/write") @@ -27,7 +28,7 @@ public ResponseEntity writeToSheet(@RequestParam String word){ try{ List> values = List.of(Collections.singletonList(word)); - googleApiService.writeToSheet(SPREADSHEET_ID, RANGE, values); + googleApiService.writeToSheet(spreadsheetId, RANGE, values); return ResponseEntity.ok("Data written successfully to the spreadsheet" + word); } catch (Exception e){ e.printStackTrace(); diff --git a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java index 3b39c61..921deba 100644 --- a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java +++ b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java @@ -1,17 +1,19 @@ package dmu.dasom.api.domain.google.service; -import com.google.api.client.googleapis.auth.oauth2.GoogleCredential; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; -import com.google.api.client.util.Value; + import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.UpdateValuesResponse; import com.google.api.services.sheets.v4.model.ValueRange; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; +import dmu.dasom.api.domain.common.exception.CustomException; +import dmu.dasom.api.domain.common.exception.ErrorCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; @@ -54,9 +56,10 @@ public void writeToSheet(String spreadsheetId, String range, List> logger.info("Updated rows: {}", result.getUpdatedRows()); } catch (IOException e) { logger.error("Failed to write data to the spreadsheet", e); - throw new RuntimeException("Failed to write data to the spreadsheet: " + e.getMessage(), e); + throw new CustomException(ErrorCode.WRITE_FAIL); } catch (GeneralSecurityException e) { - throw new RuntimeException(e); + logger.error("Failed to write data to the spreadsheet", e); + throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); } } diff --git a/src/main/resources/application-credentials.yml b/src/main/resources/application-credentials.yml index 669fbcf..d718107 100644 --- a/src/main/resources/application-credentials.yml +++ b/src/main/resources/application-credentials.yml @@ -21,4 +21,6 @@ jwt: refresh-token-expiration: ${JWT_REFRESH_TOKEN_EXPIRATION} google: credentials: - path: ${GOOGLE_CREDENTIALS_PATH} \ No newline at end of file + path: ${GOOGLE_CREDENTIALS_PATH} + spreadsheet: + id: ${GOOGLE_SPREADSHEET_ID} \ No newline at end of file From 12a679c480139faf26f0985c35915f47e248b616 Mon Sep 17 00:00:00 2001 From: hodoon Date: Wed, 12 Feb 2025 15:45:34 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20Google=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=EC=99=80=20Google=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=84=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dmu/dasom/api/domain/google/service/GoogleApiService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java index 921deba..f94fa8a 100644 --- a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java +++ b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java @@ -11,6 +11,7 @@ import com.google.auth.oauth2.GoogleCredentials; import dmu.dasom.api.domain.common.exception.CustomException; import dmu.dasom.api.domain.common.exception.ErrorCode; +import lombok.RequiredArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -22,13 +23,14 @@ import java.util.Collections; import java.util.List; +@RequiredArgsConstructor @Service public class GoogleApiService { private static final Logger logger = LoggerFactory.getLogger(GoogleApiService.class); private static final String APPLICATION_NAME = "Recruit Form"; private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); - @Value("${google.credentials.file.path}") + @Value("${google.credentials.path}") private String credentialsFilePath; private Sheets sheetsService; From 1d7876e119df178547075bdf03192b48b4e96edc Mon Sep 17 00:00:00 2001 From: hodoon Date: Wed, 12 Feb 2025 18:08:15 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix:=20Google=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=EC=99=80=20Google=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EA=B0=84=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/domain/google/service/GoogleApiService.java | 13 +++++++++---- src/main/resources/application-credentials.yml | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java index f94fa8a..08e190e 100644 --- a/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java +++ b/src/main/java/dmu/dasom/api/domain/google/service/GoogleApiService.java @@ -7,6 +7,7 @@ import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.model.UpdateValuesResponse; import com.google.api.services.sheets.v4.model.ValueRange; +import com.google.auth.Credentials; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import dmu.dasom.api.domain.common.exception.CustomException; @@ -18,7 +19,9 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.util.Collections; import java.util.List; @@ -30,16 +33,18 @@ public class GoogleApiService { private static final Logger logger = LoggerFactory.getLogger(GoogleApiService.class); private static final String APPLICATION_NAME = "Recruit Form"; private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); - @Value("${google.credentials.path}") - private String credentialsFilePath; + @Value("${google.credentials.json}") + private String credentialsJson; private Sheets sheetsService; - // 해당 메소드는 sheets의 인스턴스를 얻는데 사용 + // Google Sheets API 서비스 객체를 생성하는 메소드 private Sheets getSheetsService() throws IOException, GeneralSecurityException{ if(sheetsService == null){ + ByteArrayInputStream credentialsStream = new ByteArrayInputStream(credentialsJson.getBytes(StandardCharsets.UTF_8)); GoogleCredentials credentials = GoogleCredentials - .fromStream(new ClassPathResource(credentialsFilePath).getInputStream()) + .fromStream(credentialsStream) .createScoped(Collections.singletonList("https://www.googleapis.com/auth/spreadsheets")); + sheetsService = new Sheets.Builder(GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, new HttpCredentialsAdapter(credentials)) .setApplicationName(APPLICATION_NAME) .build(); diff --git a/src/main/resources/application-credentials.yml b/src/main/resources/application-credentials.yml index d718107..e9a7ac3 100644 --- a/src/main/resources/application-credentials.yml +++ b/src/main/resources/application-credentials.yml @@ -21,6 +21,6 @@ jwt: refresh-token-expiration: ${JWT_REFRESH_TOKEN_EXPIRATION} google: credentials: - path: ${GOOGLE_CREDENTIALS_PATH} + json: ${GOOGLE_CREDENTIALS_JSON} spreadsheet: id: ${GOOGLE_SPREADSHEET_ID} \ No newline at end of file