Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat] Rest Docs 적용 #73

Merged
merged 7 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,8 @@ out/
### MAC ###
.DS_Store

### rest docs ###
!**/src/main/**/static/docs

### log ###
*.log
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code patch you provided mainly consists of changes to the .gitignore file. It appears that the patch updates the list of files and directories that should be ignored by Git.

The added line !**/src/main/**/static/docs suggests that the rule for ignoring files in the static/docs directory has been reversed. Instead of ignoring it, the patch instructs Git to include these files in the repository. This change is likely intentional to ensure that the REST documentation (static/docs) is now being tracked by Git.

Regarding bug risks and improvement suggestions, it's challenging to provide a comprehensive review without more context or visibility into your project. However, here are a few general considerations:

  1. Ensure the importance of each change: Review all modifications made by the patch to confirm they are necessary for your project. Superfluous changes can introduce unnecessary complexity and increase the risk of errors.

  2. Validate the updated .gitignore rules: Check whether the new patterns added to the .gitignore file accurately match the files and directories you intend to ignore or include.

  3. Assess the handling of sensitive or generated files: Pay attention to how sensitive files (e.g., configuration files containing passwords) and automatically generated files are managed. Make sure they are appropriately excluded and not inadvertently committed to the repository.

  4. Consider using version control for documents cautiously: Including documentation files in the repository can lead to larger repository sizes and possible merge conflicts. Verify that this decision aligns with your project requirements and preferences.

Ultimately, a thorough code review considers the broader context, architecture, and goals of the project. It may be beneficial to involve other team members or utilize code review tools to help analyze the changes in detail and provide specific recommendations for improvement.

39 changes: 38 additions & 1 deletion module-api/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.2.0'
id 'io.spring.dependency-management' version '1.1.4'
id "org.asciidoctor.jvm.convert" version "3.3.2"
}

group = 'com.kernel360'
Expand All @@ -11,7 +12,12 @@ java {
sourceCompatibility = '17'
}

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

configurations {
asciidoctorExt
compileOnly {
extendsFrom annotationProcessor
}
Expand All @@ -30,32 +36,63 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-aop'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
//implementation 'org.springframework.boot:spring-boot-starter-security'

// jasypt
implementation 'com.github.ulisesbocchio:jasypt-spring-boot-starter:3.0.5'

// flyway
implementation 'org.flywaydb:flyway-core'

// postgresql
runtimeOnly 'org.postgresql:postgresql'

//lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

// validataion
implementation 'org.springframework.boot:spring-boot-starter-validation:2.7.4'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'

// test - fixture Monkey
testImplementation 'net.jqwik:jqwik:1.7.3'
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.0")
testImplementation("com.navercorp.fixturemonkey:fixture-monkey-jakarta-validation:1.0.0")

// rest docs
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
asciidoctorExt 'org.springframework.restdocs:spring-restdocs-asciidoctor'
}

tasks.named('test') {
outputs.dir snippetsDir
useJUnitPlatform()
}

tasks.register("prepareKotlinBuildScriptModel") {}
asciidoctor {
dependsOn test
configurations 'asciidoctorExt'
baseDirFollowsSourceFile()
inputs.dir snippetsDir
}

asciidoctor.doFirst {
delete file('src/main/resources/static/docs')
}

task copyDocument(type: Copy) {
dependsOn asciidoctor
from file("build/docs/asciidoc")
into file("src/main/resources/static/docs")
}

bootJar {
dependsOn copyDocument
}

tasks.register("prepareKotlinBuildScriptModel") {}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are some findings and suggestions for the code patch:

  1. Dependency Upgrade: Consider upgrading the dependencies to their latest versions, as newer versions often include bug fixes and improvements.

  2. Configuration Block: Move the ext block inside the java block for better organization.

  3. Output Directory: Add the output directory for Asciidoctor generated snippets (snippetsDir) as an input for the asciidoctor task to track changes properly.

  4. Configuration Name: Change asciidoctorExt to a more descriptive name, like restdocsAsciidoctor, as it is used for Spring REST Docs.

  5. Ensure Clean Slate: Delete the existing src/main/resources/static/docs directory before generating new documentation to avoid any stale files remaining.

  6. Documentation Copy: Create a task named copyDocument that depends on the asciidoctor task. This task copies the generated AsciiDoc files from build/docs/asciidoc to src/main/resources/static/docs.

  7. Jar Creation: Configure the bootJar task to depend on the copyDocument task so that the generated documentation is included in the final JAR file.

