Skip to content

Commit

Permalink
Merge pull request #14 from Findy-org/feat/유튜브-즐겨찾기에-장소-저장
Browse files Browse the repository at this point in the history
[FINDY-15] feat: 유튜브 즐겨찾기에 장소 저장
  • Loading branch information
Hoya324 authored Oct 31, 2024
2 parents d24008f + 199c713 commit e292c70
Show file tree
Hide file tree
Showing 59 changed files with 1,393 additions and 243 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ dependencies {
// jwt
implementation 'com.auth0:java-jwt:3.4.1'

//QueryDsl
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-docker-compose'
annotationProcessor 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,13 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ
final FilterChain filterChain) throws ServletException, IOException {

String tokenStr = HeaderUtil.getAccessToken(request);
if (tokenStr == null || tokenStr.isEmpty()) {
log.warn("No token found in request headers");
AuthToken token = tokenProvider.convertAuthToken(tokenStr);
if (token.validate()) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("Authenticated user: {}", authentication.getName());
} else {
AuthToken token = tokenProvider.convertAuthToken(tokenStr);
if (token.validate()) {
Authentication authentication = tokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
log.debug("Authenticated user: {}", authentication.getName());
} else {
log.error("Token validation failed for token: {}", tokenStr);
}
log.error("Token validation failed for token: {}", tokenStr);
}

filterChain.doFilter(request, response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.findy.findy_be.auth.oauth.domain.UserPrincipal;
import org.findy.findy_be.auth.oauth.info.OAuth2UserInfo;
import org.findy.findy_be.auth.oauth.info.OAuth2UserInfoFactory;
import org.findy.findy_be.bookmark.domain.Bookmark;
import org.findy.findy_be.bookmark.repository.BookmarkRepository;
import org.findy.findy_be.common.exception.custom.OAuthProviderMissMatchException;
import org.findy.findy_be.user.domain.RoleType;
import org.findy.findy_be.user.domain.User;
Expand All @@ -17,13 +19,18 @@
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

private static final String INIT_BOOKMARK_NAME = "내 장소";

private final BookmarkRepository bookmarkRepository;
private final UserRepository userRepository;

@Override
Expand Down Expand Up @@ -75,8 +82,10 @@ private User createUser(OAuth2UserInfo userInfo, SocialProviderType socialProvid
now,
now
);
userRepository.saveAndFlush(user);
return user;
User createdUser = userRepository.saveAndFlush(user);
Bookmark bookmark = Bookmark.createCustomType(INIT_BOOKMARK_NAME, createdUser);
bookmarkRepository.saveAndFlush(bookmark);
return createdUser;
}
}

Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
package org.findy.findy_be.bookmark.api;

