From 150c80668f07ad502afe19229f589c8aac0e450b Mon Sep 17 00:00:00 2001 From: ggmaleva Date: Thu, 23 Jun 2022 17:35:30 +0300 Subject: [PATCH] refactoring time bound --- pom.xml | 2 +- .../service/TimeBoundaryServiceImpl.java | 53 +++++++++- .../fraudbusters/DispatchTemplateTest.java | 2 +- .../aggragator/SumAggregatorImplTest.java | 24 ++--- .../service/TimeBoundaryServiceImplTest.java | 100 +++++++++++++----- .../util/DgraphTestAggregationUtils.java | 9 +- 6 files changed, 143 insertions(+), 47 deletions(-) diff --git a/pom.xml b/pom.xml index 55c2c18..a2b5a4c 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 8023 ${server.port} ${management.port} UTF-8 - 1.0.0 + 1.0.2 0.3.1 1.102-269908f 1.21-e4784ab diff --git a/src/main/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImpl.java b/src/main/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImpl.java index 5922ac2..bac29b3 100644 --- a/src/main/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImpl.java +++ b/src/main/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImpl.java @@ -5,24 +5,69 @@ import dev.vality.fraudo.model.TimeWindow; import org.springframework.stereotype.Service; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; +import static dev.vality.fraudo.constant.TimeUnit.*; + @Service public class TimeBoundaryServiceImpl implements TimeBoundaryService { @Override public TimeBoundDto getBoundary(Instant target, TimeWindow timeWindow) { - Instant left = calculateTimeBoundary(target, timeWindow.getStartWindowTime(), timeWindow.getTimeUnit()); - Instant right = calculateTimeBoundary(target, timeWindow.getEndWindowTime(), timeWindow.getTimeUnit()); + ChronoUnit chronoUnit = resolveTimeUnit(timeWindow.getTimeUnit()); + if (CALENDAR_MONTHS.equals(timeWindow.getTimeUnit())) { + return buildCalendarMonthsTimeBound(target, timeWindow, chronoUnit); + } + Instant left = target.minus(timeWindow.getStart(), chronoUnit); + Instant right = target.minus(timeWindow.getEnd(), chronoUnit); return TimeBoundDto.builder() .left(left) .right(right) .build(); } - private Instant calculateTimeBoundary(Instant target, Long timeInterval, ChronoUnit timeUnit) { - return timeInterval != null ? target.minus(timeInterval, timeUnit) : target; + private ChronoUnit resolveTimeUnit(String timeUnit) { + return switch (timeUnit) { + case MINUTES -> ChronoUnit.MINUTES; + case DAYS, CALENDAR_MONTHS -> ChronoUnit.DAYS; + default -> ChronoUnit.HOURS; + }; } + private TimeBoundDto buildCalendarMonthsTimeBound(Instant target, TimeWindow timeWindow, ChronoUnit chronoUnit) { + LocalDate targetDate = LocalDate.ofInstant(target, ZoneId.systemDefault()); + int start = calculateStart(timeWindow.getStart(), targetDate); + int end = calculateEnd(timeWindow.getEnd(), targetDate); + Instant left = targetDate + .minus(start, chronoUnit) + .atStartOfDay(ZoneOffset.UTC) + .toInstant() + .truncatedTo(ChronoUnit.DAYS); + Instant right = target + .minus(end, chronoUnit); + return TimeBoundDto.builder() + .left(left) + .right(right) + .build(); + } + + private int calculateStart(int start, LocalDate targetDate) { + int startInDays = targetDate.getDayOfMonth() - 1; + for (int i = 1; i <= start - 1; i++) { + startInDays += targetDate.minusMonths(i).lengthOfMonth(); + } + return startInDays; + } + + private int calculateEnd(int end, LocalDate targetDate) { + int endInDays = 0; + for (int i = 1; i <= end; i++) { + endInDays += targetDate.minusMonths(i).lengthOfMonth(); + } + return endInDays; + } } diff --git a/src/test/java/dev/vality/fraudbusters/DispatchTemplateTest.java b/src/test/java/dev/vality/fraudbusters/DispatchTemplateTest.java index 6e6d265..60eced4 100644 --- a/src/test/java/dev/vality/fraudbusters/DispatchTemplateTest.java +++ b/src/test/java/dev/vality/fraudbusters/DispatchTemplateTest.java @@ -78,7 +78,7 @@ class DispatchTemplateTest extends JUnit5IntegrationTest { //check that global reference created Unreliables.retryUntilTrue(TIMEOUT, TimeUnit.SECONDS, () -> { String result = referencePoolImpl.get(TemplateLevel.GLOBAL.name()); - if (StringUtils.isEmpty(result)) { + if (!StringUtils.hasLength(result)) { return false; } assertEquals(id, result); diff --git a/src/test/java/dev/vality/fraudbusters/fraud/aggragator/SumAggregatorImplTest.java b/src/test/java/dev/vality/fraudbusters/fraud/aggragator/SumAggregatorImplTest.java index c025931..661d33d 100644 --- a/src/test/java/dev/vality/fraudbusters/fraud/aggragator/SumAggregatorImplTest.java +++ b/src/test/java/dev/vality/fraudbusters/fraud/aggragator/SumAggregatorImplTest.java @@ -16,8 +16,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.temporal.ChronoUnit; - +import static dev.vality.fraudo.constant.TimeUnit.MINUTES; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -62,7 +61,7 @@ public class SumAggregatorImplTest { Double some = sumAggregator.sum( PaymentCheckedField.BIN, paymentModel, - TimeWindow.builder().startWindowTime(1444L).timeUnit(ChronoUnit.MINUTES).build(), + TimeWindow.builder().start(1444).timeUnit(MINUTES).build(), null ); @@ -74,9 +73,9 @@ public class SumAggregatorImplTest { PaymentModel paymentModel = new PaymentModel(); paymentModel.setAmount(1L); TimeWindow.TimeWindowBuilder timeWindowBuilder = TimeWindow.builder() - .startWindowTime(1444L) - .timeUnit(ChronoUnit.MINUTES) - .endWindowTime(400L); + .start(1444) + .timeUnit(MINUTES) + .end(400); when(paymentRepository.sumOperationByFieldWithGroupBy(any(), any(), any(), any(), any())).thenReturn(1050100L); Double sum = sumAggregator.sum(PaymentCheckedField.BIN, paymentModel, timeWindowBuilder.build(), null); @@ -84,9 +83,8 @@ public class SumAggregatorImplTest { assertEquals(Double.valueOf(1050101), sum); timeWindowBuilder = TimeWindow.builder() - .startWindowTime(1444L) - .timeUnit(ChronoUnit.MINUTES) - .endWindowTime(null); + .start(1444) + .timeUnit(MINUTES); sum = sumAggregator.sum(PaymentCheckedField.BIN, paymentModel, timeWindowBuilder.build(), null); assertEquals(Double.valueOf(1050101), sum); @@ -97,8 +95,8 @@ public class SumAggregatorImplTest { when(paymentRepository.sumOperationSuccessWithGroupBy(any(), any(), any(), any(), any())).thenReturn(1050100L); Double some = sumAggregator.sumSuccess(PaymentCheckedField.BIN, new PaymentModel(), TimeWindow.builder() - .startWindowTime(1444L) - .timeUnit(ChronoUnit.MINUTES) + .start(1444) + .timeUnit(MINUTES) .build(), null ); @@ -112,8 +110,8 @@ public class SumAggregatorImplTest { .thenReturn(1050100L); Double some = sumAggregator.sumError(PaymentCheckedField.BIN, new PaymentModel(), TimeWindow.builder() - .startWindowTime(1444L) - .timeUnit(ChronoUnit.MINUTES) + .start(1444) + .timeUnit(MINUTES) .build(), null, null diff --git a/src/test/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImplTest.java b/src/test/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImplTest.java index 0bca18c..5c81fa0 100644 --- a/src/test/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImplTest.java +++ b/src/test/java/dev/vality/fraudbusters/service/TimeBoundaryServiceImplTest.java @@ -4,31 +4,24 @@ import dev.vality.fraudo.model.TimeWindow; import org.junit.jupiter.api.Test; import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; +import static dev.vality.fraudo.constant.TimeUnit.*; import static org.junit.jupiter.api.Assertions.assertEquals; class TimeBoundaryServiceImplTest { - private TimeBoundaryService timeBoundaryService = new TimeBoundaryServiceImpl(); - - @Test - void getBoundaryWithEmptyTimeWindow() { - Instant now = Instant.now(); - - var timeBound = timeBoundaryService.getBoundary(now, TimeWindow.builder().build()); - - assertEquals(now.truncatedTo(ChronoUnit.SECONDS), timeBound.getLeft().truncatedTo(ChronoUnit.SECONDS)); - assertEquals(now.truncatedTo(ChronoUnit.SECONDS), timeBound.getRight().truncatedTo(ChronoUnit.SECONDS)); - } + private final TimeBoundaryService timeBoundaryService = new TimeBoundaryServiceImpl(); @Test void getBoundaryWithOnlyStartTime() { Instant now = Instant.now(); - long startValue = 5L; + int startValue = 5; TimeWindow timeWindow = TimeWindow.builder() - .timeUnit(ChronoUnit.MINUTES) - .startWindowTime(startValue) + .timeUnit(MINUTES) + .start(5) .build(); var timeBound = timeBoundaryService.getBoundary(now, timeWindow); @@ -41,12 +34,12 @@ class TimeBoundaryServiceImplTest { @Test void getBoundaryWithDaysUnit() { Instant now = Instant.now(); - long startValue = 5L; - long endValue = 2L; + int startValue = 5; + int endValue = 2; TimeWindow timeWindow = TimeWindow.builder() - .timeUnit(ChronoUnit.DAYS) - .startWindowTime(startValue) - .endWindowTime(endValue) + .timeUnit(DAYS) + .start(startValue) + .end(endValue) .build(); var timeBound = timeBoundaryService.getBoundary(now, timeWindow); @@ -60,12 +53,12 @@ class TimeBoundaryServiceImplTest { @Test void getBoundaryWithHoursUnit() { Instant now = Instant.now(); - long startValue = 5L; - long endValue = 2L; + int startValue = 5; + int endValue = 2; TimeWindow timeWindow = TimeWindow.builder() - .timeUnit(ChronoUnit.HOURS) - .startWindowTime(startValue) - .endWindowTime(endValue) + .timeUnit(HOURS) + .start(startValue) + .end(endValue) .build(); var timeBound = timeBoundaryService.getBoundary(now, timeWindow); @@ -76,4 +69,63 @@ class TimeBoundaryServiceImplTest { timeBound.getRight().truncatedTo(ChronoUnit.SECONDS)); } + @Test + void withOneCalMonthsTimeUnitTest() { + Instant now = Instant.now(); + int startValue = 1; + TimeWindow timeWindow = TimeWindow.builder() + .timeUnit(CALENDAR_MONTHS) + .start(startValue) + .build(); + + var timeBound = timeBoundaryService.getBoundary(now, timeWindow); + + LocalDate dateNow = LocalDate.ofInstant(now, ZoneOffset.UTC); + assertEquals(now.minus(dateNow.getDayOfMonth() - 1, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS), + timeBound.getLeft()); + assertEquals(now.truncatedTo(ChronoUnit.SECONDS), timeBound.getRight().truncatedTo(ChronoUnit.SECONDS)); + } + + @Test + void withThreeCalMonthsTimeUnitTest() { + Instant now = Instant.now(); + int startValue = 3; + TimeWindow timeWindow = TimeWindow.builder() + .timeUnit(CALENDAR_MONTHS) + .start(startValue) + .build(); + + var timeBound = timeBoundaryService.getBoundary(now, timeWindow); + + LocalDate dateNow = LocalDate.ofInstant(now, ZoneOffset.UTC); + int leftForThreeCalMonths = dateNow.getDayOfMonth() - 1 + dateNow.minusMonths(1).lengthOfMonth() + + dateNow.minusMonths(2).lengthOfMonth(); + assertEquals(now.minus(leftForThreeCalMonths, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS), + timeBound.getLeft()); + assertEquals(now.truncatedTo(ChronoUnit.SECONDS), timeBound.getRight().truncatedTo(ChronoUnit.SECONDS)); + } + + @Test + void withCalMonthsTimeUnitAndWithEndTimeTest() { + Instant now = Instant.now(); + int startValue = 4; + int endValue = 2; + TimeWindow timeWindow = TimeWindow.builder() + .timeUnit(CALENDAR_MONTHS) + .start(startValue) + .end(endValue) + .build(); + + var timeBound = timeBoundaryService.getBoundary(now, timeWindow); + + LocalDate dateNow = LocalDate.ofInstant(now, ZoneOffset.UTC); + int startForFourCalMonths = dateNow.getDayOfMonth() - 1 + dateNow.minusMonths(1).lengthOfMonth() + + dateNow.minusMonths(2).lengthOfMonth() + dateNow.minusMonths(3).lengthOfMonth(); + int endForTwoCalMonths = dateNow.minusMonths(1).lengthOfMonth() + dateNow.minusMonths(2).lengthOfMonth(); + assertEquals(now.minus(startForFourCalMonths, ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS), + timeBound.getLeft()); + assertEquals(now.minus(endForTwoCalMonths, ChronoUnit.DAYS).truncatedTo(ChronoUnit.SECONDS), + timeBound.getRight().truncatedTo(ChronoUnit.SECONDS)); + } + } \ No newline at end of file diff --git a/src/test/java/dev/vality/fraudbusters/util/DgraphTestAggregationUtils.java b/src/test/java/dev/vality/fraudbusters/util/DgraphTestAggregationUtils.java index a8774e7..f02dc74 100644 --- a/src/test/java/dev/vality/fraudbusters/util/DgraphTestAggregationUtils.java +++ b/src/test/java/dev/vality/fraudbusters/util/DgraphTestAggregationUtils.java @@ -8,20 +8,21 @@ import dev.vality.fraudo.model.TimeWindow; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import static dev.vality.fraudo.constant.TimeUnit.MINUTES; + @NoArgsConstructor(access = AccessLevel.PRIVATE) public final class DgraphTestAggregationUtils { public static TimeWindow createTestTimeWindow() { return TimeWindow.builder() - .startWindowTime(600_000L) - .endWindowTime(0L) - .timeUnit(ChronoUnit.MINUTES) + .start(600_000) + .end(0) + .timeUnit(MINUTES) .build(); }