refactoring time bound

This commit is contained in:
ggmaleva 2022-06-23 17:35:30 +03:00
parent d887242d2b
commit 150c80668f
6 changed files with 143 additions and 47 deletions

View File

@ -21,7 +21,7 @@
<management.port>8023</management.port>
<exposed.ports>${server.port} ${management.port}</exposed.ports>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<fraudo.version>1.0.0</fraudo.version>
<fraudo.version>1.0.2</fraudo.version>
<clickhouse-jdbc.version>0.3.1</clickhouse-jdbc.version>
<fraudbusters-proto.version>1.102-269908f</fraudbusters-proto.version>
<machinegun-proto.version>1.21-e4784ab</machinegun-proto.version>

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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));
}
}

View File

@ -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();
}