Skip to content

Commit

Permalink
Tht chat server 18 openapi3 환경 구축 (#19)
Browse files Browse the repository at this point in the history
* log : add disconnect case log

* feat : openapi3
  • Loading branch information
thalals authored Apr 1, 2024
1 parent 3c995f1 commit a6a1705
Show file tree
Hide file tree
Showing 7 changed files with 285 additions and 3 deletions.
60 changes: 59 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
buildscript {
ext {
restdocsApiSpecVersion = '0.18.2' // restdocsApiSpecVersion 버전 변수 설정
}
}

plugins {
id 'java'
id 'org.springframework.boot' version '3.1.1-SNAPSHOT'
id 'io.spring.dependency-management' version '1.1.0'

//restdocs-api-spec → restdocs extension 포함함
id 'com.epages.restdocs-api-spec' version "${restdocsApiSpecVersion}"

//swagger generator 플러그인 추가
id 'org.hidetake.swagger.generator' version '2.19.2'

}

group = 'com.example'
Expand Down Expand Up @@ -50,6 +63,20 @@ dependencies {
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

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

//restdocs-api-spec 의존성 추가
testImplementation 'com.epages:restdocs-api-spec-mockmvc:' + restdocsApiSpecVersion

// https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'

testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'

implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

}

// querydsl QClass 파일 생성 위치를 지정
Expand All @@ -59,7 +86,7 @@ sourceSets {
main.java.srcDirs += [ querydslDir ]
}

tasks.withType(JavaCompile) {
tasks.withType(JavaCompile).configureEach {
options.getGeneratedSourceOutputDirectory().set(file(querydslDir))
}

Expand All @@ -70,3 +97,34 @@ clean {
tasks.named('test') {
useJUnitPlatform()
}

// Task 및 설정 추가
// GenerateSwaggerUI 태스크가, openapi3 task 를 의존하도록 설정
tasks.withType(GenerateSwaggerUI) {
dependsOn 'openapi3'
}

//openAPI 작성 자동화를 위해 패키징 전에 openapi3 테스크 선실행을 유발
bootJar{
dependsOn(':openapi3')

//기존 파일 삭제했다가, build 에 출력한 json 정적파일 복사 (안해도 됨 → local 확인용)
delete file('src/main/resources/static/docs/')
copy {
from "build/resources/main/static/docs"
into "src/main/resources/static/docs/"

}
}

openapi3 {
server = "http://3.34.157.62/"
title = "THT Chat API 문서"
description = "Spring REST Docs with SwaggerUI."
version = "0.0.1"
outputFileNamePrefix = 'open-api-3.0.1'
format = 'json'

// /static/docs/open-api-3.0.1.json 생성 → jar 파일만 배포할 예정이기에 build 에 출력
outputDirectory = 'build/resources/main/static/docs'
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ public Message<?> preSend(Message<?> message, MessageChannel channel) {

jwtUtils.validateToken(authorization);

log.info("성공");
log.info("connect");
}

if (StompCommand.DISCONNECT == accessor.getCommand()) {

log.info("disconnect");
}
return message;
}
Expand Down
16 changes: 15 additions & 1 deletion src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
spring:
config:
import: security/Server-Security/application-security.yaml
import: security/Server-Security/application-security.yaml

thymeleaf:
prefix: classpath:static/
suffix: .html
cache: false

springdoc:
swagger-ui:
url: /docs/open-api-3.0.1.json
path: /docs/swagger
groups-order: DESC # path, query, body, response 순으로 출력
tags-sorter: alpha # 태그를 알파벳 순으로 정렬
default-consumes-media-type: application/json;charset=UTF-8
default-produces-media-type: application/json;charset=UTF-8
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.chatserver.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplicationExtensionsKt;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
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 org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.socket.messaging.WebSocketStompClient;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;

@AutoConfigureMockMvc
@AutoConfigureRestDocs
@MockBean(JpaMetamodelMappingContext.class)
@Import({WebSocketConfig.class, StompHandler.class, JwtUtils.class, StompExceptionHandler.class})
@ExtendWith({RestDocumentationExtension.class})
public abstract class ControllerTestConfig {

@Autowired
protected WebApplicationContext ctx;

@Autowired
protected ObjectMapper mapper;

protected static ObjectMapper objectMapper;


protected static MockMvc mockMvc;


@BeforeEach
void setUp(final RestDocumentationContextProvider restDocumentation) {

objectMapper = mapper;

mockMvc = MockMvcBuilders.webAppContextSetup(ctx)
.apply(documentationConfiguration(restDocumentation))
.addFilters(new CharacterEncodingFilter("UTF-8", true))
.alwaysDo(print())
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.example.chatserver.documentation;

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.epages.restdocs.apispec.Schema;
import com.example.chatserver.config.ControllerTestConfig;
import com.example.chatserver.controller.ChatController;
import com.example.chatserver.facade.ChatFacade;
import com.example.chatserver.fixture.ChatRequestFixture;
import com.google.api.client.json.Json;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders;
import org.springframework.restdocs.payload.JsonFieldType;

import static com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName;
import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.*;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(ChatController.class)
class ChatControllerDocumentation extends ControllerTestConfig {

@MockBean
ChatFacade chatFacade;

@Test
void documentStompEndpoint() throws Exception {

mockMvc.perform(
get("/websocket-endpoint")
.header("Authorization", testToken())

)
.andDo(document("stomp connect endpoint",
preprocessRequest(prettyPrint()),
preprocessResponse(prettyPrint()),
resource(
ResourceSnippetParameters.builder()
.tag("소켓 연결 정보")
.description("stomp connect endpoint url 주소")
.build()
)
));

}

private static String testToken() {
return "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRob3JpemF0aW9uIiwidXNlclV1aWQiOiI0NmQ5NjBmNTY1LTJhZTItNDE5My05NmZkLTMxYjZhODc1NzYxMSIsInJvbGUiOiJOT1JNQUwiLCJleHAiOjQ4NjQwMDQxODZ9.VGC6EJmCBdevtfSQspqsdu42FFl25dSBSHvHue6mAhE";
}

// @Test
// void documentStompPub() throws Exception {
//
// String requestBody = objectMapper.writeValueAsString(ChatRequestFixture.make());
//
// mockMvc.perform(post("/pub/chat/{chat-room-no}",1)
// .content(requestBody)
// .header("Authorization", testToken()))
// .andDo(document("stomp publish(전송 - send) url",
// preprocessRequest(prettyPrint()),
// preprocessResponse(prettyPrint()),
// resource(
// ResourceSnippetParameters.builder()
// .tag("소켓 연결 정보")
// .description("stomp publish (send) url 주소")
// .pathParameters(
// parameterWithName("chat-room-no").description("채팅방 idx")
// )
// .requestFields(
// fieldWithPath("sender").type(JsonFieldType.STRING).description(""),
// fieldWithPath("senderUuid").type(JsonFieldType.STRING).description("보내는 사람 user uuid"),
// fieldWithPath("imgUrl").type(JsonFieldType.STRING).description("보내는 사람 프로필 img url"),
// fieldWithPath("msg").type(JsonFieldType.STRING).description("전송 메세지 내용")
// )
// .requestFields()
// .responseFields()
// .requestSchema(Schema.schema("ChatRequest"))
// .build()
// )
// ));
// }
//
// @Test
// void documentStompSub() throws Exception {
//
// mockMvc.perform(post("/sub/chat/{chat-room-no}",1)
// .content("stomp 구독(subscribe) url")
// .header("Authorization", testToken()))
// .andDo(document("stomp subscribe(구독) url",
// preprocessRequest(prettyPrint()),
// preprocessResponse(prettyPrint()),
// resource(
// ResourceSnippetParameters.builder()
// .tag("소켓 연결 정보")
// .description("stomp subscribe url 주소")
// .pathParameters(
// parameterWithName("chat-room-no").description("채팅방 idx")
// )
// .requestFields()
// .responseFields(
// fieldWithPath("chatIdx").type(JsonFieldType.STRING).description(""),
// fieldWithPath("sender").type(JsonFieldType.STRING).description(""),
// fieldWithPath("senderUuid").type(JsonFieldType.STRING).description("보내는 사람 user uuid"),
// fieldWithPath("msg").type(JsonFieldType.STRING).description("보내는 사람 프로필 img url"),
// fieldWithPath("imgUrl").type(JsonFieldType.STRING).description("전송 메세지 내용"),
// fieldWithPath("dateTime").type(JsonFieldType.STRING).description("전송 메세지 내용")
// )
// .build()
// )
// ));
// }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.chatserver.fixture;

import com.example.chatserver.stomp.ChatRequest;

public class ChatRequestFixture{
private static final String sender = "보내는 사람 이름";
private static final String senderUuid = "보내는 사람 user uuid";
private static final String imgUrl = "보내는 사람 프로필 img url";
private static final String msg = "전송 메세지 내용";

public static ChatRequest make() {
return new ChatRequest(sender, senderUuid, imgUrl, msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.chatserver.fixture;

import com.example.chatserver.stomp.ChatResponse;

import java.time.LocalDateTime;

public class ChatResponseFixture {

private static final String chatIdx = "채팅 메세지 고유 id";
private static final String sender = "메세지를 보낸 사람 이름";
private static final String senderUuid = "메세지를 보낸 사람 user uuid";
private static final String msg = "받은 메세지 내용";
private static final String imgUrl = "받은 이미지 url";
private static final LocalDateTime dateTime = LocalDateTime.now();

public static ChatResponse make() {
return new ChatResponse(chatIdx, sender, senderUuid, msg, imgUrl, dateTime);
}
}

0 comments on commit a6a1705

Please sign in to comment.