Skip to content

Commit

Permalink
Merge pull request #350 from ghkdqhrbals/feature/chat-crud
Browse files Browse the repository at this point in the history
Feature/chat crud
  • Loading branch information
ghkdqhrbals authored Dec 31, 2023
2 parents 3fc8be1 + 49e5876 commit 3b02133
Show file tree
Hide file tree
Showing 10 changed files with 484 additions and 10 deletions.
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

0 comments on commit 3b02133

Please sign in to comment.