diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 000000000..cb6e41692
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,111 @@
+name: Java CI with Gradle
+
+on:
+ push:
+ branches: release
+
+env:
+ APPLICATION_YML_FILE_PATH: ./src/main/resources/application.yml
+ AWS_REGION: ap-northeast-2
+ S3_BUCKET_NAME: issue-tracker-s3-bucket
+ CODE_DEPLOY_APPLICATION_NAME: issue-tracker-codedeploy-app
+ CODE_DEPLOY_DEPLOYMENT_GROUP_NAME: issuetracker-codedeploy-deployment-group
+
+permissions:
+ contents: read
+
+jobs:
+ app-build-and-deploy:
+ name: BE Deploy
+ runs-on: ubuntu-latest
+ environment: production
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./be/issue
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+
+ - name: Output application information
+ run: echo '${{ secrets.APPLICATION }}' > '${{ env.APPLICATION_YML_FILE_PATH }}'
+
+ - name: Build with Gradle
+ run: |
+ chmod +x gradlew
+ ./gradlew clean build -x test
+
+ - name: Make zip file
+ run: zip -r ./$GITHUB_SHA.zip .
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v2
+ with:
+ aws-access-key-id: ${{secrets.AWS_ACCESS_KEY_ID}}
+ aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
+ aws-region: ${{env.AWS_REGION}}
+
+ - name: Upload to S3
+ run: aws s3 cp
+ --region '${{ env.AWS_REGION }}' ./$GITHUB_SHA.zip
+ s3://$S3_BUCKET_NAME/Build/$GITHUB_SHA.zip
+
+ - name: Code Deploy
+ run: aws deploy create-deployment
+ --application-name $CODE_DEPLOY_APPLICATION_NAME
+ --deployment-config-name CodeDeployDefault.AllAtOnce
+ --deployment-group-name $CODE_DEPLOY_DEPLOYMENT_GROUP_NAME
+ --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=Build/$GITHUB_SHA.zip
+
+ web-build-and-deploy:
+ name: FE Deploy
+ needs: app-build-and-deploy
+ runs-on: ubuntu-latest
+ environment: production
+ defaults:
+ run:
+ shell: bash
+ working-directory: ./frontend
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: '18'
+
+ - name: Build with npm
+ run: |
+ npm install
+ npm run build
+
+ - name: Make zip file
+ run: zip -r ./react-build.zip .
+
+ - name: Configure AWS credentials
+ uses: aws-actions/configure-aws-credentials@v2
+ with:
+ aws-access-key-id: ${{secrets.AWS_ACCESS_KEY_ID}}
+ aws-secret-access-key: ${{secrets.AWS_SECRET_ACCESS_KEY}}
+ aws-region: ${{env.AWS_REGION}}
+
+ - name: Upload to S3
+ run: aws s3 cp
+ --region ${{ env.AWS_REGION }} ./react-build.zip
+ s3://$S3_BUCKET_NAME/Build/react-build.zip
+
+ - name: Code Deploy
+ run: aws deploy create-deployment
+ --application-name $CODE_DEPLOY_APPLICATION_NAME
+ --deployment-config-name CodeDeployDefault.AllAtOnce
+ --deployment-group-name $CODE_DEPLOY_DEPLOYMENT_GROUP_NAME
+ --s3-location bucket=$S3_BUCKET_NAME,bundleType=zip,key=Build/react-build.zip
diff --git a/.gitignore b/.gitignore
index 06b59439a..d3d78dc05 100644
--- a/.gitignore
+++ b/.gitignore
@@ -141,4 +141,4 @@ dist
/.idea/compiler.xml
/.idea/codeStyles/codeStyleConfig.xml
/.idea/checkstyle-idea.xml
-*.yml
\ No newline at end of file
+/.idea/
diff --git a/README.md b/README.md
index 092fa8da9..0eae1025d 100644
--- a/README.md
+++ b/README.md
@@ -52,3 +52,7 @@
### BE
+
+## ERD
+![image](https://github.com/codesquad-issue-team-05/issue-tracker-max/assets/103398897/0ffd58d6-e2b2-4786-a916-927623f670f8)
+
diff --git a/be/issue/.gitignore b/be/issue/.gitignore
index c2065bc26..c489d2899 100644
--- a/be/issue/.gitignore
+++ b/be/issue/.gitignore
@@ -35,3 +35,5 @@ out/
### VS Code ###
.vscode/
+
+
diff --git a/be/issue/appspec.yml b/be/issue/appspec.yml
new file mode 100644
index 000000000..9e24388bf
--- /dev/null
+++ b/be/issue/appspec.yml
@@ -0,0 +1,19 @@
+version: 0.0
+os: linux
+
+files:
+ - source: /
+ destination: /home/ubuntu/app
+ overwrite: yes
+
+permissions:
+ - object: /
+ pattern: "**"
+ owner: ubuntu
+ group: ubuntu
+
+hooks:
+ AfterInstall:
+ - location: deploy-app.sh
+ timeout: 60
+ runas: ubuntu
diff --git a/be/issue/build.gradle b/be/issue/build.gradle
index e5b28f2d5..0c9bdcb6f 100644
--- a/be/issue/build.gradle
+++ b/be/issue/build.gradle
@@ -23,7 +23,6 @@ repositories {
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
-// implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
compileOnly 'org.projectlombok:lombok'
@@ -39,7 +38,6 @@ dependencies {
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
// 패스워드 암호화
implementation group: 'org.mindrot', name: 'jbcrypt', version: '0.4'
-
implementation 'org.springframework.boot:spring-boot-starter-webflux'
}
diff --git a/be/issue/deploy-app.sh b/be/issue/deploy-app.sh
new file mode 100644
index 000000000..0d14bcc7f
--- /dev/null
+++ b/be/issue/deploy-app.sh
@@ -0,0 +1,12 @@
+ISSUE_ID=$(jps | grep be | awk '{ print $1 }')
+
+if [ -z $ISSUE_ID ]; then
+ echo "동작중인 서버가 없습니다."
+else
+ echo "$ISSUE_ID 프로세스를 삭제합니다."
+ kill -9 $ISSUE_ID
+fi
+
+echo "서버 시작"
+nohup java -jar ~/app/build/libs/issue-0.0.1-SNAPSHOT.jar >/home/ubuntu/app/log.txt 2>&1 &
+echo "배포 성공"
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/controller/CommentController.java b/be/issue/src/main/java/codesquad/issueTracker/comment/controller/CommentController.java
index b15037e9c..af38e5d66 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/comment/controller/CommentController.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/controller/CommentController.java
@@ -1,12 +1,59 @@
package codesquad.issueTracker.comment.controller;
+import static codesquad.issueTracker.global.exception.SuccessCode.*;
+
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import codesquad.issueTracker.comment.dto.CommentRequestDto;
+import codesquad.issueTracker.comment.dto.CommentResponseDto;
import codesquad.issueTracker.comment.service.CommentService;
+import codesquad.issueTracker.global.common.ApiResponse;
import lombok.RequiredArgsConstructor;
-@RequiredArgsConstructor
@RestController
+@RequiredArgsConstructor
+@RequestMapping("/api")
public class CommentController {
+
private final CommentService commentService;
+
+ @GetMapping("/issues/{issueId}/comments")
+ public ApiResponse> getComments(@PathVariable Long issueId) {
+ List commentResponseDtos = commentService.getComments(issueId);
+
+ return ApiResponse.success(SUCCESS.getStatus(), commentResponseDtos);
+ }
+
+ @PostMapping("/issues/{issueId}/comments")
+ public ApiResponse save(@PathVariable Long issueId,
+ @RequestBody CommentRequestDto commentRequestDto,
+ HttpServletRequest request) {
+ commentService.save(request, issueId, commentRequestDto);
+
+ return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
+ }
+
+ @PatchMapping("/issues/comments/{commentId}")
+ public ApiResponse modify(@PathVariable Long commentId,
+ @RequestBody CommentRequestDto commentRequestDto) {
+ commentService.modify(commentId, commentRequestDto);
+ return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
+ }
+
+ @DeleteMapping("issues/comments/{commentId}")
+ public ApiResponse delete(@PathVariable Long commentId) {
+ commentService.delete(commentId);
+ return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/domain/Comment.java b/be/issue/src/main/java/codesquad/issueTracker/comment/domain/Comment.java
index 3d0abcf28..729709de9 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/comment/domain/Comment.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/domain/Comment.java
@@ -1,26 +1,23 @@
package codesquad.issueTracker.comment.domain;
import java.time.LocalDateTime;
-
import lombok.Builder;
import lombok.Getter;
@Getter
public class Comment {
+ private Long id;
+ private Long userId;
+ private Long issueId;
+ private String content;
+ private LocalDateTime createdAt;
- private Long id;
- private Long userId;
- private Long issueId;
- private String content;
- private LocalDateTime createdAt;
-
- @Builder
-
- public Comment(Long id, Long userId, Long issueId, String content, LocalDateTime createdAt) {
- this.id = id;
- this.userId = userId;
- this.issueId = issueId;
- this.content = content;
- this.createdAt = createdAt;
- }
+ @Builder
+ public Comment(Long id, Long userId, Long issueId, String content, LocalDateTime createdAt) {
+ this.id = id;
+ this.userId = userId;
+ this.issueId = issueId;
+ this.content = content;
+ this.createdAt = createdAt;
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentRequestDto.java b/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentRequestDto.java
new file mode 100644
index 000000000..3f2e07b19
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentRequestDto.java
@@ -0,0 +1,14 @@
+package codesquad.issueTracker.comment.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class CommentRequestDto {
+
+ private String content;
+
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentResponseDto.java b/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentResponseDto.java
new file mode 100644
index 000000000..e4ed8e023
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/dto/CommentResponseDto.java
@@ -0,0 +1,24 @@
+package codesquad.issueTracker.comment.dto;
+
+import java.time.LocalDateTime;
+
+import codesquad.issueTracker.comment.vo.CommentUserVo;
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+public class CommentResponseDto {
+
+ private Long id;
+ private LocalDateTime createdAt;
+ private String content;
+ private CommentUserVo writer;
+
+ @Builder
+ public CommentResponseDto(Long id, LocalDateTime createdAt, String content, CommentUserVo writer) {
+ this.id = id;
+ this.createdAt = createdAt;
+ this.content = content;
+ this.writer = writer;
+ }
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/repository/CommentRepository.java b/be/issue/src/main/java/codesquad/issueTracker/comment/repository/CommentRepository.java
index 6f9356a2d..07cedf273 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/comment/repository/CommentRepository.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/repository/CommentRepository.java
@@ -1,13 +1,113 @@
package codesquad.issueTracker.comment.repository;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.springframework.dao.support.DataAccessUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
+import codesquad.issueTracker.comment.domain.Comment;
+import codesquad.issueTracker.comment.dto.CommentRequestDto;
+import codesquad.issueTracker.comment.dto.CommentResponseDto;
+import codesquad.issueTracker.comment.vo.CommentUserVo;
+
@Repository
public class CommentRepository {
+
private final NamedParameterJdbcTemplate jdbcTemplate;
- public CommentRepository(NamedParameterJdbcTemplate jdbcTemplate) {
- this.jdbcTemplate = jdbcTemplate;
+ public CommentRepository(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+ }
+
+ public List findByIssueId(Long issueId) {
+ String sql = "SELECT c.*, u.name, u.profile_img "
+ + "FROM comments c "
+ + "JOIN users u ON c.user_id = u.id "
+ + "WHERE c.issue_id = :issueId AND c.is_deleted = false";
+ return jdbcTemplate.query(sql, Map.of("issueId", issueId), commentResponseDtoRowMapper);
+ }
+
+ private final RowMapper commentResponseDtoRowMapper = ((rs, rowNum) -> {
+ CommentUserVo writer = CommentUserVo.builder()
+ .name(rs.getString("name"))
+ .profileImg(rs.getString("profile_img"))
+ .build();
+
+ return CommentResponseDto.builder()
+ .id(rs.getLong("id"))
+ .writer(writer)
+ .content(rs.getString("content"))
+ .createdAt(rs.getTimestamp("created_At").toLocalDateTime())
+ .build();
+ });
+
+ public Optional create(Long userId, Long issueId, CommentRequestDto commentRequestDto) {
+ String sql = "INSERT INTO comments (user_id, issue_id, content) VALUES (:userId, :issueId, :content)";
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("userId", userId);
+ params.addValue("issueId", issueId);
+ params.addValue("content", commentRequestDto.getContent());
+
+ int updatedRow = jdbcTemplate.update(sql, params, keyHolder);
+
+ if (updatedRow > 0) {
+ return Optional.ofNullable(keyHolder.getKey().longValue());
+ }
+ return Optional.empty();
}
+
+ public Optional update(Long commentId, CommentRequestDto commentRequestDto) {
+ String sql = "UPDATE comments SET content = :content WHERE id = :commentId";
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("content", commentRequestDto.getContent());
+ params.addValue("commentId", commentId);
+
+ int updatedRow = jdbcTemplate.update(sql, params);
+
+ if (updatedRow > 0) {
+ return Optional.ofNullable(commentId);
+ }
+ return Optional.empty();
+ }
+
+ public Optional deleteById(Long commentId) {
+ String sql = "UPDATE comments SET is_deleted = true WHERE id = :commentId";
+ int updatedRow = jdbcTemplate.update(sql, Map.of("commentId", commentId));
+
+ if (updatedRow > 0) {
+ return Optional.ofNullable(commentId);
+ }
+ return Optional.empty();
+ }
+
+ public Optional findById(Long commentId) {
+ String sql = "SELECT * FROM comments WHERE id = :commentId";
+ return Optional.ofNullable(
+ DataAccessUtils.singleResult(
+ jdbcTemplate.query(sql, Map.of("commentId", commentId), commentRowMapper)));
+ }
+
+ public Optional findExistCommentById(Long commentId) {
+ String sql = "SELECT * FROM comments WHERE id = :commentId AND is_deleted = false";
+ return Optional.ofNullable(
+ DataAccessUtils.singleResult(
+ jdbcTemplate.query(sql, Map.of("commentId", commentId), commentRowMapper)));
+ }
+
+ private final RowMapper commentRowMapper = ((rs, rowNum) -> Comment.builder()
+ .id(rs.getLong("id"))
+ .userId(rs.getLong("user_id"))
+ .issueId(rs.getLong("issue_id"))
+ .content(rs.getString("content"))
+ .createdAt(rs.getTimestamp("created_At").toLocalDateTime())
+ .build());
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/service/CommentService.java b/be/issue/src/main/java/codesquad/issueTracker/comment/service/CommentService.java
index ac38d13ba..b666b7ca9 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/comment/service/CommentService.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/service/CommentService.java
@@ -1,12 +1,57 @@
package codesquad.issueTracker.comment.service;
-import org.springframework.stereotype.Service;
-
+import codesquad.issueTracker.comment.domain.Comment;
+import codesquad.issueTracker.comment.dto.CommentRequestDto;
+import codesquad.issueTracker.comment.dto.CommentResponseDto;
import codesquad.issueTracker.comment.repository.CommentRepository;
+import codesquad.issueTracker.global.exception.CustomException;
+import codesquad.issueTracker.global.exception.ErrorCode;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
-@RequiredArgsConstructor
@Service
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
public class CommentService {
- private final CommentRepository commentRepository;
+ private final CommentRepository commentRepository;
+
+ public List getComments(Long issueId) {
+ return commentRepository.findByIssueId(issueId);
+ }
+
+ @Transactional
+ public Long save(HttpServletRequest request, Long issueId, CommentRequestDto commentRequestDto) {
+ Long userId = Long.parseLong(String.valueOf(request.getAttribute("userId")));
+ return commentRepository.create(userId, issueId, commentRequestDto)
+ .orElseThrow(() -> new CustomException(ErrorCode.DB_EXCEPTION));
+ }
+
+ @Transactional
+ public Long modify(Long commentId, CommentRequestDto commentRequestDto) {
+ validateExistComment(commentId);
+ validateCommentStatus(commentId);
+ return commentRepository.update(commentId, commentRequestDto)
+ .orElseThrow(() -> new CustomException(ErrorCode.DB_EXCEPTION));
+ }
+
+ @Transactional
+ public Long delete(Long commentId) {
+ validateExistComment(commentId);
+ validateCommentStatus(commentId);
+ return commentRepository.deleteById(commentId)
+ .orElseThrow(() -> new CustomException(ErrorCode.DB_EXCEPTION));
+ }
+
+ private Comment validateExistComment(Long commentId) {
+ return commentRepository.findById(commentId)
+ .orElseThrow(() -> new CustomException(ErrorCode.NOT_EXIST_COMMENT));
+ }
+
+ private Comment validateCommentStatus(Long commentId) {
+ return commentRepository.findExistCommentById(commentId)
+ .orElseThrow(() -> new CustomException(ErrorCode.ALREADY_DELETED_COMMENT));
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/comment/vo/CommentUserVo.java b/be/issue/src/main/java/codesquad/issueTracker/comment/vo/CommentUserVo.java
new file mode 100644
index 000000000..735d91609
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/comment/vo/CommentUserVo.java
@@ -0,0 +1,16 @@
+package codesquad.issueTracker.comment.vo;
+
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+public class CommentUserVo {
+ private String name;
+ private String profileImg;
+
+ @Builder
+ public CommentUserVo(String name, String profileImg) {
+ this.name = name;
+ this.profileImg = profileImg;
+ }
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/ApiResponse.java b/be/issue/src/main/java/codesquad/issueTracker/global/ApiResponse.java
deleted file mode 100644
index 900655bf6..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/global/ApiResponse.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package codesquad.issueTracker.global;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.HttpStatus;
-
-@Getter
-@RequiredArgsConstructor
-public class ApiResponse {
-
- private final HttpStatus status;
- private final T message;
-
- public static ApiResponse success(HttpStatus status, T message) {
- return new ApiResponse<>(status, message);
- }
-
- public static ApiResponse fail(HttpStatus status, T message) {
- return new ApiResponse<>(status, message);
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/common/ApiResponse.java b/be/issue/src/main/java/codesquad/issueTracker/global/common/ApiResponse.java
new file mode 100644
index 000000000..06ba97a3f
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/common/ApiResponse.java
@@ -0,0 +1,22 @@
+package codesquad.issueTracker.global.common;
+
+import org.springframework.http.HttpStatus;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public class ApiResponse {
+
+ private final HttpStatus status;
+ private final T message;
+
+ public static ApiResponse success(HttpStatus status, T message) {
+ return new ApiResponse<>(status, message);
+ }
+
+ public static ApiResponse fail(HttpStatus status, T message) {
+ return new ApiResponse<>(status, message);
+ }
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/common/Status.java b/be/issue/src/main/java/codesquad/issueTracker/global/common/Status.java
new file mode 100644
index 000000000..2a0c8a69f
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/common/Status.java
@@ -0,0 +1,31 @@
+package codesquad.issueTracker.global.common;
+
+import codesquad.issueTracker.global.exception.CustomException;
+import codesquad.issueTracker.global.exception.ErrorCode;
+
+public enum Status {
+ OPEN("open", false),
+ CLOSED("closed", true);
+
+ private final String type;
+ private final boolean value;
+
+ Status(String type, boolean value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public boolean getStatus() {
+ return value;
+ }
+
+ public static Status from(String type) {
+ for (Status status : Status.values()) {
+ if (status.type.equalsIgnoreCase(type)) {
+ return status;
+ }
+ }
+ throw new CustomException(ErrorCode.ILLEGAL_STATUS_MILESTONE);
+ }
+
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/config/CorsConfig.java b/be/issue/src/main/java/codesquad/issueTracker/global/config/CorsConfig.java
new file mode 100644
index 000000000..5033d270e
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/config/CorsConfig.java
@@ -0,0 +1,17 @@
+package codesquad.issueTracker.global.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class CorsConfig implements WebMvcConfigurer {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/api/**")
+ .allowedOrigins("*")
+ .allowedMethods(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.HEAD.name(),
+ HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.OPTIONS.name());
+ }
+}
\ No newline at end of file
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/config/OauthConfig.java b/be/issue/src/main/java/codesquad/issueTracker/global/config/OauthConfig.java
index 2414a1361..f373a27c2 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/global/config/OauthConfig.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/config/OauthConfig.java
@@ -1,25 +1,27 @@
package codesquad.issueTracker.global.config;
-import codesquad.issueTracker.jwt.domain.OAuthProperties;
-import codesquad.issueTracker.jwt.util.InMemoryProviderRepository;
-import codesquad.issueTracker.jwt.util.OauthAdapter;
-import codesquad.issueTracker.jwt.util.OauthProvider;
import java.util.Map;
-import lombok.RequiredArgsConstructor;
+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import codesquad.issueTracker.jwt.util.InMemoryProviderRepository;
+import codesquad.issueTracker.oauth.domain.OAuthProperties;
+import codesquad.issueTracker.oauth.util.OauthAdapter;
+import codesquad.issueTracker.oauth.util.OauthProvider;
+import lombok.RequiredArgsConstructor;
+
@Configuration
@EnableConfigurationProperties(OAuthProperties.class)
@RequiredArgsConstructor
public class OauthConfig {
- private final OAuthProperties properties;
+ private final OAuthProperties properties;
- @Bean
- public InMemoryProviderRepository inMemoryProviderRepository() {
- Map providers = OauthAdapter.getOauthProviders(properties);
- return new InMemoryProviderRepository(providers);
- }
+ @Bean
+ public InMemoryProviderRepository inMemoryProviderRepository() {
+ Map providers = OauthAdapter.getOauthProviders(properties);
+ return new InMemoryProviderRepository(providers);
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/exception/ErrorCode.java b/be/issue/src/main/java/codesquad/issueTracker/global/exception/ErrorCode.java
index 1789da84f..72b1b3940 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/global/exception/ErrorCode.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/exception/ErrorCode.java
@@ -1,5 +1,7 @@
package codesquad.issueTracker.global.exception;
+import java.time.DateTimeException;
+
import org.springframework.http.HttpStatus;
import io.jsonwebtoken.ExpiredJwtException;
@@ -11,13 +13,15 @@ public enum ErrorCode implements StatusCode {
REQUEST_VALIDATION_FAIL(HttpStatus.BAD_REQUEST),
+ // -- [Common] -- ]
+ DB_EXCEPTION(HttpStatus.SERVICE_UNAVAILABLE, "DB 서버 오류"),
+
// -- [OAuth] -- //
NOT_SUPPORTED_PROVIDER(HttpStatus.BAD_REQUEST, "지원하지 않는 로그인 방식입니다."),
GITHUB_LOGIN_USER(HttpStatus.BAD_REQUEST, "이미 깃허브로 로그인한 유저입니다"),
-
// -- [JWT] -- //
- NOT_FOUND_REFRESH_TOKEN(HttpStatus.BAD_REQUEST,"해당하는 리프레시 토큰을 찾을 수 없습니다."),
+ NOT_FOUND_REFRESH_TOKEN(HttpStatus.BAD_REQUEST, "해당하는 리프레시 토큰을 찾을 수 없습니다."),
MALFORMED_JWT_EXCEPTION(HttpStatus.UNAUTHORIZED, "잘못된 형식의 토큰입니다."),
EXPIRED_JWT_EXCEPTION(HttpStatus.UNAUTHORIZED, "만료된 토큰입니다."),
SIGNATURE_EXCEPTION(HttpStatus.UNAUTHORIZED, "JWT의 서명이 올바르지 않습니다."),
@@ -28,7 +32,26 @@ public enum ErrorCode implements StatusCode {
ALREADY_EXIST_USER(HttpStatus.BAD_REQUEST, "이미 존재하는 유저입니다."),
NOT_FOUND_USER(HttpStatus.BAD_REQUEST, "해당하는 유저가 없습니다."),
FAILED_LOGIN_USER(HttpStatus.BAD_REQUEST, "로그인에 실패했습니다. 아이디, 비밀번호를 다시 입력해주세요. "),
- DELETE_FAIL(HttpStatus.SERVICE_UNAVAILABLE, "DB 서버 오류");
+
+ // -- [Comment] -- //
+ NOT_EXIST_COMMENT(HttpStatus.BAD_REQUEST, "존재하지 않는 댓글입니다."),
+ ALREADY_DELETED_COMMENT(HttpStatus.BAD_REQUEST, "이미 삭제된 댓글입니다."),
+
+ // -- [Milestone] -- //
+ INVALIDATE_DATE(HttpStatus.BAD_REQUEST, "현재 날짜보다 이전 날짜 입니다."),
+ NOT_FOUND_DATE(HttpStatus.BAD_REQUEST, "유효하지 않은 날짜 입니다."),
+ NOT_FOUND_MILESTONE(HttpStatus.BAD_REQUEST, "마일스톤을 찾을 수 없습니다."),
+ ILLEGAL_STATUS_MILESTONE(HttpStatus.BAD_REQUEST, "올바르지 않은 상태 입력 입니다."),
+
+ // -- [Label] -- //
+
+ LABEL_INSERT_FAILED(HttpStatus.BAD_REQUEST, "DB에서 라벨 생성에 실패했습니다."),
+ LABEL_UPDATE_FAILED(HttpStatus.BAD_REQUEST, "DB에서 라벨 수정에 실패했습니다."),
+ LABEL_DELETE_FAILED(HttpStatus.BAD_REQUEST, "DB에서 라벨 삭제에 실패했습니다"),
+ LABEL_FIND_FAILED(HttpStatus.BAD_REQUEST, "서버 오류로 라벨을 조회할 수 없습니다");
+
+
+
private HttpStatus status;
private String message;
@@ -65,6 +88,9 @@ public static StatusCode from(RuntimeException e) {
if (e instanceof UnsupportedJwtException) {
return ErrorCode.UNSUPPORTED_JWT_EXCEPTION;
}
+ if (e instanceof DateTimeException) {
+ return ErrorCode.NOT_FOUND_DATE;
+ }
return ErrorCode.ILLEGAL_ARGUMENT_EXCEPTION;
}
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/exception/GlobalExceptionHandler.java b/be/issue/src/main/java/codesquad/issueTracker/global/exception/GlobalExceptionHandler.java
index 59be27b22..dc49b3445 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/global/exception/GlobalExceptionHandler.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/exception/GlobalExceptionHandler.java
@@ -1,5 +1,6 @@
package codesquad.issueTracker.global.exception;
+import java.time.DateTimeException;
import java.util.List;
import org.springframework.http.ResponseEntity;
@@ -8,7 +9,7 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
-import codesquad.issueTracker.global.ApiResponse;
+import codesquad.issueTracker.global.common.ApiResponse;
import io.jsonwebtoken.ExpiredJwtException;
@RestControllerAdvice
@@ -39,10 +40,9 @@ public ResponseEntity> handleMethodArgumentNotValidException
.body(ApiResponse.fail(statusCode.getStatus(), errorMessage.toString()));
}
- @ExceptionHandler(ExpiredJwtException.class)
- public ResponseEntity> handleExpiredJwtException(ExpiredJwtException e) {
+ @ExceptionHandler(DateTimeException.class)
+ public ResponseEntity> handleDateTimeException(DateTimeException e) {
StatusCode statusCode = ErrorCode.from(e);
-
return ResponseEntity.status(statusCode.getStatus())
.body(ApiResponse.fail(statusCode.getStatus(), statusCode.getMessage()));
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/global/filter/AuthFilter.java b/be/issue/src/main/java/codesquad/issueTracker/global/filter/AuthFilter.java
index 0e8db0097..757b055a4 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/global/filter/AuthFilter.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/global/filter/AuthFilter.java
@@ -16,7 +16,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
-import codesquad.issueTracker.global.ApiResponse;
+import codesquad.issueTracker.global.common.ApiResponse;
import codesquad.issueTracker.global.exception.ErrorCode;
import codesquad.issueTracker.global.exception.StatusCode;
import codesquad.issueTracker.jwt.util.JwtProvider;
@@ -42,9 +42,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response;
- // if (httpServletRequest.getMethod().equals("GET")) {
- // return;
- // }
if (whiteListCheck(httpServletRequest.getRequestURI())) {
chain.doFilter(request, response);
return;
diff --git a/be/issue/src/main/java/codesquad/issueTracker/issue/controller/IssueController.java b/be/issue/src/main/java/codesquad/issueTracker/issue/controller/IssueController.java
deleted file mode 100644
index 515ec4042..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/issue/controller/IssueController.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package codesquad.issueTracker.issue.controller;
-
-import org.springframework.web.bind.annotation.RestController;
-
-import codesquad.issueTracker.issue.service.IssueService;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor
-@RestController
-public class IssueController {
- private final IssueService issueService;
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/issue/domain/Issue.java b/be/issue/src/main/java/codesquad/issueTracker/issue/domain/Issue.java
deleted file mode 100644
index 5fe40dc55..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/issue/domain/Issue.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package codesquad.issueTracker.issue.domain;
-
-import java.time.LocalDateTime;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class Issue {
-
- private Long id;
- private Long milestoneId;
- private Long userId;
- private String title;
- private String content;
- private LocalDateTime createdAt;
- private Boolean isClosed;
-
- @Builder
- public Issue(Long id, Long milestoneId, Long userId, String title, String content, LocalDateTime createdAt,
- Boolean isClosed) {
- this.id = id;
- this.milestoneId = milestoneId;
- this.userId = userId;
- this.title = title;
- this.content = content;
- this.createdAt = createdAt;
- this.isClosed = isClosed;
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/issue/repository/IssueRepository.java b/be/issue/src/main/java/codesquad/issueTracker/issue/repository/IssueRepository.java
deleted file mode 100644
index 50d6559c5..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/issue/repository/IssueRepository.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package codesquad.issueTracker.issue.repository;
-
-import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
-import org.springframework.stereotype.Repository;
-
-@Repository
-public class IssueRepository {
- private final NamedParameterJdbcTemplate jdbcTemplate;
-
- public IssueRepository(NamedParameterJdbcTemplate jdbcTemplate) {
- this.jdbcTemplate = jdbcTemplate;
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/issue/service/IssueService.java b/be/issue/src/main/java/codesquad/issueTracker/issue/service/IssueService.java
deleted file mode 100644
index fb175b54c..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/issue/service/IssueService.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package codesquad.issueTracker.issue.service;
-
-import org.springframework.stereotype.Service;
-
-import codesquad.issueTracker.issue.repository.IssueRepository;
-import lombok.RequiredArgsConstructor;
-
-@RequiredArgsConstructor
-@Service
-public class IssueService {
- private final IssueRepository issueRepository;
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OAuthProperties.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OAuthProperties.java
deleted file mode 100644
index 79b68cf2e..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OAuthProperties.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package codesquad.issueTracker.jwt.domain;
-
-
-import java.util.HashMap;
-import java.util.Map;
-import lombok.Getter;
-import lombok.Setter;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-
-@Getter
-@ConfigurationProperties(prefix = "oauth2")
-public class OAuthProperties {
-
- private final Map client = new HashMap<>();
- private final Map provider = new HashMap();
-
- @Getter
- @Setter
- public static class Client {
- private String clientId;
- private String clientSecret;
- private String redirectUrl;
- }
-
- @Getter
- @Setter
- public static class Provider {
- private String tokenUrl;
- private String userInfoUrl;
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OauthAttributes.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OauthAttributes.java
deleted file mode 100644
index 798b52673..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/jwt/domain/OauthAttributes.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package codesquad.issueTracker.jwt.domain;
-
-import codesquad.issueTracker.global.exception.CustomException;
-import codesquad.issueTracker.global.exception.ErrorCode;
-import java.util.Arrays;
-import java.util.Map;
-
-public enum OauthAttributes {
- GITHUB("github") {
- @Override
- public UserProfile of(Map attributes) {
- return UserProfile.builder()
- .email((String) attributes.get("email"))
- .name((String) attributes.get("login"))
- .imageUrl((String) attributes.get("avatar_url"))
- .build();
- }
- };
-
- private final String providerName;
-
- OauthAttributes(String name) {
- this.providerName = name;
- }
-
- public static UserProfile extract(String providerName, Map attributes) {
- return Arrays.stream(values())
- .filter(provider -> providerName.equals(provider.providerName))
- .findFirst()
- .orElseThrow(() -> new CustomException(ErrorCode.NOT_SUPPORTED_PROVIDER))
- .of(attributes);
- }
-
- public abstract UserProfile of(Map attributes);
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/dto/ResponseAccessToken.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/dto/ResponseAccessToken.java
new file mode 100644
index 000000000..25199472f
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/jwt/dto/ResponseAccessToken.java
@@ -0,0 +1,10 @@
+package codesquad.issueTracker.jwt.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public class ResponseAccessToken {
+ private String accessToken;
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/InMemoryProviderRepository.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/util/InMemoryProviderRepository.java
index f138588be..33436ed43 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/InMemoryProviderRepository.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/jwt/util/InMemoryProviderRepository.java
@@ -1,13 +1,15 @@
package codesquad.issueTracker.jwt.util;
import java.util.Map;
+
+import codesquad.issueTracker.oauth.util.OauthProvider;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class InMemoryProviderRepository {
- private final Map providers;
+ private final Map providers;
- public OauthProvider findByProviderName(String name) {
- return providers.get(name);
- }
+ public OauthProvider findByProviderName(String name) {
+ return providers.get(name);
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthAdapter.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthAdapter.java
deleted file mode 100644
index b83a88139..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthAdapter.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package codesquad.issueTracker.jwt.util;
-
-import codesquad.issueTracker.jwt.domain.OAuthProperties;
-import java.util.HashMap;
-import java.util.Map;
-import lombok.NoArgsConstructor;
-
-@NoArgsConstructor
-public class OauthAdapter {
-
- public static Map getOauthProviders(OAuthProperties properties) {
- Map oauthProvider = new HashMap<>();
-
- properties.getClient().forEach(
- (key, value) -> oauthProvider.put(
- key, new OauthProvider(value, properties.getProvider().get(key))));
- return oauthProvider;
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthProvider.java b/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthProvider.java
deleted file mode 100644
index 31edef645..000000000
--- a/be/issue/src/main/java/codesquad/issueTracker/jwt/util/OauthProvider.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package codesquad.issueTracker.jwt.util;
-
-import codesquad.issueTracker.jwt.domain.OAuthProperties;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-
-@Getter
-public class OauthProvider {
- private final String clientId;
- private final String clientSecret;
- private final String redirectUrl;
- private final String tokenUrl;
- private final String userInfoUrl;
-
- public OauthProvider(OAuthProperties.Client client, OAuthProperties.Provider provider) {
- this(client.getClientId(), client.getClientSecret(), client.getRedirectUrl(), provider.getTokenUrl(), provider.getUserInfoUrl());
- }
-
- @Builder
- public OauthProvider(String clientId, String clientSecret, String redirectUrl, String tokenUrl, String userInfoUrl) {
- this.clientId = clientId;
- this.clientSecret = clientSecret;
- this.redirectUrl = redirectUrl;
- this.tokenUrl = tokenUrl;
- this.userInfoUrl = userInfoUrl;
- }
-}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/controller/LabelController.java b/be/issue/src/main/java/codesquad/issueTracker/label/controller/LabelController.java
index 8239ef7ba..f36e29910 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/label/controller/LabelController.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/controller/LabelController.java
@@ -1,12 +1,51 @@
package codesquad.issueTracker.label.controller;
+import static codesquad.issueTracker.global.exception.SuccessCode.*;
+
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import codesquad.issueTracker.global.common.ApiResponse;
+import codesquad.issueTracker.label.dto.CreateLabelResponseDto;
+import codesquad.issueTracker.label.dto.LabelRequestDto;
+import codesquad.issueTracker.label.dto.LabelResponseDto;
import codesquad.issueTracker.label.service.LabelService;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@RestController
+@RequestMapping("/api")
public class LabelController {
+
private final LabelService labelService;
+
+ @PostMapping("/labels")
+ public ApiResponse create(@RequestBody LabelRequestDto labelRequestDto) {
+ CreateLabelResponseDto createLabelResponseDto = labelService.save(labelRequestDto);
+ return ApiResponse.success(SUCCESS.getStatus(), createLabelResponseDto);
+ }
+
+ @PatchMapping("labels/{labelId}")
+ public ApiResponse modify(@PathVariable Long labelId, @RequestBody LabelRequestDto labelRequestDto) {
+ labelService.modify(labelId, labelRequestDto);
+ return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
+ }
+
+ @DeleteMapping("labels/{labelId}")
+ public ApiResponse delete(@PathVariable Long labelId) {
+ labelService.delete(labelId);
+ return ApiResponse.success(SUCCESS.getStatus(), SUCCESS.getMessage());
+ }
+
+ @GetMapping("labels")
+ public ApiResponse findAll() {
+ LabelResponseDto labelResponseDto = labelService.findAll();
+ return ApiResponse.success(SUCCESS.getStatus(), labelResponseDto);
+ }
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/domain/Label.java b/be/issue/src/main/java/codesquad/issueTracker/label/domain/Label.java
index e2f8e98ba..2a53799e7 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/label/domain/Label.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/domain/Label.java
@@ -1,5 +1,6 @@
package codesquad.issueTracker.label.domain;
+import codesquad.issueTracker.label.dto.LabelRequestDto;
import lombok.Builder;
import lombok.Getter;
@@ -7,16 +8,26 @@
public class Label {
private Long id;
private String name;
- private String description;
- private String backgroundColor;
private String textColor;
+ private String backgroundColor;
+ private String description;
@Builder
- public Label(Long id, String name, String description, String backgroundColor, String textColor) {
+ public Label(Long id, String name, String textColor, String backgroundColor, String description) {
this.id = id;
this.name = name;
- this.description = description;
- this.backgroundColor = backgroundColor;
this.textColor = textColor;
+ this.backgroundColor = backgroundColor;
+ this.description = description;
}
+
+ public static Label toEntity(LabelRequestDto labelRequestDto) {
+ return Label.builder()
+ .name(labelRequestDto.getName())
+ .textColor(labelRequestDto.getTextColor())
+ .backgroundColor(labelRequestDto.getBackgroundColor())
+ .description(labelRequestDto.getDescription())
+ .build();
+ }
+
}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/dto/CreateLabelResponseDto.java b/be/issue/src/main/java/codesquad/issueTracker/label/dto/CreateLabelResponseDto.java
new file mode 100644
index 000000000..ec5bb3417
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/dto/CreateLabelResponseDto.java
@@ -0,0 +1,16 @@
+package codesquad.issueTracker.label.dto;
+
+import lombok.Getter;
+
+@Getter
+public class CreateLabelResponseDto {
+ private Long labelId;
+
+ public CreateLabelResponseDto(Long labelId) {
+ this.labelId = labelId;
+ }
+
+ public static CreateLabelResponseDto from(Long id) {
+ return new CreateLabelResponseDto(id);
+ }
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelRequestDto.java b/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelRequestDto.java
new file mode 100644
index 000000000..236a74c94
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelRequestDto.java
@@ -0,0 +1,18 @@
+package codesquad.issueTracker.label.dto;
+
+import javax.validation.constraints.NotNull;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@Getter
+@RequiredArgsConstructor
+public class LabelRequestDto {
+ @NotNull(message = "라벨 제목을 입력해주세요")
+ private String name;
+ @NotNull(message = "텍스트 색상을 입력해주세요")
+ private String textColor;
+ @NotNull(message = "배경 색상을 입력해주세요")
+ private String backgroundColor;
+ private String description;
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelResponseDto.java b/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelResponseDto.java
new file mode 100644
index 000000000..e32eaf8c6
--- /dev/null
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/dto/LabelResponseDto.java
@@ -0,0 +1,21 @@
+package codesquad.issueTracker.label.dto;
+
+import java.util.List;
+
+import codesquad.issueTracker.label.vo.LabelVo;
+import lombok.Getter;
+
+@Getter
+public class LabelResponseDto {
+ private List labels;
+ private int milestoneCount;
+
+ public LabelResponseDto(List labels, int milestoneCount) {
+ this.labels = labels;
+ this.milestoneCount = milestoneCount;
+ }
+
+ public static LabelResponseDto of(List labels, int milestoneCount) {
+ return new LabelResponseDto(labels, milestoneCount);
+ }
+}
diff --git a/be/issue/src/main/java/codesquad/issueTracker/label/repository/LabelRepository.java b/be/issue/src/main/java/codesquad/issueTracker/label/repository/LabelRepository.java
index 0be9015fd..18202cfaf 100644
--- a/be/issue/src/main/java/codesquad/issueTracker/label/repository/LabelRepository.java
+++ b/be/issue/src/main/java/codesquad/issueTracker/label/repository/LabelRepository.java
@@ -1,13 +1,90 @@
package codesquad.issueTracker.label.repository;
+import java.util.List;
+import java.util.Optional;
+
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
+import codesquad.issueTracker.global.exception.CustomException;
+import codesquad.issueTracker.global.exception.ErrorCode;
+import codesquad.issueTracker.label.domain.Label;
+
@Repository
public class LabelRepository {
+
private final NamedParameterJdbcTemplate jdbcTemplate;
- public LabelRepository(NamedParameterJdbcTemplate jdbcTemplate) {
- this.jdbcTemplate = jdbcTemplate;
+ public LabelRepository(JdbcTemplate jdbcTemplate) {
+ this.jdbcTemplate = new NamedParameterJdbcTemplate(jdbcTemplate);
+ }
+
+ public Long insert(Label label) {
+ String sql = "INSERT INTO labels (name,text_color,background_color,description) VALUES (:name,:text_color,:background_color,:description)";
+ KeyHolder keyHolder = new GeneratedKeyHolder();
+
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("name", label.getName());
+ params.addValue("text_color", label.getTextColor());
+ params.addValue("background_color", label.getBackgroundColor());
+ params.addValue("description", label.getDescription());
+
+ int result = jdbcTemplate.update(sql, params, keyHolder);
+ if (result == 0) {
+ throw new CustomException(ErrorCode.LABEL_INSERT_FAILED);
+ }
+ return keyHolder.getKey().longValue();
+ }
+
+ public Long update(Long id, Label label) {
+ String sql = "UPDATE labels SET name = :name, text_color = :textColor, background_color = :backgroundColor, description = :description WHERE id = :id";
+ MapSqlParameterSource params = new MapSqlParameterSource();
+ params.addValue("id", id);
+ params.addValue("name", label.getName());
+ params.addValue("textColor", label.getTextColor());
+ params.addValue("backgroundColor", label.getBackgroundColor());
+ params.addValue("description", label.getDescription());
+ int result = jdbcTemplate.update(sql, params);
+ if (result == 0) {
+ throw new CustomException(ErrorCode.LABEL_UPDATE_FAILED);
+ }
+ return id;
}
+
+ public Long delete(Long id) {
+ String sql = "UPDATE labels SET is_deleted = TRUE where id = :id";
+ int result = jdbcTemplate.update(sql, new MapSqlParameterSource()
+ .addValue("id", id));
+ if (result == 0) {
+ throw new CustomException(ErrorCode.LABEL_DELETE_FAILED);
+ }
+ return id;
+ }
+
+ public Optional> findAll() {
+ String sql = "SELECT id, name, text_color, background_color, description "
+ + "FROM labels "
+ + "WHERE is_deleted = FALSE";
+ return Optional.of(jdbcTemplate.query(sql, labelRowMapper));
+ }
+
+ public Optional findMilestonesCount() {
+ String sql = "SELECT COUNT(*) FROM milestones WHERE is_deleted = false";
+ MapSqlParameterSource params = new MapSqlParameterSource();
+
+ return Optional.ofNullable(jdbcTemplate.queryForObject(sql, params, Integer.class));
+ }
+
+ private final RowMapper