Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into feat/#26
Browse files Browse the repository at this point in the history
  • Loading branch information
sohyundoh committed Jan 7, 2024
2 parents 22c7525 + 47f1ae7 commit d2ed63b
Show file tree
Hide file tree
Showing 38 changed files with 533 additions and 24 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ dependencies {
//Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

//Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'

}

subprojects {
Expand Down
7 changes: 7 additions & 0 deletions module-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ dependencies {

//Aop
implementation 'org.springframework.boot:spring-boot-starter-aop'

//Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'

}

ext {
Expand All @@ -39,4 +43,7 @@ dependencyManagement {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}

}
tasks.named('test') {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.mile.controller.moim;

import com.mile.dto.SuccessResponse;
import com.mile.exception.message.SuccessMessage;
import com.mile.moim.serivce.MoimService;
import com.mile.moim.serivce.dto.ContentListResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.security.Principal;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/moim")
public class MoimController implements MoimControllerSwagger {

private final MoimService moimService;

@GetMapping("/{moimId}")
@Override
public SuccessResponse<ContentListResponse> getTopicsFromMoim(
@PathVariable final Long moimId,
final Principal principal
) {
return SuccessResponse.of(SuccessMessage.TOPIC_SEARCH_SUCCESS, moimService.getContentsFromMoim(moimId, Long.valueOf(principal.getName())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mile.controller.moim;

import com.mile.dto.ErrorResponse;
import com.mile.dto.SuccessResponse;
import com.mile.moim.serivce.dto.ContentListResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
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 org.springframework.web.bind.annotation.PathVariable;

import java.security.Principal;

@Tag(name = "Moim", description = "모임 관련 API")
public interface MoimControllerSwagger {
@Operation(summary = "에디터 상단 글감 조회")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "글감 조회가 완료되었습니다."),
@ApiResponse(responseCode = "403", description = "해당 사용자는 모임에 접근 권한이 없습니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "404", description = "해당 모임의 주제가 존재하지 않습니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류입니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
}
)
SuccessResponse<ContentListResponse> getTopicsFromMoim(
@PathVariable final Long moimId,
final Principal principal
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.mile.controller.post;

import com.mile.dto.SuccessResponse;
import com.mile.exception.message.SuccessMessage;
import com.mile.post.service.PostService;
import com.mile.post.service.dto.CommentCreateRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PathVariable;
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 java.security.Principal;

@RestController
@RequestMapping("/api/post")
@RequiredArgsConstructor
public class PostController implements PostControllerSwagger{

private final PostService postService;

@PostMapping("/{postId}/comment")
@Override
public SuccessResponse postComment(
@PathVariable final Long postId,
@Valid @RequestBody final CommentCreateRequest commentCreateRequest,
final Principal principal
) {
postService.createCommentOnPost(
postId,
Long.valueOf(principal.getName()),
commentCreateRequest
);
return SuccessResponse.of(SuccessMessage.COMMENT_CREATE_SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.mile.controller.post;

import com.mile.dto.ErrorResponse;
import com.mile.dto.SuccessResponse;
import com.mile.post.service.dto.CommentCreateRequest;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
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 jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

import java.security.Principal;

@Tag(name = "Post", description = "게시글 관련 API - 댓글 등록/ 조회 포함")
public interface PostControllerSwagger {

@Operation(summary = "댓글 작성")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "댓글 등록이 완료되었습니다."),
@ApiResponse(responseCode = "400",
description = "1. 댓글 최대 입력 길이(500자)를 초과하였습니다.\n" +
"2.댓글에 내용이 없습니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "403", description = "해당 사용자는 모임에 접근 권한이 없습니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류입니다.",
content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
}
)
SuccessResponse postComment(
@PathVariable final Long postId,
@Valid @RequestBody final CommentCreateRequest commentCreateRequest,
final Principal principal
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public interface UserControllerSwagger {
@Operation(summary = "소셜 로그인")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "소셜 로그인이 완료되었습니다.",
content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(responseCode = "200", description = "소셜 로그인이 완료되었습니다."),
@ApiResponse(responseCode = "400", description =
"1. 요청한 값이 유효하지 않습니다.\n" + "2. 인가 코드가 만료되었습니다.\n",
content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
Expand All @@ -38,7 +37,7 @@ SuccessResponse<LoginSuccessResponse> login(
@Operation(summary = "액세스 토큰 재발급")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "액세스 토큰 재발급이 완료되었습니다.", content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(responseCode = "200", description = "액세스 토큰 재발급이 완료되었습니다."),
@ApiResponse(responseCode = "401", description = "리프레시 토큰이 유효하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "404", description = "해당 유저의 리프레시 토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
Expand All @@ -51,7 +50,7 @@ SuccessResponse<AccessTokenGetSuccess> refreshToken(
@Operation(summary = "회원 탈퇴")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "회원 삭제가 완료되었습니다.", content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(responseCode = "200", description = "회원 삭제가 완료되었습니다."),
@ApiResponse(responseCode = "404", description =
"1. 해당 유저의 리프레시 토큰이 존재하지 않습니다.\n" +
"2. 해당 유저는 존재하지 않습니다.",
Expand All @@ -66,7 +65,7 @@ SuccessResponse deleteUser(
@Operation(summary = "로그아웃")
@ApiResponses(
value = {
@ApiResponse(responseCode = "200", description = "로그아웃이 완료되었습니다.", content = @Content(schema = @Schema(implementation = SuccessResponse.class))),
@ApiResponse(responseCode = "200", description = "로그아웃이 완료되었습니다."),
@ApiResponse(responseCode = "404", description = "해당 유저의 리프레시 토큰이 존재하지 않습니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))),
@ApiResponse(responseCode = "500", description = "서버 내부 오류입니다.", content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
}
Expand Down
7 changes: 7 additions & 0 deletions module-api/src/test/java/com/mile/ApiApplicationTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.mile;

import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class ApiApplicationTests {
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public class SecurityConfig {


private static final String[] AUTH_WHITELIST = {
"/user/login",
"/user/token-refresh",
"/api/**",
"/actuator/health",
"/v3/api-docs/**",
"/swagger-ui/**",
Expand Down
4 changes: 4 additions & 0 deletions module-common/src/main/java/com/mile/dto/ErrorResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@ public record ErrorResponse(
public static ErrorResponse of(final ErrorMessage errorMessage) {
return new ErrorResponse(errorMessage.getStatus(), errorMessage.getMessage());
}

public static ErrorResponse of(final int status, final String message) {
return new ErrorResponse(status, message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,34 @@ public enum ErrorMessage {
*/
USER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 유저는 존재하지 않습니다."),
REFRESH_TOKEN_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 유저의 리프레시 토큰이 존재하지 않습니다."),
POST_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 글은 존재하지 않습니다."),

MOIM_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 글모임이 존재하지 않습니다."),
CONTENT_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "해당 모임의 주제가 존재하지 않습니다."),
HANDLER_NOT_FOUND(HttpStatus.NOT_FOUND.value(), "요청하신 URL은 정보가 없습니다."),

/*
Bad Request
*/
ENUM_VALUE_BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "요청한 값이 유효하지 않습니다."),
AUTHENTICATION_CODE_EXPIRED(HttpStatus.BAD_REQUEST.value(), "인가 코드가 만료되었습니다."),
SOCIAL_TYPE_BAD_REQUEST(HttpStatus.BAD_REQUEST.value(), "로그인 요청이 유효하지 않습니다."),

VALIDATION_REQUEST_MISSING_EXCEPTION(HttpStatus.BAD_REQUEST.value(), "요청 값이 유효하지 않습니다."),

BEARER_LOST_ERROR(HttpStatus.BAD_REQUEST.value(), "토큰의 요청에 Bearer이 담겨 있지 않습니다."),

/*
Unauthorized
*/
ACCESS_TOKEN_EXPIRED(HttpStatus.UNAUTHORIZED.value(), "액세스 토큰이 만료되었습니다."),
TOKEN_INCORRECT_ERROR(HttpStatus.UNAUTHORIZED.value(), "리프레시 토큰이 유효하지 않습니다."),
TOKEN_VALIDATION_ERROR(HttpStatus.UNAUTHORIZED.value(), "사용자 검증 토큰이 유효하지 안습니다."),
/*
Forbidden
*/
USER_AUTHENTICATE_ERROR(HttpStatus.FORBIDDEN.value(), "해당 사용자는 모임에 접근 권한이 없습니다."),

/*
Internal Server Error
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ public enum SuccessMessage {
LOGIN_SUCCESS(HttpStatus.OK.value(), "소셜 로그인이 완료되었습니다."),
ISSUE_ACCESS_TOKEN_SUCCESS(HttpStatus.OK.value(), "액세스 토큰 재발급이 완료되었습니다."),
USER_DELETE_SUCCESS(HttpStatus.OK.value(), "회원 삭제가 완료되었습니다."),
LOGOUT_SUCCESS(HttpStatus.OK.value(), "로그아웃이 완료되었습니다.")
LOGOUT_SUCCESS(HttpStatus.OK.value(), "로그아웃이 완료되었습니다."),

/*
201 CREATED
*/
COMMENT_CREATE_SUCCESS(HttpStatus.CREATED.value(), "댓글 등록이 완료되었습니다."),


TOPIC_SEARCH_SUCCESS(HttpStatus.OK.value(), "주제 조회가 완료되었습니다."),
;

int status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.mile.exception.model;

import com.mile.exception.message.ErrorMessage;

public class ForbiddenException extends MileException {
public ForbiddenException(final ErrorMessage errorMessage) {
super(errorMessage);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.mile.authentication.UserAuthentication;
import com.mile.exception.message.ErrorMessage;
import com.mile.exception.model.BadRequestException;
import com.mile.exception.model.JwtValidationException;
import com.mile.jwt.JwtTokenProvider;
import com.mile.jwt.JwtValidationType;
Expand All @@ -10,6 +11,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.NonNull;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
Expand All @@ -20,6 +22,7 @@
import java.io.IOException;

@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

Expand Down Expand Up @@ -49,6 +52,8 @@ private String getJwtFromRequest(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring("Bearer ".length());
} else if (StringUtils.hasText(bearerToken) && !bearerToken.startsWith("Bearer ")) {
throw new BadRequestException(ErrorMessage.BEARER_LOST_ERROR);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.mile.dto.ErrorResponse;
import com.mile.exception.message.ErrorMessage;
import com.mile.exception.model.BadRequestException;
import com.mile.exception.model.ForbiddenException;
import com.mile.exception.model.JwtValidationException;
import com.mile.exception.model.NotFoundException;
import com.mile.exception.model.UnauthorizedException;
Expand All @@ -11,8 +12,11 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

@Slf4j
@RestControllerAdvice
Expand All @@ -38,11 +42,28 @@ public ResponseEntity<ErrorResponse> handleJwtValidationException(final JwtValid
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(ErrorResponse.of(e.getErrorMessage()));
}

@ExceptionHandler(MethodArgumentNotValidException.class)
protected ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) {
FieldError fieldError = e.getBindingResult().getFieldError();
if (fieldError == null) return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrorResponse.of(ErrorMessage.VALIDATION_REQUEST_MISSING_EXCEPTION));
else return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ErrorResponse.of(HttpStatus.BAD_REQUEST.value(), fieldError.getDefaultMessage()));
}

@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<ErrorResponse> handleForbiddenException(final ForbiddenException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ErrorResponse.of(e.getErrorMessage()));
}

@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFoundException(final NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(e.getErrorMessage()));
}

@ExceptionHandler({NoHandlerFoundException.class})
public ResponseEntity<ErrorResponse> handleNoHandlerFoundException(final NoHandlerFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ErrorResponse.of(ErrorMessage.HANDLER_NOT_FOUND));
}

@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleException(final Exception error, final HttpServletRequest request) {
log.error("================================================NEW===============================================");
Expand Down
3 changes: 3 additions & 0 deletions module-domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ dependencies {
implementation project(':module-auth')
implementation project(':module-common')

//Swagger
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

// QueryDSL Implementation
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
Expand Down
Loading

0 comments on commit d2ed63b

Please sign in to comment.