Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Minor] Feature/chat crud #350

Merged
merged 3 commits into from
Dec 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
.Ds_store
/redis
.gradle
**/build/
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.commondto.dto.participant;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

public class ParticipantRequest {
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Setter
@ToString
public static class RemoveParticipantDto {
private Long roomId;

@Builder
public RemoveParticipantDto(Long roomId) {
this.roomId = roomId;
}
}

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Setter
@ToString
public static class AddParticipantRequest {
private Long roomId;

@Builder
public AddParticipantRequest(Long roomId) {
this.roomId = roomId;
}
}
}
22 changes: 18 additions & 4 deletions spring-chatting-backend-server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,30 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.0.5'
id 'io.spring.dependency-management' version '1.1.0'
id 'jacoco'
}

jacoco {
toolVersion = "0.8.8"
}

ext {
set('springCloudVersion', "2022.0.1")
}

jacocoTestReport {
dependsOn test // 리포트 생성을 위해서는 test가 먼저 완료되어야 함
reports {
xml.enabled false
html.enabled true
}
}

tasks.named('test') {
useJUnitPlatform()
finalizedBy jacocoTestReport // 테스트 종료후 항상 리포트 생성
}

group = 'chattingBackend'
version = project.rootProject.ext.projectVersion
sourceCompatibility = '17'
Expand Down Expand Up @@ -85,10 +103,6 @@ dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
useJUnitPlatform()
}

