Commit 0411354e authored by hyunchu's avatar hyunchu

check out

parents
Pipeline #128 failed with stages
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="corretto-11" project-jdk-type="JavaSDK" />
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/../pushservice.iml" filepath="$PROJECT_DIR$/../pushservice.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
#!/bin/sh
JAVA_PATH=/usr/local/sbin/java
HERE_PATH=/svc/app/
SERVICE_NAME=pushservice
PID_FILE=${HERE_PATH}/pid/${SERVICE_NAME}.pid
JAR_NAME=${SERVICE_NAME}.jar
## GC config
GC_LOG_PATH=/logs/gcLog/${SERVICE_NAME}/gc.log
JAVA_OPTION="${JAVA_OPTION} -Xms512M -Xmx512M -XX:MaxMetaspaceSize=256m"
JAVA_OPTION="${JAVA_OPTION} -XX:+UseConcMarkSweepGC"
JAVA_OPTION="${JAVA_OPTION} -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintGCDetails"
JAVA_OPTION="${JAVA_OPTION} -Xloggc:${GC_LOG_PATH}"
JAVA_OPTION="${JAVA_OPTION} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=9 -XX:GCLogFileSize=20m"
## Spring config
SPRING_OPTION="${SPRING_OPTION} -Dspring.config.location=../config/ -Dspring.profiles.active=dev"
case $1 in
start)
echo ""
PCOUNT=`ps -ef |grep ${SERVICE_NAME} |grep -v grep |wc -l`
if [ $PCOUNT -ne 0 ]
then
echo "already run ${SERVICE_NAME} . pid:`cat $PID_FILE`"
else
${JAVA_PATH} -classpath .:../config ${SPRING_OPTION} ${JAVA_OPTION} -DAppName=${SERVICE_NAME} -jar ${JAR_NAME} 1>&2 &
echo "$!" > ${PID_FILE}
echo "START ${SERVICE_NAME}. pid:$!"
ps -ef |grep `cat ${PID_FILE}` |grep -v grep
fi
echo ""
;;
stop)
echo ""
kill -9 `cat ${PID_FILE}`
echo "STOP ${SERVICE_NAME}. pid:`cat ${PID_FILE}`"
echo ""
;;
status)
echo ""
echo "STATUS ${SERVICE_NAME}."
ps -ef |grep ${SERVICE_NAME} |grep -v grep |grep `cat ${PID_FILE}`
echo ""
;;
*)
echo "./run.sh start|stop|status"
esac
server:
port: 5447
spring:
application:
name: push-service
profiles:
- dev
datasource:
hikari:
pool-name: pushservice-dbcp
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://10.101.102.129:3306/solar
#driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
#url: jdbc:log4jdbc:mariadb://210.97.40.115:3306/monitoring?allowMultiQueries=true
#url: jdbc:mariadb://echall-batch-test-db.caiafcuktsql.ap-northeast-2.rds.amazonaws.com/solar?allowMultiQueries=true
username: hevitsvr
password: we&there100
#password: us&them100
kafka:
listener:
concurrency: 2
consumer:
bootstrap-servers: 10.101.102.215:9092
group-id: PUSH
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
topic: SOURCE-PUSH-TN
mvc:
throw-exception-if-no-handler-found: true
mybatis:
mapper-locations:
- classpath*:mapper/*.xml
# type-aliases-package:
# net.herit.sample.model.entity
logging:
call:
multiline: true
except-query-log: true
config: file:../config/logback-spring-dev.xml
feign:
push:
host: https://piggybank-test.heviton.com:18443
uri: /api/v2/push/send
thread-pool:
core-size: 100 # consume한 메시지를 처리할 스레드 개수(최소값)
max-size: 200 # consume한 메시지를 처리할 스레드 개수(최대값)
queue-capacity: 100000 # core-size 만큼 스레드가 모두 사용될 때 이 후에 들어오는 작업을 대기시킬 큐 개수(큐까지 모두 차면 그 때 max-size 만큼 스레드가 증가
run-delay-ms: 8 # 각각의 메시지처리 스레드가 실행되기 전 대기시간
management: # prometheus, grafana 연동을 위한 설정
endpoint:
gateway:
enable: true
endpoints:
web:
base-path: "/common/push-service/actuator"
exposure:
include: "*"
metrics:
tags:
application: ${spring.application.name}
\ No newline at end of file
server:
port: 8080
spring:
application:
name: push-service
profiles:
- local
datasource:
hikari:
pool-name: pushservice-dbcp
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://10.101.102.129:3306/solar
#driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
#url: jdbc:log4jdbc:mariadb://210.97.40.115:3306/monitoring?allowMultiQueries=true
#url: jdbc:mariadb://echall-batch-test-db.caiafcuktsql.ap-northeast-2.rds.amazonaws.com/solar?allowMultiQueries=true
username: hevitsvr
password: we&there100
#password: us&them100
kafka:
listener:
concurrency: 2
consumer:
bootstrap-servers: 10.101.102.215:9092
group-id: PUSH
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
topic: SOURCE-PUSH-TN
mvc:
throw-exception-if-no-handler-found: true
mybatis:
mapper-locations:
- classpath*:mapper/*.xml
# type-aliases-package:
# net.herit.sample.model.entity
logging:
call:
multiline: true
except-query-log: true
config: file:./config/logback-spring-local.xml
feign:
push:
host: http://localhost:5944
#host: https://piggybank-test.heviton.com:18443
uri: /api/v2/push/send
thread-pool:
core-size: 100 # consume한 메시지를 처리할 스레드 개수(최소값)
max-size: 200 # consume한 메시지를 처리할 스레드 개수(최대값)
queue-capacity: 100000 # core-size 만큼 스레드가 모두 사용될 때 이 후에 들어오는 작업을 대기시킬 큐 개수(큐까지 모두 차면 그 때 max-size 만큼 스레드가 증가
run-delay-ms: 8 # 각각의 메시지처리 스레드가 실행되기 전 대기시간
management: # prometheus, grafana 연동을 위한 설정
endpoint:
gateway:
enable: true
endpoints:
web:
base-path: "/common/push-service/actuator"
exposure:
include: "*"
metrics:
tags:
application: ${spring.application.name}
\ No newline at end of file
server:
port: 5447
spring:
application:
name: push-service
profiles:
- prod
datasource:
hikari:
pool-name: pushservice-dbcp
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://hevit-monitoring-v2.caiafcuktsql.ap-northeast-2.rds.amazonaws.com/solar
username: hevitsvr
password: us&them100
kafka:
listener:
concurrency: 2
consumer:
bootstrap-servers: gatherEx-01:9092,gatherEx-02:9092,gatherEx-03:9092
group-id: PUSH
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
topic: SOURCE-PUSH-TN
mvc:
throw-exception-if-no-handler-found: true
mybatis:
mapper-locations:
- classpath*:mapper/*.xml
# type-aliases-package:
# net.herit.sample.model.entity
logging:
call:
multiline: true
except-query-log: true
config: file:../config/logback-spring-prod.xml
feign:
push:
host: https://piggybank.heviton.com:18443
uri: /api/v2/push/send
thread-pool:
core-size: 50 # consume한 메시지를 처리할 스레드 개수(최소값)
max-size: 200 # consume한 메시지를 처리할 스레드 개수(최대값)
queue-capacity: 100000 # core-size 만큼 스레드가 모두 사용될 때 이 후에 들어오는 작업을 대기시킬 큐 개수(큐까지 모두 차면 그 때 max-size 만큼 스레드가 증가
run-delay-ms: 8 # 각각의 메시지처리 스레드가 실행되기 전 대기시간
management: # prometheus, grafana 연동을 위한 설정
endpoint:
gateway:
enable: true
endpoints:
web:
base-path: "/common/push-service/actuator"
exposure:
include: "*"
metrics:
tags:
application: ${spring.application.name}
\ No newline at end of file
server:
port: 8080
spring:
application:
name: push-service
profiles:
- local
datasource:
hikari:
pool-name: pushservice-dbcp
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://10.101.102.129:3306/solar
#driver-class-name: net.sf.log4jdbc.sql.jdbcapi.DriverSpy
#url: jdbc:log4jdbc:mariadb://210.97.40.115:3306/monitoring?allowMultiQueries=true
#url: jdbc:mariadb://echall-batch-test-db.caiafcuktsql.ap-northeast-2.rds.amazonaws.com/solar?allowMultiQueries=true
username: hevitsvr
password: we&there100
#password: us&them100
kafka:
listener:
concurrency: 2
consumer:
bootstrap-servers: 10.101.102.215:9092
group-id: PUSH
auto-offset-reset: earliest
key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
topic: SOURCE-PUSH-TN
mvc:
throw-exception-if-no-handler-found: true
mybatis:
mapper-locations:
- classpath*:mapper/*.xml
# type-aliases-package:
# net.herit.sample.model.entity
logging:
call:
multiline: true
except-query-log: true
config: file:./config/logback-spring-local.xml
feign:
push:
host: http://localhost:8080
uri: /api/1.0/feign/get
management: # prometheus, grafana 연동을 위한 설정
endpoint:
gateway:
enable: true
endpoints:
web:
base-path: "/common/push-service/actuator"
exposure:
include: "*"
metrics:
tags:
application: ${spring.application.name}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<property name="LOG_ROOT" value="/logs" />
<property name="LOG_FOLDER" value="pushservice" />
<property name="OMS_LOG_PATH" value="${LOG_ROOT}/omsLog/${LOG_FOLDER}" />
<property name="CALL_LOG_PATH" value="${LOG_ROOT}/callLog/${LOG_FOLDER}" />
<property name="ERROR_LOG_PATH" value="${LOG_ROOT}/errorLog/${LOG_FOLDER}" />
<property name="STD_LOG_PATH" value="${LOG_ROOT}/std/${LOG_FOLDER}" />
<property name="OMS_LEVEL" value="INFO" />
<property name="CALL_LEVEL" value="DEBUG" />
<property name="STD_LEVEL" value="DEBUG" />
<property name="ERROR_LEVEL" value="ERROR" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="STD_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${STD_LOG_PATH}/std.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${STD_LOG_PATH}/%d{yyyy/MM/dd}/std.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="OMS_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${OMS_LOG_PATH}/oms.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultOmsLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${OMS_LOG_PATH}/%d{yyyy/MM/dd}/oms.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="CALL_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${CALL_LOG_PATH}/call.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${CALL_LOG_PATH}/%d{yyyy/MM/dd}/call.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="ERROR_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${ERROR_LOG_PATH}/error.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout" />
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${ERROR_LOG_PATH}/%d{yyyy/MM/dd}/error.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="net.herit.svcplatform.pushservice.commons.logger.OmsLogger" level="${OMS_LEVEL}">
<appender-ref ref="OMS_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.CallLogger" level="${CALL_LEVEL}">
<appender-ref ref="CALL_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.ErrorLogger" level="${ERROR_LEVEL}">
<appender-ref ref="ERROR_LOG_FILE" />
</logger>
<root>
<appender-ref ref="STDOUT" />
<appender-ref ref="STD_LOG_FILE" />
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<property name="LOG_ROOT" value="/logs" />
<property name="LOG_FOLDER" value="pushservice" />
<property name="OMS_LOG_PATH" value="${LOG_ROOT}/omsLog/${LOG_FOLDER}" />
<property name="CALL_LOG_PATH" value="${LOG_ROOT}/callLog/${LOG_FOLDER}" />
<property name="ERROR_LOG_PATH" value="${LOG_ROOT}/errorLog/${LOG_FOLDER}" />
<property name="STD_LOG_PATH" value="${LOG_ROOT}/std/${LOG_FOLDER}" />
<property name="OMS_LEVEL" value="INFO" />
<property name="CALL_LEVEL" value="DEBUG" />
<property name="STD_LEVEL" value="INFO"/>
<property name="ERROR_LEVEL" value="ERROR" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="STD_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${STD_LOG_PATH}/std.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${STD_LOG_PATH}/%d{yyyy/MM/dd}/std.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="OMS_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${OMS_LOG_PATH}/oms.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultOmsLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${OMS_LOG_PATH}/%d{yyyy/MM/dd}/oms.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="CALL_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${CALL_LOG_PATH}/call.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${CALL_LOG_PATH}/%d{yyyy/MM/dd}/call.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="ERROR_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${ERROR_LOG_PATH}/error.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout" />
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${ERROR_LOG_PATH}/%d{yyyy/MM/dd}/error.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>7</maxHistory>
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="net.herit.svcplatform.pushservice.commons.logger.OmsLogger" level="${OMS_LEVEL}">
<appender-ref ref="OMS_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.CallLogger" level="${CALL_LEVEL}">
<appender-ref ref="CALL_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.ErrorLogger" level="${ERROR_LEVEL}">
<appender-ref ref="ERROR_LOG_FILE" />
</logger>
<root>
<appender-ref ref="STDOUT" />
<appender-ref ref="STD_LOG_FILE" />
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<property name="LOG_ROOT" value="/logs" />
<property name="LOG_FOLDER" value="pushservice" />
<property name="OMS_LOG_PATH" value="${LOG_ROOT}/omsLog/${LOG_FOLDER}" />
<property name="CALL_LOG_PATH" value="${LOG_ROOT}/callLog/${LOG_FOLDER}" />
<property name="ERROR_LOG_PATH" value="${LOG_ROOT}/errorLog/${LOG_FOLDER}" />
<property name="STD_LOG_PATH" value="${LOG_ROOT}/std/${LOG_FOLDER}" />
<property name="OMS_LEVEL" value="INFO" />
<property name="CALL_LEVEL" value="DEBUG" />
<property name="STD_LEVEL" value="DEBUG" />
<property name="ERROR_LEVEL" value="ERROR" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="STD_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${STD_LOG_PATH}/std.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${STD_LOG_PATH}/%d{yyyy/MM/dd}/std.log.%i</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>1000MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>[%date{ISO8601}][%-4relative][%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<appender name="OMS_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${OMS_LOG_PATH}/oms.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultOmsLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${OMS_LOG_PATH}/%d{yyyy/MM/dd}/oms.log.%i</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="CALL_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${CALL_LOG_PATH}/call.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout"/>
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${CALL_LOG_PATH}/%d{yyyy/MM/dd}/call.log.%i</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>50</maxHistory>
<totalSizeCap>1000MB</totalSizeCap>
</rollingPolicy>
</appender>
<appender name="ERROR_LOG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${ERROR_LOG_PATH}/error.log</file>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="net.herit.common.logger.layout.DefaultCallLogLayout" />
</encoder>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${ERROR_LOG_PATH}/%d{yyyy/MM/dd}/error.log.%i</fileNamePattern>
<maxFileSize>50MB</maxFileSize>
<maxHistory>50</maxHistory>
<totalSizeCap>1000MB</totalSizeCap>
</rollingPolicy>
</appender>
<logger name="net.herit.svcplatform.pushservice.commons.logger.OmsLogger" level="${OMS_LEVEL}">
<appender-ref ref="OMS_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.CallLogger" level="${CALL_LEVEL}">
<appender-ref ref="CALL_LOG_FILE" />
</logger>
<logger name="net.herit.svcplatform.pushservice.commons.logger.ErrorLogger" level="${ERROR_LEVEL}">
<appender-ref ref="ERROR_LOG_FILE" />
</logger>
<root>
<appender-ref ref="STDOUT" />
<appender-ref ref="STD_LOG_FILE" />
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
<property name="STD_LEVEL" value="info" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
</encoder>
</appender>
<root level="${STD_LEVEL}">
<appender-ref ref="STDOUT" />
</root>
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version> <!-- lookup parent from repository -->
</parent>
<groupId>net.herit.svcplatform</groupId>
<artifactId>pushservice</artifactId>
<version>0.0.3</version>
<name>pushservice</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<repository.url>http://10.101.101.192:28081/repository/hura-repo/</repository.url>
<maven.build.timestamp.format>yyyyMMdd_HHmmss</maven.build.timestamp.format>
<install.local.dir>${basedir}/target/${project.name}</install.local.dir>
<project.build.directory>${basedir}/target</project.build.directory>
<lib.dir>lib</lib.dir>
<package.jar.name>${project.artifactId}</package.jar.name>
<!--suppress UnresolvedMavenProperty -->
<build.number>${default.buildnumber}.default</build.number>
<created.group>avengers.herit</created.group>
</properties>
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<profile.id>local</profile.id>
<package.install.path>${basedir}/target</package.install.path>
<package.jar.version>${version}</package.jar.version>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<profile.id>dev</profile.id>
<package.install.path>${basedir}/target</package.install.path>
<package.jar.version>${version}</package.jar.version>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<profile.id>prod</profile.id>
<package.install.path>${basedir}/target</package.install.path>
<package.jar.version>${version}</package.jar.version>
</properties>
</profile>
</profiles>
<build>
<finalName>${package.jar.name}</finalName>
<plugins>
<!-- create version.info -->
<plugin>
<groupId>ru.yaal.maven</groupId>
<artifactId>write-text-files-maven-plugin</artifactId>
<version>1.1</version>
<configuration>
<charset>UTF-8</charset>
<files>
<file>
<path>${package.install.path}/${project.artifactId}_${package.jar.version}.info</path>
<lines>
<line>===PACKAGE PROPERTIES ===</line>
<line>Name: ${project.name}</line>
<line>ArtifactId: ${project.artifactId}</line>
<line>Version: ${package.jar.version}</line>
<line>PackageFileName: ${package.jar.name}.${packaging}</line>
<line>Description: ${description}</line>
<line>ProfileId: ${profile.id}</line>
<line>BuildNumber: ${build.number}</line>
<!--suppress UnresolvedMavenProperty -->
<line>BuildTime: ${current.time}</line>
<line>BuildBy: ${created.group}</line>
</lines>
</file>
</files>
</configuration>
<executions>
<execution>
<id>write-text-files</id>
<phase>prepare-package</phase>
<goals>
<goal>write-text-files</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- For Spring RestDocs -->
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.8</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<!-- For Spring RestDocs -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.outputDirectory}/static/api/1.0/rookie/docs/</outputDirectory>
<resources>
<resource>
<directory>${project.build.directory}/generated-docs</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>timestamp-property</id>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>current.time</name>
<pattern>yyyy-MM-dd HH:mm:ss</pattern>
<timeZone>Asia/Seoul</timeZone>
</configuration>
</execution>
<execution>
<id>buildtime-property</id>
<goals>
<goal>timestamp-property</goal>
</goals>
<configuration>
<name>default.buildnumber</name>
<pattern>yyyyMMdd.HHmmss</pattern>
<timeZone>Asia/Seoul</timeZone>
</configuration>
</execution>
</executions>
</plugin>
<!-- For Spring SonarQube -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.6</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<!-- Connect Herit Nexus -->
<repositories>
<repository>
<id>public</id>
<url>${repository.url}</url>
<releases>
</releases>
<snapshots>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<url>${repository.url}</url>
<releases>
</releases>
<snapshots>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2020.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Common Logger ( In Herit Nexus ) -->
<dependency>
<groupId>net.herit.common.logger</groupId>
<artifactId>herit-common-lib-logger</artifactId>
<version>0.0.3-RELEASE</version>
</dependency>
<!-- Exclution tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Use undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- h2 데이터베이스는 샘플용 데이터베이스로 MyBatis에서 DataSource관련 에러가 나지 않도록 하기 위한 용도입니다.
실제 개발 시작시, 1. h2database 의존성 삭제 2. mariaDB 의존성 주입 ( 아래 주석 해제 ) 3. config/application-*.yml의
데이터베이스 정보 업데이트 -->
<!-- Optional, H2 Embedded Database
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency> -->
<!-- Optional, mariaDB -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- API 문서화, Spring RestDocs -->
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<!-- JWT 토큰 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>3.0.0</version>
</dependency>
<!-- kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson
feign jackson en/decoder -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>9.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<!-- 모니터링 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
</dependencies>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="web" name="Web">
<configuration>
<webroots />
<sourceRoots>
<root url="file://$MODULE_DIR$/src/main/java" />
<root url="file://$MODULE_DIR$/src/main/resources" />
</sourceRoots>
</configuration>
</facet>
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: net.herit.common.logger:herit-common-lib-logger:0.0.3-RELEASE" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-classic:1.2.3" level="project" />
<orderEntry type="library" name="Maven: ch.qos.logback:logback-core:1.2.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-web:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-logging:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-to-slf4j:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.apache.logging.log4j:log4j-api:2.13.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:jul-to-slf4j:1.7.30" level="project" />
<orderEntry type="library" name="Maven: jakarta.annotation:jakarta.annotation-api:1.3.5" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.27" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-json:2.4.3" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.module:jackson-module-parameter-names:2.11.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-web:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-webmvc:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-undertow:2.4.3" level="project" />
<orderEntry type="library" name="Maven: io.undertow:undertow-core:2.2.4.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.logging:jboss-logging:3.4.1.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.xnio:xnio-api:3.8.0.Final" level="project" />
<orderEntry type="library" name="Maven: org.wildfly.common:wildfly-common:1.5.2.Final" level="project" />
<orderEntry type="library" name="Maven: org.wildfly.client:wildfly-client-config:1.0.1.Final" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.jboss.xnio:xnio-nio:3.8.0.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.threads:jboss-threads:3.1.0.Final" level="project" />
<orderEntry type="library" name="Maven: io.undertow:undertow-servlet:2.2.4.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec:2.0.1.Final" level="project" />
<orderEntry type="library" name="Maven: io.undertow:undertow-websockets-jsr:2.2.4.Final" level="project" />
<orderEntry type="library" name="Maven: org.jboss.spec.javax.websocket:jboss-websocket-api_1.1_spec:2.0.0.Final" level="project" />
<orderEntry type="library" name="Maven: jakarta.servlet:jakarta.servlet-api:4.0.4" level="project" />
<orderEntry type="library" name="Maven: org.glassfish:jakarta.el:3.0.3" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.springframework.boot:spring-boot-devtools:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-autoconfigure:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-validation:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.hibernate.validator:hibernate-validator:6.1.7.Final" level="project" />
<orderEntry type="library" name="Maven: jakarta.validation:jakarta.validation-api:2.0.2" level="project" />
<orderEntry type="library" name="Maven: org.projectlombok:lombok:1.18.18" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-starter-test:2.4.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test:2.4.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.boot:spring-boot-test-autoconfigure:2.4.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.jayway.jsonpath:json-path:2.4.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:json-smart:2.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.minidev:accessors-smart:1.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.ow2.asm:asm:5.0.4" level="project" />
<orderEntry type="library" name="Maven: jakarta.xml.bind:jakarta.xml.bind-api:2.3.3" level="project" />
<orderEntry type="library" name="Maven: jakarta.activation:jakarta.activation-api:1.2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.assertj:assertj-core:3.18.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest:2.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter:5.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-api:5.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.apiguardian:apiguardian-api:1.1.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.opentest4j:opentest4j:1.2.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-commons:1.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-params:5.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.jupiter:junit-jupiter-engine:5.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.junit.platform:junit-platform-engine:1.7.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:3.6.28" level="project" />
<orderEntry type="library" name="Maven: net.bytebuddy:byte-buddy:1.10.20" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.10.20" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:3.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-junit-jupiter:3.6.28" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.skyscreamer:jsonassert:1.5.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.vaadin.external.google:android-json:0.0.20131108.vaadin1" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.3.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework:spring-test:5.3.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.xmlunit:xmlunit-core:2.7.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-configuration-processor:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-jdbc:2.4.3" level="project" />
<orderEntry type="library" name="Maven: com.zaxxer:HikariCP:3.4.5" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jdbc:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.mybatis.spring.boot:mybatis-spring-boot-autoconfigure:2.1.3" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis:3.5.5" level="project" />
<orderEntry type="library" name="Maven: org.mybatis:mybatis-spring:2.0.5" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.mariadb.jdbc:mariadb-java-client:2.7.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter-openfeign:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-starter:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-context:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-rsa:1.0.9.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcpkix-jdk15on:1.64" level="project" />
<orderEntry type="library" name="Maven: org.bouncycastle:bcprov-jdk15on:1.64" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-openfeign-core:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-aop:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.9.6" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form-spring:3.8.0" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign.form:feign-form:3.8.0" level="project" />
<orderEntry type="library" name="Maven: commons-fileupload:commons-fileupload:1.4" level="project" />
<orderEntry type="library" name="Maven: commons-io:commons-io:2.2" level="project" />
<orderEntry type="library" name="Maven: org.springframework.cloud:spring-cloud-commons:3.0.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-crypto:5.4.5" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-core:10.10.1" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-slf4j:10.10.1" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger2:2.9.2" level="project" />
<orderEntry type="library" name="Maven: io.swagger:swagger-annotations:1.5.20" level="project" />
<orderEntry type="library" name="Maven: io.swagger:swagger-models:1.5.20" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-annotations:2.11.4" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-spi:2.9.2" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-core:2.9.2" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-schema:2.9.2" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-common:2.9.2" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-spring-web:2.9.2" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:20.0" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml:classmate:1.5.1" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.30" level="project" />
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework.plugin:spring-plugin-metadata:1.2.0.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.mapstruct:mapstruct:1.2.0.Final" level="project" />
<orderEntry type="library" name="Maven: io.springfox:springfox-swagger-ui:2.9.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.restdocs:spring-restdocs-mockmvc:2.0.5.RELEASE" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: javax.servlet:javax.servlet-api:4.0.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.springframework.restdocs:spring-restdocs-core:2.0.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: io.jsonwebtoken:jjwt:0.9.1" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-databind:2.11.4" level="project" />
<orderEntry type="library" name="Maven: com.fasterxml.jackson.core:jackson-core:2.11.4" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-runtime:3.0.0" level="project" />
<orderEntry type="library" name="Maven: com.sun.activation:jakarta.activation:1.2.2" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:jaxb-core:3.0.0" level="project" />
<orderEntry type="library" name="Maven: org.glassfish.jaxb:txw2:2.3.3" level="project" />
<orderEntry type="library" name="Maven: com.sun.istack:istack-commons-runtime:4.0.0" level="project" />
<orderEntry type="library" name="Maven: org.springframework.kafka:spring-kafka:2.6.6" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-messaging:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-tx:5.3.4" level="project" />
<orderEntry type="library" name="Maven: org.springframework.retry:spring-retry:1.3.1" level="project" />
<orderEntry type="library" name="Maven: javax.annotation:javax.annotation-api:1.3.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.kafka:kafka-clients:2.6.0" level="project" />
<orderEntry type="library" name="Maven: com.github.luben:zstd-jni:1.4.4-7" level="project" />
<orderEntry type="library" name="Maven: org.lz4:lz4-java:1.7.1" level="project" />
<orderEntry type="library" name="Maven: org.xerial.snappy:snappy-java:1.1.7.3" level="project" />
<orderEntry type="library" name="Maven: io.github.openfeign:feign-jackson:9.3.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.11" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-starter-actuator:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator-autoconfigure:2.4.3" level="project" />
<orderEntry type="library" name="Maven: org.springframework.boot:spring-boot-actuator:2.4.3" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-core:1.6.4" level="project" />
<orderEntry type="library" name="Maven: org.hdrhistogram:HdrHistogram:2.1.12" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.latencyutils:LatencyUtils:2.0.3" level="project" />
<orderEntry type="library" name="Maven: io.micrometer:micrometer-registry-prometheus:1.6.4" level="project" />
<orderEntry type="library" name="Maven: io.prometheus:simpleclient_common:0.9.0" level="project" />
<orderEntry type="library" name="Maven: io.prometheus:simpleclient:0.9.0" level="project" />
</component>
</module>
\ No newline at end of file
<script>
function ready(callbackFunc) {
if (document.readyState !== 'loading') {
// Document is already ready, call the callback directly
callbackFunc();
} else if (document.addEventListener) {
// All modern browsers to register DOMContentLoaded
document.addEventListener('DOMContentLoaded', callbackFunc);
} else {
// Old IE browsers
document.attachEvent('onreadystatechange', function () {
if (document.readyState === 'complete') {
callbackFunc();
}
});
}
}
function openPopup(event) {
const target = event.target;
if (target.className !== "popup") {
return;
}
event.preventDefault();
const screenX = event.screenX;
const screenY = event.screenY;
window.open(target.href, target.text, `left=${screenX}, top=${screenY}, width=500, height=600, status=no, menubar=no, toolbar=no, resizable=no`);
}
ready(function () {
const el = document.getElementById("content");
el.addEventListener("click", event => openPopup(event), false);
});
</script>
\ No newline at end of file
= Natural REST API Guide
Herit ;
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 4
:sectlinks:
:operation-curl-request-title: Example request
:operation-http-response-title: Example response
[[overview]]
operation::feignPost[snippets='http-request,request-headers,request-fields,http-response,response-fields,response-headers']
\ No newline at end of file
= Rookie API Guide
Herit ;
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 4
:sectlinks:
:operation-curl-request-title: Example request
:operation-http-response-title: Example response
[[overview]]
= 개요
[[overview-http-verbs]]
== HTTP 상태 코드
[cols="2,2,6"]
|===
| HTTP응답코드 | 결과 코드 | 설명
| `200`
| 0
| 성공
| `404`
| 1
| not support : ${uri}
| `400`
| 2
| invalid parameter
| `400`
| 3
| duplicated contents
| `500`
| 4
| Exception
| `401`
| 101
| invalid oauth token
| `401`
| 102
| invalid auth token
| `401`
| 103
| not exist user
| `401`
| 104
| wrong password
| `401`
| 105
| wrong group id
| `401`
| 106
| already exist user
|===
[[overview-errors]]
== 오류
NOTE: 에러 응답이 발생했을 때 (상태 코드 >= 400), 본문에 해당 문제를 기술한 JSON 객체가 담겨있다.
[[resources]]
= 리소스
[[resources-common]]
== 공통 파라메터
[[resources-common-header]]
==== 공통헤더
include::{snippets}/health-check-controller-tests/health-check/request-headers.adoc[]
[[resources-api]]
== 서비스 API
[[resources-sample-api]]
==== API..
NOTE: API 준비중입니다.
[[resources-health-check]]
==== healthCheck API
include::{snippets}/health-check-controller-tests/health-check/http-request.adoc[]
include::{snippets}/health-check-controller-tests/health-check/curl-request.adoc[]
include::{snippets}/health-check-controller-tests/health-check/http-response.adoc[]
include::{snippets}/health-check-controller-tests/health-check/response-fields.adoc[]
[[resources-json-web-token]]
==== Server Json Web Token 발급 API
include::{snippets}/jwt-controller-tests/request-server-access-token/http-request.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/curl-request.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/http-response.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/request-fields.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/response-fields.adoc[]
[[resources-json-web-token]]
==== Client Json Web Token 발급 API
include::{snippets}/jwt-controller-tests/request-client-access-token/http-request.adoc[]
include::{snippets}/jwt-controller-tests/request-client-access-token/curl-request.adoc[]
include::{snippets}/jwt-controller-tests/request-client-access-token/http-response.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/request-fields.adoc[]
include::{snippets}/jwt-controller-tests/request-client-access-token/response-fields.adoc[]
[[resources-json-web-token]]
==== Server Json Web Token 재발급 API
include::{snippets}/jwt-controller-tests/renewal-server-access-token/http-request.adoc[]
include::{snippets}/jwt-controller-tests/renewal-server-access-token/curl-request.adoc[]
include::{snippets}/jwt-controller-tests/renewal-server-access-token/http-response.adoc[]
include::{snippets}/jwt-controller-tests/request-server-access-token/request-fields.adoc[]
include::{snippets}/jwt-controller-tests/renewal-server-access-token/response-fields.adoc[]
[[resources-json-post]]
==== Json Post 테스트 API
include::{snippets}/log-test-restdocs-tests/post-controller/http-request.adoc[]
include::{snippets}/log-test-restdocs-tests/post-controller/curl-request.adoc[]
include::{snippets}/log-test-restdocs-tests/post-controller/request-fields.adoc[]
include::{snippets}/log-test-restdocs-tests/post-controller/http-response.adoc[]
include::{snippets}/log-test-restdocs-tests/post-controller/response-fields.adoc[]
[[resources-multiupload]]
==== multiupload 테스트 API
include::{snippets}/log-test-restdocs-tests/multiupload/http-request.adoc[]
include::{snippets}/log-test-restdocs-tests/multiupload/curl-request.adoc[]
include::{snippets}/log-test-restdocs-tests/multiupload/request-parameters.adoc[]
include::{snippets}/log-test-restdocs-tests/multiupload/request-parts.adoc[]
include::{snippets}/log-test-restdocs-tests/multiupload/http-response.adoc[]
include::{snippets}/log-test-restdocs-tests/multiupload/response-fields.adoc[]
[[resources-json-get]]
==== Json Get 테스트 API
include::{snippets}/log-test-restdocs-tests/get-controller/http-request.adoc[]
include::{snippets}/log-test-restdocs-tests/get-controller/curl-request.adoc[]
include::{snippets}/log-test-restdocs-tests/get-controller/request-fields.adoc[]
include::{snippets}/log-test-restdocs-tests/get-controller/http-response.adoc[]
include::{snippets}/log-test-restdocs-tests/get-controller/response-fields.adoc[]
[[resources-form-urlencoded]]
==== form-urlencoded Get 테스트 API
include::{snippets}/log-test-restdocs-tests/get-form-url-encoded-controller/http-request.adoc[]
include::{snippets}/log-test-restdocs-tests/get-form-url-encoded-controller/curl-request.adoc[]
include::{snippets}/log-test-restdocs-tests/get-form-url-encoded-controller/request-parameters.adoc[]
include::{snippets}/log-test-restdocs-tests/get-form-url-encoded-controller/http-response.adoc[]
include::{snippets}/log-test-restdocs-tests/get-form-url-encoded-controller/response-fields.adoc[]
[[resources-healthcheck-test]]
==== health-check 테스트 API
include::{snippets}/log-test-restdocs-tests/health-check/http-request.adoc[]
include::{snippets}/log-test-restdocs-tests/health-check/curl-request.adoc[]
include::{snippets}/log-test-restdocs-tests/health-check/http-response.adoc[]
include::{snippets}/log-test-restdocs-tests/health-check/response-fields.adoc[]
[[resources-healthcheck-test]]
==== feign-post 테스트 API
include::{snippets}/feign-controller-restdocs-tests/post-mapping-test/http-request.adoc[]
include::{snippets}/feign-controller-restdocs-tests/post-mapping-test/curl-request.adoc[]
include::{snippets}/feign-controller-restdocs-tests/post-mapping-test/request-fields.adoc[]
include::{snippets}/feign-controller-restdocs-tests/post-mapping-test/http-response.adoc[]
include::{snippets}/feign-controller-restdocs-tests/post-mapping-test/response-fields.adoc[]
[[resources-healthcheck-test]]
==== feign-get 테스트 API
include::{snippets}/feign-controller-restdocs-tests/get-mapping-test/http-request.adoc[]
include::{snippets}/feign-controller-restdocs-tests/get-mapping-test/curl-request.adoc[]
include::{snippets}/feign-controller-restdocs-tests/get-mapping-test/request-fields.adoc[]
include::{snippets}/feign-controller-restdocs-tests/get-mapping-test/http-response.adoc[]
include::{snippets}/feign-controller-restdocs-tests/get-mapping-test/response-fields.adoc[]
package net.herit.svcplatform.pushservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
package net.herit.svcplatform.pushservice.commons.auth;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import lombok.RequiredArgsConstructor;
@Aspect
@Component
@RequiredArgsConstructor
public class AuthAop {
private final CallLogger callLogger;
private final TokenCheck tokenCheck;
private final JwtCheck jwtCheck;
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void PostMapping() {};
@Around("PostMapping()")
public Object authCheck(ProceedingJoinPoint pjp) throws Throwable {
callLogger.info("AuthAop => Start");
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
String jwt = httpServletRequest.getHeader("Authorization");
if (isNonAuth(pjp)) return pjp.proceed();
// TODO Header 에 Authorization 정보가 있는 경우에만 jwt 확인 -> 확인 필요
if (jwt != null && !jwt.equals("")) {
jwtCheck.jwtCheck(httpServletRequest, jwt);
} else {
tokenCheck.tokenCheck(pjp.getArgs());
}
return pjp.proceed();
}
private boolean isNonAuth(ProceedingJoinPoint pjp) {
boolean result = false;
if (pjp.getSignature() instanceof MethodSignature) {
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
NonAuth nonAuth = methodSignature.getMethod().getAnnotation(NonAuth.class);
result = nonAuth != null;
}
return result;
}
}
package net.herit.svcplatform.pushservice.commons.auth;
import org.springframework.stereotype.Service;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class AuthService {
private final CallLogger callLogger;
public boolean authTokenCheck(String loginId, String authToken, String svcId, String terminalId) {
callLogger.info("authTokenCheck 로직 추가가 필요합니다. :: net.herit.svcplatform.pushservice.commons.auth.AuthService");
return true;
}
public boolean jwtCheck() {
callLogger.info("jwtCheck 로직 추가가 필요합니다. :: net.herit.svcplatform.pushservice.commons.auth.AuthService");
return true;
}
}
package net.herit.svcplatform.pushservice.commons.auth;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.AopException;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.util.JwtUtil;
import lombok.RequiredArgsConstructor;
// TODO DB 작업은 따로 되어있지 않으므로, DB 인증과정 추가 필요
@Component
@RequiredArgsConstructor
public class JwtCheck {
private final JwtUtil jwtUtil;
private final CallLogger callLogger;
private final AuthService authService;
void jwtCheck(HttpServletRequest request, String jwt) {
callLogger.info("net.herit.svcplatform.pushservice.commons.auth.JwtCheck.jwtCheck() start");
try {
Claims claims = jwtUtil.decodeAccessToken(jwt);
callLogger.info("claims: {}", claims);
// TODO 클라이언트 인증만 허용하도록 설정
if (!claims.get("typ").equals("1")) {
throw new AopException(HttpResponseStatus.INVALID_JWT_TOKEN);
}
} catch (Exception e) {
throw new AopException(HttpResponseStatus.INVALID_JWT_TOKEN);
}
if (!authService.jwtCheck()) {
throw new AopException(HttpResponseStatus.INVALID_JWT_TOKEN);
}
}
}
package net.herit.svcplatform.pushservice.commons.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 인증이 필요하지 않은 API 호출시 인증을 생략하기 위한 @interface
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface NonAuth {
String message() default "";
}
package net.herit.svcplatform.pushservice.commons.auth;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import net.herit.svcplatform.pushservice.commons.dto.model.AccountModel;
import net.herit.svcplatform.pushservice.commons.dto.request.AuthRequest;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.AopException;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import lombok.RequiredArgsConstructor;
@Component
@RequiredArgsConstructor
public class TokenCheck {
private final CallLogger callLogger;
private final AuthService authService;
void tokenCheck(Object[] args) {
callLogger.info("net.herit.svcplatform.pushservice.commons.auth.TokenCheck.tokenCheck() start");
boolean result = false;
for (Object arg : args) {
if (arg instanceof AuthRequest) {
result = true;
AuthRequest authRequest = (AuthRequest) arg;
AccountModel account = authRequest.getAccount();
if (account == null) {
throw new AopException(HttpResponseStatus.NOT_EXIST_USER);
}
String loginId = account.getLoginId();
String authToken = account.getAuthToken();
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder
.getRequestAttributes()).getRequest();
String svcId = httpServletRequest.getHeader("X-SERVICE-ID");
String terminalId = httpServletRequest.getHeader("X-TERMINAL-ID");
if (loginId == null || authToken == null || svcId == null || terminalId == null) {
throw new AopException(HttpResponseStatus.NOT_EXIST_USER);
}
boolean authCheck = authService.authTokenCheck(loginId, authToken, svcId, terminalId);
if (!authCheck) {
throw new AopException(HttpResponseStatus.NOT_EXIST_USER);
}
}
}
if (! result) {
throw new AopException(HttpResponseStatus.NOT_EXIST_USER);
}
callLogger.info("net.herit.svcplatform.pushservice.commons.auth.TokenCheck.tokenCheck end");
}
}
package net.herit.svcplatform.pushservice.commons.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import net.herit.svcplatform.pushservice.commons.filter.HttpWrapperFilter;
@Configuration
public class FilterConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean<OncePerRequestFilter> getFilterRegistrationBean() {
return new FilterRegistrationBean<>(new HttpWrapperFilter());
}
}
package net.herit.svcplatform.pushservice.commons.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import net.herit.svcplatform.pushservice.commons.logger.interceptor.LoggerInterceptor;
import net.herit.svcplatform.pushservice.commons.validation.header.HeaderCheckInterceptor;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
@Autowired
private LoggerInterceptor loggerInterceptor;
@Autowired
private HeaderCheckInterceptor headerCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
String excludeError = "/error";
String excludeRestDocs = "/api/1.0/*/docs/*.html";
String[] excludePathPatterns = {
"/swagger-ui.html",
"/v2/api-docs",
"/webjars/**",
"/swagger-resources/**",
"/",
"/csrf",
"/favicon.ico",
"/healthcheck"
};
String feignApi = "/feign/test/*";
registry.addInterceptor(loggerInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludeError)
.excludePathPatterns(excludeRestDocs)
.excludePathPatterns(excludePathPatterns)
;
registry.addInterceptor(headerCheckInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludeError)
.excludePathPatterns(excludePathPatterns)
.excludePathPatterns(excludeRestDocs)
.excludePathPatterns(feignApi)
;
}
}
package net.herit.svcplatform.pushservice.commons.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.context.annotation.RequestScope;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestSession;
@Configuration
public class RegisterConfig {
@Value("${logging.call.multiline}")
private boolean multiline;
@Bean
@RequestScope
public RequestSession requestSession() {
return new RequestSession();
}
@Bean
public ObjectMapper objectMapper() {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
builder.featuresToDisable(SerializationFeature.FAIL_ON_EMPTY_BEANS, SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
if(multiline) {
builder.featuresToEnable(SerializationFeature.INDENT_OUTPUT);
}
return builder.build();
}
}
package net.herit.svcplatform.pushservice.commons.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Parameter;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
/**
* Swagger 설정
* 밑에 parameters로 전역으로 사용되는 변수(?)를 적을 수 있다.
* 아래 예는 header 값을 설정해주는 내용
*/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
private static final String STRING = "String";
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.globalOperationParameters(parameters())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.ant("/api/1.0/**"))
.build()
.apiInfo(apiInfo())
.securitySchemes(
Arrays.asList(
new ApiKey("JWT", "Authorization", "header")
)
)
.securityContexts(Arrays.asList(securityContext()));
}
private List<Parameter> parameters(){
List<Parameter> parameters = new ArrayList<>();
parameters.add(
addHeader(
"Api-Test-Tools",
"스웨거인 경우 DB Rollback을 시키기 위함",
"String",
true,
"Swagger")
);
parameters.add(
addHeader(
"X-SERVICE-ID",
"다수 서비스 앱을 사용하는 경우 서비스를 식별하기 위한 코드. NOTE : 설치기사 앱은 'HEVITON' 으로 고정",
STRING,
true,
"HEVITON"
)
);
parameters.add(
addHeader(
"X-APP-TYPE",
"ios / android",
STRING,
true,
"android"
)
);
parameters.add(
addHeader(
"X-TERMINAL-ID",
"동일 사용자 계정으로 여러 폰으로 로그인할 경우 별도의 세션 처리 (동시로그인 허용) 앱 설치 후 최초실행 시 UUID 형식의 문자열 형식으로 X-TERMINAL-ID 생성",
STRING,
true,
"55a0fa94-5ab8-4498-9517-46876b96db0d"
)
);
parameters.add(
addHeader(
"X-API-VERSION",
"1.0",
STRING,
true,
"1.0"
)
);
parameters.add(
addHeader(
"X-REFERER",
"페이지 단위를 구분하기 위한 코드. NOTE : 앱로깅 API의 function.code와 동일",
STRING,
true,
"REFERER"
)
);
parameters.add(
addHeader(
"X-TRANSACTION-ID",
"서비스 플랫폼 또는 API_GW 에서 생성",
STRING,
true,
"X-TRANSACTION-ID.20200629115826"
)
);
parameters.add(
addHeader(
"Authorization",
"token",
STRING,
false,
""
)
);
return parameters;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Rookie API TEST")
.description("Rookie 서비스 api 테스트 페이지.")
.version("0.1")
.build();
}
private Parameter addHeader(String name, String desc, String headerType, boolean required, String defaultValue) {
return new ParameterBuilder().
name(name)
.description(desc)
.modelRef(new ModelRef(headerType))
.parameterType("header")
.required(required)
.defaultValue(defaultValue)
.build();
}
private List<SecurityReference> defaultAuth(){
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Arrays.asList(
new SecurityReference("JWT", authorizationScopes)
);
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.ant("/api/1.0/**"))
.build();
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
public class ThreadPoolConfig {
@Value(value = "${thread-pool.core-size}")
int corePoolSize;
@Value(value = "${thread-pool.max-size}")
int maxPoolSize;
@Value(value = "${thread-pool.queue-capacity}")
int queueCapacity;
@Bean(name = "push-request-executor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
threadPoolTaskExecutor.setThreadNamePrefix("push-");
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
package net.herit.svcplatform.pushservice.commons.context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
@Component
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
AutowireCapableBeanFactory beanFactory;
public static ApplicationContext getApplicationContext() {
return ctx;
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
ApplicationContextProvider.ctx = ctx;
this.beanFactory = ctx.getAutowireCapableBeanFactory();
}
}
package net.herit.svcplatform.pushservice.commons.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.logger.OmsLogger;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
@RestController
@RequestMapping("${server.error.path:${error.path:/error}}")
public class ErrorController extends AbstractErrorController {
@Autowired
private OmsLogger omsLogger;
@Autowired
private OmsObject omsObj;
public ErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
}
@Override
public String getErrorPath() {
return this.getErrorPath();
}
@RequestMapping
public ResponseEntity<ErrorResponse> error(HttpServletRequest request) {
String uri = (String) request.getAttribute("svc_uri");
omsObj.uri(uri);
HttpStatus status = getStatus(request);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Content-Type", "application/json");
String description = getDescription(status, uri);
ErrorResponse responseBody = new CommonException(
HttpResponseStatus.findResCodeByCode(status.value()),
description
).getErrorResponse();
/**
* Swagger 이용시에도 404 에러가 발생하는데, 원인을 알 수 없어 svc_uri 값이 설정되어 있는 경우에만 로그를 출력하도록 설정
*/
if (uri != null) {
omsLogger.writeErrorOms(
responseBody,
HttpResponseStatus.NOT_PROVIDE_SERVICE.getCode(),
HttpResponseStatus.NOT_PROVIDE_SERVICE.getResCode());
}
return new ResponseEntity<>(responseBody, httpHeaders, status);
}
private String getDescription(HttpStatus status, Object uri) {
String description = null;
if(status.value() == HttpResponseStatus.NOT_PROVIDE_SERVICE.getResCode()) {
description = HttpResponseStatus.NOT_PROVIDE_SERVICE.getDescription() + uri;
} else {
description = status.getReasonPhrase();
}
return description;
}
}
package net.herit.svcplatform.pushservice.commons.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
import net.herit.svcplatform.pushservice.commons.dto.response.SuccessResponse;
import net.herit.svcplatform.pushservice.commons.logger.LogTracer;
@RestController
public class HealthCheckController {
@GetMapping("/api/healthcheck")
@LogTracer(apiAppType = "", apiClass = "HC01", svcClass = "HC01")
public ResponseEntity<SuccessResponse> healthCheckGet() {
return ResponseEntity.ok(new SuccessResponse());
}
@NonAuth
@PostMapping("/api/healthcheck")
@LogTracer(apiAppType = "", apiClass = "HC01", svcClass = "HC01")
public ResponseEntity<SuccessResponse> healthCheckPost() {
return ResponseEntity.ok(new SuccessResponse());
}
}
package net.herit.svcplatform.pushservice.commons.dto.enums;
public enum ApiClasses {
PUSH_CONSUME("CONSM01", "카프카에서 푸시메시지 consume"),
;
String code;
String description;
ApiClasses(String code, String description) {
this.code= code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
}
package net.herit.svcplatform.pushservice.commons.dto.enums;
public class AppInfos {
public final static String SVC_ID = "ECHALL";
public final static String APP_NAME = "PUSH_SERVICE";
public final static String APP_TYPE = "BKND";
public final static String LOG_TYPE = "SVC";
}
package net.herit.svcplatform.pushservice.commons.dto.enums;
public enum SvcClasses {
PUSH_REQ("PUSH_REQ", "푸시 요청"),
;
String code;
String description;
SvcClasses(String code, String description) {
this.code= code;
this.description = description;
}
public String getCode() {
return code;
}
public String getDescription() {
return description;
}
}
package net.herit.svcplatform.pushservice.commons.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Builder
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class AccountModel {
@JsonProperty("login_id")
@ApiModelProperty(required = false, example = "login_id")
private String loginId;
@JsonProperty("auth_token")
@ApiModelProperty(required = false, example="auth_token")
private String authToken;
}
package net.herit.svcplatform.pushservice.commons.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AuthrizedUser {
@JsonProperty("login_id")
private String loginId;
@JsonProperty("auth_token")
private String authToken;
}
package net.herit.svcplatform.pushservice.commons.dto.model;
import net.herit.svcplatform.pushservice.commons.dto.response.Response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FailModel implements Response {
private Integer code;
private String description;
@Override
public int resultCode() {
return code;
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.dto.model;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.dto.response.Response;
import lombok.Data;
@Data
public class SuccessModel implements Response {
private int code;
public SuccessModel(){
setCode(HttpResponseStatus.SUCCESS.getCode());
}
@Override
public int resultCode() {
return code;
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.dto.request;
import net.herit.svcplatform.pushservice.commons.dto.model.AccountModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Builder
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class AuthRequest {
AccountModel account;
}
package net.herit.svcplatform.pushservice.commons.dto.request;
import net.herit.svcplatform.pushservice.commons.dto.model.AuthrizedUser;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
public class RequestBody {
private Account account;
@Data
@ToString
public class Account {
public Account(AuthrizedUser authrizedUser) {
setLoginId(authrizedUser.getLoginId());
setAuthToken(authrizedUser.getAuthToken());
}
private String loginId;
private String authToken;
}
}
package net.herit.svcplatform.pushservice.commons.dto.request;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class RequestHeader {
private String xServiceId;
private String xTerminalId;
private String xAppType;
private Map<String, String> headerMap;
public RequestHeader(HttpServletRequest request) {
toMap(request);
headerSetting(request);
}
public RequestHeader(String xServiceId, String xAppType, String xTerminalId) {
setXServiceId(xServiceId);
setXAppType(xAppType);
setXTerminalId(xTerminalId);
}
private void headerSetting(HttpServletRequest request) {
setXServiceId(request.getHeader("X-SERVICE-ID"));
setXAppType(request.getHeader("X-APP-TYPE"));
setXTerminalId(request.getHeader("X-TERMINAL-ID"));
}
private void toMap(HttpServletRequest request) {
headerMap = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String hName = headerNames.nextElement();
String hVal = request.getHeader(hName);
headerMap.put(hName, hVal);
}
}
}
package net.herit.svcplatform.pushservice.commons.dto.request;
import javax.servlet.http.HttpServletRequest;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RequestSession {
private String uri;
private RequestHeader header;
private RequestBody body;
public void setFromServletRequest(HttpServletRequest request) {
this.uri = request.getRequestURI();
this.header = new RequestHeader(request);
}
}
package net.herit.svcplatform.pushservice.commons.dto.response;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper=false)
public class ErrorResponse implements Response {
private FailModel result;
@Override
public int resultCode() {
return result.getCode();
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.dto.response;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
public enum HttpResponseStatus {
/*
* 알수없는 예외일 경우 쉽게 검색하기 위해 400 등의 응답 코드와 똑같은 코드로 정의하고, 더 세부적인 resultCode를 중복없이 만들기!
* responseCode는 중복이 많아 검색이 힘들기때문.
*/
SUCCESS(200, 0, ""),
NOT_PROVIDE_SERVICE(404, 1, "not support : "),
INVALID_PARAM(400, 2, "invalid parameter"),
DUPLICATED_CONTENTS(400, 3, "duplicated contents"),
SYSTEM_ERROR(500, 4, "Exception"),
NOT_FOUND_METHOD(405, 5, "Request method not support"),
INVALID_OAUTH_TOKEN(401, 101, "invalid oauth token"),
INVALID_AUTH_TOKEN(401, 102, "invalid auth token"),
INVALID_JWT_TOKEN(401, 103, "invalid json web token"),
NOT_EXIST_USER(401, 104, "not exist user"),
WRONG_PASSWORD(401, 105, "wrong password"),
WRONG_GROUP_ID(401, 106, "wrong group id"),
ALREADY_EXIST_USER(401, 107, "already exist user"),
JSON_PARSING_ERROR(500, 108, "json parsing error"),
ERROR_EXT_CONNECT(500, 109, "error ext connection"),
API_NOT_DEVELOPED_YET(400, 1000, "API 기획중으로 아직 개발되지 않았습니다."),
API_NOT_PROVIDED_YET(400, 1001, "해당 API는 아직 지원하지 않습니다."),
;
int resCode;
int code;
String reason;
/*
* code로 responseStatus를 찾을 일은 없겠지만, 혹시 몰라 선언.
* 초기 static 생성자에서 Map을 설정해주는 비용이 들지만,
* HashMap을 이용하기 때문에 이후 code로 뭔가를 찾을때는 반복문을 돌리는것보다 수 배 이상 빠르다고함.
* reason은 문자열이라서 검색하기 어렵고, resCode는 중복이 너무 많아 키 값으로 부적절하기 때문에 생략...
*/
private static final Map<Integer, HttpResponseStatus> findHttpResponseStatusByCode;
static {
findHttpResponseStatusByCode = new ConcurrentHashMap<>();
for (HttpResponseStatus status : HttpResponseStatus.values()) {
findHttpResponseStatusByCode.put(status.getCode(), status);
}
}
HttpResponseStatus(int resCode, int code, String description) {
this.resCode = resCode;
this.code = code;
this.reason = description;
}
public int getCode() {
return code;
}
public int getResCode() {
return resCode;
}
public String getDescription() {
return reason;
}
/**
* 컨트롤러까지 오기 전에는 ResponseEntity 객체를 이용한 응답을 못하므로 문자열로 응답해야 하는 경우에 사용.
*/
public static String getErrorResponseToJson(HttpResponseStatus httpResponseStatus) throws JsonProcessingException {
ErrorResponse responseBody = new ErrorResponse();
responseBody.setResult(new FailModel(httpResponseStatus.getCode(), httpResponseStatus.getDescription()));
return getResponseToJson(responseBody);
}
/**
* 컨트롤러까지 오기 전에는 ResponseEntity 객체를 이용한 응답을 못하므로 문자열로 응답해야 하는 경우에 사용.
*/
public static String getSuccessResponseToJson() throws JsonProcessingException {
SuccessResponse responseBody = new SuccessResponse();
return getResponseToJson(responseBody);
}
private static String getResponseToJson(Object responseBody) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
return objectMapper.writeValueAsString(responseBody);
}
public static Integer findResCodeByCode(int code) {
return nullReturnServerError(findHttpResponseStatusByCode.get(code)).getResCode();
}
public static HttpResponseStatus findHttpResponseStatusByCode(int code) {
return nullReturnServerError(findHttpResponseStatusByCode.get(code));
}
private static HttpResponseStatus nullReturnServerError(HttpResponseStatus httpResponseStatus) {
if(httpResponseStatus == null) {
httpResponseStatus = HttpResponseStatus.SYSTEM_ERROR;
}
return httpResponseStatus;
}
}
package net.herit.svcplatform.pushservice.commons.dto.response;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
public interface Response {
int resultCode() ;
default int getHttpResponseCode(int code) throws CommonException {
return HttpResponseStatus.findResCodeByCode(code);
}
}
package net.herit.svcplatform.pushservice.commons.dto.response;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import lombok.Data;
@Data
public class SuccessResponse implements Response {
private SuccessModel result;
public SuccessResponse(){
setResult(new SuccessModel());
}
@Override
public int resultCode() {
return result.getCode();
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.dto.wrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.StreamUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;
public class RequestWrapper extends ContentCachingRequestWrapper {
private final ByteArrayOutputStream cachedContent;
@Nullable
private final Integer contentCacheLimit;
@Nullable
private ServletInputStream inputStream;
@Nullable
private BufferedReader reader;
private ByteArrayInputStream bis;
private byte[] httpRequestBodyByteArray;
public RequestWrapper(HttpServletRequest request) throws IOException {
super(request);
int contentLength = request.getContentLength();
this.httpRequestBodyByteArray = StreamUtils.copyToByteArray(request.getInputStream());
this.bis = new ByteArrayInputStream(httpRequestBodyByteArray);
this.cachedContent = new ByteArrayOutputStream(contentLength >= 0 ? contentLength : 1024);
this.contentCacheLimit = null;
}
public RequestWrapper(HttpServletRequest request, int contentCacheLimit) throws IOException {
super(request);
this.cachedContent = new ByteArrayOutputStream(contentCacheLimit);
this.contentCacheLimit = contentCacheLimit;
this.httpRequestBodyByteArray = StreamUtils.copyToByteArray(request.getInputStream());
this.bis = new ByteArrayInputStream(httpRequestBodyByteArray);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (this.inputStream == null) {
this.inputStream = new ContentCachingInputStream(servletInputStream());
}
return servletInputStream();
}
public byte[] getRequestBodyByteArray() {
return httpRequestBodyByteArray;
}
public ByteArrayInputStream getByteArrayInputStream() {
return bis;
}
private class ContentCachingInputStream extends ServletInputStream {
private final ServletInputStream is;
private boolean overflow = false;
public ContentCachingInputStream(ServletInputStream is) {
this.is = is;
}
@Override
public int read() throws IOException {
int ch = this.is.read();
if (ch != -1 && !this.overflow) {
if (contentCacheLimit != null && cachedContent.size() == contentCacheLimit) {
this.overflow = true;
handleContentOverflow(contentCacheLimit);
} else {
cachedContent.write(ch);
}
}
return ch;
}
@Override
public int read(byte[] b) throws IOException {
int count = this.is.read(b);
writeToCache(b, 0, count);
return count;
}
private void writeToCache(final byte[] b, final int off, int count) {
if (!this.overflow && count > 0) {
if (contentCacheLimit != null && count + cachedContent.size() > contentCacheLimit) {
this.overflow = true;
cachedContent.write(b, off, contentCacheLimit - cachedContent.size());
handleContentOverflow(contentCacheLimit);
return;
}
cachedContent.write(b, off, count);
}
}
@Override
public int read(final byte[] b, final int off, final int len) throws IOException {
int count = this.is.read(b, off, len);
writeToCache(b, off, count);
return count;
}
@Override
public int readLine(final byte[] b, final int off, final int len) throws IOException {
int count = this.is.readLine(b, off, len);
writeToCache(b, off, count);
return count;
}
@Override
public boolean isFinished() {
return this.is.isFinished();
}
@Override
public boolean isReady() {
return this.is.isReady();
}
@Override
public void setReadListener(ReadListener readListener) {
this.is.setReadListener(readListener);
}
}
private ServletInputStream servletInputStream() {
return new ServletInputStream() {
private int lastIndexRetrieved = -1;
private ReadListener readListener = null;
@Override
public boolean isFinished() {
return (lastIndexRetrieved == httpRequestBodyByteArray.length - 1);
}
@Override
public boolean isReady() {
return isFinished();
}
@Override
public void setReadListener(ReadListener readListener) {
this.readListener = readListener;
if (!isFinished()) {
try {
readListener.onDataAvailable();
} catch (IOException e) {
readListener.onError(e);
}
} else {
try {
readListener.onAllDataRead();
} catch (IOException e) {
readListener.onError(e);
}
}
}
@Override
public int read() throws IOException {
int i;
if (!isFinished()) {
i = httpRequestBodyByteArray[lastIndexRetrieved + 1];
lastIndexRetrieved++;
if (isFinished() && (readListener != null)) {
try {
readListener.onAllDataRead();
} catch (IOException ex) {
readListener.onError(ex);
throw ex;
}
}
return i;
} else {
return -1;
}
}
@Override
public int available() throws IOException {
return (httpRequestBodyByteArray.length - lastIndexRetrieved - 1);
}
@Override
public void close() throws IOException {
lastIndexRetrieved = httpRequestBodyByteArray.length - 1;
}
};
}
}
package net.herit.svcplatform.pushservice.commons.exception;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
public class AopException extends RuntimeException {
private static final long serialVersionUID = 2125940198299377339L;
private int resultCode;
/**
* 고유한 결과 코드값을 입력해야한다.
* @param resultCode
*/
public AopException(HttpResponseStatus status) {
super(status.getDescription());
this.resultCode = status.getCode();
}
public AopException(String resultCode) {
super(resultCode);
this.resultCode = Integer.parseInt(resultCode);
}
public AopException(int resultCode) {
super(String.valueOf(resultCode));
this.resultCode = resultCode;
}
public ErrorResponse getErrorResponse() throws Exception {
ErrorResponse responseBody = new ErrorResponse();
responseBody.setResult(new FailModel(this.resultCode, HttpResponseStatus.findHttpResponseStatusByCode(this.resultCode).getDescription()));
return responseBody;
}
public HttpResponseStatus getHttpResponseStatus() throws CommonException {
return HttpResponseStatus.findHttpResponseStatusByCode(this.resultCode);
}
}
package net.herit.svcplatform.pushservice.commons.exception;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class CommonException extends RuntimeException {
private static final long serialVersionUID = -293606853491213392L;
private final Integer code;
private final Integer resCode;
private final String description;
public ErrorResponse getErrorResponse(){
ErrorResponse responseBody = new ErrorResponse();
responseBody.setResult(
new FailModel(code, description)
);
return responseBody;
}
public CommonException(HttpResponseStatus httpResponseStatus) {
this.code = httpResponseStatus.getCode();
this.resCode = httpResponseStatus.getResCode();
this.description = httpResponseStatus.getDescription();
}
public CommonException(Integer code, String description) {
this.code = code;
this.resCode = HttpResponseStatus.findResCodeByCode(code);
this.description = description;
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.exception;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import lombok.RequiredArgsConstructor;
/**
* ExceptionAdvice
*/
@ControllerAdvice
@RequiredArgsConstructor
public class ExceptionAdvice {
/**
* Api 로직 진행중, 예상한 예외들에 대한 예외처리 메서드
*/
@ExceptionHandler(CommonException.class)
public ResponseEntity<ErrorResponse> commonExceptionHandler(CommonException ex, WebRequest request) {
ErrorResponse responseBody = ex.getErrorResponse();
return ResponseEntity.status(ex.getResCode()).body(responseBody);
}
/**
* Validation 예외처리 메서드
*/
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseEntity<ErrorResponse> validationExceptionHandler(MethodArgumentNotValidException ex, WebRequest request) {
StringBuilder sb = new StringBuilder();
for (ObjectError error : ex.getBindingResult().getAllErrors()) {
sb.append(error.getDefaultMessage());
}
ErrorResponse responseBody = new CommonException(HttpResponseStatus.INVALID_PARAM.getCode(), HttpResponseStatus.INVALID_PARAM.getResCode(), sb.toString())
.getErrorResponse();
return ResponseEntity.badRequest().body(responseBody);
}
/**
* Method not Supported
* @param ex
* @param response
* @return
*/
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
public ResponseEntity<ErrorResponse> requestMethodNotSupportExceptionHandler(
HttpRequestMethodNotSupportedException ex,
HttpServletResponse response){
ErrorResponse responseBody = new CommonException(
HttpResponseStatus.NOT_FOUND_METHOD.getCode(),
HttpResponseStatus.NOT_FOUND_METHOD.getResCode(),
HttpResponseStatus.NOT_FOUND_METHOD.getDescription()).getErrorResponse();
return ResponseEntity.status(HttpResponseStatus.NOT_FOUND_METHOD.getResCode()).body(responseBody);
}
/**
* 요청 Body Missing
*/
@ExceptionHandler({HttpMessageNotReadableException.class})
public ResponseEntity<ErrorResponse> requestBodyMissingExceptionHandler(HttpMessageNotReadableException ex, WebRequest request) {
String description = ex.getMessage();
ErrorResponse responseBody = new CommonException(HttpResponseStatus.INVALID_PARAM.getCode(), HttpResponseStatus.INVALID_PARAM.getResCode(), description)
.getErrorResponse();
return ResponseEntity.badRequest().body(responseBody);
}
@ExceptionHandler(AopException.class)
public ResponseEntity<ErrorResponse> aopExceptionHandler(AopException ex, WebRequest request) {
ErrorResponse responseBody;
int resCode;
try {
responseBody = ex.getErrorResponse();
resCode = ex.getHttpResponseStatus().getResCode();
return ResponseEntity.status(resCode).body(responseBody);
} catch (Exception e) {
e.printStackTrace();
responseBody = new CommonException(HttpResponseStatus.SYSTEM_ERROR).getErrorResponse();
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
return ResponseEntity.status(httpStatus).body(responseBody);
}
}
/**
* 예상하지 못한 예외들에 대한 예외처리 메서드
* 404 NOT FOUND는 포함되지 않음
* 404 NOT_FOUND_EXCEPTION은 ${package}.core.exception.ErrorController 확인
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex, WebRequest request) {
ErrorResponse responseBody = new CommonException(
HttpResponseStatus.SYSTEM_ERROR).getErrorResponse();
HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
return ResponseEntity.status(httpStatus).body(responseBody);
}
}
package net.herit.svcplatform.pushservice.commons.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.filter.OncePerRequestFilter;
import net.herit.svcplatform.pushservice.commons.dto.wrapper.RequestWrapper;
import net.herit.svcplatform.pushservice.commons.util.BeanUtil;
import net.herit.svcplatform.pushservice.commons.util.HttpTypeUtil;
public class HttpWrapperFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
FilterChain filterChain) throws ServletException, IOException {
if (isWrapper(servletRequest)) {
HttpServletRequest request = new RequestWrapper(servletRequest);
filterChain.doFilter(request, servletResponse);
} else {
filterChain.doFilter(servletRequest, servletResponse);
}
}
private boolean isWrapper(HttpServletRequest servletRequest) {
String uri = servletRequest.getRequestURI();
if (isSwaggerUri(uri)) {
return false;
}
HttpTypeUtil httpTypeUtil = getHttpTypeUtil();
if (httpTypeUtil == null) {
return false;
}
String methodType = servletRequest.getMethod();
String contentType = servletRequest.getContentType();
if (
httpTypeUtil.isJsonPost(contentType, methodType) ||
httpTypeUtil.isJsonGet(contentType, methodType) ||
httpTypeUtil.isFormPost(contentType, methodType) ||
httpTypeUtil.isFormGet(contentType, methodType) ||
contentType == null
) {
return true;
}
return false;
}
/**
* 추가하지 않아도 swagger는 작동하나, 비정상적인 log가 지저분하게 남아서 추가
*/
private boolean isSwaggerUri(String uri) {
return uri.indexOf("swagger") != -1 || uri.indexOf("v2/api-docs") != -1 ;
}
private HttpTypeUtil getHttpTypeUtil() {
HttpTypeUtil bean = null;
Object obj = BeanUtil.getBean("httpTypeUtil");
if (obj instanceof HttpTypeUtil) {
bean = (HttpTypeUtil) obj;
}
return bean;
}
}
package net.herit.svcplatform.pushservice.commons.jwt;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
import net.herit.svcplatform.pushservice.commons.jwt.dto.ResultTokenDto;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRenewalRequest;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRequest;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import lombok.RequiredArgsConstructor;
// TODO DB 작업은 따로 되어있지 않으므로, DB 인증과정 추가 필요함 -> 토큰 발급 API이므로 Swagger에서 사용할 수 없도록 경로 지정
@RestController
@RequiredArgsConstructor
public class JwtController {
private final CallLogger callLogger;
private final JwtService jwtTokenService;
@NonAuth
@PostMapping("/api/jwt/server/request")
public ResponseEntity<ResultTokenDto> requestServerAccessToken(@Valid @RequestBody TokenRequest requestBody) {
callLogger.info("net.herit.svcplatform.pushservice.commons.jwt.JwtTokenController.requestServerAccessToken()");
return ResponseEntity.ok(jwtTokenService.requestServerAccessToken(requestBody));
}
@NonAuth
@PostMapping("/api/jwt/client/request")
public ResponseEntity<ResultTokenDto> requestClientAccessToken(@Valid @RequestBody TokenRequest requestBody) {
callLogger.info("net.herit.svcplatform.pushservice.commons.jwt.JwtTokenController.requestClientAccessToken()");
return ResponseEntity.ok(jwtTokenService.requestClientAccessToken(requestBody));
}
@NonAuth
@PostMapping("/api/jwt/access/renewal")
public ResponseEntity<ResultTokenDto> renewalServerAccessToken(@Valid @RequestBody TokenRenewalRequest requestBody) {
callLogger.info("net.herit.svcplatform.pushservice.commons.jwt.JwtTokenController.renewalServerAccessToken()");
return ResponseEntity.ok(jwtTokenService.renewalServerAccessToken(requestBody));
}
}
package net.herit.svcplatform.pushservice.commons.jwt;
import org.springframework.stereotype.Service;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.jwt.dto.ResultTokenDto;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRenewalRequest;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRequest;
import net.herit.svcplatform.pushservice.commons.jwt.properties.JwtProperties;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.util.JwtUtil;
import lombok.RequiredArgsConstructor;
//TODO DB 작업은 따로 되어있지 않으므로, DB 인증과정 추가 필요함 -> 토큰 발급 API이므로 Swagger에서 사용할 수 없도록 경로 지정
@Service
@RequiredArgsConstructor
public class JwtService {
private final CallLogger callLogger;
private final JwtUtil jwtTokenUtil;
private final JwtProperties jwtProperties;
public ResultTokenDto requestServerAccessToken(TokenRequest tokenRequest) {
callLogger.info("net.herit.svcplatform.pushservice.commons.jwt.JwtTokenService.requestServerAccessToken()");
int refreshTokenExpiration = Integer.parseInt(jwtProperties.getRefreshTokenExpirationHour());
String accessToken = jwtTokenUtil.createServerAccessToken(null, tokenRequest.getExpirationHour());
String refreshToken = jwtTokenUtil.createRefreshToken(null, refreshTokenExpiration);
return new ResultTokenDto(accessToken, refreshToken);
}
public ResultTokenDto requestClientAccessToken(TokenRequest tokenRequest) {
callLogger.info("net.herit.svcplatform.pushservice.commons.jwt.JwtTokenService.requestClientAccessToken()");
int refreshTokenExpiration = Integer.parseInt(jwtProperties.getRefreshTokenExpirationHour());
String accessToken = jwtTokenUtil.createClientAccessToken(null, tokenRequest.getExpirationHour());
String refreshToken = jwtTokenUtil.createRefreshToken(null, refreshTokenExpiration);
return new ResultTokenDto(accessToken, refreshToken);
}
public ResultTokenDto renewalServerAccessToken(TokenRenewalRequest tokenRenewalRequest) {
try {
jwtTokenUtil.decodeRefreshToken(tokenRenewalRequest.getTokenData());
} catch(Exception e) {
throw new CommonException(HttpResponseStatus.INVALID_JWT_TOKEN);
}
String accessToken = jwtTokenUtil.createServerAccessToken(null, tokenRenewalRequest.getExpirationHour());
// TODO refresh_token도 갱신해서 응답할지 아니면 기존 refresh_token으로 응답할지 확인 필요
return new ResultTokenDto(accessToken, tokenRenewalRequest.getTokenData());
}
}
package net.herit.svcplatform.pushservice.commons.jwt.dto;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.dto.response.Response;
import lombok.Data;
@Data
public class ResultTokenDto implements Response {
private SuccessModel result;
private String accessToken;
private String refreshToken;
public ResultTokenDto() {
setResult(new SuccessModel());
}
public ResultTokenDto(String accessToken, String refreshToken) {
setAccessToken(accessToken);
setRefreshToken(refreshToken);
setResult(new SuccessModel());
}
@Override
public int resultCode() {
return result.getCode();
}
}
package net.herit.svcplatform.pushservice.commons.jwt.dto;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import net.herit.svcplatform.pushservice.commons.dto.model.AccountModel;
import lombok.Data;
@Data
public class TokenRenewalRequest {
@Valid @NotNull
private AccountModel account;
@Valid @NotNull @JsonProperty("user_id")
private String userId;
@Valid @Min(value=1) @JsonProperty("application_id")
private int applicationId;
@Valid @Min(value=1)
@JsonProperty("expiration_hour")
private int expirationHour;
/**
* ACCESS_TOKEN이 아닌 REFRESH_TOKEN
*/
@NotNull
@JsonProperty("token_data")
private String tokenData;
}
package net.herit.svcplatform.pushservice.commons.jwt.dto;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonProperty;
import net.herit.svcplatform.pushservice.commons.dto.model.AccountModel;
import lombok.Data;
@Data
public class TokenRequest {
@Valid @NotNull
private AccountModel account;
@Valid @NotNull @JsonProperty("user_id")
private String userId;
@Valid @Min(value=1) @JsonProperty("application_id")
private int applicationId;
@Valid @Min(value=1) @JsonProperty("expiration_hour")
private int expirationHour;
}
package net.herit.svcplatform.pushservice.commons.jwt.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix="token")
public class JwtProperties {
private String accessTokenSecret; // ACCESS_TOKEN secret KEY
private String refreshTokenSecret; // REFRESH_TOKEN secret KEY
private String issuer; // 토큰발급자
private String accessTokenSubject; // ACCESS_TOKEN 토큰제목
private String refreshTokenSubject; // REFRESH_TOKEN 토큰제목
private String audience; // 토큰대상자
private String typeKey; // 토큰유형 서버
private String typeClient; // 토큰유형1 클라이언트
private String typeServer; // 토큰유형2 서버
private String refreshTokenExpirationHour; // REFRESH_TOKEN 만료시간
private String refreshTokenHeader; // REFRESH_TOKEN HEADER
}
package net.herit.svcplatform.pushservice.commons.logger;
import org.springframework.stereotype.Component;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
@RequiredArgsConstructor
public class CallLogger {
private final ErrorLogger errorLogger;
public void trace(String msg) { log.trace(msg); }
public void trace(String format, Object arg) { log.trace(format, arg); }
public void trace(String format, Object arg1, Object arg2) {log.trace(format, arg1, arg2);}
public void trace(String format, Object... arguments) {log.trace(format, arguments);}
public void trace(String msg, Throwable t) {log.trace(msg, t);}
public void debug(String msg) {log.debug(msg);}
public void debug(String format, Object arg) {log.debug(format, arg);}
public void debug(String format, Object arg1, Object arg2) {log.debug(format, arg1, arg2);}
public void debug(String format, Object... arguments) {log.debug(format, arguments);}
public void debug(String msg, Throwable t) {log.debug(msg, t);}
public void info(String msg) {log.info(msg);}
public void info(String format, Object arg) {log.info(format, arg);}
public void info(String format, Object arg1, Object arg2) {log.info(format, arg1, arg2);}
public void info(String format, Object... arguments) {log.info(format, arguments);}
public void info(String msg, Throwable t) {log.info(msg, t);}
public void warn(String msg) {log.warn(msg);}
public void warn(String format, Object arg) {log.warn(format, arg);}
public void warn(String format, Object... arguments) {log.warn(format, arguments);}
public void warn(String format, Object arg1, Object arg2) {log.warn(format, arg1, arg2);}
public void warn(String msg, Throwable t) {log.warn(msg, t);}
public void error(String msg) {
log.error(msg);
errorLogger.error(msg);
}
public void error(String format, Object arg) {
log.error(format, arg);
errorLogger.error(format, arg);
}
public void error(String format, Object arg1, Object arg2) {
log.error(format, arg1, arg2);
errorLogger.error(format, arg1, arg2);
}
public void error(String format, Object... arguments) {
log.error(format, arguments);
errorLogger.error(format, arguments);
}
public void error(String msg, Throwable t) {
log.error(msg, t);
errorLogger.error(msg, t);
}
}
package net.herit.svcplatform.pushservice.commons.logger;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class ErrorLogger {
public void error(String msg) {
log.error(msg);
}
public void error(String format, Object arg) {
log.error(format, arg);
}
public void error(String format, Object arg1, Object arg2) {
log.error(format, arg1, arg2);
}
public void error(String format, Object... arguments) {
log.error(format, arguments);
}
public void error(String msg, Throwable t) {
log.error(msg, t);
}
}
package net.herit.svcplatform.pushservice.commons.logger;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogTracer {
String logType() default "SVC";
String svcClass() default "";
String apiClass() default "";
String apiAppType() default "";
}
package net.herit.svcplatform.pushservice.commons.logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
import net.herit.svcplatform.pushservice.commons.util.DateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.herit.common.logger.context.HeritLoggerContext;
import net.herit.common.logger.obj.abst.AbstractOmsObject;
@Slf4j
@Component
@RequiredArgsConstructor
public class OmsLogger {
private final DateUtil dateUtil;
private final CallLogger callLogger;
private final ObjectMapper objectMapper;
@Autowired
private OmsObject oms;
public void write() {
oms.setSeqId();
oms.setLogTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
log.info(oms.toLog());
}
public void write(AbstractOmsObject oms){
oms.setSeqId();
oms.setLogTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
log.info(oms.toLog());
}
public void writeErrorOms(ErrorResponse responseBody, int code, int resCode) {
try {
callLogger.info("{}", objectMapper.writeValueAsString(responseBody));
} catch (JsonProcessingException e) {
callLogger.error("call log fail");
}
oms.resultCode(code).httpStatus(resCode)
.responseTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
write();
HeritLoggerContext.resetAttributes(); // 로그 객체 초기화
}
}
package net.herit.svcplatform.pushservice.commons.logger.advice;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.commons.dto.response.Response;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.logger.OmsLogger;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
import net.herit.svcplatform.pushservice.commons.util.DateUtil;
import lombok.RequiredArgsConstructor;
@Order(0)
@ControllerAdvice
@RequiredArgsConstructor
public class LoggerResponseAdvice implements ResponseBodyAdvice<Object> {
private final OmsObject omsObj;
private final DateUtil dateUtil;
private final OmsLogger omsLogger;
private final CallLogger callLogger;
private final ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter parameter, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
if (selectedContentType == MediaType.APPLICATION_JSON) {
logWrite(body);
}
return body;
}
private void logWrite(Object body) {
try {
callLogger.info("{}", objectMapper.writeValueAsString(body));
if (body instanceof Response) {
Response responseBody = (Response) body;
int resultCode = responseBody.resultCode();
int responseCode = responseBody.getHttpResponseCode(resultCode);
omsObj.resultCode(resultCode).httpStatus(responseCode)
.responseTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
omsLogger.write();
}
} catch (JsonProcessingException e) {
callLogger.error("ResponseAdvice Error : ", e);
} catch (CommonException e) {
callLogger.error("LoggerResponseAdvice Error : ", e);
}
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.logger.config;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import net.herit.svcplatform.pushservice.commons.logger.interceptor.SqlLoggingInterceptor;
import org.springframework.context.annotation.Bean;
@Profile(value = {"prod", "dev", "local"})
@Configuration
public class DBLogConfig {
@Bean("sqlLoggingInterceptor")
public Interceptor sqlLoggingInterceptor() {
return new SqlLoggingInterceptor();
}
}
package net.herit.svcplatform.pushservice.commons.logger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.annotation.RequestScope;
import net.herit.svcplatform.pushservice.commons.logger.dto.CallObject;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
@Configuration
public class LoggerConfig {
@Bean
@RequestScope
public CallObject defaultCallLog() {
return new CallObject();
}
@Bean
@RequestScope
public OmsObject defaultOmsLog() {
return new OmsObject();
}
}
package net.herit.svcplatform.pushservice.commons.logger.dto;
import net.herit.common.logger.obj.abst.AbstractCallLogObject;
import net.herit.svcplatform.pushservice.commons.dto.enums.AppInfos;
import javax.servlet.http.HttpServletRequest;
public class CallObject extends AbstractCallLogObject implements Cloneable {
public void init() {
super.appName = AppInfos.APP_NAME;
setCallId(AppInfos.SVC_ID);
setSvcId(AppInfos.SVC_ID);
}
public CallObject request(HttpServletRequest request) {
String svcId = request.getHeader("X-SERVICE-ID");
String transactionId = request.getHeader("X-TRANSACTION-ID");
String appName = request.getHeader("X-APP-TYPE");
super.appName = appName;
setCallId(generateCallString());
setTransactionId(transactionId);
setSvcId(svcId);
return this;
}
@Override
public int objId() {
return System.identityHashCode(this);
}
@Override
public String toLog() {
StringBuilder sb = new StringBuilder();
sb.append("[").append(getDirection()).append("]");
sb.append("[").append(getCallId()).append("]");
sb.append("[").append(getTransactionId()).append("]");
sb.append("[").append(getUserId()).append("]");
sb.append("[").append(getMsgType()).append("]");
sb.append("[").append(getSvcId()).append("]");
return sb.toString();
}
@Override
public AbstractCallLogObject cloneObj() throws CloneNotSupportedException {
return (CallObject) this.clone();
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package net.herit.svcplatform.pushservice.commons.logger.dto;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.herit.svcplatform.pushservice.commons.logger.LogTracer;
import net.herit.common.logger.obj.abst.AbstractOmsObject;
public class OmsObject extends AbstractOmsObject implements Cloneable {
public OmsObject request(HttpServletRequest request) {
String svcId = request.getHeader("X-SERVICE-ID");
String terminalId = request.getHeader("X-TERMINAL-ID");
String transactionId = request.getHeader("X-TRANSACTION-ID");
String appType = request.getHeader("X-APP-TYPE");
String xForwardedFor = request.getHeader("X-Forwarded-For");
if (svcId == null)
svcId = "";
if (terminalId == null)
terminalId = "";
if (transactionId == null)
transactionId = "";
if (appType == null)
appType = "";
if (xForwardedFor == null)
xForwardedFor = "";
String apiUri = request.getRequestURI();
String httpVer = request.getProtocol();
setSeqId();
setSvcId(svcId);
setClientIp(getClientIp(xForwardedFor));
setHttpVer(httpVer);
setOsInfo(appType);
setAppType(appType);
setTerminalId(terminalId);
setTxId(transactionId);
setSessionId(transactionId);
setApiUri(apiUri);
setResultCode(0);
return this;
}
public OmsObject uri(String apiUri) {
setApiUri(apiUri);
return this;
}
public OmsObject requestTime(String reqTime) {
setReqTime(reqTime);
return this;
}
public OmsObject response(HttpServletResponse response) {
setStatusCode(response.getStatus());
return this;
}
public OmsObject responseTime(String resTime) {
setResTime(resTime);
return this;
}
public OmsObject httpStatus(int status) {
setStatusCode(status);
return this;
}
public OmsObject resultCode(int responseCode) {
setResultCode(responseCode);
return this;
}
public OmsObject logTrace(LogTracer logTracer) {
if (logTracer != null) {
setLogType(logTracer.logType());
setApiClass(logTracer.apiClass());
addSvcClass(logTracer.svcClass());
setAppType(logTracer.apiAppType());
}
return this;
}
@Override
public int objId() {
return System.identityHashCode(this);
}
/**
* Oms 로그 형식
* ${seqId}|${logTime}|${logType}|${svcId}|${statusCode}|${resultCode}|${reqTime}|${resTime}|${clientIp}|${httpVer}|${osInfo}|${appType}|${terminalId}|${txId}|${userId}|${sessionId}|${apiClass}|${apiUri}|${r1|...|...|r10|}|
*/
@Override
public String toLog() {
StringBuilder sb = new StringBuilder();
sb.append(seqId).append("|");
sb.append(logTime).append("|");
sb.append(logType).append("|");
sb.append(svcId).append("|");
sb.append(statusCode).append("|");
sb.append(resultCode).append("|");
sb.append(reqTime).append("|");
sb.append(resTime).append("|");
sb.append(clientIp).append("|");
sb.append(httpVer).append("|");
sb.append(osInfo).append("|");
sb.append(appType).append("|");
sb.append(terminalId).append("|");
sb.append(txId).append("|");
sb.append(userId).append("|");
sb.append(sessionId).append("|");
sb.append(apiClass).append("|");
sb.append(apiUri).append("|");
sb.append(getSvcClass()).append("|");
sb.append(r1).append("|");
sb.append(r2).append("|");
sb.append(r3).append("|");
sb.append(r4).append("|");
sb.append(r5).append("|");
sb.append(r6).append("|");
sb.append(r7).append("|");
sb.append(r8).append("|");
sb.append(r9).append("|");
sb.append(r10).append("|");
return sb.toString();
}
@Override
public AbstractOmsObject cloneObj() throws CloneNotSupportedException {
OmsObject oms = (OmsObject) this.clone();
oms.svcClassList = new ArrayList<String>(oms.svcClassList);
return oms;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
private static String getClientIp(String xForwardedFor) {
if(xForwardedFor == null || "".equals(xForwardedFor)) return "";
StringTokenizer tokenizer = new StringTokenizer(xForwardedFor, ",");
String clientIp = tokenizer.nextToken();
return clientIp==null?"":clientIp;
}
}
package net.herit.svcplatform.pushservice.commons.logger.dto.model;
public class LoggerConstant {
private LoggerConstant() {
throw new IllegalAccessError("Util class");
}
public static final String APP_TO_SVC = "APP --> SVC";
public static final String SVC_TO_APP = "APP <-- SVC";
public static final String SVC_TO_DB = "SVC --> DB";
public static final String DB_TO_SVC = "SVC <-- DB";
}
package net.herit.svcplatform.pushservice.commons.logger.interceptor;
import java.nio.charset.StandardCharsets;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.commons.dto.model.AuthrizedUser;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestBody;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestSession;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.dto.wrapper.RequestWrapper;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.logger.LogTracer;
import net.herit.svcplatform.pushservice.commons.logger.OmsLogger;
import net.herit.svcplatform.pushservice.commons.logger.dto.CallObject;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
import net.herit.svcplatform.pushservice.commons.logger.dto.model.LoggerConstant;
import net.herit.svcplatform.pushservice.commons.util.BeanUtil;
import net.herit.svcplatform.pushservice.commons.util.DateUtil;
import net.herit.svcplatform.pushservice.commons.util.HttpTypeUtil;
import lombok.RequiredArgsConstructor;
import net.herit.common.logger.context.HeritLoggerContext;
import net.herit.common.logger.properties.LogProperties;
@Component
@RequiredArgsConstructor
public class LoggerInterceptor implements HandlerInterceptor {
private final DateUtil dateUtil;
private final OmsLogger omsLogger;
private final CallLogger callLogger;
private final HttpTypeUtil httpTypeUtil;
private final ObjectMapper objectMapper;
@Autowired
private RequestSession requestSession;
@Autowired
private CallObject callObj;
@Autowired
private OmsObject omsObj;
@Value("${logging.call.multiline}")
private boolean multiline;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
setRequestLogObj(request, handler);
setRequestSession(request);
setResponse(request, response);
startLog(request);
return true;
}
private void setRequestLogObj(HttpServletRequest request, Object handler) {
HeritLoggerContext.applyLogObject(callObj, omsObj); // STEP 1 에서 생성한 로그 객체를 등록하는 과정
omsObj.setReqTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
callObj.request(request);
omsObj.request(request);
setRequestOms(handler);
}
private void setRequestOms(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
LogTracer logTracer = handlerMethod.getMethodAnnotation(LogTracer.class);
omsObj.logTrace(logTracer);
}
}
private void setRequestSession(HttpServletRequest request) {
requestSession.setFromServletRequest(request);
requestSession.setUri(request.getRequestURI());
}
private void setResponse(HttpServletRequest request, HttpServletResponse response) {
String transactionId = request.getHeader("X-TRANSACTION-ID");
response.setHeader("X-TRANSACTION-ID", transactionId);
setResponseOms();
}
private void setResponseOms() {
setUserId();
callObj.setMsgType(LogProperties.MSG_TYPE_RES);
callObj.setDirection(LoggerConstant.SVC_TO_APP);
}
private void setUserId() {
if (requestSession.getBody() != null && requestSession.getBody().getAccount() != null) {
String userId = requestSession.getBody().getAccount().getLoginId();
callObj.setUserId(userId);
omsObj.setUserId(userId);
}
}
private void startLog(HttpServletRequest request) {
String contentType = request.getContentType();
String methodType = request.getMethod();
if (httpTypeUtil.isJsonPost(contentType, methodType) || httpTypeUtil.isJsonGet(contentType, methodType)) {
requestJsonLog(request);
} else if (httpTypeUtil.isFormPost(contentType, methodType)
|| httpTypeUtil.isFormGet(contentType, methodType)) {
requestFormLog(request);
} else if (httpTypeUtil.isFormDataPost(contentType, methodType)
|| httpTypeUtil.isFormDataGet(contentType, methodType)) {
multipartRequestLog(request);
} else if (contentType == null) {
requestBodyNullLog();
}
}
private void requestJsonLog(HttpServletRequest request) {
RequestWrapper requestWrapper = null;
if (request instanceof RequestWrapper) {
requestWrapper = (RequestWrapper) request;
byte[] requestBodyArray = requestWrapper.getRequestBodyByteArray();
String requestBody = new String(requestBodyArray, StandardCharsets.US_ASCII);
try {
JsonNode account = objectMapper.readTree(requestBody).get("account");
AuthrizedUser user = objectMapper.treeToValue(account, AuthrizedUser.class);
RequestBody body = new RequestBody();
body.setAccount(body.new Account(user));
requestSession.setBody(body);
requestCallLog(requestBody, true);
} catch (Exception e) {
try {
requestCallLog(requestBody, false);
} catch (JsonProcessingException e1) {
e1.printStackTrace();
}
}
}
}
private void requestCallLog(String requestBody, boolean isJson) throws JsonProcessingException {
if (isJson) {
JsonNode jsonNode = objectMapper.readTree(requestBody);
requestBody = objectMapper.writeValueAsString(jsonNode);
}
callLog(requestBody);
}
private void requestFormLog(HttpServletRequest request) {
RequestWrapper requestWrapper = null;
if (request instanceof RequestWrapper) {
requestWrapper = (RequestWrapper) request;
byte[] requestBodyArray = requestWrapper.getRequestBodyByteArray();
String requestBody = new String(requestBodyArray, StandardCharsets.US_ASCII);
requestCallLog(requestBody);
}
}
private void multipartRequestLog(HttpServletRequest request) {
if (request instanceof StandardMultipartHttpServletRequest) {
StandardMultipartHttpServletRequest multipartRequest = (StandardMultipartHttpServletRequest) request;
formDataRequestLog(multipartRequest);
}
}
private void formDataRequestLog(StandardMultipartHttpServletRequest request) {
StringBuilder sb = new StringBuilder();
request.getMultiFileMap().forEach((key, value) -> {
sb.append(key).append('=');
for (int i = 0; i < value.size(); i++) {
sb.append(value.get(i).getOriginalFilename());
sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(",")).append('&');
});
request.getParameterMap().forEach((key, value) -> {
sb.append(key).append('=');
for (int i = 0; i < value.length; i++) {
sb.append(value[i]).append(',');
}
sb.deleteCharAt(sb.lastIndexOf(",")).append('&');
});
sb.deleteCharAt(sb.lastIndexOf("&"));
callLog(sb.toString());
}
private void requestCallLog(String requestBody) {
try {
requestCallLog(requestBody, false);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
private void requestBodyNullLog() {
callLog(null);
}
private void callLog(String requestBody) {
String uri = requestSession.getUri();
String requestHeader = null;
try {
requestHeader = requestSession.getHeader().getHeaderMap() == null ? ""
: objectMapper.writeValueAsString(requestSession.getHeader().getHeaderMap());
} catch (JsonProcessingException e) {
callLogger.error("request header parsing error");
}
StringBuilder sb = new StringBuilder();
if (multiline) {
sb.append(System.lineSeparator()).append("Request Uri").append(" : ").append(uri)
.append(System.lineSeparator()).append("Header").append(" : ").append(requestHeader)
.append(System.lineSeparator()).append("Body").append(" : ").append(requestBody)
.append(System.lineSeparator());
} else {
sb.append("Request Uri").append(" : ").append(uri).append(" ").append("Header").append(" : ")
.append(requestHeader).append(" ").append("Body").append(" : ").append(requestBody);
}
callLogger.info(sb.toString());
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
HttpTypeUtil httpTypeUtil = getHttpTypeUtil();
if (httpTypeUtil != null && httpTypeUtil.isOctetStream(response.getContentType())) {
String handlerMethod = handler.toString();
String contentDisposition = response.getHeader("Content-Disposition");
octetStreamLog(handlerMethod, contentDisposition);
}
HeritLoggerContext.resetAttributes(); // 로그 객체 초기화
}
private void octetStreamLog(String handlerMethod, String contentDisposition) {
omsObj.resultCode(HttpResponseStatus.SUCCESS.getCode()).httpStatus(HttpResponseStatus.SUCCESS.getResCode())
.responseTime(dateUtil.generateTimeStamp("yyyyMMddHHmmssSSS"));
if (multiline) {
callLogger.info("\nMethod: {}, \n{}", handlerMethod, contentDisposition);
} else {
callLogger.info("Method: {}, {}", handlerMethod, contentDisposition);
}
omsLogger.write();
}
private HttpTypeUtil getHttpTypeUtil() {
HttpTypeUtil bean = null;
Object obj = BeanUtil.getBean("httpTypeUtil");
if (obj instanceof HttpTypeUtil) {
bean = (HttpTypeUtil) obj;
}
return bean;
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.logger.interceptor;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
@Intercepts({
@Signature(type = StatementHandler.class, method = "update", args = {Statement.class}),
@Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
})
public class SqlLoggingInterceptor implements Interceptor {
@Autowired
private CallLogger callLogger;
@Value("${logging.call.multiline}")
private boolean multiline;
@Value("${logging.call.except-query-log}")
private boolean exceptQueryLog;
@Override
public Object intercept(Invocation invocation) throws Exception {
long start = System.currentTimeMillis();
Object origin = invocation.proceed();
long end = System.currentTimeMillis();
if(!exceptQueryLog) {
StatementHandler handler = (StatementHandler) invocation.getTarget();
Object parameter = handler.getParameterHandler().getParameterObject();
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
sql = getConstantQuery(sql, parameter, boundSql);
StringBuilder sb = new StringBuilder();
if(multiline) {
sb.append(System.lineSeparator());
sb.append("Time (millisecond)").append(" >> ").append(end-start).append(System.lineSeparator());
sb.append("Query").append(" >> ").append(sql).append(System.lineSeparator());
sb.append("Param").append(" >> ").append(parameter);
} else {
sql = sql.replaceAll("\n", "").replaceAll("\t", " ");
sb.append("Time (millisecond)").append(" >> ").append(end-start).append(", ");
sb.append("Query").append(" >> ").append(sql).append(", ");
sb.append("Param").append(" >> ").append(parameter);
}
callLogger.info("{}", sb.toString());
}
return origin;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
Interceptor.super.setProperties(properties);
}
public String getConstantQuery(String sql, Object parameter, BoundSql boundSql ) throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException {
if (parameter == null) {
sql = sql.replaceFirst("\\?", "''");
} else {
if(parameter instanceof Integer || parameter instanceof Long || parameter instanceof Float || parameter instanceof Double) {
sql = sql.replaceFirst("\\?", parameter.toString());
} else if(parameter instanceof String) {
sql = sql.replaceFirst("\\?", "'" + parameter.toString() + "'");
} else if (parameter instanceof Map) {
List<ParameterMapping> paramMapping = boundSql.getParameterMappings();
Object value = null;
for(ParameterMapping mapping : paramMapping) {
String key = mapping.getProperty();
if(boundSql.hasAdditionalParameter(key)) {
value = boundSql.getAdditionalParameter(key);
} else {
value = ((Map<?, ?>)parameter).get(key);
}
if(value instanceof String) {
sql = sql.replaceFirst("\\?", "'" + value + "'");
} else if(value != null) {
sql = sql.replaceFirst("\\?", value.toString());
} else {
sql = sql.replaceFirst("\\?", "''");
}
}
} else {
List<ParameterMapping> paramMapping = boundSql.getParameterMappings();
Class<? extends Object> paramClass = parameter.getClass();
for(ParameterMapping mapping : paramMapping) {
String key = mapping.getProperty();
Field field = paramClass.getDeclaredField(key);
if(field == null) {
sql = sql.replaceFirst("\\?", "''");
} else {
field.setAccessible(true);
Class<?> javaType = mapping.getJavaType();
if(String.class == javaType) {
String sqlParam = "'" + field.get(parameter) + "'";
sql = sql.replaceFirst("\\?", sqlParam);
} else {
String sqlParam = null;
if(field.get(parameter) == null) {
sqlParam = "NULL";
} else {
sqlParam = field.get(parameter).toString();
}
sql = sql.replaceFirst("\\?", sqlParam);
}
}
}
}
}
return sql;
}
}
package net.herit.svcplatform.pushservice.commons.transaction;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.RequiredArgsConstructor;
@Aspect
@Component
@RequiredArgsConstructor
@Profile(value = {"local", "dev"})
public class DBTransactionAop {
private final DataSourceTransactionManager transactionManager;
@Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)")
public void postMapping() {
}
@Around("postMapping()")
public Object serviceExceptionHandler(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder
.currentRequestAttributes()).getRequest();
Object obj = proceedingJoinPoint.proceed();
if(checkSwagger(httpServletRequest) && !httpServletRequest.getRequestURI().contains("list")) {
rollback();
}
return obj;
}
private boolean checkSwagger(HttpServletRequest request) {
return request.getHeader("Api-Test-Tools") != null && request.getHeader("Api-Test-Tools").equals("Swagger");
}
private void rollback() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("swagger-rollback");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
transactionManager.rollback(status);
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.util;
import org.springframework.context.ApplicationContext;
import net.herit.svcplatform.pushservice.commons.context.ApplicationContextProvider;
public class BeanUtil {
private BeanUtil() {
throw new IllegalAccessError();
}
public static Object getBean(String beanId) {
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
if( applicationContext == null ) {
throw new NullPointerException("Spring의 ApplicationContext init fail");
}
return applicationContext.getBean(beanId);
}
}
package net.herit.svcplatform.pushservice.commons.util;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Component
public class DateTimeUtil {
DateTimeFormatter yyyyMMddHHmm = DateTimeFormatter.ofPattern("uuuuMMddHHmm", Locale.KOREA);
DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("uuuuMMddHHmmss", Locale.KOREA);
DateTimeFormatter yyyyMMddHHmmssSSS = DateTimeFormatter.ofPattern("uuuuMMddHHmmssSSS", Locale.KOREA);
public String getNow_yyyyMMddHHmmssSSS() {
return LocalDateTime.now().format(yyyyMMddHHmmssSSS);
}
public String getNow_yyyyMMddHHmmss() {
return LocalDateTime.now().format(yyyyMMddHHmmss);
}
public String getNow_yyyyMMddHHmm() {
return LocalDateTime.now().format(yyyyMMddHHmm);
}
public long getNow_epochMillis() {
return Instant.now().toEpochMilli();
}
public long getNow_epochNano() {
return System.nanoTime();
}
public List<String> getPrevDateTimes(String dateTime, int prevDays) {
List<String> prevDateTimes = new ArrayList<>();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.KOREA);
LocalDateTime drStartTimeLocal = LocalDateTime.parse(dateTime, dateTimeFormatter);
for(int i=1; i<=prevDays; i++) {
prevDateTimes.add(drStartTimeLocal.minusDays(i).format(dateTimeFormatter));
}
return prevDateTimes;
}
}
package net.herit.svcplatform.pushservice.commons.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.stereotype.Component;
@Component
public class DateUtil {
public String generateTimeStamp(String format) {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));
}
}
package net.herit.svcplatform.pushservice.commons.util;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
@Component
public class HttpTypeUtil {
public boolean isJsonPost(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.APPLICATION_JSON_VALUE, HttpMethod.POST);
}
public boolean isFormPost(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.APPLICATION_FORM_URLENCODED_VALUE, HttpMethod.POST);
}
public boolean isFormDataPost(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.MULTIPART_FORM_DATA_VALUE, HttpMethod.POST);
}
public boolean isFormDataGet(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.MULTIPART_FORM_DATA_VALUE, HttpMethod.GET);
}
public boolean isJsonGet(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.APPLICATION_JSON_VALUE, HttpMethod.GET);
}
public boolean isFormGet(String contentType, String methodType) {
return isEqualsType(contentType, methodType, MediaType.APPLICATION_FORM_URLENCODED_VALUE, HttpMethod.GET);
}
public boolean isOctetStream(String contentType) {
return contentType != null &&
contentType.equals(MediaType.APPLICATION_OCTET_STREAM_VALUE);
}
private boolean isEqualsType(String expectContentType, String expectMethodType, String actualContentType,
HttpMethod actualMethodType) {
return expectContentType != null && expectContentType.contains(actualContentType)
&& actualMethodType.matches(expectMethodType);
}
}
package net.herit.svcplatform.pushservice.commons.util;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;
import java.util.UUID;
@Component
public class IdGenerator {
private final DateTimeUtil dateTimeUtil;
public IdGenerator(DateTimeUtil dateTimeUtil) {
this.dateTimeUtil = dateTimeUtil;
}
/**
* 알파벳(대소문자)+숫자로 조합된 문자열을 반환합니다.
*
* @param length : 문자열 길이(최소값 5, 최대값 64)
* @return
*/
public String makeRandomAlphaNumeric(int length) {
if(length < 5) {
length = 5;
}
if(length > 64) {
length = 64;
}
return RandomStringUtils.randomAlphanumeric(length);
}
/**
* 알파벳(대소문자)으로 조합된 문자열을 반환합니다.
*
* @param length : 문자열 길이(최소값 5, 최대값 64)
* @return
*/
public String makeRandomAlpha(int length) {
if(length < 5) {
length = 5;
}
if(length > 64) {
length = 64;
}
return RandomStringUtils.randomAlphanumeric(length);
}
/**
* UUID 를 생성해서 반환합니다.
*
* @return
*/
public String makeUUID() {
return UUID.randomUUID().toString();
}
/**
* UUID 를 생성해서 하이픈(-)을 제거한 UUID 를 반환합니다.
*
* @return
*/
public String makeUUIDWithoutHypen() {
return makeUUID().replaceAll("-", "");
}
/**
* 랜덤으로 생성된 트랜젝션 아이디를 반환합니다.
* 포맷 : yyyyMMddHHmmss.12자리영문(대소문자)숫자조합
* @return
*/
public String makeTxId() {
return dateTimeUtil.getNow_yyyyMMddHHmmss() +"."+ makeRandomAlphaNumeric(5);
}
}
package net.herit.svcplatform.pushservice.commons.util;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* JsonUtil
*/
@Component
public class JsonUtil {
private ObjectMapper objectMapper;
public JsonUtil() {
objectMapper = new ObjectMapper();
}
public String objectToJson(Object object){
String requestBody = null;
try {
requestBody = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return requestBody;
}
public String stringToPrettyJson(String message) throws JsonProcessingException {
Object jsonObject = objectMapper.readValue(message, Object.class);
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.util;
import java.security.Key;
import java.util.Calendar;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import net.herit.svcplatform.pushservice.commons.jwt.properties.JwtProperties;
@Component
public class JwtUtil {
private final String ACCESS_SECRET_KEY; // ACCESS_TOKEN secret KEY
private final String REFRESH_SECRET_KEY; // REFRESH_TOKEN secret KEY
private final String ISSUER; // 토큰발급자
private final String ACCESS_TOKEN_SUBJECT; // ACCESS_TOKEN 토큰제목
private final String REFRESH_TOKEN_SUBJECT; // REFRESH_TOKEN 토큰제목
private final String AUDIENCE; // 토큰대상자
private final String TYPE_KEY; // 토큰유형 서버
private final String TYPE_CLICENT; // 토큰유형1 클라이언트
private final String TYPE_SERVER; // 토큰유형2 서버
@Autowired
public JwtUtil(JwtProperties jwtProperties) {
ACCESS_SECRET_KEY = jwtProperties.getAccessTokenSecret();
REFRESH_SECRET_KEY = jwtProperties.getRefreshTokenSecret();
ISSUER = jwtProperties.getIssuer();
ACCESS_TOKEN_SUBJECT = jwtProperties.getAccessTokenSecret();
REFRESH_TOKEN_SUBJECT = jwtProperties.getRefreshTokenSecret();
AUDIENCE = jwtProperties.getAudience();
TYPE_KEY = jwtProperties.getTypeKey();
TYPE_CLICENT = jwtProperties.getTypeClient();
TYPE_SERVER = jwtProperties.getTypeServer();
}
/**
* 서버 액세스토큰 발급
* @param audience 토큰발급자
* @param ttlMillis 만료시간
* @return String 토큰
*/
public String createServerAccessToken(String adu, int hour) {
Date now = new Date(System.currentTimeMillis());
Date exp = getCalculationTokenExpirationDate(hour);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(ACCESS_SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder().setIssuer(ISSUER)
.setSubject(ACCESS_TOKEN_SUBJECT)
.setAudience((adu!=null || !"".equals(adu))? adu:AUDIENCE)
.claim(TYPE_KEY, TYPE_SERVER)
.setIssuedAt(now)
.setExpiration(exp)
.signWith(signatureAlgorithm, signingKey);
return builder.compact();
}
/**
* 클라이언트 액세스토큰 발급
* @param audience 토큰발급자
* @param ttlMillis 만료시간
* @return String 토큰
*/
public String createClientAccessToken(String adu, int hour) {
Date now = new Date(System.currentTimeMillis());
Date exp = getCalculationTokenExpirationDate(hour);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(ACCESS_SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder().setIssuer(ISSUER)
.setSubject(ACCESS_TOKEN_SUBJECT)
.setAudience((adu!=null || !"".equals(adu))? adu:AUDIENCE)
.claim(TYPE_KEY, TYPE_CLICENT)
.setIssuedAt(now)
.setExpiration(exp)
.signWith(signatureAlgorithm, signingKey);
return builder.compact();
}
/**
* 액세스토큰 해석
* @param jwt 토큰데이터
* @return Claims 해석데이터 Map형식과 유사
*/
public Claims decodeAccessToken(String jwt) {
return Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(ACCESS_SECRET_KEY))
.parseClaimsJws(jwt).getBody();
}
/**
* 리프레시토큰 발급
* @param adu
* @param ttlMillis
* @return String 토큰
*/
public String createRefreshToken(String adu, int hour) {
Date now = new Date(System.currentTimeMillis());
Date exp = getCalculationTokenExpirationDate(hour);
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(REFRESH_SECRET_KEY);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
JwtBuilder builder = Jwts.builder().setIssuer(ISSUER)
.setSubject(REFRESH_TOKEN_SUBJECT)
.setAudience((adu!=null || !"".equals(adu))? adu:AUDIENCE)
.setIssuedAt(now)
.setExpiration(exp)
.signWith(signatureAlgorithm, signingKey);
return builder.compact();
}
/**
* 리프래시 토큰 해석
* @param jwt
* @return Claims 해석데이터 Map형식과 유사
*/
public Claims decodeRefreshToken(String jwt) {
return Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(REFRESH_SECRET_KEY))
.parseClaimsJws(jwt).getBody();
}
public Date getCalculationTokenExpirationDate(int hour) {
Date now = new Date(System.currentTimeMillis());
Calendar cal = Calendar.getInstance();
cal.setTime(now);
cal.add(Calendar.HOUR, hour);
return new Date(cal.getTimeInMillis());
}
}
package net.herit.svcplatform.pushservice.commons.util;
public class StaticUtils {
private StaticUtils() {
throw new IllegalAccessError("Utility class");
}
public static boolean checkSize(int min, int max, String data) throws Exception {
if (data == null) return false;
int size = data.length();
if (min < 0) min = 0;
if (min > max) throw new Exception("'min' 값은 'max'값 보다 클 수 없습니다.");
if (max <= 0) throw new Exception("'max' 값은 0보다 작거나 같을 수 없습니다.");
if (size > max || size < min) return false;
return true;
}
public static boolean checkNotEmpty(String... strings) {
for(String value : strings) {
if(value == null || "".equals(value)) {
return false;
}
}
return true;
}
}
package net.herit.svcplatform.pushservice.commons.validation;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.validation.body.CustomValid;
@CustomValid
public interface CustomValidation {
public String isValid() throws CommonException;
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.validation.body;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Documented
@Constraint(validatedBy = CustomValidImpl.class)
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomValid {
String message() default "";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.validation.body;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.validation.CustomValidation;
public class CustomValidImpl implements ConstraintValidator<CustomValid, CustomValidation>{
@Autowired
private CallLogger call;
@Override
public boolean isValid(CustomValidation value, ConstraintValidatorContext context) {
String result = null;
try {
result = value.isValid();
} catch (Exception e) {
addViolation(context);
return false;
}
if( ! result.equals("SUCCESS")){
call.info(result);
addViolation(context);
return false;
}
return true;
}
private void addViolation(ConstraintValidatorContext ctx) {
ctx.disableDefaultConstraintViolation();
ctx.buildConstraintViolationWithTemplate(
HttpResponseStatus.INVALID_PARAM
.getDescription())
.addConstraintViolation();
}
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.commons.validation.header;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.logger.OmsLogger;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
@Component
public class HeaderCheckInterceptor implements HandlerInterceptor {
@Autowired
OmsObject omsObject;
@Autowired
OmsLogger omsLogger;
@Autowired
CallLogger callLogger;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(handler instanceof ResourceHttpRequestHandler) {
request.setAttribute("svc_uri", request.getRequestURI());
return true;
}
if(! RequestHeaderValidation.isValid(request)) {
callLogger.info("RequestHeader validation Check is fail");
setFailResponse(response);
return false;
}
return true;
}
private void setFailResponse(HttpServletResponse response) throws IOException {
omsObject.setResultCode(HttpResponseStatus.INVALID_AUTH_TOKEN.getResCode());
omsObject.setStatusCode(HttpResponseStatus.INVALID_PARAM.getResCode());
omsLogger.write();
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(HttpResponseStatus.getErrorResponseToJson(HttpResponseStatus.INVALID_PARAM));
response.setStatus(HttpResponseStatus.INVALID_PARAM.getResCode());
}
}
package net.herit.svcplatform.pushservice.commons.validation.header;
import javax.servlet.http.HttpServletRequest;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestHeader;
import net.herit.svcplatform.pushservice.commons.util.StaticUtils;
public class RequestHeaderValidation {
private RequestHeaderValidation() {
throw new IllegalStateException("Utility class");
}
public static boolean isValid(HttpServletRequest request) {
RequestHeader requestHeader = new RequestHeader(request);
boolean result = false;
try {
result = headerLengthCheck(requestHeader);
} catch (Exception e) {
return false;
}
return result;
}
protected static boolean headerLengthCheck(RequestHeader requestHeader) throws Exception {
String xServiceId = requestHeader.getXServiceId();
String xTerminalId = requestHeader.getXTerminalId();
String xAppType = requestHeader.getXAppType();
return StaticUtils.checkSize(0, 20, xServiceId) &&
StaticUtils.checkSize(0, 50, xTerminalId) &&
StaticUtils.checkSize(0, 50, xAppType)
;
}
}
package net.herit.svcplatform.pushservice.features.auth;
import net.herit.svcplatform.pushservice.commons.dto.request.AuthRequest;
import net.herit.svcplatform.pushservice.features.logger.DefaultModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class AuthRequestDto extends AuthRequest {
private DefaultModel data;
}
package net.herit.svcplatform.pushservice.features.auth;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.ApiOperation;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestSession;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.features.logger.DefaultModel;
import net.herit.svcplatform.pushservice.features.logger.ResponseDto;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class AuthTestController {
private final CallLogger callLogger;
@Autowired
private RequestSession requestSession;
@PostMapping("/api/1.0/auth/test")
@ApiOperation(value = "[Test] auth Post 테스트", notes = "참조 테이블 : 없음")
public ResponseEntity<ResponseDto> authTestController(
@RequestBody AuthRequestDto requestBody) {
callLogger.info("loginId: {}", requestSession.getBody().getAccount().getLoginId());
callLogger.info("authToken: {}", requestSession.getBody().getAccount().getAuthToken());
callLogger.info("xServiceId: {}", requestSession.getHeader().getXServiceId());
callLogger.info("xAppType: {}", requestSession.getHeader().getXAppType());
callLogger.info("xTerminalId: {}", requestSession.getHeader().getXTerminalId());
callLogger.info("uri: {}", requestSession.getUri());
return ResponseEntity.ok(new ResponseDto(new SuccessModel()));
}
@NonAuth
@PostMapping("/api/1.0/auth/non/test")
@ApiOperation(value = "[Test] non auth Post 테스트", notes = "참조 테이블 : 없음")
public ResponseEntity<ResponseDto> nonAuthTestController(
@RequestBody AuthRequestDto requestBody) {
DefaultModel model = new DefaultModel();
model.setData("it is data");
return ResponseEntity.ok(new ResponseDto(new SuccessModel()));
}
}
package net.herit.svcplatform.pushservice.features.feign;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.annotations.ApiOperation;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.features.logger.RequestDto;
import net.herit.svcplatform.pushservice.features.logger.ResponseDto;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class FeignController {
private final CallLogger callLogger;
private final FeignService feignService;
@NonAuth
@PostMapping("/api/1.0/feign/post")
@ApiOperation(value = "[Test] feign Post 테스트", notes = "참조 테이블 : 없음")
public ResponseEntity<ResponseDto> postMapping(@RequestBody RequestDto requestBody) {
callLogger.info("feign/post");
ParamA a = new ParamA();
a.data = "data";
ParamB paramB = feignService.postMapping("0", a);
callLogger.info("{}", paramB);
return ResponseEntity.ok(new ResponseDto(new SuccessModel()));
}
@GetMapping("/api/1.0/feign/get")
@ApiOperation(value = "[Test] feign get 테스트", notes = "참조 테이블 : 없음")
public ResponseEntity<ResponseDto> getMapping(@RequestBody RequestDto requestBody) {
callLogger.info("feign/get");
return ResponseEntity.ok(new ResponseDto(new SuccessModel()));
}
}
package net.herit.svcplatform.pushservice.features.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import feign.Headers;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
@FeignClient(name = "test", url = "http://localhost:8080")
public interface FeignService {
@NonAuth
@Headers({
"name: post"
})
@PostMapping("/feign/test/post")
public ParamB postMapping(@RequestHeader("name") String name, ParamA data);
@Headers({
"name: get"
})
@GetMapping("/feign/test/get")
public ParamB getMapping(@RequestHeader("name") String name, ParamA data);
}
package net.herit.svcplatform.pushservice.features.feign;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import lombok.RequiredArgsConstructor;
@RestController
@RequiredArgsConstructor
public class FeignTestController {
private final CallLogger callLogger;
@RequestMapping(value = "/feign/test/post", method = {RequestMethod.POST})
public ResponseEntity<ParamB> feignSamplePost(
HttpServletRequest request,
ParamA data) {
callLogger.info("FeignSampleController..............");
return ResponseEntity.ok(new ParamB(request.getHeader("name")));
}
@RequestMapping(value = "/feign/test/get", method = {RequestMethod.GET, RequestMethod.POST})
public ResponseEntity<ParamB> feignSampleGet(
HttpServletRequest request,
@RequestBody ParamA data) {
callLogger.info("FeignSampleController..............");
return ResponseEntity.ok(new ParamB(request.getHeader("name")));
}
}
package net.herit.svcplatform.pushservice.features.feign;
import lombok.Data;
@Data
public class ParamA {
String data;
}
package net.herit.svcplatform.pushservice.features.feign;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ParamB {
String result;
}
package net.herit.svcplatform.pushservice.features.feign;
import org.springframework.web.bind.annotation.RequestBody;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import net.herit.svcplatform.pushservice.features.feign.dto.model.PushFeignBody;
public interface PushFeignClient {
@RequestLine("POST {uri}")
@Headers({"Content-Type: application/json", "X-PROJECT-ID: {xProjectId}"})
void send(@Param("uri") String uri, @Param("xProjectId") String xProjectId, @RequestBody PushFeignBody requestBody);
}
package net.herit.svcplatform.pushservice.features.feign.dto.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushFeignBody {
private PushFeignBodyMessage message;
private PushFeignBodyAccount account;
}
package net.herit.svcplatform.pushservice.features.feign.dto.model;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushFeignBodyAccount {
@JsonProperty("push_token")
private String pushToken;
}
package net.herit.svcplatform.pushservice.features.feign.dto.model;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import net.herit.svcplatform.pushservice.features.kafka.dto.model.PushKafkaData;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushFeignBodyMessage {
private Map<String, Object> data;
private Map<String, Object> android;
private String title;
private String body;
private Map<String, Object> ios;
}
package net.herit.svcplatform.pushservice.features.kafka;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.features.send.PushRequestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class KafkaConsumer {
@Autowired
private CallLogger call;
@Autowired
private PushRequestService pushRequestService;
@Autowired
@Qualifier("push-request-executor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Value("${thread-pool.run-delay-ms}")
private int runDelay;
@KafkaListener(topics = "${spring.kafka.consumer.topic}")
public void consume(String message) {
call.info("message consumed:{}", message);
try {
TimeUnit.MILLISECONDS.sleep(runDelay);
threadPoolTaskExecutor.execute(() -> pushRequestService.request(message));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
package net.herit.svcplatform.pushservice.features.kafka.dto.model;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushKafkaData {
private Map<String, Object> data;
private String title;
private String body;
private String pushToken;
private String xProjectId;
}
package net.herit.svcplatform.pushservice.features.kafka.dto.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushKafkaHeader {
private String txId;
private String from;
private String to;
private String ti;
}
package net.herit.svcplatform.pushservice.features.kafka.dto.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
@Builder
public class PushKafkaMessage{
private PushKafkaHeader header;
private PushKafkaData data;
}
package net.herit.svcplatform.pushservice.features.logger;
import javax.validation.constraints.NotNull;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class DefaultModel {
@NotNull
@ApiModelProperty(required = true, example = "data")
public String data;
}
package net.herit.svcplatform.pushservice.features.logger;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import javax.validation.Valid;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import io.swagger.annotations.ApiOperation;
import net.herit.svcplatform.pushservice.commons.auth.NonAuth;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.logger.LogTracer;
@RestController
public class LogTestController {
private static final String ERROR = "error";
@NonAuth
@PostMapping("/api/1.0/post")
@ApiOperation(value = "[Test] Json Post 테스트", notes = "참조 테이블 : 없음")
@LogTracer(apiAppType = "postman", logType = "API", apiClass = "POST_TEST", svcClass = "JSON")
public ResponseEntity<ResponseDto> postController(@RequestBody @Valid RequestDto requestBody)
throws CommonException {
if (requestBody.getData().getData().equals(ERROR)) {
throw new CommonException(HttpResponseStatus.SYSTEM_ERROR);
}
return ResponseEntity.ok(responseDto());
}
@NonAuth
@PostMapping(value = "/multiupload", produces = MediaType.APPLICATION_JSON_VALUE, consumes = {
MediaType.MULTIPART_FORM_DATA_VALUE })
public ResponseEntity<ResponseDto> multiUpload(@RequestParam(value = "data", required = true) @Valid String data,
@RequestPart(value = "filename", required = true) MultipartFile[] files) throws CommonException {
if (data.equals(ERROR)) {
throw new CommonException(HttpResponseStatus.SYSTEM_ERROR);
}
return ResponseEntity.ok(responseDto());
}
@GetMapping("/api/1.0/get")
@ApiOperation(value = "[Test] Json Get 테스트", notes = "참조 테이블 : 없음")
@LogTracer(apiAppType = "postman", logType = "API", apiClass = "GET_TEST", svcClass = "JSON")
public ResponseEntity<ResponseDto> getController(@RequestBody @Valid RequestDto requestBody)
throws CommonException {
if (requestBody.getData().getData().equals(ERROR)) {
throw new CommonException(HttpResponseStatus.SYSTEM_ERROR);
}
ResponseDto dto = new ResponseDto(new SuccessModel());
return ResponseEntity.ok(dto);
}
@GetMapping(value = "/api/1.0/get/form/urlencoded", produces = MediaType.APPLICATION_JSON_VALUE, consumes = {
MediaType.APPLICATION_FORM_URLENCODED_VALUE })
@ApiOperation(value = "[Test] form-urlencoded Get 테스트", notes = "참조 테이블 : 없음")
@LogTracer(apiAppType = "postman", logType = "API", apiClass = "GET_TEST", svcClass = "FORM_URL_ENCODED")
public ResponseEntity<ResponseDto> getFormUrlEncodedController(@RequestBody MultiValueMap<String, String> formData)
throws CommonException {
String data = formData.getFirst("data");
if (data == null || data.equals(ERROR)) {
throw new CommonException(HttpResponseStatus.SYSTEM_ERROR);
}
return ResponseEntity.ok(responseDto());
}
@NonAuth
@ApiOperation(value = "[Test] healthcheck Get&Post 테스트", notes = "참조 테이블 : 없음")
@RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, value = "/api/1.0/healthcheck")
@LogTracer(apiAppType = "postman", logType = "API", apiClass = "GET&POST_TEST", svcClass = "CONTENT_TYPE_NULL")
public ResponseEntity<ResponseDto> healthCheck() {
return ResponseEntity.ok(responseDto());
}
private ResponseDto responseDto() {
return new ResponseDto(new SuccessModel());
}
/**
* Response 로그 확인을 위한 multipart_form_data 응답 컨트롤러.
* 파일의 경우는 컴퓨터에 따라 없을수도있으므로 테스트는 따로 만들지 않음.
* @return
* @throws FileNotFoundException
*/
@Deprecated
@GetMapping(value = "/filedownload")
public ResponseEntity<Resource> filedownload() throws FileNotFoundException {
File file = new File("C:\\Users\\mysend\\Pictures\\energyplus.PNG");
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
.contentLength(file.length()).contentType(MediaType.parseMediaType("application/octet-stream"))
.body(resource);
}
}
package net.herit.svcplatform.pushservice.features.logger;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import io.swagger.annotations.ApiModelProperty;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
import net.herit.svcplatform.pushservice.commons.validation.CustomValidation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RequestDto implements CustomValidation {
@Valid @NotNull
@ApiModelProperty(required = true)
private DefaultModel data;
@Override
public String isValid() throws CommonException {
String result = "SUCCESS";
if (data.getData().equals("error2")) {
result = "error";
}
return result;
}
}
package net.herit.svcplatform.pushservice.features.logger;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.dto.response.Response;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResponseDto implements Response {
@Valid @NotNull
private SuccessModel result;
@Override
public int resultCode() {
return result.getCode();
}
}
package net.herit.svcplatform.pushservice.features.send;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import feign.AsyncFeign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import net.herit.common.logger.context.HeritLoggerContext;
import net.herit.svcplatform.pushservice.commons.dto.enums.ApiClasses;
import net.herit.svcplatform.pushservice.commons.dto.enums.AppInfos;
import net.herit.svcplatform.pushservice.commons.dto.enums.SvcClasses;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.logger.OmsLogger;
import net.herit.svcplatform.pushservice.commons.logger.dto.CallObject;
import net.herit.svcplatform.pushservice.commons.logger.dto.OmsObject;
import net.herit.svcplatform.pushservice.features.feign.PushFeignClient;
import net.herit.svcplatform.pushservice.features.feign.dto.model.PushFeignBody;
import net.herit.svcplatform.pushservice.features.feign.dto.model.PushFeignBodyAccount;
import net.herit.svcplatform.pushservice.features.feign.dto.model.PushFeignBodyMessage;
import net.herit.svcplatform.pushservice.features.kafka.dto.model.PushKafkaMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionException;
@Service
public class PushRequestService {
@Autowired
private CallLogger call;
@Autowired
private OmsLogger oms;
@Autowired
private ObjectMapper mapper;
@Value("${feign.push.host}")
private String pushHost;
@Value("${feign.push.uri}")
private String pushUri;
public void request(String message) {
CallObject callObject = new CallObject();
OmsObject omsObject = new OmsObject();
PushKafkaMessage msg;
try {
msg = mapper.readValue(message, PushKafkaMessage.class);
callObject.init();
String txId = msg.getHeader().getTxId();
callObject.setTransactionId(txId);
HeritLoggerContext.applyLogObject(callObject, omsObject, true);
omsObject.setReqTime();
omsObject.setTxId(txId);
omsObject.setLogType(AppInfos.LOG_TYPE);
omsObject.setSvcId(AppInfos.SVC_ID);
omsObject.setAppType(AppInfos.APP_TYPE);
omsObject.setApiClass(ApiClasses.PUSH_CONSUME.getCode());
omsObject.addSvcClass(SvcClasses.PUSH_REQ.getCode());
// push api 전송 규격 생성
PushFeignBodyMessage feignBody = PushFeignBodyMessage.builder().data(msg.getData().getData())
.android(defaultAndroidMap())
.title(msg.getData().getTitle())
.body(msg.getData().getBody())
.ios(defaultIosMap())
.build();
PushFeignBodyAccount feignAccount = PushFeignBodyAccount.builder()
.pushToken(msg.getData().getPushToken())
.build();
PushFeignBody reqBody = PushFeignBody.builder()
.message(feignBody)
.account(feignAccount)
.build();
call.debug("push-reqBody:{}", mapper.writeValueAsString(reqBody));
omsObject.setR1(msg.getData().getPushToken());
AsyncFeign.asyncBuilder()
.encoder(new JacksonEncoder(new ObjectMapper()))
.decoder(new JacksonDecoder(new ObjectMapper()))
.target(PushFeignClient.class, pushHost)
.send(pushUri, msg.getData().getXProjectId(), reqBody);;
omsObject.setStatusCode(HttpResponseStatus.SUCCESS.getResCode());
omsObject.setResultCode(HttpResponseStatus.SUCCESS.getCode());
} catch (JsonProcessingException e) {
call.error(e.toString());
omsObject.setStatusCode(HttpResponseStatus.JSON_PARSING_ERROR.getResCode());
omsObject.setResultCode(HttpResponseStatus.JSON_PARSING_ERROR.getCode());
throw new RuntimeException(e);
} catch (CompletionException e) {
call.error(e.toString());
omsObject.setStatusCode(HttpResponseStatus.ERROR_EXT_CONNECT.getResCode());
omsObject.setResultCode(HttpResponseStatus.ERROR_EXT_CONNECT.getCode());
} catch (Exception e) {
call.error(e.toString());
omsObject.setStatusCode(HttpResponseStatus.SYSTEM_ERROR.getResCode());
omsObject.setResultCode(HttpResponseStatus.SYSTEM_ERROR.getCode());
} finally {
omsObject.setResTime();
oms.write(omsObject);
}
}
public Map<String, Object> defaultAndroidMap(){
Map<String, String> notification = new HashMap<>();
notification.put("click_action", "ALERT");
Map<String, Object> android = new HashMap<>();
android.put("notification", notification);
return android;
}
public Map<String, Object> defaultIosMap(){
Map<String, String> payload = new HashMap<>();
payload.put("category", "ALERT");
Map<String, Object> ios = new HashMap<>();
ios.put("payload", payload);
return ios;
}
Map<String, String> makeEchallApiRequestHeaders(String xProjectId) {
Map<String, String> headers = new HashMap<>();
headers.put("Content-Type", "application/json");
headers.put("X-PROJECT-ID", xProjectId);
return headers;
}
}
package net.herit.svcplatform.pushservice.base.bases;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ActiveProfiles;
import lombok.extern.slf4j.Slf4j;
import net.herit.svcplatform.pushservice.base.watcher.TestResultCounter;
import net.herit.svcplatform.pushservice.base.watcher.TestResultHandler;
@Slf4j
@ExtendWith(TestResultHandler.class)
@ActiveProfiles(value = {"local", "test"})
public class BaseTest {
@AfterAll
public static void printResultCount() {
log.info("successCount: " + TestResultCounter.successCount);
log.info(TestResultCounter.successDisplayNames.toString());
log.info("------------------------------------------");
log.info("failCount: " + TestResultCounter.failCount);
log.info(TestResultCounter.failDisplayNames.toString());
log.info("------------------------------------------");
log.info("abortedCount: " + TestResultCounter.abortedDisplayNames);
log.info(TestResultCounter.abortedDisplayNames.toString());
log.info("------------------------------------------");
log.info("disabledCount: " + TestResultCounter.disabledCount);
log.info(TestResultCounter.disabledDisplayNames.toString());
}
}
package net.herit.svcplatform.pushservice.base.bases;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
@AutoConfigureMockMvc
@ExtendWith(MockitoExtension.class)
public class MockitoBase extends BaseTest {
protected MockMvc mockMvc;
protected static ObjectMapper objectMapper;
@BeforeEach
public void setUp() {
objectMapper = new ObjectMapper();
}
}
package net.herit.svcplatform.pushservice.base.bases;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@SpringBootTest
public class RepositoryBase extends BaseTest {
}
\ No newline at end of file
package net.herit.svcplatform.pushservice.base.bases;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentationContextProvider;
import org.springframework.restdocs.RestDocumentationExtension;
import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
import org.springframework.restdocs.snippet.Snippet;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.filter.CharacterEncodingFilter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.base.restdoc.RestDocAttributes;
import net.herit.svcplatform.pushservice.commons.auth.AuthService;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
@Transactional
@SpringBootTest
@AutoConfigureMockMvc
@AutoConfigureRestDocs
@ExtendWith(RestDocumentationExtension.class)
public class RestDocsBaseWithSpringBoot extends BaseTest {
protected RestDocumentationResultHandler document;
protected MockMvc mockMvc;
@MockBean(name = "authService")
protected AuthService authService;
@Autowired
protected ObjectMapper objectMapper;
protected ErrorResponse errResp =ErrorResponse.builder()
.result(
new FailModel(
HttpResponseStatus.API_NOT_DEVELOPED_YET.getCode(),
HttpResponseStatus.API_NOT_DEVELOPED_YET.getDescription()
)
)
.build();
protected Snippet defaultRequestHeader;
@BeforeEach
public void setup(WebApplicationContext context, RestDocumentationContextProvider restDocument) {
Mockito.when(authService.authTokenCheck(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(true);
this.document = document("{class-name}/{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()));
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.alwaysDo(this.document)
.addFilters(new CharacterEncodingFilter("UTF-8", true))
.apply(documentationConfiguration(restDocument).snippets().withEncoding("UTF-8"))
.build();
this.errResp = ErrorResponse.builder()
.result(
new FailModel(
HttpResponseStatus.API_NOT_DEVELOPED_YET.getCode(),
HttpResponseStatus.API_NOT_DEVELOPED_YET.getDescription()
)
)
.build();
this.defaultRequestHeader = requestHeaders(
headerWithName("X-SERVICE-ID")
.description("X-SERVICE-ID")
.attributes(
RestDocAttributes.length("255"),
RestDocAttributes.format(""),
RestDocAttributes.etc("다수 서비스 앱을 사용하는 경우 서비스를 식별하기 위한 코드. NOTE : 설치기사 앱은 'HEVITON' 으로 고정")
),
headerWithName("X-APP-TYPE")
.description("X-APP-TYPE")
.attributes(
RestDocAttributes.length("10"),
RestDocAttributes.format(""),
RestDocAttributes.etc("ios / android")
),
headerWithName("X-TERMINAL-ID")
.description("X-TERMINAL-ID")
.attributes(
RestDocAttributes.length("50"),
RestDocAttributes.format(""),
RestDocAttributes.etc("동일 사용자 계정으로 여러 폰으로 로그인할 경우 별도의 세션 처리 (동시로그인 허용) 앱 설치 후 최초실행 시 UUID 형식의 문자열 형식으로 X-TERMINAL-ID 생성")
),
headerWithName("X-API-VERSION")
.description("X-API-VERSION")
.attributes(
RestDocAttributes.length("10"),
RestDocAttributes.format(""),
RestDocAttributes.etc("1.0")
),
headerWithName("X-REFERER")
.description("X-REFERER")
.attributes(
RestDocAttributes.length("50"),
RestDocAttributes.format(""),
RestDocAttributes.etc("페이지 단위를 구분하기 위한 코드. NOTE : 앱로깅 API의 function.code와 동일")
),
headerWithName("X-TRANSACTION-ID")
.description("X-TRANSACTION-ID")
.attributes(
RestDocAttributes.length("50"),
RestDocAttributes.format(""),
RestDocAttributes.etc("서비스 플랫폼 또는 API_GW 에서 생성")
),
headerWithName("Authorization")
.description("JWT 인증 토큰")
.attributes(
RestDocAttributes.length(""),
RestDocAttributes.format(""),
RestDocAttributes.etc("JWT 인증 토큰")
)
);
}
public void apiNotDevelopedYet(String uri) throws Exception {
ResultActions result = this.mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON)
.headers(getTestHeader()));
result
.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath("result.code").value(HttpResponseStatus.API_NOT_DEVELOPED_YET.getCode()))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("API가 기획단계에 있습니다.")
.attributes(RestDocAttributes.length("Integer.MAX_VALUE")),
fieldWithPath("result.description")
.description("API가 기획단계에 있습니다.")
.attributes(RestDocAttributes.length("100"))
)));
}
public HttpHeaders getTestHeader() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("X-SERVICE-ID", "HEVITON");
httpHeaders.add("X-APP-TYPE", "android");
httpHeaders.add("X-TERMINAL-ID", "55a0fa94-5ab8-4498-9517-46876b96db0d");
httpHeaders.add("X-API-VERSION", "1.0.0");
httpHeaders.add("X-REFERER", "REFERER");
httpHeaders.add("X-TRANSACTION-ID", "X-TRANSACTION-ID.20200629115826");
httpHeaders.add("Authorization", "bearer eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJoZXJpdCIsInN1YiI6ImFjZXRrbiIsImF1ZCI6InRlc3QiLCJ0eXAiOiIyIiwiaWF0IjoxNTkzMTMwODAyLCJleHAiOjE1OTQ5NTgxODl9.4xBUAbzdTlRfBhPtM-3Dl3gdLHFDhVuVh_qf6jqtsTY");
return httpHeaders;
}
public ResultActions performPostMockMvc(String uri, Object request) throws JsonProcessingException, Exception {
ResultActions result = this.mockMvc.perform(post(uri)
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
return result;
}
}
package net.herit.svcplatform.pushservice.base.bases;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.herit.svcplatform.pushservice.commons.jwt.properties.JwtProperties;
import net.herit.svcplatform.pushservice.commons.logger.CallLogger;
import net.herit.svcplatform.pushservice.commons.util.JwtUtil;
@SpringBootTest
@AutoConfigureMockMvc
@ExtendWith(SpringExtension.class)
public class SpringBootBase extends BaseTest {
@Autowired
protected MockMvc mockMvc;
@Autowired
protected ObjectMapper objectMapper;
@Autowired
protected JwtUtil jwtTokenUtil;
@Autowired
protected JwtProperties jwtProperties;
@Autowired
protected CallLogger callLogger;
}
package net.herit.svcplatform.pushservice.base.restdoc;
import static org.springframework.restdocs.snippet.Attributes.key;
import org.springframework.restdocs.snippet.Attributes;
public class RestDocAttributes {
private RestDocAttributes() {
throw new IllegalStateException("Utility class");
}
public static Attributes.Attribute length(String length) {
return key("length").value(length);
}
public static Attributes.Attribute length(int length) {
return key("length").value(String.valueOf(length));
}
public static Attributes.Attribute length(int min, int max) {
StringBuilder sb = new StringBuilder();
sb.append(min).append("~").append(max);
return key("length").value(sb.toString());
}
public static Attributes.Attribute format(String format) {
return key("format").value(format);
}
public static Attributes.Attribute etc(String etc) {
if(null == etc || "".equals(etc.trim())) {
return key("etc").value(etc);
} else {
StringBuilder sb = new StringBuilder();
sb.append("(").append(" ").append(etc).append(" ").append(")");
return key("etc").value(sb.toString());
}
}
}
package net.herit.svcplatform.pushservice.base.watcher;
import java.util.ArrayList;
import java.util.List;
public class TestResultCounter {
private TestResultCounter() throws IllegalAccessException {
throw new IllegalAccessException();
}
public static int disabledCount;
public static int successCount;
public static int abortedCount;
public static int failCount;
public static List<String> successDisplayNames;
public static List<String> failDisplayNames;
public static List<String> disabledDisplayNames;
public static List<String> abortedDisplayNames;
static {
successDisplayNames = new ArrayList<>();
failDisplayNames = new ArrayList<>();
disabledDisplayNames = new ArrayList<>();
abortedDisplayNames = new ArrayList<>();
}
}
package net.herit.svcplatform.pushservice.base.watcher;
import java.util.Optional;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestWatcher;
public class TestResultHandler implements TestWatcher{
public void testDisabled(ExtensionContext context, Optional<String> reason) {
System.out.println("disabled : " + context.getDisplayName());
System.out.println(reason);
System.out.println(TestResultCounter.disabledCount++);
TestResultCounter.disabledDisplayNames.add(context.getDisplayName());
}
public void testSuccessful(ExtensionContext context) {
System.out.println("success: " + context.getDisplayName());
System.out.println(TestResultCounter.successCount++);
TestResultCounter.successDisplayNames.add(context.getDisplayName());
}
public void testAborted(ExtensionContext context, Throwable cause) {
System.out.println("aborted: " + context.getDisplayName());
System.out.println(cause);
System.out.println(TestResultCounter.abortedCount++);
TestResultCounter.abortedDisplayNames.add(context.getDisplayName());
}
public void testFailed(ExtensionContext context, Throwable cause) {
System.out.println("failed: " + context.getDisplayName());
System.out.println(cause);
System.out.println(TestResultCounter.failCount++);
TestResultCounter.failDisplayNames.add(context.getDisplayName());
}
}
package net.herit.svcplatform.pushservice.commons.dto.response;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import com.fasterxml.jackson.core.JsonProcessingException;
import net.herit.svcplatform.pushservice.base.bases.MockitoBase;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
class HttpResponseStatusTests extends MockitoBase {
@MethodSource
@ParameterizedTest(name = "컨트롤러 도착전 에러 발생시, 에러 응답 문자생성 성공 테스트 - {2}")
@DisplayName("컨트롤러 도착전 에러 발생시, 에러 응답 문자생성 성공 테스트")
void getErrorResponseToJsonTest(HttpResponseStatus httpResponseStatus, String expected, String displayName) throws JsonProcessingException {
String actual = HttpResponseStatus.getErrorResponseToJson(httpResponseStatus);
assertEquals(expected, actual);
}
private static Stream<Arguments> getErrorResponseToJsonTest() throws JsonProcessingException {
String invalidResponseJson = getResponseJson(HttpResponseStatus.INVALID_PARAM);
String notFoundResponseJson = getResponseJson(HttpResponseStatus.NOT_PROVIDE_SERVICE);
String badRequestResponseJson = getResponseJson(HttpResponseStatus.DUPLICATED_CONTENTS);
return Stream.of(
Arguments.of(
HttpResponseStatus.INVALID_PARAM,
invalidResponseJson,
"INVALID_PARAM"),
Arguments.of(
HttpResponseStatus.NOT_PROVIDE_SERVICE,
notFoundResponseJson,
"NOT_FOUND"),
Arguments.of(
HttpResponseStatus.DUPLICATED_CONTENTS,
badRequestResponseJson,
"BAD_REQUEST")
);
}
private static String getResponseJson(HttpResponseStatus httpResponseStatus) throws JsonProcessingException {
ErrorResponse response = new ErrorResponse();
FailModel failModel = new FailModel();
failModel.setCode(httpResponseStatus.getCode());
failModel.setDescription(httpResponseStatus.getDescription());
response.setResult(failModel);
return objectMapper.writeValueAsString(response);
}
@ValueSource(strings = {
"{\"result\":{\"code\":0}}"
})
@DisplayName("컨트롤러 도착전 응답시, 성공 응답 문자생성 성공 테스트")
@ParameterizedTest(name = "컨트롤러 도착전 응답시, 성공 응답 문자생성 성공 테스트 - {0}")
void getSuccessResponseToJsonTest(String expected) throws JsonProcessingException {
String result = HttpResponseStatus.getSuccessResponseToJson();
assertEquals(expected, result);
}
@MethodSource
@DisplayName("resultCode로 responseCode를 찾는 테스트")
@ParameterizedTest(name = "resultCode로 responseCode를 찾는 테스트 - {2}")
void findResCodeByCodeTest(int resCode, int code, String displayName) {
assertEquals(resCode, HttpResponseStatus.findResCodeByCode(code));
}
private static Stream<Arguments> findResCodeByCodeTest() {
return Stream.of(
Arguments.of(
HttpResponseStatus.SUCCESS.getResCode(),
HttpResponseStatus.SUCCESS.getCode(),
"SUCCESS"),
Arguments.of(
HttpResponseStatus.NOT_PROVIDE_SERVICE.getResCode(),
HttpResponseStatus.NOT_PROVIDE_SERVICE.getCode(),
"NOT_FOUND"),
Arguments.of(
HttpResponseStatus.DUPLICATED_CONTENTS.getResCode(),
HttpResponseStatus.DUPLICATED_CONTENTS.getCode(),
"BAD_REQUEST"),
Arguments.of(
HttpResponseStatus.SYSTEM_ERROR.getResCode(),
-23, "INTERNAL_SERVER_ERROR")
);
}
@MethodSource
@DisplayName("resultCode로 httpResponseStatus를 찾는 테스트")
@ParameterizedTest(name = "resultCode로 httpResponseStatus를 찾는 테스트 - {2}")
void findHttpResponseStatusByCodeTest(HttpResponseStatus httpResponseStatus, int code, String displayName) {
assertEquals(httpResponseStatus, HttpResponseStatus.findHttpResponseStatusByCode(code));
}
private static Stream<Arguments> findHttpResponseStatusByCodeTest() {
return Stream.of(
Arguments.of(
HttpResponseStatus.SUCCESS,
HttpResponseStatus.SUCCESS.getCode(),
"SUCCESS"),
Arguments.of(
HttpResponseStatus.NOT_PROVIDE_SERVICE,
HttpResponseStatus.NOT_PROVIDE_SERVICE.getCode(),
"NOT_FOUND"),
Arguments.of(
HttpResponseStatus.DUPLICATED_CONTENTS,
HttpResponseStatus.DUPLICATED_CONTENTS.getCode(),
"BAD_REQUEST"),
Arguments.of(
HttpResponseStatus.SYSTEM_ERROR,
-23, "INTERNAL_SERVER_ERROR")
);
}
}
package net.herit.svcplatform.pushservice.commons.exception;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import net.herit.svcplatform.pushservice.base.bases.MockitoBase;
import net.herit.svcplatform.pushservice.commons.dto.model.FailModel;
import net.herit.svcplatform.pushservice.commons.dto.response.ErrorResponse;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.exception.CommonException;
class CommonExceptionTests extends MockitoBase {
@MethodSource
@ParameterizedTest
@DisplayName("CommonException의 getErrorResponse 테스트")
void getErrorResponseTest(HttpResponseStatus status) {
CommonException commonException = new CommonException(status);
ErrorResponse responseBody = new ErrorResponse();
responseBody.setResult(
new FailModel(
status.getCode(),
status.getDescription()
));
assertEquals(responseBody, commonException.getErrorResponse());
}
private static Stream<Arguments> getErrorResponseTest() {
return Stream.of(
Arguments.of(HttpResponseStatus.NOT_PROVIDE_SERVICE),
Arguments.of(HttpResponseStatus.INVALID_PARAM),
Arguments.of(HttpResponseStatus.DUPLICATED_CONTENTS),
Arguments.of(HttpResponseStatus.SYSTEM_ERROR)
);
}
}
package net.herit.svcplatform.pushservice.commons.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.fasterxml.jackson.core.JsonProcessingException;
import net.herit.svcplatform.pushservice.base.bases.MockitoBase;
import net.herit.svcplatform.pushservice.commons.util.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
class JsonUtilTest extends MockitoBase {
private JsonUtil jsonUtil;
@BeforeEach
void jsonSetUp() {
this.jsonUtil = new JsonUtil();
}
@Test
@DisplayName("object -> json 성공 테스트")
void objectToJsonSuccessTest() throws JsonProcessingException {
JsonUtilTestObject jsonUtilTestObject = new JsonUtilTestObject("name", "addr", 30);
String expected = objectMapper.writeValueAsString(jsonUtilTestObject);
assertEquals(expected, jsonUtil.objectToJson(jsonUtilTestObject));
}
@Test
@DisplayName("object -> json 객체가 바뀌었을 경우, 실패 테스트")
void objectToJsonFailTest() throws JsonProcessingException {
JsonUtilTestObject jsonUtilTestObject = new JsonUtilTestObject("name", "addr", 30);
String expected = objectMapper.writeValueAsString(jsonUtilTestObject);
jsonUtilTestObject.setName("test");
assertNotEquals(expected, jsonUtil.objectToJson(jsonUtilTestObject));
}
@Test
@DisplayName("string -> pretty json 성공 테스트")
void stringToPrettyJsonSuccessTest() throws JsonProcessingException {
JsonUtilTestObject jsonUtilTestObject = new JsonUtilTestObject("name", "addr", 30);
String json = objectMapper.writeValueAsString(jsonUtilTestObject);
Object jsonObject = objectMapper.readValue(json, Object.class);
String expected = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
assertEquals(expected, jsonUtil.stringToPrettyJson(json));
}
@Test
@DisplayName("string -> pretty json 실패 테스트 pretty와 1 line")
void stringToPrettyJsonFailTest() throws JsonProcessingException {
JsonUtilTestObject jsonUtilTestObject = new JsonUtilTestObject("name", "addr", 30);
String json = objectMapper.writeValueAsString(jsonUtilTestObject);
assertNotEquals(json, jsonUtil.stringToPrettyJson(json));
}
@Data
@AllArgsConstructor
private class JsonUtilTestObject {
private String name;
private String addr;
private int age;
}
}
package net.herit.svcplatform.pushservice.commons.validation.body;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.ValidationException;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import net.herit.svcplatform.pushservice.base.bases.MockitoBase;
import net.herit.svcplatform.pushservice.commons.validation.CustomValidation;
import lombok.AllArgsConstructor;
import lombok.Data;
class CustomValidTests extends MockitoBase {
private static Validator validator;
@BeforeEach
public void setUp() {
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
@Test
@DisplayName("성공 테스트")
void validationSuccessTest() {
ValidationTestObject validationTestObject = getTestObejct("Kim", 22, 30);
Set<ConstraintViolation<ValidationTestObject>> constraintViolations = validator.validate(validationTestObject);
assertNotNull(constraintViolations);
assertTrue(constraintViolations.isEmpty());
}
@Test
@DisplayName("@NotNull 실패 테스트")
void validationNullFailTest() {
ValidationTestObject validationTestObject = getTestObejct("Kim", 2, null);
Set<ConstraintViolation<ValidationTestObject>> constraintViolations = validator.validate(validationTestObject);
assertFalse(constraintViolations.isEmpty());
}
@Test
@DisplayName("valid()에서 나이확인 실패 테스트")
void validationAgeFailTest() {
ValidationTestObject validationTestObject = getTestObejct("Kim", 2, 30);
assertThrows(ValidationException.class, () -> validator.validate(validationTestObject));
}
@Test
@DisplayName("valid()에서 이름확인 실패 테스트")
void validationNameFailTest() {
ValidationTestObject validationTestObject = getTestObejct("KIM", 22, 30);
assertThrows(ValidationException.class, () -> validator.validate(validationTestObject));
}
@Test
@DisplayName("valid()에서 가장 좋아하는 숫자 실패 테스트")
void validationFavoriteNumberFailTest() {
ValidationTestObject validationTestObject = getTestObejct("Kim", 22, 3);
assertThrows(ValidationException.class, () -> validator.validate(validationTestObject));
}
@Test
@DisplayName("isValid() 메서드의 성공 로직 테스트 (validation 기능 검사아님)")
void validSuccessResultTest() {
ValidationTestObject validationTestObject = getTestObejct("Kim", 22, 30);
String isValidResult = validationTestObject.isValid();
assertEquals("SUCCESS", isValidResult);
}
@Test
@DisplayName("isValid() 메서드의 실패 로직 테스트 (validation 기능 검사아님)")
void validAllFailResultTest() {
ValidationTestObject validationTestObject = getTestObejct("KIM", 2, 3);
String isValidResult = validationTestObject.isValid();
assertEquals("favorite number fail, name fail, age fail", isValidResult);
}
private ValidationTestObject getTestObejct(String name, int age, int favoriteNumber) {
return new ValidationTestObject(
name,
age,
new ValidationTestObjectInside(favoriteNumber))
;
}
private ValidationTestObject getTestObejct(String name, int age, ValidationTestObjectInside validationTestObjectInside) {
return new ValidationTestObject(
name,
age,
validationTestObjectInside)
;
}
@Data
@AllArgsConstructor
private class ValidationTestObject implements CustomValidation{
@NotNull
private String name;
private int age;
@NotNull @Valid
private ValidationTestObjectInside validationTestObjectInside;
@Override
public String isValid() {
String result = "";
if(validationTestObjectInside.getFavoriteNumber() < 10)
result = "favorite number fail, ";
if(name.equals("KIM")) {
result += "name fail, ";
}
if(age < 20)
result += "age fail";
return result.equals("") ? "SUCCESS" : result;
}
}
@Data
@AllArgsConstructor
private class ValidationTestObjectInside {
@NotNull
private int favoriteNumber;
}
}
package net.herit.svcplatform.pushservice.commons.validation.header;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import net.herit.svcplatform.pushservice.base.bases.MockitoBase;
import net.herit.svcplatform.pushservice.commons.dto.request.RequestHeader;
class RequestHeaderValidationTests extends MockitoBase {
@Mock
HttpServletRequest request;
@Test
@DisplayName("header 성공 테스트")
void requestHeaderSuccessTest() throws Exception {
RequestHeader requestHeader = getHeader();
assertTrue(RequestHeaderValidation.headerLengthCheck(requestHeader));
}
@Test
@DisplayName("null 실패 테스트1. headerLengthCheck()")
void requestHeaderNullFailTest1() throws Exception {
RequestHeader requestHeader = getHeader();
String xServiceId = null;
requestHeader.setXServiceId(xServiceId);
assertFalse(RequestHeaderValidation.headerLengthCheck(requestHeader));
}
@Test
@DisplayName("header길이 실패 테스트1. headerLengthCheck()")
void requestHeaderLengthFailTest1() throws Exception {
RequestHeader requestHeader = getHeader();
String xServiceId = "X-SERVICE-ID-TEST, X-SERVICE-ID-TEST, X-SERVICE-ID-TEST";
requestHeader.setXServiceId(xServiceId);
assertFalse(RequestHeaderValidation.headerLengthCheck(requestHeader));
}
private RequestHeader getHeader() {
String xServiceId = "X-SERVICE-ID-TEST";
String xTerminalId = "X-TERMINAL-ID-TEST";
String xAppType = "X-APP-TYPE";
return new RequestHeader(xServiceId, xAppType, xTerminalId);
}
}
package net.herit.svcplatform.pushservice.restdocs.commons.controller;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import net.herit.svcplatform.pushservice.base.bases.RestDocsBaseWithSpringBoot;
import net.herit.svcplatform.pushservice.base.restdoc.RestDocAttributes;
import net.herit.svcplatform.pushservice.commons.dto.model.SuccessModel;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
class HealthCheckControllerTests extends RestDocsBaseWithSpringBoot {
@Test
@DisplayName("healthcheck")
void healthCheck() throws Exception {
SuccessModel request = new SuccessModel();
ResultActions result = this.mockMvc.perform(post("/api/healthcheck")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath("result.code")
.value(HttpResponseStatus.SUCCESS.getCode()))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("결과 코드")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
}
package net.herit.svcplatform.pushservice.restdocs.commons.jwt;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import com.fasterxml.jackson.core.JsonProcessingException;
import net.herit.svcplatform.pushservice.base.bases.RestDocsBaseWithSpringBoot;
import net.herit.svcplatform.pushservice.base.restdoc.RestDocAttributes;
import net.herit.svcplatform.pushservice.commons.dto.model.AccountModel;
import net.herit.svcplatform.pushservice.commons.dto.response.HttpResponseStatus;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRenewalRequest;
import net.herit.svcplatform.pushservice.commons.jwt.dto.TokenRequest;
@Disabled
class JwtControllerTests extends RestDocsBaseWithSpringBoot {
@Test
@DisplayName("server json access token 발급")
void requestServerAccessToken() throws Exception {
TokenRequest request = new TokenRequest();
request.setAccount(new AccountModel("login_id", "auth_token"));
request.setApplicationId(1);
request.setExpirationHour(3);
request.setUserId("user_id");
ResultActions result = this.mockMvc
.perform(post("/api/jwt/server/request").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)).headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath("result.code").value(HttpResponseStatus.SUCCESS.getCode()))
.andDo(this.document.document(this.defaultRequestHeader,
requestFields(
fieldWithPath("account.login_id").description("login_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("account.auth_token").description("auth_token").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("user_id").description("user_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("application_id").description("application_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("expiration_hour").description("토큰 유효 시간").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""))
),
responseFields(
fieldWithPath("result.code").description("결과 코드").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("accessToken").description("ACCESS_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("refreshToken").description("REFRESH_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")))));
}
@Test
@DisplayName("client json access token 발급")
void requestClientAccessToken() throws JsonProcessingException, Exception {
TokenRequest request = new TokenRequest();
request.setAccount(new AccountModel("login_id", "auth_token"));
request.setApplicationId(1);
request.setExpirationHour(3);
request.setUserId("user_id");
ResultActions result = this.mockMvc
.perform(post("/api/jwt/client/request").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)).headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath("result.code").value(HttpResponseStatus.SUCCESS.getCode()))
.andDo(this.document.document(this.defaultRequestHeader,
requestFields(
fieldWithPath("account.login_id").description("login_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("account.auth_token").description("auth_token").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("user_id").description("user_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("application_id").description("application_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("expiration_hour").description("토큰 유효 시간").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""))
),
responseFields(
fieldWithPath("result.code").description("결과 코드").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("accessToken").description("ACCESS_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("refreshToken").description("REFRESH_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")))));
}
@Test
@DisplayName("server json access token 재발급 - refresh_token 이용")
void renewalServerAccessToken() throws JsonProcessingException, Exception {
TokenRenewalRequest request = new TokenRenewalRequest();
request.setAccount(new AccountModel("login_id", "auth_token"));
request.setApplicationId(1);
request.setExpirationHour(3);
request.setUserId("user_id");
request.setTokenData(
"eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJoZXJpdCIsInN1YiI6Imhlcml0V2VTb0ZseSIsImlhdCI6MTYxNTg3NDg5OCwiZXhwIjoxNjQ3NDEwODk4fQ.99RKbVfZN_k8FPH8XKMGfqMT-c48UV1MORl7ylrMCw0");
ResultActions result = this.mockMvc
.perform(post("/api/jwt/access/renewal").contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)).headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andExpect(jsonPath("result.code").value(HttpResponseStatus.SUCCESS.getCode()))
.andDo(this.document.document(this.defaultRequestHeader,
requestFields(
fieldWithPath("account.login_id").description("login_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("account.auth_token").description("auth_token").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("user_id").description("user_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("application_id").description("application_id").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("expiration_hour").description("토큰 유효 시간").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format("")),
fieldWithPath("token_data").description("REFRESH_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""))
),
responseFields(
fieldWithPath("result.code").description("결과 코드").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("accessToken").description("ACCESS_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")),
fieldWithPath("refreshToken").description("REFRESH_TOKEN").attributes(
RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""),
RestDocAttributes.etc("")))));
}
}
package net.herit.svcplatform.pushservice.restdocs.feign;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.ResultActions;
import net.herit.svcplatform.pushservice.base.bases.RestDocsBaseWithSpringBoot;
import net.herit.svcplatform.pushservice.base.restdoc.RestDocAttributes;
import net.herit.svcplatform.pushservice.features.logger.DefaultModel;
import net.herit.svcplatform.pushservice.features.logger.RequestDto;
/**
* 샘플 코드로 현재 openfeign으로 이 프로젝트의 controller에 요청을 보내고 있으므로,
* 테스트 성공을 위해 DEFINED_PORT 적용 ( 스프링 부트 서버는 한번 더 띄움 )
*/
@Disabled
@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
class FeignControllerRestdocsTests extends RestDocsBaseWithSpringBoot {
@Test
@DisplayName("feign post restdocs 생성 테스트")
void postMappingTest() throws Exception {
RequestDto request = new RequestDto();
DefaultModel defaultModel = new DefaultModel();
defaultModel.setData("data");
request.setData(defaultModel);
ResultActions result = this.mockMvc.perform(post("/api/1.0/feign/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestFields(
fieldWithPath("data.data").description("테스트 요청 data")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc(""))
)
))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
@Test
@DisplayName("feign get restdocs 생성 테스트")
void getMappingTest() throws Exception {
RequestDto request = new RequestDto();
DefaultModel defaultModel = new DefaultModel();
defaultModel.setData("data");
request.setData(defaultModel);
ResultActions result = this.mockMvc.perform(get("/api/1.0/feign/get")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestFields(
fieldWithPath("data.data").description("테스트 요청 data")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc(""))
)
))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
}
package net.herit.svcplatform.pushservice.restdocs.logger;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.partWithName;
import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
import static org.springframework.restdocs.request.RequestDocumentation.requestParts;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.ResultActions;
import net.herit.svcplatform.pushservice.base.bases.RestDocsBaseWithSpringBoot;
import net.herit.svcplatform.pushservice.base.restdoc.RestDocAttributes;
import net.herit.svcplatform.pushservice.features.logger.DefaultModel;
import net.herit.svcplatform.pushservice.features.logger.RequestDto;
class LogTestRestdocsTests extends RestDocsBaseWithSpringBoot {
@Test
@DisplayName("Json Post 테스트 API - Restdocs 생성 테스트")
void postController() throws Exception {
RequestDto request = new RequestDto();
DefaultModel defaultModel = new DefaultModel();
defaultModel.setData("data");
request.setData(defaultModel);
ResultActions result = this.mockMvc.perform(post("/api/1.0/post")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestFields(
fieldWithPath("data.data").description("테스트 요청 data")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc(""))
)
))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
@Test
@DisplayName("multiupload 테스트 API - Restdocs 생성 테스트")
void multiupload() throws Exception {
MockMultipartFile image = new MockMultipartFile("filename", "<<jpg data>>".getBytes());
MockMultipartFile image2 = new MockMultipartFile("filename", "<<jpg2 data>>".getBytes());
ResultActions result = this.mockMvc.perform(
multipart("/multiupload")
.file(image)
.file(image2)
.param("data", "data")
.contentType(MediaType.MULTIPART_FORM_DATA_VALUE)
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestParts(
partWithName("filename").description("테스트 요청 filename")),
requestParameters(
parameterWithName("data").description("테스트 요청 data"))
))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
@Test
@DisplayName("form-urlencoded Get 테스트 API - Restdocs 생성 테스트")
void getFormUrlEncodedController() throws Exception {
ResultActions result = this.mockMvc.perform(get("/api/1.0/get/form/urlencoded")
.contentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE)
.content("data=data")
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestParameters(
parameterWithName("data").description("테스트 요청 data"))))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
@Test
@DisplayName("Json Get 테스트 API - Restdocs 생성 테스트")
void getController() throws Exception {
RequestDto request = new RequestDto();
DefaultModel defaultModel = new DefaultModel();
defaultModel.setData("data");
request.setData(defaultModel);
ResultActions result = this.mockMvc.perform(get("/api/1.0/get")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request))
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(
defaultRequestHeader,
requestFields(
fieldWithPath("data.data")
.description("data.data")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc(""))
)
))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
@Test
@DisplayName("healthCheck 테스트 API - Restdocs 생성 테스트")
void healthCheck() throws Exception {
ResultActions result = this.mockMvc.perform(post("/api/1.0/healthcheck")
.headers(getTestHeader()));
result.andDo(print()).andExpect(status().isOk())
.andDo(this.document.document(defaultRequestHeader))
.andExpect(jsonPath("result.code")
.value("0"))
.andDo(this.document.document(
this.defaultRequestHeader,
responseFields(
fieldWithPath("result.code")
.description("result.code")
.attributes(RestDocAttributes.length(Integer.MAX_VALUE), RestDocAttributes.format(""), RestDocAttributes.etc("")))));
}
}
INSERT INTO TBL_SAMPLE (NAME, AGE, ADDR) VALUES('GilDong Hong', 27, 'Seoul');
INSERT INTO TBL_SAMPLE (NAME, AGE, ADDR) VALUES('GilDong KANG', 29, 'Busan');
INSERT INTO TBL_SAMPLE (NAME, AGE, ADDR) VALUES('GilDong KIM', 31, 'Seoul');
.RequestBody
[cols="2,1,1,1,1,4"]
|===
|Path|Type|Length|Format|Optional|Description
{{#fields}}
|{{path}}
|{{type}}
|{{length}}
|{{#format}}{{.}}{{/format}}
|{{#optional}}true{{/optional}}
|{{description}} {{#etc}} {{.}} {{/etc}}
{{/fields}}
|===
\ No newline at end of file
.RequestHeader
[cols="2,8"]
|===
|Name|Description
{{#headers}}
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
|{{#tableCellContent}}{{description}} {{etc}} {{/tableCellContent}}
{{/headers}}
|===
\ No newline at end of file
.ResponseBody
[cols="2,1,1,1,1,4"]
|===
|Path|Type|Length|Format|Optional|Description
{{#fields}}
|{{path}}
|{{type}}
|{{length}}
|{{#format}}{{.}}{{/format}}
|{{#optional}}true{{/optional}}
|{{description}} {{#etc}} {{.}} {{/etc}}
{{/fields}}
|===
\ No newline at end of file
DROP TABLE IF EXISTS TBL_SAMPLE;
CREATE TABLE IF NOT EXISTS TBL_SAMPLE(
SAMPLE_ID INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
`NAME` VARCHAR(20) NOT NULL,
AGE VARCHAR(20) NOT NULL,
ADDR VARCHAR(50) NOT NULL
)
;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment