-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: ApiFolderErrorCode 오타 수정 (#468) * [REFACTOR] 픽 리팩토링, 검색 테스트 구현 (#474) * refactor: FolderType label 수정 * refactor: 파라미터 idList -> pickIdList 명시적으로 변경 * refactor: folder 내부에 있는 pick_order 리스트에 pickId가 중복 생성되는 문제 해결 * refactor: root folder 검색 못하도록 예외 처리 * refactor: pick controller 메서드명 수정 * refactor: DB fleid 함수를 이용하여 정렬된 픽 리스트 조회할 수 있는 메서드 구현 * refactor: 픽 검색 시 폴더 리스트가 null인 경우 검증하지 않도록 변경, tag 검증 추가 * refactor: 테스트 코드 작성 * refactor: 테스트 코드 ParameterizedTest로 리팩토링 (#476) * [FIX] 픽 수정 리팩토링 및 검증로직 개선 (#477) * refactor: 불필요한 초기값 제거 * fix: 엔티티를 list로 조회시 존재하지 않는 엔티티를 조회하려고 하면 NOT_FOUND 예외 발생 * fix: pick update시 부모폴더 id도 변경 가능하도록 수정, 누락된 검증 로직 추가 * fix: 기능 변경에 의한 테스트 코드 수정 * fix: idList로 폴더 조회시 존재하지 않는 폴더id로 조회하면 예외 발생 * refactor: 파라미터명 명확하게 수정 idList -> folderIdList * feat: 폴더 응답에 생성 수정 일자 추가 (#485) * [FEAT] 에러 요청에 대한 로깅 구현 (#481) * feat: error request logging 구현 * feat: 기존 예외처리 로직에 요청 로깅 추가 * chore: 주석 추가 * fix: 로깅 방식 변경 - application.log에 sql 로깅 제외 * fix: 누락된 로깅 로직 추가 * �[FEAT] 폴더 export 기능 구현 (#504) * feat: 폴더 export 기능 구현 * feat: url 수정 * [FEAT] 픽 제목 최대 길이 검증 및 Validation 검증 리팩토링 (#499) * refactor: 픽 제목 최대 길이 제약조건 추가 * feat: 픽 제목 최대 길이 제약조건 예외, 예외 코드 추가 * refactor: bean validation 예외 상세하게 반환하도록 수정 * refactor: tag 이동 시 리스트 길이보다 큰 인덱스로 이동 시 IndexOutOfBoundsException 발생하는 문제 해결 * fix: title이 null일 때 터지는 오류 해결 * fix: && 문법 오류 수정 * [BUG] 픽 태그 삭제 버그, 픽 수정 버그 수정 (#509) * refactor: 픽에 사용되었던 유저 태그 삭제 시 500에러 해결 * refactor: pick unique index title 제거 * refactor: 픽을 휴지통으로 이동 시 폴더 내에 있는 픽 리스트 제거 및 가독성을 위해 로직 공통 처리 * refactor: 불필요한 메서드 제거 및 메서드명 변경 * refactor: 운영 환경에서 스웨거 접근 못하도록 하는 설정 추가 * refactor: 폴더 삭제 시 픽들이 휴지통으로 이동될 때 부모 폴더의 픽 리스트 수정이 필요하지 않음. * refactor: 미분류, 휴지통 폴더에 폴더 생성하지 못하도록 변경, 에러 메세지 변경 * refactor: 메서드명 수정 * refactor: 픽 생성 시 타이틀이 없으면 터지는 예외 해결 * refactor: 데드락 문제 해결을 위해 트랜잭션 범위 최소화 * [BUG] 픽 태그 리스트 수정 시 데드락 문제 수정 (#514) * refactor: 픽 생성 시 타이틀이 없으면 터지는 예외 해결 * refactor: 데드락 문제 해결을 위해 트랜잭션 범위 최소화 * hotfix: Pick 엔티티의 Link Lazy -> Eager * refactor: PickTag에 낙관적 락 설정 * refactor: Spring retry 추가 * [REFACTOR] PickTag 테이블에 낙관적 락 설정 (#519) * refactor: PickTag에 낙관적 락 설정 * refactor: Spring retry 추가 * refactor: 픽태그 삭제 부분 수정 * refactor: pick 수정하는 부분에 비관적 락 추가 (#521) * [FEAT] 크롬 북마크 Import 기능 구현 (#522) * fix: access token, JSESSIONID 로깅 안하도록 수정, multipart file 캐싱 안하도록 수정 * fix: 입력값이 공백 또는 null일 경우 update하지 않음, 기본값을 빈스트링("")으로 설정 * feat: ogTag 업데이트 구현 및 필요한 의존성 추가 * feat: chrome 북마크 import를 위한 메소드 추가 * feat: chrome 북마크 import export 구현 * refactor: 주석 수정 * refactor: swagger 설정 수정 * refactor: link의 title과 description 필드 text타입으로 수정 * chore: 주석 typo 수정 * refactor: typo 수정 * feat: og 태그 데이터 가져오는 api 구현 (#525) * refactor: 운영 서버에서 스웨거 접근 못하도록 변경 (#527) --------- Co-authored-by: Pak Su Hyung <[email protected]> Co-authored-by: Sangwon Yang <[email protected]>
- Loading branch information
1 parent
d53ce07
commit 28ed30b
Showing
50 changed files
with
1,030 additions
and
148 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
108 changes: 108 additions & 0 deletions
108
...ain/java/techpick/api/application/chromebookmark/controller/ChromeBookmarkController.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,108 @@ | ||
package techpick.api.application.chromebookmark.controller; | ||
|
||
import java.io.IOException; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.List; | ||
import java.util.concurrent.CompletableFuture; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Executors; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
import org.springframework.core.io.ByteArrayResource; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.MediaType; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.PathVariable; | ||
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 org.springframework.web.multipart.MultipartFile; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import techpick.api.domain.chromebookmark.dto.ChromeImportResult; | ||
import techpick.api.domain.chromebookmark.service.ChromeBookmarkService; | ||
import techpick.api.domain.folder.dto.FolderCommand; | ||
import techpick.api.domain.link.service.LinkService; | ||
import techpick.security.annotation.LoginUserId; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/chrome") | ||
@Tag(name = "Chrome API", description = "Chrome Bookmark Import / Export API") | ||
public class ChromeBookmarkController { | ||
private final ChromeBookmarkService chromeBookmarkService; | ||
private final LinkService linkService; | ||
|
||
@GetMapping("/{folderId}/export") | ||
@Operation(summary = "특정 폴더 다운로드", description = "사용자의 특정 폴더를 크롬 브라우저 북마크에 import 가능한 형태로 다운로드 받습니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "다운로드 성공"), | ||
@ApiResponse(responseCode = "401", description = "본인 폴더만 다운로드할 수 있습니다.") | ||
}) | ||
public ResponseEntity<ByteArrayResource> exportFolder(@LoginUserId Long userId, @PathVariable Long folderId) { | ||
String exportResult = chromeBookmarkService.exportFolder(new FolderCommand.Export(userId, folderId)); | ||
|
||
ByteArrayResource resource = new ByteArrayResource(exportResult.getBytes()); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exported-folder.html"); | ||
headers.add(HttpHeaders.CONTENT_TYPE, "text/html; charset=UTF-8"); | ||
|
||
return ResponseEntity | ||
.status(HttpStatus.OK) | ||
.headers(headers) | ||
.body(resource); | ||
} | ||
|
||
@GetMapping("/export") | ||
@Operation(summary = "전체 폴더 다운로드", description = "사용자의 특정 폴더를 크롬 브라우저 북마크에 import 가능한 형태로 다운로드 받습니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "다운로드 성공") | ||
}) | ||
public ResponseEntity<ByteArrayResource> exportUserFolder(@LoginUserId Long userId) { | ||
String exportResult = chromeBookmarkService.exportUserFolder(userId); | ||
|
||
ByteArrayResource resource = new ByteArrayResource(exportResult.getBytes()); | ||
|
||
HttpHeaders headers = new HttpHeaders(); | ||
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=exported-folder.html"); | ||
headers.add(HttpHeaders.CONTENT_TYPE, "text/html; charset=UTF-8"); | ||
|
||
return ResponseEntity | ||
.status(HttpStatus.OK) | ||
.headers(headers) | ||
.body(resource); | ||
} | ||
|
||
@PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) | ||
@Operation(summary = "크롬 북마크 업로드", description = "내보내기한 크롬 북마크(.html)을 업로드 하여 일괄 추가합니다. 이미 등록된 url(중복 url)을 응답으로 보냅니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "다운로드 성공"), | ||
@ApiResponse(responseCode = "500", description = "파일 형식 및 파싱 오류") | ||
}) | ||
public ResponseEntity<List<String>> importBookmark(@LoginUserId Long userId, @RequestParam("file") | ||
@Parameter(required = true) MultipartFile file) throws | ||
InterruptedException, IOException { | ||
String html = new String(file.getBytes(), StandardCharsets.UTF_8); | ||
ChromeImportResult result = chromeBookmarkService.importChromeBookmarks(userId, html); | ||
|
||
// og 태그의 경우 정적 크롤링이 필요해, 최초 등록시에는 og 태그를 모두 빈스트링("")으로 등록하고, | ||
// 이후 비동기적으로 업데이트 진행 | ||
int maxThreadPoolSize = 5; | ||
ExecutorService executor = Executors.newFixedThreadPool(maxThreadPoolSize); | ||
for (String url : result.ogTagUpdateUrls()) { | ||
CompletableFuture.runAsync(() -> linkService.updateOgTag(url), executor) | ||
.orTimeout(60, TimeUnit.SECONDS); | ||
} | ||
|
||
return ResponseEntity.ok(result.alreadyExistBookmarks()); | ||
} | ||
} |
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
...echpick-api/src/main/java/techpick/api/application/link/controller/LinkApiController.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 techpick.api.application.link.controller; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.Parameter; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import lombok.RequiredArgsConstructor; | ||
import techpick.api.application.link.dto.LinkApiMapper; | ||
import techpick.api.application.link.dto.LinkApiResponse; | ||
import techpick.api.domain.link.dto.LinkResult; | ||
import techpick.api.domain.link.service.LinkService; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/links") | ||
@Tag(name = "Link API", description = "링크 API") | ||
public class LinkApiController { | ||
|
||
private final LinkService linkService; | ||
private final LinkApiMapper linkApiMapper; | ||
|
||
@GetMapping | ||
@Operation(summary = "해당 링크 og 데이터 조회", description = "해당 링크의 og 태그 데이터를 스크래핑을 통해 가져옵니다.") | ||
@ApiResponses(value = { | ||
@ApiResponse(responseCode = "200", description = "조회 성공") | ||
}) | ||
public ResponseEntity<LinkApiResponse> getLinkData( | ||
@Parameter(description = "og 태그 데이터 가져올 url") @RequestParam String url | ||
) { | ||
LinkResult linkResult = linkService.getUpdateOgTag(url); | ||
return ResponseEntity.ok(linkApiMapper.toLinkResult(linkResult)); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
backend/techpick-api/src/main/java/techpick/api/application/link/dto/LinkApiMapper.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,17 @@ | ||
package techpick.api.application.link.dto; | ||
|
||
import org.mapstruct.InjectionStrategy; | ||
import org.mapstruct.Mapper; | ||
import org.mapstruct.ReportingPolicy; | ||
|
||
import techpick.api.domain.link.dto.LinkResult; | ||
|
||
@Mapper( | ||
componentModel = "spring", | ||
injectionStrategy = InjectionStrategy.CONSTRUCTOR, | ||
unmappedTargetPolicy = ReportingPolicy.ERROR | ||
) | ||
public interface LinkApiMapper { | ||
|
||
LinkApiResponse toLinkResult(LinkResult linkResult); | ||
} |
8 changes: 8 additions & 0 deletions
8
backend/techpick-api/src/main/java/techpick/api/application/link/dto/LinkApiResponse.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,8 @@ | ||
package techpick.api.application.link.dto; | ||
|
||
public record LinkApiResponse( | ||
String title, | ||
String description, | ||
String imageUrl | ||
) { | ||
} |
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
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
28 changes: 28 additions & 0 deletions
28
backend/techpick-api/src/main/java/techpick/api/config/ValidationConfig.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,28 @@ | ||
package techpick.api.config; | ||
|
||
import org.springframework.context.MessageSource; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.support.ReloadableResourceBundleMessageSource; | ||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; | ||
|
||
@Configuration | ||
public class ValidationConfig { | ||
|
||
@Bean | ||
public MessageSource messageSource() { | ||
ReloadableResourceBundleMessageSource messageSource | ||
= new ReloadableResourceBundleMessageSource(); | ||
|
||
messageSource.setBasename("classpath:messages"); | ||
messageSource.setDefaultEncoding("UTF-8"); | ||
return messageSource; | ||
} | ||
|
||
@Bean | ||
public LocalValidatorFactoryBean getValidator() { | ||
LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); | ||
bean.setValidationMessageSource(messageSource()); | ||
return bean; | ||
} | ||
} |
Oops, something went wrong.