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

[Chore/domain-setting] 도메인 설계 및 swagger 적용하였습니다. #8

Merged
merged 12 commits into from
Aug 8, 2023
51 changes: 21 additions & 30 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ sonar {
property 'sonar.language', 'java'
property 'sonar.sourceEncoding', 'UTF-8'
property("sonar.test.inclusions", "**/*Test.java")
property "sonar.exclusions", "**/test/**, **/*Application*.java"
property "sonar.exclusions", "**/test/**, **/*Application*.java, **/dto/**, **/entity/**, **/*Exception*.java, **/*RepositoryImpl.java, **/global/**, **/resources/**"
property "sonar.java.coveragePlugin", "jacoco"
property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/test/jacocoTestReport.xml'
}
Expand Down Expand Up @@ -52,42 +52,19 @@ dependencies {
//logging
implementation 'org.springframework.boot:spring-boot-starter-logging'

// spring web
implementation 'org.springframework.boot:spring-boot-starter-web'

//lombok
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok'

//Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

//SWAGGER
implementation 'org.springdoc:springdoc-openapi-webmvc-core:1.7.0'
implementation 'org.springframework.boot:spring-boot-starter-actuator'

//Spring Data JPA
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.hibernate.orm:hibernate-core:6.2.5.Final'
//Mongo
implementation 'org.springframework.data:spring-data-mongodb'

//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"

//테스트용 H2
implementation 'com.h2database:h2'

//prometheus
implementation 'org.springframework.boot:spring-boot-starter-actuator'
runtimeOnly 'io.micrometer:micrometer-registry-prometheus'

//AOP
implementation 'org.springframework.boot:spring-boot-starter-aop'
//SWAGGER
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.4'

//MAYBE Nullable 빌드 에러 경고 해결
implementation 'com.google.code.findbugs:jsr305:3.0.2'
}

tasks.named('test') {
Expand All @@ -108,7 +85,14 @@ jacocoTestReport {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
"**/*Application*",
"**/config/*"
"**/config/*",
"**/entity/**",
"**/dto/**",
"**/exception/**",
"**/repository/**",
"**/global/*",
"**/service/**",
"**/controller/**"
])
}))
}
Expand Down Expand Up @@ -136,7 +120,14 @@ jacocoTestCoverageVerification {
classDirectories.setFrom(files(classDirectories.files.collect {
fileTree(dir: it, exclude: [
"**/*Application*",
"**/config/*"
"**/config/*",
"**/entity/**",
"**/dto/**",
"**/exception/**",
"**/repository/**",
"**/global/*",
"**/service/**",
"**/controller/**"
])
}))
}
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/everymeal/server/global/config/SwaggerConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package everymeal.server.global.config;


import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@SecurityScheme(
name = "bearerAuth",
description = "JWT auth bearer token",
scheme = "bearer",
type = SecuritySchemeType.HTTP,
bearerFormat = "JWT",
in = SecuritySchemeIn.HEADER)
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
Info info =
new Info()
.title("EveryMeal API Doc")
.version("v0.0.1")
.description("EveryMeal API 명세서입니다.");
return new OpenAPI().components(new Components()).info(info);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package everymeal.server.global.dto.response;


import everymeal.server.global.exception.ApplicationException;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;
import org.springframework.web.bind.annotation.ResponseBody;

