Skip to content

Commit 3df89e4

Browse files
authored
Merge pull request #3 from YAPP-Github/test/documentation
[Test] Rest Docs + Swagger UI를 이용한 문서화 환경 세팅
2 parents 99d2d24 + fdc380f commit 3df89e4

File tree

9 files changed

+304
-3
lines changed

9 files changed

+304
-3
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,6 @@ out/
3535

3636
### VS Code ###
3737
.vscode/
38+
39+
### Rest Docs Documentation ###
40+
/src/main/resources/static/docs/openapi3.yaml

build.gradle

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ plugins {
33
id 'org.springframework.boot' version '3.5.0'
44
id 'io.spring.dependency-management' version '1.1.7'
55
id 'org.asciidoctor.jvm.convert' version '3.3.2'
6+
7+
// Rest Docs & Swagger UI
8+
id 'com.epages.restdocs-api-spec' version '0.18.2'
9+
id 'org.hidetake.swagger.generator' version '2.18.2'
610
}
711

812
group = 'baegam'
@@ -46,14 +50,45 @@ dependencies {
4650
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
4751
testImplementation 'org.springframework.boot:spring-boot-starter-test'
4852
testImplementation 'io.rest-assured:rest-assured:5.5.0'
53+
54+
// Documentation
55+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
56+
testImplementation 'org.springframework.restdocs:spring-restdocs-restassured'
57+
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2'
58+
testImplementation 'com.epages:restdocs-api-spec-restassured:0.18.2'
59+
}
60+
61+
bootJar {
62+
dependsOn("openapi3")
4963
}
5064

5165
tasks.named('test', Test) {
5266
outputs.dir snippetsDir
5367
useJUnitPlatform()
5468
}
5569

