Skip to content

Commit

Permalink
Feat: 사용자는 허브 링크 분류 목록을 조회할 수 있다.
Browse files Browse the repository at this point in the history
Feat: 사용자는 허브 링크 분류 목록을 조회할 수 있다.
  • Loading branch information
hseong3243 authored Feb 18, 2024
2 parents 88a21f5 + 55c48b4 commit f4bb631
Show file tree
Hide file tree
Showing 24 changed files with 573 additions and 32 deletions.
10 changes: 10 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ operation::link-controller-test/create-hub-link[snippets='http-response,response

operation::link-controller-test/find-links[snippets='http-response,response-fields']

=== 허브 링크 묶음 목록 조회

==== request

operation::link-bundle-controller-test/find-hub-link-bundles[snippets='http-request,request-headers,path-parameters']

==== response

operation::link-bundle-controller-test/find-hub-link-bundles[snippets='http-response,response-fields']

== 허브

=== 허브 생성
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/com/seong/shoutlink/domain/auth/NullableUser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.seong.shoutlink.domain.auth;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface NullableUser {

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
import com.seong.shoutlink.domain.hub.Hub;
import com.seong.shoutlink.domain.hub.service.HubRepository;
import com.seong.shoutlink.domain.hub.service.result.HubPaginationResult;
import com.seong.shoutlink.domain.hubMember.repository.HubMemberEntity;
import com.seong.shoutlink.domain.hubMember.repository.HubMemberJpaRepository;
import com.seong.shoutlink.domain.hubmember.repository.HubMemberEntity;
import com.seong.shoutlink.domain.hubmember.repository.HubMemberJpaRepository;
import com.seong.shoutlink.domain.member.Member;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seong.shoutlink.domain.hubMember;
package com.seong.shoutlink.domain.hubmember;

public enum HubMemberRole {
MASTER, PARTICIPANTS
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.seong.shoutlink.domain.hubMember.repository;
package com.seong.shoutlink.domain.hubmember.repository;

import com.seong.shoutlink.domain.hub.Hub;
import com.seong.shoutlink.domain.hub.repository.HubEntity;
import com.seong.shoutlink.domain.hubMember.HubMemberRole;
import com.seong.shoutlink.domain.hubmember.HubMemberRole;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seong.shoutlink.domain.hubMember.repository;
package com.seong.shoutlink.domain.hubmember.repository;

import java.util.Optional;
import org.springframework.data.domain.Page;
Expand All @@ -12,11 +12,11 @@ public interface HubMemberJpaRepository extends JpaRepository<HubMemberEntity, L
@Query("select hm from HubMemberEntity hm "
+ "join hm.hubEntity h "
+ "where h.hubId = :hubId "
+ "and hm.hubMemberRole = com.seong.shoutlink.domain.hubMember.HubMemberRole.MASTER")
+ "and hm.hubMemberRole = com.seong.shoutlink.domain.hubmember.HubMemberRole.MASTER")
Optional<HubMemberEntity> findHubMasterByHubIdWithHub(@Param("hubId") Long hubId);

@Query("select hm from HubMemberEntity hm "
+ "join fetch hm.hubEntity h "
+ "where hm.hubMemberRole = com.seong.shoutlink.domain.hubMember.HubMemberRole.MASTER")
+ "where hm.hubMemberRole = com.seong.shoutlink.domain.hubmember.HubMemberRole.MASTER")
Page<HubMemberEntity> findHubs(PageRequest pageRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.seong.shoutlink.domain.hubmember.repository;

import com.seong.shoutlink.domain.hub.Hub;
import com.seong.shoutlink.domain.hubmember.service.HubMemberRepository;
import com.seong.shoutlink.domain.member.Member;
import org.springframework.stereotype.Repository;

@Repository
public class HubMemberRepositoryImpl implements HubMemberRepository {

@Override
public boolean isHubMember(Hub hub, Member member) {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.seong.shoutlink.domain.hubmember.service;

import com.seong.shoutlink.domain.hub.Hub;
import com.seong.shoutlink.domain.member.Member;

public interface HubMemberRepository {

boolean isHubMember(Hub hub, Member member);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.seong.shoutlink.domain.linkbundle.controller;

import com.seong.shoutlink.domain.auth.LoginUser;
import com.seong.shoutlink.domain.auth.NullableUser;
import com.seong.shoutlink.domain.linkbundle.controller.request.CreateLinkBundleRequest;
import com.seong.shoutlink.domain.linkbundle.service.LinkBundleService;
import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand;
import com.seong.shoutlink.domain.linkbundle.service.request.FindHubLinkBundlesCommand;
import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse;
Expand Down Expand Up @@ -59,4 +61,13 @@ public ResponseEntity<CreateLinkBundleResponse> createHubLinkBundle(
request.isDefault()));
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@GetMapping("/hubs/{hubId}/link-bundles")
public ResponseEntity<FindLinkBundlesResponse> findHubLinkBundles(
@NullableUser Long nullableMemberId,
@PathVariable("hubId") Long hubId) {
FindLinkBundlesResponse response = linkBundleService.findHubLinkBundles(
new FindHubLinkBundlesCommand(hubId, nullableMemberId));
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,8 @@ public interface LinkBundleJpaRepository extends JpaRepository<LinkBundleEntity,
Optional<LinkBundle> findHubLinkBundle(
@Param("linkBundleId") Long linkBundleId,
@Param("hubId") Long hubId);

@Query("select lb from HubLinkBundleEntity lb "
+ "where lb.hubId = :hubId")
List<LinkBundleEntity> findAllByHubId(@Param("hubId") Long hubId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,12 @@ public Long save(HubLinkBundle hubLinkBundle) {
public Optional<LinkBundle> findHubLinkBundle(Long linkBundleId, Hub hub) {
return linkBundleJpaRepository.findHubLinkBundle(linkBundleId, hub.getHubId());
}

@Override
public List<LinkBundle> findHubLinkBundles(Hub hub) {
return linkBundleJpaRepository.findAllByHubId(hub.getHubId())
.stream()
.map(LinkBundleEntity::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ public interface LinkBundleRepository {
Long save(HubLinkBundle hubLinkBundle);

Optional<LinkBundle> findHubLinkBundle(Long linkBundleId, Hub hub);

List<LinkBundle> findHubLinkBundles(Hub hubId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,21 @@
import com.seong.shoutlink.domain.exception.ShoutLinkException;
import com.seong.shoutlink.domain.hub.Hub;
import com.seong.shoutlink.domain.hub.service.HubRepository;
import com.seong.shoutlink.domain.hubmember.service.HubMemberRepository;
import com.seong.shoutlink.domain.linkbundle.HubLinkBundle;
import com.seong.shoutlink.domain.linkbundle.LinkBundle;
import com.seong.shoutlink.domain.linkbundle.MemberLinkBundle;
import com.seong.shoutlink.domain.linkbundle.service.request.CreateHubLinkBundleCommand;
import com.seong.shoutlink.domain.linkbundle.service.request.FindHubLinkBundlesCommand;
import com.seong.shoutlink.domain.linkbundle.service.request.FindLinkBundlesCommand;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleCommand;
import com.seong.shoutlink.domain.linkbundle.service.response.CreateLinkBundleResponse;
import com.seong.shoutlink.domain.linkbundle.service.response.FindLinkBundlesResponse;
import com.seong.shoutlink.domain.member.Member;
import com.seong.shoutlink.domain.member.service.MemberRepository;
import jakarta.annotation.Nullable;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -25,6 +29,7 @@ public class LinkBundleService {

private final MemberRepository memberRepository;
private final HubRepository hubRepository;
private final HubMemberRepository hubMemberRepository;
private final LinkBundleRepository linkBundleRepository;

@Transactional
Expand Down Expand Up @@ -63,6 +68,32 @@ public CreateLinkBundleResponse createHubLinkBundle(CreateHubLinkBundleCommand c
return new CreateLinkBundleResponse(linkBundleRepository.save(hubLinkBundle));
}

@Transactional(readOnly = true)
public FindLinkBundlesResponse findHubLinkBundles(FindHubLinkBundlesCommand command) {
Hub hub = getHub(command.hubId());
if(hub.isPrivate()) {
checkAuthenticated(command.nullableMemberId());
Long memberId = command.nullableMemberId();
Member member = getMember(memberId);
checkHubMemberAuthority(hub, member);
}
List<LinkBundle> hubLinkBundles = linkBundleRepository.findHubLinkBundles(hub);
return FindLinkBundlesResponse.from(hubLinkBundles);
}

private void checkAuthenticated(@Nullable Long memberId) {
if(Objects.isNull(memberId)) {
throw new ShoutLinkException("인증되지 않은 회원입니다.", ErrorCode.UNAUTHENTICATED);
}
}

private void checkHubMemberAuthority(Hub hub, Member member) {
if(hubMemberRepository.isHubMember(hub, member)) {
return;
}
throw new ShoutLinkException("권한이 없습니다.", ErrorCode.UNAUTHORIZED);
}

private Hub getHub(Long hubId) {
return hubRepository.findById(hubId)
.orElseThrow(() -> new ShoutLinkException("존재하지 않는 허브입니다.", ErrorCode.NOT_FOUND));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.seong.shoutlink.domain.linkbundle.service.request;

import jakarta.annotation.Nullable;

public record FindHubLinkBundlesCommand(Long hubId, @Nullable Long nullableMemberId) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.seong.shoutlink.global.auth.resolver;

import com.seong.shoutlink.domain.auth.NullableUser;
import com.seong.shoutlink.global.auth.authentication.Authentication;
import com.seong.shoutlink.global.auth.authentication.AuthenticationContext;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
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;

@RequiredArgsConstructor
public class NullableUserArgumentResolver implements HandlerMethodArgumentResolver {

private final AuthenticationContext authenticationContext;

@Override
public boolean supportsParameter(MethodParameter parameter) {
boolean hasParameterAnnotation = parameter.hasParameterAnnotation(NullableUser.class);
boolean hasLongParameterType = parameter.getParameterType().isAssignableFrom(Long.class);
return hasParameterAnnotation && hasLongParameterType;
}

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Authentication authentication = authenticationContext.getAuthentication();
if(Objects.isNull(authentication)) {
return null;
}
return authentication.getPrincipal();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.seong.shoutlink.global.auth.authentication.AuthenticationContext;
import com.seong.shoutlink.global.auth.authentication.JwtAuthenticationInterceptor;
import com.seong.shoutlink.global.auth.authentication.JwtAuthenticationProvider;
import com.seong.shoutlink.global.auth.resolver.NullableUserArgumentResolver;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
Expand All @@ -30,6 +31,7 @@ public void addInterceptors(InterceptorRegistry registry) {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(new LoginUserArgumentResolver(authenticationContext));
resolvers.add(new NullableUserArgumentResolver(authenticationContext));
}

@Override
Expand Down
Loading

0 comments on commit f4bb631

Please sign in to comment.