Skip to content

Commit

Permalink
[PR] Feat : 내커리어 활동 태그 생성/조회/삭제 API 추가
Browse files Browse the repository at this point in the history
Merge pull request #13 from kkijuk/mvp/career
  • Loading branch information
tape4 authored Jul 26, 2024
2 parents e64e0a1 + 7f32010 commit 4e698e8
Show file tree
Hide file tree
Showing 17 changed files with 414 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import umc.kkijuk.server.career.controller.exception.CareerValidationException;
import umc.kkijuk.server.common.domian.exception.CareerValidationException;
import umc.kkijuk.server.career.controller.response.CareerGroupedByResponse;
import umc.kkijuk.server.career.controller.response.CareerResponseMessage;
import umc.kkijuk.server.career.domain.Career;
import umc.kkijuk.server.career.dto.CareerRequestDto;
import umc.kkijuk.server.career.dto.converter.CareerConverter;
import umc.kkijuk.server.career.repository.CareerRepository;
import umc.kkijuk.server.career.repository.CategoryRepository;
import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -90,7 +92,7 @@ public List<? extends CareerGroupedByResponse> getCareerGroupedBy(String status)
@Override
public Optional<Career> findCareer(Long careerId) {
return Optional.ofNullable(careerRepository.findById(careerId).orElseThrow(
() -> new CareerValidationException(CareerResponseMessage.CAREER_NOT_FOUND.toString())));
() -> new ResourceNotFoundException("Career",careerId)));
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package umc.kkijuk.server.career.controller.exception;
package umc.kkijuk.server.common.controller;


import com.fasterxml.jackson.databind.exc.InvalidFormatException;
Expand All @@ -10,6 +10,7 @@
import org.springframework.web.bind.annotation.RestControllerAdvice;
import umc.kkijuk.server.career.controller.response.CareerResponse;
import umc.kkijuk.server.career.controller.response.CareerResponseMessage;
import umc.kkijuk.server.common.domian.exception.CareerValidationException;