56-
tasks.named('asciidoctor') {
57-
inputs.dir snippetsDir
58-
dependsOn test
70+
generateSwaggerUI {
71+
dependsOn("openapi3")
72+
73+
delete(fileTree("src/main/resources/static/docs/") {
74+
exclude(".gitkeep")
75+
})
76+
copy {
77+
from("build/resources/main/static/docs/")
78+
into("src/main/resources/static/docs/")
79+
}
80+
}
81+
82+
openapi3 {
83+
servers = [ // 서버 상황에 맞춰 추가 예정
84+
{
85+
url = "http://localhost:8080"
86+
description = "Local Server"
87+
}
88+
]
89+
title = "백암 순대 API" // 수정 예정
90+
description = "백암 순대 API" // 수정 예정
91+
version = "0.0.1"
92+
format = "yaml"
93+
outputDirectory = "build/resources/main/static/docs"
5994
}

src/main/resources/application.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
springdoc:
2+
swagger-ui:
3+
path: /docs/swagger
4+
url: /docs/openapi3.yaml
5+
disable-swagger-default-url: true
6+
filter: true
7+
persist-authorization: true
8+
display-request-duration: true
9+
default-model-expand-depth: 10

src/main/resources/static/docs/.gitkeep

Whitespace-only changes.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package baegam.sundae.document;
2+
3+
import io.restassured.RestAssured;
4+
import io.restassured.builder.RequestSpecBuilder;
5+
import io.restassured.specification.RequestSpecification;
6+
import org.junit.jupiter.api.BeforeEach;
7+
import org.junit.jupiter.api.extension.ExtendWith;
8+
import org.mockito.junit.jupiter.MockitoExtension;
9+
import org.springframework.boot.test.context.SpringBootTest;
10+
import org.springframework.boot.test.web.server.LocalServerPort;
11+
import org.springframework.restdocs.RestDocumentationContextProvider;
12+
import org.springframework.restdocs.RestDocumentationExtension;
13+
import org.springframework.restdocs.restassured.RestAssuredRestDocumentation;
14+
import org.springframework.restdocs.restassured.RestAssuredRestDocumentationConfigurer;
15+
import org.springframework.restdocs.restassured.RestDocumentationFilter;
16+
17+
@ExtendWith({RestDocumentationExtension.class, MockitoExtension.class})
18+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
19+
public abstract class BaseDocumentTest {
20+
21+
// @MockitoBean 을 이용하여 Service Layer 및 특정 객체 Mocking
22+
23+
@LocalServerPort
24+
private int port;
25+
26+
private RequestSpecification spec;
27+
28+
29+
@BeforeEach
30+
void setEnvironment(RestDocumentationContextProvider restDocumentation) {
31+
RestAssured.port = port;
32+
RestAssuredRestDocumentationConfigurer webConfigurer =
33+
RestAssuredRestDocumentation.documentationConfiguration(restDocumentation);
34+
spec = new RequestSpecBuilder()
35+
.addFilter(webConfigurer)
36+
.build();
37+
}
38+
39+
protected final RestDocsRequest request() {
40+
return new RestDocsRequest();
41+
}
42+
43+
protected final RestDocsResponse response() {
44+
return new RestDocsResponse();
45+
}
46+
47+
protected final RestDocsFilterBuilder document(String identifierPrefix, int statusCode) {
48+
return new RestDocsFilterBuilder(identifierPrefix, Integer.toString(statusCode));
49+
}
50+
51+
protected RequestSpecification given(RestDocumentationFilter documentationFilter) {
52+
return RestAssured.given(spec)
53+
.filter(documentationFilter);
54+
}
55+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package baegam.sundae.document;
2+
3+
import static com.epages.restdocs.apispec.RestAssuredRestDocumentationWrapper.document;
4+
5+
import com.epages.restdocs.apispec.ResourceSnippetParametersBuilder;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import org.springframework.http.HttpHeaders;
9+
import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor;
10+
import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor;
11+
import org.springframework.restdocs.operation.preprocess.Preprocessors;
12+
import org.springframework.restdocs.restassured.RestDocumentationFilter;
13+
import org.springframework.restdocs.snippet.Snippet;
14+
15+
public class RestDocsFilterBuilder {
16+
17+
private static final String IDENTIFIER_DELIMITER = "/";
18+
private static final OperationRequestPreprocessor REQUEST_PREPROCESSOR = Preprocessors.preprocessRequest(
19+
Preprocessors.prettyPrint(),
20+
Preprocessors.modifyHeaders()
21+
.remove(HttpHeaders.HOST)
22+
.remove(HttpHeaders.CONTENT_LENGTH)
23+
);
24+
private static final OperationResponsePreprocessor RESPONSE_PREPROCESSOR = Preprocessors.preprocessResponse(
25+
Preprocessors.prettyPrint(),
26+
Preprocessors.modifyHeaders()
27+
.remove(HttpHeaders.TRANSFER_ENCODING)
28+
.remove(HttpHeaders.DATE)
29+
.remove(HttpHeaders.CONNECTION)
30+
.remove(HttpHeaders.CONTENT_LENGTH)
31+
);
32+
33+
private final String identifier;
34+
private final List<Snippet> snippets;
35+
private ResourceSnippetParametersBuilder resourceBuilder;
36+
37+
private RestDocsFilterBuilder(String identifier) {
38+
this.identifier = identifier;
39+
this.resourceBuilder = new ResourceSnippetParametersBuilder();
40+
this.snippets = new ArrayList<>();
41+
}
42+
43+
public RestDocsFilterBuilder(String identifierPrefix, String identifier) {
44+
this(identifierPrefix + IDENTIFIER_DELIMITER + identifier);
45+
}
46+
47+
public RestDocsFilterBuilder request(RestDocsRequest request) {
48+
resourceBuilder = request.getResourceBuilder();
49+
snippets.addAll(request.getSnippets());
50+
return this;
51+
}
52+
53+
public RestDocsFilterBuilder response(RestDocsResponse response) {
54+
snippets.addAll(response.getSnippets());
55+
return this;
56+
}
57+
58+
public RestDocumentationFilter build() {
59+
return document(
60+
identifier,
61+
resourceBuilder,
62+
REQUEST_PREPROCESSOR,
63+
RESPONSE_PREPROCESSOR,
64+
snippets.toArray(Snippet[]::new)
65+
);
66+
}
67+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package baegam.sundae.document;
2+
3+
import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies;
4+
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
5+
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
6+
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
7+
import static org.springframework.restdocs.request.RequestDocumentation.queryParameters;
8+
9+
import com.epages.restdocs.apispec.ResourceSnippetParametersBuilder;
10+
import java.util.LinkedList;
11+
import java.util.List;
12+
import org.springframework.restdocs.cookies.CookieDescriptor;
13+
import org.springframework.restdocs.headers.HeaderDescriptor;
14+
import org.springframework.restdocs.payload.FieldDescriptor;
15+
import org.springframework.restdocs.request.ParameterDescriptor;
16+
import org.springframework.restdocs.snippet.Snippet;
17+
18+
public class RestDocsRequest {
19+
20+
private final ResourceSnippetParametersBuilder resourceBuilder;
21+
private final List<Snippet> snippets;
22+
23+
public RestDocsRequest() {
24+
this.resourceBuilder = new ResourceSnippetParametersBuilder();
25+
this.snippets = new LinkedList<>();
26+
}
27+
28+
public RestDocsRequest tag(Tag tag) {
29+
resourceBuilder.tag(tag.getDisplayName());
30+
return this;
31+
}
32+
33+
public RestDocsRequest summary(String summary) {
34+
resourceBuilder.summary(summary);
35+
return this;
36+
}
37+
38+
public RestDocsRequest description(String description) {
39+
resourceBuilder.description(description);
40+
return this;
41+
}
42+
43+
public RestDocsRequest pathParameter(ParameterDescriptor... descriptors) {
44+
snippets.add(pathParameters(descriptors));
45+
return this;
46+
}
47+
48+
public RestDocsRequest queryParameter(ParameterDescriptor... descriptors) {
49+
snippets.add(queryParameters(descriptors));
50+
return this;
51+
}
52+
53+
public RestDocsRequest requestHeader(HeaderDescriptor... descriptors) {
54+
snippets.add(requestHeaders(descriptors));
55+
return this;
56+
}
57+
58+
public RestDocsRequest requestCookie(CookieDescriptor... descriptors) {
59+
snippets.add(requestCookies(descriptors));
60+
return this;
61+
}
62+
63+
public RestDocsRequest requestBodyField(FieldDescriptor... descriptors) {
64+
snippets.add(requestFields(descriptors));
65+
return this;
66+
}
67+
68+
public ResourceSnippetParametersBuilder getResourceBuilder() {
69+
return resourceBuilder;
70+
}
71+
72+
public List<Snippet> getSnippets() {
73+
return List.copyOf(snippets);
74+
}
75+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package baegam.sundae.document;
2+
3+
import static org.springframework.restdocs.cookies.CookieDocumentation.responseCookies;
4+
import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
5+
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
6+
7+
import java.util.LinkedList;
8+
import java.util.List;
9+
import org.springframework.restdocs.cookies.CookieDescriptor;
10+
import org.springframework.restdocs.headers.HeaderDescriptor;
11+
import org.springframework.restdocs.payload.FieldDescriptor;
12+
import org.springframework.restdocs.snippet.Snippet;
13+
14+
public class RestDocsResponse {
15+
16+
private final List<Snippet> snippets;
17+
18+
public RestDocsResponse() {
19+
this.snippets = new LinkedList<>();
20+
}
21+
22+
public RestDocsResponse responseHeader(HeaderDescriptor... descriptors) {
23+
snippets.add(responseHeaders(descriptors));
24+
return this;
25+
}
26+
27+
public RestDocsResponse responseCookie(CookieDescriptor... descriptors) {
28+
snippets.add(responseCookies(descriptors));
29+
return this;
30+
}
31+
32+
public RestDocsResponse responseBodyField(FieldDescriptor... descriptors) {
33+
snippets.add(responseFields(descriptors));
34+
return this;
35+
}
36+
37+
public List<Snippet> getSnippets() {
38+
return List.copyOf(snippets);
39+
}
40+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package baegam.sundae.document;
2+
3+
public enum Tag {
4+
5+
MEMBER_API("Member API"), // Example (추후 삭제)
6+
;
7+
8+
private final String displayName;
9+
10+
Tag(String displayName) {
11+
this.displayName = displayName;
12+
}
13+
14+
public String getDisplayName() {
15+
return displayName;
16+
}
17+
}

0 commit comments

Comments
 (0)