Skip to content

Commit 5b07a57

Browse files
committed
Merge branch 'main' of https://github.com/YAPP-Github/26th-Web-Team-1-BE into ci/actions-auto
# Conflicts: # build.gradle
2 parents b185da4 + 3df89e4 commit 5b07a57

File tree

15 files changed

+437
-6
lines changed

15 files changed

+437
-6
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: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ 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'
10+
11+
// Jacoco, sonarcloud
612
id 'jacoco'
713
id("org.sonarqube") version "6.2.0.5505"
814
}
@@ -61,9 +67,19 @@ dependencies {
6167
runtimeOnly 'com.mysql:mysql-connector-j'
6268

6369
// Test
64-
testImplementation 'org.springframework.boot:spring-boot-starter-test'
65-
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
6670
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
71+
testImplementation 'org.springframework.boot:spring-boot-starter-test'
72+
testImplementation 'io.rest-assured:rest-assured:5.5.0'
73+
74+
// Documentation
75+
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'
76+
testImplementation 'org.springframework.restdocs:spring-restdocs-restassured'
77+
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.18.2'
78+
testImplementation 'com.epages:restdocs-api-spec-restassured:0.18.2'
79+
}
80+
81+
bootJar {
82+
dependsOn("openapi3")
6783
}
6884

6985
tasks.named('test', Test) {
@@ -72,9 +88,30 @@ tasks.named('test', Test) {
7288
finalizedBy tasks.jacocoTestReport
7389
}
7490

75-
tasks.named('asciidoctor') {
76-
inputs.dir snippetsDir
77-
dependsOn test
91+
generateSwaggerUI {
92+
dependsOn("openapi3")
93+
94+
delete(fileTree("src/main/resources/static/docs/") {
95+
exclude(".gitkeep")
96+
})
97+
copy {
98+
from("build/resources/main/static/docs/")
99+
into("src/main/resources/static/docs/")
100+
}
101+
}
102+
103+
openapi3 {
104+
servers = [ // 서버 상황에 맞춰 추가 예정
105+
{
106+
url = "http://localhost:8080"
107+
description = "Local Server"
108+
}
109+
]
110+
title = "백암 순대 API" // 수정 예정
111+
description = "백암 순대 API" // 수정 예정
112+
version = "0.0.1"
113+
format = "yaml"
114+
outputDirectory = "build/resources/main/static/docs"
78115
}
79116

80117
tasks.named('jacocoTestReport', JacocoReport) {

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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package baegam.sundae;
2+
3+
import jakarta.persistence.EntityManager;
4+
import java.util.List;
5+
import org.junit.jupiter.api.extension.BeforeEachCallback;
6+
import org.junit.jupiter.api.extension.ExtensionContext;
7+
import org.springframework.context.ApplicationContext;
8+
import org.springframework.test.context.junit.jupiter.SpringExtension;
9+
import org.springframework.transaction.support.TransactionTemplate;
10+
11+
public class DatabaseCleaner implements BeforeEachCallback {
12+
13+
@Override
14+
public void beforeEach(ExtensionContext extensionContext) {
15+
ApplicationContext context = SpringExtension.getApplicationContext(extensionContext);
16+
cleanup(context);
17+
}
18+
19+
private void cleanup(ApplicationContext context) {
20+
EntityManager em = context.getBean(EntityManager.class);
21+
TransactionTemplate transactionTemplate = context.getBean(TransactionTemplate.class);
22+
23+
transactionTemplate.execute(action -> {
24+
em.clear();
25+
truncateTables(em);
26+
return null;
27+
});
28+
}
29+
30+
private void truncateTables(EntityManager em) {
31+
em.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate();
32+
for (String tableName : findTableNames(em)) {
33+
em.createNativeQuery("TRUNCATE TABLE %s RESTART IDENTITY".formatted(tableName)).executeUpdate();
34+
}
35+
em.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate();
36+
}
37+
38+
@SuppressWarnings("unchecked")
39+
private List<String> findTableNames(EntityManager em) {
40+
String tableNameSelectQuery = """
41+
SELECT TABLE_NAME
42+
FROM INFORMATION_SCHEMA.TABLES
43+
WHERE TABLE_SCHEMA = 'PUBLIC'
44+
""";
45+
return em.createNativeQuery(tableNameSelectQuery).getResultList();
46+
}
47+
}

src/test/java/baegam/sundae/SundaeApplicationTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@ class SundaeApplicationTests {
99
@Test
1010
void contextLoads() {
1111
}
12-
1312
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package baegam.sundae.controller;
2+
3+
import baegam.sundae.DatabaseCleaner;
4+
import io.restassured.RestAssured;
5+
import io.restassured.builder.RequestSpecBuilder;
6+
import io.restassured.filter.Filter;
7+
import io.restassured.filter.log.RequestLoggingFilter;
8+
import io.restassured.filter.log.ResponseLoggingFilter;
9+
import io.restassured.specification.RequestSpecification;
10+
import java.util.List;
11+
import org.junit.jupiter.api.BeforeEach;
12+
import org.junit.jupiter.api.extension.ExtendWith;
13+
import org.springframework.boot.test.context.SpringBootTest;
14+
import org.springframework.boot.test.web.server.LocalServerPort;
15+
16+
@ExtendWith(DatabaseCleaner.class)
17+
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
18+
public class BaseControllerTest {
19+
20+
private static final List<Filter> SPEC_FILTERS = List.of(new RequestLoggingFilter(), new ResponseLoggingFilter());
21+
22+
@LocalServerPort
23+
private int port;
24+
25+
private RequestSpecification spec;
26+
27+
@BeforeEach
28+
void setEnvironment() {
29+
RestAssured.port = port;
30+
spec = new RequestSpecBuilder()
31+
.addFilters(SPEC_FILTERS)
32+
.build();
33+
}
34+
35+
protected final RequestSpecification given() {
36+
return RestAssured.given(spec);
37+
}
38+
}
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+
}

0 commit comments

Comments
 (0)