From 97efab60cbb8b6fcfc5eb3017422f619c74cd57b Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Tue, 9 Nov 2021 23:21:06 +0900 Subject: [PATCH 01/24] =?UTF-8?q?refactor:=20LogAspect=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B0=8F=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95=20(#740)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zzimkkong/config/LogAspectConfig.java | 9 ++++--- .../zzimkkong/config/logaspect/LogAspect.java | 22 ++++++++-------- .../config/logaspect/LogAspectConfigurer.java | 26 +++++++++---------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java index 29f556200..3fee535d2 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java @@ -1,6 +1,7 @@ package com.woowacourse.zzimkkong.config; import com.woowacourse.zzimkkong.config.logaspect.LogAspectConfigurer; +import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; import org.reflections.Reflections; import org.springframework.context.annotation.Configuration; @@ -9,13 +10,13 @@ @Configuration public class LogAspectConfig extends LogAspectConfigurer { @Override - protected void registerBeans(LogRegistry logRegistry) { + protected void registerBeans(LogEntries logEntries) { Reflections reflections = new Reflections("com.woowacourse.zzimkkong"); - Set> typesAnnotatedWith = reflections.getTypesAnnotatedWith(com.woowacourse.zzimkkong.config.logaspect.LogRegistry.class); + Set> typesAnnotatedWith = reflections.getTypesAnnotatedWith(LogRegistry.class); for (Class clazz : typesAnnotatedWith) { - String logGroup = clazz.getAnnotation(com.woowacourse.zzimkkong.config.logaspect.LogRegistry.class).group(); - logRegistry.add(clazz, logGroup); + String logGroup = clazz.getAnnotation(LogRegistry.class).group(); + logEntries.add(clazz, logGroup); } } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java index 6a68e001b..af892b01c 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java @@ -46,30 +46,30 @@ private static void logExecutionInfo(MethodSignature methodSignature, long timeT logExecutionInfo(declaringType, method, timeTaken, logGroup); } - private static void logExecutionInfo(Class declaringType, Method method, long timeTaken, String logGroup) { + private static void logExecutionInfo(Class typeToLog, Method method, long timeTaken, String logGroup) { log.info("{} took {} ms. (info group by '{}')", - value("method", declaringType.getName() + "." + method.getName() + "()"), + value("method", typeToLog.getName() + "." + method.getName() + "()"), value("execution_time", timeTaken), value("group", logGroup)); } - static T createLogProxy(Object target, Class requiredType, String logGroup) { - final LogProxyHandler logProxyHandler = new LogProxyHandler(target, requiredType, logGroup); - return requiredType.cast( + static T createLogProxy(Object target, Class typeToLog, String logGroup) { + final LogProxyHandler logProxyHandler = new LogProxyHandler(target, typeToLog, logGroup); + return typeToLog.cast( Proxy.newProxyInstance( - requiredType.getClassLoader(), - new Class[]{requiredType}, + typeToLog.getClassLoader(), + new Class[]{typeToLog}, logProxyHandler)); } private static class LogProxyHandler implements InvocationHandler { private final Object target; - private final Class declaringType; + private final Class typeToLog; private final String logGroup; - public LogProxyHandler(Object target, Class declaringType, String logGroup) { + private LogProxyHandler(Object target, Class typeToLog, String logGroup) { this.target = target; - this.declaringType = declaringType; + this.typeToLog = typeToLog; this.logGroup = logGroup; } @@ -80,7 +80,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl long endTime = System.currentTimeMillis(); long timeTaken = endTime - startTime; - logExecutionInfo(declaringType, method, timeTaken, logGroup); + logExecutionInfo(typeToLog, method, timeTaken, logGroup); return invokeResult; } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java index 0a4e6c1f7..137da0248 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java @@ -13,7 +13,7 @@ public abstract class LogAspectConfigurer { private ConfigurableListableBeanFactory beanFactory; private BeanDefinitionRegistry beanDefinitionRegistry; - private final LogRegistry logRegistry = new LogRegistry(); + private final LogEntries logEntries = new LogEntries(); @Autowired public final void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { @@ -26,16 +26,16 @@ public final void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { @PostConstruct protected final void init() { - registerBeans(logRegistry); + registerBeans(logEntries); - final List logTargetEntries = logRegistry.getLogTargetEntries(); + final List logTargetEntries = logEntries.getLogTargetEntries(); - for (LogTargetEntry logTargetEntry : logTargetEntries) { - replaceByProxy(logTargetEntry.getTargetClass(), logTargetEntry.getLogGroup()); + for (LogEntry logEntry : logTargetEntries) { + replaceByProxy(logEntry.getTargetClass(), logEntry.getLogGroup()); } } - abstract protected void registerBeans(final LogRegistry logRegistry); + abstract protected void registerBeans(final LogEntries logEntries); private void replaceByProxy(Class targetClass, String logGroupName) { String[] beanNames = beanFactory.getBeanNamesForType(targetClass); @@ -56,26 +56,26 @@ private void replaceByProxy(String beanName, Class targetClass, String logGro beanFactory.registerSingleton(beanName, logProxy); } - public final static class LogRegistry { - private final List entries = new ArrayList<>(); + public final static class LogEntries { + private final List entries = new ArrayList<>(); - private LogRegistry() { + private LogEntries() { } public void add(Class clazz, String logGroup) { - this.entries.add(new LogTargetEntry(clazz, logGroup)); + this.entries.add(new LogEntry(clazz, logGroup)); } - private List getLogTargetEntries() { + private List getLogTargetEntries() { return entries; } } - private static class LogTargetEntry { + private static class LogEntry { Class targetClass; String logGroup; - private LogTargetEntry(Class targetClass, String logGroup) { + private LogEntry(Class targetClass, String logGroup) { this.targetClass = targetClass; this.logGroup = logGroup; } From 603e91647ba7c24f05edf6db941b439cd347d31a Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Mon, 15 Nov 2021 22:27:08 +0900 Subject: [PATCH 02/24] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=20=ED=94=84?= =?UTF-8?q?=EB=A1=9D=EC=8B=9C=20=EB=93=B1=EB=A1=9D=20=EA=B3=BC=EC=A0=95?= =?UTF-8?q?=EC=9D=84=20BeanPostProcessor=EB=A5=BC=20=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=ED=95=B4=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=20(#742)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: LogAspectConfigurer -> PostProcessor 로 기능 대체 * refactor: LogAspect, 로그 프록시를 ProxyFactory를 사용해 만듦 * refactor: LogRegistry -> FindInstanceAndCreateLogProxy 이름 변경 * refactor: 로그 프록시 생성시 CGLIB 사용 및 불필요 캐스팅문 삭제 * refactor: 로그 프록시 public 메소드에만 적용 되도록 변경 * refactor: LogMethodExecutionTime @target 으로 변경 부모 클래스의 메소드도 로깅 기준에 포함토록 합니다. * refactor: LogProxyPostProcessor 생성자 protected로 변경 * refactor: LogProxyPostProcessor를 LogAspect 내부 private 클래스로 은닉 * refactor: LogAspect 내부 클래스 외부로 추출, static 메소드 삭제 --- .../zzimkkong/config/LogAspectConfig.java | 22 ----- .../logaspect/ExecutionTimeLogAdvice.java | 31 +++++++ ...ava => FindInstanceAndCreateLogProxy.java} | 2 +- .../zzimkkong/config/logaspect/LogAspect.java | 58 ++++-------- .../config/logaspect/LogAspectConfigurer.java | 91 ------------------- .../logaspect/LogProxyPostProcessor.java | 37 ++++++++ .../zzimkkong/repository/MapRepository.java | 4 +- .../repository/MemberRepository.java | 4 +- .../repository/PresetRepository.java | 4 +- .../repository/ReservationRepository.java | 4 +- .../zzimkkong/repository/SpaceRepository.java | 4 +- 11 files changed, 99 insertions(+), 162 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java rename backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/{LogRegistry.java => FindInstanceAndCreateLogProxy.java} (85%) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java deleted file mode 100644 index 3fee535d2..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/LogAspectConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.woowacourse.zzimkkong.config; - -import com.woowacourse.zzimkkong.config.logaspect.LogAspectConfigurer; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; -import org.reflections.Reflections; -import org.springframework.context.annotation.Configuration; - -import java.util.Set; - -@Configuration -public class LogAspectConfig extends LogAspectConfigurer { - @Override - protected void registerBeans(LogEntries logEntries) { - Reflections reflections = new Reflections("com.woowacourse.zzimkkong"); - Set> typesAnnotatedWith = reflections.getTypesAnnotatedWith(LogRegistry.class); - - for (Class clazz : typesAnnotatedWith) { - String logGroup = clazz.getAnnotation(LogRegistry.class).group(); - logEntries.add(clazz, logGroup); - } - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java new file mode 100644 index 000000000..509f2860b --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java @@ -0,0 +1,31 @@ +package com.woowacourse.zzimkkong.config.logaspect; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +import java.lang.reflect.Method; + +public class ExecutionTimeLogAdvice implements MethodInterceptor { + private final LogAspect logAspect; + private final Class typeToLog; + private final String logGroup; + + protected ExecutionTimeLogAdvice(LogAspect logAspect, Class typeToLog, String logGroup) { + this.logAspect = logAspect; + this.typeToLog = typeToLog; + this.logGroup = logGroup; + } + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + long startTime = System.currentTimeMillis(); + final Object result = invocation.proceed(); + long endTime = System.currentTimeMillis(); + long timeTaken = endTime - startTime; + + Method method = invocation.getMethod(); + logAspect.logExecutionInfo(typeToLog, method, timeTaken, logGroup); + + return result; + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogRegistry.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/FindInstanceAndCreateLogProxy.java similarity index 85% rename from backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogRegistry.java rename to backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/FindInstanceAndCreateLogProxy.java index 72964a72c..fafee396b 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogRegistry.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/FindInstanceAndCreateLogProxy.java @@ -7,6 +7,6 @@ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) -public @interface LogRegistry { +public @interface FindInstanceAndCreateLogProxy { String group(); } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java index af892b01c..9e37127a5 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java @@ -5,11 +5,11 @@ import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; +import org.springframework.aop.framework.ProxyFactory; import org.springframework.stereotype.Component; -import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.lang.reflect.Proxy; import static net.logstash.logback.argument.StructuredArguments.value; @@ -17,8 +17,9 @@ @Component @Aspect public class LogAspect { - @Around("@within(com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime)" + - "&& execution(public * *.*(..))") + + @Around("@target(com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime)" + + "&& execution(public * com.woowacourse.zzimkkong..*(..))") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); @@ -34,55 +35,36 @@ public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { return result; } - private String getLogGroupFromAnnotation(ProceedingJoinPoint joinPoint) { - Class targetClass = joinPoint.getTarget().getClass(); - return targetClass.getAnnotation(LogMethodExecutionTime.class).group(); - } - - private static void logExecutionInfo(MethodSignature methodSignature, long timeTaken, String logGroup) { + void logExecutionInfo(MethodSignature methodSignature, long timeTaken, String logGroup) { final Class declaringType = methodSignature.getDeclaringType(); final Method method = methodSignature.getMethod(); logExecutionInfo(declaringType, method, timeTaken, logGroup); } - private static void logExecutionInfo(Class typeToLog, Method method, long timeTaken, String logGroup) { + void logExecutionInfo(Class typeToLog, Method method, long timeTaken, String logGroup) { log.info("{} took {} ms. (info group by '{}')", value("method", typeToLog.getName() + "." + method.getName() + "()"), value("execution_time", timeTaken), value("group", logGroup)); } - static T createLogProxy(Object target, Class typeToLog, String logGroup) { - final LogProxyHandler logProxyHandler = new LogProxyHandler(target, typeToLog, logGroup); - return typeToLog.cast( - Proxy.newProxyInstance( - typeToLog.getClassLoader(), - new Class[]{typeToLog}, - logProxyHandler)); - } - - private static class LogProxyHandler implements InvocationHandler { - private final Object target; - private final Class typeToLog; - private final String logGroup; + Object createLogProxy(Object target, Class typeToLog, String logGroup) { + AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); + advisor.setExpression("execution(public * com.woowacourse.zzimkkong..*(..))"); - private LogProxyHandler(Object target, Class typeToLog, String logGroup) { - this.target = target; - this.typeToLog = typeToLog; - this.logGroup = logGroup; - } + ExecutionTimeLogAdvice advice = new ExecutionTimeLogAdvice(this, typeToLog, logGroup); + advisor.setAdvice(advice); - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - long startTime = System.currentTimeMillis(); - final Object invokeResult = method.invoke(target, args); - long endTime = System.currentTimeMillis(); - long timeTaken = endTime - startTime; + ProxyFactory proxyFactory = new ProxyFactory(target); + proxyFactory.addAdvisor(advisor); + proxyFactory.setProxyTargetClass(true); - logExecutionInfo(typeToLog, method, timeTaken, logGroup); + return proxyFactory.getProxy(); + } - return invokeResult; - } + private String getLogGroupFromAnnotation(ProceedingJoinPoint joinPoint) { + Class targetClass = joinPoint.getTarget().getClass(); + return targetClass.getAnnotation(LogMethodExecutionTime.class).group(); } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java deleted file mode 100644 index 137da0248..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspectConfigurer.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.woowacourse.zzimkkong.config.logaspect; - -import com.woowacourse.zzimkkong.exception.config.logaspect.InvalidModifiableBeanFactoryException; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; - -import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.List; - -public abstract class LogAspectConfigurer { - private ConfigurableListableBeanFactory beanFactory; - private BeanDefinitionRegistry beanDefinitionRegistry; - private final LogEntries logEntries = new LogEntries(); - - @Autowired - public final void setBeanFactory(ConfigurableListableBeanFactory beanFactory) { - this.beanFactory = beanFactory; - if (!(beanFactory instanceof BeanDefinitionRegistry)) { - throw new InvalidModifiableBeanFactoryException(); - } - this.beanDefinitionRegistry = (BeanDefinitionRegistry) beanFactory; - } - - @PostConstruct - protected final void init() { - registerBeans(logEntries); - - final List logTargetEntries = logEntries.getLogTargetEntries(); - - for (LogEntry logEntry : logTargetEntries) { - replaceByProxy(logEntry.getTargetClass(), logEntry.getLogGroup()); - } - } - - abstract protected void registerBeans(final LogEntries logEntries); - - private void replaceByProxy(Class targetClass, String logGroupName) { - String[] beanNames = beanFactory.getBeanNamesForType(targetClass); - - for (String beanName : beanNames) { - replaceByProxy(beanName, targetClass, logGroupName); - } - } - - private void replaceByProxy(String beanName, Class targetClass, String logGroupName) { - final Object target = beanFactory.getBean(beanName); - final BeanDefinition targetBeanDefinition = beanFactory.getBeanDefinition(beanName); - - beanDefinitionRegistry.removeBeanDefinition(beanName); - beanDefinitionRegistry.registerBeanDefinition(beanName, targetBeanDefinition); - - final Object logProxy = LogAspect.createLogProxy(target, targetClass, logGroupName); - beanFactory.registerSingleton(beanName, logProxy); - } - - public final static class LogEntries { - private final List entries = new ArrayList<>(); - - private LogEntries() { - } - - public void add(Class clazz, String logGroup) { - this.entries.add(new LogEntry(clazz, logGroup)); - } - - private List getLogTargetEntries() { - return entries; - } - } - - private static class LogEntry { - Class targetClass; - String logGroup; - - private LogEntry(Class targetClass, String logGroup) { - this.targetClass = targetClass; - this.logGroup = logGroup; - } - - private Class getTargetClass() { - return targetClass; - } - - private String getLogGroup() { - return logGroup; - } - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java new file mode 100644 index 000000000..76ab595fe --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java @@ -0,0 +1,37 @@ +package com.woowacourse.zzimkkong.config.logaspect; + +import org.reflections.Reflections; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.Set; + +@Component +public class LogProxyPostProcessor implements BeanPostProcessor { + private final LogAspect logAspect; + private final Set> typesAnnotatedWith; + + protected LogProxyPostProcessor(LogAspect logAspect) { + this.logAspect = logAspect; + + Reflections reflections = new Reflections("com.woowacourse.zzimkkong"); + typesAnnotatedWith = Collections.unmodifiableSet(reflections.getTypesAnnotatedWith(FindInstanceAndCreateLogProxy.class)); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return typesAnnotatedWith.stream() + .filter(typeToLog -> typeToLog.isAssignableFrom(bean.getClass())) + .findAny() + .map(typeToLog -> createLogProxy(bean, typeToLog)) + .orElse(bean); + } + + private Object createLogProxy(Object bean, Class typeToLog) { + FindInstanceAndCreateLogProxy annotation = typeToLog.getAnnotation(FindInstanceAndCreateLogProxy.class); + String groupName = annotation.group(); + return logAspect.createLogProxy(bean, typeToLog, groupName); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/MapRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/MapRepository.java index 81a6b3bed..ce8fd3d85 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/MapRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/MapRepository.java @@ -1,6 +1,6 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; +import com.woowacourse.zzimkkong.config.logaspect.FindInstanceAndCreateLogProxy; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import org.springframework.data.domain.Page; @@ -12,7 +12,7 @@ import java.util.List; import java.util.Optional; -@LogRegistry(group = "repository") +@FindInstanceAndCreateLogProxy(group = "repository") public interface MapRepository extends JpaRepository { List findAllByMember(final Member member); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/MemberRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/MemberRepository.java index ee6e050e8..819bd7639 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/MemberRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/MemberRepository.java @@ -1,6 +1,6 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; +import com.woowacourse.zzimkkong.config.logaspect.FindInstanceAndCreateLogProxy; import com.woowacourse.zzimkkong.domain.Member; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -10,7 +10,7 @@ import java.util.Optional; -@LogRegistry(group = "repository") +@FindInstanceAndCreateLogProxy(group = "repository") public interface MemberRepository extends JpaRepository { boolean existsByEmail(String email); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/PresetRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/PresetRepository.java index aa1a78f37..c529f0673 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/PresetRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/PresetRepository.java @@ -1,9 +1,9 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; +import com.woowacourse.zzimkkong.config.logaspect.FindInstanceAndCreateLogProxy; import com.woowacourse.zzimkkong.domain.Preset; import org.springframework.data.jpa.repository.JpaRepository; -@LogRegistry(group = "repository") +@FindInstanceAndCreateLogProxy(group = "repository") public interface PresetRepository extends JpaRepository { } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java index d0dfe2339..964cb08be 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java @@ -1,6 +1,6 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; +import com.woowacourse.zzimkkong.config.logaspect.FindInstanceAndCreateLogProxy; import com.woowacourse.zzimkkong.domain.Reservation; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -12,7 +12,7 @@ import java.util.Collection; import java.util.List; -@LogRegistry(group = "repository") +@FindInstanceAndCreateLogProxy(group = "repository") public interface ReservationRepository extends JpaRepository, ReservationRepositoryCustom { List findAllBySpaceIdInAndDate(final Collection spaceIds, final LocalDate date); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/SpaceRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/SpaceRepository.java index 98417ee05..2e0ef5578 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/SpaceRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/SpaceRepository.java @@ -1,13 +1,13 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.config.logaspect.LogRegistry; +import com.woowacourse.zzimkkong.config.logaspect.FindInstanceAndCreateLogProxy; import com.woowacourse.zzimkkong.domain.Space; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; -@LogRegistry(group = "repository") +@FindInstanceAndCreateLogProxy(group = "repository") public interface SpaceRepository extends JpaRepository { @Query(value = "select s from Space s inner join fetch s.map m " + "inner join fetch m.member " + From cd119ac8a6cdbc63095a9ef9f41d1a33c9ba19c2 Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Thu, 18 Nov 2021 15:50:17 +0900 Subject: [PATCH 03/24] =?UTF-8?q?feat:=20=EC=9A=94=EC=B2=AD=EB=A7=88?= =?UTF-8?q?=EB=8B=A4=20=EA=B3=A0=EC=9C=A0=ED=95=9C=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EB=94=94(traceId)=EB=A5=BC=20=EB=A7=8C=EB=93=A4=EC=96=B4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=97=90=20=EB=82=A8=EA=B2=A8,=20=EC=B6=94?= =?UTF-8?q?=EC=A0=81=EC=9D=84=20=EC=9A=A9=EC=9D=B4=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=ED=95=9C=EB=8B=A4.=20(#744)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: LogAspectConfigurer -> PostProcessor 로 기능 대체 * refactor: LogAspect, 로그 프록시를 ProxyFactory를 사용해 만듦 * refactor: LogRegistry -> FindInstanceAndCreateLogProxy 이름 변경 * refactor: 로그 프록시 생성시 CGLIB 사용 및 불필요 캐스팅문 삭제 * refactor: 로그 프록시 public 메소드에만 적용 되도록 변경 * refactor: LogMethodExecutionTime @target 으로 변경 부모 클래스의 메소드도 로깅 기준에 포함토록 합니다. * refactor: LogProxyPostProcessor 생성자 protected로 변경 * refactor: LogProxyPostProcessor를 LogAspect 내부 private 클래스로 은닉 * refactor: LogAspect 내부 클래스 외부로 추출, static 메소드 삭제 * feat: 요청마다 로그에 트랜잭션 아이디를 남기도록 수정 * refactor: 웜업이 끝나고 트랜잭션 쓰레드 로컬 초기화 * test: UUIDThreadLocalTest 테스트 코드 작성 * refactor: zzimkkongExceptionHandler 로그 레벨 INFO로 재조정 실수로 바꾸어놓았었습니다. * refactor: 생성자 파라미터에 final 속성 부여 * refactor: ThreadLocal 관련 빈을 MDC를 이용하여 대체 * refactor: ControllerAdvice 내의 traceId 상수화 --- .../zzimkkong/config/LogConfig.java | 21 ++++++++ .../logaspect/ExecutionTimeLogAdvice.java | 2 +- .../zzimkkong/config/logaspect/LogAspect.java | 18 +++++-- .../logaspect/LogProxyPostProcessor.java | 2 +- .../logaspect/LogTraceIdInterceptor.java | 32 ++++++++++++ .../zzimkkong/config/warmup/WarmerConfig.java | 5 +- .../controller/ControllerAdvice.java | 49 +++++++++++++++---- .../infrastructure/warmup/Warmer.java | 1 + 8 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/LogConfig.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogTraceIdInterceptor.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/LogConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/LogConfig.java new file mode 100644 index 000000000..b44ec67e3 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/LogConfig.java @@ -0,0 +1,21 @@ +package com.woowacourse.zzimkkong.config; + +import com.woowacourse.zzimkkong.config.logaspect.LogTraceIdInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class LogConfig implements WebMvcConfigurer { + private final LogTraceIdInterceptor logTraceIdInterceptor; + + public LogConfig(final LogTraceIdInterceptor logTraceIdInterceptor) { + this.logTraceIdInterceptor = logTraceIdInterceptor; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(logTraceIdInterceptor) + .addPathPatterns("/**"); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java index 509f2860b..f755213d8 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/ExecutionTimeLogAdvice.java @@ -10,7 +10,7 @@ public class ExecutionTimeLogAdvice implements MethodInterceptor { private final Class typeToLog; private final String logGroup; - protected ExecutionTimeLogAdvice(LogAspect logAspect, Class typeToLog, String logGroup) { + protected ExecutionTimeLogAdvice(final LogAspect logAspect, final Class typeToLog, final String logGroup) { this.logAspect = logAspect; this.typeToLog = typeToLog; this.logGroup = logGroup; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java index 9e37127a5..b2dd48470 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogAspect.java @@ -4,7 +4,9 @@ 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.slf4j.MDC; import org.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor; import org.springframework.aop.framework.ProxyFactory; import org.springframework.stereotype.Component; @@ -17,9 +19,10 @@ @Component @Aspect public class LogAspect { + public static final String ALL_ZZIMKKONG_PUBLIC_METHOD_POINTCUT_EXPRESSION = "execution(public * com.woowacourse.zzimkkong..*(..))"; @Around("@target(com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime)" + - "&& execution(public * com.woowacourse.zzimkkong..*(..))") + "&& allZzimkkongPublicMethod()") public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); @@ -43,15 +46,18 @@ void logExecutionInfo(MethodSignature methodSignature, long timeTaken, String lo } void logExecutionInfo(Class typeToLog, Method method, long timeTaken, String logGroup) { - log.info("{} took {} ms. (info group by '{}')", + String traceId = MDC.get("traceId"); + + log.info("{} took {} ms. (info group: '{}', traceId: {})", value("method", typeToLog.getName() + "." + method.getName() + "()"), value("execution_time", timeTaken), - value("group", logGroup)); + value("group", logGroup), + value("traceId", traceId)); } Object createLogProxy(Object target, Class typeToLog, String logGroup) { AspectJExpressionPointcutAdvisor advisor = new AspectJExpressionPointcutAdvisor(); - advisor.setExpression("execution(public * com.woowacourse.zzimkkong..*(..))"); + advisor.setExpression(ALL_ZZIMKKONG_PUBLIC_METHOD_POINTCUT_EXPRESSION); ExecutionTimeLogAdvice advice = new ExecutionTimeLogAdvice(this, typeToLog, logGroup); advisor.setAdvice(advice); @@ -67,4 +73,8 @@ private String getLogGroupFromAnnotation(ProceedingJoinPoint joinPoint) { Class targetClass = joinPoint.getTarget().getClass(); return targetClass.getAnnotation(LogMethodExecutionTime.class).group(); } + + @Pointcut(ALL_ZZIMKKONG_PUBLIC_METHOD_POINTCUT_EXPRESSION) + private void allZzimkkongPublicMethod() { + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java index 76ab595fe..ce220e8ef 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogProxyPostProcessor.java @@ -13,7 +13,7 @@ public class LogProxyPostProcessor implements BeanPostProcessor { private final LogAspect logAspect; private final Set> typesAnnotatedWith; - protected LogProxyPostProcessor(LogAspect logAspect) { + protected LogProxyPostProcessor(final LogAspect logAspect) { this.logAspect = logAspect; Reflections reflections = new Reflections("com.woowacourse.zzimkkong"); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogTraceIdInterceptor.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogTraceIdInterceptor.java new file mode 100644 index 000000000..b135f4d05 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/logaspect/LogTraceIdInterceptor.java @@ -0,0 +1,32 @@ +package com.woowacourse.zzimkkong.config.logaspect; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.UUID; + +@Slf4j +@Component +public class LogTraceIdInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + String traceId = generateTraceId(); + MDC.put("traceId", traceId); + return true; + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + MDC.clear(); + } + + private String generateTraceId() { + return UUID.randomUUID().toString().substring(24); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java index de3cf4dd5..2cf14e110 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java @@ -18,7 +18,10 @@ public class WarmerConfig { private final SlackUrl slackUrl; private final WebClient webClient; - public WarmerConfig(final BatikConverter batikConverter, final SlackUrl slackUrl, final WebClient webClient) { + public WarmerConfig( + final BatikConverter batikConverter, + final SlackUrl slackUrl, + final WebClient webClient) { this.batikConverter = batikConverter; this.slackUrl = slackUrl; this.webClient = webClient; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java index a8ffd06a0..aa1cf3aa4 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java @@ -9,6 +9,7 @@ import com.woowacourse.zzimkkong.exception.infrastructure.InfrastructureMalfunctionException; import com.woowacourse.zzimkkong.exception.member.NoSuchOAuthMemberException; import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; import org.springframework.dao.DataAccessException; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; @@ -17,16 +18,22 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.validation.ConstraintViolationException; +import java.io.PrintWriter; +import java.io.StringWriter; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.FORMAT_MESSAGE; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.SERVER_ERROR_MESSAGE; +import static net.logstash.logback.argument.StructuredArguments.value; @Slf4j @RestControllerAdvice public class ControllerAdvice { + private static final String MESSAGE_FORMAT = "{} (traceId: {})"; + private static final String TRACE_ID_KEY = "traceId"; + @ExceptionHandler(NoSuchOAuthMemberException.class) public ResponseEntity oAuthLoginFailHandler(final NoSuchOAuthMemberException exception) { - log.info(exception.getMessage()); + logInfo(exception.getMessage()); return ResponseEntity .status(exception.getStatus()) .body(OAuthLoginFailErrorResponse.from(exception)); @@ -34,7 +41,7 @@ public ResponseEntity oAuthLoginFailHandler(final N @ExceptionHandler(InputFieldException.class) public ResponseEntity inputFieldExceptionHandler(final InputFieldException exception) { - log.info(exception.getMessage()); + logInfo(exception.getMessage()); return ResponseEntity .status(exception.getStatus()) .body(InputFieldErrorResponse.from(exception)); @@ -42,7 +49,7 @@ public ResponseEntity inputFieldExceptionHandler(final @ExceptionHandler(InfrastructureMalfunctionException.class) public ResponseEntity wrongConfigurationOfInfrastructureException(final InfrastructureMalfunctionException exception) { - log.warn(exception.getMessage(), exception); + logWarn(exception.getMessage(), exception); return ResponseEntity .status(exception.getStatus()) .body(ErrorResponse.from(exception)); @@ -50,7 +57,7 @@ public ResponseEntity wrongConfigurationOfInfrastructureException @ExceptionHandler(ZzimkkongException.class) public ResponseEntity zzimkkongExceptionHandler(final ZzimkkongException exception) { - log.info(exception.getMessage()); + logInfo(exception.getMessage()); return ResponseEntity .status(exception.getStatus()) .body(ErrorResponse.from(exception)); @@ -58,31 +65,55 @@ public ResponseEntity zzimkkongExceptionHandler(final ZzimkkongEx @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity invalidArgumentHandler(final MethodArgumentNotValidException exception) { - log.info(exception.getMessage()); + logInfo(exception.getMessage()); return ResponseEntity.badRequest().body(InputFieldErrorResponse.from(exception)); } @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity invalidParamHandler(final ConstraintViolationException exception) { - log.info(exception.getMessage()); + logInfo(exception.getMessage()); return ResponseEntity.badRequest().body(ErrorResponse.from(exception)); } @ExceptionHandler({InvalidFormatException.class, HttpMessageNotReadableException.class}) public ResponseEntity invalidFormatHandler() { - log.info(FORMAT_MESSAGE); + logInfo(FORMAT_MESSAGE); return ResponseEntity.badRequest().body(ErrorResponse.invalidFormat()); } @ExceptionHandler(DataAccessException.class) public ResponseEntity invalidDataAccessHandler(final DataAccessException exception) { - log.warn(SERVER_ERROR_MESSAGE, exception); + logWarn(SERVER_ERROR_MESSAGE, exception); return ResponseEntity.internalServerError().build(); } @ExceptionHandler(Exception.class) public ResponseEntity unhandledExceptionHandler(final Exception exception) { - log.warn(exception.getMessage(), exception); + logWarn(exception.getMessage(), exception); return ResponseEntity.internalServerError().build(); } + + private void logInfo(String message) { + log.info(MESSAGE_FORMAT, + message, + value(TRACE_ID_KEY, getTraceId())); + } + + private void logWarn(String message, Exception exception) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + + exception.printStackTrace(printWriter); + String stackTrace = stringWriter.toString(); + + log.warn(MESSAGE_FORMAT, + message, + value(TRACE_ID_KEY, getTraceId()), + value("stack_trace", stackTrace)); + } + + private Object getTraceId() { + return MDC.get(TRACE_ID_KEY); + } + } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java index 15f4caa27..236eb9f58 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java @@ -3,6 +3,7 @@ import com.woowacourse.zzimkkong.domain.SlackUrl; import com.woowacourse.zzimkkong.infrastructure.thumbnail.BatikConverter; import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; From 5028b8a3cc6a455c6b0333d47b9b5f8e60912826 Mon Sep 17 00:00:00 2001 From: Jungseok Sung <58401309+sakjung@users.noreply.github.com> Date: Thu, 25 Nov 2021 23:59:05 +0900 Subject: [PATCH 04/24] =?UTF-8?q?feat:=20Reservation=20hasConflictWith=20m?= =?UTF-8?q?ethod=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95=20(#747)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: Reservation hasConflictWith method 로직 수정 * fix: ReservationTest 코드분기 다 타도록 수정 * index.html 제거 * refactor: 테스트에서 불필요한 필드 제거 --- .../zzimkkong/domain/Reservation.java | 21 +++++-------------- .../zzimkkong/domain/ReservationTest.java | 11 +++++++++- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java index dd4191b85..49ba5d473 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java @@ -64,26 +64,15 @@ protected Reservation( } public boolean hasConflictWith(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { - boolean contains = contains(startDateTime, endDateTime); - boolean intersects = intersects(startDateTime, endDateTime); - boolean equals = equals(startDateTime, endDateTime); - - return contains || intersects || equals; - } - - private boolean contains(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { - return (startDateTime.isAfter(startTime) && endDateTime.isBefore(endTime)) - || (startDateTime.isEqual(startTime) && endDateTime.isBefore(endTime)) - || (startDateTime.isAfter(startTime) && endDateTime.isEqual(endTime)); + return !(isEarlier(endDateTime) || isLater(startDateTime)); } - private boolean intersects(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { - return (startDateTime.isBefore(startTime) && endDateTime.isAfter(startTime)) - || (endDateTime.isAfter(endTime) && startDateTime.isBefore(endTime)); + private boolean isEarlier(final LocalDateTime endDateTime) { + return endDateTime.equals(this.startTime) || endDateTime.isBefore(this.startTime); } - private boolean equals(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { - return startDateTime.isEqual(startTime) && endDateTime.isEqual(endTime); + private boolean isLater(final LocalDateTime startDateTime) { + return startDateTime.equals(this.endTime) || startDateTime.isAfter(this.endTime); } public void update(final Reservation updateReservation, final Space space) { diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/domain/ReservationTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/domain/ReservationTest.java index 4b0ff84ef..e69198878 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/domain/ReservationTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/domain/ReservationTest.java @@ -2,6 +2,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -20,17 +21,25 @@ void setUp() { reservation = Reservation.builder() .startTime(THE_DAY_AFTER_TOMORROW.atTime(8, 0)) .endTime(THE_DAY_AFTER_TOMORROW.atTime(9, 0)) + .password("1234") .build(); } @ParameterizedTest @CsvSource(value = {"08:01+08:59+true", "07:59+08:01+true", "08:59+09:01+true", "07:59+09:01+true", "08:00+09:00+true", "07:59+08:00+false", - "09:00+09:01+false", "07:00+08:00+false", "09:00+10:00+false"}, delimiter = '+') + "09:00+09:01+false", "07:00+08:00+false", "09:00+10:00+false", "07:00+07:50+false"}, delimiter = '+') @DisplayName("겹치는 시간 정보가 주어지면 true, 예약 가능한 시간대면 false") void hasConflictWith(String startTime, String endTime, Boolean result) { LocalDateTime start = THE_DAY_AFTER_TOMORROW.atTime(LocalTime.parse(startTime, DateTimeFormatter.ofPattern("HH:mm"))); LocalDateTime end = THE_DAY_AFTER_TOMORROW.atTime(LocalTime.parse(endTime, DateTimeFormatter.ofPattern("HH:mm"))); assertThat(reservation.hasConflictWith(start, end)).isEqualTo(result); } + + @Test + @DisplayName("예약 비밀번호가 잘못되었으면 true, 정확하면 false") + void isWrongPassword() { + assertThat(reservation.isWrongPassword("1321")).isTrue(); + assertThat(reservation.isWrongPassword("1234")).isFalse(); + } } From 8c098876335ac56489bb1ec9da08e524309eb41d Mon Sep 17 00:00:00 2001 From: xrabcde Date: Sun, 12 Dec 2021 18:21:10 +0900 Subject: [PATCH 05/24] =?UTF-8?q?feat:=20=EC=9A=B4=EC=98=81=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=97=90=EC=84=9C=20=EB=A7=B5=EB=B3=84=20=EC=8A=AC?= =?UTF-8?q?=EB=9E=99=20=EC=98=88=EC=95=BD=EC=95=8C=EB=A6=BC=20=EC=B1=84?= =?UTF-8?q?=EB=84=90=20=EB=B6=84=EB=A6=AC=20(#749)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 루터용 Sender와 일반 Sender 분리 * refactor: SlackService 템플릿콜백 패턴 사용하여 리팩터링 * feat: 서브모듈 업데이트 --- .../GuestReservationController.java | 2 +- .../ManagerReservationController.java | 3 +- .../zzimkkong/service/SlackService.java | 51 ------------------- .../zzimkkong/service/slack/LutherSender.java | 24 +++++++++ .../zzimkkong/service/slack/Sender.java | 31 +++++++++++ .../zzimkkong/service/slack/SlackService.java | 48 +++++++++++++++++ .../service/slack/StandardSender.java | 22 ++++++++ backend/src/main/resources/config | 2 +- .../controller/AdminControllerTest.java | 2 +- .../GuestReservationControllerTest.java | 2 +- .../ManagerReservationControllerTest.java | 2 +- 11 files changed, 131 insertions(+), 58 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java index d9364382a..38ff307d5 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java @@ -4,7 +4,7 @@ import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.dto.slack.SlackResponse; import com.woowacourse.zzimkkong.service.ReservationService; -import com.woowacourse.zzimkkong.service.SlackService; +import com.woowacourse.zzimkkong.service.slack.SlackService; import com.woowacourse.zzimkkong.service.strategy.GuestReservationStrategy; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java index 065302749..1c78d68f6 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java @@ -2,12 +2,11 @@ import com.woowacourse.zzimkkong.domain.LoginEmail; import com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime; -import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.dto.slack.SlackResponse; import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.service.ReservationService; -import com.woowacourse.zzimkkong.service.SlackService; +import com.woowacourse.zzimkkong.service.slack.SlackService; import com.woowacourse.zzimkkong.service.strategy.ManagerReservationStrategy; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java deleted file mode 100644 index 5b2deabca..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.woowacourse.zzimkkong.service; - -import com.woowacourse.zzimkkong.domain.SlackUrl; -import com.woowacourse.zzimkkong.dto.slack.Attachments; -import com.woowacourse.zzimkkong.dto.slack.SlackResponse; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.reactive.function.client.WebClient; - -@Service -@Transactional(readOnly = true) -public class SlackService { - private final WebClient slackWebClient; - private final String titleLink; - - public SlackService(@Value("${service.url}") String titleLink, - final SlackUrl slackUrl, - final WebClient webClient) { - this.titleLink = titleLink; - this.slackWebClient = webClient.mutate() - .baseUrl(slackUrl.getUrl()) - .build(); - } - - public void sendCreateMessage(SlackResponse slackResponse) { - Attachments attachments = Attachments.createMessageOf(slackResponse, titleLink); - send(attachments); - } - - public void sendUpdateMessage(SlackResponse slackResponse) { - Attachments attachments = Attachments.updateMessageOf(slackResponse, titleLink); - send(attachments); - } - - public void sendDeleteMessage(SlackResponse slackResponse) { - Attachments attachments = Attachments.deleteMessageOf(slackResponse, titleLink); - send(attachments); - } - - private void send(final Attachments attachments) { - slackWebClient.post() - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(attachments.toString()) - .retrieve() - .bodyToMono(String.class) - .then() - .subscribe(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java new file mode 100644 index 000000000..96c732779 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java @@ -0,0 +1,24 @@ +package com.woowacourse.zzimkkong.service.slack; + +import com.woowacourse.zzimkkong.dto.slack.SlackResponse; +import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +@Profile("prod") +public class LutherSender extends Sender { + public LutherSender(final WebClient slackWebClient, + @Value("${slack.webhook.luther}") final String slackUrl, + final SharingIdGenerator sharingIdGenerator) { + super(slackWebClient.mutate().baseUrl(slackUrl).build(), sharingIdGenerator); + } + + @Override + public boolean isSupport(SlackResponse slackResponse) { + Long mapId = sharingIdGenerator.parseIdFrom(slackResponse.getSharingMapId()); + return LUTHER_ID.equals(mapId); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java new file mode 100644 index 000000000..0c9433cd4 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java @@ -0,0 +1,31 @@ +package com.woowacourse.zzimkkong.service.slack; + +import com.woowacourse.zzimkkong.dto.slack.Attachments; +import com.woowacourse.zzimkkong.dto.slack.SlackResponse; +import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; + +public abstract class Sender { + protected static final Long LUTHER_ID = 7L; + protected final SharingIdGenerator sharingIdGenerator; + private final WebClient slackWebClient; + + protected Sender(final WebClient webClient, + final SharingIdGenerator sharingIdGenerator) { + this.slackWebClient = webClient; + this.sharingIdGenerator = sharingIdGenerator; + } + + public abstract boolean isSupport(SlackResponse slackResponse); + + public void send(Attachments attachments) { + slackWebClient.post() + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(attachments.toString()) + .retrieve() + .bodyToMono(String.class) + .then() + .subscribe(); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java new file mode 100644 index 000000000..60e36874f --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java @@ -0,0 +1,48 @@ +package com.woowacourse.zzimkkong.service.slack; + +import com.woowacourse.zzimkkong.dto.slack.Attachments; +import com.woowacourse.zzimkkong.dto.slack.SlackResponse; +import com.woowacourse.zzimkkong.service.slack.Sender; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional(readOnly = true) +public class SlackService { + private final String titleLink; + private final List senders; + + public SlackService(@Value("${service.url}") final String titleLink, + final List senders) { + this.titleLink = titleLink; + this.senders = senders; + } + + public void sendCreateMessage(SlackResponse slackResponse) { + send(slackResponse, () -> Attachments.createMessageOf(slackResponse, titleLink)); + } + + public void sendUpdateMessage(SlackResponse slackResponse) { + send(slackResponse, () -> Attachments.updateMessageOf(slackResponse, titleLink)); + } + + public void sendDeleteMessage(SlackResponse slackResponse) { + send(slackResponse, () -> Attachments.deleteMessageOf(slackResponse, titleLink)); + } + + private void send(SlackResponse slackResponse, AttachmentsStrategy attachments) { + Sender sender = senders.stream() + .filter(s -> s.isSupport(slackResponse)) + .findAny() + .orElseThrow(); + + sender.send(attachments.getAttachments()); + } + + private interface AttachmentsStrategy { + Attachments getAttachments(); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java new file mode 100644 index 000000000..faa72e352 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java @@ -0,0 +1,22 @@ +package com.woowacourse.zzimkkong.service.slack; + +import com.woowacourse.zzimkkong.domain.SlackUrl; +import com.woowacourse.zzimkkong.dto.slack.SlackResponse; +import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; +import org.springframework.stereotype.Component; +import org.springframework.web.reactive.function.client.WebClient; + +@Component +public class StandardSender extends Sender { + public StandardSender(final WebClient slackWebClient, + final SlackUrl slackUrl, + final SharingIdGenerator sharingIdGenerator) { + super(slackWebClient.mutate().baseUrl(slackUrl.getUrl()).build(), sharingIdGenerator); + } + + @Override + public boolean isSupport(SlackResponse slackResponse) { + Long mapId = sharingIdGenerator.parseIdFrom(slackResponse.getSharingMapId()); + return !LUTHER_ID.equals(mapId); + } +} diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index a2d168ac3..1da639b04 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit a2d168ac3adfb324408ad8afa2bd3e4d3ac35220 +Subproject commit 1da639b04150332ac6c05a21179c8f1c1dcc5796 diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java index c63118ed4..7a4725c14 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java @@ -11,7 +11,7 @@ import com.woowacourse.zzimkkong.dto.space.SpaceFindDetailWithIdResponse; import com.woowacourse.zzimkkong.infrastructure.auth.AuthorizationExtractor; import com.woowacourse.zzimkkong.service.AdminService; -import com.woowacourse.zzimkkong.service.SlackService; +import com.woowacourse.zzimkkong.service.slack.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java index f515d721c..87cd9b03a 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java @@ -2,7 +2,7 @@ import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; -import com.woowacourse.zzimkkong.service.SlackService; +import com.woowacourse.zzimkkong.service.slack.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java index d907aebd7..31c5a0fbf 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java @@ -3,7 +3,7 @@ import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.infrastructure.auth.AuthorizationExtractor; -import com.woowacourse.zzimkkong.service.SlackService; +import com.woowacourse.zzimkkong.service.slack.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; From 550d1e9974fa510659444f328d4cee5a9a2e765c Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Fri, 7 Jan 2022 14:30:07 +0900 Subject: [PATCH 06/24] =?UTF-8?q?refactor:=20ElasticSearch,=20Logstash,=20?= =?UTF-8?q?Kibana,=20Kafka=20=EC=82=AD=EC=A0=9C=20(#755)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 카프카 로그 Appender 사용 중단 * refactor: infra-appender 서브모듈 삭제 * refactor: logstash appender 삭제 * chore: Kafka Appender 의존성 제거 --- .gitmodules | 4 ---- backend/build.gradle | 3 --- .../src/main/resources/appenders/logstash-appender-dev.xml | 7 ------- .../main/resources/appenders/logstash-appender-prod.xml | 7 ------- backend/src/main/resources/infra-appender | 1 - backend/src/main/resources/logback-spring.xml | 5 ----- 6 files changed, 27 deletions(-) delete mode 100644 backend/src/main/resources/appenders/logstash-appender-dev.xml delete mode 100644 backend/src/main/resources/appenders/logstash-appender-prod.xml delete mode 160000 backend/src/main/resources/infra-appender diff --git a/.gitmodules b/.gitmodules index 5ad3462f2..aa8c6b31b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,10 +6,6 @@ path = s3proxy/src/main/resources/s3proxy-config url = git@github.com:zzimkkong/s3proxy-config.git branch = main -[submodule "backend/src/main/resources/infra-appender"] - path = backend/src/main/resources/infra-appender - url = git@github.com:zzimkkong/infra-appender.git - branch = main [submodule "s3proxy/src/main/resources/config"] path = s3proxy/src/main/resources/config url = git@github.com:zzimkkong/config.git diff --git a/backend/build.gradle b/backend/build.gradle index 75d5b6282..5adcd2c88 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -75,9 +75,6 @@ dependencies { // Logstash implementation 'net.logstash.logback:logstash-logback-encoder:6.6' - // Kafka Appender - implementation 'com.github.danielwegener:logback-kafka-appender:0.2.0-RC2' - // Reflections implementation 'org.reflections:reflections:0.9.11' } diff --git a/backend/src/main/resources/appenders/logstash-appender-dev.xml b/backend/src/main/resources/appenders/logstash-appender-dev.xml deleted file mode 100644 index 0a4c4fc37..000000000 --- a/backend/src/main/resources/appenders/logstash-appender-dev.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 192.168.1.222:5044 - - - - diff --git a/backend/src/main/resources/appenders/logstash-appender-prod.xml b/backend/src/main/resources/appenders/logstash-appender-prod.xml deleted file mode 100644 index d36a59e94..000000000 --- a/backend/src/main/resources/appenders/logstash-appender-prod.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - 192.168.1.210:5044 - - - - diff --git a/backend/src/main/resources/infra-appender b/backend/src/main/resources/infra-appender deleted file mode 160000 index 1a4ed48a7..000000000 --- a/backend/src/main/resources/infra-appender +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1a4ed48a7bb9a8c399c6bc8cb5f2111e9e3c2cb9 diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml index c35ab77a6..7369644ac 100644 --- a/backend/src/main/resources/logback-spring.xml +++ b/backend/src/main/resources/logback-spring.xml @@ -24,7 +24,6 @@ - @@ -32,7 +31,6 @@ - @@ -41,7 +39,6 @@ - @@ -49,7 +46,6 @@ - @@ -57,5 +53,4 @@ - From 8a3212ed881a2720b1cbbcb79b00124bfc836907 Mon Sep 17 00:00:00 2001 From: xrabcde Date: Fri, 7 Jan 2022 15:36:12 +0900 Subject: [PATCH 07/24] =?UTF-8?q?refactor:=20DB=20Replication,=20Redis=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#754)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: datasource 관련 코드 제거 * chore: 서브모듈 업데이트 * refactor: Redis 관련 코드 제거 * build: Redis 의존성 제거 * feat: 서브모듈 업데이트 * feat: 서브모듈 업데이트 --- backend/build.gradle | 4 -- .../datasource/CustomDataSourceConfig.java | 62 ------------------- .../ReplicationRoutingDataSource.java | 21 ------- .../redis/RedisCachingConfiguration.java | 40 ------------ .../config/redis/RedisConfiguration.java | 43 ------------- .../zzimkkong/service/MapService.java | 7 --- backend/src/main/resources/config | 2 +- 7 files changed, 1 insertion(+), 178 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/CustomDataSourceConfig.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/ReplicationRoutingDataSource.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisCachingConfiguration.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisConfiguration.java diff --git a/backend/build.gradle b/backend/build.gradle index 5adcd2c88..ac82fc69e 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -26,10 +26,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' - // Redis - implementation 'org.springframework.boot:spring-boot-starter-data-redis' - implementation 'org.springframework.boot:spring-boot-starter-cache:2.5.4' - // Security implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.security:spring-security-test' diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/CustomDataSourceConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/CustomDataSourceConfig.java deleted file mode 100644 index 2bec4c289..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/CustomDataSourceConfig.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.woowacourse.zzimkkong.config.datasource; - -import com.zaxxer.hikari.HikariDataSource; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.context.annotation.Profile; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import javax.sql.DataSource; -import java.util.*; - -import static com.woowacourse.zzimkkong.config.datasource.ReplicationRoutingDataSource.DATASOURCE_KEY_MASTER; -import static com.woowacourse.zzimkkong.config.datasource.ReplicationRoutingDataSource.DATASOURCE_KEY_SLAVE; - -@Configuration -@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class}) -@EnableTransactionManagement -@EnableJpaRepositories(basePackages = {"com.woowacourse.zzimkkong"}) -@Profile("prod") -public class CustomDataSourceConfig { - - @Bean - @ConfigurationProperties(prefix = "spring.datasource.hikari.master") - public DataSource masterDataSource() { - return DataSourceBuilder.create().type(HikariDataSource.class).build(); - } - - @Bean - @ConfigurationProperties(prefix = "spring.datasource.hikari.slave") - public DataSource slaveDataSource() { - return DataSourceBuilder.create().type(HikariDataSource.class).build(); - } - - @Bean - public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource master, - @Qualifier("slaveDataSource") DataSource slave) { - ReplicationRoutingDataSource routingDataSource = new ReplicationRoutingDataSource(); - - HashMap sources = new HashMap<>(); - sources.put(DATASOURCE_KEY_MASTER, master); - sources.put(DATASOURCE_KEY_SLAVE, slave); - - routingDataSource.setTargetDataSources(sources); - routingDataSource.setDefaultTargetDataSource(master); - - return routingDataSource; - } - - @Primary - @Bean - public DataSource dataSource(@Qualifier("routingDataSource") DataSource routingDataSource) { - return new LazyConnectionDataSourceProxy(routingDataSource); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/ReplicationRoutingDataSource.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/ReplicationRoutingDataSource.java deleted file mode 100644 index 9e5f19c89..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/datasource/ReplicationRoutingDataSource.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.woowacourse.zzimkkong.config.datasource; - -import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; -import org.springframework.transaction.support.TransactionSynchronizationManager; - -public class ReplicationRoutingDataSource extends AbstractRoutingDataSource { - public static final String DATASOURCE_KEY_MASTER = "master"; - public static final String DATASOURCE_KEY_SLAVE = "slave"; - - @Override - protected Object determineCurrentLookupKey() { - boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); - if (isReadOnly) { - logger.info("Connection Slave"); - return DATASOURCE_KEY_SLAVE; - } else { - logger.info("Connection Master"); - return DATASOURCE_KEY_MASTER; - } - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisCachingConfiguration.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisCachingConfiguration.java deleted file mode 100644 index 178e31601..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisCachingConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.woowacourse.zzimkkong.config.redis; - -import com.woowacourse.zzimkkong.dto.map.MapFindResponse; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.redis.cache.RedisCacheConfiguration; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; -import org.springframework.data.redis.serializer.RedisSerializationContext; -import org.springframework.data.redis.serializer.StringRedisSerializer; - -import java.time.Duration; - -@EnableCaching -@Configuration -@Profile("prod") -public class RedisCachingConfiguration { - private final RedisConnectionFactory redisConnectionFactory; - - public RedisCachingConfiguration(RedisConnectionFactory redisConnectionFactory) { - this.redisConnectionFactory = redisConnectionFactory; - } - - @Bean(name = "cacheManager") - public CacheManager redisCacheManager() { - RedisCacheConfiguration redisCachingConfiguration = RedisCacheConfiguration.defaultCacheConfig() - .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) - .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( - new Jackson2JsonRedisSerializer<>(MapFindResponse.class))) - .entryTtl(Duration.ofDays(1)); - - return RedisCacheManager.RedisCacheManagerBuilder - .fromConnectionFactory(redisConnectionFactory) - .cacheDefaults(redisCachingConfiguration).build(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisConfiguration.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisConfiguration.java deleted file mode 100644 index 37f428a16..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/redis/RedisConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.woowacourse.zzimkkong.config.redis; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.redis.connection.RedisConnectionFactory; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; - -@EnableRedisRepositories -@Configuration -@Profile("prod") -public class RedisConfiguration { - private final String host; - private final String password; - private final int port; - - public RedisConfiguration( - @Value("${spring.redis.host}") String host, - @Value("${spring.redis.password}") String password, - @Value("${spring.redis.port}") int port) { - this.host = host; - this.password = password; - this.port = port; - } - - @Bean - public RedisConnectionFactory redisConnectionFactory() { - RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(host, port); - configuration.setPassword(password); - return new LettuceConnectionFactory(configuration); - } - - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(redisConnectionFactory()); - return redisTemplate; - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java index 977ec2907..8c762ed45 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java @@ -17,8 +17,6 @@ import com.woowacourse.zzimkkong.repository.MapRepository; import com.woowacourse.zzimkkong.repository.MemberRepository; import com.woowacourse.zzimkkong.repository.ReservationRepository; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -86,9 +84,6 @@ public MapFindAllResponse findAllMaps(final LoginEmailDto loginEmailDto) { .collect(collectingAndThen(toList(), mapFindResponses -> MapFindAllResponse.of(mapFindResponses, manager))); } - @Cacheable(key = "#sharingMapId", - value = "map", - unless = "#result == null") @Transactional(readOnly = true) public MapFindResponse findMapBySharingId(final String sharingMapId) { Long mapId = sharingIdGenerator.parseIdFrom(sharingMapId); @@ -97,8 +92,6 @@ public MapFindResponse findMapBySharingId(final String sharingMapId) { return MapFindResponse.of(map, sharingIdGenerator.from(map)); } - @CacheEvict(value = "map", - allEntries = true) public void updateMap(final Long mapId, final MapCreateUpdateRequest mapCreateUpdateRequest, final LoginEmailDto loginEmailDto) { diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index 1da639b04..f4353025e 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit 1da639b04150332ac6c05a21179c8f1c1dcc5796 +Subproject commit f4353025e5325afb704bae8f45065b5e7000901d From 869d0989b53187e73a0efc45bda46a36fd6582d1 Mon Sep 17 00:00:00 2001 From: xrabcde Date: Fri, 14 Jan 2022 13:03:22 +0900 Subject: [PATCH 08/24] =?UTF-8?q?feat:=20=EC=84=9C=EB=B8=8C=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20(#758)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/main/resources/config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index f4353025e..cba79ba47 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit f4353025e5325afb704bae8f45065b5e7000901d +Subproject commit cba79ba473cf676f1b40a70cb0273dc0d6be6b5e From a00763bb058c95d807ea1e3026ea7176f1fbd4c7 Mon Sep 17 00:00:00 2001 From: Yeonwoo Cho Date: Fri, 21 Jan 2022 13:44:18 +0900 Subject: [PATCH 09/24] =?UTF-8?q?refactor:=20jacoco,=20sonarqube=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=84=A4=EC=A0=95=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?(#760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: build 시 jacoco, sonarqube 관련 설정 제거 * refactor: 동작하지 않는 test disabled 처리 --- backend/build.gradle | 59 ------------------- .../thumbnail/S3ProxyUploaderTest.java | 2 + 2 files changed, 2 insertions(+), 59 deletions(-) diff --git a/backend/build.gradle b/backend/build.gradle index ac82fc69e..19d3cd838 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -3,8 +3,6 @@ plugins { id 'io.spring.dependency-management' version '1.0.11.RELEASE' id "org.asciidoctor.convert" version "1.5.10" id 'java' - id 'jacoco' - id "org.sonarqube" version "3.3" } group = 'com.woowacourse' @@ -78,10 +76,6 @@ dependencies { test { outputs.dir snippetsDir useJUnitPlatform() - jacoco { - destinationFile = file("$buildDir/jacoco/jacoco.exec") - } - finalizedBy jacocoTestReport } asciidoctor { @@ -101,56 +95,3 @@ bootJar { into 'static/docs' } } - -jacocoTestReport { - reports { - html.enabled true - xml.enabled true - csv.enabled false - } - finalizedBy jacocoTestCoverageVerification -} - -jacocoTestCoverageVerification { - violationRules { - rule { - element = 'CLASS' - excludes = ["**.exception.**", "**.ControllerAdvice", "**.*ErrorResponse", "**.ValidatorMessage", - "**.ZzimkkongApplication", "**.DataLoader", "**.config.**", - "**.LoginInterceptor", "**.AuthenticationPrincipalArgumentResolver", - "**.slack.**", "**.Slack*", "**.AdminPageController", "**.Warmer*"] - - limit { - counter = 'BRANCH' - value = 'COVEREDRATIO' - minimum = 1.0 - } - - limit { - counter = 'INSTRUCTION' - value = 'COVEREDRATIO' - minimum = 0.9 - } - } - } -} - -project.tasks["jacocoTestCoverageVerification"].finalizedBy "sonarqube" -sonarqube { - def sonarProperties = new Properties() - sonarProperties.load(new FileInputStream(file("src/main/resources/config/sonar.properties"))) - def sonarToken = sonarProperties.getProperty("SONAR_TOKEN") - - properties { - property "sonar.host.url", "http://zzimkkong-service.o-r.kr:8000" - property "sonar.login", sonarToken - property 'sonar.sources', 'src' - property 'sonar.language', 'java' - property 'sonar.projectVersion', '0.0.1-SNAPSHOT' - property 'sonar.sourceEncoding', 'UTF-8' - property 'sonar.coverage.jacoco.xmlReportPaths', 'build/reports/jacoco/test/jacocoTestReport.xml' - property 'sonar.java.binaries', 'build/classes' - property 'sonar.test.inclusions', '**/*Test.java' - property 'sonar.exclusions', '**/*Doc*.java, **/resources/**, **/config/datasource/**, **/DataLoader.java, **/Warmer.java' - } -} diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java index e31f11cf2..153890726 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java @@ -7,6 +7,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,7 @@ @SpringBootTest @ActiveProfiles("test") +@Disabled class S3ProxyUploaderTest { private static final String URL_REGEX = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX); From 2612899a3cfea6099ce5f45d82aa965a80966b19 Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Tue, 25 Jan 2022 00:31:32 +0900 Subject: [PATCH 10/24] =?UTF-8?q?refactor:=20=EC=8D=B8=EB=84=A4=EC=9D=BC?= =?UTF-8?q?=EC=9A=A9=20=EC=9D=B4=EB=AF=B8=EC=A7=80=EB=A5=BC=20png=EB=A1=9C?= =?UTF-8?q?=20=EB=B3=80=ED=99=98=ED=95=98=EC=A7=80=20=EC=95=8A=EA=B3=A0=20?= =?UTF-8?q?svg=EB=A1=9C=20=EC=A0=80=EC=9E=A5=ED=95=9C=EB=8B=A4.=20(#764)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 8 - backend/build.gradle | 6 - .../com/woowacourse/zzimkkong/DataLoader.java | 2 +- .../zzimkkong/config/S3ProxyConfig.java | 37 ---- .../config/warmup/WarmUpScheduler.java | 26 --- .../zzimkkong/config/warmup/WarmerConfig.java | 45 ----- .../com/woowacourse/zzimkkong/domain/Map.java | 14 +- .../dto/map/MapCreateUpdateRequest.java | 6 +- .../zzimkkong/dto/map/MapFindResponse.java | 14 +- .../thumbnail/BatikConverter.java | 30 --- .../thumbnail/S3ProxyUploader.java | 78 -------- .../thumbnail/StorageUploader.java | 9 - .../thumbnail/SvgConverter.java | 8 - .../thumbnail/ThumbnailManager.java | 9 - .../thumbnail/ThumbnailManagerImpl.java | 57 ------ .../infrastructure/warmup/Warmer.java | 88 --------- .../zzimkkong/service/MapService.java | 13 +- .../zzimkkong/service/SpaceService.java | 9 +- .../migration/prod/V13__thumbnail_as_svg.sql | 11 ++ .../com/woowacourse/zzimkkong/Constants.java | 1 - .../zzimkkong/controller/AcceptanceTest.java | 7 - .../controller/AdminControllerTest.java | 3 +- .../GuestReservationControllerTest.java | 3 +- .../controller/GuestSpaceControllerTest.java | 3 +- .../ManagerReservationControllerTest.java | 2 +- .../ManagerSpaceControllerTest.java | 2 +- .../controller/MapControllerTest.java | 9 +- .../woowacourse/zzimkkong/domain/MapTest.java | 9 +- .../zzimkkong/domain/SpaceTest.java | 3 +- .../dto/SpaceCreateUpdateRequestTest.java | 1 - .../SharingIdGeneratorTest.java | 6 +- .../thumbnail/BatikConverterTest.java | 66 ------- .../thumbnail/S3ProxyUploaderTest.java | 117 ----------- .../thumbnail/ThumbnailManagerImplTest.java | 51 ----- .../repository/MapRepositoryTest.java | 6 +- .../ReservationRepositoryImplTest.java | 3 +- .../repository/ReservationRepositoryTest.java | 2 +- .../repository/SpaceRepositoryTest.java | 3 +- .../zzimkkong/service/AdminServiceTest.java | 6 +- .../service/GuestReservationServiceTest.java | 3 +- .../ManagerReservationServiceTest.java | 3 +- .../zzimkkong/service/MapServiceTest.java | 11 +- .../zzimkkong/service/ServiceTest.java | 4 - .../zzimkkong/service/SpaceServiceTest.java | 13 +- s3proxy/.gitignore | 37 ---- s3proxy/build.gradle | 81 -------- s3proxy/docker/main/Dockerfile | 17 -- s3proxy/gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 5 - s3proxy/gradlew | 185 ------------------ s3proxy/gradlew.bat | 89 --------- s3proxy/script/deploy.sh | 20 -- s3proxy/settings.gradle | 1 - s3proxy/src/docs/asciidoc/index.adoc | 24 --- .../s3proxy/S3proxyApplication.java | 13 -- .../s3proxy/config/AuthInterceptorConfig.java | 26 --- .../config/AuthenticationPrincipalConfig.java | 21 -- .../s3proxy/config/MultipartConfig.java | 15 -- .../s3proxy/config/StorageConfig.java | 30 --- .../s3proxy/controller/ControllerAdvice.java | 20 -- .../s3proxy/controller/S3ProxyController.java | 36 ---- .../s3proxy/dto/ErrorResponse.java | 19 -- ...uthorizationHeaderUninvolvedException.java | 10 - .../s3proxy/exception/S3ProxyException.java | 19 -- .../s3proxy/exception/S3UploadException.java | 12 -- .../UnsupportedFileExtensionException.java | 11 -- .../infrastructure/AuthInterceptor.java | 35 ---- .../AuthorizationExtractor.java | 20 -- .../s3proxy/infrastructure/S3Uploader.java | 82 -------- .../s3proxy/service/S3Service.java | 26 --- .../resources/appenders/console-appender.xml | 9 - .../appenders/file-appender-debug.xml | 24 --- .../appenders/file-appender-error.xml | 24 --- .../appenders/file-appender-info.xml | 24 --- .../appenders/file-appender-trace.xml | 24 --- .../appenders/file-appender-warn.xml | 24 --- .../appenders/kafka-appender-dev.xml | 11 -- .../appenders/kafka-appender-prod.xml | 11 -- .../src/main/resources/application-dev.yml | 9 - .../src/main/resources/application-local.yml | 14 -- .../src/main/resources/application-test.yml | 14 -- s3proxy/src/main/resources/application.yml | 3 - s3proxy/src/main/resources/config | 1 - s3proxy/src/main/resources/logback-spring.xml | 58 ------ s3proxy/src/main/resources/s3proxy-config | 1 - .../com/woowacourse/s3proxy/Constants.java | 6 - .../woowacourse/s3proxy/DocumentUtils.java | 31 --- .../s3proxy/controller/AcceptanceTest.java | 37 ---- .../controller/S3ProxyControllerTest.java | 102 ---------- .../infrastructure/S3UploaderTest.java | 71 ------- .../s3proxy/service/S3ServiceTest.java | 57 ------ .../s3proxy/service/ServiceTest.java | 9 - s3proxy/src/test/resources/luther.png | Bin 4577 -> 0 bytes 93 files changed, 75 insertions(+), 2150 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/S3ProxyConfig.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmUpScheduler.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverter.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploader.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/StorageUploader.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SvgConverter.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManager.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImpl.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java create mode 100644 backend/src/main/resources/db/migration/prod/V13__thumbnail_as_svg.sql rename backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/{thumbnail => sharingid}/SharingIdGeneratorTest.java (91%) delete mode 100644 backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverterTest.java delete mode 100644 backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java delete mode 100644 backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImplTest.java delete mode 100644 s3proxy/.gitignore delete mode 100644 s3proxy/build.gradle delete mode 100644 s3proxy/docker/main/Dockerfile delete mode 100644 s3proxy/gradle/wrapper/gradle-wrapper.jar delete mode 100644 s3proxy/gradle/wrapper/gradle-wrapper.properties delete mode 100755 s3proxy/gradlew delete mode 100644 s3proxy/gradlew.bat delete mode 100644 s3proxy/script/deploy.sh delete mode 100644 s3proxy/settings.gradle delete mode 100644 s3proxy/src/docs/asciidoc/index.adoc delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/S3proxyApplication.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthInterceptorConfig.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthenticationPrincipalConfig.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/config/MultipartConfig.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/config/StorageConfig.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/controller/ControllerAdvice.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/controller/S3ProxyController.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/dto/ErrorResponse.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/exception/AuthorizationHeaderUninvolvedException.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3ProxyException.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3UploadException.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/exception/UnsupportedFileExtensionException.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthInterceptor.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthorizationExtractor.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/S3Uploader.java delete mode 100644 s3proxy/src/main/java/com/woowacourse/s3proxy/service/S3Service.java delete mode 100644 s3proxy/src/main/resources/appenders/console-appender.xml delete mode 100644 s3proxy/src/main/resources/appenders/file-appender-debug.xml delete mode 100644 s3proxy/src/main/resources/appenders/file-appender-error.xml delete mode 100644 s3proxy/src/main/resources/appenders/file-appender-info.xml delete mode 100644 s3proxy/src/main/resources/appenders/file-appender-trace.xml delete mode 100644 s3proxy/src/main/resources/appenders/file-appender-warn.xml delete mode 100644 s3proxy/src/main/resources/appenders/kafka-appender-dev.xml delete mode 100644 s3proxy/src/main/resources/appenders/kafka-appender-prod.xml delete mode 100644 s3proxy/src/main/resources/application-dev.yml delete mode 100644 s3proxy/src/main/resources/application-local.yml delete mode 100644 s3proxy/src/main/resources/application-test.yml delete mode 100644 s3proxy/src/main/resources/application.yml delete mode 160000 s3proxy/src/main/resources/config delete mode 100644 s3proxy/src/main/resources/logback-spring.xml delete mode 160000 s3proxy/src/main/resources/s3proxy-config delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/Constants.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/DocumentUtils.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/controller/AcceptanceTest.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/controller/S3ProxyControllerTest.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/infrastructure/S3UploaderTest.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/service/S3ServiceTest.java delete mode 100644 s3proxy/src/test/java/com/woowacourse/s3proxy/service/ServiceTest.java delete mode 100644 s3proxy/src/test/resources/luther.png diff --git a/.gitmodules b/.gitmodules index aa8c6b31b..3c09b9341 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,11 +2,3 @@ path = backend/src/main/resources/config url = git@github.com:zzimkkong/config.git branch = main -[submodule "s3proxy/src/main/resources/s3proxy-config"] - path = s3proxy/src/main/resources/s3proxy-config - url = git@github.com:zzimkkong/s3proxy-config.git - branch = main -[submodule "s3proxy/src/main/resources/config"] - path = s3proxy/src/main/resources/config - url = git@github.com:zzimkkong/config.git - branch = main diff --git a/backend/build.gradle b/backend/build.gradle index 19d3cd838..304f0ea30 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -46,12 +46,6 @@ dependencies { // Jwt implementation 'io.jsonwebtoken:jjwt:0.9.1' - // SvgToPng - implementation 'org.apache.xmlgraphics:batik-all:1.12' - implementation 'org.apache.xmlgraphics:xmlgraphics-commons:2.4' - implementation 'xml-apis:xml-apis:1.4.01' - implementation 'xml-apis:xml-apis-ext:1.3.04' - // Cryptor implementation 'commons-codec:commons-codec:1.15' diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/DataLoader.java b/backend/src/main/java/com/woowacourse/zzimkkong/DataLoader.java index da635b8b3..9276ee639 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/DataLoader.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/DataLoader.java @@ -48,7 +48,7 @@ public void run(String... args) { new Map( "루터회관", "{'id': '1', 'type': 'polyline', 'fill': '', 'stroke': 'rgba(111, 111, 111, 1)', 'points': '['60,250', '1,231', '242,252']', 'd': '[]', 'transform': ''}", - "https://d1dgzmdd5f1fx6.cloudfront.net/thumbnails-local/1.png", + "프론트 강의실1프론트 강의실 2회의실 3회의실 4회의실 51234방송실전화백엔드 강의실회의실 1회의실 25트랙방", pobi) ); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/S3ProxyConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/S3ProxyConfig.java deleted file mode 100644 index 68825ba00..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/S3ProxyConfig.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.woowacourse.zzimkkong.config; - -import com.woowacourse.zzimkkong.infrastructure.thumbnail.S3ProxyUploader; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.StorageUploader; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.context.annotation.PropertySource; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -@PropertySource("classpath:config/s3proxy.properties") -public class S3ProxyConfig { - private final WebClient webClient; - - public S3ProxyConfig(final WebClient webClient) { - this.webClient = webClient; - } - - @Bean(name = "storageUploader") - @Profile("prod") - public StorageUploader storageUploaderProd( - @Value("${s3proxy.server-uri.prod}") final String serverUri, - @Value("${s3proxy.secret-key.prod}") final String secretKey) { - return new S3ProxyUploader(serverUri, secretKey, webClient); - } - - @Bean(name = "storageUploader") - @Profile({"dev", "local", "test"}) - public StorageUploader storageUploaderDev( - @Value("${s3proxy.server-uri.dev}") final String serverUri, - @Value("${s3proxy.secret-key.dev}") final String secretKey) { - return new S3ProxyUploader(serverUri, secretKey, webClient); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmUpScheduler.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmUpScheduler.java deleted file mode 100644 index 120976f30..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmUpScheduler.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.woowacourse.zzimkkong.config.warmup; - -import com.woowacourse.zzimkkong.infrastructure.warmup.Warmer; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; - -import javax.annotation.PostConstruct; - -@EnableScheduling -@Configuration -@Profile("!test") -public class WarmUpScheduler { - private Warmer warmer; - - public WarmUpScheduler(final Warmer warmer) { - this.warmer = warmer; - } - - @Scheduled(fixedDelay = 60 * 60 * 1000) - public void warmUp() { - warmer.warmUp(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java deleted file mode 100644 index 2cf14e110..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.woowacourse.zzimkkong.config.warmup; - -import com.woowacourse.zzimkkong.domain.SlackUrl; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.BatikConverter; -import com.woowacourse.zzimkkong.infrastructure.warmup.Warmer; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.context.annotation.PropertySource; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -@PropertySource("classpath:config/s3proxy.properties") -@Profile("!test") -public class WarmerConfig { - private final BatikConverter batikConverter; - private final SlackUrl slackUrl; - private final WebClient webClient; - - public WarmerConfig( - final BatikConverter batikConverter, - final SlackUrl slackUrl, - final WebClient webClient) { - this.batikConverter = batikConverter; - this.slackUrl = slackUrl; - this.webClient = webClient; - } - - @Bean(name = "warmer") - @Profile("prod") - public Warmer warmerProd( - @Value("${s3proxy.server-uri.prod}") final String serverUri, - @Value("${s3proxy.secret-key.prod}") final String secretKey) { - return new Warmer(batikConverter, slackUrl, webClient, serverUri, secretKey); - } - - @Bean(name = "warmer") - @Profile({"dev", "local"}) - public Warmer warmerDev( - @Value("${s3proxy.server-uri.dev}") final String serverUri, - @Value("${s3proxy.secret-key.dev}") final String secretKey) { - return new Warmer(batikConverter, slackUrl, webClient, serverUri, secretKey); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java index 863dfe519..ca0a58000 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java @@ -26,7 +26,7 @@ public class Map { @Column(nullable = false) @Lob - private String mapImageUrl; + private String thumbnail; @ManyToOne @JoinColumn(name = "member_id", foreignKey = @ForeignKey(name = "fk_map_member"), nullable = false) @@ -35,10 +35,10 @@ public class Map { @OneToMany(mappedBy = "map", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, orphanRemoval = true) private List spaces = new ArrayList<>(); - public Map(final String name, final String mapDrawing, final String mapImageUrl, final Member member) { + public Map(final String name, final String mapDrawing, final String thumbnail, final Member member) { this.name = name; this.mapDrawing = mapDrawing; - this.mapImageUrl = mapImageUrl; + this.thumbnail = thumbnail; this.member = member; if (member != null) { @@ -46,8 +46,8 @@ public Map(final String name, final String mapDrawing, final String mapImageUrl, } } - public Map(final Long id, final String name, final String mapDrawing, final String mapImageUrl, final Member member) { - this(name, mapDrawing, mapImageUrl, member); + public Map(final Long id, final String name, final String mapDrawing, final String thumbnail, final Member member) { + this(name, mapDrawing, thumbnail, member); this.id = id; } @@ -71,8 +71,8 @@ public Optional findSpaceById(final Long spaceId) { .findFirst(); } - public void updateImageUrl(final String mapImageUrl) { - this.mapImageUrl = mapImageUrl; + public void updateThumbnail(final String thumbnail) { + this.thumbnail = thumbnail; } public void addSpace(final Space space) { diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapCreateUpdateRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapCreateUpdateRequest.java index 94d172cf9..8b4ff174d 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapCreateUpdateRequest.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapCreateUpdateRequest.java @@ -17,11 +17,11 @@ public class MapCreateUpdateRequest { private String mapDrawing; @NotBlank(message = EMPTY_MESSAGE) - private String mapImageSvg; + private String thumbnail; - public MapCreateUpdateRequest(final String mapName, final String mapDrawing, final String mapImageSvg) { + public MapCreateUpdateRequest(final String mapName, final String mapDrawing, final String thumbnail) { this.mapName = mapName; this.mapDrawing = mapDrawing; - this.mapImageSvg = mapImageSvg; + this.thumbnail = thumbnail; } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapFindResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapFindResponse.java index ea4a04dcc..381cb3d88 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapFindResponse.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/MapFindResponse.java @@ -10,32 +10,32 @@ public class MapFindResponse { private Long mapId; private String mapName; private String mapDrawing; - private String mapImageUrl; + private String thumbnail; private String sharingMapId; private String managerEmail; private MapFindResponse(final Long mapId, final String mapName, final String mapDrawing, - final String mapImageUrl, + final String thumbnail, final String sharingMapId) { this.mapId = mapId; this.mapName = mapName; this.mapDrawing = mapDrawing; - this.mapImageUrl = mapImageUrl; + this.thumbnail = thumbnail; this.sharingMapId = sharingMapId; } private MapFindResponse(final Long mapId, final String mapName, final String mapDrawing, - final String mapImageUrl, + final String thumbnail, final String sharingMapId, final String managerEmail) { this.mapId = mapId; this.mapName = mapName; this.mapDrawing = mapDrawing; - this.mapImageUrl = mapImageUrl; + this.thumbnail = thumbnail; this.sharingMapId = sharingMapId; this.managerEmail = managerEmail; } @@ -46,7 +46,7 @@ public static MapFindResponse of(final Map map, map.getId(), map.getName(), map.getMapDrawing(), - map.getMapImageUrl(), + map.getThumbnail(), sharingMapId ); } @@ -57,7 +57,7 @@ public static MapFindResponse ofAdmin(final Map map, map.getId(), map.getName(), map.getMapDrawing(), - map.getMapImageUrl(), + map.getThumbnail(), sharingMapId, map.getMember().getEmail() ); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverter.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverter.java deleted file mode 100644 index 9fa382f04..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime; -import com.woowacourse.zzimkkong.exception.infrastructure.SvgToPngConvertException; -import org.apache.batik.transcoder.TranscoderException; -import org.apache.batik.transcoder.TranscoderInput; -import org.apache.batik.transcoder.TranscoderOutput; -import org.apache.batik.transcoder.image.PNGTranscoder; -import org.springframework.stereotype.Component; - -import java.io.InputStream; -import java.io.OutputStream; - -@Component -@LogMethodExecutionTime(group = "infrastructure") -public class BatikConverter implements SvgConverter { - private final PNGTranscoder pngTranscoder = new PNGTranscoder(); - - @Override - public void convertSvgToPng(InputStream inputStream, OutputStream outputStream) { - try { - TranscoderInput transcoderInput = new TranscoderInput(inputStream); - TranscoderOutput transcoderOutput = new TranscoderOutput(outputStream); - - pngTranscoder.transcode(transcoderInput, transcoderOutput); - } catch (TranscoderException e) { - throw new SvgToPngConvertException(e); - } - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploader.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploader.java deleted file mode 100644 index a6a155409..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploader.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime; -import com.woowacourse.zzimkkong.exception.infrastructure.S3ProxyRespondedFailException; -import com.woowacourse.zzimkkong.exception.infrastructure.S3UploadException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.client.MultipartBodyBuilder; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; - -import java.io.InputStream; -import java.util.Objects; - -@LogMethodExecutionTime(group = "infrastructure") -public class S3ProxyUploader implements StorageUploader { - private static final String PATH_DELIMITER = "/"; - private static final String API_PATH = "/api/storage"; - private static final String CONTENT_DISPOSITION_HEADER_VALUE_FORMAT = "form-data; name=file; filename=%s"; - - private final WebClient proxyServerClient; - private final String secretKey; - - public S3ProxyUploader( - @Value("${s3proxy.server-uri}") final String serverUri, - final String secretKey, - final WebClient webClient) { - this.proxyServerClient = webClient.mutate() - .baseUrl(serverUri) - .build(); - this.secretKey = secretKey; - } - - @Override - public String upload(String directoryName, String uploadFileName, InputStream inputStream) { - MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder(); - multipartBodyBuilder.part("file", new InputStreamResource(inputStream)) - .header(HttpHeaders.CONTENT_DISPOSITION, - String.format(CONTENT_DISPOSITION_HEADER_VALUE_FORMAT, uploadFileName)); - - return requestMultipartUpload(directoryName, multipartBodyBuilder); - } - - private String requestMultipartUpload(String directoryName, MultipartBodyBuilder multipartBodyBuilder) { - return proxyServerClient - .method(HttpMethod.POST) - .uri(String.join(PATH_DELIMITER, API_PATH, directoryName)) - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .header(HttpHeaders.AUTHORIZATION, secretKey) - .body(BodyInserters.fromMultipartData(multipartBodyBuilder.build())) - .exchangeToMono(clientResponse -> { - if (clientResponse.statusCode().equals(HttpStatus.CREATED)) { - String location = Objects.requireNonNull( - clientResponse.headers().asHttpHeaders().get(HttpHeaders.LOCATION)) - .stream().findFirst() - .orElseThrow(S3UploadException::new); - return Mono.just(location); - } - return Mono.error(S3ProxyRespondedFailException::new); - }) - .block(); - } - - @Override - public void delete(String directoryName, String fileName) { - proxyServerClient - .method(HttpMethod.DELETE) - .uri(String.join(PATH_DELIMITER, API_PATH, directoryName, fileName)) - .retrieve() - .bodyToMono(String.class) - .block(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/StorageUploader.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/StorageUploader.java deleted file mode 100644 index e4a03d18d..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/StorageUploader.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import java.io.InputStream; - -public interface StorageUploader { - String upload(final String directoryName, String uploadFileName, final InputStream inputStream); - - void delete(final String directoryName, final String fileName); -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SvgConverter.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SvgConverter.java deleted file mode 100644 index e7808ea78..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SvgConverter.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import java.io.InputStream; -import java.io.OutputStream; - -public interface SvgConverter { - void convertSvgToPng(final InputStream inputStream, final OutputStream outputStream); -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManager.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManager.java deleted file mode 100644 index 1400277b9..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManager.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.domain.Map; - -public interface ThumbnailManager { - String uploadMapThumbnail(final String svgData, final Map map); - - void deleteThumbnail(final Map map); -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImpl.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImpl.java deleted file mode 100644 index dc02db47b..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.domain.Map; -import com.woowacourse.zzimkkong.exception.infrastructure.CannotGenerateInputStreamFromSvgDataException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.io.*; - -@Component -public class ThumbnailManagerImpl implements ThumbnailManager { - public static final String THUMBNAIL_EXTENSION = ".png"; - private static final String THUMBNAIL_FILE_FORMAT = "%s"; - - private final SvgConverter svgConverter; - private final StorageUploader storageUploader; - private final String thumbnailsDirectoryName; - - public ThumbnailManagerImpl( - final SvgConverter svgConverter, - final StorageUploader storageUploader, - @Value("${storage.thumbnails-directory}") final String thumbnailsDirectoryName) { - this.svgConverter = svgConverter; - this.storageUploader = storageUploader; - this.thumbnailsDirectoryName = thumbnailsDirectoryName; - } - - public String uploadMapThumbnail(final String svgData, final Map map) { - try (final ByteArrayInputStream svgInputStream = new ByteArrayInputStream(svgData.getBytes()); - final ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream()) { - final BufferedInputStream bufferedSvgInputStream = new BufferedInputStream(svgInputStream); - final BufferedOutputStream bufferedPngOutputStream = new BufferedOutputStream(pngOutputStream); - - final String fileName = makeThumbnailFileName(map); - svgConverter.convertSvgToPng(bufferedSvgInputStream, bufferedPngOutputStream); - - return uploadFromByteArray(fileName, pngOutputStream.toByteArray()); - } catch (IOException exception) { - throw new CannotGenerateInputStreamFromSvgDataException(exception); - } - } - - private String makeThumbnailFileName(final Map map) { - return String.format(THUMBNAIL_FILE_FORMAT, map.getId().toString() + THUMBNAIL_EXTENSION); - } - - private String uploadFromByteArray(String fileName, byte[] byteArray) throws IOException { - try (final BufferedInputStream bufferedPngInputStream = new BufferedInputStream(new ByteArrayInputStream(byteArray))) { - return storageUploader.upload(thumbnailsDirectoryName, fileName, bufferedPngInputStream); - } - } - - public void deleteThumbnail(final Map map) { - String fileName = makeThumbnailFileName(map); - storageUploader.delete(thumbnailsDirectoryName, fileName + THUMBNAIL_EXTENSION); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java deleted file mode 100644 index 236eb9f58..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.warmup; - -import com.woowacourse.zzimkkong.domain.SlackUrl; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.BatikConverter; -import lombok.extern.slf4j.Slf4j; -import org.slf4j.MDC; -import org.springframework.core.io.InputStreamResource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.client.MultipartBodyBuilder; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.reactive.function.client.WebClient; -import reactor.core.publisher.Mono; - -import java.io.*; - -@Slf4j -public class Warmer { - private final BatikConverter batikConverter; - private final SlackUrl slackUrl; - private final WebClient webClient; - private final String s3ProxyServerUri; - private final String s3ProxyServerSecretKey; - - public Warmer( - final BatikConverter batikConverter, - final SlackUrl slackUrl, - final WebClient webClient, - final String s3ProxyServerUri, - final String s3ProxyServerSecretKey) { - this.batikConverter = batikConverter; - this.slackUrl = slackUrl; - this.webClient = webClient; - this.s3ProxyServerUri = s3ProxyServerUri; - this.s3ProxyServerSecretKey = s3ProxyServerSecretKey; - } - - public void warmUp() { - log.info("warm up을 시작합니다"); - - initSvgConverter(); - initWebClient(); - - log.info("warm up이 완료 되었습니다."); - } - - private void initSvgConverter() { - String svgData = " "; - - try (final ByteArrayInputStream svgInputStream = new ByteArrayInputStream(svgData.getBytes()); - final ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream()) { - final BufferedInputStream bufferedSvgInputStream = new BufferedInputStream(svgInputStream); - final BufferedOutputStream bufferedPngOutputStream = new BufferedOutputStream(pngOutputStream); - batikConverter.convertSvgToPng(bufferedSvgInputStream, bufferedPngOutputStream); - } catch (IOException ignored) { - } - } - - private void initWebClient() { - webClient - .post() - .uri(slackUrl.getUrl()) - .contentType(MediaType.APPLICATION_JSON) - .bodyValue("let's warm up") - .retrieve() - .bodyToMono(String.class) - .onErrorResume(e -> Mono.just("warm up finisehd")) - .subscribe(); - - try (final BufferedInputStream bufferedPngInputStream = new BufferedInputStream(new ByteArrayInputStream("warmUp".getBytes()))) { - MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder(); - multipartBodyBuilder.part("file", new InputStreamResource(bufferedPngInputStream)) - .header(HttpHeaders.CONTENT_DISPOSITION, "form-data; name=file; filename=warmFile"); - - webClient - .post() - .uri(s3ProxyServerUri + "/api/storage/warmup") - .header(HttpHeaders.CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE) - .header(HttpHeaders.AUTHORIZATION, s3ProxyServerSecretKey) - .body(BodyInserters.fromMultipartData(multipartBodyBuilder.build())) - .retrieve() - .bodyToMono(String.class) - .onErrorResume(e -> Mono.just("warm up finished")) - .block(); - } catch (IOException ignored) { - } - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java index 8c762ed45..2505bb542 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java @@ -7,13 +7,12 @@ import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; import com.woowacourse.zzimkkong.dto.map.MapFindAllResponse; import com.woowacourse.zzimkkong.dto.map.MapFindResponse; +import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.exception.authorization.NoAuthorityOnMapException; import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.member.NoSuchMemberException; import com.woowacourse.zzimkkong.exception.space.ReservationExistOnSpaceException; -import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.ThumbnailManager; import com.woowacourse.zzimkkong.repository.MapRepository; import com.woowacourse.zzimkkong.repository.MemberRepository; import com.woowacourse.zzimkkong.repository.ReservationRepository; @@ -32,19 +31,16 @@ public class MapService { private final MemberRepository members; private final MapRepository maps; private final ReservationRepository reservations; - private final ThumbnailManager thumbnailManager; private final SharingIdGenerator sharingIdGenerator; public MapService( final MemberRepository members, final MapRepository maps, final ReservationRepository reservations, - final ThumbnailManager thumbnailManager, final SharingIdGenerator sharingIdGenerator) { this.members = members; this.maps = maps; this.reservations = reservations; - this.thumbnailManager = thumbnailManager; this.sharingIdGenerator = sharingIdGenerator; } @@ -54,12 +50,9 @@ public MapCreateResponse saveMap(final MapCreateUpdateRequest mapCreateUpdateReq Map saveMap = maps.save(new Map( mapCreateUpdateRequest.getMapName(), mapCreateUpdateRequest.getMapDrawing(), - mapCreateUpdateRequest.getMapImageSvg().substring(0, 10), + mapCreateUpdateRequest.getThumbnail(), manager)); - final String thumbnailUrl = thumbnailManager.uploadMapThumbnail(mapCreateUpdateRequest.getMapImageSvg(), saveMap); - saveMap.updateImageUrl(thumbnailUrl); - return MapCreateResponse.from(saveMap); } @@ -99,7 +92,6 @@ public void updateMap(final Long mapId, .orElseThrow(NoSuchMapException::new); validateManagerOfMap(map, loginEmailDto.getEmail()); - thumbnailManager.uploadMapThumbnail(mapCreateUpdateRequest.getMapImageSvg(), map); map.update( mapCreateUpdateRequest.getMapName(), mapCreateUpdateRequest.getMapDrawing()); @@ -113,7 +105,6 @@ public void deleteMap(final Long mapId, final LoginEmailDto loginEmailDto) { validateExistReservations(map); maps.delete(map); - thumbnailManager.deleteThumbnail(map); } private void validateExistReservations(final Map map) { diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java index 7b52d0fff..2c7d40aca 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java @@ -9,7 +9,6 @@ import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; import com.woowacourse.zzimkkong.exception.space.ReservationExistOnSpaceException; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.ThumbnailManager; import com.woowacourse.zzimkkong.repository.MapRepository; import com.woowacourse.zzimkkong.repository.ReservationRepository; import com.woowacourse.zzimkkong.repository.SpaceRepository; @@ -25,17 +24,14 @@ public class SpaceService { private final MapRepository maps; private final SpaceRepository spaces; private final ReservationRepository reservations; - private final ThumbnailManager thumbnailManager; public SpaceService( final MapRepository maps, final SpaceRepository spaces, - final ReservationRepository reservations, - final ThumbnailManager thumbnailManager) { + final ReservationRepository reservations) { this.maps = maps; this.spaces = spaces; this.reservations = reservations; - this.thumbnailManager = thumbnailManager; } public SpaceCreateResponse saveSpace( @@ -57,7 +53,6 @@ public SpaceCreateResponse saveSpace( .build(); Space saveSpace = spaces.save(space); - thumbnailManager.uploadMapThumbnail(spaceCreateUpdateRequest.getMapImageSvg(), map); return SpaceCreateResponse.from(saveSpace); } @@ -119,7 +114,6 @@ public void updateSpace( .build(); space.update(updateSpace); - thumbnailManager.uploadMapThumbnail(spaceCreateUpdateRequest.getMapImageSvg(), map); } public void deleteSpace( @@ -137,7 +131,6 @@ public void deleteSpace( validateReservationExistence(spaceId); spaces.delete(space); - thumbnailManager.uploadMapThumbnail(spaceDeleteRequest.getMapImageSvg(), map); } private Setting getSetting(final SpaceCreateUpdateRequest spaceCreateUpdateRequest) { diff --git a/backend/src/main/resources/db/migration/prod/V13__thumbnail_as_svg.sql b/backend/src/main/resources/db/migration/prod/V13__thumbnail_as_svg.sql new file mode 100644 index 000000000..1357c4398 --- /dev/null +++ b/backend/src/main/resources/db/migration/prod/V13__thumbnail_as_svg.sql @@ -0,0 +1,11 @@ +SET foreign_key_checks = 0; + +TRUNCATE TABLE map; +TRUNCATE TABLE member; +TRUNCATE TABLE preset; +TRUNCATE TABLE reservation; +TRUNCATE TABLE space; + +SET foreign_key_checks = 1; + +ALTER TABLE map CHANGE COLUMN map_image_url thumbnail longtext not null; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java b/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java index 2a46adade..2fbb59ef4 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java @@ -21,7 +21,6 @@ private Constants() { public static final String DESCRIPTION = "찜꽁 1차 회의"; public static final String USER_NAME = "찜꽁"; public static final String RESERVATION_PW = "1234"; - public static final String MAP_IMAGE_URL = "https://zzimkkong-personal.s3.ap-northeast-2.amazonaws.com/thumbnails/2387563.png"; public static final String MAP_SVG = " "; public static final String SPACE_DRAWING = "{ \"id\": \"1\", \"type\" : \"rect\", \"x\": \"10\", \"y\": \"10\", \"width\": \"30\", \"height\": \"30\" }"; public static final String LUTHER_NAME = "루터회관"; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java index 1aac0fa30..fb5e82e14 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java @@ -9,7 +9,6 @@ import com.woowacourse.zzimkkong.dto.space.SpaceCreateUpdateRequest; import com.woowacourse.zzimkkong.infrastructure.oauth.GithubRequester; import com.woowacourse.zzimkkong.infrastructure.oauth.GoogleRequester; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.StorageUploader; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.specification.RequestSpecification; @@ -88,9 +87,6 @@ class AcceptanceTest { @Autowired private DatabaseCleaner databaseCleaner; - @MockBean - private StorageUploader storageUploader; - @Autowired protected PasswordEncoder passwordEncoder; @@ -110,9 +106,6 @@ void setUp(RestDocumentationContextProvider restDocumentation) { saveMember(memberSaveRequest); accessToken = getToken(); - - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); } @AfterEach diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java index 7a4725c14..c3d6760f5 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.controller; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.admin.*; import com.woowacourse.zzimkkong.dto.map.MapFindResponse; @@ -40,7 +41,7 @@ class AdminControllerTest extends AcceptanceTest { private SlackService slackService; private static final Member POBI = new Member(memberSaveRequest.getEmail(), memberSaveRequest.getPassword(), memberSaveRequest.getOrganization()); - private static final Map LUTHER = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, POBI); + private static final Map LUTHER = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, POBI); private static final Setting BE_SETTING = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) .availableEndTime(BE_AVAILABLE_END_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java index 87cd9b03a..00dc7b35b 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.controller; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.service.slack.SlackService; @@ -62,7 +63,7 @@ void setUp() { SALLY_DESCRIPTION); Member pobi = new Member(EMAIL, passwordEncoder.encode(PW), ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestSpaceControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestSpaceControllerTest.java index 2b54ee2ff..76404468f 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestSpaceControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestSpaceControllerTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.controller; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Setting; @@ -39,7 +40,7 @@ void setUp() { Long feSpaceId = Long.valueOf(saveFe1SpaceResponse.header("location").split("/")[6]); Member pobi = new Member(EMAIL, passwordEncoder.encode(PW), ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) .availableEndTime(BE_AVAILABLE_END_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java index 31c5a0fbf..953389800 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java @@ -60,7 +60,7 @@ void setUp() { SALLY_DESCRIPTION); Member pobi = new Member(EMAIL, passwordEncoder.encode(PW), ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerSpaceControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerSpaceControllerTest.java index 28ef82ed8..5464241b3 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerSpaceControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerSpaceControllerTest.java @@ -41,7 +41,7 @@ void setUp() { Long feSpaceId = Long.valueOf(saveFe1SpaceResponse.header("location").split("/")[6]); Member pobi = new Member(EMAIL, passwordEncoder.encode(PW), ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) .availableEndTime(BE_AVAILABLE_END_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java index 61fdcc1de..a18e77dd8 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.controller; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; @@ -43,8 +44,8 @@ void setUp() { // For Test Comparison pobi = new Member(EMAIL, passwordEncoder.encode(PW), ORGANIZATION); - luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); - smallHouse = new Map(SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); + smallHouse = new Map(SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); } @Test @@ -72,8 +73,8 @@ void findAll() { String lutherId = createdMapApi.split("/")[4]; String smallHouseId = smallHouseCreatedMapApi.header("location").split("/")[4]; - Map lutherWithId = new Map(Long.parseLong(lutherId), luther.getName(), luther.getMapDrawing(), luther.getMapImageUrl(), luther.getMember()); - Map smallHouseWithId = new Map(Long.parseLong(smallHouseId), smallHouse.getName(), smallHouse.getMapDrawing(), smallHouse.getMapImageUrl(), smallHouse.getMember()); + Map lutherWithId = new Map(Long.parseLong(lutherId), luther.getName(), luther.getMapDrawing(), luther.getThumbnail(), luther.getMember()); + Map smallHouseWithId = new Map(Long.parseLong(smallHouseId), smallHouse.getName(), smallHouse.getMapDrawing(), smallHouse.getThumbnail(), smallHouse.getMember()); Iterator expectedMapIterator = List.of(lutherWithId, smallHouseWithId).iterator(); // when diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java index 906e9190e..cf993a744 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.domain; +import com.woowacourse.zzimkkong.Constants; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -13,7 +14,7 @@ class MapTest { @DisplayName("Space가 생성되면 Map에 Space를 추가한다") void addSpace() { Member pobi = new Member(EMAIL, PW, ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); assertThat(luther.getSpaces().size()).isZero(); Space.builder() @@ -27,7 +28,7 @@ void addSpace() { @DisplayName("맵의 관리자가 맞으면 true, 아니면 false") void isOwnedBy(String email, boolean expected) { Member pobi = new Member(EMAIL, PW, ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); boolean result = luther.isOwnedBy(email); assertThat(result).isEqualTo(expected); @@ -39,12 +40,12 @@ void isOwnedBy(String email, boolean expected) { void addMap(boolean nullable) { Map luther; if (nullable) { - luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, null); + luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, null); assertThat(luther.getMember()).isNull(); return; } Member pobi = new Member(EMAIL, PW, ORGANIZATION); - luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); assertThat(pobi.getMaps()).contains(luther); } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java index fd2dfdbe5..58d4e6f76 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.domain; +import com.woowacourse.zzimkkong.Constants; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -18,7 +19,7 @@ class SpaceTest { @Test void update() { Member member = new Member(EMAIL, PW, ORGANIZATION); - Map map = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, member); + Map map = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, member); Setting setting = Setting.builder() .availableStartTime(LocalTime.of(10, 0)) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/dto/SpaceCreateUpdateRequestTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/dto/SpaceCreateUpdateRequestTest.java index aeb6246f0..e05d9567b 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/dto/SpaceCreateUpdateRequestTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/dto/SpaceCreateUpdateRequestTest.java @@ -5,7 +5,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; -import static com.woowacourse.zzimkkong.Constants.MAP_SVG; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.EMPTY_MESSAGE; import static org.assertj.core.api.Assertions.assertThat; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SharingIdGeneratorTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/sharingid/SharingIdGeneratorTest.java similarity index 91% rename from backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SharingIdGeneratorTest.java rename to backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/sharingid/SharingIdGeneratorTest.java index bb8bd68ee..2da355387 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/SharingIdGeneratorTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/sharingid/SharingIdGeneratorTest.java @@ -1,10 +1,8 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; +package com.woowacourse.zzimkkong.infrastructure.sharingid; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.exception.map.InvalidAccessLinkException; -import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; -import com.woowacourse.zzimkkong.infrastructure.sharingid.Transcoder; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -34,7 +32,7 @@ void setUp() { luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, - MAP_IMAGE_URL, + MAP_SVG, pobi); } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverterTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverterTest.java deleted file mode 100644 index 5dfae0c88..000000000 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/BatikConverterTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.exception.infrastructure.SvgToPngConvertException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; - -import java.io.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; - -@SpringBootTest -@ActiveProfiles("test") -class BatikConverterTest { - private static final String LUTHER_SVG = "프론트 강의실1프론트 강의실 2회의실 3회의실 4회의실 51234방송실전화백엔드 강의실회의실 1회의실 25트랙방"; - - @Autowired - private BatikConverter batikConverter; - - private File testFile; - - @Test - @DisplayName("svg 데이터를 png 데이터로 변환한다.") - void convertAndWriteToOutputStream() { - // given - try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(LUTHER_SVG.getBytes()); - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { - BufferedInputStream bufferedInputStream = new BufferedInputStream(byteArrayInputStream); - BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream); - - // when, then - assertDoesNotThrow(() -> batikConverter.convertSvgToPng(bufferedInputStream, bufferedOutputStream)); - } catch (IOException ignore) { - } - } - - @Test - @DisplayName("옳지 않은 svg 데이터가 들어오면 오류가 발생한다.") - void convertException() { - // given - String rawSvgData = "strangeSvgData"; - - // when, then - try (final BufferedInputStream bufferedInputStream = new BufferedInputStream(new ByteArrayInputStream(rawSvgData.getBytes())); - final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new ByteArrayOutputStream())) { - - assertThatThrownBy(() -> batikConverter.convertSvgToPng(bufferedInputStream, bufferedOutputStream)) - .isInstanceOf(SvgToPngConvertException.class); - } catch (IOException exception) { - exception.printStackTrace(); - } - } - - @AfterEach - void deleteFile() { - if (testFile != null) { - testFile.delete(); - testFile = null; - } - } -} diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java deleted file mode 100644 index 153890726..000000000 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/S3ProxyUploaderTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.exception.infrastructure.S3ProxyRespondedFailException; -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.http.HttpStatus; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.web.reactive.function.client.WebClient; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -@SpringBootTest -@ActiveProfiles("test") -@Disabled -class S3ProxyUploaderTest { - private static final String URL_REGEX = "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"; - private static final Pattern URL_PATTERN = Pattern.compile(URL_REGEX); - - @Autowired - private S3ProxyUploader s3ProxyUploader; - - private final String testDirectoryName = "testDirectoryName"; - private File testFile; - - @Test - @DisplayName("데이터를 업로드한 후 파일의 url을 받아온다.") - void uploadInputStreamData() throws FileNotFoundException { - // given - final String fileName = "luther.png"; - String filePath = getClass().getClassLoader().getResource(fileName).getFile(); - testFile = new File(filePath); - - final FileInputStream fileInputStream = new FileInputStream(testFile); - - // when - String uri = s3ProxyUploader.upload(testDirectoryName, fileName, fileInputStream); - Matcher matcher = URL_PATTERN.matcher(uri); - - // then - assertThat(matcher.find()).isTrue(); - } - - @Test - @DisplayName("업로드된 파일을 삭제한다.") - void delete() throws FileNotFoundException { - final String fileName = "luther.png"; - String filePath = getClass().getClassLoader().getResource(fileName).getFile(); - testFile = new File(filePath); - - final FileInputStream fileInputStream = new FileInputStream(testFile); - - String uri = s3ProxyUploader.upload(testDirectoryName, fileName, fileInputStream); - - // when - s3ProxyUploader.delete(testDirectoryName, testFile.getName()); - RestAssured.port = RestAssured.UNDEFINED_PORT; - ExtractableResponse response = RestAssured - .given().log().all() - .accept("application/json") - .when().get(uri) - .then().log().all().extract(); - - // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.FORBIDDEN.value()); - } - - @Test - @DisplayName("업로드시 서버측이 201 응답을 보내지 않으면 예외가 발생한다.") - void invalidUrl() { - // given - try (MockWebServer mockGithubServer = new MockWebServer()) { - mockGithubServer.start(); - mockGithubServer.enqueue(new MockResponse() - .setResponseCode(400)); - - String hostName = mockGithubServer.getHostName(); - int port = mockGithubServer.getPort(); - - S3ProxyUploader s3ProxyUploader = new S3ProxyUploader("http://" + hostName + ":" + port, "secretKey", WebClient.create()); - - String filePath = getClass().getClassLoader().getResource("luther.png").getFile(); - testFile = new File(filePath); - - // when, then - try (final FileInputStream fileInputStream = new FileInputStream(testFile)) { - assertThatThrownBy(() -> s3ProxyUploader.upload(testDirectoryName, testFile.getName(), fileInputStream)) - .isInstanceOf(S3ProxyRespondedFailException.class); - } - } catch (IOException ignored) { - } - } - - @AfterEach - void tearDown() { - if (testFile != null) { - s3ProxyUploader.delete(testDirectoryName, testFile.getName()); - testFile = null; - } - } -} diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImplTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImplTest.java deleted file mode 100644 index 3b68567cf..000000000 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/thumbnail/ThumbnailManagerImplTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.woowacourse.zzimkkong.infrastructure.thumbnail; - -import com.woowacourse.zzimkkong.domain.Map; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ActiveProfiles; - -import java.io.InputStream; -import java.util.Random; - -import static com.woowacourse.zzimkkong.Constants.MAP_IMAGE_URL; -import static com.woowacourse.zzimkkong.Constants.MAP_SVG; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -@SpringBootTest -@ActiveProfiles("test") -class ThumbnailManagerImplTest { - @Autowired - ThumbnailManagerImpl thumbnailManager; - - @MockBean - SvgConverter svgConverter; - - @MockBean - StorageUploader storageUploader; - - @Test - @DisplayName("Map의 svg 데이터와 Map을 받고 썸네일의 url을 받아온다.") - void uploadMapThumbnailInmemory() { - // given - Map mockMap = mock(Map.class); - long mapId = new Random().nextLong(); - given(mockMap.getId()) - .willReturn(mapId); - - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); - - // when - String mapThumbnailUrl = thumbnailManager.uploadMapThumbnail(MAP_SVG, mockMap); - - assertThat(mapThumbnailUrl).isEqualTo(MAP_IMAGE_URL); - } -} diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/MapRepositoryTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/MapRepositoryTest.java index b74a9705e..8cad84689 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/MapRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/MapRepositoryTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.repository; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Setting; @@ -13,7 +14,6 @@ import org.springframework.data.domain.Sort; import java.util.List; -import java.util.Optional; import static com.woowacourse.zzimkkong.Constants.*; import static org.assertj.core.api.Assertions.assertThat; @@ -26,8 +26,8 @@ class MapRepositoryTest extends RepositoryTest { @BeforeEach void setUp() { pobi = new Member(EMAIL, PW, ORGANIZATION); - luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); - smallHouse = new Map(SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); + smallHouse = new Map(SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); members.save(pobi); } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java index 8d2ad8a53..f4038ff12 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.repository; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.DisplayName; @@ -20,7 +21,7 @@ void existsReservationsByMember(boolean isReservationExists) { Member sakjung = new Member(NEW_EMAIL, PW, ORGANIZATION); Member savedMember = members.save(sakjung); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, savedMember); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, savedMember); maps.save(luther); Setting beSetting = Setting.builder() diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java index da1438d76..d866ce59d 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java @@ -32,7 +32,7 @@ class ReservationRepositoryTest extends RepositoryTest { @BeforeEach void setUp() { pobi = new Member(EMAIL, PW, ORGANIZATION); - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/SpaceRepositoryTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/SpaceRepositoryTest.java index f4b5d6475..6659d135d 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/SpaceRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/SpaceRepositoryTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.repository; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Setting; @@ -24,7 +25,7 @@ class SpaceRepositoryTest extends RepositoryTest { @BeforeEach void setUp() { Member pobi = new Member(EMAIL, PW, ORGANIZATION); - luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java index bf28f7918..dc5cd9e5e 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java @@ -84,7 +84,7 @@ void findMembers() { @DisplayName("모든 맵을 페이지네이션을 이용해 조회한다.") void findMaps() { //given - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); PageRequest pageRequest = PageRequest.of(0, 20, Sort.unsorted()); given(maps.findAllByFetch(any(Pageable.class))) .willReturn(new PageImpl<>(List.of(luther), pageRequest, 1)); @@ -106,7 +106,7 @@ void findMaps() { @DisplayName("모든 공간을 페이지네이션을 이용해 조회한다.") void findSpaces() { //given - Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) .availableEndTime(BE_AVAILABLE_END_TIME) @@ -147,7 +147,7 @@ void findSpaces() { @DisplayName("모든 예약을 페이지네이션을 이용해 조회한다.") void findReservations() { //given - Map luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + Map luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) .availableEndTime(BE_AVAILABLE_END_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java index 503312444..ef04dfedb 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.service; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; @@ -60,7 +61,7 @@ class GuestReservationServiceTest extends ServiceTest { @BeforeEach void setUp() { Member pobi = new Member(EMAIL, PW, ORGANIZATION); - luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java index 2af8fba0a..c656bb4ee 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java @@ -7,7 +7,6 @@ import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.reservation.*; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; -import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.service.strategy.ManagerReservationStrategy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -74,7 +73,7 @@ void setUp() { sakjung = new Member(NEW_EMAIL, PW, ORGANIZATION); pobiEmail = LoginEmailDto.from(EMAIL); sakjungEmail = LoginEmailDto.from(NEW_EMAIL); - luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/MapServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/MapServiceTest.java index 2f43fad5e..ad9c2cf02 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/MapServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/MapServiceTest.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.service; +import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Setting; @@ -18,8 +19,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.io.File; -import java.io.InputStream; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -46,8 +45,8 @@ class MapServiceTest extends ServiceTest { void setUp() { pobi = new Member(EMAIL, PW, ORGANIZATION); pobiEmail = LoginEmailDto.from(EMAIL); - luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); - smallHouse = new Map(2L, SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); + smallHouse = new Map(2L, SMALL_HOUSE_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); lutherId = luther.getId(); Setting beSetting = Setting.builder() @@ -102,8 +101,6 @@ void create() { .willReturn(Optional.of(pobi)); given(maps.save(any(Map.class))) .willReturn(luther); - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); //then MapCreateResponse mapCreateResponse = mapService.saveMap(mapCreateUpdateRequest, pobiEmail); @@ -151,8 +148,6 @@ void update() { MapCreateUpdateRequest mapCreateUpdateRequest = new MapCreateUpdateRequest("이름을 바꿔요", luther.getMapDrawing(), MAP_SVG); given(maps.findById(anyLong())) .willReturn(Optional.of(luther)); - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); //when, then assertDoesNotThrow(() -> mapService.updateMap(luther.getId(), mapCreateUpdateRequest, pobiEmail)); diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/ServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/ServiceTest.java index 7208c5759..162dad607 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/ServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/ServiceTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.service; -import com.woowacourse.zzimkkong.infrastructure.thumbnail.StorageUploader; import com.woowacourse.zzimkkong.repository.MapRepository; import com.woowacourse.zzimkkong.repository.MemberRepository; import com.woowacourse.zzimkkong.repository.ReservationRepository; @@ -28,9 +27,6 @@ class ServiceTest { @MockBean protected SpaceRepository spaces; - @MockBean - protected StorageUploader storageUploader; - @Autowired protected PasswordEncoder passwordEncoder; } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/SpaceServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/SpaceServiceTest.java index 16f1ace91..603607c99 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/SpaceServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/SpaceServiceTest.java @@ -4,19 +4,17 @@ import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Setting; import com.woowacourse.zzimkkong.domain.Space; +import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.dto.space.*; import com.woowacourse.zzimkkong.exception.authorization.NoAuthorityOnMapException; import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; import com.woowacourse.zzimkkong.exception.space.ReservationExistOnSpaceException; -import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import java.io.File; -import java.io.InputStream; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -25,7 +23,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.BDDMockito.given; class SpaceServiceTest extends ServiceTest { @@ -79,7 +78,7 @@ void setUp() { sakjung = new Member(NEW_EMAIL, PW, ORGANIZATION); pobiEmail = LoginEmailDto.from(EMAIL); sakjungEmail = LoginEmailDto.from(NEW_EMAIL); - luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_IMAGE_URL, pobi); + luther = new Map(1L, LUTHER_NAME, MAP_DRAWING_DATA, MAP_SVG, pobi); Setting beSetting = Setting.builder() .availableStartTime(BE_AVAILABLE_START_TIME) @@ -153,8 +152,6 @@ void save() { .willReturn(Optional.of(luther)); given(spaces.save(any(Space.class))) .willReturn(newSpace); - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); // when SpaceCreateResponse spaceCreateResponse = spaceService.saveSpace(luther.getId(), spaceCreateUpdateRequest, pobiEmail); @@ -278,8 +275,6 @@ void update() { // given, when given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(storageUploader.upload(anyString(), anyString(), any(InputStream.class))) - .willReturn(MAP_IMAGE_URL); // then assertDoesNotThrow(() -> spaceService.updateSpace( diff --git a/s3proxy/.gitignore b/s3proxy/.gitignore deleted file mode 100644 index c2065bc26..000000000 --- a/s3proxy/.gitignore +++ /dev/null @@ -1,37 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ diff --git a/s3proxy/build.gradle b/s3proxy/build.gradle deleted file mode 100644 index 2b1206883..000000000 --- a/s3proxy/build.gradle +++ /dev/null @@ -1,81 +0,0 @@ -plugins { - id 'org.springframework.boot' version '2.5.4' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' - id "org.asciidoctor.convert" version "1.5.10" - id 'java' -} - -group = 'com.woowacourse' -version = '0.0.1-SNAPSHOT' -sourceCompatibility = '11' - -configurations { - compileOnly { - extendsFrom annotationProcessor - } -} - -repositories { - mavenCentral() -} - -ext { - snippetsDir = file('build/generated-snippets') -} - -dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - implementation 'org.springframework.boot:spring-boot-starter-webflux' - - // Lombok - compileOnly 'org.projectlombok:lombok' - annotationProcessor 'org.projectlombok:lombok' - - // Multi-Part - implementation 'commons-io:commons-io:2.11.0' - implementation 'commons-fileupload:commons-fileupload:1.4' - - // AWS - implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' - - // Rest Docs - asciidoctor 'org.springframework.restdocs:spring-restdocs-asciidoctor' - testImplementation 'org.springframework.restdocs:spring-restdocs-restassured' - - testImplementation 'io.rest-assured:rest-assured' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'io.projectreactor:reactor-test' - - // Logstash - implementation 'net.logstash.logback:logstash-logback-encoder:6.6' - - // Kafka Appender - implementation 'com.github.danielwegener:logback-kafka-appender:0.2.0-RC2' - - implementation 'javax.xml.bind:jaxb-api:2.3.1' -} - -test { - outputs.dir snippetsDir - useJUnitPlatform() -} - -asciidoctor { - inputs.dir snippetsDir - dependsOn test -} - -task createDocument(type: Copy) { - dependsOn asciidoctor - from file("build/asciidoc/html5/index.html") - into file("src/main/resources/static/docs") -} - -bootJar { - dependsOn createDocument - from("${asciidoctor.outputDir}/html5") { - into 'static/docs' - } -} - - diff --git a/s3proxy/docker/main/Dockerfile b/s3proxy/docker/main/Dockerfile deleted file mode 100644 index 02320bc06..000000000 --- a/s3proxy/docker/main/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:18.04 - -LABEL email="ssangyu123@gmail.com" -LABEL name="sakjung" -LABEL description="zzimkkong s3proxy application" - -RUN apt-get -y update -RUN apt-get install -y openjdk-11-jdk - -# run application -WORKDIR /home/ubuntu -COPY build/libs/s3proxy-0.0.1-SNAPSHOT.jar app.jar -RUN mkdir zzimkkong && mkdir zzimkkong/tmp - -EXPOSE 8080 - -ENTRYPOINT ["java", "-Dspring.profiles.active=prod", "-jar", "app.jar", "--spring.config.location=classpath:s3proxy-config/application-prod.yml"] diff --git a/s3proxy/gradle/wrapper/gradle-wrapper.jar b/s3proxy/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59536 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 QuEJmfE|#`Dqx|px0L@K;Y5)KL diff --git a/s3proxy/gradle/wrapper/gradle-wrapper.properties b/s3proxy/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index da9702f9e..000000000 --- a/s3proxy/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/s3proxy/gradlew b/s3proxy/gradlew deleted file mode 100755 index 744e882ed..000000000 --- a/s3proxy/gradlew +++ /dev/null @@ -1,185 +0,0 @@ -#!/usr/bin/env sh - -# -# Copyright 2015 the original author or authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -## -## Gradle start up script for UN*X -## -############################################################################## - -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn () { - echo "$*" -} - -die () { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -exec "$JAVACMD" "$@" diff --git a/s3proxy/gradlew.bat b/s3proxy/gradlew.bat deleted file mode 100644 index ac1b06f93..000000000 --- a/s3proxy/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/s3proxy/script/deploy.sh b/s3proxy/script/deploy.sh deleted file mode 100644 index 7ac72174a..000000000 --- a/s3proxy/script/deploy.sh +++ /dev/null @@ -1,20 +0,0 @@ -PROFILE=$1 - -JAR_FILE_NAME=s3proxy-0.0.1-SNAPSHOT.jar - -echo "Checking currently running process id..." -RUNNING_PROCESS_ID=$(pgrep -fl java | awk '{print $1}') - -if [ -z "$RUNNING_PROCESS_ID" ]; then - echo "No s3Proxy server is running." -else - echo "Killing process whose id is $RUNNING_PROCESS_ID" - kill -15 $RUNNING_PROCESS_ID - sleep 5 -fi - -echo "Running jar file..." -nohup java -jar -Dspring.profiles.active=$PROFILE $JAR_FILE_NAME > ~/nohup.out 2>&1 & - -CURRENT_PROCESS_ID=$(pgrep -fl java | awk '{print $1}') -echo "Application is running as pid: $CURRENT_PROCESS_ID" diff --git a/s3proxy/settings.gradle b/s3proxy/settings.gradle deleted file mode 100644 index 78517cd75..000000000 --- a/s3proxy/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 's3proxy' diff --git a/s3proxy/src/docs/asciidoc/index.adoc b/s3proxy/src/docs/asciidoc/index.adoc deleted file mode 100644 index d4d7b0d2b..000000000 --- a/s3proxy/src/docs/asciidoc/index.adoc +++ /dev/null @@ -1,24 +0,0 @@ -= ZZIMKKONG S3 Proxy Server Application API Document -:doctype: book -:icons: font -:source-highlighter: highlightjs -:toc: left -:toclevels: 3 -:sectlinks: - -== Multi-Part Upload - -=== Upload -==== Request -include::{snippets}/s3/post/path-parameters.adoc[] -include::{snippets}/s3/post/http-request.adoc[] - -==== Response -include::{snippets}/s3/post/http-response.adoc[] - -=== Delete -==== Request -include::{snippets}/s3/delete/path-parameters.adoc[] -include::{snippets}/s3/delete/http-request.adoc[] -==== Response -include::{snippets}/s3/delete/http-response.adoc[] diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/S3proxyApplication.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/S3proxyApplication.java deleted file mode 100644 index 1ff565504..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/S3proxyApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.woowacourse.s3proxy; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class S3proxyApplication { - - public static void main(String[] args) { - SpringApplication.run(S3proxyApplication.class, args); - } - -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthInterceptorConfig.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthInterceptorConfig.java deleted file mode 100644 index 0c285aac0..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthInterceptorConfig.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.woowacourse.s3proxy.config; - -import com.woowacourse.s3proxy.infrastructure.AuthInterceptor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.context.annotation.PropertySource; - -@Configuration -@PropertySource("classpath:config/s3proxy.properties") -public class AuthInterceptorConfig { - @Bean(name = "authInterceptor") - @Profile("prod") - public AuthInterceptor authInterceptorProd( - @Value("${s3proxy.secret-key.prod}") String secretKey) { - return new AuthInterceptor(secretKey); - } - - @Bean(name = "authInterceptor") - @Profile({"dev", "local", "test"}) - public AuthInterceptor authInterceptorDev( - @Value("${s3proxy.secret-key.dev}") String secretKey) { - return new AuthInterceptor(secretKey); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthenticationPrincipalConfig.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthenticationPrincipalConfig.java deleted file mode 100644 index 2fbca605b..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/AuthenticationPrincipalConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.woowacourse.s3proxy.config; - -import com.woowacourse.s3proxy.infrastructure.AuthInterceptor; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class AuthenticationPrincipalConfig implements WebMvcConfigurer { - private final AuthInterceptor authInterceptor; - - public AuthenticationPrincipalConfig(AuthInterceptor authInterceptor) { - this.authInterceptor = authInterceptor; - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(authInterceptor) - .addPathPatterns("/api/storage/*"); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/MultipartConfig.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/config/MultipartConfig.java deleted file mode 100644 index da2013334..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/MultipartConfig.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.woowacourse.s3proxy.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.multipart.commons.CommonsMultipartResolver; - -@Configuration -public class MultipartConfig { - @Bean(name = "multipartResolver") - public CommonsMultipartResolver multipartResolver() { - CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(); - multipartResolver.setMaxUploadSize(1048576); // 1MB - return multipartResolver; - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/StorageConfig.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/config/StorageConfig.java deleted file mode 100644 index 6cd5f6262..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/config/StorageConfig.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.woowacourse.s3proxy.config; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; - -@Configuration -public class StorageConfig { - - @Bean - @Profile({"prod", "dev"}) - public AmazonS3 amazonS3() { - return AmazonS3ClientBuilder - .standard() - .build(); - } - - @Bean(name = "amazonS3") - @Profile({"test", "local"}) - public AmazonS3 amazonS3Local(@Value("${cloud.aws.region.static}") String region) { - - return AmazonS3ClientBuilder - .standard() - .withRegion(region) - .build(); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/ControllerAdvice.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/ControllerAdvice.java deleted file mode 100644 index 8e3c40efd..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/ControllerAdvice.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.woowacourse.s3proxy.controller; - -import com.woowacourse.s3proxy.dto.ErrorResponse; -import com.woowacourse.s3proxy.exception.S3ProxyException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -@Slf4j -@RestControllerAdvice -public class ControllerAdvice { - @ExceptionHandler(S3ProxyException.class) - public ResponseEntity s3ProxyExceptionHandler(final S3ProxyException exception) { - log.warn(exception.getMessage(), exception); - return ResponseEntity - .status(exception.getStatus()) - .body(ErrorResponse.from(exception)); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/S3ProxyController.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/S3ProxyController.java deleted file mode 100644 index d1337db4a..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/controller/S3ProxyController.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.woowacourse.s3proxy.controller; - -import com.woowacourse.s3proxy.service.S3Service; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.net.URI; - -@Slf4j -@RestController -@RequestMapping("/api/storage") -public class S3ProxyController { - private final S3Service s3Service; - - public S3ProxyController(final S3Service s3Service) { - this.s3Service = s3Service; - } - - @PostMapping("/{directoryPath}") - public ResponseEntity submit( - @RequestParam("file") MultipartFile file, - @PathVariable("directoryPath") String directoryPath) { - URI location = s3Service.upload(file, directoryPath); - return ResponseEntity.created(location).build(); - } - - @DeleteMapping("/{directoryPath}/{fileName}") - public ResponseEntity delete( - @PathVariable("directoryPath") String directoryPath, - @PathVariable("fileName") String fileName) { - s3Service.delete(directoryPath, fileName); - return ResponseEntity.noContent().build(); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/dto/ErrorResponse.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/dto/ErrorResponse.java deleted file mode 100644 index 41582fe6c..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/dto/ErrorResponse.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.woowacourse.s3proxy.dto; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -public class ErrorResponse { - private String message; - - protected ErrorResponse(final String message) { - this.message = message; - } - - public static ErrorResponse from(final RuntimeException exception) { - return new ErrorResponse(exception.getMessage()); - } - -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/AuthorizationHeaderUninvolvedException.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/AuthorizationHeaderUninvolvedException.java deleted file mode 100644 index c82f87d68..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/AuthorizationHeaderUninvolvedException.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.woowacourse.s3proxy.exception; - -import org.springframework.http.HttpStatus; - -public class AuthorizationHeaderUninvolvedException extends S3ProxyException { - private static final String MESSAGE = "인가에 실패했습니다."; - public AuthorizationHeaderUninvolvedException() { - super(MESSAGE, HttpStatus.UNAUTHORIZED); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3ProxyException.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3ProxyException.java deleted file mode 100644 index 5b783b2c6..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3ProxyException.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.woowacourse.s3proxy.exception; - -import lombok.Getter; -import org.springframework.http.HttpStatus; - -@Getter -public abstract class S3ProxyException extends RuntimeException { - protected final HttpStatus status; - - public S3ProxyException(String message, HttpStatus status) { - super(message); - this.status = status; - } - - public S3ProxyException(String message, Throwable cause, HttpStatus status) { - super(message, cause); - this.status = status; - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3UploadException.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3UploadException.java deleted file mode 100644 index 6084d6379..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/S3UploadException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.woowacourse.s3proxy.exception; - -import org.springframework.http.HttpStatus; - -public class S3UploadException extends S3ProxyException { - private static final String MESSAGE = "이미지 버킷 업로드에 실패했습니다."; - - public S3UploadException(final Throwable cause) { - super(MESSAGE, cause, HttpStatus.INTERNAL_SERVER_ERROR); - } -} - diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/UnsupportedFileExtensionException.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/UnsupportedFileExtensionException.java deleted file mode 100644 index d8176c0d0..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/exception/UnsupportedFileExtensionException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.woowacourse.s3proxy.exception; - -import org.springframework.http.HttpStatus; - -public class UnsupportedFileExtensionException extends S3ProxyException { - private static final String MESSAGE = "지원하지 않는 확장자입니다."; - - public UnsupportedFileExtensionException() { - super(MESSAGE, HttpStatus.BAD_REQUEST); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthInterceptor.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthInterceptor.java deleted file mode 100644 index 39f9157f5..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthInterceptor.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.woowacourse.s3proxy.infrastructure; - -import com.woowacourse.s3proxy.exception.AuthorizationHeaderUninvolvedException; -import org.springframework.http.HttpMethod; -import org.springframework.web.servlet.HandlerInterceptor; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - - -public class AuthInterceptor implements HandlerInterceptor { - private final String secretKey; - - public AuthInterceptor(String secretKey) { - this.secretKey = secretKey; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { - if (isPreflight(request)) { - return true; - } - - String secretKey = AuthorizationExtractor.extractAccessToken(request); - if (secretKey.equals(this.secretKey)) { - return true; - } - - throw new AuthorizationHeaderUninvolvedException(); - } - - private boolean isPreflight(HttpServletRequest request) { - return request.getMethod().equals(HttpMethod.OPTIONS.toString()); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthorizationExtractor.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthorizationExtractor.java deleted file mode 100644 index eb2abdc7e..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/AuthorizationExtractor.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.woowacourse.s3proxy.infrastructure; - -import com.woowacourse.s3proxy.exception.AuthorizationHeaderUninvolvedException; - -import javax.servlet.http.HttpServletRequest; -import java.util.Enumeration; - -public class AuthorizationExtractor { - private static final String AUTHORIZATION_HEADER_KEY = "Authorization"; - - private AuthorizationExtractor() { - } - public static String extractAccessToken(HttpServletRequest request) { - Enumeration headers = request.getHeaders(AUTHORIZATION_HEADER_KEY); - if (headers.hasMoreElements()) { - return headers.nextElement(); - } - throw new AuthorizationHeaderUninvolvedException(); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/S3Uploader.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/S3Uploader.java deleted file mode 100644 index cb729b248..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/infrastructure/S3Uploader.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.woowacourse.s3proxy.infrastructure; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.DeleteObjectRequest; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.woowacourse.s3proxy.exception.S3UploadException; -import com.woowacourse.s3proxy.exception.UnsupportedFileExtensionException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.MediaType; -import org.springframework.http.MediaTypeFactory; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; - -@Component -public class S3Uploader { - public static final String PATH_DELIMITER = "/"; - private static final String S3_HOST_URL_SUFFIX = "amazonaws.com"; - private static final int RESOURCE_URL_INDEX = 1; - - private final AmazonS3 amazonS3; - private final String bucketName; - private final String cloudFrontUrl; - - public S3Uploader( - final AmazonS3 amazonS3, - @Value("${aws.s3.bucket-name}") final String bucketName, - @Value("${aws.s3.mapped-cloudfront}") final String cloudFrontUrl) { - this.amazonS3 = amazonS3; - this.bucketName = bucketName; - this.cloudFrontUrl = cloudFrontUrl; - } - - public URI upload(MultipartFile multipartFile, String directoryPath) { - ObjectMetadata objectMetadata = createObjectMetadata(multipartFile); - - String fileName = multipartFile.getOriginalFilename(); - String fileFullPath = generateFullPath(directoryPath, fileName); - - try(InputStream inputStream = multipartFile.getInputStream()) { - - amazonS3.putObject(this.bucketName, fileFullPath, inputStream, objectMetadata); - - URL fileUrl = amazonS3.getUrl(this.bucketName, fileFullPath); - - return makeAccessibleUrl(fileUrl, cloudFrontUrl); - } catch (AmazonClientException | IOException exception) { - throw new S3UploadException(exception); - } - } - - private ObjectMetadata createObjectMetadata(MultipartFile multipartFile) { - String filename = multipartFile.getOriginalFilename(); - MediaType mediaType = MediaTypeFactory.getMediaType(filename) - .orElseThrow(UnsupportedFileExtensionException::new); - - ObjectMetadata objectMetadata = new ObjectMetadata(); - objectMetadata.setContentType(mediaType.toString()); - objectMetadata.setContentLength(multipartFile.getSize()); - - return objectMetadata; - } - - private String generateFullPath(String directoryPath, String fileName) { - return directoryPath + PATH_DELIMITER + fileName; - } - - private URI makeAccessibleUrl(final URL origin, final String cloudFrontUrl) { - String uriWithoutHost = origin.toString().split(S3_HOST_URL_SUFFIX)[RESOURCE_URL_INDEX]; - String replacedUrl = cloudFrontUrl + uriWithoutHost; - return URI.create(replacedUrl); - } - - public void delete(final String fullPathOfFile) { - amazonS3.deleteObject(new DeleteObjectRequest(bucketName, fullPathOfFile)); - } -} diff --git a/s3proxy/src/main/java/com/woowacourse/s3proxy/service/S3Service.java b/s3proxy/src/main/java/com/woowacourse/s3proxy/service/S3Service.java deleted file mode 100644 index ab41ed023..000000000 --- a/s3proxy/src/main/java/com/woowacourse/s3proxy/service/S3Service.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.woowacourse.s3proxy.service; - -import com.woowacourse.s3proxy.infrastructure.S3Uploader; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.web.multipart.MultipartFile; - -import java.net.URI; - -@Slf4j -@Service -public class S3Service { - private final S3Uploader s3Uploader; - - public S3Service(final S3Uploader s3Uploader) { - this.s3Uploader = s3Uploader; - } - - public URI upload(MultipartFile multipartFile, String directoryPath) { - return s3Uploader.upload(multipartFile, directoryPath); - } - - public void delete(String directoryPath, String fileName) { - s3Uploader.delete(directoryPath + S3Uploader.PATH_DELIMITER + fileName); - } -} diff --git a/s3proxy/src/main/resources/appenders/console-appender.xml b/s3proxy/src/main/resources/appenders/console-appender.xml deleted file mode 100644 index a9c4fb53c..000000000 --- a/s3proxy/src/main/resources/appenders/console-appender.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - ${CONSOLE_LOG_PATTERN} - - - - diff --git a/s3proxy/src/main/resources/appenders/file-appender-debug.xml b/s3proxy/src/main/resources/appenders/file-appender-debug.xml deleted file mode 100644 index d46254437..000000000 --- a/s3proxy/src/main/resources/appenders/file-appender-debug.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ${FILE_PATH}/${DATE_FORMAT}/debug/debug.log - - - DEBUG - ACCEPT - DENY - - - - ${FILE_LOG_PATTERN} - - - - ${FILE_PATH}/%d{yyyy-MM-dd}/debug/debug_%i.log - 100MB - 100 - 10GB - - - - diff --git a/s3proxy/src/main/resources/appenders/file-appender-error.xml b/s3proxy/src/main/resources/appenders/file-appender-error.xml deleted file mode 100644 index 1c6d064dc..000000000 --- a/s3proxy/src/main/resources/appenders/file-appender-error.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ${FILE_PATH}/${DATE_FORMAT}/error/error.log - - - ERROR - ACCEPT - DENY - - - - ${FILE_LOG_PATTERN} - - - - ${FILE_PATH}/%d{yyyy-MM-dd}/error/error_%i.log - 100MB - 100 - 10GB - - - - diff --git a/s3proxy/src/main/resources/appenders/file-appender-info.xml b/s3proxy/src/main/resources/appenders/file-appender-info.xml deleted file mode 100644 index abd5116ff..000000000 --- a/s3proxy/src/main/resources/appenders/file-appender-info.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ${FILE_PATH}/${DATE_FORMAT}/info/info.log - - - INFO - ACCEPT - DENY - - - - ${FILE_LOG_PATTERN} - - - - ${FILE_PATH}/%d{yyyy-MM-dd}/info/info_%i.log - 100MB - 100 - 10GB - - - - diff --git a/s3proxy/src/main/resources/appenders/file-appender-trace.xml b/s3proxy/src/main/resources/appenders/file-appender-trace.xml deleted file mode 100644 index 6f24e2375..000000000 --- a/s3proxy/src/main/resources/appenders/file-appender-trace.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ${FILE_PATH}/${DATE_FORMAT}/trace/trace.log - - - TRACE - ACCEPT - DENY - - - - ${FILE_LOG_PATTERN} - - - - ${FILE_PATH}/%d{yyyy-MM-dd}/trace/trace_%i.log - 100MB - 100 - 10GB - - - - diff --git a/s3proxy/src/main/resources/appenders/file-appender-warn.xml b/s3proxy/src/main/resources/appenders/file-appender-warn.xml deleted file mode 100644 index 9908dd63d..000000000 --- a/s3proxy/src/main/resources/appenders/file-appender-warn.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - ${FILE_PATH}/${DATE_FORMAT}/warn/warn.log - - - WARN - ACCEPT - DENY - - - - ${FILE_LOG_PATTERN} - - - - ${FILE_PATH}/%d{yyyy-MM-dd}/warn/warn_%i.log - 100MB - 100 - 10GB - - - - diff --git a/s3proxy/src/main/resources/appenders/kafka-appender-dev.xml b/s3proxy/src/main/resources/appenders/kafka-appender-dev.xml deleted file mode 100644 index 1f771dfd9..000000000 --- a/s3proxy/src/main/resources/appenders/kafka-appender-dev.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - logstash-s3proxy - - - - bootstrap.servers=192.168.2.139:8080,192.168.2.44:8080,192.168.2.24:8080 - - diff --git a/s3proxy/src/main/resources/appenders/kafka-appender-prod.xml b/s3proxy/src/main/resources/appenders/kafka-appender-prod.xml deleted file mode 100644 index 5a139a88a..000000000 --- a/s3proxy/src/main/resources/appenders/kafka-appender-prod.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - logstash-s3proxy - - - - bootstrap.servers=192.168.2.242:8080,192.168.2.150:8080,192.168.2.152:8080 - - diff --git a/s3proxy/src/main/resources/application-dev.yml b/s3proxy/src/main/resources/application-dev.yml deleted file mode 100644 index 4d1ee6e7c..000000000 --- a/s3proxy/src/main/resources/application-dev.yml +++ /dev/null @@ -1,9 +0,0 @@ -cloud: - aws: - stack: - auto: false -aws: - s3: - bucket-name: zzimkkong-thumbnail-dev - mapped-cloudfront: https://d3tdpsdxqmqd52.cloudfront.net - region: ap-northeast-2 diff --git a/s3proxy/src/main/resources/application-local.yml b/s3proxy/src/main/resources/application-local.yml deleted file mode 100644 index bbbbb87dc..000000000 --- a/s3proxy/src/main/resources/application-local.yml +++ /dev/null @@ -1,14 +0,0 @@ -cloud: - aws: - stack: - auto: false - credentials: - instance-profile: false - region: - static: ap-northeast-2 - -aws: - s3: - bucket-name: zzimkkong-personal - mapped-cloudfront: https://zzimkkong-personal.s3.ap-northeast-2.amazonaws.com - region: ap-northeast-2 diff --git a/s3proxy/src/main/resources/application-test.yml b/s3proxy/src/main/resources/application-test.yml deleted file mode 100644 index bbbbb87dc..000000000 --- a/s3proxy/src/main/resources/application-test.yml +++ /dev/null @@ -1,14 +0,0 @@ -cloud: - aws: - stack: - auto: false - credentials: - instance-profile: false - region: - static: ap-northeast-2 - -aws: - s3: - bucket-name: zzimkkong-personal - mapped-cloudfront: https://zzimkkong-personal.s3.ap-northeast-2.amazonaws.com - region: ap-northeast-2 diff --git a/s3proxy/src/main/resources/application.yml b/s3proxy/src/main/resources/application.yml deleted file mode 100644 index d74c444c1..000000000 --- a/s3proxy/src/main/resources/application.yml +++ /dev/null @@ -1,3 +0,0 @@ -spring: - profiles: - active: local diff --git a/s3proxy/src/main/resources/config b/s3proxy/src/main/resources/config deleted file mode 160000 index a2d168ac3..000000000 --- a/s3proxy/src/main/resources/config +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a2d168ac3adfb324408ad8afa2bd3e4d3ac35220 diff --git a/s3proxy/src/main/resources/logback-spring.xml b/s3proxy/src/main/resources/logback-spring.xml deleted file mode 100644 index a23731343..000000000 --- a/s3proxy/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/s3proxy/src/main/resources/s3proxy-config b/s3proxy/src/main/resources/s3proxy-config deleted file mode 160000 index e33c8a691..000000000 --- a/s3proxy/src/main/resources/s3proxy-config +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e33c8a691c32dfdc9e8f5ce4b33c753363b8a054 diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/Constants.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/Constants.java deleted file mode 100644 index 439c4ae2e..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/Constants.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.woowacourse.s3proxy; - -public class Constants { - public static final String LUTHER_IMAGE_URI_CLOUDFRONT = "https://d3tdpsdxqmqd52.cloudfront.net/testdir/luther.png"; - public static final String LUTHER_IMAGE_URI_S3 = "https://zzimkkong-thumbnail-dev.s3.ap-northeast-2.amazonaws.com/testdir/luther.png"; -} diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/DocumentUtils.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/DocumentUtils.java deleted file mode 100644 index 4503786f0..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/DocumentUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.woowacourse.s3proxy; - -import io.restassured.specification.RequestSpecification; -import org.springframework.restdocs.operation.preprocess.OperationRequestPreprocessor; -import org.springframework.restdocs.operation.preprocess.OperationResponsePreprocessor; - -import static org.springframework.restdocs.operation.preprocess.Preprocessors.*; -import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; - -public final class DocumentUtils { - private static RequestSpecification preConfiguredRequestSpecification; - - private DocumentUtils() { - } - - public static RequestSpecification getRequestSpecification() { - return preConfiguredRequestSpecification; - } - - public static void setRequestSpecification(RequestSpecification preConfiguredRequestSpecification) { - DocumentUtils.preConfiguredRequestSpecification = preConfiguredRequestSpecification; - } - - public static OperationRequestPreprocessor getRequestPreprocessor() { - return preprocessRequest(prettyPrint()); - } - - public static OperationResponsePreprocessor getResponsePreprocessor() { - return preprocessResponse(prettyPrint()); - } -} diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/AcceptanceTest.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/AcceptanceTest.java deleted file mode 100644 index d927f021f..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/AcceptanceTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.woowacourse.s3proxy.controller; - -import io.restassured.RestAssured; -import io.restassured.builder.RequestSpecBuilder; -import io.restassured.specification.RequestSpecification; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.restdocs.RestDocumentationContextProvider; -import org.springframework.restdocs.RestDocumentationExtension; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static com.woowacourse.s3proxy.DocumentUtils.setRequestSpecification; -import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.documentationConfiguration; - -@ExtendWith({RestDocumentationExtension.class, SpringExtension.class}) -@AutoConfigureRestDocs -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -public class AcceptanceTest { - @LocalServerPort - int port; - - @BeforeEach - void setUp(RestDocumentationContextProvider restDocumentation) { - RestAssured.port = this.port; - RequestSpecification spec = new RequestSpecBuilder() - .addFilter(documentationConfiguration(restDocumentation)) - .build(); - setRequestSpecification(spec); - } -} - - diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/S3ProxyControllerTest.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/S3ProxyControllerTest.java deleted file mode 100644 index eccc5aa55..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/controller/S3ProxyControllerTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.woowacourse.s3proxy.controller; - -import com.woowacourse.s3proxy.infrastructure.S3Uploader; -import io.restassured.RestAssured; -import io.restassured.response.ExtractableResponse; -import io.restassured.response.Response; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.PropertySource; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.net.URI; - -import static com.woowacourse.s3proxy.Constants.LUTHER_IMAGE_URI_CLOUDFRONT; -import static com.woowacourse.s3proxy.DocumentUtils.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; -import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; -import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document; - -@PropertySource("classpath:config/s3proxy.properties") -class S3ProxyControllerTest extends AcceptanceTest { - @MockBean - S3Uploader s3Uploader; - - @Value("${s3proxy.secret-key.prod}") - private String secretKey; - - @BeforeEach - void setUp() { - given(s3Uploader.upload(any(MultipartFile.class), anyString())) - .willReturn(URI.create(LUTHER_IMAGE_URI_CLOUDFRONT)); - } - - @Test - @DisplayName("스토리지에 파일을 업로드한다.") - void upload() { - // given - String directory = "testdir"; - String filePath = getClass().getClassLoader().getResource("luther.png").getFile(); - File file = new File(filePath); - - // when - ExtractableResponse response = uploadFile(directory, file); - - // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); - } - - @Test - @DisplayName("스토리지의 파일을 삭제한다.") - void delete() { - // given - String fileName = "filename.png"; - String directory = "testdir"; - - // when - ExtractableResponse response = deleteFile(directory, fileName); - - // then - assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); - } - - private ExtractableResponse uploadFile(String directory, File file) { - return RestAssured.given(getRequestSpecification()) - .log().all() - .filter(document( - "s3/post", getRequestPreprocessor(), getResponsePreprocessor(), - pathParameters(parameterWithName("directory").description("저장하고자 하는 스토리지 내의 디렉토리 이름")))) - .contentType(MediaType.MULTIPART_FORM_DATA_VALUE) - .multiPart("file", file) - .header(HttpHeaders.AUTHORIZATION, secretKey) - .pathParam("directory", directory) - .when().post("/api/storage/{directory}") - .then().log().all().extract(); - } - - private ExtractableResponse deleteFile(String directory, String fileName) { - return RestAssured.given(getRequestSpecification()) - .log().all() - .filter(document("s3/delete", getRequestPreprocessor(), getResponsePreprocessor(), - pathParameters( - parameterWithName("directory").description("저장하고자 하는 스토리지 내의 디렉토리 이름"), - parameterWithName("filename").description("삭제하고자 하는 파일의 이름(확장자 포함)")))) - .when() - .header(HttpHeaders.AUTHORIZATION, secretKey) - .pathParam("directory", directory) - .pathParam("filename", fileName) - .delete("/api/storage/{directory}/{filename}") - .then().log().all().extract(); - } -} diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/infrastructure/S3UploaderTest.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/infrastructure/S3UploaderTest.java deleted file mode 100644 index 7bf98f5a2..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/infrastructure/S3UploaderTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.woowacourse.s3proxy.infrastructure; - -import com.amazonaws.services.s3.AmazonS3; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.web.multipart.MultipartFile; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URL; -import java.util.Random; - -import static com.woowacourse.s3proxy.Constants.LUTHER_IMAGE_URI_S3; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -class S3UploaderTest { - - @Test - @DisplayName("멀티 파트 파일을 업로드하고 URI를 얻어, 접근 가능한 URI(CloudFront)로 변경해 리턴한다.") - void upload() throws IOException { - // given - AmazonS3 amazonS3 = mock(AmazonS3.class); - String bucketName = "testBucketName"; - String cloudFrontUrl = "https://expectedCloudFrontUrl.net"; - - S3Uploader s3Uploader = new S3Uploader(amazonS3, bucketName, cloudFrontUrl); - - given(amazonS3.getUrl(anyString(), anyString())) - .willReturn(new URL(LUTHER_IMAGE_URI_S3)); - - MultipartFile mockMultipartFile = mock(MultipartFile.class); - given(mockMultipartFile.getOriginalFilename()) - .willReturn("somePngFileName.png"); - given(mockMultipartFile.getSize()) - .willReturn(new Random().nextLong()); - given(mockMultipartFile.getInputStream()) - .willReturn(mock(InputStream.class)); - - // when - String directoryName = "testDirectoryName"; - URI actual = s3Uploader.upload(mockMultipartFile, directoryName); - - String resourceUriWithoutHost = LUTHER_IMAGE_URI_S3.split("amazonaws.com")[1]; - String expectedUri = cloudFrontUrl + resourceUriWithoutHost; - - // then - assertThat(actual).isEqualTo(URI.create(expectedUri)); - } - - @Test - @DisplayName("경로를 입력받아 파일을 삭제할 수 있다.") - void delete() { - // given - AmazonS3 amazonS3 = mock(AmazonS3.class); - String bucketName = "testBucketName"; - String cloudFrontUrl = "https://testCloudFrontUrl.net"; - - S3Uploader s3Uploader = new S3Uploader(amazonS3, bucketName, cloudFrontUrl); - - String fileName = "filename.png"; - String directory = "directoryName"; - - // when, then - assertDoesNotThrow(() -> s3Uploader.delete(directory + "/" + fileName)); - } -} diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/service/S3ServiceTest.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/service/S3ServiceTest.java deleted file mode 100644 index 7e5c8de29..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/service/S3ServiceTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.woowacourse.s3proxy.service; - -import com.woowacourse.s3proxy.infrastructure.S3Uploader; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.web.multipart.MultipartFile; - -import java.net.URI; - -import static com.woowacourse.s3proxy.Constants.LUTHER_IMAGE_URI_CLOUDFRONT; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -class S3ServiceTest extends ServiceTest { - @MockBean - private S3Uploader s3Uploader; - - @Autowired - private S3Service s3Service; - - @BeforeEach - void mockingS3Uploader() { - given(s3Uploader.upload(any(MultipartFile.class), anyString())) - .willReturn(URI.create(LUTHER_IMAGE_URI_CLOUDFRONT)); - } - - @Test - @DisplayName("멀티파트로 전송된 파일을 요청한 디렉토리에 업로드한다.") - void upload() { - // given - MultipartFile multipartFile = mock(MultipartFile.class); - - // when - URI actual = s3Service.upload(multipartFile, "thumbnails"); - - // then - assertThat(actual).isEqualTo(URI.create(LUTHER_IMAGE_URI_CLOUDFRONT)); - } - - @Test - @DisplayName("스토리지의 파일을 삭제할 수 있다.") - void delete() { - // given - String fileName = "filename.png"; - String directory = "directoryName"; - - // when, then - assertDoesNotThrow(() -> s3Service.delete(directory, fileName)); - } -} diff --git a/s3proxy/src/test/java/com/woowacourse/s3proxy/service/ServiceTest.java b/s3proxy/src/test/java/com/woowacourse/s3proxy/service/ServiceTest.java deleted file mode 100644 index e6bbd09a1..000000000 --- a/s3proxy/src/test/java/com/woowacourse/s3proxy/service/ServiceTest.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.woowacourse.s3proxy.service; - -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; - -@SpringBootTest -@ActiveProfiles("test") -public class ServiceTest { -} diff --git a/s3proxy/src/test/resources/luther.png b/s3proxy/src/test/resources/luther.png deleted file mode 100644 index c2eac157e85568ff61369e67cc78637ad9cf481b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4577 zcmds52~?9;7Jgw94GJC+a1_}bYz9#f1VNEN6+}b~R*_;+S?YjDSO+QuG9?azC^$&j zjTR#sEsG)nAq1%!Tv(+bK@1~Q0TCi$kwj|W`|(U@iWNJZGwmcN$$kI-a^HR5_uYG6 z62E<${oFZ!ngals>#%vF3jneu0LXYMD!?m3U5|zEL)&Y!ixU8c^#P#L01&}T)E)rB zi~#7{2Y_Wd0P2C+*GcQ)4Y>n0PBs7(orn^XQs5XD;$m+L^6QrL!hwRPqy0wk20hQP z^U&?tft%ez0GM|Zy>O`p^A-azf33qtn@=KmFFKr$sy)-J3gT)WX&BiaYlWA0OjG?< ze7Sr3bqYV$$2n4!J=~CuXE12GH^+|H3K|>-tQLM95%of6*(`qlMbFbVZK2AxRpmER zGy-#-pULCT69z7w7_2i9j_7(|g-JM8a1>>*&yq z`p8Zv%F6aAN}QE01S(kH9|wR66bjHr7V)9XREt_D4;4a5wF5-@5tV@EtbfUr*Lf!D zEszEK@!*ij<=AVxwW%p29C4jnf)bpH=A&BxRIB~Oq8Y-WqC0#;)*=lhg|w|Ww66E1 zj}nRqlhY-Wu!ykfzZ@I3)_Yd%FEEt>ole&|Y*Ah8I{SL7FG`03c5wH&QG&&!r7VG#0PSBwWe4cl5P!XeDU?~QrDv$6IgVh6%;HXFfs0RyKs zcXYfohfh`?U@)2SEOjXkFuXCPNFh(nCxn#NsMro&fIQcj`CD84vt&Kf^=g^xRwpQ~ zp4E;g->Wu^Q6dXU4m4C;DqsA-hi*bMI%a%gs}>5QqC|MG-JNpOmugn1y6EQ;!({j2 zI+|uTXYyROce5{fo4)kSBv`%XXFo~^V$UzBs$Q{40sMObOeVj)8nsg88W`T`D+iJ} zsoyPUCDEg!oSJ2@Elu=~DQ_^92co+!-ft6oGQ}~yOqab4hkWL#mq9s*hlnm)x|mFT zCrz@EMq*Y)r6?jT;cE?XPayGgJ0N(%*F)vOaG}?V(e~xEFY9uO3I%5 zvd>I8t|>0pT~%-0kzG5o;`Z911*p?C;p%mnMTHc#JFcSDoLaUguluEFEyvR6m=E2Y zmK-I*0p7itE1NODn?MW9eXOMZFig#BNrnc}qi@!J&C9oBaM_8lVSi}-%Q3?1Eb|g9 zji9&tgcPJ7Hsq{gnC3J6nwnoK#NK#ylf!&Icj+J$kC*cn*tZ?gsxprch%VgO96b=R zAZ4PmB&KbWYPSWb#Y?3mCU%TwNv24wDU?bYI;IhcZk8NvQoOPTuXAqGsL5Y2Of8LX zpCrJgc7Mom;5~t9`yYoMr^Mso;Vbur<;W#ICAhya2R|!l0J7INJDqPcTj$({99HdQ zZGp$1*c+V)pOnAG>0RQ6E5xv)8<$b@OUL8(&T-ESAIpp3xRCSH<=6?loDtu|BBOXM zJ64&X-|-*uV+B@p#~onz(wX)w={%5v3d8K=p@>nq22-^MA^%fp_xaE0HL$|Gbf>|Uq~ zIa%aeSf{92?f1s<5o#S&|J+@ZM*siJAg((F50KmXA}V&1qg}`cbZF`?EMZ5_v?NpV z7ms}Y>dWVce-Kp)&Y|OH)tjB%KC+rpW3%95+(;ST82}!4rdcQ@W=t~Dyhw?8dR~5A z>6?ynEXhJq0}XA4H8FGj>5~{QjCgpY!2!{Y-__oTi%;1l=W)M(nNxUm!=a%FdrqVP zp8^l{G(*QCp|E~srVQmlcbWwNp>d3w|3*o9TcfOk`ks%iN{7EjsD-sHMfTwcTZ~Gn z3MEbNJ2{XlewZ66y-2y9>}NC3%M4s&QY6NuTI}&w)LB!e1);0{+eLJ&X^9qJrnA?5 z=FQ1Z=NGi915ovwO5z~;O)M4Re_r_|4keZZO^GF;M`FzwSIo^jW393=;6?vkhXyN1 z4+%$LL~bHwo5pLU-_o$ZO^wN$sE`qe(SIL*xJS;YR!L=X$oSpIPf_d+o3?Gtx83*U Ek8>jU^Z)<= From adc91d40ea0c617813811ac954f6b3f87c557687 Mon Sep 17 00:00:00 2001 From: xrabcde Date: Tue, 25 Jan 2022 01:06:55 +0900 Subject: [PATCH 11/24] =?UTF-8?q?feat:=20=EB=A7=B5=EB=B3=84=EB=A1=9C=20?= =?UTF-8?q?=EC=98=88=EC=95=BD=EC=95=8C=EB=A6=BC=EC=9D=84=20=EB=B0=9B?= =?UTF-8?q?=EC=9D=84=20=EC=8A=AC=EB=9E=99=20=EC=B1=84=EB=84=90=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D,=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#766)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 맵별 알림받을 슬랙 url추가, 조회 기능 구현 * feat: map 테이블에 slack_url 칼럼 추가 sql문 작성 * refactor: slack 전송 시 config가 아닌 map의 slackUrl 사용하도록 수정 * test: 슬랙 조회, 입력 관련 controller test 작성 * docs: 슬랙 조회, 입력 관련 api 문서 작성 * refactor: SlackUrl 사용하던 WarmUp 코드 제거 * feat: 서브모듈 업데이트 * feat: 서브모듈 업데이트 * feat: 서브모듈 업데이트(사이렌 웹훅 추가) --- backend/src/docs/asciidoc/map.adoc | 12 +++++ .../zzimkkong/config/SlackConfig.java | 34 ------------- .../zzimkkong/config/warmup/WarmerConfig.java | 0 .../GuestReservationController.java | 2 +- .../ManagerReservationController.java | 2 +- .../zzimkkong/controller/MapController.java | 23 +++++++-- .../com/woowacourse/zzimkkong/domain/Map.java | 7 +++ .../zzimkkong/domain/SlackUrl.java | 14 ----- .../zzimkkong/dto/map/SlackCreateRequest.java | 14 +++++ .../zzimkkong/dto/map/SlackFindResponse.java | 19 +++++++ .../ReservationCreateResponse.java | 4 +- .../zzimkkong/dto/slack/SlackResponse.java | 10 ++-- .../infrastructure/warmup/Warmer.java | 0 .../zzimkkong/service/MapService.java | 22 ++++++-- .../zzimkkong/service/ReservationService.java | 6 +-- .../zzimkkong/service/SlackService.java | 50 ++++++++++++++++++ .../zzimkkong/service/slack/LutherSender.java | 24 --------- .../zzimkkong/service/slack/Sender.java | 31 ----------- .../zzimkkong/service/slack/SlackService.java | 48 ----------------- .../service/slack/StandardSender.java | 22 -------- backend/src/main/resources/config | 2 +- .../prod/V14__map_add_column_slack.sql | 1 + .../controller/AdminControllerTest.java | 2 +- .../GuestReservationControllerTest.java | 2 +- .../ManagerReservationControllerTest.java | 2 +- .../controller/MapControllerTest.java | 51 +++++++++++++++++-- 26 files changed, 205 insertions(+), 199 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/SlackConfig.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/domain/SlackUrl.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackCreateRequest.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackFindResponse.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java create mode 100644 backend/src/main/resources/db/migration/prod/V14__map_add_column_slack.sql diff --git a/backend/src/docs/asciidoc/map.adoc b/backend/src/docs/asciidoc/map.adoc index 42861b9a0..e4b7db0fd 100644 --- a/backend/src/docs/asciidoc/map.adoc +++ b/backend/src/docs/asciidoc/map.adoc @@ -35,3 +35,15 @@ include::{snippets}/map/delete/http-response.adoc[] include::{snippets}/map/getBySharingId/http-request.adoc[] ==== Response include::{snippets}/map/getBySharingId/http-response.adoc[] + +=== 맵별 슬랙알림 url 등록 +==== Request +include::{snippets}/map/slackPost/http-request.adoc[] +==== Response +include::{snippets}/map/slackPost/http-response.adoc[] + +=== 맵별 슬랙알림 url 조회 +==== Request +include::{snippets}/map/slackGet/http-request.adoc[] +==== Response +include::{snippets}/map/slackGet/http-response.adoc[] \ No newline at end of file diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/SlackConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/SlackConfig.java deleted file mode 100644 index 82317f2bc..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/config/SlackConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.woowacourse.zzimkkong.config; - -import com.woowacourse.zzimkkong.domain.SlackUrl; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.context.annotation.PropertySource; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -@PropertySource("classpath:config/slack.properties") -public class SlackConfig implements WebMvcConfigurer { - @Bean - @Profile("prod") - public SlackUrl slackUrlProd( - @Value("${slack.webhook.prod}") final String prodUrl) { - return new SlackUrl(prodUrl); - } - - @Bean - @Profile({"local", "dev"}) - public SlackUrl slackUrlDev( - @Value("${slack.webhook.local}") final String devUrl) { - return new SlackUrl(devUrl); - } - - @Bean - @Profile("test") - public SlackUrl slackUrlTest( - @Value("${slack.webhook.test}") final String testUrl) { - return new SlackUrl(testUrl); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java new file mode 100644 index 000000000..e69de29bb diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java index 38ff307d5..d9364382a 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/GuestReservationController.java @@ -4,7 +4,7 @@ import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.dto.slack.SlackResponse; import com.woowacourse.zzimkkong.service.ReservationService; -import com.woowacourse.zzimkkong.service.slack.SlackService; +import com.woowacourse.zzimkkong.service.SlackService; import com.woowacourse.zzimkkong.service.strategy.GuestReservationStrategy; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java index 1c78d68f6..1e5031767 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ManagerReservationController.java @@ -6,7 +6,7 @@ import com.woowacourse.zzimkkong.dto.slack.SlackResponse; import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.service.ReservationService; -import com.woowacourse.zzimkkong.service.slack.SlackService; +import com.woowacourse.zzimkkong.service.SlackService; import com.woowacourse.zzimkkong.service.strategy.ManagerReservationStrategy; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.http.ResponseEntity; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/MapController.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/MapController.java index 4beb75e0e..df72c7f9f 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/MapController.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/MapController.java @@ -2,11 +2,7 @@ import com.woowacourse.zzimkkong.domain.LoginEmail; import com.woowacourse.zzimkkong.config.logaspect.LogMethodExecutionTime; -import com.woowacourse.zzimkkong.domain.Member; -import com.woowacourse.zzimkkong.dto.map.MapCreateResponse; -import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; -import com.woowacourse.zzimkkong.dto.map.MapFindAllResponse; -import com.woowacourse.zzimkkong.dto.map.MapFindResponse; +import com.woowacourse.zzimkkong.dto.map.*; import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.service.MapService; import org.springframework.http.ResponseEntity; @@ -58,5 +54,22 @@ public ResponseEntity delete(@PathVariable final Long mapId, @LoginEmail f mapService.deleteMap(mapId, loginEmailDto); return ResponseEntity.noContent().build(); } + + @PostMapping("/{mapId}/slack") + public ResponseEntity createSlackUrl( + @PathVariable final Long mapId, + @RequestBody final SlackCreateRequest slackCreateRequest, + @LoginEmail final LoginEmailDto loginEmailDto) { + mapService.saveSlackUrl(mapId, slackCreateRequest, loginEmailDto); + return ResponseEntity.ok().build(); + } + + @GetMapping("/{mapId}/slack") + public ResponseEntity findSlackUrl( + @PathVariable final Long mapId, + @LoginEmail final LoginEmailDto loginEmailDto) { + SlackFindResponse slackFindResponse = mapService.findSlackUrl(mapId, loginEmailDto); + return ResponseEntity.ok().body(slackFindResponse); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java index ca0a58000..9e269aeeb 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Map.java @@ -28,6 +28,9 @@ public class Map { @Lob private String thumbnail; + @Lob + private String slackUrl; + @ManyToOne @JoinColumn(name = "member_id", foreignKey = @ForeignKey(name = "fk_map_member"), nullable = false) private Member member; @@ -75,6 +78,10 @@ public void updateThumbnail(final String thumbnail) { this.thumbnail = thumbnail; } + public void updateSlackUrl(final String slackUrl) { + this.slackUrl = slackUrl; + } + public void addSpace(final Space space) { spaces.add(space); } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/SlackUrl.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/SlackUrl.java deleted file mode 100644 index 96ec9caa9..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/SlackUrl.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.woowacourse.zzimkkong.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; - -@Getter -@NoArgsConstructor -public class SlackUrl { - private String url; - - public SlackUrl(final String url) { - this.url = url; - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackCreateRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackCreateRequest.java new file mode 100644 index 000000000..2985a4a17 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackCreateRequest.java @@ -0,0 +1,14 @@ +package com.woowacourse.zzimkkong.dto.map; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class SlackCreateRequest { + private String slackUrl; + + public SlackCreateRequest(final String slackUrl) { + this.slackUrl = slackUrl; + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackFindResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackFindResponse.java new file mode 100644 index 000000000..a3ff4f4ee --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/map/SlackFindResponse.java @@ -0,0 +1,19 @@ +package com.woowacourse.zzimkkong.dto.map; + +import com.woowacourse.zzimkkong.domain.Map; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class SlackFindResponse { + private String slackUrl; + + private SlackFindResponse(final String slackUrl) { + this.slackUrl = slackUrl; + } + + public static SlackFindResponse from(Map map) { + return new SlackFindResponse(map.getSlackUrl()); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateResponse.java index 130457fac..56e10f137 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateResponse.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateResponse.java @@ -16,8 +16,8 @@ private ReservationCreateResponse(final Long id, final SlackResponse slackRespon this.slackResponse = slackResponse; } - public static ReservationCreateResponse of(final Reservation reservation, final String sharingMapId) { - SlackResponse slackResponse = SlackResponse.of(reservation, sharingMapId); + public static ReservationCreateResponse of(final Reservation reservation, final String sharingMapId, final String slackUrl) { + SlackResponse slackResponse = SlackResponse.of(reservation, sharingMapId, slackUrl); return new ReservationCreateResponse(reservation.getId(), slackResponse); } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java index e2d0609ed..a88d4229c 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java @@ -14,6 +14,7 @@ public class SlackResponse { private String reservationTime; private String description; private String sharingMapId; + private String slackUrl; private SlackResponse( final String spaceName, @@ -21,22 +22,25 @@ private SlackResponse( final LocalDateTime startTime, final LocalDateTime endTime, final String description, - final String sharingMapId) { + final String sharingMapId, + final String slackUrl) { this.spaceName = "회의실명 : " + spaceName; this.userName = "예약자명 : " + userName; this.reservationTime = "예약시간 : " + startTime + " ~ " + endTime; this.description = "예약내용 : " + description; this.sharingMapId = sharingMapId; + this.slackUrl = slackUrl; } - public static SlackResponse of(final Reservation reservation, final String sharingMapId) { + public static SlackResponse of(final Reservation reservation, final String sharingMapId, final String slackUrl) { return new SlackResponse( reservation.getSpace().getName(), reservation.getUserName(), reservation.getStartTime(), reservation.getEndTime(), reservation.getDescription(), - sharingMapId); + sharingMapId, + slackUrl); } @Override diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java new file mode 100644 index 000000000..e69de29bb diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java index 2505bb542..986778eef 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java @@ -3,10 +3,7 @@ import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Space; -import com.woowacourse.zzimkkong.dto.map.MapCreateResponse; -import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; -import com.woowacourse.zzimkkong.dto.map.MapFindAllResponse; -import com.woowacourse.zzimkkong.dto.map.MapFindResponse; +import com.woowacourse.zzimkkong.dto.map.*; import com.woowacourse.zzimkkong.dto.member.LoginEmailDto; import com.woowacourse.zzimkkong.exception.authorization.NoAuthorityOnMapException; import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; @@ -107,6 +104,23 @@ public void deleteMap(final Long mapId, final LoginEmailDto loginEmailDto) { maps.delete(map); } + public void saveSlackUrl(final Long mapId, + final SlackCreateRequest slackCreateRequest, + final LoginEmailDto loginEmailDto) { + Map map = maps.findById(mapId) + .orElseThrow(NoSuchMapException::new); + validateManagerOfMap(map, loginEmailDto.getEmail()); + map.updateSlackUrl(slackCreateRequest.getSlackUrl()); + } + + @Transactional(readOnly = true) + public SlackFindResponse findSlackUrl(final Long mapId, final LoginEmailDto loginEmailDto) { + Map map = maps.findById(mapId) + .orElseThrow(NoSuchMapException::new); + validateManagerOfMap(map, loginEmailDto.getEmail()); + return SlackFindResponse.from(map); + } + private void validateExistReservations(final Map map) { List findSpaces = map.getSpaces(); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java index a5207418d..7bcdbc37f 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java @@ -71,7 +71,7 @@ public ReservationCreateResponse saveReservation( .space(space) .build()); String sharingMapId = sharingIdGenerator.from(map); - return ReservationCreateResponse.of(reservation, sharingMapId); + return ReservationCreateResponse.of(reservation, sharingMapId, map.getSlackUrl()); } @Transactional(readOnly = true) @@ -173,7 +173,7 @@ public SlackResponse updateReservation( reservation.update(updateReservation, space); String sharingMapId = sharingIdGenerator.from(map); - return SlackResponse.of(reservation, sharingMapId); + return SlackResponse.of(reservation, sharingMapId, map.getSlackUrl()); } public SlackResponse deleteReservation( @@ -200,7 +200,7 @@ public SlackResponse deleteReservation( reservations.delete(reservation); String sharingMapId = sharingIdGenerator.from(map); - return SlackResponse.of(reservation, sharingMapId); + return SlackResponse.of(reservation, sharingMapId, map.getSlackUrl()); } private void validateTime(final ReservationCreateDto reservationCreateDto, final boolean managerFlag) { diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java new file mode 100644 index 000000000..aeebc6c60 --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java @@ -0,0 +1,50 @@ +package com.woowacourse.zzimkkong.service; + +import com.woowacourse.zzimkkong.dto.slack.Attachments; +import com.woowacourse.zzimkkong.dto.slack.SlackResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.reactive.function.client.WebClient; + +@Service +@Transactional(readOnly = true) +public class SlackService { + private final WebClient slackWebClient; + private final String titleLink; + + public SlackService(@Value("${service.url}") final String titleLink, + final WebClient webClient) { + this.titleLink = titleLink; + slackWebClient = webClient; + } + + public void sendCreateMessage(SlackResponse slackResponse) { + Attachments attachments = Attachments.createMessageOf(slackResponse, titleLink); + send(attachments, slackResponse.getSlackUrl()); + } + + public void sendUpdateMessage(SlackResponse slackResponse) { + Attachments attachments = Attachments.updateMessageOf(slackResponse, titleLink); + send(attachments, slackResponse.getSlackUrl()); + } + + public void sendDeleteMessage(SlackResponse slackResponse) { + Attachments attachments = Attachments.deleteMessageOf(slackResponse, titleLink); + send(attachments, slackResponse.getSlackUrl()); + } + + private void send(final Attachments attachments, final String slackUrl) { + slackWebClient.mutate() + .baseUrl(slackUrl) + .build() + .post() + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(attachments.toString()) + .retrieve() + .bodyToMono(String.class) + .then() + .subscribe(); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java deleted file mode 100644 index 96c732779..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/LutherSender.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.woowacourse.zzimkkong.service.slack; - -import com.woowacourse.zzimkkong.dto.slack.SlackResponse; -import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.client.WebClient; - -@Component -@Profile("prod") -public class LutherSender extends Sender { - public LutherSender(final WebClient slackWebClient, - @Value("${slack.webhook.luther}") final String slackUrl, - final SharingIdGenerator sharingIdGenerator) { - super(slackWebClient.mutate().baseUrl(slackUrl).build(), sharingIdGenerator); - } - - @Override - public boolean isSupport(SlackResponse slackResponse) { - Long mapId = sharingIdGenerator.parseIdFrom(slackResponse.getSharingMapId()); - return LUTHER_ID.equals(mapId); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java deleted file mode 100644 index 0c9433cd4..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/Sender.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.woowacourse.zzimkkong.service.slack; - -import com.woowacourse.zzimkkong.dto.slack.Attachments; -import com.woowacourse.zzimkkong.dto.slack.SlackResponse; -import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; -import org.springframework.http.MediaType; -import org.springframework.web.reactive.function.client.WebClient; - -public abstract class Sender { - protected static final Long LUTHER_ID = 7L; - protected final SharingIdGenerator sharingIdGenerator; - private final WebClient slackWebClient; - - protected Sender(final WebClient webClient, - final SharingIdGenerator sharingIdGenerator) { - this.slackWebClient = webClient; - this.sharingIdGenerator = sharingIdGenerator; - } - - public abstract boolean isSupport(SlackResponse slackResponse); - - public void send(Attachments attachments) { - slackWebClient.post() - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(attachments.toString()) - .retrieve() - .bodyToMono(String.class) - .then() - .subscribe(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java deleted file mode 100644 index 60e36874f..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/SlackService.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.woowacourse.zzimkkong.service.slack; - -import com.woowacourse.zzimkkong.dto.slack.Attachments; -import com.woowacourse.zzimkkong.dto.slack.SlackResponse; -import com.woowacourse.zzimkkong.service.slack.Sender; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@Service -@Transactional(readOnly = true) -public class SlackService { - private final String titleLink; - private final List senders; - - public SlackService(@Value("${service.url}") final String titleLink, - final List senders) { - this.titleLink = titleLink; - this.senders = senders; - } - - public void sendCreateMessage(SlackResponse slackResponse) { - send(slackResponse, () -> Attachments.createMessageOf(slackResponse, titleLink)); - } - - public void sendUpdateMessage(SlackResponse slackResponse) { - send(slackResponse, () -> Attachments.updateMessageOf(slackResponse, titleLink)); - } - - public void sendDeleteMessage(SlackResponse slackResponse) { - send(slackResponse, () -> Attachments.deleteMessageOf(slackResponse, titleLink)); - } - - private void send(SlackResponse slackResponse, AttachmentsStrategy attachments) { - Sender sender = senders.stream() - .filter(s -> s.isSupport(slackResponse)) - .findAny() - .orElseThrow(); - - sender.send(attachments.getAttachments()); - } - - private interface AttachmentsStrategy { - Attachments getAttachments(); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java deleted file mode 100644 index faa72e352..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/slack/StandardSender.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.woowacourse.zzimkkong.service.slack; - -import com.woowacourse.zzimkkong.domain.SlackUrl; -import com.woowacourse.zzimkkong.dto.slack.SlackResponse; -import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; -import org.springframework.stereotype.Component; -import org.springframework.web.reactive.function.client.WebClient; - -@Component -public class StandardSender extends Sender { - public StandardSender(final WebClient slackWebClient, - final SlackUrl slackUrl, - final SharingIdGenerator sharingIdGenerator) { - super(slackWebClient.mutate().baseUrl(slackUrl.getUrl()).build(), sharingIdGenerator); - } - - @Override - public boolean isSupport(SlackResponse slackResponse) { - Long mapId = sharingIdGenerator.parseIdFrom(slackResponse.getSharingMapId()); - return !LUTHER_ID.equals(mapId); - } -} diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index cba79ba47..4a9faf736 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit cba79ba473cf676f1b40a70cb0273dc0d6be6b5e +Subproject commit 4a9faf736b0fdb7de9b9d5b7f087fa38b8d563ec diff --git a/backend/src/main/resources/db/migration/prod/V14__map_add_column_slack.sql b/backend/src/main/resources/db/migration/prod/V14__map_add_column_slack.sql new file mode 100644 index 000000000..fb81cdbd6 --- /dev/null +++ b/backend/src/main/resources/db/migration/prod/V14__map_add_column_slack.sql @@ -0,0 +1 @@ +ALTER TABLE map ADD COLUMN slack_url longtext; \ No newline at end of file diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java index c3d6760f5..97c641ea3 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java @@ -12,7 +12,7 @@ import com.woowacourse.zzimkkong.dto.space.SpaceFindDetailWithIdResponse; import com.woowacourse.zzimkkong.infrastructure.auth.AuthorizationExtractor; import com.woowacourse.zzimkkong.service.AdminService; -import com.woowacourse.zzimkkong.service.slack.SlackService; +import com.woowacourse.zzimkkong.service.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java index 00dc7b35b..eddf6a246 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java @@ -3,7 +3,7 @@ import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; -import com.woowacourse.zzimkkong.service.slack.SlackService; +import com.woowacourse.zzimkkong.service.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java index 953389800..a457b80a4 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java @@ -3,7 +3,7 @@ import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.infrastructure.auth.AuthorizationExtractor; -import com.woowacourse.zzimkkong.service.slack.SlackService; +import com.woowacourse.zzimkkong.service.SlackService; import io.restassured.RestAssured; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java index a18e77dd8..81159273b 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/MapControllerTest.java @@ -3,9 +3,7 @@ import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.Map; import com.woowacourse.zzimkkong.domain.Member; -import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; -import com.woowacourse.zzimkkong.dto.map.MapFindAllResponse; -import com.woowacourse.zzimkkong.dto.map.MapFindResponse; +import com.woowacourse.zzimkkong.dto.map.*; import com.woowacourse.zzimkkong.infrastructure.auth.AuthorizationExtractor; import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; import io.restassured.RestAssured; @@ -128,6 +126,30 @@ void delete() { assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); } + @Test + @DisplayName("맵에 슬랙url을 추가한다.") + void saveSlackUrl() { + // given, when + ExtractableResponse response = saveSlackUrl(createdMapApi + "/slack", new SlackCreateRequest("slack.url")); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + } + + @Test + @DisplayName("해당 맵의 슬랙url을 조회한다.") + void findSlackUrl() { + // given, when + ExtractableResponse response = findSlackUrl(createdMapApi + "/slack"); + SlackFindResponse actualResponse = response.as(SlackFindResponse.class); + SlackFindResponse expectedResponse = SlackFindResponse.from(luther); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(actualResponse).usingRecursiveComparison() + .isEqualTo(expectedResponse); + } + static ExtractableResponse saveMap(final String api, MapCreateUpdateRequest mapCreateUpdateRequest) { return RestAssured .given(getRequestSpecification()).log().all() @@ -140,6 +162,29 @@ static ExtractableResponse saveMap(final String api, MapCreateUpdateRe .then().log().all().extract(); } + private ExtractableResponse saveSlackUrl(final String api, SlackCreateRequest slackCreateRequest) { + return RestAssured + .given(getRequestSpecification()).log().all() + .accept("application/json") + .header("Authorization", AuthorizationExtractor.AUTHENTICATION_TYPE + " " + accessToken) + .filter(document("map/slackPost", getRequestPreprocessor(), getResponsePreprocessor())) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(slackCreateRequest) + .when().post(api) + .then().log().all().extract(); + } + + private ExtractableResponse findSlackUrl(final String api) { + return RestAssured + .given(getRequestSpecification()).log().all() + .accept("application/json") + .header("Authorization", AuthorizationExtractor.AUTHENTICATION_TYPE + " " + accessToken) + .filter(document("map/slackGet", getRequestPreprocessor(), getResponsePreprocessor())) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().get(api) + .then().log().all().extract(); + } + static ExtractableResponse findMap(final String api) { return RestAssured .given(getRequestSpecification()).log().all() From 4bb63ea019a44b9c3a84ac830ff692a3566e9206 Mon Sep 17 00:00:00 2001 From: Yeonwoo Cho Date: Wed, 26 Jan 2022 20:59:26 +0900 Subject: [PATCH 12/24] =?UTF-8?q?refactor:=20map=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=20(#769)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/woowacourse/zzimkkong/service/MapService.java | 1 + .../src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java index 986778eef..f006ddf5e 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/MapService.java @@ -92,6 +92,7 @@ public void updateMap(final Long mapId, map.update( mapCreateUpdateRequest.getMapName(), mapCreateUpdateRequest.getMapDrawing()); + map.updateThumbnail(mapCreateUpdateRequest.getThumbnail()); } public void deleteMap(final Long mapId, final LoginEmailDto loginEmailDto) { diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java index cf993a744..64c0b6c61 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/domain/MapTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.domain; -import com.woowacourse.zzimkkong.Constants; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; From 00963cdba55135ce0c8edf48729aabe62af3585b Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Sat, 29 Jan 2022 17:49:19 +0900 Subject: [PATCH 13/24] =?UTF-8?q?feat:=20=EC=8A=AC=EB=9E=99=20=EC=9B=B9?= =?UTF-8?q?=ED=9B=85=20URL=20=EC=84=A4=EC=A0=95=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#772)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 슬랙 웹훅 URL 로드 및 설정 API 기능 구현 * feat: 슬랙 웹훅 URL 설정 UI 및 기능 구현 * chore: 개발용 서버 URL 변경 * chore: 개발용 서버 URL 오타 수정 * chore: 개발용 서버 API 주소 변경 * refactor: 코드 리뷰에 따른 리팩토링 - `!selectedMapId` -> `selectedMapId === null`로 조건식 변경 - ` + + + + + + )} ); }; diff --git a/frontend/src/pages/ManagerMain/hooks/useSlackWebhookUrl.ts b/frontend/src/pages/ManagerMain/hooks/useSlackWebhookUrl.ts new file mode 100644 index 000000000..ec62f4549 --- /dev/null +++ b/frontend/src/pages/ManagerMain/hooks/useSlackWebhookUrl.ts @@ -0,0 +1,17 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { QueryKey, useQuery, UseQueryOptions, UseQueryResult } from 'react-query'; +import { querySlackWebhookUrl, QuerySlackWebhookURLParams } from 'api/managerMap'; +import { ErrorResponse, QuerySlackWebhookUrlSuccess } from 'types/response'; + +const useSlackWebhookUrl = >( + { mapId }: QuerySlackWebhookURLParams, + options?: UseQueryOptions< + AxiosResponse, + AxiosError, + TData, + [QueryKey, QuerySlackWebhookURLParams] + > +): UseQueryResult> => + useQuery(['getSlackWebhookUrl', { mapId }], querySlackWebhookUrl, options); + +export default useSlackWebhookUrl; diff --git a/frontend/src/types/response.ts b/frontend/src/types/response.ts index 841da3d3b..8654aeea8 100644 --- a/frontend/src/types/response.ts +++ b/frontend/src/types/response.ts @@ -62,3 +62,7 @@ export interface QueryManagerSpacesSuccess { export interface QueryPresetsSuccess { presets: Preset[]; } + +export interface QuerySlackWebhookUrlSuccess { + slackUrl: string; +} From 8af14102770f9b489f6f1dc7a3f6f9e2ab4d8900 Mon Sep 17 00:00:00 2001 From: xrabcde Date: Sat, 29 Jan 2022 23:19:46 +0900 Subject: [PATCH 14/24] =?UTF-8?q?chore:=20=EA=B0=9C=EB=B0=9C=EC=9A=A9=20?= =?UTF-8?q?=ED=99=98=EA=B2=BD=20=EC=84=B8=ED=8C=85=20(#773)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: 개발용 포트번호 설정 * build: 개발용 db 설정 --- backend/src/main/resources/application-dev.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/main/resources/application-dev.properties b/backend/src/main/resources/application-dev.properties index feed1af88..14bdb42e0 100644 --- a/backend/src/main/resources/application-dev.properties +++ b/backend/src/main/resources/application-dev.properties @@ -1,6 +1,8 @@ +server.port=8081 + # Database spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -spring.datasource.url=jdbc:mysql://localhost:3306/zzimkkong?characterEncoding=UTF-8&useLegacyDatetimeCode=false +spring.datasource.url=jdbc:mysql://192.168.0.164:3306/zzimkkong_dev?characterEncoding=UTF-8&useLegacyDatetimeCode=false spring.datasource.username=root spring.datasource.password=1234 spring.datasource.hikari.maximum-pool-size=45 From 8811d0439748d2c7997068d1586e47621936e8eb Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Tue, 1 Feb 2022 16:36:04 +0900 Subject: [PATCH 15/24] =?UTF-8?q?fix:=20=EA=B3=B5=EA=B0=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20=EC=88=98=EC=A0=95,=20=EC=82=AD=EC=A0=9C=EC=8B=9C?= =?UTF-8?q?=20=EB=B0=94=EB=94=94=EC=9D=98=20=EC=8D=B8=EB=84=A4=EC=9D=BC=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=9D=B8=EC=9E=90=EA=B0=92=EC=9D=84=20map?= =?UTF-8?q?ImageSvg=EC=97=90=EC=84=9C=20thumbnail=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=9C=EB=8B=A4.=20(#781)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 공간 생성, 수정, 삭제시 바디의 썸네일 정보 인자값을 thumbnail로 수정 * refactor: 썸네일 관련 미사용 예외 클래스 삭제 * refactor: 썸네일 관련 미사용 예외 클래스 삭제 2 --- .../dto/space/SpaceCreateUpdateRequest.java | 6 +++--- .../zzimkkong/dto/space/SpaceDeleteRequest.java | 6 +++--- .../CannotDeleteConvertedFileException.java | 11 ----------- ...otGenerateInputStreamFromSvgDataException.java | 11 ----------- .../S3ProxyRespondedFailException.java | 11 ----------- .../infrastructure/S3UploadException.java | 15 --------------- .../infrastructure/SvgToPngConvertException.java | 11 ----------- 7 files changed, 6 insertions(+), 65 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotDeleteConvertedFileException.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotGenerateInputStreamFromSvgDataException.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3ProxyRespondedFailException.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3UploadException.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/SvgToPngConvertException.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceCreateUpdateRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceCreateUpdateRequest.java index aa198459f..8f1b98073 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceCreateUpdateRequest.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceCreateUpdateRequest.java @@ -24,7 +24,7 @@ public class SpaceCreateUpdateRequest { private String area; @NotBlank(message = EMPTY_MESSAGE) - private String mapImageSvg; + private String thumbnail; @Valid private SettingsRequest settingsRequest; @@ -35,12 +35,12 @@ public SpaceCreateUpdateRequest( final String description, final String area, final SettingsRequest settingsRequest, - final String mapImageSvg) { + final String thumbnail) { this.name = name; this.color = color; this.description = description; this.area = area; this.settingsRequest = settingsRequest; - this.mapImageSvg = mapImageSvg; + this.thumbnail = thumbnail; } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceDeleteRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceDeleteRequest.java index e7cf4ee48..e6f384fb4 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceDeleteRequest.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/space/SpaceDeleteRequest.java @@ -11,9 +11,9 @@ @NoArgsConstructor public class SpaceDeleteRequest { @NotBlank(message = EMPTY_MESSAGE) - private String mapImageSvg; + private String thumbnail; - public SpaceDeleteRequest(final String mapImageSvg) { - this.mapImageSvg = mapImageSvg; + public SpaceDeleteRequest(final String thumbnail) { + this.thumbnail = thumbnail; } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotDeleteConvertedFileException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotDeleteConvertedFileException.java deleted file mode 100644 index b24d3ef49..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotDeleteConvertedFileException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import org.springframework.http.HttpStatus; - -public class CannotDeleteConvertedFileException extends InfrastructureMalfunctionException { - private static final String MESSAGE = "변환된 이미지를 삭제하는 데에 실패했습니다. 관리자에게 문의하세요."; - - public CannotDeleteConvertedFileException() { - super(MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotGenerateInputStreamFromSvgDataException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotGenerateInputStreamFromSvgDataException.java deleted file mode 100644 index 1ed8fd57a..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/CannotGenerateInputStreamFromSvgDataException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import org.springframework.http.HttpStatus; - -public class CannotGenerateInputStreamFromSvgDataException extends InfrastructureMalfunctionException { - private static final String MESSAGE = "svg 데이터를 읽어올 수 없습니다."; - - public CannotGenerateInputStreamFromSvgDataException(Throwable throwable) { - super(MESSAGE, throwable, HttpStatus.BAD_REQUEST); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3ProxyRespondedFailException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3ProxyRespondedFailException.java deleted file mode 100644 index 831b73242..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3ProxyRespondedFailException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import org.springframework.http.HttpStatus; - -public class S3ProxyRespondedFailException extends InfrastructureMalfunctionException { - private static final String MESSAGE = "이미지 버킷 업로드에 실패했습니다."; - - public S3ProxyRespondedFailException() { - super(MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3UploadException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3UploadException.java deleted file mode 100644 index d705eb2db..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/S3UploadException.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import org.springframework.http.HttpStatus; - -public class S3UploadException extends InfrastructureMalfunctionException { - private static final String MESSAGE = "이미지 버킷 업로드에 실패했습니다."; - - public S3UploadException() { - super(MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR); - } - - public S3UploadException(final Exception exception) { - super(MESSAGE, exception, HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/SvgToPngConvertException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/SvgToPngConvertException.java deleted file mode 100644 index 1b5ad60bf..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/SvgToPngConvertException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import org.springframework.http.HttpStatus; - -public class SvgToPngConvertException extends InfrastructureMalfunctionException { - private static final String MESSAGE = "svg 데이터를 png 파일로 변환할 수 없습니다. 데이터 형식을 확인해주세요."; - - public SvgToPngConvertException(final Exception exception) { - super(MESSAGE, exception, HttpStatus.BAD_REQUEST); - } -} From a77ca510c0632ef15c227c3ada5686b26a6c70fd Mon Sep 17 00:00:00 2001 From: Kimun Kim Date: Tue, 1 Feb 2022 23:28:10 +0900 Subject: [PATCH 16/24] =?UTF-8?q?fix:=20=EA=B3=B5=EA=B0=84=20CUD=20API?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=A7=B5=EC=9D=98=20=EC=8D=B8=EB=84=A4?= =?UTF-8?q?=EC=9D=BC=EC=9D=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#785)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/woowacourse/zzimkkong/service/SpaceService.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java index 2c7d40aca..7f40b9ebf 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/SpaceService.java @@ -53,6 +53,8 @@ public SpaceCreateResponse saveSpace( .build(); Space saveSpace = spaces.save(space); + map.updateThumbnail(spaceCreateUpdateRequest.getThumbnail()); + return SpaceCreateResponse.from(saveSpace); } @@ -114,6 +116,8 @@ public void updateSpace( .build(); space.update(updateSpace); + + map.updateThumbnail(spaceCreateUpdateRequest.getThumbnail()); } public void deleteSpace( @@ -131,6 +135,8 @@ public void deleteSpace( validateReservationExistence(spaceId); spaces.delete(space); + + map.updateThumbnail(spaceDeleteRequest.getThumbnail()); } private Setting getSetting(final SpaceCreateUpdateRequest spaceCreateUpdateRequest) { From db750f7e4b64c2d8e92ae44ac63fc72581e6d198 Mon Sep 17 00:00:00 2001 From: Jungseok Sung <58401309+sakjung@users.noreply.github.com> Date: Wed, 2 Feb 2022 14:40:24 +0900 Subject: [PATCH 17/24] =?UTF-8?q?feat:=20build=20&=20deploy=20=EC=89=98=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#7?= =?UTF-8?q?77)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: build & deploy 쉘 스크립트 추가 * feat: build deploy script 메서드 명 수정 * feat: WarmerConfig.java 제거 * feat: 빌드 옵션을 줄 수 있도록하는 기능 추가 --- .../com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java | 0 backend/src/main/resources/config | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java b/backend/src/main/java/com/woowacourse/zzimkkong/config/warmup/WarmerConfig.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config index 4a9faf736..428882614 160000 --- a/backend/src/main/resources/config +++ b/backend/src/main/resources/config @@ -1 +1 @@ -Subproject commit 4a9faf736b0fdb7de9b9d5b7f087fa38b8d563ec +Subproject commit 428882614a89e1aa792745f16567e619a4f55c7c From 8f00f32ac158446bd1a945e3a89f0e9aad8c0f88 Mon Sep 17 00:00:00 2001 From: xrabcde Date: Wed, 2 Feb 2022 14:40:38 +0900 Subject: [PATCH 18/24] =?UTF-8?q?refactor:=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EB=B0=8F=20=EC=98=88=EC=95=BD=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EB=A1=9C=EC=A7=81=20=EB=B6=84=EB=A6=AC=20(#7?= =?UTF-8?q?82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 예외 메시지 명확하게 리팩터링 * refactor: 사용하지 않는 예외 제거 * fix: 슬랙 웹훅 url이 등록되어 있지 않은 경우에 대한 예외처리 추가 * refactor: Duration Time을 최소예약가능시간과 최대예약가능시간 검증으로 분리 * test: 예약시간 검증 관련 터지는 테스트 수정 * test: 예약시간 검증이 분리됨에 따라 최소/최대 시간 테스트도 분리 * refactor: 이미 예약이 존재할 때의 예외이름을 더 명확하게 수정 --- .../woowacourse/zzimkkong/domain/Space.java | 8 +- .../NoMasterDataSourceException.java | 12 -- .../InvalidDayOfWeekException.java | 10 +- .../InvalidDurationTimeException.java | 4 - ... InvalidMaximumDurationTimeException.java} | 6 +- .../InvalidMinimumDurationTimeException.java | 12 ++ .../InvalidReservationEnableException.java | 10 +- .../InvalidStartEndTimeException.java | 10 +- .../reservation/InvalidTimeUnitException.java | 10 +- ...=> ReservationAlreadyExistsException.java} | 6 +- .../zzimkkong/service/ReservationService.java | 10 +- .../zzimkkong/service/SlackService.java | 24 ++-- .../zzimkkong/domain/SpaceTest.java | 27 ++--- .../service/GuestReservationServiceTest.java | 52 +++++++-- .../ManagerReservationServiceTest.java | 105 +++++++++++++++--- 15 files changed, 224 insertions(+), 82 deletions(-) delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/NoMasterDataSourceException.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDurationTimeException.java rename backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/{ConflictSpaceSettingException.java => InvalidMaximumDurationTimeException.java} (50%) create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMinimumDurationTimeException.java rename backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/{ImpossibleReservationTimeException.java => ReservationAlreadyExistsException.java} (52%) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Space.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Space.java index 20de01418..e0d509a51 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Space.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Space.java @@ -96,8 +96,12 @@ public boolean isNotDivisibleByTimeUnit(final int minute) { return setting.isNotDivisibleByTimeUnit(minute); } - public boolean isIncorrectMinimumMaximumTimeUnit(final int durationMinutes) { - return durationMinutes < getReservationMinimumTimeUnit() || durationMinutes > getReservationMaximumTimeUnit(); + public boolean isIncorrectMinimumTimeUnit(final int durationMinutes) { + return durationMinutes < getReservationMinimumTimeUnit(); + } + + public boolean isIncorrectMaximumTimeUnit(final int durationMinutes) { + return durationMinutes > getReservationMaximumTimeUnit(); } public boolean isUnableToReserve() { diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/NoMasterDataSourceException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/NoMasterDataSourceException.java deleted file mode 100644 index 81f47ac2d..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/infrastructure/NoMasterDataSourceException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.woowacourse.zzimkkong.exception.infrastructure; - -import com.woowacourse.zzimkkong.exception.ZzimkkongException; -import org.springframework.http.HttpStatus; - -public class NoMasterDataSourceException extends ZzimkkongException { - private static final String MESSAGE = "Master DB의 DataSource 설정이 올바르지 않습니다."; - - public NoMasterDataSourceException() { - super(MESSAGE, HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDayOfWeekException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDayOfWeekException.java index b83765cb9..69c1e4741 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDayOfWeekException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDayOfWeekException.java @@ -1,4 +1,12 @@ package com.woowacourse.zzimkkong.exception.reservation; -public class InvalidDayOfWeekException extends ConflictSpaceSettingException { +import com.woowacourse.zzimkkong.exception.ZzimkkongException; +import org.springframework.http.HttpStatus; + +public class InvalidDayOfWeekException extends ZzimkkongException { + private static final String MESSAGE = "해당 요일에 예약이 불가능한 공간입니다."; + + public InvalidDayOfWeekException() { + super(MESSAGE, HttpStatus.BAD_REQUEST); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDurationTimeException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDurationTimeException.java deleted file mode 100644 index 954aa253d..000000000 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidDurationTimeException.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.woowacourse.zzimkkong.exception.reservation; - -public class InvalidDurationTimeException extends ConflictSpaceSettingException { -} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ConflictSpaceSettingException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMaximumDurationTimeException.java similarity index 50% rename from backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ConflictSpaceSettingException.java rename to backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMaximumDurationTimeException.java index d1a7917e5..5e9962843 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ConflictSpaceSettingException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMaximumDurationTimeException.java @@ -3,10 +3,10 @@ import com.woowacourse.zzimkkong.exception.ZzimkkongException; import org.springframework.http.HttpStatus; -public class ConflictSpaceSettingException extends ZzimkkongException { - private static final String MESSAGE = "공간의 예약조건을 확인해주세요."; +public class InvalidMaximumDurationTimeException extends ZzimkkongException { + private static final String MESSAGE = "최대 예약가능시간을 확인해주세요."; - public ConflictSpaceSettingException() { + public InvalidMaximumDurationTimeException() { super(MESSAGE, HttpStatus.BAD_REQUEST); } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMinimumDurationTimeException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMinimumDurationTimeException.java new file mode 100644 index 000000000..8189d4ace --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidMinimumDurationTimeException.java @@ -0,0 +1,12 @@ +package com.woowacourse.zzimkkong.exception.reservation; + +import com.woowacourse.zzimkkong.exception.ZzimkkongException; +import org.springframework.http.HttpStatus; + +public class InvalidMinimumDurationTimeException extends ZzimkkongException { + private static final String MESSAGE = "최소 예약가능시간을 확인해주세요."; + + public InvalidMinimumDurationTimeException() { + super(MESSAGE, HttpStatus.BAD_REQUEST); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidReservationEnableException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidReservationEnableException.java index d3bb56423..541fcd0a2 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidReservationEnableException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidReservationEnableException.java @@ -1,4 +1,12 @@ package com.woowacourse.zzimkkong.exception.reservation; -public class InvalidReservationEnableException extends ConflictSpaceSettingException { +import com.woowacourse.zzimkkong.exception.ZzimkkongException; +import org.springframework.http.HttpStatus; + +public class InvalidReservationEnableException extends ZzimkkongException { + private static final String MESSAGE = "현재 예약이 불가능한 공간입니다."; + + public InvalidReservationEnableException() { + super(MESSAGE, HttpStatus.BAD_REQUEST); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidStartEndTimeException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidStartEndTimeException.java index 9edcc1bb3..9a74ad372 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidStartEndTimeException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidStartEndTimeException.java @@ -1,4 +1,12 @@ package com.woowacourse.zzimkkong.exception.reservation; -public class InvalidStartEndTimeException extends ConflictSpaceSettingException { +import com.woowacourse.zzimkkong.exception.ZzimkkongException; +import org.springframework.http.HttpStatus; + +public class InvalidStartEndTimeException extends ZzimkkongException { + private static final String MESSAGE = "공간의 예약가능 시간을 확인해주세요."; + + public InvalidStartEndTimeException() { + super(MESSAGE, HttpStatus.BAD_REQUEST); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidTimeUnitException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidTimeUnitException.java index 30f5be9c0..20e7d1dd7 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidTimeUnitException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/InvalidTimeUnitException.java @@ -1,4 +1,12 @@ package com.woowacourse.zzimkkong.exception.reservation; -public class InvalidTimeUnitException extends ConflictSpaceSettingException { +import com.woowacourse.zzimkkong.exception.ZzimkkongException; +import org.springframework.http.HttpStatus; + +public class InvalidTimeUnitException extends ZzimkkongException { + private static final String MESSAGE = "예약 시간단위를 확인해주세요."; + + public InvalidTimeUnitException() { + super(MESSAGE, HttpStatus.BAD_REQUEST); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ImpossibleReservationTimeException.java b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ReservationAlreadyExistsException.java similarity index 52% rename from backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ImpossibleReservationTimeException.java rename to backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ReservationAlreadyExistsException.java index 5ecefafe1..71da5fccd 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ImpossibleReservationTimeException.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/exception/reservation/ReservationAlreadyExistsException.java @@ -3,10 +3,10 @@ import com.woowacourse.zzimkkong.exception.InputFieldException; import org.springframework.http.HttpStatus; -public class ImpossibleReservationTimeException extends InputFieldException { - private static final String MESSAGE = "예약할 수 없는 시간입니다."; +public class ReservationAlreadyExistsException extends InputFieldException { + private static final String MESSAGE = "해당 시간에 이미 예약이 존재합니다."; - public ImpossibleReservationTimeException() { + public ReservationAlreadyExistsException() { super(MESSAGE, HttpStatus.BAD_REQUEST, START_DATE_TIME); } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java index 7bcdbc37f..4cf5e03d6 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java @@ -248,8 +248,12 @@ private void validateSpaceSetting(Space space, LocalDateTime startDateTime, Loca throw new InvalidTimeUnitException(); } - if (space.isIncorrectMinimumMaximumTimeUnit(durationMinutes)) { - throw new InvalidDurationTimeException(); + if (space.isIncorrectMinimumTimeUnit(durationMinutes)) { + throw new InvalidMinimumDurationTimeException(); + } + + if (space.isIncorrectMaximumTimeUnit(durationMinutes)) { + throw new InvalidMaximumDurationTimeException(); } if (space.isNotBetweenAvailableTime(startDateTime, endDateTime)) { @@ -271,7 +275,7 @@ private void validateTimeConflicts( final List reservationsOnDate) { for (Reservation existingReservation : reservationsOnDate) { if (existingReservation.hasConflictWith(startDateTime, endDateTime)) { - throw new ImpossibleReservationTimeException(); + throw new ReservationAlreadyExistsException(); } } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java index aeebc6c60..8f3d23c52 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/SlackService.java @@ -8,6 +8,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.reactive.function.client.WebClient; +import java.util.Objects; + @Service @Transactional(readOnly = true) public class SlackService { @@ -36,15 +38,17 @@ public void sendDeleteMessage(SlackResponse slackResponse) { } private void send(final Attachments attachments, final String slackUrl) { - slackWebClient.mutate() - .baseUrl(slackUrl) - .build() - .post() - .contentType(MediaType.APPLICATION_JSON) - .bodyValue(attachments.toString()) - .retrieve() - .bodyToMono(String.class) - .then() - .subscribe(); + if (!Objects.isNull(slackUrl)) { + slackWebClient.mutate() + .baseUrl(slackUrl) + .build() + .post() + .contentType(MediaType.APPLICATION_JSON) + .bodyValue(attachments.toString()) + .retrieve() + .bodyToMono(String.class) + .then() + .subscribe(); + } } } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java index 58d4e6f76..88285baf9 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/domain/SpaceTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.domain; -import com.woowacourse.zzimkkong.Constants; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -155,10 +154,9 @@ void isCorrectTimeUnitFail(int minute) { assertThat(actual).isTrue(); } - @ParameterizedTest - @ValueSource(ints = {10, 120}) - @DisplayName("예약 시간의 단위가 최소최대 예약시간단위 내에 있다면 false를 반환한다.") - void isCorrectMinimumMaximumTimeUnit(int durationMinutes) { + @Test + @DisplayName("예약 시간의 단위가 최소/최대 예약시간단위 내에 있다면 false를 반환한다.") + void isCorrectMinimumMaximumTimeUnit() { Setting availableTimeSetting = Setting.builder() .availableStartTime(FE_AVAILABLE_START_TIME) .availableEndTime(FE_AVAILABLE_END_TIME) @@ -170,15 +168,16 @@ void isCorrectMinimumMaximumTimeUnit(int durationMinutes) { .build(); Space availableTimeSpace = Space.builder().setting(availableTimeSetting).build(); - boolean actual = availableTimeSpace.isIncorrectMinimumMaximumTimeUnit(durationMinutes); + boolean minActual = availableTimeSpace.isIncorrectMinimumTimeUnit(10); + boolean maxActual = availableTimeSpace.isIncorrectMinimumTimeUnit(120); - assertThat(actual).isFalse(); + assertThat(minActual).isFalse(); + assertThat(maxActual).isFalse(); } - @ParameterizedTest - @ValueSource(ints = {9, 121}) - @DisplayName("예약 시간의 단위가 최소시간단위보다 작거나 최대시간단위보다 크다면 true를 반환한다.") - void isCorrectMinimumMaximumTimeUnitFail(int durationMinutes) { + @Test + @DisplayName("예약 시간의 단위가 최소/최대 시간단위보다 작다면 true를 반환한다.") + void isCorrectMinimumMaximumTimeUnitFail() { Setting availableTimeSetting = Setting.builder() .availableStartTime(FE_AVAILABLE_START_TIME) .availableEndTime(FE_AVAILABLE_END_TIME) @@ -190,9 +189,11 @@ void isCorrectMinimumMaximumTimeUnitFail(int durationMinutes) { .build(); Space availableTimeSpace = Space.builder().setting(availableTimeSetting).build(); - boolean actual = availableTimeSpace.isIncorrectMinimumMaximumTimeUnit(durationMinutes); + boolean minActual = availableTimeSpace.isIncorrectMinimumTimeUnit(9); + boolean maxActual = availableTimeSpace.isIncorrectMaximumTimeUnit(121); - assertThat(actual).isTrue(); + assertThat(minActual).isTrue(); + assertThat(maxActual).isTrue(); } @Test diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java index ef04dfedb..2509fa2c3 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.service; -import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; @@ -352,7 +351,7 @@ void saveAvailabilityException(int startMinute, int endMinute) { assertThatThrownBy(() -> reservationService.saveReservation( reservationCreateDto, guestReservationStrategy)) - .isInstanceOf(ImpossibleReservationTimeException.class); + .isInstanceOf(ReservationAlreadyExistsException.class); } @Test @@ -510,10 +509,9 @@ void saveReservationTimeUnitException(int additionalStartMinute, int additionalE .isInstanceOf(InvalidTimeUnitException.class); } - @ParameterizedTest - @ValueSource(ints = {50, 130}) - @DisplayName("예약 생성/수정 요청 시, space setting의 minimum, maximum 시간이 옳지 않으면 예외가 발생한다.") - void saveReservationMinimumMaximumTimeUnitException(int duration) { + @Test + @DisplayName("예약 생성/수정 요청 시, space setting의 minimum 시간이 옳지 않으면 예외가 발생한다.") + void saveReservationMinimumDurationTimeException() { //given given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); @@ -523,7 +521,7 @@ void saveReservationMinimumMaximumTimeUnitException(int duration) { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(duration), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -543,12 +541,42 @@ void saveReservationMinimumMaximumTimeUnitException(int duration) { assertThatThrownBy(() -> reservationService.saveReservation( reservationCreateDto, guestReservationStrategy)) - .isInstanceOf(InvalidDurationTimeException.class); + .isInstanceOf(InvalidMinimumDurationTimeException.class); + } - assertThatThrownBy(() -> reservationService.updateReservation( - reservationUpdateDto, + @Test + @DisplayName("예약 생성/수정 요청 시, space setting의 maximum 시간이 옳지 않으면 예외가 발생한다.") + void saveReservationMaximumTimeUnitException() { + //given + given(maps.findByIdFetch(anyLong())) + .willReturn(Optional.of(luther)); + given(reservations.findById(anyLong())) + .willReturn(Optional.of(reservation)); + + //when + ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( + THE_DAY_AFTER_TOMORROW.atTime(10, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + RESERVATION_PW, + USER_NAME, + DESCRIPTION); + Long reservationId = reservation.getId(); + + ReservationCreateDto reservationCreateDto = ReservationCreateDto.of( + lutherId, + beId, + reservationCreateUpdateWithPasswordRequest); + ReservationUpdateDto reservationUpdateDto = ReservationUpdateDto.of( + lutherId, + beId, + reservationId, + reservationCreateUpdateWithPasswordRequest); + + //then + assertThatThrownBy(() -> reservationService.saveReservation( + reservationCreateDto, guestReservationStrategy)) - .isInstanceOf(InvalidDurationTimeException.class); + .isInstanceOf(InvalidMaximumDurationTimeException.class); } @Test @@ -975,7 +1003,7 @@ void updateImpossibleTimeException(int startTime, int endTime) { assertThatThrownBy(() -> reservationService.updateReservation( reservationUpdateDto, guestReservationStrategy)) - .isInstanceOf(ImpossibleReservationTimeException.class); + .isInstanceOf(ReservationAlreadyExistsException.class); } @ParameterizedTest diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java index c656bb4ee..9fc81d698 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java @@ -411,7 +411,7 @@ void saveAvailabilityException(int startMinute, int endMinute) { assertThatThrownBy(() -> reservationService.saveReservation( reservationCreateDto, managerReservationStrategy)) - .isInstanceOf(ImpossibleReservationTimeException.class); + .isInstanceOf(ReservationAlreadyExistsException.class); } @Test @@ -582,12 +582,10 @@ void saveReservationTimeUnitException(int additionalStartMinute, int additionalE .isInstanceOf(InvalidTimeUnitException.class); } - @ParameterizedTest - @ValueSource(ints = {50, 130}) - @DisplayName("예약 생성/수정 요청 시, space setting의 minimum, maximum 시간이 옳지 않으면 예외가 발생한다.") - void saveReservationMinimumMaximumTimeUnitException(int duration) { + @Test + @DisplayName("예약 생성 요청 시, space setting의 minimum 시간이 옳지 않으면 예외가 발생한다.") + void saveReservationMinimumTimeUnitException() { //given - given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) @@ -596,24 +594,72 @@ void saveReservationMinimumMaximumTimeUnitException(int duration) { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(duration), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), RESERVATION_PW, USER_NAME, DESCRIPTION); - ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( + + ReservationCreateDto reservationCreateDto = ReservationCreateDto.of( + lutherId, + beId, + reservationCreateUpdateWithPasswordRequest, + pobiEmail); + + //then + assertThatThrownBy(() -> reservationService.saveReservation( + reservationCreateDto, + managerReservationStrategy)) + .isInstanceOf(InvalidMinimumDurationTimeException.class); + } + + @Test + @DisplayName("예약 생성 요청 시, space setting의 maximum 시간이 옳지 않으면 예외가 발생한다.") + void saveReservationMaximumTimeUnitException() { + //given + given(maps.findByIdFetch(anyLong())) + .willReturn(Optional.of(luther)); + given(reservations.findById(anyLong())) + .willReturn(Optional.of(reservation)); + + //when + ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(duration), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + RESERVATION_PW, USER_NAME, DESCRIPTION); - Long reservationId = reservation.getId(); - ReservationCreateDto reservationCreateDto = ReservationCreateDto.of( lutherId, beId, reservationCreateUpdateWithPasswordRequest, pobiEmail); + //then + assertThatThrownBy(() -> reservationService.saveReservation( + reservationCreateDto, + managerReservationStrategy)) + .isInstanceOf(InvalidMaximumDurationTimeException.class); + } + + @Test + @DisplayName("예약 수정 요청 시, space setting의 minimum 시간이 옳지 않으면 예외가 발생한다.") + void updateReservationMinimumDurationTimeException() { + //given + given(maps.findByIdFetch(anyLong())) + .willReturn(Optional.of(luther)); + given(reservations.findById(anyLong())) + .willReturn(Optional.of(reservation)); + + //when + ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( + THE_DAY_AFTER_TOMORROW.atTime(10, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), + USER_NAME, + DESCRIPTION); + + Long reservationId = reservation.getId(); + ReservationUpdateDto reservationUpdateDto = ReservationUpdateDto.of( lutherId, beId, @@ -622,15 +668,42 @@ void saveReservationMinimumMaximumTimeUnitException(int duration) { pobiEmail); //then - assertThatThrownBy(() -> reservationService.saveReservation( - reservationCreateDto, + assertThatThrownBy(() -> reservationService.updateReservation( + reservationUpdateDto, managerReservationStrategy)) - .isInstanceOf(InvalidDurationTimeException.class); + .isInstanceOf(InvalidMinimumDurationTimeException.class); + } + @Test + @DisplayName("예약 수정 요청 시, space setting의 maximum 시간이 옳지 않으면 예외가 발생한다.") + void updateReservationMaximumDurationTimeException() { + //given + given(maps.findByIdFetch(anyLong())) + .willReturn(Optional.of(luther)); + given(reservations.findById(anyLong())) + .willReturn(Optional.of(reservation)); + + //when + ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( + THE_DAY_AFTER_TOMORROW.atTime(10, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + USER_NAME, + DESCRIPTION); + + Long reservationId = reservation.getId(); + + ReservationUpdateDto reservationUpdateDto = ReservationUpdateDto.of( + lutherId, + beId, + reservationId, + reservationCreateUpdateRequest, + pobiEmail); + + //then assertThatThrownBy(() -> reservationService.updateReservation( reservationUpdateDto, managerReservationStrategy)) - .isInstanceOf(InvalidDurationTimeException.class); + .isInstanceOf(InvalidMaximumDurationTimeException.class); } @Test @@ -1110,7 +1183,7 @@ void updateImpossibleTimeException(int startTime, int endTime) { assertThatThrownBy(() -> reservationService.updateReservation( reservationUpdateDto, managerReservationStrategy)) - .isInstanceOf(ImpossibleReservationTimeException.class); + .isInstanceOf(ReservationAlreadyExistsException.class); } @ParameterizedTest From 7e4204741bfcf0ff2c3edc96bd61895df155be66 Mon Sep 17 00:00:00 2001 From: JO YUN HO Date: Wed, 2 Feb 2022 21:56:00 +0900 Subject: [PATCH 19/24] =?UTF-8?q?refactor:=20=EA=B4=80=EB=A6=AC=EC=9E=90?= =?UTF-8?q?=20=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=8D=B8=EB=84=A4=EC=9D=BC=EC=9D=84=20png=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20svg=EB=A1=9C=20=EB=B3=B4=EC=97=AC=EC=A3=BC=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#787)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: map 생성, 수정 시 보내는 payload 변경 - mapImageSvg -> thumbnail * test: mock data type 변경 * refactor: 공간관리자 메인 페이지에서 img대신 svg를 보여주도록 변경 * refactor: 공간 편집 시 서버에 전송하는 필드 이름 수정 - mapImagesSvg -> thumbnail * fix: 삭제 버튼이 클릭되지 않는 오류 수정 --- frontend/src/__mocks__/mockData.ts | 2 +- frontend/src/api/managerMap.ts | 12 +++++----- frontend/src/api/managerSpace.ts | 8 +++---- .../MapListItem/MapListItem.stories.tsx | 5 +--- .../MapListItem/MapListItem.styles.ts | 7 +++--- .../components/MapListItem/MapListItem.tsx | 23 +++---------------- .../src/pages/ManagerMain/units/MapDrawer.tsx | 4 ++-- .../ManagerMapEditor/ManagerMapEditor.tsx | 6 ++--- .../pages/ManagerSpaceEditor/units/Form.tsx | 10 ++++---- frontend/src/types/common.ts | 2 +- 10 files changed, 29 insertions(+), 50 deletions(-) diff --git a/frontend/src/__mocks__/mockData.ts b/frontend/src/__mocks__/mockData.ts index 07a6e910c..c440be244 100644 --- a/frontend/src/__mocks__/mockData.ts +++ b/frontend/src/__mocks__/mockData.ts @@ -30,7 +30,7 @@ export const guestMaps: GuestMaps = { mapName: 'GUEST_TEST_MAP', mapDrawing: '{"width":800,"height":600,"mapElements":[{"id":2,"type":"rect","stroke":"#333333","points":["210,90","650,230"]},{"id":3,"type":"rect","stroke":"#333333","width":440,"height":140,"x":210,"y":90,"points":["210, 90","650, 230"]}]}', - mapImageUrl: '', + thumbnail: '', sharingMapId: 'JMTGR', }, }; diff --git a/frontend/src/api/managerMap.ts b/frontend/src/api/managerMap.ts index 66156414a..a6f3243c1 100644 --- a/frontend/src/api/managerMap.ts +++ b/frontend/src/api/managerMap.ts @@ -14,14 +14,14 @@ export interface QueryManagerMapParams { interface PostMapParams { mapName: string; mapDrawing: string; - mapImageSvg: string; + thumbnail: string; } interface PutMapParams { mapId: number; mapName: string; mapDrawing: string; - mapImageSvg: string; + thumbnail: string; } interface DeleteMapParams { @@ -53,17 +53,17 @@ export const queryManagerMap: QueryFunction< export const postMap = ({ mapName, mapDrawing, - mapImageSvg, + thumbnail, }: PostMapParams): Promise> => - api.post('/managers/maps', { mapName, mapDrawing, mapImageSvg }); + api.post('/managers/maps', { mapName, mapDrawing, thumbnail }); export const putMap = ({ mapId, mapName, mapDrawing, - mapImageSvg, + thumbnail, }: PutMapParams): Promise> => - api.put(`/managers/maps/${mapId}`, { mapName, mapDrawing, mapImageSvg }); + api.put(`/managers/maps/${mapId}`, { mapName, mapDrawing, thumbnail }); export const deleteMap = ({ mapId }: DeleteMapParams): Promise> => api.delete(`/managers/maps/${mapId}`); diff --git a/frontend/src/api/managerSpace.ts b/frontend/src/api/managerSpace.ts index d5e3cf39d..deafb44ec 100644 --- a/frontend/src/api/managerSpace.ts +++ b/frontend/src/api/managerSpace.ts @@ -21,7 +21,7 @@ export interface PostManagerSpaceParams { description: string; area: string; settingsRequest: ReservationSettings; - mapImageSvg: string; + thumbnail: string; }; } @@ -32,7 +32,7 @@ export interface PutManagerSpaceParams extends PostManagerSpaceParams { export interface DeleteManagerSpaceParams { mapId: number; spaceId: number; - mapImageSvg: string; + thumbnail: string; } export const queryManagerSpaces: QueryFunction< @@ -71,6 +71,6 @@ export const putManagerSpace = ({ export const deleteManagerSpace = ({ mapId, spaceId, - mapImageSvg, + thumbnail, }: DeleteManagerSpaceParams): Promise> => - api.delete(`/managers/maps/${mapId}/spaces/${spaceId}`, { data: { mapImageSvg } }); + api.delete(`/managers/maps/${mapId}/spaces/${spaceId}`, { data: { thumbnail } }); diff --git a/frontend/src/components/MapListItem/MapListItem.stories.tsx b/frontend/src/components/MapListItem/MapListItem.stories.tsx index 75b8dc3a3..a6023ef2e 100644 --- a/frontend/src/components/MapListItem/MapListItem.stories.tsx +++ b/frontend/src/components/MapListItem/MapListItem.stories.tsx @@ -10,10 +10,7 @@ export default { const Template: Story = (args) => ; -const thumbnail = { - src: './images/luther.png', - alt: '루터회관 14F 공간', -}; +const thumbnail = ` `; export const Default = Template.bind({}); Default.args = { diff --git a/frontend/src/components/MapListItem/MapListItem.styles.ts b/frontend/src/components/MapListItem/MapListItem.styles.ts index 3d1246522..897d6ad23 100644 --- a/frontend/src/components/MapListItem/MapListItem.styles.ts +++ b/frontend/src/components/MapListItem/MapListItem.styles.ts @@ -28,11 +28,10 @@ export const ImageInner = styled.div` display: flex; justify-content: center; align-items: center; -`; -export const Image = styled.img` - max-width: 100%; - max-height: 100%; + svg { + z-index: -1; + } `; export const TitleWrapper = styled.div` diff --git a/frontend/src/components/MapListItem/MapListItem.tsx b/frontend/src/components/MapListItem/MapListItem.tsx index 02971cd47..b6bf4956a 100644 --- a/frontend/src/components/MapListItem/MapListItem.tsx +++ b/frontend/src/components/MapListItem/MapListItem.tsx @@ -1,12 +1,8 @@ -import { ReactNode, useState } from 'react'; -import MapDefault from 'assets/images/map-default.jpg'; +import { ReactNode } from 'react'; import * as Styled from './MapListItem.styles'; export interface Props { - thumbnail: { - src: string; - alt: string; - }; + thumbnail: string; title: string; control?: ReactNode; selected?: boolean; @@ -20,23 +16,10 @@ const MapListItem = ({ selected = false, onClick = () => null, }: Props): JSX.Element => { - const [thumbnailSrc, setThumbnailSrc] = useState(thumbnail.src); - - const onImgError = () => { - setThumbnailSrc(MapDefault); - }; - return ( - - - + {title} diff --git a/frontend/src/pages/ManagerMain/units/MapDrawer.tsx b/frontend/src/pages/ManagerMain/units/MapDrawer.tsx index e8a3a67a2..74a925d69 100644 --- a/frontend/src/pages/ManagerMain/units/MapDrawer.tsx +++ b/frontend/src/pages/ManagerMain/units/MapDrawer.tsx @@ -32,11 +32,11 @@ const MapDrawer = ({ {organization} - {maps.map(({ mapId, mapName, mapImageUrl }) => ( + {maps.map(({ mapId, mapName, thumbnail }) => ( onSelectMap(mapId, mapName)} - thumbnail={{ src: mapImageUrl, alt: mapName }} + thumbnail={thumbnail} title={mapName} selected={mapId === selectedMapId} control={ diff --git a/frontend/src/pages/ManagerMapEditor/ManagerMapEditor.tsx b/frontend/src/pages/ManagerMapEditor/ManagerMapEditor.tsx index 9f497811f..d7663f202 100644 --- a/frontend/src/pages/ManagerMapEditor/ManagerMapEditor.tsx +++ b/frontend/src/pages/ManagerMapEditor/ManagerMapEditor.tsx @@ -129,7 +129,7 @@ const ManagerMapEditor = (): JSX.Element => { mapElements: mapElements.map(({ ref, ...props }) => props), }); - const mapImageSvg = createMapImageSvg({ + const thumbnail = createMapImageSvg({ mapElements, spaces, width, @@ -137,12 +137,12 @@ const ManagerMapEditor = (): JSX.Element => { }); if (isEdit) { - updateMap.mutate({ mapId: Number(mapId), mapName: name, mapDrawing, mapImageSvg }); + updateMap.mutate({ mapId: Number(mapId), mapName: name, mapDrawing, thumbnail }); return; } - createMap.mutate({ mapName: name, mapDrawing, mapImageSvg }); + createMap.mutate({ mapName: name, mapDrawing, thumbnail }); }; useListenManagerMainState({ mapId: Number(mapId) }, { enabled: isEdit }); diff --git a/frontend/src/pages/ManagerSpaceEditor/units/Form.tsx b/frontend/src/pages/ManagerSpaceEditor/units/Form.tsx index bde12ddb3..869a9f025 100644 --- a/frontend/src/pages/ManagerSpaceEditor/units/Form.tsx +++ b/frontend/src/pages/ManagerSpaceEditor/units/Form.tsx @@ -67,13 +67,13 @@ const Form = ({ const handleSubmit: FormEventHandler = (event) => { event.preventDefault(); - const mapImageSvg = generateSvg({ ...mapData, spaces: getSpacesForSvg() }); + const thumbnail = generateSvg({ ...mapData, spaces: getSpacesForSvg() }); const valuesForRequest = getRequestValues(); if (selectedSpaceId === null) { onCreateSpace({ space: { - mapImageSvg, + thumbnail, ...valuesForRequest.space, settingsRequest: { ...valuesForRequest.space.settings }, }, @@ -85,7 +85,7 @@ const Form = ({ onUpdateSpace({ spaceId: selectedSpaceId, space: { - mapImageSvg, + thumbnail, ...valuesForRequest.space, settingsRequest: { ...valuesForRequest.space.settings }, }, @@ -97,11 +97,11 @@ const Form = ({ if (!window.confirm(MESSAGE.MANAGER_SPACE.DELETE_SPACE_CONFIRM)) return; const filteredSpaces = spaces.filter(({ id }) => id !== selectedSpaceId); - const mapImageSvg = generateSvg({ ...mapData, spaces: filteredSpaces }); + const thumbnail = generateSvg({ ...mapData, spaces: filteredSpaces }); onDeleteSpace({ spaceId: selectedSpaceId, - mapImageSvg, + thumbnail, }); resetForm(); diff --git a/frontend/src/types/common.ts b/frontend/src/types/common.ts index 0978e0a75..db230c4c5 100644 --- a/frontend/src/types/common.ts +++ b/frontend/src/types/common.ts @@ -40,7 +40,7 @@ export interface MapItem { mapId: number; mapName: string; mapDrawing: MapDrawing; - mapImageUrl: string; + thumbnail: string; sharingMapId: string; } From 7c14352feb0e23289d0b5cb25aa9e200c6baf4aa Mon Sep 17 00:00:00 2001 From: xrabcde Date: Mon, 7 Feb 2022 00:18:10 +0900 Subject: [PATCH 20/24] =?UTF-8?q?feat:=20RequestRejectedException=20?= =?UTF-8?q?=ED=95=B8=EB=93=A4=EB=A7=81=20=EC=BD=94=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#788)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zzimkkong/controller/ControllerAdvice.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java index aa1cf3aa4..ef7896049 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/controller/ControllerAdvice.java @@ -13,21 +13,26 @@ import org.springframework.dao.DataAccessException; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.web.firewall.RequestRejectedException; +import org.springframework.security.web.firewall.RequestRejectedHandler; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.validation.ConstraintViolationException; +import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import static com.woowacourse.zzimkkong.dto.ValidatorMessage.FORMAT_MESSAGE; -import static com.woowacourse.zzimkkong.dto.ValidatorMessage.SERVER_ERROR_MESSAGE; +import static com.woowacourse.zzimkkong.dto.ValidatorMessage.*; import static net.logstash.logback.argument.StructuredArguments.value; @Slf4j @RestControllerAdvice -public class ControllerAdvice { +public class ControllerAdvice implements RequestRejectedHandler { private static final String MESSAGE_FORMAT = "{} (traceId: {})"; private static final String TRACE_ID_KEY = "traceId"; @@ -116,4 +121,8 @@ private Object getTraceId() { return MDC.get(TRACE_ID_KEY); } + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, RequestRejectedException requestRejectedException) throws IOException, ServletException { + response.sendError(HttpServletResponse.SC_BAD_REQUEST); + } } From 97bb6d8c4715082cc6e9d59264db5f1d1455a32a Mon Sep 17 00:00:00 2001 From: Sunny K Date: Wed, 9 Feb 2022 10:04:05 +0900 Subject: [PATCH 21/24] =?UTF-8?q?fix:=20=EC=98=88=EC=95=BD=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EC=9D=B4=2012=EC=8B=9C=EC=9D=B8=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=20=EC=98=88=EC=95=BD=EC=9D=84=20=EC=99=84=EB=A3=8C=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=97=86=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20(#792)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 예약 시간이 오후 1 2시인 경우 예약을 완료할 수 없는 문제 * fix: 예약 시간이 오전 12시 인 경우 예약을 완료할 수 없는 문제 --- frontend/src/utils/datetime.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/src/utils/datetime.ts b/frontend/src/utils/datetime.ts index f898aa431..ff0728075 100644 --- a/frontend/src/utils/datetime.ts +++ b/frontend/src/utils/datetime.ts @@ -21,7 +21,7 @@ export const formatDateWithDay = (value: Date): string => { return `${year}/${month}/${date} (${DAY[day]})`; }; -// Note: HH:MM 형태로 변환함 +// Note: HH:MM (24시간 기준) 형태로 변환함 export const formatTime = (time: Date | Time): string => { if (time instanceof Date) { const hour = time.getHours(); @@ -30,9 +30,14 @@ export const formatTime = (time: Date | Time): string => { return `${hour < 10 ? `0${hour}` : `${hour}`}:${minute < 10 ? `0${minute}` : `${minute}`}`; } - const hour = time.midday === Midday.AM ? `${time.hour}`.padStart(2, '0') : `${time.hour + 12}`; const minute = `${time.minute}`.padStart(2, '0'); + if (time.hour === 12) { + return `${time.midday === Midday.AM ? '00' : '12'}:${minute}`; + } + + const hour = time.midday === Midday.AM ? `${time.hour}`.padStart(2, '0') : `${time.hour + 12}`; + return `${hour}:${minute}`; }; From 0d76d3dc762849f3b1ec39e6380fb0a3d926618c Mon Sep 17 00:00:00 2001 From: Shim MunSeong Date: Thu, 10 Mar 2022 21:14:36 +0900 Subject: [PATCH 22/24] =?UTF-8?q?refactor:=20=ED=83=80=EC=9E=84=EC=A1=B4?= =?UTF-8?q?=20=EA=B4=80=EB=A6=AC=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=9D=B8=ED=95=9C=20API=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91=20=EB=B0=8F=20=ED=83=80=EC=9E=84=EC=A1=B4=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=20(#795)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 타임존 문제 해결을 위한 API 변경에 따른 date 관련 데이터 요청 타입 변경 * chore: `Dayjs` 라이브러리 설치 및 설정 * refactor: 기존 시간 관련 함수에 Dayjs 타입도 처리할 수 있도록 수정 구현 * refactor: Dayjs를 이용하여 타임존이 `Asia/Seoul`로 고정되어 보이도록 수정 * refactor: 삼항 연산자가 중첩되어 읽기 어려운 코드 재작성 * fix: 12시일 때 초 단위가 포함되지 않아 정상적으로 예약되지 않는 문제 수정 * chore: package.json 스크립트에서 TZ env 설정 구문 변경 --- frontend/package.json | 5 +- frontend/src/App.tsx | 7 ++ frontend/src/api/guestReservation.ts | 4 +- frontend/src/api/managerReservation.ts | 8 +-- .../DateInput/DateInput.stories.tsx | 3 +- .../src/components/DateInput/DateInput.tsx | 11 ++-- .../ReservationListItem.tsx | 5 +- frontend/src/constants/date.ts | 1 + frontend/src/hooks/useTimePicker.ts | 23 +++++-- frontend/src/pages/GuestMap/GuestMap.tsx | 7 +- .../GuestMap/units/ReservationDrawer.tsx | 3 +- .../GuestReservation/GuestReservation.tsx | 21 +++--- .../GuestReservationSuccess.tsx | 9 +-- .../units/GuestReservationForm.tsx | 12 +++- .../src/pages/ManagerMain/ManagerMain.tsx | 5 +- .../ManagerReservation/ManagerReservation.tsx | 33 +++++----- .../units/ManagerReservationForm.tsx | 6 +- frontend/src/utils/datetime.ts | 66 +++++++++++++++---- frontend/yarn.lock | 5 ++ 19 files changed, 157 insertions(+), 77 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index ed59e0383..4a33dd3f4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,12 +29,13 @@ "build:dev": "cross-env NODE_ENV=production DEPLOY_ENV=development webpack", "storybook": "start-storybook -s ./src/assets -p 6006", "build-storybook": "build-storybook -s ./src/assets", - "test": "TZ=UTC+9 jest --watch --passWithNoTests", - "test:ci": "TZ=UTC+9 jest --passWithNoTests", + "test": "TZ=Asia/Seoul jest --watch --passWithNoTests", + "test:ci": "TZ=Asia/Seoul jest --passWithNoTests", "prepare": "cd .. && husky install frontend/.husky && cd frontend" }, "dependencies": { "axios": "^0.21.1", + "dayjs": "^1.10.7", "react": "^17.0.2", "react-dom": "^17.0.2", "react-query": "^3.18.1", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 748a7abaa..6f9ef17e9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,3 +1,6 @@ +import dayjs from 'dayjs'; +import timezone from 'dayjs/plugin/timezone'; +import utc from 'dayjs/plugin/utc'; import { createBrowserHistory } from 'history'; import { Suspense } from 'react'; import { QueryClient, QueryClientProvider } from 'react-query'; @@ -12,6 +15,10 @@ import AccessTokenProvider from 'providers/AccessTokenProvider'; import { GlobalStyle, theme } from './App.styles'; import NotFound from './pages/NotFound/NotFound'; +dayjs.extend(utc); +dayjs.extend(timezone); +dayjs.tz.setDefault('Asia/Seoul'); + export const history = createBrowserHistory(); export const queryClient = new QueryClient(); diff --git a/frontend/src/api/guestReservation.ts b/frontend/src/api/guestReservation.ts index cc3c6febf..b4cd62bce 100644 --- a/frontend/src/api/guestReservation.ts +++ b/frontend/src/api/guestReservation.ts @@ -15,8 +15,8 @@ export interface QuerySpaceReservationsParams extends QueryMapReservationsParams export interface ReservationParams { reservation: { - startDateTime: Date; - endDateTime: Date; + startDateTime: string; + endDateTime: string; password: string; name: string; description: string; diff --git a/frontend/src/api/managerReservation.ts b/frontend/src/api/managerReservation.ts index e93c631ba..fb1f44ae4 100644 --- a/frontend/src/api/managerReservation.ts +++ b/frontend/src/api/managerReservation.ts @@ -19,8 +19,8 @@ export interface PostReservationParams { mapId: number; spaceId: number; reservation: { - startDateTime: Date; - endDateTime: Date; + startDateTime: string; + endDateTime: string; name: string; description: string; password: string; @@ -32,8 +32,8 @@ export interface PutReservationParams { spaceId: number; reservationId: number; reservation: { - startDateTime: Date; - endDateTime: Date; + startDateTime: string; + endDateTime: string; name: string; description: string; }; diff --git a/frontend/src/components/DateInput/DateInput.stories.tsx b/frontend/src/components/DateInput/DateInput.stories.tsx index dd1561ec5..379df812e 100644 --- a/frontend/src/components/DateInput/DateInput.stories.tsx +++ b/frontend/src/components/DateInput/DateInput.stories.tsx @@ -1,4 +1,5 @@ import { Story } from '@storybook/react'; +import dayjs from 'dayjs'; import DateInput, { Props } from './DateInput'; export default { @@ -10,5 +11,5 @@ const Template: Story = (args) => ; export const Default = Template.bind({}); Default.args = { - date: new Date(), + date: dayjs().tz(), }; diff --git a/frontend/src/components/DateInput/DateInput.tsx b/frontend/src/components/DateInput/DateInput.tsx index 09d85bf96..ce9c81f1a 100644 --- a/frontend/src/components/DateInput/DateInput.tsx +++ b/frontend/src/components/DateInput/DateInput.tsx @@ -1,24 +1,25 @@ +import dayjs, { Dayjs } from 'dayjs'; import { ChangeEventHandler, Dispatch, InputHTMLAttributes, SetStateAction } from 'react'; import IconButton from 'components/IconButton/IconButton'; import { formatDate, formatDateWithDay } from 'utils/datetime'; import * as Styled from './DateInput.styles'; export interface Props extends Omit, 'type' | 'value'> { - date: Date; - setDate: Dispatch>; + date: Dayjs; + setDate: Dispatch>; } const DateInput = ({ date, setDate, ...props }: Props): JSX.Element => { const onChange: ChangeEventHandler = (event) => { - setDate(new Date(event.target.value)); + setDate(dayjs(event.target.value).tz()); }; const onClickPrev = () => { - setDate(new Date(date.setDate(date.getDate() - 1))); + setDate(dayjs(date).subtract(1, 'day')); }; const onClickNext = () => { - setDate(new Date(date.setDate(date.getDate() + 1))); + setDate(dayjs(date).add(1, 'day')); }; return ( diff --git a/frontend/src/components/ReservationListItem/ReservationListItem.tsx b/frontend/src/components/ReservationListItem/ReservationListItem.tsx index ada608f1f..fc18aa890 100644 --- a/frontend/src/components/ReservationListItem/ReservationListItem.tsx +++ b/frontend/src/components/ReservationListItem/ReservationListItem.tsx @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import { ReactNode } from 'react'; import { Reservation, ReservationStatus } from 'types/common'; import { formatTime } from 'utils/datetime'; @@ -12,8 +13,8 @@ export interface Props { const ReservationListItem = ({ reservation, control, status, ...props }: Props): JSX.Element => { const { name, description, startDateTime, endDateTime } = reservation; - const start = formatTime(new Date(startDateTime)); - const end = formatTime(new Date(endDateTime)); + const start = formatTime(dayjs(startDateTime).tz()); + const end = formatTime(dayjs(endDateTime).tz()); return ( diff --git a/frontend/src/constants/date.ts b/frontend/src/constants/date.ts index ba0024931..2b8f0a2e1 100644 --- a/frontend/src/constants/date.ts +++ b/frontend/src/constants/date.ts @@ -3,6 +3,7 @@ const DATE = { MIN_DATE_STRING: '2000-01-01', MAX_DATE: new Date('2100-12-31'), MAX_DATE_STRING: '2100-12-31', + TIMEZONE_OFFSET: '+09:00', }; export default DATE; diff --git a/frontend/src/hooks/useTimePicker.ts b/frontend/src/hooks/useTimePicker.ts index 192c455b0..6b5e64b22 100644 --- a/frontend/src/hooks/useTimePicker.ts +++ b/frontend/src/hooks/useTimePicker.ts @@ -1,3 +1,4 @@ +import dayjs, { Dayjs, isDayjs } from 'dayjs'; import { MouseEventHandler, useState } from 'react'; import { Step } from 'components/TimePicker/TimePicker'; import { Midday, Range, Time } from 'types/time'; @@ -16,9 +17,19 @@ const generateTo12Hour = (hour: number) => { return result === 0 ? 12 : result; }; -const generateDateToTime = (time: Date, step: Props['step'] = 1): Time => { - const minute = Math.ceil(time.getMinutes() / step) * step; - const hour = minute < 60 ? time.getHours() : time.getHours() + 1; +const generateDateToTime = (time: Date | Dayjs, step: Props['step'] = 1): Time => { + const minute = isDayjs(time) + ? Math.ceil(time.minute() / step) * step + : Math.ceil(time.getMinutes() / step) * step; + + const hour = (() => { + if (isDayjs(time)) { + return minute < 60 ? time.hour() : time.hour() + 1; + } + + return minute < 60 ? time.getHours() : time.getHours() + 1; + })(); + const midday = hour < 12 ? Midday.AM : Midday.PM; return { @@ -41,13 +52,13 @@ const useTimePicker = ({ } => { const [selectedTime, setSelectedTime] = useState(null); const [range, setRange] = useState({ - start: initialStartTime ? generateDateToTime(initialStartTime, step) : null, - end: initialEndTime ? generateDateToTime(initialEndTime, step) : null, + start: initialStartTime ? generateDateToTime(dayjs(initialStartTime).tz(), step) : null, + end: initialEndTime ? generateDateToTime(dayjs(initialEndTime).tz(), step) : null, }); const setInitialTime = (key: keyof Range) => { if (key === 'start' || (key === 'end' && range.start === null)) { - const now = new Date(); + const now = dayjs().tz(); setRange((prev) => ({ ...prev, diff --git a/frontend/src/pages/GuestMap/GuestMap.tsx b/frontend/src/pages/GuestMap/GuestMap.tsx index 4ec95d4e1..e864175fe 100644 --- a/frontend/src/pages/GuestMap/GuestMap.tsx +++ b/frontend/src/pages/GuestMap/GuestMap.tsx @@ -1,4 +1,5 @@ import { AxiosError } from 'axios'; +import dayjs, { Dayjs } from 'dayjs'; import { FormEventHandler, useEffect, useMemo, useRef, useState } from 'react'; import { useMutation } from 'react-query'; import { useHistory, useLocation, useParams } from 'react-router-dom'; @@ -29,7 +30,7 @@ import ReservationDrawer from './units/ReservationDrawer'; export interface GuestMapState { spaceId?: Space['id']; - targetDate?: Date; + targetDate?: Dayjs; scrollPosition?: ScrollPosition; } @@ -76,7 +77,7 @@ const GuestMap = (): JSX.Element => { const [spaceList, setSpaceList] = useState([]); const [selectedSpaceId, setSelectedSpaceId] = useState(spaceId ?? null); - const [date, setDate] = useState(targetDate ? new Date(targetDate) : new Date()); + const [date, setDate] = useState(targetDate ? dayjs(targetDate).tz() : dayjs().tz()); const spaces = useMemo(() => { const result: { [key: string]: Space } = {}; @@ -186,7 +187,7 @@ const GuestMap = (): JSX.Element => { useEffect(() => { if (targetDate) { - setDate(new Date(targetDate)); + setDate(dayjs(targetDate).tz()); } }, [targetDate]); diff --git a/frontend/src/pages/GuestMap/units/ReservationDrawer.tsx b/frontend/src/pages/GuestMap/units/ReservationDrawer.tsx index 0842dca7f..45116f728 100644 --- a/frontend/src/pages/GuestMap/units/ReservationDrawer.tsx +++ b/frontend/src/pages/GuestMap/units/ReservationDrawer.tsx @@ -1,3 +1,4 @@ +import { Dayjs } from 'dayjs'; import { ReactComponent as DeleteIcon } from 'assets/svg/delete.svg'; import { ReactComponent as EditIcon } from 'assets/svg/edit.svg'; import Drawer from 'components/Drawer/Drawer'; @@ -11,7 +12,7 @@ import * as Styled from './ReservationDrawer.styles'; interface Props { reservations: Reservation[]; space: Space; - date: Date; + date: Dayjs; open: boolean; isSuccess: boolean; isLoadingError: boolean; diff --git a/frontend/src/pages/GuestReservation/GuestReservation.tsx b/frontend/src/pages/GuestReservation/GuestReservation.tsx index 6cc3a3280..384851199 100644 --- a/frontend/src/pages/GuestReservation/GuestReservation.tsx +++ b/frontend/src/pages/GuestReservation/GuestReservation.tsx @@ -1,4 +1,5 @@ import { AxiosError } from 'axios'; +import dayjs from 'dayjs'; import React, { useEffect } from 'react'; import { useMutation } from 'react-query'; import { useHistory, useLocation, useParams } from 'react-router-dom'; @@ -51,7 +52,7 @@ const GuestReservation = (): JSX.Element => { const getReservations = useGuestReservations( { mapId, spaceId: space.id, date }, { - enabled: !isPastDate(new Date(date), DATE.MIN_DATE) && !!date, + enabled: !isPastDate(dayjs(date).tz(), DATE.MIN_DATE) && !!date, } ); const reservations = getReservations.data?.data?.reservations ?? []; @@ -70,7 +71,7 @@ const GuestReservation = (): JSX.Element => { name, description, }, - targetDate: new Date(date), + targetDate: dayjs(date).tz(), }, }); }, @@ -85,7 +86,7 @@ const GuestReservation = (): JSX.Element => { pathname: HREF.GUEST_MAP(sharingMapId), state: { spaceId: space.id, - targetDate: new Date(date), + targetDate: dayjs(date).tz(), }, }); }, @@ -121,12 +122,12 @@ const GuestReservation = (): JSX.Element => { target: { value }, } = event; - if (isPastDate(new Date(date), DATE.MIN_DATE)) { + if (isPastDate(dayjs(date).tz(), DATE.MIN_DATE)) { setDate(DATE.MIN_DATE_STRING); return; } - if (isFutureDate(new Date(date), DATE.MAX_DATE)) { + if (isFutureDate(dayjs(date).tz(), DATE.MAX_DATE)) { setDate(DATE.MAX_DATE_STRING); return; } @@ -153,7 +154,7 @@ const GuestReservation = (): JSX.Element => { ) { location.state = { spaceId: space.id, - targetDate: new Date(date), + targetDate: dayjs(date).tz(), scrollPosition, }; } @@ -187,16 +188,16 @@ const GuestReservation = (): JSX.Element => { )} {getReservations.isSuccess && reservations.length === 0 && - !isPastDate(new Date(date)) && ( + !isPastDate(dayjs(date).tz()) && ( {MESSAGE.RESERVATION.SUGGESTION} )} {getReservations.isSuccess && reservations.length === 0 && - isPastDate(new Date(date)) && ( + isPastDate(dayjs(date).tz()) && ( {MESSAGE.RESERVATION.NOT_EXIST} )} - {(isPastDate(new Date(date), DATE.MIN_DATE) || - isFutureDate(new Date(date), DATE.MAX_DATE)) && ( + {(isPastDate(dayjs(date).tz(), DATE.MIN_DATE) || + isFutureDate(dayjs(date).tz(), DATE.MAX_DATE)) && ( {MESSAGE.RESERVATION.NOT_EXIST} )} {getReservations.isSuccess && reservations.length > 0 && ( diff --git a/frontend/src/pages/GuestReservation/GuestReservationSuccess.tsx b/frontend/src/pages/GuestReservation/GuestReservationSuccess.tsx index bca9ee792..a641961df 100644 --- a/frontend/src/pages/GuestReservation/GuestReservationSuccess.tsx +++ b/frontend/src/pages/GuestReservation/GuestReservationSuccess.tsx @@ -1,3 +1,4 @@ +import dayjs from 'dayjs'; import { Redirect, useLocation, useParams } from 'react-router'; import AnimatedLogo from 'components/AnimatedLogo/AnimatedLogo'; import Header from 'components/Header/Header'; @@ -14,8 +15,8 @@ export interface GuestReservationSuccessState { reservation: { name: string; description: string; - startDateTime: Date; - endDateTime: Date; + startDateTime: string; + endDateTime: string; }; } @@ -27,8 +28,8 @@ const GuestReservationSuccess = (): JSX.Element => { const { space, reservation, targetDate } = location.state; - const startDateTimeObject = new Date(reservation.startDateTime.toISOString().slice(0, -1)); - const endDateTimeObject = new Date(reservation.endDateTime.toISOString().slice(0, -1)); + const startDateTimeObject = dayjs(reservation.startDateTime).tz(); + const endDateTimeObject = dayjs(reservation.endDateTime).tz(); const reservationDate = formatDateWithDay(startDateTimeObject); const reservationStartTime = formatTime(startDateTimeObject); diff --git a/frontend/src/pages/GuestReservation/units/GuestReservationForm.tsx b/frontend/src/pages/GuestReservation/units/GuestReservationForm.tsx index fb0a2de45..a8f85ada7 100644 --- a/frontend/src/pages/GuestReservation/units/GuestReservationForm.tsx +++ b/frontend/src/pages/GuestReservation/units/GuestReservationForm.tsx @@ -10,7 +10,13 @@ import useInputs from 'hooks/useInputs'; import useScrollToTop from 'hooks/useScrollToTop'; import useTimePicker from 'hooks/useTimePicker'; import { Reservation, Space } from 'types/common'; -import { formatDate, formatTime, formatTimePrettier, isPastDate } from 'utils/datetime'; +import { + formatDate, + formatTime, + formatTimePrettier, + formatTimeWithSecond, + isPastDate, +} from 'utils/datetime'; import { EditReservationParams } from '../GuestReservation'; import * as Styled from './GuestReservationForm.styles'; @@ -66,8 +72,8 @@ const GuestReservationForm = ({ if (range.start === null || range.end === null) return; - const startDateTime = new Date(`${date}T${formatTime(range.start)}Z`); - const endDateTime = new Date(`${date}T${formatTime(range.end)}Z`); + const startDateTime = `${date}T${formatTimeWithSecond(range.start)}${DATE.TIMEZONE_OFFSET}`; + const endDateTime = `${date}T${formatTimeWithSecond(range.end)}${DATE.TIMEZONE_OFFSET}`; onSubmit(event, { reservation: { diff --git a/frontend/src/pages/ManagerMain/ManagerMain.tsx b/frontend/src/pages/ManagerMain/ManagerMain.tsx index a500d0824..331852904 100644 --- a/frontend/src/pages/ManagerMain/ManagerMain.tsx +++ b/frontend/src/pages/ManagerMain/ManagerMain.tsx @@ -1,4 +1,5 @@ import { AxiosError } from 'axios'; +import dayjs, { Dayjs } from 'dayjs'; import React, { useEffect, useMemo, useState } from 'react'; import { useMutation } from 'react-query'; import { useHistory, useLocation } from 'react-router-dom'; @@ -33,7 +34,7 @@ import ReservationList from './units/ReservationList'; export interface ManagerMainState { mapId?: number; - targetDate?: Date; + targetDate?: Dayjs; } const ManagerMain = (): JSX.Element => { @@ -43,7 +44,7 @@ const ManagerMain = (): JSX.Element => { const mapId = location.state?.mapId; const targetDate = location.state?.targetDate; - const [date, setDate] = useState(targetDate ?? new Date()); + const [date, setDate] = useState(targetDate ?? dayjs().tz()); const [open, setOpen] = useState(false); const [slackModalOpen, setSlackModalOpen] = useState(false); diff --git a/frontend/src/pages/ManagerReservation/ManagerReservation.tsx b/frontend/src/pages/ManagerReservation/ManagerReservation.tsx index 8b3c23da7..08cfafbaa 100644 --- a/frontend/src/pages/ManagerReservation/ManagerReservation.tsx +++ b/frontend/src/pages/ManagerReservation/ManagerReservation.tsx @@ -1,4 +1,5 @@ import { AxiosError } from 'axios'; +import dayjs from 'dayjs'; import { useEffect } from 'react'; import { useMutation } from 'react-query'; import { useHistory, useLocation } from 'react-router-dom'; @@ -50,7 +51,7 @@ const ManagerReservation = (): JSX.Element => { const getReservations = useManagerSpaceReservations( { mapId, spaceId: space.id, date }, { - enabled: !isPastDate(new Date(date), DATE.MIN_DATE) && !!date, + enabled: !isPastDate(dayjs(date), DATE.MIN_DATE) && !!date, } ); const reservations = getReservations.data?.data?.reservations ?? []; @@ -61,7 +62,7 @@ const ManagerReservation = (): JSX.Element => { pathname: PATH.MANAGER_MAIN, state: { mapId, - targetDate: new Date(date), + targetDate: dayjs(date), }, }); }, @@ -76,7 +77,7 @@ const ManagerReservation = (): JSX.Element => { pathname: PATH.MANAGER_MAIN, state: { mapId, - targetDate: new Date(date), + targetDate: dayjs(date), }, }); }, @@ -112,12 +113,12 @@ const ManagerReservation = (): JSX.Element => { target: { value }, } = event; - if (isPastDate(new Date(date), DATE.MIN_DATE)) { + if (isPastDate(dayjs(date), DATE.MIN_DATE)) { setDate(DATE.MIN_DATE_STRING); return; } - if (isFutureDate(new Date(date), DATE.MAX_DATE)) { + if (isFutureDate(dayjs(date), DATE.MAX_DATE)) { setDate(DATE.MAX_DATE_STRING); return; } @@ -133,7 +134,7 @@ const ManagerReservation = (): JSX.Element => { ) { location.state = { mapId, - targetDate: new Date(date), + targetDate: dayjs(date), }; } }); @@ -165,18 +166,14 @@ const ManagerReservation = (): JSX.Element => { {getReservations.isLoading && !getReservations.isLoadingError && ( {MESSAGE.RESERVATION.PENDING} )} - {getReservations.isSuccess && - reservations.length === 0 && - !isPastDate(new Date(date)) && ( - {MESSAGE.RESERVATION.SUGGESTION} - )} - {getReservations.isSuccess && - reservations.length === 0 && - isPastDate(new Date(date)) && ( - {MESSAGE.RESERVATION.NOT_EXIST} - )} - {(isPastDate(new Date(date), DATE.MIN_DATE) || - isFutureDate(new Date(date), DATE.MAX_DATE)) && ( + {getReservations.isSuccess && reservations.length === 0 && !isPastDate(dayjs(date)) && ( + {MESSAGE.RESERVATION.SUGGESTION} + )} + {getReservations.isSuccess && reservations.length === 0 && isPastDate(dayjs(date)) && ( + {MESSAGE.RESERVATION.NOT_EXIST} + )} + {(isPastDate(dayjs(date), DATE.MIN_DATE) || + isFutureDate(dayjs(date), DATE.MAX_DATE)) && ( {MESSAGE.RESERVATION.NOT_EXIST} )} {getReservations.isSuccess && reservations.length > 0 && ( diff --git a/frontend/src/pages/ManagerReservation/units/ManagerReservationForm.tsx b/frontend/src/pages/ManagerReservation/units/ManagerReservationForm.tsx index f591ade31..3fb2cdac9 100644 --- a/frontend/src/pages/ManagerReservation/units/ManagerReservationForm.tsx +++ b/frontend/src/pages/ManagerReservation/units/ManagerReservationForm.tsx @@ -11,7 +11,7 @@ import useInputs from 'hooks/useInputs'; import useScrollToTop from 'hooks/useScrollToTop'; import useTimePicker from 'hooks/useTimePicker'; import { ManagerSpaceAPI, Reservation } from 'types/common'; -import { formatDate, formatTime, formatTimePrettier } from 'utils/datetime'; +import { formatDate, formatTime, formatTimePrettier, formatTimeWithSecond } from 'utils/datetime'; import { CreateReservationParams, EditReservationParams } from '../ManagerReservation'; import * as Styled from './ManagerReservationForm.styles'; @@ -67,8 +67,8 @@ const ManagerReservationForm = ({ if (range.start === null || range.end === null) return; - const startDateTime = new Date(`${date}T${formatTime(range.start)}Z`); - const endDateTime = new Date(`${date}T${formatTime(range.end)}Z`); + const startDateTime = `${date}T${formatTimeWithSecond(range.start)}${DATE.TIMEZONE_OFFSET}`; + const endDateTime = `${date}T${formatTimeWithSecond(range.end)}${DATE.TIMEZONE_OFFSET}`; if (!reservation) { onCreateReservation({ diff --git a/frontend/src/utils/datetime.ts b/frontend/src/utils/datetime.ts index ff0728075..4703b3593 100644 --- a/frontend/src/utils/datetime.ts +++ b/frontend/src/utils/datetime.ts @@ -1,7 +1,12 @@ +import dayjs, { Dayjs, isDayjs } from 'dayjs'; import { Midday, Time } from 'types/time'; // Note: YYYY-MM-DD 형식으로 변환함 -export const formatDate = (value: Date): string => { +export const formatDate = (value: Date | Dayjs): string => { + if (isDayjs(value)) { + return value.format('YYYY-MM-DD'); + } + const year = value.getFullYear(); const month = `${value.getMonth() + 1}`.padStart(2, '0'); const date = `${value.getDate()}`.padStart(2, '0'); @@ -12,7 +17,11 @@ export const formatDate = (value: Date): string => { const DAY = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT']; // Note: YYYY/MM/DD (MON) 형식으로 변환함 -export const formatDateWithDay = (value: Date): string => { +export const formatDateWithDay = (value: Date | Dayjs): string => { + if (isDayjs(value)) { + return value.format('YYYY-MM-DD (ddd)').toUpperCase(); + } + const year = value.getFullYear(); const month = `${value.getMonth() + 1}`.padStart(2, '0'); const date = `${value.getDate()}`.padStart(2, '0'); @@ -22,7 +31,11 @@ export const formatDateWithDay = (value: Date): string => { }; // Note: HH:MM (24시간 기준) 형태로 변환함 -export const formatTime = (time: Date | Time): string => { +export const formatTime = (time: Date | Time | Dayjs): string => { + if (isDayjs(time)) { + return time.format('HH:mm'); + } + if (time instanceof Date) { const hour = time.getHours(); const minute = time.getMinutes(); @@ -41,11 +54,30 @@ export const formatTime = (time: Date | Time): string => { return `${hour}:${minute}`; }; -// Note: HH:MM:SS 형태로 변환함 -export const formatTimeWithSecond = (time: Date): string => { - const hour = `${time.getHours()}`.padStart(2, '0'); - const minute = `${time.getMinutes()}`.padStart(2, '0'); - const second = `${time.getSeconds()}`.padStart(2, '0'); +// Note: HH:MM:SS (24시간 기준) 형태로 변환함 +export const formatTimeWithSecond = (time: Date | Time | Dayjs): string => { + if (isDayjs(time)) { + return time.format('HH:mm:ss'); + } + + if (time instanceof Date) { + const hour = time.getHours(); + const minute = time.getMinutes(); + const second = `${time.getSeconds()}`.padStart(2, '0'); + + return `${hour < 10 ? `0${hour}` : `${hour}`}:${ + minute < 10 ? `0${minute}` : `${minute}` + }:${second}`; + } + + const minute = `${time.minute}`.padStart(2, '0'); + const second = '00'; + + if (time.hour === 12) { + return `${time.midday === Midday.AM ? '00' : '12'}:${minute}:${second}`; + } + + const hour = time.midday === Midday.AM ? `${time.hour}`.padStart(2, '0') : `${time.hour + 12}`; return `${hour}:${minute}:${second}`; }; @@ -58,14 +90,26 @@ export const formatTimePrettier = (minutes: number): string => { return `${hour ? `${hour}시간` : ''}${minute ? ' ' : ''}${minute ? `${minute}분` : ''}`; }; -export const isPastTime = (time: Date, baseDate: Date = new Date()): boolean => { +export const isPastTime = (time: Date | Dayjs, baseDate: Date = new Date()): boolean => { + if (isDayjs(time)) { + return +time < +dayjs(baseDate).tz(); + } + return time.getTime() < baseDate.getTime(); }; -export const isPastDate = (time: Date, baseDate: Date = new Date()): boolean => { +export const isPastDate = (time: Date | Dayjs, baseDate: Date = new Date()): boolean => { + if (isDayjs(time)) { + return +time < +dayjs(baseDate).tz() - 1000 * 60 * 60 * 24; + } + return time.getTime() < baseDate.getTime() - 1000 * 60 * 60 * 24; }; -export const isFutureDate = (time: Date, baseDate: Date = new Date()): boolean => { +export const isFutureDate = (time: Date | Dayjs, baseDate: Date = new Date()): boolean => { + if (isDayjs(time)) { + return +time > +dayjs(baseDate).tz() + 1000 * 60 * 60 * 24; + } + return time.getTime() > baseDate.getTime() + 1000 * 60 * 60 * 24; }; diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 1fe14b0dd..e175ea737 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -5899,6 +5899,11 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +dayjs@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" From d472a797d102360f7c25c3fff7dea87b2ffd6493 Mon Sep 17 00:00:00 2001 From: Jungseok Sung <58401309+sakjung@users.noreply.github.com> Date: Wed, 27 Apr 2022 23:25:59 +0900 Subject: [PATCH 23/24] =?UTF-8?q?refactor:=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EB=82=B4=EB=B6=80=EC=9D=98=20=EB=AA=A8=EB=93=A0=20DateTime=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EB=93=A4=EC=9D=84=20UTC=EB=A1=9C=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=84=B0=EB=A7=81=20(#793)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: reservation 관련 DTO들에서 datetime 관련 데이터들이 timezone 정보를 갖도록 리팩터링 * chore: indent 컨벤션에 맞게 리포맷팅 * fix: 날짜 관련 로직이 timezone을 고려하도록 수정 * chore: 코드 리포맷팅 및 코드 컨벤션에 맞게 리팩터링 * fix: slack 알람 송신 시 KST 시간대로 바꿔서 보내도록 수정 --- .../zzimkkong/ZzimkkongApplication.java | 4 +- .../zzimkkong/domain/Reservation.java | 10 ++ .../zzimkkong/dto/ValidatorMessage.java | 2 +- .../dto/reservation/ReservationCreateDto.java | 4 +- .../ReservationCreateUpdateRequest.java | 36 ++-- ...vationCreateUpdateWithPasswordRequest.java | 6 +- .../dto/reservation/ReservationResponse.java | 11 +- .../zzimkkong/dto/slack/SlackResponse.java | 14 +- .../datetime/TimeZoneUtils.java | 19 +++ .../infrastructure/warmup/Warmer.java | 0 .../repository/ReservationRepository.java | 5 +- .../zzimkkong/service/ReservationService.java | 34 +++- .../com/woowacourse/zzimkkong/Constants.java | 20 ++- .../zzimkkong/controller/AcceptanceTest.java | 14 +- .../controller/AdminControllerTest.java | 10 +- .../GuestReservationControllerTest.java | 89 +++++----- .../ManagerReservationControllerTest.java | 85 ++++----- .../ReservationCreateUpdateRequestTest.java | 28 +-- ...onCreateUpdateWithPasswordRequestTest.java | 9 +- .../infrastructure/auth/JwtUtilsTest.java | 1 - .../ReservationRepositoryImplTest.java | 8 +- .../repository/ReservationRepositoryTest.java | 76 ++++----- .../zzimkkong/service/AdminServiceTest.java | 7 +- .../service/GuestReservationServiceTest.java | 148 ++++++++-------- .../ManagerReservationServiceTest.java | 161 ++++++++++-------- 25 files changed, 462 insertions(+), 339 deletions(-) create mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/datetime/TimeZoneUtils.java delete mode 100644 backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/ZzimkkongApplication.java b/backend/src/main/java/com/woowacourse/zzimkkong/ZzimkkongApplication.java index e45a1d105..f1902de5e 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/ZzimkkongApplication.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/ZzimkkongApplication.java @@ -5,10 +5,12 @@ import java.util.TimeZone; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; + @SpringBootApplication public class ZzimkkongApplication { public static void main(String[] args) { - TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul")); + TimeZone.setDefault(UTC); SpringApplication.run(ZzimkkongApplication.class, args); } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java index 49ba5d473..e01fe8539 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/domain/Reservation.java @@ -1,5 +1,6 @@ package com.woowacourse.zzimkkong.domain; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -7,6 +8,9 @@ import javax.persistence.*; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.TimeZone; + +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; @Getter @Builder @@ -87,4 +91,10 @@ public void update(final Reservation updateReservation, final Space space) { public boolean isWrongPassword(final String password) { return !this.password.equals(password); } + + public boolean isBookedOn(final LocalDate date, final TimeZone timeZone) { + LocalDate convertedStartTimeDate = TimeZoneUtils.convert(startTime, UTC, timeZone).toLocalDate(); + LocalDate convertedEndTimeDate = TimeZoneUtils.convert(endTime, UTC, timeZone).toLocalDate(); + return date.equals(convertedStartTimeDate) && date.equals(convertedEndTimeDate); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/ValidatorMessage.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/ValidatorMessage.java index 77038eccb..1d11c406f 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/ValidatorMessage.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/ValidatorMessage.java @@ -19,7 +19,7 @@ private ValidatorMessage() { public static final String DATE_FORMAT = "yyyy-MM-dd"; public static final String TIME_FORMAT = "HH:mm:ss"; - public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + public static final String DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ssxxx"; public static final String MEMBER_PW_FORMAT = "^(?=.*[a-zA-Z])(?=.*[0-9]).{8,20}$"; public static final String RESERVATION_PW_FORMAT = "^[0-9]{4}$"; public static final String ORGANIZATION_FORMAT = "^[ a-zA-Z0-9ㄱ-힣]{1,20}$"; diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateDto.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateDto.java index ec305aa02..27e202b7d 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateDto.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateDto.java @@ -25,8 +25,8 @@ protected ReservationCreateDto( final LoginEmailDto loginEmailDto) { this.mapId = mapId; this.spaceId = spaceId; - this.startDateTime = request.getStartDateTime(); - this.endDateTime = request.getEndDateTime(); + this.startDateTime = request.localStartDateTime(); + this.endDateTime = request.localEndDateTime(); this.password = request.getPassword(); this.name = request.getName(); this.description = request.getDescription(); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateRequest.java index 98edaf43e..080927444 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateRequest.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateRequest.java @@ -1,27 +1,31 @@ package com.woowacourse.zzimkkong.dto.reservation; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import javax.validation.constraints.Size; import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.util.TimeZone; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; @Getter @NoArgsConstructor public class ReservationCreateUpdateRequest { - @DateTimeFormat(pattern = DATETIME_FORMAT) + @JsonFormat(pattern = DATETIME_FORMAT) @NotNull(message = EMPTY_MESSAGE) - protected LocalDateTime startDateTime; + protected ZonedDateTime startDateTime; - @DateTimeFormat(pattern = DATETIME_FORMAT) + @JsonFormat(pattern = DATETIME_FORMAT) @NotNull(message = EMPTY_MESSAGE) - protected LocalDateTime endDateTime; + protected ZonedDateTime endDateTime; @NotBlank(message = EMPTY_MESSAGE) @Pattern(regexp = NAMING_FORMAT, message = NAME_MESSAGE) @@ -31,13 +35,13 @@ public class ReservationCreateUpdateRequest { @Size(max = 100, message = DESCRIPTION_MESSAGE) protected String description; - public ReservationCreateUpdateRequest( - final LocalDateTime startDateTime, - final LocalDateTime endDateTime, - final String name, - final String description) { - this.startDateTime = startDateTime; - this.endDateTime = endDateTime; + public ReservationCreateUpdateRequest(ZonedDateTime startDateTime, ZonedDateTime endDateTime, String name, String description) { + if (startDateTime != null) { + this.startDateTime = startDateTime.withZoneSameInstant(UTC.toZoneId()); + } + if (endDateTime != null) { + this.endDateTime = endDateTime.withZoneSameInstant(UTC.toZoneId()); + } this.name = name; this.description = description; } @@ -45,4 +49,12 @@ public ReservationCreateUpdateRequest( public String getPassword() { return null; } + + public LocalDateTime localStartDateTime() { + return startDateTime.toLocalDateTime(); + } + + public LocalDateTime localEndDateTime() { + return endDateTime.toLocalDateTime(); + } } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateWithPasswordRequest.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateWithPasswordRequest.java index ca9a0c63a..84012a85f 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateWithPasswordRequest.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationCreateUpdateWithPasswordRequest.java @@ -5,7 +5,7 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; -import java.time.LocalDateTime; +import java.time.ZonedDateTime; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.*; @@ -17,8 +17,8 @@ public class ReservationCreateUpdateWithPasswordRequest extends ReservationCreat private String password; public ReservationCreateUpdateWithPasswordRequest( - final LocalDateTime startDateTime, - final LocalDateTime endDateTime, + final ZonedDateTime startDateTime, + final ZonedDateTime endDateTime, final String password, final String name, final String description) { diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationResponse.java index acbc88a9b..cc0003c12 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationResponse.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/reservation/ReservationResponse.java @@ -6,12 +6,15 @@ import com.woowacourse.zzimkkong.domain.Member; import com.woowacourse.zzimkkong.domain.Reservation; import com.woowacourse.zzimkkong.domain.Space; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import lombok.Getter; import lombok.NoArgsConstructor; import java.time.LocalDateTime; +import java.time.ZonedDateTime; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.DATETIME_FORMAT; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; @Getter @NoArgsConstructor @@ -19,9 +22,9 @@ public class ReservationResponse { @JsonProperty private Long id; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DATETIME_FORMAT) - private LocalDateTime startDateTime; + private ZonedDateTime startDateTime; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = DATETIME_FORMAT) - private LocalDateTime endDateTime; + private ZonedDateTime endDateTime; @JsonProperty private String name; @JsonProperty @@ -38,8 +41,8 @@ private ReservationResponse( final String name, final String description) { this.id = id; - this.startDateTime = startDateTime; - this.endDateTime = endDateTime; + this.startDateTime = startDateTime.atZone(UTC.toZoneId()); + this.endDateTime = endDateTime.atZone(UTC.toZoneId()); this.name = name; this.description = description; } diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java b/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java index a88d4229c..d3c5780ed 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/dto/slack/SlackResponse.java @@ -1,6 +1,7 @@ package com.woowacourse.zzimkkong.dto.slack; import com.woowacourse.zzimkkong.domain.Reservation; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import lombok.Getter; import lombok.NoArgsConstructor; @@ -33,11 +34,20 @@ private SlackResponse( } public static SlackResponse of(final Reservation reservation, final String sharingMapId, final String slackUrl) { + LocalDateTime reservationStartTimeKST = TimeZoneUtils.convert( + reservation.getStartTime(), + TimeZoneUtils.UTC, + TimeZoneUtils.KST); + LocalDateTime reservationEndTimeKST = TimeZoneUtils.convert( + reservation.getEndTime(), + TimeZoneUtils.UTC, + TimeZoneUtils.KST); + return new SlackResponse( reservation.getSpace().getName(), reservation.getUserName(), - reservation.getStartTime(), - reservation.getEndTime(), + reservationStartTimeKST, + reservationEndTimeKST, reservation.getDescription(), sharingMapId, slackUrl); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/datetime/TimeZoneUtils.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/datetime/TimeZoneUtils.java new file mode 100644 index 000000000..3cae39f5e --- /dev/null +++ b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/datetime/TimeZoneUtils.java @@ -0,0 +1,19 @@ +package com.woowacourse.zzimkkong.infrastructure.datetime; + +import java.time.LocalDateTime; +import java.util.TimeZone; + +public class TimeZoneUtils { + public static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + public static final TimeZone KST = TimeZone.getTimeZone("Asia/Seoul"); + public static final Long ONE_DAY_OFFSET = 1L; + + public static LocalDateTime convert( + final LocalDateTime dateTime, + final TimeZone fromTimeZone, + final TimeZone toTimeZone) { + return dateTime.atZone(fromTimeZone.toZoneId()) + .withZoneSameInstant(toTimeZone.toZoneId()) + .toLocalDateTime(); + } +} diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java b/backend/src/main/java/com/woowacourse/zzimkkong/infrastructure/warmup/Warmer.java deleted file mode 100644 index e69de29bb..000000000 diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java b/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java index 964cb08be..ff5da4363 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/repository/ReservationRepository.java @@ -14,7 +14,10 @@ @FindInstanceAndCreateLogProxy(group = "repository") public interface ReservationRepository extends JpaRepository, ReservationRepositoryCustom { - List findAllBySpaceIdInAndDate(final Collection spaceIds, final LocalDate date); + List findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( + final Collection spaceIds, + final LocalDate lowerBoundDate, + final LocalDate upperBoundDate); Boolean existsBySpaceIdAndEndTimeAfter(Long spaceId, LocalDateTime now); diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java index 4cf5e03d6..ed22ac854 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java @@ -8,6 +8,7 @@ import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.reservation.*; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import com.woowacourse.zzimkkong.infrastructure.sharingid.SharingIdGenerator; import com.woowacourse.zzimkkong.repository.MapRepository; import com.woowacourse.zzimkkong.repository.ReservationRepository; @@ -24,8 +25,11 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.TimeZone; import java.util.stream.Collectors; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.*; + @Service @Transactional public class ReservationService { @@ -213,12 +217,14 @@ private void validateTime(final ReservationCreateDto reservationCreateDto, final throw new ImpossibleEndTimeException(); } - if (!startDateTime.toLocalDate().isEqual(endDateTime.toLocalDate())) { + LocalDate startDateKST = TimeZoneUtils.convert(startDateTime, UTC, KST).toLocalDate(); + LocalDate endDateKST = TimeZoneUtils.convert(endDateTime, UTC, KST).toLocalDate(); + if (!startDateKST.isEqual(endDateKST)) { throw new NonMatchingStartAndEndDateException(); } } - private void validatePastTimeAndManager(LocalDateTime startDateTime, boolean managerFlag) { + private void validatePastTimeAndManager(final LocalDateTime startDateTime, final boolean managerFlag) { if (startDateTime.isBefore(LocalDateTime.now()) && !managerFlag) { throw new ImpossibleStartTimeException(); } @@ -241,10 +247,15 @@ private void validateAvailability( validateTimeConflicts(startDateTime, endDateTime, reservationsOnDate); } - private void validateSpaceSetting(Space space, LocalDateTime startDateTime, LocalDateTime endDateTime) { - int durationMinutes = (int) ChronoUnit.MINUTES.between(startDateTime, endDateTime); + private void validateSpaceSetting( + final Space space, + final LocalDateTime startDateTime, + final LocalDateTime endDateTime) { + LocalDateTime startDateTimeKST = TimeZoneUtils.convert(startDateTime, UTC, KST); + LocalDateTime endDateTimeKST = TimeZoneUtils.convert(endDateTime, UTC, KST); + int durationMinutes = (int) ChronoUnit.MINUTES.between(startDateTimeKST, endDateTimeKST); - if (space.isNotDivisibleByTimeUnit(startDateTime.getMinute()) || space.isNotDivisibleByTimeUnit(durationMinutes)) { + if (space.isNotDivisibleByTimeUnit(startDateTimeKST.getMinute()) || space.isNotDivisibleByTimeUnit(durationMinutes)) { throw new InvalidTimeUnitException(); } @@ -256,7 +267,7 @@ private void validateSpaceSetting(Space space, LocalDateTime startDateTime, Loca throw new InvalidMaximumDurationTimeException(); } - if (space.isNotBetweenAvailableTime(startDateTime, endDateTime)) { + if (space.isNotBetweenAvailableTime(startDateTimeKST, endDateTimeKST)) { throw new InvalidStartEndTimeException(); } @@ -264,7 +275,7 @@ private void validateSpaceSetting(Space space, LocalDateTime startDateTime, Loca throw new InvalidReservationEnableException(); } - if (space.isClosedOn(startDateTime.getDayOfWeek())) { + if (space.isClosedOn(startDateTimeKST.getDayOfWeek())) { throw new InvalidDayOfWeekException(); } } @@ -285,7 +296,14 @@ private List getReservations(final Collection findSpaces, fi .map(Space::getId) .collect(Collectors.toList()); - return reservations.findAllBySpaceIdInAndDate(spaceIds, date); + List reservationsWithinDateRange = reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( + spaceIds, + date.minusDays(ONE_DAY_OFFSET), + date.plusDays(ONE_DAY_OFFSET)); + + return reservationsWithinDateRange.stream() + .filter(reservation -> reservation.isBookedOn(date, KST)) + .collect(Collectors.toList()); } private void validateSpaceExistence(final Map map, final Long spaceId) { diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java b/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java index 2fbb59ef4..11c58d29b 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/Constants.java @@ -1,8 +1,10 @@ package com.woowacourse.zzimkkong; import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.ZonedDateTime; + +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; public class Constants { private Constants() { @@ -54,27 +56,27 @@ private Constants() { public static final String PRESET_NAME1 = "프리셋1"; public static final String PRESET_NAME2 = "프리셋2"; - public static final LocalDateTime BE_AM_TEN_ELEVEN_START_TIME = THE_DAY_AFTER_TOMORROW.atTime(10,0); - public static final LocalDateTime BE_AM_TEN_ELEVEN_END_TIME = THE_DAY_AFTER_TOMORROW.atTime(11,0); + public static final ZonedDateTime BE_AM_TEN_ELEVEN_START_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()); + public static final ZonedDateTime BE_AM_TEN_ELEVEN_END_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()); public static final String BE_AM_TEN_ELEVEN_DESCRIPTION = DESCRIPTION; public static final String BE_AM_TEN_ELEVEN_USERNAME = USER_NAME; public static final String BE_AM_TEN_ELEVEN_PW = RESERVATION_PW; - public static final LocalDateTime BE_PM_ONE_TWO_START_TIME = THE_DAY_AFTER_TOMORROW.atTime(13, 0); - public static final LocalDateTime BE_PM_ONE_TWO_END_TIME = THE_DAY_AFTER_TOMORROW.atTime(14, 0); + public static final ZonedDateTime BE_PM_ONE_TWO_START_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(13, 0).atZone(KST.toZoneId()); + public static final ZonedDateTime BE_PM_ONE_TWO_END_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(14, 0).atZone(KST.toZoneId()); public static final String BE_PM_ONE_TWO_DESCRIPTION = "찜꽁 2차 회의"; public static final String BE_PM_ONE_TWO_USERNAME = USER_NAME; public static final String BE_PM_ONE_TWO_PW = RESERVATION_PW; - public static final LocalDateTime BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME = THE_DAY_AFTER_TOMORROW.plusDays(1L).atTime(16, 0); - public static final LocalDateTime BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME = THE_DAY_AFTER_TOMORROW.plusDays(1L).atTime(18, 0); + public static final ZonedDateTime BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME_KST = THE_DAY_AFTER_TOMORROW.plusDays(1L).atTime(16, 0).atZone(KST.toZoneId()); + public static final ZonedDateTime BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME_KST = THE_DAY_AFTER_TOMORROW.plusDays(1L).atTime(18, 0).atZone(KST.toZoneId()); public static final String BE_NEXT_DAY_PM_FOUR_TO_SIX_DESCRIPTION = "찜꽁 3차 회의"; public static final String BE_NEXT_DAY_PM_FOUR_TO_SIX_USERNAME = USER_NAME; public static final String BE_NEXT_DAY_PM_FOUR_TO_SIX_PW = RESERVATION_PW; - public static final LocalDateTime FE1_AM_TEN_ELEVEN_START_TIME = THE_DAY_AFTER_TOMORROW.atTime(10, 0); - public static final LocalDateTime FE1_AM_TEN_ELEVEN_END_TIME = THE_DAY_AFTER_TOMORROW.atTime(11, 0); + public static final ZonedDateTime FE1_AM_TEN_ELEVEN_START_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()); + public static final ZonedDateTime FE1_AM_TEN_ELEVEN_END_TIME_KST = THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()); public static final String FE1_AM_TEN_ELEVEN_DESCRIPTION = "찜꽁 5차 회의"; public static final String FE1_AM_TEN_ELEVEN_USERNAME = USER_NAME; public static final String FE1_AM_TEN_ELEVEN_PW = RESERVATION_PW; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java index fb5e82e14..4c98cb85d 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AcceptanceTest.java @@ -1,6 +1,7 @@ package com.woowacourse.zzimkkong.controller; import com.woowacourse.zzimkkong.DatabaseCleaner; +import com.woowacourse.zzimkkong.domain.Reservation; import com.woowacourse.zzimkkong.dto.map.MapCreateUpdateRequest; import com.woowacourse.zzimkkong.dto.member.LoginRequest; import com.woowacourse.zzimkkong.dto.member.MemberSaveRequest; @@ -26,15 +27,16 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit.jupiter.SpringExtension; -import java.io.InputStream; +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; import static com.woowacourse.zzimkkong.Constants.*; import static com.woowacourse.zzimkkong.DocumentUtils.setRequestSpecification; import static com.woowacourse.zzimkkong.controller.AuthControllerTest.getToken; import static com.woowacourse.zzimkkong.controller.MemberControllerTest.saveMember; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.documentationConfiguration; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @@ -112,4 +114,10 @@ void setUp(RestDocumentationContextProvider restDocumentation) { void deleteAll() { databaseCleaner.execute(); } + + protected List filterReservationsByKST(List reservations, LocalDate date) { + return reservations.stream() + .filter(reservation -> reservation.isBookedOn(date, KST)) + .collect(Collectors.toList()); + } } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java index 97c641ea3..512d68893 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/AdminControllerTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.controller; -import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.admin.*; import com.woowacourse.zzimkkong.dto.map.MapFindResponse; @@ -33,6 +32,7 @@ import static com.woowacourse.zzimkkong.controller.ManagerReservationControllerTest.saveReservation; import static com.woowacourse.zzimkkong.controller.ManagerSpaceControllerTest.saveSpace; import static com.woowacourse.zzimkkong.controller.MapControllerTest.saveMap; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; @@ -153,15 +153,15 @@ void getReservations() { ExtractableResponse saveBeSpaceResponse = saveSpace(spaceApi, beSpaceCreateUpdateRequest); String beReservationApi = saveBeSpaceResponse.header("location") + "/reservations"; ReservationCreateUpdateWithPasswordRequest newReservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 0), - THE_DAY_AFTER_TOMORROW.atTime(20, 0), + THE_DAY_AFTER_TOMORROW.atTime(19, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 0).atZone(KST.toZoneId()), SALLY_PW, SALLY_NAME, SALLY_DESCRIPTION); saveReservation(beReservationApi, newReservationCreateUpdateWithPasswordRequest); Reservation reservation = Reservation.builder() - .startTime(newReservationCreateUpdateWithPasswordRequest.getStartDateTime()) - .endTime(newReservationCreateUpdateWithPasswordRequest.getEndDateTime()) + .startTime(newReservationCreateUpdateWithPasswordRequest.localStartDateTime()) + .endTime(newReservationCreateUpdateWithPasswordRequest.localEndDateTime()) .userName(newReservationCreateUpdateWithPasswordRequest.getName()) .password(newReservationCreateUpdateWithPasswordRequest.getPassword()) .description(newReservationCreateUpdateWithPasswordRequest.getDescription()) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java index eddf6a246..a118cd3a2 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/GuestReservationControllerTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.controller; -import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import com.woowacourse.zzimkkong.dto.reservation.*; import com.woowacourse.zzimkkong.service.SlackService; @@ -15,11 +14,14 @@ import org.springframework.http.MediaType; import java.util.Arrays; +import java.util.List; import static com.woowacourse.zzimkkong.Constants.*; import static com.woowacourse.zzimkkong.DocumentUtils.*; import static com.woowacourse.zzimkkong.controller.ManagerSpaceControllerTest.saveSpace; import static com.woowacourse.zzimkkong.controller.MapControllerTest.saveMap; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document; @@ -56,8 +58,8 @@ void setUp() { .replaceAll("managers", "guests") + "/reservations"; reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(15, 0), - THE_DAY_AFTER_TOMORROW.atTime(16, 0), + THE_DAY_AFTER_TOMORROW.atTime(15, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(16, 0).atZone(KST.toZoneId()), SALLY_PW, SALLY_NAME, SALLY_DESCRIPTION); @@ -107,8 +109,8 @@ void setUp() { saveExampleReservations(); savedReservationId = getReservationIdAfterSave(beReservationApi, reservationCreateUpdateWithPasswordRequest); savedReservation = Reservation.builder() - .startTime(reservationCreateUpdateWithPasswordRequest.getStartDateTime()) - .endTime(reservationCreateUpdateWithPasswordRequest.getEndDateTime()) + .startTime(reservationCreateUpdateWithPasswordRequest.localStartDateTime()) + .endTime(reservationCreateUpdateWithPasswordRequest.localEndDateTime()) .password(reservationCreateUpdateWithPasswordRequest.getPassword()) .userName(reservationCreateUpdateWithPasswordRequest.getName()) .description(reservationCreateUpdateWithPasswordRequest.getDescription()) @@ -121,8 +123,8 @@ void setUp() { void save() { //given ReservationCreateUpdateWithPasswordRequest newReservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 0), - THE_DAY_AFTER_TOMORROW.atTime(20, 0), + THE_DAY_AFTER_TOMORROW.atTime(19, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 0).atZone(KST.toZoneId()), SALLY_PW, SALLY_NAME, SALLY_DESCRIPTION); @@ -141,10 +143,13 @@ void find() { ExtractableResponse response = findReservations(beReservationApi, THE_DAY_AFTER_TOMORROW.toString()); ReservationFindResponse actualResponse = response.as(ReservationFindResponse.class); - ReservationFindResponse expectedResponse = ReservationFindResponse.from( + + List expectedFindReservations = filterReservationsByKST( Arrays.asList(savedReservation, beAmZeroOne, - bePmOneTwo)); + bePmOneTwo), + THE_DAY_AFTER_TOMORROW); + ReservationFindResponse expectedResponse = ReservationFindResponse.from(expectedFindReservations); //then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -162,12 +167,17 @@ void findAll() { ExtractableResponse response = findAllReservations(api, THE_DAY_AFTER_TOMORROW.toString()); ReservationFindAllResponse actualResponse = response.as(ReservationFindAllResponse.class); - ReservationFindAllResponse expectedResponse = ReservationFindAllResponse.of( - Arrays.asList(be, fe), - Arrays.asList(savedReservation, + + List expectedFindReservations = filterReservationsByKST( + Arrays.asList( + savedReservation, beAmZeroOne, bePmOneTwo, - fe1ZeroOne)); + fe1ZeroOne), + THE_DAY_AFTER_TOMORROW); + ReservationFindAllResponse expectedResponse = ReservationFindAllResponse.of( + Arrays.asList(be, fe), + expectedFindReservations); //then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -183,8 +193,8 @@ void findAll() { void update_sameSpace() { //given ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequestSameSpace = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 0), - THE_DAY_AFTER_TOMORROW.atTime(20, 30), + THE_DAY_AFTER_TOMORROW.atTime(19, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 30).atZone(KST.toZoneId()), reservationCreateUpdateWithPasswordRequest.getPassword(), "sally", "회의입니다." @@ -199,8 +209,8 @@ void update_sameSpace() { ReservationResponse expectedResponse = ReservationResponse.from( Reservation.builder() .id(savedReservationId) - .startTime(reservationCreateUpdateWithPasswordRequestSameSpace.getStartDateTime()) - .endTime(reservationCreateUpdateWithPasswordRequestSameSpace.getEndDateTime()) + .startTime(reservationCreateUpdateWithPasswordRequestSameSpace.localStartDateTime()) + .endTime(reservationCreateUpdateWithPasswordRequestSameSpace.localEndDateTime()) .description(reservationCreateUpdateWithPasswordRequestSameSpace.getDescription()) .userName(reservationCreateUpdateWithPasswordRequestSameSpace.getName()) .space(be) @@ -218,8 +228,8 @@ void update_sameSpace() { void update_spaceUpdate() { //given ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequestDifferentSpace = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 30), - THE_DAY_AFTER_TOMORROW.atTime(20, 30), + THE_DAY_AFTER_TOMORROW.atTime(19, 30).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 30).atZone(KST.toZoneId()), SALLY_PW, "sally", "회의입니다." @@ -232,19 +242,20 @@ void update_spaceUpdate() { ExtractableResponse findResponse = findReservations(fe1ReservationApi, THE_DAY_AFTER_TOMORROW.toString()); ReservationFindResponse actualResponse = findResponse.as(ReservationFindResponse.class); - ReservationFindResponse expectedResponse = ReservationFindResponse.from( + + List expectedFindReservations = filterReservationsByKST( Arrays.asList( Reservation.builder() - .startTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.getStartDateTime()) - .endTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.getEndDateTime()) + .startTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.localStartDateTime()) + .endTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.localEndDateTime()) .description(reservationCreateUpdateWithPasswordRequestDifferentSpace.getDescription()) .userName(reservationCreateUpdateWithPasswordRequestDifferentSpace.getName()) .password(reservationCreateUpdateWithPasswordRequestDifferentSpace.getPassword()) .space(fe) .build(), - fe1ZeroOne - ) - ); + fe1ZeroOne), + THE_DAY_AFTER_TOMORROW); + ReservationFindResponse expectedResponse = ReservationFindResponse.from(expectedFindReservations); //then assertThat(updateResponse.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -286,37 +297,37 @@ void findOne() { private void saveExampleReservations() { ReservationCreateUpdateWithPasswordRequest beAmZeroOneRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_AM_TEN_ELEVEN_START_TIME, - BE_AM_TEN_ELEVEN_END_TIME, + BE_AM_TEN_ELEVEN_START_TIME_KST, + BE_AM_TEN_ELEVEN_END_TIME_KST, BE_AM_TEN_ELEVEN_PW, BE_AM_TEN_ELEVEN_USERNAME, BE_AM_TEN_ELEVEN_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest bePmOneTwoRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_PM_ONE_TWO_START_TIME, - BE_PM_ONE_TWO_END_TIME, + BE_PM_ONE_TWO_START_TIME_KST, + BE_PM_ONE_TWO_END_TIME_KST, BE_PM_ONE_TWO_PW, BE_PM_ONE_TWO_USERNAME, BE_PM_ONE_TWO_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest beNextDayAmSixTwelveRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME, - BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME, + BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME_KST, + BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME_KST, BE_NEXT_DAY_PM_FOUR_TO_SIX_PW, BE_NEXT_DAY_PM_FOUR_TO_SIX_USERNAME, BE_NEXT_DAY_PM_FOUR_TO_SIX_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest feZeroOneRequest = new ReservationCreateUpdateWithPasswordRequest( - FE1_AM_TEN_ELEVEN_START_TIME, - FE1_AM_TEN_ELEVEN_END_TIME, + FE1_AM_TEN_ELEVEN_START_TIME_KST, + FE1_AM_TEN_ELEVEN_END_TIME_KST, FE1_AM_TEN_ELEVEN_PW, FE1_AM_TEN_ELEVEN_USERNAME, FE1_AM_TEN_ELEVEN_DESCRIPTION); beAmZeroOne = Reservation.builder() .id(getReservationIdAfterSave(beReservationApi, beAmZeroOneRequest)) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -325,8 +336,8 @@ private void saveExampleReservations() { bePmOneTwo = Reservation.builder() .id(getReservationIdAfterSave(beReservationApi, bePmOneTwoRequest)) - .startTime(BE_PM_ONE_TWO_START_TIME) - .endTime(BE_PM_ONE_TWO_END_TIME) + .startTime(BE_PM_ONE_TWO_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_PM_ONE_TWO_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_PM_ONE_TWO_DESCRIPTION) .userName(BE_PM_ONE_TWO_USERNAME) .password(BE_PM_ONE_TWO_PW) @@ -337,8 +348,8 @@ private void saveExampleReservations() { fe1ZeroOne = Reservation.builder() .id(getReservationIdAfterSave(fe1ReservationApi, feZeroOneRequest)) - .startTime(FE1_AM_TEN_ELEVEN_START_TIME) - .endTime(FE1_AM_TEN_ELEVEN_END_TIME) + .startTime(FE1_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(FE1_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(FE1_AM_TEN_ELEVEN_DESCRIPTION) .userName(FE1_AM_TEN_ELEVEN_USERNAME) .password(FE1_AM_TEN_ELEVEN_PW) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java index a457b80a4..832329a15 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/controller/ManagerReservationControllerTest.java @@ -15,11 +15,14 @@ import org.springframework.http.MediaType; import java.util.Arrays; +import java.util.List; import static com.woowacourse.zzimkkong.Constants.*; import static com.woowacourse.zzimkkong.DocumentUtils.*; import static com.woowacourse.zzimkkong.controller.ManagerSpaceControllerTest.saveSpace; import static com.woowacourse.zzimkkong.controller.MapControllerTest.saveMap; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.restdocs.restassured3.RestAssuredRestDocumentation.document; @@ -53,8 +56,8 @@ void setUp() { fe1ReservationApi = saveFe1SpaceResponse.header("location") + "/reservations"; ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(15, 0), - THE_DAY_AFTER_TOMORROW.atTime(16, 0), + THE_DAY_AFTER_TOMORROW.atTime(15, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(16, 0).atZone(KST.toZoneId()), SALLY_PW, SALLY_NAME, SALLY_DESCRIPTION); @@ -104,8 +107,8 @@ void setUp() { saveExampleReservations(); savedReservationId = getReservationIdAfterSave(beReservationApi, reservationCreateUpdateWithPasswordRequest); savedReservation = Reservation.builder() - .startTime(reservationCreateUpdateWithPasswordRequest.getStartDateTime()) - .endTime(reservationCreateUpdateWithPasswordRequest.getEndDateTime()) + .startTime(reservationCreateUpdateWithPasswordRequest.localStartDateTime()) + .endTime(reservationCreateUpdateWithPasswordRequest.localEndDateTime()) .password(reservationCreateUpdateWithPasswordRequest.getPassword()) .userName(reservationCreateUpdateWithPasswordRequest.getName()) .description(reservationCreateUpdateWithPasswordRequest.getDescription()) @@ -118,8 +121,8 @@ void setUp() { void save() { //given ReservationCreateUpdateWithPasswordRequest newReservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 0), - THE_DAY_AFTER_TOMORROW.atTime(20, 0), + THE_DAY_AFTER_TOMORROW.atTime(19, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 0).atZone(KST.toZoneId()), SALLY_PW, SALLY_NAME, SALLY_DESCRIPTION); @@ -138,10 +141,10 @@ void find() { ExtractableResponse response = findReservations(beReservationApi, THE_DAY_AFTER_TOMORROW.toString()); ReservationFindResponse actualResponse = response.as(ReservationFindResponse.class); - ReservationFindResponse expectedResponse = ReservationFindResponse.from( - Arrays.asList(savedReservation, - beAmZeroOne, - bePmOneTwo)); + List expectedFindReservations = filterReservationsByKST( + Arrays.asList(savedReservation, beAmZeroOne, bePmOneTwo), + THE_DAY_AFTER_TOMORROW); + ReservationFindResponse expectedResponse = ReservationFindResponse.from(expectedFindReservations); //then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -159,12 +162,13 @@ void findAll() { ExtractableResponse response = findAllReservations(api, THE_DAY_AFTER_TOMORROW.toString()); ReservationFindAllResponse actualResponse = response.as(ReservationFindAllResponse.class); + + List expectedFindReservations = filterReservationsByKST( + Arrays.asList(savedReservation, beAmZeroOne, bePmOneTwo, fe1ZeroOne), + THE_DAY_AFTER_TOMORROW); ReservationFindAllResponse expectedResponse = ReservationFindAllResponse.of( Arrays.asList(be, fe), - Arrays.asList(savedReservation, - beAmZeroOne, - bePmOneTwo, - fe1ZeroOne)); + expectedFindReservations); //then assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -196,8 +200,8 @@ void findOne() { void update_sameSpace() { //given ReservationCreateUpdateRequest reservationCreateUpdateRequestSameSpace = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 0), - THE_DAY_AFTER_TOMORROW.atTime(20, 30), + THE_DAY_AFTER_TOMORROW.atTime(19, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 30).atZone(KST.toZoneId()), "sally", "회의입니다." ); @@ -211,8 +215,8 @@ void update_sameSpace() { ReservationResponse expectedResponse = ReservationResponse.from( Reservation.builder() .id(savedReservationId) - .startTime(reservationCreateUpdateRequestSameSpace.getStartDateTime()) - .endTime(reservationCreateUpdateRequestSameSpace.getEndDateTime()) + .startTime(reservationCreateUpdateRequestSameSpace.localStartDateTime()) + .endTime(reservationCreateUpdateRequestSameSpace.localEndDateTime()) .description(reservationCreateUpdateRequestSameSpace.getDescription()) .userName(reservationCreateUpdateRequestSameSpace.getName()) .space(be) @@ -231,8 +235,8 @@ void update_sameSpace() { void update_spaceUpdate() { //given ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequestDifferentSpace = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(19, 30), - THE_DAY_AFTER_TOMORROW.atTime(20, 30), + THE_DAY_AFTER_TOMORROW.atTime(19, 30).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(20, 30).atZone(KST.toZoneId()), SALLY_PW, "sally", "회의입니다." @@ -247,19 +251,20 @@ void update_spaceUpdate() { THE_DAY_AFTER_TOMORROW.toString()); ReservationFindResponse actualResponse = findResponse.as(ReservationFindResponse.class); - ReservationFindResponse expectedResponse = ReservationFindResponse.from( + + List expectedFindReservations = filterReservationsByKST( Arrays.asList( Reservation.builder() - .startTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.getStartDateTime()) - .endTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.getEndDateTime()) + .startTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.localStartDateTime()) + .endTime(reservationCreateUpdateWithPasswordRequestDifferentSpace.localEndDateTime()) .description(reservationCreateUpdateWithPasswordRequestDifferentSpace.getDescription()) .userName(reservationCreateUpdateWithPasswordRequestDifferentSpace.getName()) .password(reservationCreateUpdateWithPasswordRequestDifferentSpace.getPassword()) .space(fe) .build(), - fe1ZeroOne - ) - ); + fe1ZeroOne), + THE_DAY_AFTER_TOMORROW); + ReservationFindResponse expectedResponse = ReservationFindResponse.from(expectedFindReservations); //then assertThat(updateResponse.statusCode()).isEqualTo(HttpStatus.OK.value()); @@ -284,37 +289,37 @@ void delete() { private void saveExampleReservations() { ReservationCreateUpdateWithPasswordRequest beAmZeroOneRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_AM_TEN_ELEVEN_START_TIME, - BE_AM_TEN_ELEVEN_END_TIME, + BE_AM_TEN_ELEVEN_START_TIME_KST, + BE_AM_TEN_ELEVEN_END_TIME_KST, BE_AM_TEN_ELEVEN_PW, BE_AM_TEN_ELEVEN_USERNAME, BE_AM_TEN_ELEVEN_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest bePmOneTwoRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_PM_ONE_TWO_START_TIME, - BE_PM_ONE_TWO_END_TIME, + BE_PM_ONE_TWO_START_TIME_KST, + BE_PM_ONE_TWO_END_TIME_KST, BE_PM_ONE_TWO_PW, BE_PM_ONE_TWO_USERNAME, BE_PM_ONE_TWO_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest beNextDayAmSixTwelveRequest = new ReservationCreateUpdateWithPasswordRequest( - BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME, - BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME, + BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME_KST, + BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME_KST, BE_NEXT_DAY_PM_FOUR_TO_SIX_PW, BE_NEXT_DAY_PM_FOUR_TO_SIX_USERNAME, BE_NEXT_DAY_PM_FOUR_TO_SIX_DESCRIPTION); ReservationCreateUpdateWithPasswordRequest feZeroOneRequest = new ReservationCreateUpdateWithPasswordRequest( - FE1_AM_TEN_ELEVEN_START_TIME, - FE1_AM_TEN_ELEVEN_END_TIME, + FE1_AM_TEN_ELEVEN_START_TIME_KST, + FE1_AM_TEN_ELEVEN_END_TIME_KST, FE1_AM_TEN_ELEVEN_PW, FE1_AM_TEN_ELEVEN_USERNAME, FE1_AM_TEN_ELEVEN_DESCRIPTION); beAmZeroOne = Reservation.builder() .id(getReservationIdAfterSave(beReservationApi, beAmZeroOneRequest)) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -323,8 +328,8 @@ private void saveExampleReservations() { bePmOneTwo = Reservation.builder() .id(getReservationIdAfterSave(beReservationApi, bePmOneTwoRequest)) - .startTime(BE_PM_ONE_TWO_START_TIME) - .endTime(BE_PM_ONE_TWO_END_TIME) + .startTime(BE_PM_ONE_TWO_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_PM_ONE_TWO_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_PM_ONE_TWO_DESCRIPTION) .userName(BE_PM_ONE_TWO_USERNAME) .password(BE_PM_ONE_TWO_PW) @@ -335,8 +340,8 @@ private void saveExampleReservations() { fe1ZeroOne = Reservation.builder() .id(getReservationIdAfterSave(fe1ReservationApi, feZeroOneRequest)) - .startTime(FE1_AM_TEN_ELEVEN_START_TIME) - .endTime(FE1_AM_TEN_ELEVEN_END_TIME) + .startTime(FE1_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(FE1_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(FE1_AM_TEN_ELEVEN_DESCRIPTION) .userName(FE1_AM_TEN_ELEVEN_USERNAME) .password(FE1_AM_TEN_ELEVEN_PW) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateRequestTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateRequestTest.java index 284812029..aa50b9ee9 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateRequestTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateRequestTest.java @@ -10,9 +10,11 @@ import org.junit.jupiter.params.provider.ValueSource; import java.time.LocalDateTime; +import java.time.ZonedDateTime; import java.time.format.DateTimeParseException; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -20,16 +22,16 @@ class ReservationCreateUpdateRequestTest extends RequestTest { @ParameterizedTest @NullSource @DisplayName("예약 생성에 빈 dateTime이 들어오면 처리한다.") - void blankDateTime(LocalDateTime dateTime) { + void blankDateTime(ZonedDateTime zonedDateTime) { ReservationCreateUpdateRequest startTime = new ReservationCreateUpdateRequest( - dateTime, - LocalDateTime.now(), + zonedDateTime, + LocalDateTime.now().atZone(KST.toZoneId()), "name", "description"); ReservationCreateUpdateRequest endTime = new ReservationCreateUpdateRequest( - LocalDateTime.now(), - dateTime, + LocalDateTime.now().atZone(KST.toZoneId()), + zonedDateTime, "name", "description"); @@ -47,8 +49,8 @@ void blankDateTime(LocalDateTime dateTime) { @DisplayName("예약 생성에 빈 이름이 들어오면 처리한다.") void blankName(String data) { ReservationCreateUpdateRequest nameRequest = new ReservationCreateUpdateRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), data, "description"); @@ -62,8 +64,8 @@ void blankName(String data) { @DisplayName("예약 생성의 이름에 옳지 않은 형식의 문자열이 들어오면 처리한다.") void invalidName(String name, boolean flag) { ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), name, "description"); @@ -77,8 +79,8 @@ void invalidName(String name, boolean flag) { @DisplayName("예약 생성에 빈 이름이 들어오면 처리한다.") void blankDescription(String description) { ReservationCreateUpdateRequest descriptionRequest = new ReservationCreateUpdateRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), "name", description); @@ -91,8 +93,8 @@ void blankDescription(String description) { @DisplayName("예약 생성의 이름에 옳지 않은 형식의 문자열이 들어오면 처리한다.") void invalidDescription() { ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), "name", "iamtenwordiamtenwordiamtenwordiamtenwordiamtenwordiamtenwordiamtenwordiamtenwordiamtenwordiamtenword1"); diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateWithPasswordRequestTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateWithPasswordRequestTest.java index 0c0aa2723..acb67a807 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateWithPasswordRequestTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/dto/ReservationCreateUpdateWithPasswordRequestTest.java @@ -10,6 +10,7 @@ import static com.woowacourse.zzimkkong.dto.ValidatorMessage.EMPTY_MESSAGE; import static com.woowacourse.zzimkkong.dto.ValidatorMessage.RESERVATION_PW_MESSAGE; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; import static org.assertj.core.api.Assertions.assertThat; class ReservationCreateUpdateWithPasswordRequestTest extends RequestTest { @@ -18,8 +19,8 @@ class ReservationCreateUpdateWithPasswordRequestTest extends RequestTest { @DisplayName("예약 비밀번호에 빈 문자열이 들어오면 처리한다.") void blankReservationPassword(String password) { ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), password, "name", "description"); @@ -34,8 +35,8 @@ void blankReservationPassword(String password) { @DisplayName("예약 비밀번호에 옳지 않은 형식의 비밀번호가 들어오면 처리한다.") void invalidEmail(String password, boolean flag) { ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - LocalDateTime.now(), - LocalDateTime.now(), + LocalDateTime.now().atZone(KST.toZoneId()), + LocalDateTime.now().atZone(KST.toZoneId()), password, "name", "description"); diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/auth/JwtUtilsTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/auth/JwtUtilsTest.java index 16aa3c997..d7633183a 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/auth/JwtUtilsTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/infrastructure/auth/JwtUtilsTest.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.woowacourse.zzimkkong.exception.authorization.InvalidTokenException; import com.woowacourse.zzimkkong.exception.authorization.TokenExpiredException; -import com.woowacourse.zzimkkong.infrastructure.auth.JwtUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java index f4038ff12..20a22dbcf 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryImplTest.java @@ -1,6 +1,5 @@ package com.woowacourse.zzimkkong.repository; -import com.woowacourse.zzimkkong.Constants; import com.woowacourse.zzimkkong.domain.*; import org.assertj.core.api.AssertionsForClassTypes; import org.junit.jupiter.api.DisplayName; @@ -11,6 +10,7 @@ import java.time.LocalDateTime; import static com.woowacourse.zzimkkong.Constants.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; class ReservationRepositoryImplTest extends RepositoryTest { @ParameterizedTest @@ -59,9 +59,9 @@ void existsReservationsByMember(boolean isReservationExists) { if (isReservationExists) { Reservation beAmZeroOne = Reservation.builder() - .date(BE_AM_TEN_ELEVEN_START_TIME.toLocalDate()) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .date(BE_AM_TEN_ELEVEN_START_TIME_KST.toLocalDate()) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java index d866ce59d..bf87338d3 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/repository/ReservationRepositoryTest.java @@ -15,6 +15,8 @@ import java.util.List; import static com.woowacourse.zzimkkong.Constants.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.ONE_DAY_OFFSET; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -78,9 +80,9 @@ void setUp() { spaces.save(fe); beAmZeroOne = Reservation.builder() - .date(BE_AM_TEN_ELEVEN_START_TIME.toLocalDate()) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .date(BE_AM_TEN_ELEVEN_START_TIME_KST.toLocalDate()) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -88,9 +90,9 @@ void setUp() { .build(); bePmOneTwo = Reservation.builder() - .date(BE_PM_ONE_TWO_START_TIME.toLocalDate()) - .startTime(BE_PM_ONE_TWO_START_TIME) - .endTime(BE_PM_ONE_TWO_END_TIME) + .date(BE_PM_ONE_TWO_START_TIME_KST.toLocalDate()) + .startTime(BE_PM_ONE_TWO_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_PM_ONE_TWO_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_PM_ONE_TWO_DESCRIPTION) .userName(BE_PM_ONE_TWO_USERNAME) .password(BE_PM_ONE_TWO_PW) @@ -98,9 +100,9 @@ void setUp() { .build(); beNextDayAmSixTwelve = Reservation.builder() - .date(BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME.toLocalDate()) - .startTime(BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME) - .endTime(BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME) + .date(BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME_KST.toLocalDate()) + .startTime(BE_NEXT_DAY_PM_FOUR_TO_SIX_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_NEXT_DAY_PM_FOUR_TO_SIX_DESCRIPTION) .userName(BE_NEXT_DAY_PM_FOUR_TO_SIX_USERNAME) .password(BE_NEXT_DAY_PM_FOUR_TO_SIX_PW) @@ -108,9 +110,9 @@ void setUp() { .build(); fe1ZeroOne = Reservation.builder() - .date(FE1_AM_TEN_ELEVEN_START_TIME.toLocalDate()) - .startTime(FE1_AM_TEN_ELEVEN_START_TIME) - .endTime(FE1_AM_TEN_ELEVEN_END_TIME) + .date(FE1_AM_TEN_ELEVEN_START_TIME_KST.toLocalDate()) + .startTime(FE1_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(FE1_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(FE1_AM_TEN_ELEVEN_DESCRIPTION) .userName(FE1_AM_TEN_ELEVEN_USERNAME) .password(FE1_AM_TEN_ELEVEN_PW) @@ -146,8 +148,8 @@ void save() { } @Test - @DisplayName("map id, space id, 특정 시간이 주어질 때, 해당 spaceId와 해당 시간에 속하는 예약들만 찾아온다") - void findAllBySpaceIdInAndDate() { + @DisplayName("map id, space id, 특정 날짜가 주어질 때, 해당 spaceId와 해당 날짜 전,훗날에 속하는 예약들을 찾아온다") + void findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual() { // given, when List foundReservations = getReservations( List.of(be.getId(), fe.getId()), @@ -155,43 +157,24 @@ void findAllBySpaceIdInAndDate() { // then assertThat(foundReservations).usingRecursiveComparison() - .isEqualTo(List.of(beAmZeroOne, bePmOneTwo, fe1ZeroOne)); + .ignoringCollectionOrder() + .isEqualTo(List.of(beAmZeroOne, beNextDayAmSixTwelve, bePmOneTwo, fe1ZeroOne)); } @Test @DisplayName("특정 날짜에 부합하는 예약이 없으면 빈 리스트를 반환한다") - void findAllBySpaceIdInAndDate_noMatchingTime() { + void findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual_noMatchingTimeAndSpace() { // given, when - List foundReservations = getReservations( + List foundReservationsBe = getReservations( List.of(be.getId()), - THE_DAY_AFTER_TOMORROW.plusDays(2)); - - // then - assertThat(foundReservations).isEmpty(); - } - - @Test - @DisplayName("특정 공간에 부합하는 예약이 없으면 빈 리스트를 반환한다") - void findAllBySpaceIdInAndDate_noMatchingReservation() { - // given, when - List foundReservations = getReservations( + THE_DAY_AFTER_TOMORROW.plusDays(3)); + List foundReservationsFe = getReservations( List.of(fe.getId()), - THE_DAY_AFTER_TOMORROW.plusDays(1)); - - // then - assertThat(foundReservations).isEmpty(); - } - - @Test - @DisplayName("map id와 특정 날짜가 주어질 때, 해당 날짜에 속하는 해당 map의 모든 space들의 예약들을 찾아온다") - void findAllBySpaceIdInAndDate_allSpaces() { - // given, when - List foundReservations = getReservations( - List.of(be.getId(), fe.getId()), - THE_DAY_AFTER_TOMORROW); + THE_DAY_AFTER_TOMORROW.minusDays(3)); // then - assertThat(foundReservations).containsExactlyInAnyOrderElementsOf(List.of(beAmZeroOne, bePmOneTwo, fe1ZeroOne)); + assertThat(foundReservationsBe).isEmpty(); + assertThat(foundReservationsFe).isEmpty(); } @Test @@ -216,7 +199,9 @@ void existsBySpace() { @DisplayName("특정 시간 이후의 예약이 존재하는지 확인한다.") void existsBySpaceIdAndDateGreaterThanEqual(int minusMinute, boolean expected) { //given, when - Boolean actual = reservations.existsBySpaceIdAndEndTimeAfter(be.getId(), BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME.minusMinutes(minusMinute)); + Boolean actual = reservations.existsBySpaceIdAndEndTimeAfter( + be.getId(), + BE_NEXT_DAY_PM_FOUR_TO_SIX_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime().minusMinutes(minusMinute)); //then assertThat(actual).isEqualTo(expected); @@ -237,6 +222,9 @@ void findAllByPaging() { } private List getReservations(List spaceIds, LocalDate date) { - return reservations.findAllBySpaceIdInAndDate(spaceIds, date); + return reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( + spaceIds, + date.minusDays(ONE_DAY_OFFSET), + date.plusDays(ONE_DAY_OFFSET)); } } diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java index dc5cd9e5e..9ed8cbe78 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/AdminServiceTest.java @@ -22,6 +22,7 @@ import java.util.List; import static com.woowacourse.zzimkkong.Constants.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -168,9 +169,9 @@ void findReservations() { .build(); Reservation beAmZeroOne = Reservation.builder() - .date(BE_AM_TEN_ELEVEN_START_TIME.toLocalDate()) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .date(BE_AM_TEN_ELEVEN_START_TIME_KST.toLocalDate()) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java index 2509fa2c3..931f61bfc 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/GuestReservationServiceTest.java @@ -5,6 +5,7 @@ import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.reservation.*; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import com.woowacourse.zzimkkong.service.strategy.GuestReservationStrategy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -23,6 +24,8 @@ import java.util.Optional; import static com.woowacourse.zzimkkong.Constants.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -38,8 +41,8 @@ class GuestReservationServiceTest extends ServiceTest { private final GuestReservationStrategy guestReservationStrategy = new GuestReservationStrategy(); private ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(13, 0), - THE_DAY_AFTER_TOMORROW.atTime(14, 0), + THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -103,8 +106,8 @@ void setUp() { beAmZeroOne = Reservation.builder() .id(1L) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -113,8 +116,8 @@ void setUp() { bePmOneTwo = Reservation.builder() .id(2L) - .startTime(BE_PM_ONE_TWO_START_TIME) - .endTime(BE_PM_ONE_TWO_END_TIME) + .startTime(BE_PM_ONE_TWO_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_PM_ONE_TWO_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_PM_ONE_TWO_DESCRIPTION) .userName(BE_PM_ONE_TWO_USERNAME) .password(BE_PM_ONE_TWO_PW) @@ -122,8 +125,8 @@ void setUp() { .build(); reservation = makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime(), - reservationCreateUpdateWithPasswordRequest.getEndDateTime(), + reservationCreateUpdateWithPasswordRequest.localStartDateTime(), + reservationCreateUpdateWithPasswordRequest.localEndDateTime(), be); lutherId = luther.getId(); @@ -204,8 +207,8 @@ void saveStartTimeBeforeNow() { // when reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - LocalDateTime.now().minusHours(3), - LocalDateTime.now().plusHours(3), + LocalDateTime.now().minusHours(3).atZone(KST.toZoneId()), + LocalDateTime.now().plusHours(3).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -230,8 +233,8 @@ void saveEndTimeBeforeNow() { // when reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(14, 0), - THE_DAY_AFTER_TOMORROW.atTime(13, 0), + THE_DAY_AFTER_TOMORROW.atTime(14, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(13, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -256,8 +259,8 @@ void saveStartTimeEqualsEndTime() { //when reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -282,8 +285,8 @@ void saveStartTimeDateNotEqualsEndTimeDate() { //when reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -309,8 +312,8 @@ void saveInvalidTimeSetting(int startTime, int endTime) { //when reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(startTime, 0), - THE_DAY_AFTER_TOMORROW.atTime(endTime, 30), + THE_DAY_AFTER_TOMORROW.atTime(startTime, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(endTime, 30).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -334,12 +337,13 @@ void saveAvailabilityException(int startMinute, int endMinute) { //given, when given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of(makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(startMinute), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(endMinute), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(startMinute), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(endMinute), be))); ReservationCreateDto reservationCreateDto = ReservationCreateDto.of( @@ -441,18 +445,20 @@ void saveSameThresholdTime(int duration) { //given, when given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be))); + given(reservations.save(any(Reservation.class))) .willReturn(reservation); @@ -481,8 +487,8 @@ void saveReservationTimeUnitException(int additionalStartMinute, int additionalE //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - theDayAfterTomorrowTen.plusMinutes(additionalStartMinute), - theDayAfterTomorrowTen.plusMinutes(additionalEndMinute), + theDayAfterTomorrowTen.plusMinutes(additionalStartMinute).atZone(KST.toZoneId()), + theDayAfterTomorrowTen.plusMinutes(additionalEndMinute).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -520,8 +526,8 @@ void saveReservationMinimumDurationTimeException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -555,8 +561,8 @@ void saveReservationMaximumTimeUnitException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -590,8 +596,8 @@ void pastReservationSaveUpdateException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(5), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(5).plusMinutes(60), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(5).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(5).plusMinutes(60).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -626,18 +632,19 @@ void findReservations() { int duration = 30; List foundReservations = Arrays.asList( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be)); given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(foundReservations); @@ -703,8 +710,9 @@ void findEmptyReservations() { .willReturn(Optional.of(luther)); given(maps.existsById(anyLong())) .willReturn(true); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(Collections.emptyList()); @@ -739,27 +747,28 @@ void findAllReservation() { int duration = 30; List foundReservations = List.of( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), fe), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), fe)); List findSpaces = List.of(be, fe); given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(foundReservations); @@ -859,8 +868,8 @@ void update() { .willReturn(Optional.of(reservation)); Long reservationId = reservation.getId(); ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(11, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()), reservation.getPassword(), CHANGED_NAME, CHANGED_DESCRIPTION); @@ -890,8 +899,8 @@ void updateInvalidEndTimeException(int endTime) { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(12, 0), - THE_DAY_AFTER_TOMORROW.atTime(12, 0).minusHours(endTime), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).minusHours(endTime).atZone(KST.toZoneId()), reservation.getPassword(), CHANGED_NAME, CHANGED_DESCRIPTION); @@ -919,8 +928,8 @@ void updateInvalidDateException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - LocalDateTime.now().plusDays(1), - LocalDateTime.now().plusDays(2), + LocalDateTime.now().plusDays(1).atZone(KST.toZoneId()), + LocalDateTime.now().plusDays(2).atZone(KST.toZoneId()), reservation.getPassword(), CHANGED_NAME, CHANGED_DESCRIPTION); @@ -950,8 +959,8 @@ void updateIncorrectPasswordException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - reservation.getStartTime(), - reservation.getEndTime(), + TimeZoneUtils.convert(reservation.getStartTime(), UTC, KST).atZone(KST.toZoneId()), + TimeZoneUtils.convert(reservation.getEndTime(), UTC, KST).atZone(KST.toZoneId()), "1231", CHANGED_NAME, CHANGED_DESCRIPTION); @@ -979,15 +988,18 @@ void updateImpossibleTimeException(int startTime, int endTime) { .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) .willReturn(Optional.of(reservation)); - given(reservations.findAllBySpaceIdInAndDate(anyList(), any())) + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( + anyList(), + any(LocalDate.class), + any(LocalDate.class))) .willReturn(Arrays.asList( beAmZeroOne, bePmOneTwo)); //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - bePmOneTwo.getStartTime().plusMinutes(startTime), - bePmOneTwo.getEndTime().plusMinutes(endTime), + TimeZoneUtils.convert(bePmOneTwo.getStartTime().plusMinutes(startTime), UTC, KST).atZone(KST.toZoneId()), + TimeZoneUtils.convert(bePmOneTwo.getEndTime().plusMinutes(endTime), UTC, KST).atZone(KST.toZoneId()), reservation.getPassword(), reservation.getUserName(), reservation.getDescription()); @@ -1018,8 +1030,8 @@ void updateInvalidTimeSetting(int startTime, int endTime) { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(startTime, 0), - THE_DAY_AFTER_TOMORROW.atTime(endTime, 30), + THE_DAY_AFTER_TOMORROW.atTime(startTime, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(endTime, 30).atZone(KST.toZoneId()), RESERVATION_PW, CHANGED_NAME, CHANGED_DESCRIPTION); @@ -1133,8 +1145,8 @@ void deleteReservation() { .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) .willReturn(Optional.of(makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime(), - reservationCreateUpdateWithPasswordRequest.getEndDateTime(), + reservationCreateUpdateWithPasswordRequest.localStartDateTime(), + reservationCreateUpdateWithPasswordRequest.localEndDateTime(), be))); Long reservationId = reservation.getId(); @@ -1184,8 +1196,8 @@ void deleteReservationPasswordException() { .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) .willReturn(Optional.of(makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime(), - reservationCreateUpdateWithPasswordRequest.getEndDateTime(), + reservationCreateUpdateWithPasswordRequest.localStartDateTime(), + reservationCreateUpdateWithPasswordRequest.localEndDateTime(), be))); ReservationPasswordAuthenticationRequest reservationPasswordAuthenticationRequest @@ -1214,8 +1226,8 @@ void deletePastReservationException() { .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) .willReturn(Optional.of(makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusDays(5), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusDays(5), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusDays(5), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusDays(5), be))); ReservationPasswordAuthenticationRequest reservationPasswordAuthenticationRequest diff --git a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java index 9fc81d698..831fda344 100644 --- a/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java +++ b/backend/src/test/java/com/woowacourse/zzimkkong/service/ManagerReservationServiceTest.java @@ -7,6 +7,7 @@ import com.woowacourse.zzimkkong.exception.map.NoSuchMapException; import com.woowacourse.zzimkkong.exception.reservation.*; import com.woowacourse.zzimkkong.exception.space.NoSuchSpaceException; +import com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils; import com.woowacourse.zzimkkong.service.strategy.ManagerReservationStrategy; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -19,12 +20,15 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.ZonedDateTime; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Optional; import static com.woowacourse.zzimkkong.Constants.*; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.KST; +import static com.woowacourse.zzimkkong.infrastructure.datetime.TimeZoneUtils.UTC; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @@ -40,14 +44,14 @@ class ManagerReservationServiceTest extends ServiceTest { private final ManagerReservationStrategy managerReservationStrategy = new ManagerReservationStrategy(); private ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(13, 0), - THE_DAY_AFTER_TOMORROW.atTime(14, 0), + THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); private final ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(13, 0), - THE_DAY_AFTER_TOMORROW.atTime(14, 0), + THE_DAY_AFTER_TOMORROW.atTime(11, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); @@ -116,8 +120,8 @@ void setUp() { beAmZeroOne = Reservation.builder() .id(1L) - .startTime(BE_AM_TEN_ELEVEN_START_TIME) - .endTime(BE_AM_TEN_ELEVEN_END_TIME) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -126,8 +130,8 @@ void setUp() { bePmOneTwo = Reservation.builder() .id(2L) - .startTime(BE_PM_ONE_TWO_START_TIME) - .endTime(BE_PM_ONE_TWO_END_TIME) + .startTime(BE_PM_ONE_TWO_START_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_PM_ONE_TWO_END_TIME_KST.withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_PM_ONE_TWO_DESCRIPTION) .userName(BE_PM_ONE_TWO_USERNAME) .password(BE_PM_ONE_TWO_PW) @@ -135,8 +139,8 @@ void setUp() { .build(); reservation = makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime(), - reservationCreateUpdateWithPasswordRequest.getEndDateTime(), + reservationCreateUpdateWithPasswordRequest.localStartDateTime(), + reservationCreateUpdateWithPasswordRequest.localEndDateTime(), be); lutherId = luther.getId(); @@ -176,8 +180,8 @@ void savePastReservation() { //given Reservation pastReservation = Reservation.builder() .id(1L) - .startTime(BE_AM_TEN_ELEVEN_START_TIME.minusDays(5)) - .endTime(BE_AM_TEN_ELEVEN_END_TIME.minusDays(5)) + .startTime(BE_AM_TEN_ELEVEN_START_TIME_KST.minusDays(5).withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) + .endTime(BE_AM_TEN_ELEVEN_END_TIME_KST.minusDays(5).withZoneSameInstant(UTC.toZoneId()).toLocalDateTime()) .description(BE_AM_TEN_ELEVEN_DESCRIPTION) .userName(BE_AM_TEN_ELEVEN_USERNAME) .password(BE_AM_TEN_ELEVEN_PW) @@ -189,9 +193,11 @@ void savePastReservation() { .willReturn(pastReservation); //when + ZonedDateTime pastReservationStartTimeKST = TimeZoneUtils.convert(pastReservation.getStartTime(), UTC, KST).atZone(KST.toZoneId()); + ZonedDateTime pastReservationEndTimeKST = TimeZoneUtils.convert(pastReservation.getEndTime(), UTC, KST).atZone(KST.toZoneId()); ReservationCreateUpdateWithPasswordRequest pastReservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - pastReservation.getStartTime(), - pastReservation.getEndTime(), + pastReservationStartTimeKST, + pastReservationEndTimeKST, pastReservation.getPassword(), pastReservation.getUserName(), pastReservation.getDescription()); @@ -280,8 +286,8 @@ void saveEndTimeBeforeNow() { .willReturn(Optional.of(luther)); reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(14, 0), - THE_DAY_AFTER_TOMORROW.atTime(13, 0), + THE_DAY_AFTER_TOMORROW.atTime(14, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(13, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -308,8 +314,8 @@ void saveStartTimeEqualsEndTime() { .willReturn(Optional.of(luther)); reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -336,8 +342,8 @@ void saveStartTimeDateNotEqualsEndTimeDate() { .willReturn(Optional.of(luther)); reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -365,8 +371,8 @@ void saveInvalidTimeSetting(int startTime, int endTime) { .willReturn(Optional.of(luther)); reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(startTime, 0), - THE_DAY_AFTER_TOMORROW.atTime(endTime, 30), + THE_DAY_AFTER_TOMORROW.atTime(startTime, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(endTime, 30).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -392,12 +398,13 @@ void saveAvailabilityException(int startMinute, int endMinute) { //given, when given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of(makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(startMinute), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(endMinute), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(startMinute), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(endMinute), be))); //when @@ -504,18 +511,20 @@ void saveSameThresholdTime(int duration) { //given, when given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be))); + given(reservations.save(any(Reservation.class))) .willReturn(reservation); @@ -545,14 +554,14 @@ void saveReservationTimeUnitException(int additionalStartMinute, int additionalE //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - theDayAfterTomorrowTen.plusMinutes(additionalStartMinute), - theDayAfterTomorrowTen.plusMinutes(additionalEndMinute), + theDayAfterTomorrowTen.plusMinutes(additionalStartMinute).atZone(KST.toZoneId()), + theDayAfterTomorrowTen.plusMinutes(additionalEndMinute).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - theDayAfterTomorrowTen.plusMinutes(additionalStartMinute), - theDayAfterTomorrowTen.plusMinutes(additionalEndMinute), + theDayAfterTomorrowTen.plusMinutes(additionalStartMinute).atZone(KST.toZoneId()), + theDayAfterTomorrowTen.plusMinutes(additionalEndMinute).atZone(KST.toZoneId()), USER_NAME, DESCRIPTION); @@ -593,8 +602,8 @@ void saveReservationMinimumTimeUnitException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -623,8 +632,8 @@ void saveReservationMaximumTimeUnitException() { //when ReservationCreateUpdateWithPasswordRequest reservationCreateUpdateWithPasswordRequest = new ReservationCreateUpdateWithPasswordRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130).atZone(KST.toZoneId()), RESERVATION_PW, USER_NAME, DESCRIPTION); @@ -653,8 +662,8 @@ void updateReservationMinimumDurationTimeException() { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(50).atZone(KST.toZoneId()), USER_NAME, DESCRIPTION); @@ -685,8 +694,8 @@ void updateReservationMaximumDurationTimeException() { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusMinutes(130).atZone(KST.toZoneId()), USER_NAME, DESCRIPTION); @@ -713,18 +722,19 @@ void findReservations() { int duration = 30; List foundReservations = Arrays.asList( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be)); given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(foundReservations); @@ -819,8 +829,9 @@ void findEmptyReservations() { .willReturn(Optional.of(luther)); given(maps.existsById(anyLong())) .willReturn(true); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(Collections.emptyList()); @@ -855,20 +866,20 @@ void findAllReservation() { int duration = 30; List foundReservations = List.of( makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), be), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().minusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().minusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().minusMinutes(duration), fe), makeReservation( - reservationCreateUpdateWithPasswordRequest.getStartDateTime().plusMinutes(duration), - reservationCreateUpdateWithPasswordRequest.getEndDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localStartDateTime().plusMinutes(duration), + reservationCreateUpdateWithPasswordRequest.localEndDateTime().plusMinutes(duration), fe)); List findSpaces = List.of(be, fe); @@ -876,8 +887,9 @@ void findAllReservation() { given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(foundReservations); @@ -903,8 +915,9 @@ void findAllReservationsNotOwner() { .willReturn(Optional.of(sakjung)); given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of(beAmZeroOne, bePmOneTwo)); @@ -929,8 +942,9 @@ void findReservationsNotOwner() { .willReturn(Optional.of(sakjung)); given(maps.findByIdFetch(anyLong())) .willReturn(Optional.of(luther)); - given(reservations.findAllBySpaceIdInAndDate( + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( anyList(), + any(LocalDate.class), any(LocalDate.class))) .willReturn(List.of(beAmZeroOne, bePmOneTwo)); @@ -1036,8 +1050,8 @@ void update(int beforeDay) { // when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(beforeDay), - THE_DAY_AFTER_TOMORROW.atTime(11, 0).minusDays(beforeDay), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).minusDays(beforeDay).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(11, 0).minusDays(beforeDay).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); Long reservationId = reservation.getId(); @@ -1068,8 +1082,8 @@ void updateNoAuthorityException() { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(20, 0), - THE_DAY_AFTER_TOMORROW.atTime(21, 0), + THE_DAY_AFTER_TOMORROW.atTime(20, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(21, 0).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); Long reservationId = reservation.getId(); @@ -1099,8 +1113,8 @@ void updateInvalidEndTimeException(int endTime) { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(12, 0), - THE_DAY_AFTER_TOMORROW.atTime(12, 0).minusHours(endTime), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(12, 0).minusHours(endTime).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); Long reservationId = reservation.getId(); @@ -1129,8 +1143,8 @@ void updateInvalidDateException() { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(10, 0), - THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(10, 0).plusDays(1).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); Long reservationId = reservation.getId(); @@ -1159,15 +1173,18 @@ void updateImpossibleTimeException(int startTime, int endTime) { .willReturn(Optional.of(luther)); given(reservations.findById(anyLong())) .willReturn(Optional.of(reservation)); - given(reservations.findAllBySpaceIdInAndDate(anyList(), any())) + given(reservations.findAllBySpaceIdInAndDateGreaterThanEqualAndDateLessThanEqual( + anyList(), + any(LocalDate.class), + any(LocalDate.class))) .willReturn(Arrays.asList( beAmZeroOne, bePmOneTwo)); //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - bePmOneTwo.getStartTime().plusMinutes(startTime), - bePmOneTwo.getEndTime().plusMinutes(endTime), + TimeZoneUtils.convert(bePmOneTwo.getStartTime(), UTC, KST).plusMinutes(startTime).atZone(KST.toZoneId()), + TimeZoneUtils.convert(bePmOneTwo.getEndTime(), UTC, KST).plusMinutes(endTime).atZone(KST.toZoneId()), reservation.getUserName(), reservation.getDescription()); Long reservationId = reservation.getId(); @@ -1199,8 +1216,8 @@ void updateInvalidTimeSetting(int startTime, int endTime) { //when ReservationCreateUpdateRequest reservationCreateUpdateRequest = new ReservationCreateUpdateRequest( - THE_DAY_AFTER_TOMORROW.atTime(startTime, 0), - THE_DAY_AFTER_TOMORROW.atTime(endTime, 30), + THE_DAY_AFTER_TOMORROW.atTime(startTime, 0).atZone(KST.toZoneId()), + THE_DAY_AFTER_TOMORROW.atTime(endTime, 30).atZone(KST.toZoneId()), CHANGED_NAME, CHANGED_DESCRIPTION); Long reservationId = reservation.getId(); From 753f7839f6545361c0c08a3b85ef9d3906df7b32 Mon Sep 17 00:00:00 2001 From: Jungseok Sung <58401309+sakjung@users.noreply.github.com> Date: Thu, 28 Apr 2022 00:13:34 +0900 Subject: [PATCH 24/24] =?UTF-8?q?fix:=20reservation=20date=20=ED=95=9C?= =?UTF-8?q?=EA=B5=AD=20=EC=8B=9C=EA=B0=84=20=EC=9C=BC=EB=A1=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5,=20=EC=88=98=EC=A0=95=20(#807)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../woowacourse/zzimkkong/service/ReservationService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java index ed22ac854..f9d62f9b8 100644 --- a/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java +++ b/backend/src/main/java/com/woowacourse/zzimkkong/service/ReservationService.java @@ -64,11 +64,12 @@ public ReservationCreateResponse saveReservation( validateAvailability(space, reservationCreateDto, new ExcludeReservationCreateStrategy()); + LocalDate date = TimeZoneUtils.convert(reservationCreateDto.getStartDateTime(), UTC, KST).toLocalDate(); Reservation reservation = reservations.save( Reservation.builder() .startTime(reservationCreateDto.getStartDateTime()) .endTime(reservationCreateDto.getEndDateTime()) - .date(reservationCreateDto.getStartDateTime().toLocalDate()) + .date(date) .password(reservationCreateDto.getPassword()) .userName(reservationCreateDto.getName()) .description(reservationCreateDto.getDescription()) @@ -165,10 +166,11 @@ public SlackResponse updateReservation( validateAvailability(space, reservationUpdateDto, new ExcludeReservationUpdateStrategy(reservation)); + LocalDate date = TimeZoneUtils.convert(reservationUpdateDto.getStartDateTime(), UTC, KST).toLocalDate(); Reservation updateReservation = Reservation.builder() .startTime(reservationUpdateDto.getStartDateTime()) .endTime(reservationUpdateDto.getEndDateTime()) - .date(reservationUpdateDto.getStartDateTime().toLocalDate()) + .date(date) .userName(reservationUpdateDto.getName()) .description(reservationUpdateDto.getDescription()) .space(space)