Overall, these changes improve the build process and ensure that the latest documentation is included in the application's distribution.

11 changes: 11 additions & 0 deletions module-api/src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
= API Document
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 2
:sectlinks:

include::overview.adoc[]
include::sample-api.adoc[]
// FIXME :: 위 라인 1줄은 추후 삭제 예정입니다
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provided code patch appears to be written in AsciiDoc format and aimed at generating an API documentation book. Here are some observations and suggestions:

  1. It is missing the AsciiDoc header that defines document attributes, such as the title, author, and date. Make sure to add those attributes for a complete documentation.
  2. The "include" statements at the bottom indicate the inclusion of other AsciiDoc files (overview.adoc and sample-api.adoc). Check if these file paths are correct and if the content they provide is relevant and up-to-date.
  3. The line "// FIXME :: 위 라인 1줄은 추후 삭제 예정입니다" seems to be a comment indicating that it should be removed later. It's good to have a plan for clearing out such placeholders after taking the necessary actions. Make sure to remove it when appropriate.
  4. Consider adding more sections to the table of contents (TOC) by adjusting the :toclevels attribute based on the desired depth of the TOC.

Since this is only a small portion of your code, a more comprehensive review might require examining the included AsciiDoc files and the overall structure of your API documentation.

14 changes: 14 additions & 0 deletions module-api/src/docs/asciidoc/overview.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[[overview]]
== Overview

[[overview-host]]
=== Domain

[cols="3,7"]
|===
| 환경 | Domain
| 개발 서버
| `http://washpedia.my-project.life`
| 운영 서버
|
|===
23 changes: 23 additions & 0 deletions module-api/src/docs/asciidoc/sample-api.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// FIXME :: 삭제 예정 파일입니다
== 샘플 API

// [[]] 안에는 a 태그 이름 들어갑니다 (http://localhost:8080/docs/index#공통코드-조회)
[[공통코드-조회]]
=== 공통코드 조회

// [Request] 실제로 API 에서 필요한 내용들만 아래 내용을 추가합니다
==== Request
include::{snippets}/commoncode/get-common-codes/path-parameters.adoc[]
// include::{snippets}/commoncode/get-common-codes/query-parameters.adoc[]
// include::{snippets}/commoncode/get-common-codes/request-fields.adoc[]

===== HTTP Request 예시
include::{snippets}/commoncode/get-common-codes/http-request.adoc[]

// [Response] 실제로 API 에서 필요한 내용들만 아래 내용을 추가합니다
==== Response
// include::{snippets}/commoncode/get-common-codes/response-fields.adoc[]
include::{snippets}/commoncode/get-common-codes/response-fields-value.adoc[]

===== HTTP Response 예시
include::{snippets}/commoncode/get-common-codes/http-response.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@

import com.kernel360.commoncode.service.CommonCodeService;
import com.kernel360.commoncode.dto.CommonCodeDto;
import com.kernel360.response.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

import static com.kernel360.commoncode.code.CommonCodeBusinessCode.GET_COMMON_CODE_SUCCESS;