// find the latest jar file in the build/libs directory
def findLatestJar() {
def jarDir = file("${project.buildDir}/libs")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public RoomService roomService() {

@Bean
public ParticipantService participantService() {
return new ParticipantServiceImpl(participantRepository, userRepository);
return new ParticipantServiceImpl(participantRepository, userRepository, roomRepository);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package chatting.chat.domain.participant.api;

import chatting.chat.domain.participant.dto.ParticipantDto;
import chatting.chat.domain.participant.service.ParticipantService;
import chatting.chat.web.filter.UserContext;
import io.swagger.v3.oas.annotations.Operation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@Slf4j
@RestController
@AllArgsConstructor
public class ParticipantController {

private final ParticipantService participantService;

/**
* 내가 참여중인 채팅방에서 나가기
* @param roomId
* @implNote {@link UserContext#getUserId()} 가 필요합니다.
* {@link UserContext#setUserId(String)} 를 통해 userId 를 저장해주세요.
* UserConext 의 userId 는 {@link chatting.chat.web.filter.UserContextInterceptor} 에서 자동으로 설정됩니다.
* @return if success return "success" else throw {@link com.example.commondto.error.CustomException}
*/
@DeleteMapping("/participant")
@Operation(summary = "Delete a participant")
public String removeParticipant(@RequestParam Long roomId) {
return participantService.remove(roomId, UserContext.getUserId());
}

/**
* 채팅방에 참여
* @param roomId
* @implNote {@link UserContext#getUserId()} 가 필요합니다.
* {@link UserContext#setUserId(String)} 를 통해 userId 를 저장해주세요.
* UserConext 의 userId 는 {@link chatting.chat.web.filter.UserContextInterceptor} 에서 자동으로 설정됩니다.
* @return if success return "success" else throw {@link com.example.commondto.error.CustomException}
*/
@PostMapping("/participant")
@Operation(summary = "Add a participant")
public String addParticipant(@RequestParam Long roomId) {
return participantService.addParticipant(roomId, UserContext.getUserId());
}

/**
* 채팅방에 참여중인 유저 목록 조회
* @return List {@link ParticipantDto}
*/
@GetMapping("/participants")
@Operation(summary = "Get List of participants by room id")
public List<ParticipantDto> getParticipants(@RequestParam Long roomId) {
return participantService.findParticipantByRoomId(roomId);
}

/**
* 내가 참여중인 채팅방 목록 조회
* @return List {@link ParticipantDto}
*/
@GetMapping("/participant")
@Operation(summary = "Get List of my participants")
public List<ParticipantDto> getParticipant() {
return participantService.findAllByUserId(UserContext.getUserId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
import chatting.chat.domain.participant.entity.Participant;

import chatting.chat.domain.user.entity.User;
import com.example.commondto.dto.participant.ParticipantRequest;
import com.example.commondto.error.CustomException;
import java.util.List;

public interface ParticipantService {
List<ParticipantDto> findAllByUserId(String userId);
List<ParticipantDto> findParticipantByRoomId(Long roomId);
String save(Participant participant);
String remove(Participant participant);
String remove(Long roomId, String userId) throws CustomException;
String addParticipant(Long roomId, String userId) throws CustomException;


}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
import chatting.chat.domain.participant.dto.ParticipantDto;
import chatting.chat.domain.participant.entity.Participant;
import chatting.chat.domain.participant.repository.ParticipantRepository;
import chatting.chat.domain.room.entity.Room;
import chatting.chat.domain.room.repository.RoomRepository;
import chatting.chat.domain.user.entity.User;
import chatting.chat.domain.user.repository.UserRepository;
import com.example.commondto.dto.participant.ParticipantRequest;
import java.util.Optional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -20,13 +24,20 @@
public class ParticipantServiceImpl implements ParticipantService{
private final ParticipantRepository participantRepository;
private final UserRepository userRepository;
private final RoomRepository roomRepository;

public ParticipantServiceImpl(ParticipantRepository participantRepository,
UserRepository userRepository) {
UserRepository userRepository, RoomRepository roomRepository) {
this.participantRepository = participantRepository;
this.userRepository = userRepository;
this.roomRepository = roomRepository;
}

/**
* 유저 아이디로 현재 참여중인 채팅방 조회
* @param userId
* @return List {@link ParticipantDto}
*/
@Override
public List<ParticipantDto> findAllByUserId(String userId) {
return participantRepository.findAllByUserId(userId).stream().map(Participant::toDto).toList();
Expand All @@ -51,8 +62,29 @@ public String save(Participant participant) throws CustomException {
}

@Override
public String remove(Participant participant) {
public String remove(Long roomId, String userId) throws CustomException {
Participant participant = participantRepository.findByRoomIdAndUserId(roomId, userId);
if (participant == null){
throw new CustomException(INVALID_PARTICIPANT);
}
participantRepository.delete(participant);
return null;
return "success";
}

@Override
public String addParticipant(Long roomId, String userId) throws CustomException {
User user = userRepository.findByUserId(userId)
.orElseThrow(() -> new CustomException(CANNOT_FIND_USER));

Room room = roomRepository.findById(roomId)
.orElseThrow(() -> new CustomException(CANNOT_FIND_ROOM));

Participant participant = Participant.builder()
.room(room)
.user(user)
.build();

participantRepository.save(participant);
return "success";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,23 @@
import java.util.Optional;
import java.util.*;

/**
* 초기 요청 시, 쿠키에서 userId 를 추출하여 {@link UserRedisSessionRepository} 를 통해 Redis 에서 userId 에 해당하는
* {@link UserRedisSession} 을 가져옵니다. 이후 {@link UserContext} 에 userId 를 저장합니다.
*/
@Slf4j
@Component
public class UserContextInterceptor implements HandlerInterceptor {

private final UserRedisSessionRepository userRedisSessionRepository;

private final Map<String, Set<HttpMethod>> whiteList = new HashMap<>();
// private final List<String> whiteList = Arrays.asList("/health","/user");

/**
* @param userRedisSessionRepository
* @implNote WhiteList를 설정할 수 있으며 등록된 path 는 {@link UserContextInterceptor#preHandle} 에서 검증하지
* 않습니다.
*/
public UserContextInterceptor(UserRedisSessionRepository userRedisSessionRepository) {
this.userRedisSessionRepository = userRedisSessionRepository;

Expand All @@ -34,6 +42,11 @@ public UserContextInterceptor(UserRedisSessionRepository userRedisSessionReposit
addWhiteList("/health", HttpMethod.GET);
}

/**
* @param path
* @param method
* @implNote WhiteList 에 path 를 추가합니다.
*/
private void addWhiteList(String path, HttpMethod method) {
if (whiteList.containsKey(path)) {
whiteList.get(path).add(method);
Expand All @@ -42,6 +55,16 @@ private void addWhiteList(String path, HttpMethod method) {
}
}

/**
* @param request current HTTP request
* @param response current HTTP response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return {@code true} if the execution chain should proceed with the next interceptor or the
* handler itself.
* @throws Exception
* @implNote 요청이 들어올 때마다 userId 를 추출하여 {@link UserContext} 에 저장합니다. 추출된 userId 가 없을 경우 401 을
* 반환합니다. 또한 Redis 에 refreshToken 이 저장되어있지 않을 때도 401 에러를 반환합니다. 테스트 시 {@link UserContext#setUserId} 로 ThreadLocal 에 저장해주세요.
*/
@Override
@Timed(value = "interceptor.preHandle")
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Expand Down
Loading
Loading