@RestControllerAdvice(assignableTypes = {umc.kkijuk.server.career.controller.CareerController.class})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import umc.kkijuk.server.common.domian.exception.InvalidTagNameException;
import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException;
import umc.kkijuk.server.common.domian.exception.ReviewRecruitNotMatchException;
import umc.kkijuk.server.common.domian.response.ErrorResponse;
Expand All @@ -24,4 +25,10 @@ public ErrorResponse resourceNotFoundException(ResourceNotFoundException excepti
public ErrorResponse ReviewRecruitMatchException(ReviewRecruitNotMatchException exception) {
return new ErrorResponse(exception.getMessage());
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(InvalidTagNameException.class)
public ErrorResponse InvalidTagNameException(InvalidTagNameException exception) {
return new ErrorResponse(exception.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package umc.kkijuk.server.career.controller.exception;
package umc.kkijuk.server.common.domian.exception;

public class CareerValidationException extends RuntimeException{

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package umc.kkijuk.server.common.domian.exception;

public class InvalidTagNameException extends RuntimeException{
public InvalidTagNameException(String message) {
super(message);

}
}
43 changes: 43 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/controller/TagController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package umc.kkijuk.server.tag.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import umc.kkijuk.server.tag.controller.response.TagResponse;
import umc.kkijuk.server.tag.domain.Tag;
import umc.kkijuk.server.tag.dto.TagRequestDto;
import umc.kkijuk.server.tag.dto.TagResponseDto;
import umc.kkijuk.server.tag.dto.converter.TagConverter;
import umc.kkijuk.server.tag.service.TagService;

@io.swagger.v3.oas.annotations.tags.Tag(name="tag",description = "태그 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("/career")
public class TagController {

private final TagService tagService;

@PostMapping("/tag")
@Operation(summary = "태그 추가 API", description = "태그 - 태그를 생성하는 API")
public TagResponse<TagResponseDto.ResultTagDto> create(@RequestBody @Valid TagRequestDto.CreateTagDto request) {
Tag createTag = tagService.createTag(request);
return TagResponse.success(HttpStatus.OK,"태그를 성공적으로 생성했습니다.", TagConverter.toTagResult(createTag));
}
@GetMapping("/tag")
@Operation(summary = "태그 조회 API", description = "태그 - 태그 조회하는 API")
public TagResponse<TagResponseDto.ResultTagDtoList> read() {
return TagResponse.success(HttpStatus.OK, "모든 태그를 성공적으로 조회했습니다.", tagService.findAllTags());
}
@DeleteMapping("/tag/{tagId}")
@Operation(summary = "태그 삭제 API",description = "태그 - 태그를 삭제하는 API")
@Parameter(name = "tagId",description = "태그 Id, path variable 입니다. 존재하는 태그 Id 값을 넘겨 주세요.",example = "1")
public TagResponse<Object> delete(@PathVariable Long tagId) {
tagService.delete(tagId);
return TagResponse.success(HttpStatus.OK, "태그 삭제가 성공적으로 이루어졌습니다.", null);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package umc.kkijuk.server.tag.controller.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Builder
@Getter
public class TagResponse<T> {
private int status;
private String message;
@JsonInclude(JsonInclude.Include.NON_NULL)
private T data;

public TagResponse(final int status,final String message,T data){
this.status = status;
this.message = message;
this.data = data;
}
public static <T> TagResponse<T> success(HttpStatus status, String message, T data){
return new TagResponse<>(status.value(),message,data);
}
}
20 changes: 20 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/domain/Tag.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package umc.kkijuk.server.tag.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import lombok.*;

@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Tag {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="tag_id")
private Long id;
@NotNull
@Column(name="tag_name", length = 30)
private String name;

}
21 changes: 21 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/dto/TagRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package umc.kkijuk.server.tag.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


public class TagRequestDto {
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class CreateTagDto{
@Size(max = 30)
@Schema(description = "태그 이름", example = "역량 키워드", type="string")
String tagName;
}
}
32 changes: 32 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/dto/TagResponseDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package umc.kkijuk.server.tag.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

public class TagResponseDto {
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ResultTagDto{
@Schema(description = "생성된 활동 태그 Id", example = "1", type = "Long")
private Long id;
@Schema(description = "생성된 활동 태그 이름", example = "핵심 역량", type = "String")
private String tagName;
}
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class ResultTagDtoList{
@Schema(description = "활동 태그들의 개수",example = "3")
private int count;
@Schema(description = "활동 태그들 리스트")
private List<ResultTagDto> tagList;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package umc.kkijuk.server.tag.dto.converter;

import umc.kkijuk.server.tag.domain.Tag;
import umc.kkijuk.server.tag.dto.TagRequestDto;
import umc.kkijuk.server.tag.dto.TagResponseDto;

import java.util.List;
import java.util.stream.Collectors;

public class TagConverter {
public static Tag toTag(TagRequestDto.CreateTagDto request) {
return Tag.builder()
.name(request.getTagName())
.build();
}
public static TagResponseDto.ResultTagDto toTagResult(Tag hashTag) {
return TagResponseDto.ResultTagDto.builder()
.id(hashTag.getId())
.tagName(hashTag.getName())
.build();
}

public static TagResponseDto.ResultTagDtoList toResultTagDtoList(List<Tag> tagList) {
return TagResponseDto.ResultTagDtoList.builder()
.count(tagList.size())
.tagList(tagList.stream().map(
value -> TagResponseDto.ResultTagDto.builder()
.tagName(value.getName())
.id(value.getId())
.build()
).collect(Collectors.toList()))
.build();
}

}
11 changes: 11 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/repository/TagRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package umc.kkijuk.server.tag.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import umc.kkijuk.server.tag.domain.Tag;

import java.util.List;

public interface TagRepository extends JpaRepository<Tag,Long> {
boolean existsByName(String name);
List<Tag> findAll();
}
12 changes: 12 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/service/TagService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package umc.kkijuk.server.tag.service;

import umc.kkijuk.server.tag.domain.Tag;
import umc.kkijuk.server.tag.dto.TagRequestDto;
import umc.kkijuk.server.tag.dto.TagResponseDto;


public interface TagService {
Tag createTag(TagRequestDto.CreateTagDto request);
TagResponseDto.ResultTagDtoList findAllTags();
void delete(Long tagId);
}
46 changes: 46 additions & 0 deletions src/main/java/umc/kkijuk/server/tag/service/TagServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package umc.kkijuk.server.tag.service;


import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import umc.kkijuk.server.common.domian.exception.InvalidTagNameException;
import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException;
import umc.kkijuk.server.common.domian.response.ErrorResponse;
import umc.kkijuk.server.tag.domain.Tag;
import umc.kkijuk.server.tag.dto.TagRequestDto;
import umc.kkijuk.server.tag.dto.TagResponseDto;
import umc.kkijuk.server.tag.dto.converter.TagConverter;
import umc.kkijuk.server.tag.repository.TagRepository;

import java.util.List;
import java.util.Optional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class TagServiceImpl implements TagService {
private final TagRepository tagRepository;
@Override
@Transactional
public Tag createTag(TagRequestDto.CreateTagDto request) {
String tagName = request.getTagName();
if (tagName==null || tagName.trim().isEmpty() || tagRepository.existsByName(tagName)) {
throw new InvalidTagNameException("태그 이름은 비워둘 수 없으며, 이미 존재하는 이름은 사용할 수 없습니다.");
}
Tag hashTag = TagConverter.toTag(request);
return tagRepository.save(hashTag);
}
@Override
public TagResponseDto.ResultTagDtoList findAllTags() {
return TagConverter.toResultTagDtoList(tagRepository.findAll());
}
@Override
@Transactional
public void delete(Long tagId) {
Tag deleteTag = tagRepository.findById(tagId).orElseThrow(() -> new ResourceNotFoundException("Tag", tagId));
tagRepository.delete(deleteTag);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import umc.kkijuk.server.career.controller.exception.CareerExceptionControllerAdvice;
import umc.kkijuk.server.career.controller.exception.CareerValidationException;
import umc.kkijuk.server.common.controller.CareerExceptionControllerAdvice;
import umc.kkijuk.server.common.domian.exception.CareerValidationException;
import umc.kkijuk.server.career.controller.response.CareerGroupedByResponse;
import umc.kkijuk.server.career.controller.response.CareerResponse;
import umc.kkijuk.server.career.controller.response.CareerResponseMessage;
Expand All @@ -21,6 +21,7 @@
import umc.kkijuk.server.career.dto.CareerResponseDto;
import umc.kkijuk.server.career.repository.CareerRepository;
import umc.kkijuk.server.career.repository.CategoryRepository;
import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException;

import java.time.LocalDate;
import java.util.List;
Expand Down Expand Up @@ -172,7 +173,7 @@ void init() {
.build();
//when
//then
assertThrows(CareerValidationException.class, () -> careerService.updateCareer(999L,updateCareerDto));
assertThrows(ResourceNotFoundException.class, () -> careerService.updateCareer(999L,updateCareerDto));
}
@Test
void update_수정시_날짜_형식이_잘못된_경우_에러() {
Expand Down Expand Up @@ -225,7 +226,7 @@ void init() {
//given
//when
//then
assertThrows(CareerValidationException.class, () -> careerService.deleteCareer(999L));
assertThrows(ResourceNotFoundException.class, () -> careerService.deleteCareer(999L));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import umc.kkijuk.server.career.controller.exception.CareerValidationException;
import umc.kkijuk.server.common.domian.exception.CareerValidationException;
import umc.kkijuk.server.career.domain.Career;
import umc.kkijuk.server.career.domain.Category;
import umc.kkijuk.server.career.dto.CareerRequestDto;
import umc.kkijuk.server.career.dto.CareerResponseDto;
import umc.kkijuk.server.career.repository.CareerRepository;
import umc.kkijuk.server.common.domian.exception.ResourceNotFoundException;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -96,7 +98,7 @@ void init() {
when(careerRepository.findById(anyLong())).thenReturn(Optional.empty());
//when
//then
assertThrows(CareerValidationException.class, ()->{
assertThrows(ResourceNotFoundException.class, ()->{
careerService.deleteCareer(1L);});
verify(careerRepository,never()).delete(any(Career.class));
}
Expand All @@ -115,7 +117,7 @@ void init() {

// when
// then
assertThrows(CareerValidationException.class, () -> careerService.updateCareer(999L, updateCareerDto));
assertThrows(ResourceNotFoundException.class, () -> careerService.updateCareer(999L, updateCareerDto));
verify(careerRepository, never()).save(any(Career.class));
}

Expand Down
Loading

0 comments on commit 4e698e8

Please sign in to comment.