@RestController
@RequestMapping("/commoncode")
public class CommonCodeController {
Expand All @@ -26,4 +31,13 @@ public List<CommonCodeDto> getCommonCode (@PathVariable String codeName){

return commonCodeService.getCodes(codeName);
}

// FIXME :: 아래 메서드는 추후 삭제 예정입니다
@GetMapping("/test/{codeName}")
public ResponseEntity<ApiResponse> getCommonCode_1 (@PathVariable String codeName){
List<CommonCodeDto> codes = commonCodeService.getCodes(codeName);
ApiResponse<List<CommonCodeDto>> response = ApiResponse.of(GET_COMMON_CODE_SUCCESS, codes);

return new ResponseEntity<>(response, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.kernel360.common;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.kernel360.commoncode.controller.CommonCodeController;
import com.kernel360.commoncode.service.CommonCodeService;
import com.kernel360.member.controller.MemberController;
import com.kernel360.member.service.MemberService;
import com.kernel360.product.controller.ProductController;
import com.kernel360.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest({
CommonCodeController.class,
MemberController.class,
ProductController.class
})
@AutoConfigureRestDocs
public abstract class ControllerTest {

@Autowired
protected MockMvc mockMvc;

@Autowired
protected ObjectMapper objectMapper;

@MockBean
protected CommonCodeService commonCodeService;

@MockBean
protected MemberService memberService;

@MockBean
protected ProductService productService;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.kernel360.common.utils;

import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;

import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;

public interface RestDocumentUtils {

static OperationRequestPreprocessor getDocumentRequest() {
return preprocessRequest(modifyUris().scheme("http")
.host("washpedia.my-project.life")
.removePort(), prettyPrint());
}

static OperationResponsePreprocessor getDocumentResponse() {
return preprocessResponse(prettyPrint());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.kernel360.commoncode.controller;

import com.kernel360.common.ControllerTest;
import com.kernel360.commoncode.dto.CommonCodeDto;
import org.junit.jupiter.api.Test;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.web.servlet.ResultActions;

import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

import static com.kernel360.common.utils.RestDocumentUtils.getDocumentRequest;
import static com.kernel360.common.utils.RestDocumentUtils.getDocumentResponse;
import static org.mockito.BDDMockito.given;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.*;
import static org.springframework.restdocs.request.RequestDocumentation.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

// FIXME :: 삭제 예정 파일입니다
class CommonCodeControllerRestDocsTest extends ControllerTest {

@Test
void commmonCodeSearch() throws Exception {
//given
List<CommonCodeDto> responseList = Arrays.asList(
CommonCodeDto.of(
11L,
"Sedan",
10,
"cartype",
1,
true,
"세단",
LocalDate.now(),
"admin",
null,
null),
CommonCodeDto.of(
12L,
"Hatchback",
10,
"cartype",
2,
true,
"해치백",
LocalDate.now(),
"admin",
null,
null)
);

String pathVariable = "color";
given(commonCodeService.getCodes(pathVariable)).willReturn(responseList);


//when
ResultActions result = this.mockMvc.perform(
get("/commoncode/test/{codeName}", pathVariable));


//then
//pathParameters, pathParameters, requestFields, responseFields는 필요 시 각각 작성
result.andExpect(status().isOk())
.andDo(document(
"commoncode/get-common-codes",
getDocumentRequest(),
getDocumentResponse(),
pathParameters(
parameterWithName("codeName").description("코드명")
),
// queryParameters(
// parameterWithName("size").description("size").optional(),
// parameterWithName("page").description("page").optional()
// ),
// requestFields(
// fieldWithPath("codeName").type(JsonFieldType.STRING).description("코드명"),
// fieldWithPath("upperName").type(JsonFieldType.STRING).description("상위 코드명").optional()
// ),
// responseFields(
// fieldWithPath("codeNo").type(JsonFieldType.NUMBER).description("코드번호"),
// )
responseFields(beneathPath("value").withSubsectionId("value"),
fieldWithPath("codeNo").type(JsonFieldType.NUMBER).description("코드번호"),
fieldWithPath("codeName").type(JsonFieldType.STRING).description("코드명"),
fieldWithPath("upperNo").type(JsonFieldType.NUMBER).description("상위 코드번호").optional(),
fieldWithPath("upperName").type(JsonFieldType.STRING).description("상위 코드명").optional(),
fieldWithPath("sortOrder").type(JsonFieldType.NUMBER).description("정렬순서"),
fieldWithPath("isUsed").type(JsonFieldType.BOOLEAN).description("사용여부"),
fieldWithPath("description").type(JsonFieldType.STRING).description("설명"),
fieldWithPath("createdAt").type(JsonFieldType.STRING).description("생성일시"),
fieldWithPath("createdBy").type(JsonFieldType.STRING).description("생성자"),
fieldWithPath("modifiedAt").type(JsonFieldType.STRING).description("수정일시").optional(),
fieldWithPath("modifiedBy").type(JsonFieldType.STRING).description("수정자").optional()
)
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
===== End Point
{{path}}

===== Path Parameters
|===

|파라미터|설명

{{#parameters}}
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/parameters}}

|===
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
===== Query Parameters
|===

|파라미터|필수값|설명

{{#parameters}}
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{^optional}}true{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/parameters}}

|===
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
===== Request Fields
|===

|필드명|타입|필수값|설명

{{#fields}}
|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{^optional}}true{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
{{/fields}}

|===
Loading
Loading