Skip to content

Commit

Permalink
chore: restdocs를 위한 설정 (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
birdieHyun committed Oct 18, 2023
1 parent 7d0e87f commit f3e25a6
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 0 deletions.
42 changes: 42 additions & 0 deletions src/docs/asciidoc/common.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
== Host

|===
| 환경 | Host

| Production
| `api.yonsei-golf.co.kr`
|===

[[HTTP-STATUS-CODE]]
== HTTP status codes

|===
| 상태 코드 | 설명

| `200 OK`
| 성공

| `400 Bad Request`
| 잘못된 요청

| `401 Unauthorized`
| 비인증 상태

| `403 Forbidden`
| 권한 거부

| `404 Not Found`
| 존재하지 않는 요청 리소스

| `500 Internal Server Error`
| 서버 에러
|===

=== Common Response
include::{snippets}/common-docs/custom-response-fields.adoc[]

=== Response Example
include::{snippets}/common-docs/http-response.adoc[]

=== Response Error Example
include::{snippets}/error-docs/http-response.adoc[]
11 changes: 11 additions & 0 deletions src/test/java/yonseigolf/server/ServerApplicationTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package yonseigolf.server;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class ServerApplicationTests {
@Test
void contextLoads() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package yonseigolf.server.docs.common;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.restdocs.payload.PayloadSubsectionExtractor;
import org.springframework.test.web.servlet.ResultActions;
import yonseigolf.server.docs.utils.CustomResponseFieldsSnippet;
import yonseigolf.server.docs.utils.RestDocsSupport;

import java.util.Arrays;
import java.util.Map;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static yonseigolf.server.docs.utils.ApiDocumentUtils.getDocumentRequest;
import static yonseigolf.server.docs.utils.ApiDocumentUtils.getDocumentResponse;

public class CommonDocumentationTest extends RestDocsSupport {

@Override
protected Object initController() {
return new CustomController();
}


public static CustomResponseFieldsSnippet customResponseFields(String type,
PayloadSubsectionExtractor<?> subsectionExtractor,
Map<String, Object> attributes, FieldDescriptor... descriptors) {
return new CustomResponseFieldsSnippet(type, subsectionExtractor, Arrays.asList(descriptors), attributes
, true);
}

@DisplayName("모든 응답은 응답 코드, 데이터, 메시지를 포함한다.")
@Test
public void commons() throws Exception {
ResultActions result = this.mockMvc.perform(
RestDocumentationRequestBuilders.get("/docs/common")
.accept(MediaType.APPLICATION_JSON)
);

result.andExpect(status().isOk())
.andDo(document("common-docs",
getDocumentRequest(),
getDocumentResponse(),
customResponseFields("custom-response", null, null,
subsectionWithPath("data").description("데이터"),
fieldWithPath("code").type(JsonFieldType.NUMBER).description("결과 코드"),
fieldWithPath("message").type(JsonFieldType.STRING).description("결과 메시지"),
fieldWithPath("status").type(JsonFieldType.STRING).description("상태")
)
));

}

@DisplayName("오류가 발생한 응답은 응답 코드, 데이터, 메시지를 포함한다.")
@Test
public void errorCommons() throws Exception {
ResultActions result = this.mockMvc.perform(
RestDocumentationRequestBuilders.get("/docs/error")
.accept(MediaType.APPLICATION_JSON)
);

result.andExpect(status().isOk())
.andDo(document("error-docs",
getDocumentRequest(),
getDocumentResponse(),
customResponseFields("custom-response", null, null,
subsectionWithPath("data").description("데이터"),
fieldWithPath("code").type(JsonFieldType.NUMBER).description("결과 코드"),
fieldWithPath("message").type(JsonFieldType.STRING).description("결과 메시지"),
fieldWithPath("status").type(JsonFieldType.STRING).description("상태")
)
));

}

}
34 changes: 34 additions & 0 deletions src/test/java/yonseigolf/server/docs/common/CustomController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package yonseigolf.server.docs.common;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import yonseigolf.server.util.CustomResponse;

@RestController
@RequestMapping("/docs")
public class CustomController {

@GetMapping("/common")
public ResponseEntity<CustomResponse> showCommon() {

return ResponseEntity
.ok()
.body(new CustomResponse<>(
"success",
200,
"요청 성공 메시지","yonseiGolf"));
}

@GetMapping("/error")
public ResponseEntity<CustomResponse> showError() {

return ResponseEntity
.ok()
.body(new CustomResponse<>(
"fail",
400,
"잘못된 요청입니다.","yonseiGolf"));
}
}
23 changes: 23 additions & 0 deletions src/test/java/yonseigolf/server/docs/utils/ApiDocumentUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package yonseigolf.server.docs.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 ApiDocumentUtils {
static OperationRequestPreprocessor getDocumentRequest() {

return preprocessRequest(
modifyUris()
.scheme("https")
.host("api.yonsei-golf.co.kr")
.removePort(),
prettyPrint());
}

static OperationResponsePreprocessor getDocumentResponse() {

return preprocessResponse(prettyPrint());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package yonseigolf.server.docs.utils;


import org.springframework.http.MediaType;
import org.springframework.restdocs.operation.Operation;
import org.springframework.restdocs.payload.AbstractFieldsSnippet;
import org.springframework.restdocs.payload.FieldDescriptor;
import org.springframework.restdocs.payload.PayloadSubsectionExtractor;

import java.io.IOException;
import java.util.List;
import java.util.Map;

public class CustomResponseFieldsSnippet extends AbstractFieldsSnippet {
public CustomResponseFieldsSnippet(String type, PayloadSubsectionExtractor<?> subsectionExtractor,
List<FieldDescriptor> descriptors, Map<String, Object> attributes,
boolean ignoreUndocumentedFields) {
super(type, descriptors, attributes, ignoreUndocumentedFields,
subsectionExtractor);
}

@Override
protected MediaType getContentType(Operation operation) {

return operation.getResponse().getHeaders().getContentType();
}

@Override
protected byte[] getContent(Operation operation) throws IOException {

return operation.getResponse().getContent();
}
}
40 changes: 40 additions & 0 deletions src/test/java/yonseigolf/server/docs/utils/RestDocsSupport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package yonseigolf.server.docs.utils;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.web.PageableHandlerMethodArgumentResolver;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;

@ExtendWith(RestDocumentationExtension.class)
public abstract class RestDocsSupport {

protected MockMvc mockMvc;
protected ObjectMapper objectMapper = new ObjectMapper();

@BeforeEach
void setup(RestDocumentationContextProvider provider) {

this.mockMvc = MockMvcBuilders.standaloneSetup(initController())
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.apply(documentationConfiguration(provider))
.build();
objectMapper.registerModule(new JavaTimeModule());
}

public static MockMvc setControllerAdvice(Object initController, Object controllerAdvice) {

return MockMvcBuilders.standaloneSetup(initController)
.setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
.setControllerAdvice(controllerAdvice)
.build();
}

protected abstract Object initController();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
==== Response Fields
|===
|Path|Description

{{#fields}}

|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}

{{/fields}}

|===
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
==== Request Fields
|===
|Path|Type|Optional|Description

{{#fields}}

|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{#optional}}O{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}

{{/fields}}

|===
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
==== Response Fields
|===
|Path|Type|Optional|Description

{{#fields}}

|{{#tableCellContent}}`+{{path}}+`{{/tableCellContent}}
|{{#tableCellContent}}`+{{type}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{#optional}}O{{/optional}}{{/tableCellContent}}
|{{#tableCellContent}}{{description}}{{/tableCellContent}}

{{/fields}}

|===

0 comments on commit f3e25a6

Please sign in to comment.