Skip to content

Commit

Permalink
Add(.footprint) : 발자취 생성 기능 및 API 추가 (#185)
Browse files Browse the repository at this point in the history
<!-- PR 내용
어떤 작업을 했는지 작성해주세요!
PR이 너무 크면 너무 많은 내용이 들어가겠죠? -->
# 🔍 어떤 PR인가요?
- 발자취 생성 기능과 API를 구현했습니다.
- pr 단위를 작게 하기 위해 태그 저장부분은 반영하지 않았습니다.
- 태그는 담당자와 논의한 뒤 새로 PR을 만들어 개선하고 발자취에 반영하려고 합니다.

<!-- 리뷰어에게
어떤 부분을 자세하게 리뷰할지 서술해주세요. -->
# 😋 To Reviewer
- 기존 `Requset` 와 역할 구분이 어려웠던 `FootprintParam` 을 연관관계를 가지지 않는 발자취 데이터로
변경했습니다.
- `FootprintParam` 탬플릿에 따른 요청, 응답값 데이터 변경 관리를 할 수 있는 엔드 포인트입니다.
-  변경사항을 쉽게 관리하기 위해 표현 계층에서 사용하는 객체와, param의 관계는 다음과 같습니다.
```
Request {
   static Create{
       Param.Create param;
   }
}
```

- 표현 계층에서 사용하는 request 가 서비스 로직까지 오는 것을 최대한 방지하고자 했습니다.   
- 발자취 생성 API url 에 대한 피드백 부탁드립니다. 
   최대한 자원 식별을 할 수 있는 계층 구조를 생각해봤습니다.
   `/targets/{target-id}/footprints`


<!-- 테스트 
반영한 테스트 메서드 이름과 어떤 테스트를 했는지 작성해주세요.
(ex. save_Fail_ByDuplicateEmail) -->
-  url 구성에 따라 controller 테스트에서도 "{}{}{}_END_POINT" 사용을 보류했습니다.
- url 피드백 이후 반영하도록 하겠습니다

# ✅ 작성한 테스트
- [x] createFootPrint_Success_With_NoExceptions
- [x] createFootprint_Fail_By_NotFountTarget
- [x] createFootprint_Success_With_NoExceptions
- [x] createFootprint_Fail_By_InvalidContents

<!-- 관련 이슈
프론트에서 작성해준 이슈와 연관되는 PR이라면 
주석을 풀고 작성해주세요! --> 

[//]: # (# 🫡 관련 Issue )
  • Loading branch information
Hejow authored Feb 29, 2024
2 parents 42f1cd9 + 2ad1b80 commit aa2d139
Show file tree
Hide file tree
Showing 18 changed files with 319 additions and 128 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package me.ajaja.module.footprint.adapter.in.web;

import static org.springframework.http.HttpStatus.*;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import lombok.RequiredArgsConstructor;
import me.ajaja.global.common.AjajaResponse;
import me.ajaja.global.security.annotation.Authorization;
import me.ajaja.global.util.SecurityUtil;
import me.ajaja.module.footprint.application.port.in.CreateFootprintUseCase;
import me.ajaja.module.footprint.dto.FootprintRequest;

@RestController
@RequiredArgsConstructor
public class CreateFootprintController {
private final CreateFootprintUseCase createFootprintUseCase;

@Authorization
@PostMapping("/footprints")
@ResponseStatus(CREATED)
public AjajaResponse<Void> createFootprint(@RequestBody FootprintRequest.Create request) {
Long userId = SecurityUtil.getUserId();
createFootprintUseCase.create(userId, request);
return AjajaResponse.ok();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package me.ajaja.module.footprint.application.port;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import me.ajaja.module.footprint.application.port.in.CreateFootprintUseCase;
import me.ajaja.module.footprint.application.port.out.CreateFootprintPort;
import me.ajaja.module.footprint.domain.Footprint;
import me.ajaja.module.footprint.dto.FootprintRequest;

@Service
@Transactional
@RequiredArgsConstructor
public class CreateFootprintService implements CreateFootprintUseCase {
private final CreateFootprintPort createFootprintPort;

@Override
public void create(Long userId, FootprintRequest.Create param) {
Footprint footprint = Footprint.init(userId, param);
createFootprintPort.create(footprint);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package me.ajaja.module.footprint.application.port.in;

import me.ajaja.module.footprint.dto.FootprintRequest;

public interface CreateFootprintUseCase {
void create(Long userId, FootprintRequest.Create param);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@

public interface GetFootprintPort {
Footprint getFootprint(Long id);

}
24 changes: 24 additions & 0 deletions src/main/java/me/ajaja/module/footprint/domain/Footprint.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.ajaja.global.common.SelfValidating;
import me.ajaja.module.footprint.dto.FootprintRequest;

@Getter
@AllArgsConstructor
Expand All @@ -23,4 +24,27 @@ public enum Type {
public Footprint(Target target, Writer writer, Type type, Title title, boolean visible) {
this(null, target, writer, type, title, visible, false);
}

public static Footprint init(Long userId, FootprintRequest.Create param) {
return switch (param.getType()) {
case FREE -> new FreeFootprint(
Target.init(param.getTargetId()),
Writer.init(userId),
param.getType(),
Title.init(param.getTitle()),
param.isVisible(),
param.getContent()
);
case KPT -> new KptFootprint(
Target.init(param.getTargetId()),
Writer.init(userId),
param.getType(),
Title.init(param.getTitle()),
param.isVisible(),
param.getKeepContent(),
param.getProblemContent(),
param.getTryContent()
);
};
}
}

This file was deleted.

9 changes: 4 additions & 5 deletions src/main/java/me/ajaja/module/footprint/domain/Target.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@

import java.beans.ConstructorProperties;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import me.ajaja.global.common.SelfValidating;

@Getter
public class Target extends SelfValidating<Target> {
@NotNull
private final Long id;

@NotBlank
@Size(max = 20)
private final String title;

@ConstructorProperties({"id", "title"})
Expand All @@ -23,4 +18,8 @@ public Target(Long id, String title) {
this.title = title;
this.validateSelf();
}

public static Target init(Long id) {
return new Target(id, null);
}
}
4 changes: 4 additions & 0 deletions src/main/java/me/ajaja/module/footprint/domain/Title.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ public Title(String title) {
this.title = title;
this.validateSelf();
}

public static Title init(String title) {
return new Title(title);
}
}
9 changes: 4 additions & 5 deletions src/main/java/me/ajaja/module/footprint/domain/Writer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,14 @@

import java.beans.ConstructorProperties;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import me.ajaja.global.common.SelfValidating;

@Getter
public class Writer extends SelfValidating<Writer> {
@NotNull
private final Long id;

@NotBlank
@Size(max = 20)
private final String nickname;

@ConstructorProperties({"id", "nickname"})
Expand All @@ -23,4 +18,8 @@ public Writer(Long id, String nickname) {
this.nickname = nickname;
this.validateSelf();
}

public static Writer init(Long id) {
return new Writer(id, null);
}
}
39 changes: 0 additions & 39 deletions src/main/java/me/ajaja/module/footprint/dto/FootprintParam.java

This file was deleted.

21 changes: 21 additions & 0 deletions src/main/java/me/ajaja/module/footprint/dto/FootprintRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package me.ajaja.module.footprint.dto;

import java.util.List;

import lombok.Data;
import me.ajaja.module.footprint.domain.Footprint;

public final class FootprintRequest {
@Data
public static class Create {
private final Long targetId;
private final Footprint.Type type;
private final String title;
private final boolean visible;
private final String content;
private final String keepContent;
private final String problemContent;
private final String tryContent;
private final List<String> tags;
}
}
6 changes: 6 additions & 0 deletions src/test/java/me/ajaja/common/support/WebMvcTestSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import me.ajaja.module.feedback.application.LoadFeedbackInfoService;
import me.ajaja.module.feedback.application.LoadTotalAchieveService;
import me.ajaja.module.feedback.application.UpdateFeedbackService;
import me.ajaja.module.footprint.application.port.in.CreateFootprintUseCase;
import me.ajaja.module.plan.application.port.in.CreatePlanUseCase;
import me.ajaja.module.plan.application.port.in.DeletePlanUseCase;
import me.ajaja.module.plan.application.port.in.LoadPlanDetailUseCase;
Expand Down Expand Up @@ -70,6 +71,7 @@ public abstract class WebMvcTestSupport extends MonkeySupport {
protected static final String PLAN_END_POINT = "/plans";
protected static final String FEEDBACK_END_POINT = "/feedbacks";
protected static final String REMIND_END_POINT = "/reminds";
protected static final String FOOTPRINT_END_POINT = "/footprints";
protected static final String BEARER_TOKEN = "Bearer eyJhbGxMiJ9.eyJzWpvdyJ9.avFKonhbIIhEg8H1dycQkhQ";

@Autowired
Expand Down Expand Up @@ -171,4 +173,8 @@ protected static Stream<Arguments> authenticationFailResults() {
protected FindTargetRemindQuery findTargetRemindQuery;
@MockBean
protected SendTestRemindUseCase sendTestRemindUseCase;

// footprint
@MockBean
protected CreateFootprintUseCase createFootprintUseCase;
}
1 change: 1 addition & 0 deletions src/test/java/me/ajaja/common/util/ApiTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public enum ApiTag {
FEEDBACK("피드백 API"),
PLAN("계획 API"),
REMIND("리마인드 API"),
FOOTPRINT("발자취 API"),
USER("사용자 API");

final String content;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package me.ajaja.module.footprint.adapter.in.web;

import static me.ajaja.global.exception.ErrorCode.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.http.MediaType.*;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.List;

import org.junit.jupiter.api.DisplayName;
import org.springframework.http.HttpHeaders;

import jakarta.validation.ConstraintViolationException;
import me.ajaja.common.annotation.ApiTest;
import me.ajaja.common.support.WebMvcTestSupport;
import me.ajaja.common.util.ApiTag;
import me.ajaja.common.util.RestDocument;
import me.ajaja.module.footprint.domain.Footprint;
import me.ajaja.module.footprint.dto.FootprintRequest;

class CreateFootprintControllerTest extends WebMvcTestSupport {

@ApiTest
@DisplayName("발자취 유형에 맞는 형식에 요청 값에 대해 발자취 생성에 성공한다.")
void createFootprint_Success_With_NoExceptions() throws Exception {
// given
FootprintRequest.Create request = sut.giveMeBuilder(FootprintRequest.Create.class)
.set("param.type", Footprint.Type.FREE)
.set("param.content", "contents")
.set("tags", List.of("tag1", "tag2"))
.sample();

doNothing().when(createFootprintUseCase).create(anyLong(), any());

// when
var result = mockMvc.perform(post(FOOTPRINT_END_POINT)
.header(HttpHeaders.AUTHORIZATION, BEARER_TOKEN)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

// then
result.andExpect(status().isCreated());

// docs
result.andDo(RestDocument.builder()
.identifier("footprint-create-success")
.tag(ApiTag.FOOTPRINT)
.summary("발자취 생성 API")
.description("발자취 유형에 맞는 항목에 빈 문자가 아닌 상태로 요청할 떄 발자취 정보를 생성 합니다.")
.secured(true)
.result(result)
.generateDocs());
}

@ApiTest
@DisplayName("발자취 유형에 해당 하는 항목 값이 빈 문자일 때 대해 발자취 생성에 실패한다.")
void createFootprint_Fail_By_InvalidContents() throws Exception {
// given
FootprintRequest.Create request = sut.giveMeBuilder(FootprintRequest.Create.class)
.set("param.content", "")
.set("tags", List.of("tag1", "tag2"))
.sample();

doThrow(ConstraintViolationException.class).when(createFootprintUseCase)
.create(anyLong(), any());

// when
var result = mockMvc.perform(post(FOOTPRINT_END_POINT)
.header(HttpHeaders.AUTHORIZATION, BEARER_TOKEN)
.accept(APPLICATION_JSON)
.contentType(APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

result.andExpectAll(
status().isBadRequest(),
jsonPath("$.httpStatus").value(BAD_REQUEST.name()),
jsonPath("$.errorName").value(BEAN_VALIDATION_FAIL_EXCEPTION.name()),
jsonPath("$.errorMessage").value(BEAN_VALIDATION_FAIL_EXCEPTION.getMessage())
);

// docs
result.andDo(RestDocument.builder()
.identifier("footprint-create-fail-invalid-type-contents")
.tag(ApiTag.FOOTPRINT)
.secured(true)
.result(result)
.generateDocs());
}
}
Loading

0 comments on commit aa2d139

Please sign in to comment.