import org.findy.findy_be.bookmark.api.swagger.BookmarkAPIPresentation;
import org.findy.findy_be.bookmark.application.register.RegisterYoutubeBookmark;
import org.findy.findy_be.bookmark.dto.request.YoutubeBookmarkRequest;
import org.findy.findy_be.common.meta.LoginUser;
import org.findy.findy_be.user.domain.User;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/bookmarks")
public class BookmarkController {
public class BookmarkController implements BookmarkAPIPresentation {

private final RegisterYoutubeBookmark registerYoutubeBookmark;

@PostMapping("/youtube")
public void registerPlace(@LoginUser User user, @Valid @RequestBody YoutubeBookmarkRequest request) {
registerYoutubeBookmark.invoke(user.getUserId(), request);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.findy.findy_be.bookmark.api.swagger;

import org.findy.findy_be.bookmark.dto.request.YoutubeBookmarkRequest;
import org.findy.findy_be.common.meta.CustomApiResponse;
import org.findy.findy_be.common.meta.CustomApiResponses;
import org.findy.findy_be.common.meta.LoginUser;
import org.findy.findy_be.user.domain.User;
import org.springframework.web.bind.annotation.RequestBody;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

@Tag(name = "Bookmark API", description = "북마크 관련 API")
public interface BookmarkAPIPresentation {

@Operation(summary = "유튜브 북마크 등록", description = "유저가 유튜브 북마크를 등록하는 API", responses = {
@ApiResponse(responseCode = "200", description = "유튜브 북마크 등록 성공"),
})
@CustomApiResponses({
@CustomApiResponse(error = "IllegalArgumentException", status = 400, message = "유튜브 즐겨찾기는 장소를 추가할 수 없습니다.", description = "유튜브 북마크에 잘못된 요청이 있는 경우"),
@CustomApiResponse(error = "ForbiddenAccessException", status = 403, message = "해당 즐겨찾기에 접근할 권한이 없습니다.", description = "권한이 없는 유저가 즐겨찾기에 접근할 경우"),
@CustomApiResponse(error = "EntityNotFoundException", status = 404, message = "해당 id : {id}의 즐겨찾기가 존재하지 않습니다.", description = "존재하지 않는 즐겨찾기에 접근할 경우"),
@CustomApiResponse(error = "InternalServerError", status = 500, message = "내부 서버 오류가 발생했습니다.", description = "서버 내부에서 예기치 않은 오류가 발생한 경우")
})
void registerPlace(@LoginUser User user, @Valid @RequestBody YoutubeBookmarkRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,5 @@
import org.findy.findy_be.bookmark.domain.Bookmark;

public interface FindBookMark {

Bookmark invokeById(Long bookmark_id);
Bookmark invoke(Long bookmarkId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FindBookMarkService implements FindBookMark {
private final BookmarkRepository bookmarkRepository;

@Override
public Bookmark invokeById(final Long bookmarkId) {
public Bookmark invoke(final Long bookmarkId) {
return bookmarkRepository.findById(bookmarkId)
.orElseThrow(
() -> new EntityNotFoundException(String.format(NOT_FOUND_BOOKMARK_BY_ID.getMessage(), bookmarkId)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.findy.findy_be.bookmark.application.register;

import org.findy.findy_be.bookmark.dto.request.YoutubeBookmarkRequest;

public interface RegisterYoutubeBookmark {
void invoke(final String userId, final YoutubeBookmarkRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.findy.findy_be.bookmark.application.register;

import static org.findy.findy_be.common.exception.ErrorCode.*;

import java.util.List;

import org.findy.findy_be.bookmark.domain.Bookmark;
import org.findy.findy_be.bookmark.dto.request.YoutubeBookmarkRequest;
import org.findy.findy_be.bookmark.repository.BookmarkRepository;
import org.findy.findy_be.common.exception.custom.ForbiddenAccessException;
import org.findy.findy_be.place.application.register.BatchRegisterPlace;
import org.findy.findy_be.place.dto.request.RegisterPlaceRequest;
import org.findy.findy_be.user.application.UserService;
import org.findy.findy_be.user.domain.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class RegisterYoutubeBookmarkService implements RegisterYoutubeBookmark {

private final UserService userService;
private final BookmarkRepository bookmarkRepository;
private final BatchRegisterPlace batchRegisterPlace;

@Override
public void invoke(final String userId, final YoutubeBookmarkRequest request) {
User user = userService.findUser(userId);
List<RegisterPlaceRequest> placeRequests = request.places();
bookmarkRepository.findByUserAndYoutuberId(user, request.youtuberId()).ifPresentOrElse(
existingBookmark -> {
validateBookmarkOwner(userId, existingBookmark);
batchRegisterPlace.invoke(existingBookmark, placeRequests);
},
() -> {
log.info("새로운 북마크에 저장합니다.");
Bookmark bookmark = Bookmark.createYoutubeType(request.youtuberName(), request.youtuberId(),
request.youtuberProfile(), user);
bookmarkRepository.save(bookmark);
batchRegisterPlace.invoke(bookmark, placeRequests);
}
);
}

private void validateBookmarkOwner(String userId, Bookmark bookmark) {
String bookmarkUserId = bookmark.getUser().getUserId();
if (!bookmarkUserId.equals(userId)) {
throw new ForbiddenAccessException(FORBIDDEN_BOOKMARK_ACCESS.getMessage());
}
}
}
18 changes: 14 additions & 4 deletions src/main/java/org/findy/findy_be/bookmark/domain/Bookmark.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,35 @@ public class Bookmark extends BaseEntity {
private BookmarkType bookmarkType;

private String youtuberId;
private String youtuberProfile;

@NotNull
private Long markersCount;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public static Bookmark of(String name, BookmarkType type, String youtuberId, User user) {
public void incrementMarkersCount(int incrementValue) {
this.markersCount += incrementValue;
}

public static Bookmark of(String name, BookmarkType type, String youtuberId, String youtuberProfile, User user) {
return Bookmark.builder()
.name(name)
.bookmarkType(type)
.youtuberId(youtuberId)
.youtuberProfile(youtuberProfile)
.markersCount(0L)
.user(user)
.build();
}

public static Bookmark createYoutubeType(String name, String youtuberId, User user) {
return Bookmark.of(name, BookmarkType.YOUTUBE, youtuberId, user);
public static Bookmark createYoutubeType(String name, String youtuberId, String youtuberProfile, User user) {
return Bookmark.of(name, BookmarkType.YOUTUBE, youtuberId, youtuberProfile, user);
}

public static Bookmark createCustomType(String name, User user) {
return Bookmark.of(name, BookmarkType.CUSTOM, null, user);
return Bookmark.of(name, BookmarkType.CUSTOM, null, null, user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.findy.findy_be.bookmark.dto.request;

import org.findy.findy_be.place.domain.MajorCategory;
import org.findy.findy_be.place.domain.MiddleCategory;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

public record CategoryRequest(
@NotNull(message = "대분류는 비어있을 수 없습니다.")
@Schema(description = "대분류", example = "RESTAURANT")
MajorCategory majorCategory,

@Schema(description = "중분류", example = "KOREAN")
MiddleCategory middleCategory
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.findy.findy_be.bookmark.dto.request;

import java.util.List;

import org.findy.findy_be.common.validation.ValidYoutuberId;
import org.findy.findy_be.place.dto.request.RegisterPlaceRequest;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

@Schema(description = "유튜브 즐겨찾기 DTO")
public record YoutubeBookmarkRequest(

@ValidYoutuberId(message = "유튜버 ID는 @으로 시작해야합니다.")
@Schema(description = "유튜버 ID", example = "@iammingki")
String youtuberId,

@NotNull(message = "유튜버 이름은 비어있을 수 없습니다.")
@Schema(description = "유튜버 이름", example = "걍밍경")
String youtuberName,

@Schema(description = "유튜버 프로필 link", example = "https://yt3.googleusercontent.com/ytc/AIdro_mieTH2WSE4oBMmczfLHB3HhikzOg1nz9tFD-MLad93Xnw=s160-c-k-c0x00ffffff-no-rj")
String youtuberProfile,

@Schema(description = "유튜브 링크", example = "https://www.youtube.com/watch?v=hE2wMo5Coco")
String youtubeLink,

List<RegisterPlaceRequest> places
) {
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.findy.findy_be.bookmark.repository;

import java.util.Optional;

import org.findy.findy_be.bookmark.domain.Bookmark;
import org.findy.findy_be.user.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
Optional<Bookmark> findByUserAndYoutuberId(final User user, final String youtuberId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void doFilter(final ServletRequest req, final ServletResponse res, final
String origin = request.getHeader("Origin");

if (origin != null && (origin.equals("http://localhost:5173") ||
origin.equals("http://localhost:8080") ||
origin.equals("http://localhost:8081") ||
origin.equals("https://nid.naver.com") ||
origin.equals("https://kauth.kakao.com"))) {
response.setHeader("Access-Control-Allow-Origin", origin);
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/org/findy/findy_be/common/config/QueryDslConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.findy.findy_be.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.querydsl.jpa.impl.JPAQueryFactory;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;

@Configuration
public class QueryDslConfig {

@PersistenceContext
private EntityManager entityManager;

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
25 changes: 15 additions & 10 deletions src/main/java/org/findy/findy_be/common/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,21 @@ public OperationCustomizer operationCustomizer() {

private void handleCustomApiResponses(ApiResponses apiResponses, HandlerMethod handlerMethod) {
Method method = handlerMethod.getMethod();

CustomApiResponses customApiResponses = method.getAnnotation(CustomApiResponses.class);
if (customApiResponses == null) {
for (Class<?> customApiInterface : handlerMethod.getBeanType().getInterfaces()) {
try {
Method interfaceMethod = customApiInterface.getMethod(method.getName(), method.getParameterTypes());
customApiResponses = interfaceMethod.getAnnotation(CustomApiResponses.class);
if (customApiResponses != null) {
break;
}
} catch (NoSuchMethodException e) {
}
}
}

if (customApiResponses != null) {
for (CustomApiResponse customApiResponse : customApiResponses.value()) {
ApiResponse apiResponse = new ApiResponse();
Expand All @@ -80,15 +94,6 @@ private void handleCustomApiResponses(ApiResponses apiResponses, HandlerMethod h
customApiResponse.message(), customApiResponse.isArray());
apiResponses.addApiResponse(String.valueOf(customApiResponse.status()), apiResponse);
}
} else {
CustomApiResponse customApiResponse = method.getAnnotation(CustomApiResponse.class);
if (customApiResponse != null) {
ApiResponse apiResponse = new ApiResponse();
apiResponse.setDescription(customApiResponse.description());
addContent(apiResponse, customApiResponse.error(), customApiResponse.status(),
customApiResponse.message(), customApiResponse.isArray());
apiResponses.addApiResponse(String.valueOf(customApiResponse.status()), apiResponse);
}
}
}

Expand Down Expand Up @@ -120,4 +125,4 @@ private Schema<ErrorResponse> createErrorResponseSchema() {
"message", new Schema<String>().type("string")
));
}
}
}
Loading

0 comments on commit e292c70

Please sign in to comment.