@Getter
@Builder
@ResponseBody
public class ApplicationErrorResponse<T> {
@Schema(title = "성공여부", example = "false", description = "성공여부", defaultValue = "false")
private Boolean isSuccess;

@Schema(title = "에러메세지", example = "T0001", description = "에러메세지", defaultValue = "T0001")
private String errorCode;

@Schema(
title = "에러발생시간",
example = "2021-08-01T00:00:00",
description = "에러발생시간",
defaultValue = "2021-08-01T00:00:00")
private LocalDateTime localDateTime;

@Schema(
title = "에러메세지",
example = "에러가 발생했습니다.",
description = "에러메세지",
defaultValue = "에러가 발생했습니다.")
private String message;

public static <T> ApplicationErrorResponse<T> error(ApplicationException e) {
return error(e.getErrorCode(), e.getMessage());
}

public static <T> ApplicationErrorResponse<T> error(String errorCode, String message) {
return (ApplicationErrorResponse<T>)
ApplicationErrorResponse.builder()
.isSuccess(false)
.errorCode(errorCode)
.localDateTime(LocalDateTime.now())
.message(message)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package everymeal.server.global.dto.response;


import java.time.LocalDateTime;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class ApplicationResponse<T> {

private boolean isSuccess;
private LocalDateTime localDateTime;
private String message;
private T data; // == body

public static <T> ApplicationResponse<T> create(T data) {
return makeResponse("created", data);
}

public static <T> ApplicationResponse<T> create(String message, T data) {
return makeResponse(message, data);
}

public static <T> ApplicationResponse<T> ok() {
return makeResponse("성공", null);
}

public static <T> ApplicationResponse<T> ok(T data) {
return makeResponse("성공", data);
}

private static <T> ApplicationResponse<T> makeResponse(String message, T data) {
return (ApplicationResponse<T>)
ApplicationResponse.builder()
.isSuccess(true)
.localDateTime(LocalDateTime.now())
.message(message)
.data(data)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package everymeal.server.global.exception;


import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
public class ApplicationException extends RuntimeException {
private final String errorCode;
private final HttpStatus httpStatus;

public ApplicationException(ExceptionList exceptionList) {
super(exceptionList.getMESSAGE());
this.errorCode = exceptionList.getCODE();
this.httpStatus = exceptionList.getHttpStatus();
}
}
18 changes: 18 additions & 0 deletions src/main/java/everymeal/server/global/exception/ExceptionList.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package everymeal.server.global.exception;


import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@Getter
@RequiredArgsConstructor
public enum ExceptionList {
MEAL_NOT_FOUND("M0001", HttpStatus.NOT_FOUND, "Meal Not Found"),
INVALID_REQUEST("R0001", HttpStatus.BAD_REQUEST, "Request의 Data Type이 올바르지 않습니다."),
;

public final String CODE;
public final HttpStatus httpStatus;
public final String MESSAGE;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package everymeal.server.global.exception;

import static everymeal.server.global.exception.ExceptionList.INVALID_REQUEST;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import everymeal.server.global.dto.response.ApplicationErrorResponse;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
private static final String LOG_FORMAT = "Class : {}, CODE : {}, Message : {}";
private static final String INTERNAL_SERVER_ERROR_CODE = "S0001";

@ExceptionHandler(ApplicationException.class)
public ResponseEntity applicationException(ApplicationException e) {
String errorCode = e.getErrorCode();
log.warn(LOG_FORMAT, e.getClass().getSimpleName(), errorCode, e.getMessage());
return new ResponseEntity<>(
ApplicationErrorResponse.error(errorCode, e.getMessage()), e.getHttpStatus());
}

@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(RuntimeException.class)
public ApplicationErrorResponse<Object> runtimeException(RuntimeException e) {
log.error(
LOG_FORMAT,
e.getClass().getSimpleName(),
INTERNAL_SERVER_ERROR_CODE,
e.getMessage());
return ApplicationErrorResponse.error(INTERNAL_SERVER_ERROR_CODE, "런타임 에러가 발생했습니다.");
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApplicationErrorResponse<Object> validationException(MethodArgumentNotValidException e) {
log.error(
LOG_FORMAT,
e.getClass().getSimpleName(),
INTERNAL_SERVER_ERROR_CODE,
e.getMessage());
return ApplicationErrorResponse.error(
"V0001", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public ApplicationErrorResponse<Object> validationException(BindException e) {
log.error(
LOG_FORMAT,
e.getClass().getSimpleName(),
INTERNAL_SERVER_ERROR_CODE,
e.getMessage());
return ApplicationErrorResponse.error(
"V0001", e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}

@ApiResponse(
responseCode = "400",
description = "잘못된 요청",
content = @Content(schema = @Schema(implementation = ApplicationErrorResponse.class)))
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(HttpMessageNotReadableException.class)
public ApplicationErrorResponse<Object> typeErrorException(HttpMessageNotReadableException e) {
String errorCode = "T0001";
String errorMessage = e.getMessage();
log.error(
LOG_FORMAT,
e.getClass().getSimpleName(),
INTERNAL_SERVER_ERROR_CODE,
e.getMessage());
if (e.getCause() instanceof InvalidFormatException) {
errorCode = INVALID_REQUEST.getCODE();
errorMessage = INVALID_REQUEST.getMESSAGE();
}
return ApplicationErrorResponse.error(errorCode, errorMessage);
}

@ApiResponse(responseCode = "500", description = "서버 에러", content = @Content())
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler(Exception.class)
public ApplicationErrorResponse<Object> runtimeException(Exception e) {
log.error(
LOG_FORMAT,
e.getClass().getSimpleName(),
INTERNAL_SERVER_ERROR_CODE,
e.getMessage());
return ApplicationErrorResponse.error(INTERNAL_SERVER_ERROR_CODE, "런타임 에러가 발생했습니다.");
}
}
17 changes: 17 additions & 0 deletions src/main/java/everymeal/server/meal/controller/MealController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package everymeal.server.meal.controller;


import everymeal.server.meal.service.MealService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1/meals")
@Tag(name = "Meal API", description = "학식 식단 관련 API입니다")
public class MealController {

private final MealService mealService;
}
12 changes: 12 additions & 0 deletions src/main/java/everymeal/server/meal/entity/Area.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package everymeal.server.meal.entity;


import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(value = "area")
public class Area {
@Id private String _id;
private String name;
private University university;
}
Loading
Loading