diff --git a/linkmind/src/main/java/com/app/toaster/controller/ToastController.java b/linkmind/src/main/java/com/app/toaster/controller/ToastController.java index aab4bf71..5aa2201b 100644 --- a/linkmind/src/main/java/com/app/toaster/controller/ToastController.java +++ b/linkmind/src/main/java/com/app/toaster/controller/ToastController.java @@ -76,7 +76,7 @@ public ApiResponse updateIsRead( public ApiResponse deleteToast( //나중에 softDelete로 변경 @UserId Long userId, @RequestParam Long toastId - ) throws IOException { + ) { toastService.deleteToast(userId, toastId); return ApiResponse.success(Success.DELETE_TOAST_SUCCESS); } diff --git a/linkmind/src/main/java/com/app/toaster/service/parse/ParsingServiceImpl.java b/linkmind/src/main/java/com/app/toaster/service/parse/ParsingServiceImpl.java index 7d053098..e4e5e62f 100644 --- a/linkmind/src/main/java/com/app/toaster/service/parse/ParsingServiceImpl.java +++ b/linkmind/src/main/java/com/app/toaster/service/parse/ParsingServiceImpl.java @@ -1,8 +1,13 @@ package com.app.toaster.service.parse; +import static org.springframework.web.util.UriUtils.*; + import java.io.IOException; import java.net.ConnectException; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.net.ssl.SSLHandshakeException; @@ -86,11 +91,13 @@ private String getImage(String linkUrl){ } private String findImageAnywhere(Elements metaCase, Elements imgCase, Elements iframeCase){ - if (!metaCase.isEmpty()){ - return metaCase.get(0).attr("content"); + if (!metaCase.isEmpty()) { //naver,brunch + String excludeSlug = metaCase.get(0).attr("content"); + String responseUrl = extractSlugUrl(excludeSlug); + return responseUrl == null ? excludeSlug : responseUrl; } else if(!imgCase.isEmpty()){ - return metaCase.get(0).text(); + return imgCase.get(0).text(); } else if(!iframeCase.isEmpty()){ return iframeCase.get(0).attr("content"); @@ -100,4 +107,21 @@ else if(!iframeCase.isEmpty()){ } } + private String extractSlugUrl(String slug) { + // 정규표현식 패턴 설정 + Pattern pattern = Pattern.compile("(?<=fname=)([^&]+(?:%[0-9a-fA-F]{2})*)"); //fname=이라는게 앞에 나오면 그때부터 패턴 시작. &는 파라미터 구별하니 제외. + // "/"같은거 16진수로 나오는 패턴이 %XX형태로 나오니 %이후에 16진수로 올수있는거 2자리가 *로 반복해서 올 수 있다를 명시. + Matcher matcher = pattern.matcher(slug); + + // 매칭된 URL 추출 + if (matcher.find()) { + // URL 디코딩 수행 + String url = matcher.group(); + url = decode(url, StandardCharsets.UTF_8); + return url; + } + + return null; + } + } diff --git a/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java b/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java index b1386e59..4ebc18c5 100644 --- a/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java +++ b/linkmind/src/main/java/com/app/toaster/service/toast/ToastService.java @@ -105,7 +105,7 @@ public IsReadResponse readToast(Long userId, IsReadDto isReadDto){ } @Transactional - public void deleteToast(Long userId, Long toastId) throws IOException { + public void deleteToast(Long userId, Long toastId) { User presentUser = userRepository.findByUserId(userId).orElseThrow( ()-> new NotFoundException(Error.NOT_FOUND_USER_EXCEPTION, Error.NOT_FOUND_USER_EXCEPTION.getMessage()) ); diff --git a/linkmind/src/test/java/com/app/toaster/base/MockUserIdResolver.java b/linkmind/src/test/java/com/app/toaster/base/MockUserIdResolver.java new file mode 100644 index 00000000..19ff06ae --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/base/MockUserIdResolver.java @@ -0,0 +1,31 @@ +package com.app.toaster.base; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import com.app.toaster.config.UserId; +import com.app.toaster.config.UserIdResolver; +import com.app.toaster.config.jwt.JwtService; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.constraints.NotNull; + + +public class MockUserIdResolver extends UserIdResolver { + + public MockUserIdResolver(JwtService jwtService) { + super(jwtService); + } + + @Override + public Object resolveArgument(@NotNull MethodParameter parameter, ModelAndViewContainer modelAndViewContainer, @NotNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) { + return 125L; + } + +} diff --git a/linkmind/src/test/java/com/app/toaster/base/WithMockCustomUser.java b/linkmind/src/test/java/com/app/toaster/base/WithMockCustomUser.java new file mode 100644 index 00000000..985b4070 --- /dev/null +++ b/linkmind/src/test/java/com/app/toaster/base/WithMockCustomUser.java @@ -0,0 +1,11 @@ +package com.app.toaster.base; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +public @interface WithMockCustomUser { +} diff --git a/linkmind/src/test/java/com/app/toaster/parse/ParseServiceTest.java b/linkmind/src/test/java/com/app/toaster/parse/ParseServiceTest.java index 17117b46..7ddc891d 100644 --- a/linkmind/src/test/java/com/app/toaster/parse/ParseServiceTest.java +++ b/linkmind/src/test/java/com/app/toaster/parse/ParseServiceTest.java @@ -1,7 +1,14 @@ package com.app.toaster.parse; +import static org.springframework.web.util.UriUtils.*; + import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.ConnectException; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + import javax.net.ssl.SSLHandshakeException; import org.assertj.core.api.Assertions; import org.jsoup.HttpStatusException; @@ -31,14 +38,89 @@ void basicSitesAvailable() throws IOException { given */ String basicUrl = "https://www.naver.com"; + /* + when + */ String resultOfTitle = getOg(basicUrl).titleAdvanced(); String resultOfImage = getOg(basicUrl).imageAdvanced(); + + /* + then + */ System.out.println(resultOfTitle); System.out.println(resultOfImage); Assertions.assertThat(resultOfImage).isNotEmpty(); // Assuming a non-empty image URL Assertions.assertThat(resultOfTitle).isEqualTo("네이버"); } + @Test + @DisplayName("브런치 Url 성공 테스트") + void brunchSitesAvailable() throws IOException { + /* + given + */ + String basicUrl = "https://brunch.co.kr/@cat4348/74"; + /* + when + */ + String resultOfTitle = getOg(basicUrl).titleAdvanced(); + String resultOfImage = getOg(basicUrl).imageAdvanced(); + + /* + then + */ + System.out.println(resultOfTitle); + System.out.println(resultOfImage); + Assertions.assertThat(resultOfImage).isNotEmpty(); // Assuming a non-empty image URL + Assertions.assertThat(resultOfTitle).isEqualTo("오프라인 아이디어 수집, UX라이팅 등"); + } + + @Test + @DisplayName("티스토리 Url 성공 테스트") + void tistorySitesAvailable() throws IOException { + /* + given + */ + String basicUrl = "https://rorobong.tistory.com/m/125"; + + /* + when + */ + String resultOfTitle = getOg(basicUrl).titleAdvanced(); + String resultOfImage = getOg(basicUrl).imageAdvanced(); + + /* + then + */ + System.out.println(resultOfTitle); + System.out.println(resultOfImage); + Assertions.assertThat(resultOfImage).isNotEmpty(); // Assuming a non-empty image URL + Assertions.assertThat(resultOfTitle).isEqualTo("[JAVA] 자바 제어자 (접근 지정자, static 제어자)"); + } + + @Test + @DisplayName("깃허브 Url 성공 테스트") + void gitHubSitesAvailable() throws IOException { + /* + given + */ + String basicUrl = "https://github.com/team-winey/Winey-Server"; + + /* + when + */ + String resultOfTitle = getOg(basicUrl).titleAdvanced(); + String resultOfImage = getOg(basicUrl).imageAdvanced(); + + /* + then + */ + System.out.println(resultOfTitle); + System.out.println(resultOfImage); + Assertions.assertThat(resultOfImage).isNotEmpty(); // Assuming a non-empty image URL + Assertions.assertThat(resultOfTitle).isEqualTo("GitHub - team-winey/Winey-Server: \uD83D\uDCB8 아니, 어제만 해도 왕족이었던 내가 평민이라고?"); + } + //현재 로직을 오버라이드해서 가져온다. @Override public OgResponse getOg(String linkUrl) throws IOException { @@ -60,6 +142,8 @@ private String getTitle(String linkUrl) throws IOException { Document doc = Jsoup.connect(linkUrl).get(); Elements ogTitleElements = doc.select("meta[property=og:title]"); Elements titleElements = doc.select("head").select("title"); + System.out.println(titleElements); + System.out.println(ogTitleElements); if (ogTitleElements.isEmpty() && titleElements.isEmpty()) { return null; } @@ -75,12 +159,16 @@ private String getImage(String linkUrl){ Document doc = Jsoup.connect(linkUrl).get(); Elements iframes = doc.select("iframe"); Elements ogBlogImage = new Elements(); + //인스타의 경우 if (!iframes.isEmpty()){ Document iframeDoc = Jsoup.parse(doc.select("iframe").get(0).html()); + // System.out.println("durl"); ogBlogImage = iframeDoc.select("meta[property=og:image]"); } Elements ogImageElements = doc.select("meta[property=og:image]"); Elements ogImage = doc.select("img[property=src]"); + // System.out.println("1번"+ogImageElements); // tistory,naver + // System.out.println("2번"+ogImage); //brunch //짜증나게 iframe 안에 박아놓은 경우. return findImageAnywhere(ogImageElements, ogImage, ogBlogImage); }catch (MalformedURLException e){ @@ -93,18 +181,39 @@ private String getImage(String linkUrl){ } private String findImageAnywhere(Elements metaCase, Elements imgCase, Elements iframeCase){ - if (!metaCase.isEmpty()){ - return metaCase.get(0).attr("content"); - } - else if(!imgCase.isEmpty()){ + if (!metaCase.isEmpty()) { //naver,brunch + System.out.println("metaCase"); + String excludeSlug = metaCase.get(0).attr("content"); + String responseUrl = extractSlugUrl(excludeSlug); + return responseUrl==null ? excludeSlug : responseUrl; + } else if (!imgCase.isEmpty()) { + System.out.println("imgCase"); return metaCase.get(0).text(); - } - else if(!iframeCase.isEmpty()){ + } else if (!iframeCase.isEmpty()) { + System.out.println("iframeCase"); return iframeCase.get(0).attr("content"); - } - else{ + } else { return null; } } + private String extractSlugUrl(String slug) { + // 정규표현식 패턴 설정 + Pattern pattern = Pattern.compile("(?<=fname=)([^&]+(?:%[0-9a-fA-F]{2})*)"); //fname=이라는게 앞에 나오면 그때부터 패턴 시작. &는 파라미터 구별하니 제외. + // "/"같은거 16진수로 나오는 패턴이 %XX형태로 나오니 %이후에 16진수로 올수있는거 2자리가 *로 반복해서 올 수 있다를 명시. + Matcher matcher = pattern.matcher(slug); + + // 매칭된 URL 추출 + if (matcher.find()) { + // URL 디코딩 수행 + String url = matcher.group(); + url = decode(url, StandardCharsets.UTF_8); + return url; + } + + return null; + } } + + +