diff --git a/BE/server/.gitignore b/BE/server/.gitignore new file mode 100644 index 000000000..84d0277bf --- /dev/null +++ b/BE/server/.gitignore @@ -0,0 +1,201 @@ +HELP.md +.gradle +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +# Created by https://www.toptal.com/developers/gitignore/api/java,macos,intellij +# Edit at https://www.toptal.com/developers/gitignore?templates=java,macos,intellij + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# End of https://www.toptal.com/developers/gitignore/api/java,macos,intellij +/src/main/resources/application-oauth.properties +/src/main/resources/application-prod.properties +/src/main/resources/application-local.properties diff --git a/BE/server/build.gradle b/BE/server/build.gradle new file mode 100644 index 000000000..6c6248783 --- /dev/null +++ b/BE/server/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'org.springframework.boot' version '2.5.0' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'java' +} + +group = 'com.team11' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '11' + +repositories { + mavenCentral() +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-web' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + runtimeOnly 'mysql:mysql-connector-java' + + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + implementation 'com.auth0:java-jwt:3.16.0' + + implementation 'io.springfox:springfox-boot-starter:3.0.0' + +} + +test { + useJUnitPlatform() +} diff --git a/BE/server/gradle/wrapper/gradle-wrapper.jar b/BE/server/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..e708b1c02 Binary files /dev/null and b/BE/server/gradle/wrapper/gradle-wrapper.jar differ diff --git a/BE/server/gradle/wrapper/gradle-wrapper.properties b/BE/server/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..0f80bbf51 --- /dev/null +++ b/BE/server/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/BE/server/gradlew b/BE/server/gradlew new file mode 100755 index 000000000..4f906e0c8 --- /dev/null +++ b/BE/server/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/BE/server/gradlew.bat b/BE/server/gradlew.bat new file mode 100644 index 000000000..107acd32c --- /dev/null +++ b/BE/server/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/BE/server/settings.gradle b/BE/server/settings.gradle new file mode 100644 index 000000000..48778a3e6 --- /dev/null +++ b/BE/server/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'issue' diff --git a/BE/server/src/main/java/com/team11/issue/IssueApplication.java b/BE/server/src/main/java/com/team11/issue/IssueApplication.java new file mode 100644 index 000000000..88437d7fe --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/IssueApplication.java @@ -0,0 +1,13 @@ +package com.team11.issue; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class IssueApplication { + + public static void main(String[] args) { + SpringApplication.run(IssueApplication.class, args); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/config/JwtAuthInterceptor.java b/BE/server/src/main/java/com/team11/issue/config/JwtAuthInterceptor.java new file mode 100644 index 000000000..247fbbe1d --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/config/JwtAuthInterceptor.java @@ -0,0 +1,42 @@ +package com.team11.issue.config; + +import com.team11.issue.exception.JWTTokenException; +import com.team11.issue.util.JwtUtil; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@Component +public class JwtAuthInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + return true; + } + + String token = getJwtToken(request); + String userName = JwtUtil.getUserIdFromJwtToken(token); + request.setAttribute("userName", userName); + return true; + + } + + private String getJwtToken(HttpServletRequest request) { + String authorizationHeader = request.getHeader("Authorization"); + + if (authorizationHeader == null) { + throw new JWTTokenException("토큰이 없습니다."); + } + + if (!authorizationHeader.startsWith("Bearer ")) { + throw new JWTTokenException("토큰 타입이 이상합니다."); + } + + return authorizationHeader.substring(7); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/config/RequestLoggingFilterConfig.java b/BE/server/src/main/java/com/team11/issue/config/RequestLoggingFilterConfig.java new file mode 100644 index 000000000..b8632f461 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/config/RequestLoggingFilterConfig.java @@ -0,0 +1,21 @@ +package com.team11.issue.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.CommonsRequestLoggingFilter; + +@Configuration +public class RequestLoggingFilterConfig { + + @Bean + public CommonsRequestLoggingFilter logFilter() { + CommonsRequestLoggingFilter filter = new CommonsRequestLoggingFilter(); + filter.setIncludeHeaders(false); + filter.setIncludeQueryString(true); + filter.setIncludePayload(true); + filter.setMaxPayloadLength(10000); + filter.setAfterMessagePrefix("REQUEST DATA : "); + return filter; + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/config/SwaggerConfig.java b/BE/server/src/main/java/com/team11/issue/config/SwaggerConfig.java new file mode 100644 index 000000000..b171252a6 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/config/SwaggerConfig.java @@ -0,0 +1,72 @@ +package com.team11.issue.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Configuration +public class SwaggerConfig { + + @Bean + public Docket newsApi() { + Set responseContentType = new HashSet<>(); + responseContentType.add("application/json;charset=UTF-8"); + + return new Docket(DocumentationType.SWAGGER_2) + .groupName("issue-tracker") + .produces(responseContentType) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.ant("/api/**")) + .build() + .securityContexts(Arrays.asList(securityContext())) + .securitySchemes(Arrays.asList(apiKey())); + + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("issue-tracker API") + .description("issue-tracker API") + .license("Apache License Version 2.0") + .version("2.0") + .build(); + } + + private ApiKey apiKey() { + return new ApiKey("JWT", "Authorization", "header"); + } + + private SecurityContext securityContext() { + return springfox + .documentation + .spi.service + .contexts + .SecurityContext + .builder() + .securityReferences(defaultAuth()).forPaths(PathSelectors.any()).build(); + } + + List defaultAuth() { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + return Arrays.asList(new SecurityReference("JWT", authorizationScopes)); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/config/WebConfig.java b/BE/server/src/main/java/com/team11/issue/config/WebConfig.java new file mode 100644 index 000000000..9bd06635c --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/config/WebConfig.java @@ -0,0 +1,28 @@ +package com.team11.issue.config; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@RequiredArgsConstructor +public class WebConfig implements WebMvcConfigurer { + + private final JwtAuthInterceptor jwtAuthInterceptor; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("*"); + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(jwtAuthInterceptor) + .addPathPatterns("/api/**") + .excludePathPatterns("/api/user/login", "/api"); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/CommentController.java b/BE/server/src/main/java/com/team11/issue/controller/CommentController.java new file mode 100644 index 000000000..377b66e0a --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/CommentController.java @@ -0,0 +1,48 @@ +package com.team11.issue.controller; + +import com.team11.issue.dto.ResponseDTO; +import com.team11.issue.dto.comment.CommentRequestDTO; +import com.team11.issue.service.CommentService; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("api") +@RequiredArgsConstructor +public class CommentController { + + private final CommentService commentService; + private final Logger logger = LoggerFactory.getLogger(CommentController.class); + + @PostMapping("/issue/{issueId}/comment") + public ResponseEntity createComment(@PathVariable Long issueId, + @ApiParam(hidden = true) @RequestAttribute String userName, + @RequestBody CommentRequestDTO commentRequestDTO) { + logger.info("코멘트 등록 요청"); + commentService.createComment(issueId, userName, commentRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @PutMapping("/issue/{issueId}/comment/{commentId}") + public ResponseEntity updateComment(@PathVariable Long issueId, + @PathVariable Long commentId, + @ApiParam(hidden = true) @RequestAttribute String userName, + @RequestBody CommentRequestDTO commentRequestDTO) { + logger.info("코멘트 수정 요청"); + commentService.updateComment(issueId, commentId, userName, commentRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @DeleteMapping("/issue/{issueId}/comment/{commentId}") + public ResponseEntity deleteComment(@PathVariable Long issueId, + @PathVariable Long commentId, + @ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("코멘트 삭제 요청"); + commentService.deleteComment(issueId, commentId, userName); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/IssueController.java b/BE/server/src/main/java/com/team11/issue/controller/IssueController.java new file mode 100644 index 000000000..591814f12 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/IssueController.java @@ -0,0 +1,66 @@ +package com.team11.issue.controller; + +import com.team11.issue.dto.ResponseDTO; +import com.team11.issue.dto.issue.*; +import com.team11.issue.service.IssueService; +import io.swagger.annotations.ApiParam; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor +@RequestMapping("api") +@RestController +public class IssueController { + + private final IssueService issueService; + private final Logger logger = LoggerFactory.getLogger(IssueController.class); + + @GetMapping("/issues") + public ResponseEntity showAllIssue() { + logger.info("이슈 전체목록 조회 요청"); + return ResponseEntity.ok().body(issueService.showAllIssue()); + } + + @GetMapping("/issue/{issueId}") + public ResponseEntity showIssue(@PathVariable Long issueId) { + logger.info("이슈 상세조회 요청"); + return ResponseEntity.ok().body(issueService.showIssue(issueId)); + } + + @PostMapping("/issue") + public ResponseEntity createIssue(@RequestBody IssueRequestDTO issueRequestDTO, + @ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("이슈 등록 요청"); + issueService.createIssue(issueRequestDTO, userName); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @PutMapping("/issues/status") + public ResponseEntity changeIssueStatus(@RequestBody IssueChangeStatusRequestDTO issueChangeStatusRequestDTO, + @ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("이슈 상태 수정 요청"); + issueService.changeIssueStatus(issueChangeStatusRequestDTO, userName); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @PutMapping("/issue/{issueId}") + public ResponseEntity updateIssue(@PathVariable Long issueId, + @RequestBody IssueRequestDTO issueRequestDTO, + @ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("이슈 수정 요청"); + issueService.updateIssue(issueId, issueRequestDTO, userName); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @DeleteMapping("/issue/{issueId}") + public ResponseEntity deleteIssue(@PathVariable Long issueId, + @ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("이슈 삭제 요청"); + issueService.deleteIssue(issueId, userName); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/LabelController.java b/BE/server/src/main/java/com/team11/issue/controller/LabelController.java new file mode 100644 index 000000000..20de16f93 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/LabelController.java @@ -0,0 +1,57 @@ +package com.team11.issue.controller; + +import com.team11.issue.dto.ResponseDTO; +import com.team11.issue.dto.label.LabelRequestDTO; +import com.team11.issue.dto.label.LabelResponseDTO; +import com.team11.issue.dto.label.LabelsResponseDTO; +import com.team11.issue.service.LabelService; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor +@RequestMapping("api") +@RestController +public class LabelController { + + private final LabelService labelService; + private final Logger logger = LoggerFactory.getLogger(LabelController.class); + + @GetMapping("/labels") + public ResponseEntity showAllLabel() { + logger.info("라벨 전체목록 조회 요청"); + return ResponseEntity.ok().body(labelService.showAllLabel()); + } + + @GetMapping("/label/{labelId}") + public ResponseEntity showLabel(@PathVariable Long labelId) { + logger.info("라벨 상세조회 요청"); + return ResponseEntity.ok().body(labelService.showLabel(labelId)); + } + + @PostMapping("/label") + public ResponseEntity createLabel(@RequestBody LabelRequestDTO labelRequestDTO) { + logger.info("라벨 등록 요청"); + labelService.createLabel(labelRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @PutMapping("/label/{labelId}") + public ResponseEntity updateLabel(@PathVariable Long labelId, + @RequestBody LabelRequestDTO labelRequestDTO) { + logger.info("라벨 수정 요청"); + labelService.updateLabel(labelId, labelRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @DeleteMapping("/label/{labelId}") + public ResponseEntity deleteLabel(@PathVariable Long labelId) { + logger.info("라벨 삭제 요청"); + labelService.deleteLabel(labelId); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/MilestoneController.java b/BE/server/src/main/java/com/team11/issue/controller/MilestoneController.java new file mode 100644 index 000000000..600c628fc --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/MilestoneController.java @@ -0,0 +1,56 @@ +package com.team11.issue.controller; + +import com.team11.issue.dto.ResponseDTO; +import com.team11.issue.dto.milestone.MilestoneRequestDTO; +import com.team11.issue.dto.milestone.MilestoneResponseDTO; +import com.team11.issue.dto.milestone.MilestonesResponseDTO; +import com.team11.issue.service.MilestoneService; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RequiredArgsConstructor +@RequestMapping("api") +@RestController +public class MilestoneController { + + private final MilestoneService milestoneService; + private final Logger logger = LoggerFactory.getLogger(MilestoneController.class); + + @GetMapping("/milestones") + public ResponseEntity showAllMilestone() { + logger.info("마일스톤 전체목록 조회 요청"); + return ResponseEntity.ok().body(milestoneService.showAllMilestone()); + } + + @GetMapping("/milestone/{milestoneId}") + public ResponseEntity showMilestone(@PathVariable Long milestoneId) { + logger.info("마일스톤 상세조회 요청"); + return ResponseEntity.ok().body(milestoneService.showMilestone(milestoneId)); + } + + @PostMapping("/milestone") + public ResponseEntity createMilestone(@RequestBody MilestoneRequestDTO milestoneRequestDTO) { + logger.info("마일스톤 등록 요청"); + milestoneService.createMilestone(milestoneRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @PutMapping("/milestone/{milestoneId}") + public ResponseEntity updateMilestone(@PathVariable Long milestoneId, + @RequestBody MilestoneRequestDTO milestoneRequestDTO) { + logger.info("마일스톤 수정 요청"); + milestoneService.updateMilestone(milestoneId, milestoneRequestDTO); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + + @DeleteMapping("/milestone/{milestoneId}") + public ResponseEntity deleteMilestone(@PathVariable Long milestoneId) { + logger.info("마일스톤 삭제 요청"); + milestoneService.deleteMilestone(milestoneId); + return ResponseEntity.ok().body(new ResponseDTO("OK")); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/SwaggerController.java b/BE/server/src/main/java/com/team11/issue/controller/SwaggerController.java new file mode 100644 index 000000000..93cc622c8 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/SwaggerController.java @@ -0,0 +1,16 @@ +package com.team11.issue.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import springfox.documentation.annotations.ApiIgnore; + +@ApiIgnore +@Controller +public class SwaggerController { + + @GetMapping("/api") + public String getApiDocs(){ + return "redirect:/swagger-ui/index.html"; + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/controller/UserController.java b/BE/server/src/main/java/com/team11/issue/controller/UserController.java new file mode 100644 index 000000000..6cc5e2e6c --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/controller/UserController.java @@ -0,0 +1,53 @@ +package com.team11.issue.controller; + +import com.team11.issue.dto.ResponseDTO; +import com.team11.issue.dto.user.LoginRequestDTO; +import com.team11.issue.dto.user.LoginResponseDTO; +import com.team11.issue.dto.user.UsersResponseDTO; +import com.team11.issue.service.UserService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Api(tags = {"USER 관련 API"}, description = "로그인, 로그아웃, 전체 사용자 조회와 관련된 API") +@RestController +@RequestMapping("api") +public class UserController { + + private final Logger logger = LoggerFactory.getLogger(UserController.class); + + private final UserService userService; + + public UserController(UserService userService) { + this.userService = userService; + } + + + @PostMapping("/user/login") + @ApiOperation(value = "로그인", notes = "code를 가지고 로그인을 합니다.") + public ResponseEntity login(@ApiParam("IssueTrackerFE || IssueTrackerIOS") @RequestHeader("User-Agent") String userAgent, + @ApiParam("github code") @RequestBody LoginRequestDTO loginRequestDTO) { + logger.info("로그인 요청: " + userAgent); + return ResponseEntity.ok().body(userService.login(userAgent, loginRequestDTO)); + } + + @PutMapping("/user/logout") + @ApiOperation(value = "로그아웃", notes = "jwt token을 가지고 로그아웃을 합니다.") + public ResponseEntity logout(@ApiParam(hidden = true) @RequestAttribute String userName) { + logger.info("로그아웃 요청"); + userService.logout(userName); + return ResponseEntity.ok().body(new ResponseDTO("logout")); + } + + @GetMapping("/users") + @ApiOperation(value = "User 전체 목록 조회", notes = "유저 전체 목록을 조회합니다.") + public ResponseEntity userList() { + logger.info("User 전체 목록 조회 요청"); + return ResponseEntity.ok().body(userService.userList()); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/Assignees.java b/BE/server/src/main/java/com/team11/issue/domain/Assignees.java new file mode 100644 index 000000000..f71d8f3b6 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/Assignees.java @@ -0,0 +1,56 @@ +package com.team11.issue.domain; + +import lombok.*; + +import javax.persistence.*; +import java.util.Objects; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "assignees") +public class Assignees { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "issue_id") + private Issue issue; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + private User user; + + public static Assignees createAssignees(Issue issue, User user) { + return Assignees.builder() + .issue(issue) + .user(user) + .build(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Assignees assignees = (Assignees) o; + return Objects.equals(user, assignees.user); + } + + @Override + public int hashCode() { + return Objects.hash(user); + } + + @Override + public String toString() { + return "Assignees{" + + "id=" + id + + ", issue=" + issue + + ", user=" + user + + '}'; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/Comment.java b/BE/server/src/main/java/com/team11/issue/domain/Comment.java new file mode 100644 index 000000000..fb3ef3e63 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/Comment.java @@ -0,0 +1,48 @@ +package com.team11.issue.domain; + +import com.team11.issue.dto.comment.CommentRequestDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String contents; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "issue_id") + private Issue issue; + + @OneToOne + @JoinColumn(name = "user_id") + private User user; + + private LocalDateTime createDateTime; + + public static Comment createComment(User user, Issue issue, CommentRequestDTO commentRequestDTO) { + return Comment.builder() + .contents(commentRequestDTO.getContents()) + .issue(issue) + .user(user) + .createDateTime(LocalDateTime.now()) + .build(); + } + + public void updateComment(String contents) { + this.contents = contents; + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/History.java b/BE/server/src/main/java/com/team11/issue/domain/History.java new file mode 100644 index 000000000..0074808b9 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/History.java @@ -0,0 +1,53 @@ +package com.team11.issue.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "history") +public class History { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne + @JoinColumn(name = "user_id") + private User user; + + private LocalDateTime historyDateTime; + + private String flag; + + @ManyToOne + @JoinColumn(name = "issue_id") + private Issue issue; + + public static History createHistory(User user, Issue issue, String flag) { + return History.builder() + .user(user) + .historyDateTime(LocalDateTime.now()) + .flag(flag) + .issue(issue) + .build(); + } + + @Override + public String toString() { + return "History{" + + "id=" + id + + ", user=" + user + + ", historyDateTime=" + historyDateTime + + ", flag='" + flag + '\'' + + '}'; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/Issue.java b/BE/server/src/main/java/com/team11/issue/domain/Issue.java new file mode 100644 index 000000000..6e1aeb808 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/Issue.java @@ -0,0 +1,79 @@ +package com.team11.issue.domain; + +import com.team11.issue.dto.issue.IssueRequestDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "issue") +public class Issue { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(columnDefinition = "tinyint(1) default 1") + private boolean isOpen; + + private String title; + + private String contents; + + @Column(columnDefinition = "tinyint(1) default 0") + private boolean isDelete; + + @OneToOne + @JoinColumn(name = "user_id") + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "milestone_id") + private Milestone milestone; + + @OneToMany(mappedBy = "issue") + private List assignees = new ArrayList<>(); + + @OneToMany(mappedBy = "issue") + private List issueHasLabels = new ArrayList<>(); + + public static Issue createIssue(User user, IssueRequestDTO issueRequestDTO, Milestone milestone) { + return Issue.builder() + .isOpen(true) + .title(issueRequestDTO.getTitle()) + .contents(issueRequestDTO.getContents()) + .user(user) + .milestone(milestone) + .build(); + } + + public Issue updateStatus(String status) { + if (status.equals("open")) { + this.isOpen = true; + } + + if (status.equals("closed")) { + this.isOpen = false; + } + return this; + } + + public void updateIssue(IssueRequestDTO issueRequestDTO, Milestone milestone) { + this.title = issueRequestDTO.getTitle(); + this.contents = issueRequestDTO.getContents(); + this.milestone = milestone; + } + + public void deleteIssue() { + this.isDelete = true; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/IssueHasLabel.java b/BE/server/src/main/java/com/team11/issue/domain/IssueHasLabel.java new file mode 100644 index 000000000..3cc77391d --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/IssueHasLabel.java @@ -0,0 +1,34 @@ +package com.team11.issue.domain; + +import lombok.*; + +import javax.persistence.*; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "issue_has_label") +public class IssueHasLabel { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "issue_id") + private Issue issue; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "label_id") + private Label label; + + public static IssueHasLabel createIssueHasLabel(Issue issue, Label label) { + return IssueHasLabel.builder() + .issue(issue) + .label(label) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/Label.java b/BE/server/src/main/java/com/team11/issue/domain/Label.java new file mode 100644 index 000000000..62bc12a90 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/Label.java @@ -0,0 +1,49 @@ +package com.team11.issue.domain; + +import com.team11.issue.dto.label.LabelRequestDTO; +import lombok.*; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "label") +public class Label { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + private String description; + + private String color; + + private String bgColor; + + @OneToMany(mappedBy = "label") + private List issueHasLabels = new ArrayList<>(); + + public static Label createLabel(LabelRequestDTO labelRequestDTO) { + return Label.builder() + .title(labelRequestDTO.getTitle()) + .description(labelRequestDTO.getDescription()) + .color(labelRequestDTO.getColor()) + .bgColor(labelRequestDTO.getBgColor()) + .build(); + } + + public void updateLabel(LabelRequestDTO labelRequestDTO) { + this.title = labelRequestDTO.getTitle(); + this.description = labelRequestDTO.getDescription(); + this.color = labelRequestDTO.getColor(); + this.bgColor = labelRequestDTO.getBgColor(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/Milestone.java b/BE/server/src/main/java/com/team11/issue/domain/Milestone.java new file mode 100644 index 000000000..2433cb679 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/Milestone.java @@ -0,0 +1,41 @@ +package com.team11.issue.domain; + +import com.team11.issue.dto.milestone.MilestoneRequestDTO; +import lombok.*; + +import javax.persistence.*; +import java.time.LocalDate; + +@ToString +@Getter +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Entity +@Table(name = "milestone") +public class Milestone { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + private LocalDate deadLineDate; + + private String description; + + public static Milestone createMilestone(MilestoneRequestDTO milestoneRequestDTO) { + return Milestone.builder() + .title(milestoneRequestDTO.getTitle()) + .deadLineDate(milestoneRequestDTO.getDeadLineDate()) + .description(milestoneRequestDTO.getDescription()) + .build(); + } + + public void updateMilestone(MilestoneRequestDTO milestoneRequestDTO) { + this.title = milestoneRequestDTO.getTitle(); + this.description = milestoneRequestDTO.getDescription(); + this.deadLineDate = milestoneRequestDTO.getDeadLineDate(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/domain/User.java b/BE/server/src/main/java/com/team11/issue/domain/User.java new file mode 100644 index 000000000..d3f667437 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/domain/User.java @@ -0,0 +1,78 @@ +package com.team11.issue.domain; + +import com.team11.issue.dto.oauth.UserInfoDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +@Table(name = "user") +public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(unique = true) + private String name; + + private String email; + + private String profileImage; + + private String accessToken; + + @OneToMany(mappedBy = "user") + private List assignees = new ArrayList<>(); + + public static User createUser(UserInfoDTO userInfoDTO, String accessToken) { + return User.builder() + .name(userInfoDTO.getName()) + .email(userInfoDTO.getEmail()) + .profileImage(userInfoDTO.getProfileImage()) + .accessToken(accessToken) + .build(); + } + + public void updateUser(String accessToken) { + this.accessToken = accessToken; + } + + public void removeAccessToken() { + this.accessToken = null; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + User user = (User) o; + return Objects.equals(id, user.id) && Objects.equals(name, user.name); + } + + @Override + public int hashCode() { + return Objects.hash(id, name); + } + + @Override + public String toString() { + return "User{" + + "id=" + id + + ", name='" + name + '\'' + + ", email='" + email + '\'' + + ", profileImage='" + profileImage + '\'' + + ", accessToken='" + accessToken + '\'' + + '}'; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/ExceptionResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/ExceptionResponseDTO.java new file mode 100644 index 000000000..0a98ea3a4 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/ExceptionResponseDTO.java @@ -0,0 +1,16 @@ +package com.team11.issue.dto; + +import lombok.Getter; + +@Getter +public class ExceptionResponseDTO { + + private String status; + private String message; + + + public ExceptionResponseDTO(String message) { + this.status = "error"; + this.message = message; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/ResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/ResponseDTO.java new file mode 100644 index 000000000..679dca50e --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/ResponseDTO.java @@ -0,0 +1,13 @@ +package com.team11.issue.dto; + +import lombok.Getter; + +@Getter +public class ResponseDTO { + + private final String status; + + public ResponseDTO(String status) { + this.status = status; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/comment/CommentRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentRequestDTO.java new file mode 100644 index 000000000..51bea9004 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentRequestDTO.java @@ -0,0 +1,15 @@ +package com.team11.issue.dto.comment; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class CommentRequestDTO { + + private Long authorId; + private String authorName; + private String contents; +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/comment/CommentResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentResponseDTO.java new file mode 100644 index 000000000..b9b441774 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentResponseDTO.java @@ -0,0 +1,31 @@ +package com.team11.issue.dto.comment; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.Comment; +import com.team11.issue.dto.user.UserResponseDTO; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; + +@JsonPropertyOrder({"commentId", "author", "contents", "createDateTime"}) +@RequiredArgsConstructor +@Builder +@Getter +public class CommentResponseDTO { + + private final Long commentId; + private final UserResponseDTO author; + private final String contents; + private final LocalDateTime createDateTime; + + public static CommentResponseDTO from(Comment comment) { + return CommentResponseDTO.builder() + .commentId(comment.getId()) + .author(UserResponseDTO.from(comment.getUser())) + .contents(comment.getContents()) + .createDateTime(comment.getCreateDateTime()) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/comment/CommentsResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentsResponseDTO.java new file mode 100644 index 000000000..037b48703 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/comment/CommentsResponseDTO.java @@ -0,0 +1,21 @@ +package com.team11.issue.dto.comment; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +@Builder +@Getter +public class CommentsResponseDTO { + + private final List comments; + + public static CommentsResponseDTO from(List commentResponseDTOS) { + return CommentsResponseDTO.builder() + .comments(commentResponseDTOS) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/history/HistoryResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/history/HistoryResponseDTO.java new file mode 100644 index 000000000..a928a035e --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/history/HistoryResponseDTO.java @@ -0,0 +1,30 @@ +package com.team11.issue.dto.history; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.History; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDateTime; + +@JsonPropertyOrder({"userName", "historyDateTime", "flag"}) +@RequiredArgsConstructor +@Builder +@Getter +public class HistoryResponseDTO { + + @JsonProperty("userName") + private final String userName; + private final LocalDateTime historyDateTime; + private final String flag; + + public static HistoryResponseDTO from(History history) { + return HistoryResponseDTO.builder() + .userName(history.getUser().getName()) + .historyDateTime(history.getHistoryDateTime()) + .flag(history.getFlag()) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/issue/IssueChangeStatusRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueChangeStatusRequestDTO.java new file mode 100644 index 000000000..6df8f55e8 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueChangeStatusRequestDTO.java @@ -0,0 +1,16 @@ +package com.team11.issue.dto.issue; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +public class IssueChangeStatusRequestDTO { + + private List issueIds; + private String changeState; +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/issue/IssueDetailResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueDetailResponseDTO.java new file mode 100644 index 000000000..8edc448e2 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueDetailResponseDTO.java @@ -0,0 +1,81 @@ +package com.team11.issue.dto.issue; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.*; +import com.team11.issue.dto.comment.CommentResponseDTO; +import com.team11.issue.dto.history.HistoryResponseDTO; +import com.team11.issue.dto.label.LabelResponseDTO; +import com.team11.issue.dto.milestone.MilestoneResponseDTO; +import com.team11.issue.dto.user.UserResponseDTO; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@JsonPropertyOrder({"issueId", "history", "isOpen", "title", "contents", "author", "assignees", "milestone", "labels", "comments"}) +@RequiredArgsConstructor +@Builder +@Getter +public class IssueDetailResponseDTO { + + private final Long issueId; + private final HistoryResponseDTO history; + private final boolean isOpen; + private final String title; + private final String contents; + private final UserResponseDTO author; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private final List assignees; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private final MilestoneResponseDTO milestone; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private final List labels; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private final List comments; + + + public boolean getIsOpen() { + return isOpen; + } + + private static List convertLabels(List issueHasLabels) { + return issueHasLabels.stream() + .map(issueHasLabel -> LabelResponseDTO.from(issueHasLabel.getLabel())) + .collect(Collectors.toList()); + } + + private static List convertUsers(List assignees) { + return assignees.stream() + .map(assignee -> UserResponseDTO.from(assignee.getUser())) + .collect(Collectors.toList()); + } + + private static List convertComments(List comments) { + return comments.stream() + .map(comment -> CommentResponseDTO.from(comment)) + .collect(Collectors.toList()); + } + + public static IssueDetailResponseDTO from(Issue issue, History history, User user, List assignees, MilestoneResponseDTO milestoneResponseDTO, List issueHasLabels, List comments) { + return IssueDetailResponseDTO.builder() + .issueId(issue.getId()) + .history(HistoryResponseDTO.from(history)) + .isOpen(issue.isOpen()) + .title(issue.getTitle()) + .contents(issue.getContents()) + .author(UserResponseDTO.from(user)) + .assignees(convertUsers(assignees)) + .milestone(milestoneResponseDTO) + .labels(convertLabels(issueHasLabels)) + .comments(convertComments(comments)) + .build(); + + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/issue/IssueRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueRequestDTO.java new file mode 100644 index 000000000..160d6bc21 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/issue/IssueRequestDTO.java @@ -0,0 +1,28 @@ +package com.team11.issue.dto.issue; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +public class IssueRequestDTO { + + private String title; + private String contents; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private List assignees; + + private Long authorId; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private List labels; + + @JsonInclude(JsonInclude.Include.NON_NULL) + private Long milestone; +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/issue/IssuesResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/issue/IssuesResponseDTO.java new file mode 100644 index 000000000..93ba8bb4f --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/issue/IssuesResponseDTO.java @@ -0,0 +1,24 @@ +package com.team11.issue.dto.issue; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +@Builder +@Getter +public class IssuesResponseDTO { + + private final List issues; + + + public static IssuesResponseDTO from(List issues) { + return IssuesResponseDTO.builder() + .issues(issues) + .build(); + } + + +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/label/LabelRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/label/LabelRequestDTO.java new file mode 100644 index 000000000..2653afff8 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/label/LabelRequestDTO.java @@ -0,0 +1,16 @@ +package com.team11.issue.dto.label; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class LabelRequestDTO { + + private String title; + private String description; + private String color; + private String bgColor; +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/label/LabelResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/label/LabelResponseDTO.java new file mode 100644 index 000000000..4a79566da --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/label/LabelResponseDTO.java @@ -0,0 +1,30 @@ +package com.team11.issue.dto.label; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.Label; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@JsonPropertyOrder({"labelId", "title", "description", "color", "bgColor"}) +@RequiredArgsConstructor +@Builder +@Getter +public class LabelResponseDTO { + + private final Long labelId; + private final String title; + private final String description; + private final String color; + private final String bgColor; + + public static LabelResponseDTO from(Label label) { + return LabelResponseDTO.builder() + .labelId(label.getId()) + .title(label.getTitle()) + .description(label.getDescription()) + .color(label.getColor()) + .bgColor(label.getBgColor()) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/label/LabelsResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/label/LabelsResponseDTO.java new file mode 100644 index 000000000..d7d306571 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/label/LabelsResponseDTO.java @@ -0,0 +1,21 @@ +package com.team11.issue.dto.label; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +@Builder +@Getter +public class LabelsResponseDTO { + + private final List labels; + + public static LabelsResponseDTO from(List labels) { + return LabelsResponseDTO.builder() + .labels(labels) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/milestone/IssueCountResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/milestone/IssueCountResponseDTO.java new file mode 100644 index 000000000..1c8f66caa --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/milestone/IssueCountResponseDTO.java @@ -0,0 +1,23 @@ +package com.team11.issue.dto.milestone; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@JsonPropertyOrder({"open", "closed"}) +@Getter +@RequiredArgsConstructor +@Builder +public class IssueCountResponseDTO { + + private final Integer open; + private final Integer closed; + + public static IssueCountResponseDTO from(int open, int closed) { + return IssueCountResponseDTO.builder() + .open(open) + .closed(closed) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneRequestDTO.java new file mode 100644 index 000000000..cdd6ae6cb --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneRequestDTO.java @@ -0,0 +1,17 @@ +package com.team11.issue.dto.milestone; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDate; + +@Getter +@Setter +@AllArgsConstructor +public class MilestoneRequestDTO { + + private String title; + private String description; + private LocalDate deadLineDate; +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneResponseDTO.java new file mode 100644 index 000000000..e61ac849c --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestoneResponseDTO.java @@ -0,0 +1,32 @@ +package com.team11.issue.dto.milestone; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.Milestone; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.time.LocalDate; + +@JsonPropertyOrder({"milestoneId", "title", "deadLineDate", "description", "issueCount"}) +@RequiredArgsConstructor +@Builder +@Getter +public class MilestoneResponseDTO { + + private final Long milestoneId; + private final String title; + private final LocalDate deadLineDate; + private final String description; + private final IssueCountResponseDTO issueCount; + + public static MilestoneResponseDTO from(Milestone milestone, IssueCountResponseDTO issueCountResponseDTO) { + return MilestoneResponseDTO.builder() + .milestoneId(milestone.getId()) + .title(milestone.getTitle()) + .deadLineDate(milestone.getDeadLineDate()) + .description(milestone.getDescription()) + .issueCount(issueCountResponseDTO) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestonesResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestonesResponseDTO.java new file mode 100644 index 000000000..e876e9e0f --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/milestone/MilestonesResponseDTO.java @@ -0,0 +1,21 @@ +package com.team11.issue.dto.milestone; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@Getter +@RequiredArgsConstructor +@Builder +public class MilestonesResponseDTO { + + private final List milestones; + + public static MilestonesResponseDTO from(List milestones) { + return MilestonesResponseDTO.builder() + .milestones(milestones) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/oauth/AccessTokenDTO.java b/BE/server/src/main/java/com/team11/issue/dto/oauth/AccessTokenDTO.java new file mode 100644 index 000000000..bab78b88e --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/oauth/AccessTokenDTO.java @@ -0,0 +1,17 @@ +package com.team11.issue.dto.oauth; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class AccessTokenDTO { + + @JsonProperty("access_token") + private String accessToken; + + @JsonProperty("token_type") + private String tokenType; + + private String scope; + +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/oauth/UserInfoDTO.java b/BE/server/src/main/java/com/team11/issue/dto/oauth/UserInfoDTO.java new file mode 100644 index 000000000..65fa77236 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/oauth/UserInfoDTO.java @@ -0,0 +1,29 @@ +package com.team11.issue.dto.oauth; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Getter; + +@Getter +public class UserInfoDTO { + + @JsonProperty("login") + private String name; + + @JsonProperty("avatar_url") + private String profileImage; + + private String email; + + public String getName() { + return name; + } + + public String getProfileImage() { + return profileImage; + } + + public String getEmail() { + return email; + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/user/LoginRequestDTO.java b/BE/server/src/main/java/com/team11/issue/dto/user/LoginRequestDTO.java new file mode 100644 index 000000000..1ea344db2 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/user/LoginRequestDTO.java @@ -0,0 +1,13 @@ +package com.team11.issue.dto.user; + +import lombok.Getter; + +@Getter +public class LoginRequestDTO { + + private String code; + + public LoginRequestDTO(String type, String code) { + this.code = code; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/user/LoginResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/user/LoginResponseDTO.java new file mode 100644 index 000000000..5df3f9253 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/user/LoginResponseDTO.java @@ -0,0 +1,31 @@ +package com.team11.issue.dto.user; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.User; +import lombok.Getter; + +@Getter +@JsonPropertyOrder({"userId", "userName", "email", "profileImage", "jwtToken"}) +public class LoginResponseDTO { + + @JsonProperty("userId") + private final Long id; + + @JsonProperty("userName") + private final String name; + + private final String email; + + private final String profileImage; + + private final String jwtToken; + + public LoginResponseDTO(User user, String jwtToken) { + this.id = user.getId(); + this.name = user.getName(); + this.email = user.getEmail(); + this.profileImage = user.getProfileImage(); + this.jwtToken = jwtToken; + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/user/UserResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/user/UserResponseDTO.java new file mode 100644 index 000000000..46f097b61 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/user/UserResponseDTO.java @@ -0,0 +1,26 @@ +package com.team11.issue.dto.user; + +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.team11.issue.domain.User; +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@JsonPropertyOrder({"userId", "userName", "profileImage"}) +@RequiredArgsConstructor +@Builder +@Getter +public class UserResponseDTO { + + private final Long userId; + private final String userName; + private final String profileImage; + + public static UserResponseDTO from(User user) { + return UserResponseDTO.builder() + .userId(user.getId()) + .userName(user.getName()) + .profileImage(user.getProfileImage()) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/dto/user/UsersResponseDTO.java b/BE/server/src/main/java/com/team11/issue/dto/user/UsersResponseDTO.java new file mode 100644 index 000000000..fc84337a1 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/dto/user/UsersResponseDTO.java @@ -0,0 +1,21 @@ +package com.team11.issue.dto.user; + +import lombok.Builder; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +@Builder +@Getter +public class UsersResponseDTO { + + private final List users; + + public static UsersResponseDTO from(List userResponseDTOS) { + return UsersResponseDTO.builder() + .users(userResponseDTOS) + .build(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/AccessTokenNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/AccessTokenNotFoundException.java new file mode 100644 index 000000000..65fc854ee --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/AccessTokenNotFoundException.java @@ -0,0 +1,8 @@ +package com.team11.issue.exception; + +public class AccessTokenNotFoundException extends RuntimeException { + + public AccessTokenNotFoundException() { + super("AccessToken이 존재하지 않습니다.code를 확인해 주세요."); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/AssigneeIllegalException.java b/BE/server/src/main/java/com/team11/issue/exception/AssigneeIllegalException.java new file mode 100644 index 000000000..6718668fc --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/AssigneeIllegalException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class AssigneeIllegalException extends RuntimeException { + + public AssigneeIllegalException() { + super("로그인한 유저는 이슈 수정/삭제 권한이 없습니다."); + } + + public AssigneeIllegalException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/CommentNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/CommentNotFoundException.java new file mode 100644 index 000000000..4624b1c39 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/CommentNotFoundException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class CommentNotFoundException extends RuntimeException { + + public CommentNotFoundException() { + super("해당하는 코멘트를 찾을 수 없습니다."); + } + + public CommentNotFoundException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/GlobalExceptionHandler.java b/BE/server/src/main/java/com/team11/issue/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..c5958b90b --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/GlobalExceptionHandler.java @@ -0,0 +1,26 @@ +package com.team11.issue.exception; + +import com.auth0.jwt.JWT; +import com.team11.issue.dto.ExceptionResponseDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + @ExceptionHandler({OauthException.class, AccessTokenNotFoundException.class, + JWTTokenException.class, UserNotFoundException.class, + MilestoneNotFoundException.class, LabelNotFoundException.class, + IssueNotFoundException.class, AssigneeIllegalException.class, + CommentNotFoundException.class, UserIllegalException.class}) + protected ResponseEntity handleException(Exception e) { + logger.error(e.getMessage()); + return new ResponseEntity(new ExceptionResponseDTO(e.getMessage()), HttpStatus.BAD_REQUEST); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/IssueNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/IssueNotFoundException.java new file mode 100644 index 000000000..c0b8fa79a --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/IssueNotFoundException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class IssueNotFoundException extends RuntimeException { + + public IssueNotFoundException() { + super("해당하는 이슈를 찾을 수 없습니다."); + } + + public IssueNotFoundException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/JWTTokenException.java b/BE/server/src/main/java/com/team11/issue/exception/JWTTokenException.java new file mode 100644 index 000000000..f7b1b3c9f --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/JWTTokenException.java @@ -0,0 +1,8 @@ +package com.team11.issue.exception; + +public class JWTTokenException extends RuntimeException { + + public JWTTokenException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/LabelNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/LabelNotFoundException.java new file mode 100644 index 000000000..584079074 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/LabelNotFoundException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class LabelNotFoundException extends RuntimeException { + + public LabelNotFoundException() { + super("해당하는 라벨을 찾을 수 없습니다."); + } + + public LabelNotFoundException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/MilestoneNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/MilestoneNotFoundException.java new file mode 100644 index 000000000..521c51b5b --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/MilestoneNotFoundException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class MilestoneNotFoundException extends RuntimeException { + + public MilestoneNotFoundException() { + super("해당하는 마일스톤을 찾을 수 없습니다."); + } + + public MilestoneNotFoundException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/OauthException.java b/BE/server/src/main/java/com/team11/issue/exception/OauthException.java new file mode 100644 index 000000000..27a27e799 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/OauthException.java @@ -0,0 +1,9 @@ +package com.team11.issue.exception; + +public class OauthException extends RuntimeException { + + public OauthException(String message) { + super(message); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/UserIllegalException.java b/BE/server/src/main/java/com/team11/issue/exception/UserIllegalException.java new file mode 100644 index 000000000..6cdcca365 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/UserIllegalException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class UserIllegalException extends RuntimeException { + + public UserIllegalException() { + super("로그인한 유저는 본 글에 대한 권한이 없습니다."); + } + + public UserIllegalException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/exception/UserNotFoundException.java b/BE/server/src/main/java/com/team11/issue/exception/UserNotFoundException.java new file mode 100644 index 000000000..23515696d --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/exception/UserNotFoundException.java @@ -0,0 +1,12 @@ +package com.team11.issue.exception; + +public class UserNotFoundException extends RuntimeException { + + public UserNotFoundException() { + super("유저를 찾을 수 없습니다."); + } + + public UserNotFoundException(String message) { + super(message); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauth.java b/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauth.java new file mode 100644 index 000000000..a615eeef2 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauth.java @@ -0,0 +1,73 @@ +package com.team11.issue.oauth; + +import com.team11.issue.dto.oauth.AccessTokenDTO; +import com.team11.issue.dto.oauth.UserInfoDTO; +import com.team11.issue.dto.user.LoginRequestDTO; +import com.team11.issue.oauth.errorHandler.RestTemplateResponseErrorHandler; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import java.util.Optional; + +@RequiredArgsConstructor +@Component +public class GitHubOauth implements Oauth { + + private static final String CLIENT_ID = "client_id"; + private static final String CLIENT_SECRET = "client_secret"; + + private static final String CODE = "code"; + private static final String TOKEN = "token"; + + private final RestTemplate restTemplate = new RestTemplateBuilder() + .errorHandler(new RestTemplateResponseErrorHandler()) + .build(); + + private final GitHubOauthUtil gitHubOauthUtil; + + + private GitHubOauthUtil getGitHubOauthInfo(String userAgent) { + String type = null; + + if (UserAgent.isIOS(userAgent)) + type = "ios"; + else { + type = "fe"; + } + + gitHubOauthUtil.setGitHubOauthInfo(type); + return gitHubOauthUtil; + } + + @Override + public Optional getAccessToken(String userAgent, LoginRequestDTO loginRequestDTO) { + + getGitHubOauthInfo(userAgent); + + MultiValueMap parameters = new LinkedMultiValueMap(); + parameters.add(CLIENT_ID, gitHubOauthUtil.getClientId()); + parameters.add(CLIENT_SECRET, gitHubOauthUtil.getClientSecret()); + parameters.add(CODE, loginRequestDTO.getCode()); + + return Optional.ofNullable(restTemplate.postForObject(gitHubOauthUtil.getAccessTokenUrl(), parameters, AccessTokenDTO.class).getAccessToken()); + } + + @Override + public UserInfoDTO getUserInfoFromGitHub(String accessToken) { + + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.set(HttpHeaders.AUTHORIZATION, TOKEN + " " + accessToken); + HttpEntity httpEntity = new HttpEntity<>(httpHeaders); + + ResponseEntity userInfoDTO = restTemplate.exchange(gitHubOauthUtil.getUserInfoUrl(), HttpMethod.GET, httpEntity, UserInfoDTO.class); + return userInfoDTO.getBody(); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauthUtil.java b/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauthUtil.java new file mode 100644 index 000000000..fe7b8b5bb --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/oauth/GitHubOauthUtil.java @@ -0,0 +1,54 @@ +package com.team11.issue.oauth; + +import com.team11.issue.exception.OauthException; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + + +@Getter +@Component +public class GitHubOauthUtil { + + private static final String FE = "fe"; + private static final String IOS = "ios"; + + @Value("${github.fe.client.id}") + private String feClientId; + + @Value("${github.fe.client.secret}") + private String feClientSecret; + + @Value("${github.ios.client.id}") + private String iOSClientId; + + @Value("${github.ios.client.secret}") + private String iOSClientSecret; + + @Value("${github.access.token.url}") + private String accessTokenUrl; + + @Value("${github.user.info.url}") + private String userInfoUrl; + + private String clientId; + private String clientSecret; + + + public void setGitHubOauthInfo(String type) { + if (type == null) { + throw new OauthException("잘못된 헤더의 요청을 보내셨습니다."); + } + + if (type.equals(FE)) { + this.clientId = feClientId; + this.clientSecret = feClientSecret; + } + + if (type.equals(IOS)) { + this.clientId = iOSClientId; + this.clientSecret = iOSClientSecret; + } + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/oauth/Oauth.java b/BE/server/src/main/java/com/team11/issue/oauth/Oauth.java new file mode 100644 index 000000000..2646667d8 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/oauth/Oauth.java @@ -0,0 +1,14 @@ +package com.team11.issue.oauth; + +import com.team11.issue.dto.oauth.UserInfoDTO; +import com.team11.issue.dto.user.LoginRequestDTO; + +import java.util.Optional; + +public interface Oauth { + + Optional getAccessToken(String userAgent, LoginRequestDTO loginRequestDTO); + + UserInfoDTO getUserInfoFromGitHub(String accessToken); + +} diff --git a/BE/server/src/main/java/com/team11/issue/oauth/UserAgent.java b/BE/server/src/main/java/com/team11/issue/oauth/UserAgent.java new file mode 100644 index 000000000..37f5d4fe8 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/oauth/UserAgent.java @@ -0,0 +1,23 @@ +package com.team11.issue.oauth; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +enum UserAgent { + + FRONT("IssueTrackerFE"), + IOS("IssueTrackerIOS"); + + private final String userAgent; + + public static boolean isFront(String userAgent) { + return FRONT.userAgent.equals(userAgent); + } + + public static boolean isIOS(String userAgent) { + return IOS.userAgent.equals(userAgent); + } + +} diff --git a/BE/server/src/main/java/com/team11/issue/oauth/errorHandler/RestTemplateResponseErrorHandler.java b/BE/server/src/main/java/com/team11/issue/oauth/errorHandler/RestTemplateResponseErrorHandler.java new file mode 100644 index 000000000..f6a4f9948 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/oauth/errorHandler/RestTemplateResponseErrorHandler.java @@ -0,0 +1,25 @@ +package com.team11.issue.oauth.errorHandler; + +import com.team11.issue.exception.OauthException; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.client.ResponseErrorHandler; + +import java.io.IOException; + +import static org.springframework.http.HttpStatus.Series.CLIENT_ERROR; +import static org.springframework.http.HttpStatus.Series.SERVER_ERROR; + +@Component +public class RestTemplateResponseErrorHandler implements ResponseErrorHandler { + + @Override + public boolean hasError(ClientHttpResponse httpResponse) throws IOException { + return (httpResponse.getStatusCode().series() == CLIENT_ERROR || httpResponse.getStatusCode().series() == SERVER_ERROR); + } + + @Override + public void handleError(ClientHttpResponse httpResponse) { + throw new OauthException("Oauth에 문제가 발생했습니다"); + } +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/AssigneeRepository.java b/BE/server/src/main/java/com/team11/issue/repository/AssigneeRepository.java new file mode 100644 index 000000000..4da4ec943 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/AssigneeRepository.java @@ -0,0 +1,15 @@ +package com.team11.issue.repository; + +import com.team11.issue.domain.Assignees; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AssigneeRepository extends CrudRepository { + + void deleteAllByIssueId(Long issueId); + + List findAllByIssueId(Long issueId); +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/CommentRepository.java b/BE/server/src/main/java/com/team11/issue/repository/CommentRepository.java new file mode 100644 index 000000000..eff32ef99 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/CommentRepository.java @@ -0,0 +1,12 @@ +package com.team11.issue.repository; + +import com.team11.issue.domain.Comment; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface CommentRepository extends CrudRepository { + List findAllByIssueId(Long issueId); +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/HistoryRepository.java b/BE/server/src/main/java/com/team11/issue/repository/HistoryRepository.java new file mode 100644 index 000000000..7421eb7f3 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/HistoryRepository.java @@ -0,0 +1,11 @@ +package com.team11.issue.repository; + +import com.team11.issue.domain.History; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface HistoryRepository extends CrudRepository { + History findFirstByIssueIdOrderByHistoryDateTimeDesc(Long issueId); + +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/IssueHasLabelRepository.java b/BE/server/src/main/java/com/team11/issue/repository/IssueHasLabelRepository.java new file mode 100644 index 000000000..af9cf32b7 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/IssueHasLabelRepository.java @@ -0,0 +1,17 @@ +package com.team11.issue.repository; + +import com.team11.issue.domain.IssueHasLabel; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface IssueHasLabelRepository extends CrudRepository { + @Override + Iterable saveAll(Iterable entities); + + void deleteAllByIssueId(Long issueId); + + List findAllByIssueId(Long issueId); +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/IssueRepository.java b/BE/server/src/main/java/com/team11/issue/repository/IssueRepository.java new file mode 100644 index 000000000..e8f9d0dd0 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/IssueRepository.java @@ -0,0 +1,16 @@ +package com.team11.issue.repository; + + +import com.team11.issue.domain.Issue; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface IssueRepository extends CrudRepository { + + List findAllByIsDeleteFalse(); + + int countByMilestoneIdAndIsOpen(Long milestoneId, boolean isOpen); +} diff --git a/BE/server/src/main/java/com/team11/issue/repository/LabelRepository.java b/BE/server/src/main/java/com/team11/issue/repository/LabelRepository.java new file mode 100644 index 000000000..a20b4c0f5 --- /dev/null +++ b/BE/server/src/main/java/com/team11/issue/repository/LabelRepository.java @@ -0,0 +1,14 @@ +package com.team11.issue.repository; + +import com.team11.issue.domain.Label; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface LabelRepository extends CrudRepository { + + @Override + List