Skip to content

Commit

Permalink
Merge pull request #35 from dongkyun0713/feature/restdocs
Browse files Browse the repository at this point in the history
Feature(#33): restdocs 적용
  • Loading branch information
dongkyun0713 authored Jul 22, 2024
2 parents 64b6a1e + a4ad357 commit 11556f8
Show file tree
Hide file tree
Showing 12 changed files with 373 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
distribution: 'temurin'

- name: Set up application-prod.yml
run: echo "${{ secrets.APPLICATION }}" > ./src/main/resources/application-prod.yml
run: echo "${{ secrets.DEV_APPLICATION }}" > ./src/main/resources/application-prod.yml

- name: Build with Gradle
run: ./gradlew build -x test
Expand Down
27 changes: 27 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ plugins {
id 'java'
id 'org.springframework.boot' version '3.3.1'
id 'io.spring.dependency-management' version '1.1.5'
id 'org.asciidoctor.jvm.convert' version '4.0.2'
}

group = 'shop.kkeujeok'
Expand All @@ -17,6 +18,7 @@ configurations {
compileOnly {
extendsFrom annotationProcessor
}
asciidoctorExt
}

repositories {
Expand Down Expand Up @@ -50,6 +52,31 @@ dependencies {
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// restdocs
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor:3.0.1'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:3.0.1'

}
ext {
snippetsDir = file('build/generated-snippets')
}

test {
outputs.dir snippetsDir
}

asciidoctor {
inputs.dir snippetsDir
configurations 'asciidoctorExt'
dependsOn test
baseDirFollowsSourceFile()
}

bootJar {
dependsOn asciidoctor
from("${asciidoctor.outputDir}") {
into 'static/docs'
}
}

tasks.named('test') {
Expand Down
12 changes: 12 additions & 0 deletions src/docs/asciidoc/block.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
= 블록 API 문서

== 블록 생성 API

=== 요청
include::{snippets}/block/save/http-request.adoc[]
include::{snippets}/block/save/request-fields.adoc[]

=== 응답

include::{snippets}/block/save/http-response.adoc[]
include::{snippets}/block/save/response-fields.adoc[]
9 changes: 9 additions & 0 deletions src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
= 끄적끄적 API 문서
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 3

include::block.adoc[]

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockSaveReqDto;
import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockUpdateReqDto;
Expand All @@ -30,4 +31,11 @@ public RspTemplate<BlockInfoResDto> update(@PathVariable(name = "blockId") Long
@RequestBody BlockUpdateReqDto blockUpdateReqDto) {
return new RspTemplate<>(HttpStatus.OK, "블록 수정", blockService.update(blockId, blockUpdateReqDto));
}

@PatchMapping("/{blockId}/progress")
public RspTemplate<BlockInfoResDto> progressUpdate(@PathVariable(name = "blockId") Long blockId,
@RequestParam(name = "progress") String progress) {
return new RspTemplate<>(HttpStatus.OK, "블록 상태 수정", blockService.progressUpdate(blockId, progress));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@
import shop.kkeujeok.kkeujeokbackend.block.api.dto.request.BlockUpdateReqDto;
import shop.kkeujeok.kkeujeokbackend.block.api.dto.response.BlockInfoResDto;
import shop.kkeujeok.kkeujeokbackend.block.domain.Block;
import shop.kkeujeok.kkeujeokbackend.block.domain.Progress;
import shop.kkeujeok.kkeujeokbackend.block.domain.repository.BlockRepository;
import shop.kkeujeok.kkeujeokbackend.block.exception.BlockNotFoundException;
import shop.kkeujeok.kkeujeokbackend.block.exception.InvalidProgressException;
import shop.kkeujeok.kkeujeokbackend.member.domain.Member;
import shop.kkeujeok.kkeujeokbackend.member.domain.repository.MemberRepository;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class BlockService {
private final MemberRepository memberRepository;
private final BlockRepository blockRepository;

// 블록 생성
Expand All @@ -34,12 +38,35 @@ public BlockInfoResDto update(Long blockId, BlockUpdateReqDto blockUpdateReqDto)
Member member = Member.builder().nickname("member").build();
Block block = blockRepository.findById(blockId).orElseThrow(BlockNotFoundException::new);

block.update(blockUpdateReqDto.title(), blockUpdateReqDto.contents());
if (!block.getTitle().equals(blockUpdateReqDto.title()) ||
!block.getContents().equals(blockUpdateReqDto.contents())) {
block.update(blockUpdateReqDto.title(), blockUpdateReqDto.contents());
}

return BlockInfoResDto.of(block, member);
}

// 블록 상태 업데이트 (Progress)
@Transactional
public BlockInfoResDto progressUpdate(Long blockId, String progressString) {
// 로그인/회원가입 코드 완성 후 사용자 정보 받아올 예정
Member member = Member.builder().nickname("member").build();
Block block = blockRepository.findById(blockId).orElseThrow(BlockNotFoundException::new);

Progress progress = parseProgress(progressString);

block.progressUpdate(progress);

return BlockInfoResDto.of(block, member);
}

private Progress parseProgress(String progressString) {
try {
return Progress.valueOf(progressString);
} catch (IllegalArgumentException e) {
throw new InvalidProgressException();
}
}

// 블록 삭제 (논리 삭제)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ public void update(String title, String contents) {
this.title = title;
this.contents = contents;
}

public void progressUpdate(Progress progress) {
this.progress = progress;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package shop.kkeujeok.kkeujeokbackend.block.exception;

import shop.kkeujeok.kkeujeokbackend.global.error.exception.InvalidGroupException;

public class InvalidProgressException extends InvalidGroupException {
public InvalidProgressException(String message) {
super(message);
}

public InvalidProgressException() {
this("유효하지 않은 상태입니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand All @@ -25,6 +26,7 @@
import shop.kkeujeok.kkeujeokbackend.block.application.BlockService;
import shop.kkeujeok.kkeujeokbackend.block.domain.Block;
import shop.kkeujeok.kkeujeokbackend.block.domain.Progress;
import shop.kkeujeok.kkeujeokbackend.block.exception.InvalidProgressException;
import shop.kkeujeok.kkeujeokbackend.member.domain.Member;

@WebMvcTest(BlockController.class)
Expand Down Expand Up @@ -98,4 +100,42 @@ void setUp() {
.andExpect(jsonPath("$.data.progress").value("NOT_STARTED"))
.andExpect(jsonPath("$.data.nickname").value("웅이"));
}

@DisplayName("Patch 블록 상태 수정 컨트롤러 로직 확인")
@Test
void 블록_상태_수정() throws Exception {
// given
Long blockId = 1L;
String progressString = "IN_PROGRESS";
BlockInfoResDto response = BlockInfoResDto.of(block, member);

given(blockService.progressUpdate(anyLong(), anyString())).willReturn(response);

// when & then
mockMvc.perform(patch("/api/blocks/{blockId}/progress", blockId)
.param("progress", progressString)
.accept(MediaType.APPLICATION_JSON)
).andDo(print())
.andExpect(status().isOk())
.andExpect(jsonPath("$.statusCode").value(200))
.andExpect(jsonPath("$.message").value("블록 상태 수정"))
.andExpect(jsonPath("$.data").exists());
}

@DisplayName("Patch 블록 상태 수정 실패 컨트롤러 로직 확인(400 Bad Request가 발생한다)")
@Test
void 블록_상태_수정_실패() throws Exception {
// given
Long blockId = 1L;
String progressString = "STATUS_PROGRESS";

given(blockService.progressUpdate(anyLong(), anyString())).willThrow(new InvalidProgressException());

// when & then
mockMvc.perform(patch("/api/blocks/{blockId}/progress", blockId)
.param("progress", progressString)
.accept(MediaType.APPLICATION_JSON)
).andDo(print())
.andExpect(status().isBadRequest());
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package shop.kkeujeok.kkeujeokbackend.block.application;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
Expand All @@ -20,6 +21,7 @@
import shop.kkeujeok.kkeujeokbackend.block.domain.Block;
import shop.kkeujeok.kkeujeokbackend.block.domain.Progress;
import shop.kkeujeok.kkeujeokbackend.block.domain.repository.BlockRepository;
import shop.kkeujeok.kkeujeokbackend.block.exception.InvalidProgressException;

@ExtendWith(MockitoExtension.class)
class BlockServiceTest {
Expand Down Expand Up @@ -81,5 +83,53 @@ void setUp() {
});
}

@DisplayName("블록 제목과 내용이 기존과 동일하면 수정 하지 않습니다.")
@Test
void 블록_수정_X() {
// given
Long blockId = 1L;
BlockUpdateReqDto originBlockUpdateReqDto = new BlockUpdateReqDto("Title", "Contents");
when(blockRepository.findById(blockId)).thenReturn(Optional.of(block));

// when
BlockInfoResDto result = blockService.update(blockId, originBlockUpdateReqDto);

// then
assertAll(() -> {
assertThat(result.title()).isEqualTo("Title");
assertThat(result.contents()).isEqualTo("Contents");
assertThat(block.getUpdatedAt()).isNull();
});
}

@DisplayName("블록의 상태를 수정합니다.")
@Test
void 블록_상태_수정() {
// given
Long blockId = 1L;
when(blockRepository.findById(blockId)).thenReturn(Optional.of(block));

// when
BlockInfoResDto result = blockService.progressUpdate(blockId, "IN_PROGRESS");

// then
assertAll(() -> {
assertThat(result.title()).isEqualTo("Title");
assertThat(result.contents()).isEqualTo("Contents");
assertThat(result.progress()).isEqualTo(Progress.IN_PROGRESS);
});
}

@DisplayName("블록의 상태를 수정하는데 실패합니다.(Progress 문자열 파싱 실패)")
@Test
void 블록_상태_수정_실패() {
// given
Long blockId = 1L;
when(blockRepository.findById(blockId)).thenReturn(Optional.of(block));

// when & then
assertThatThrownBy(() -> blockService.progressUpdate(blockId, "String"))
.isInstanceOf(InvalidProgressException.class);
}

}
Loading

0 comments on commit 11556f8

Please sign in to comment.