diff --git a/application/src/main/java/org/togetherjava/tjbot/features/analytics/Metrics.java b/application/src/main/java/org/togetherjava/tjbot/features/analytics/Metrics.java index 9da595eaab..54ce0aa90c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/analytics/Metrics.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/analytics/Metrics.java @@ -44,6 +44,16 @@ public void count(String event) { count(event, Map.of()); } + /** + * Track an event execution with dimensions provided. + * + * @param event the event to save + * @param dimensions the dimensions to save + */ + public void count(String event, Map dimensions) { + count(event, dimensions, true); + } + /** * Track an event execution with additional contextual data. * @@ -53,15 +63,21 @@ public void count(String event) { * "John Smith", channel_name: "chit-chat" etc. This data helps with filtering, grouping, * and analyzing events later. Note: A value for a metric should be a Java primitive * (String, int, double, long float). + * @param doAsync A flag to enable/disable async event process. */ - public void count(String event, Map dimensions) { + void count(String event, Map dimensions, boolean doAsync) { logger.debug("Counting new record for event: {}", event); Instant happenedAt = Instant.now(); - String serializedDimensions = serializeDimensions(dimensions); + String serializedDimensions = dimensions.isEmpty() ? null : serializeDimensions(dimensions); + + Runnable task = () -> processEvent(event, happenedAt, serializedDimensions); - service.submit(() -> processEvent(event, happenedAt, - dimensions.isEmpty() ? null : serializedDimensions)); + if (doAsync) { + service.submit(task); + } else { + task.run(); + } } private static String serializeDimensions(Map dimensions) { @@ -73,6 +89,7 @@ private static String serializeDimensions(Map dimensions) { } /** + * Process event persistence. * * @param event the event to save * @param happenedAt the moment when the event is dispatched @@ -85,5 +102,4 @@ private void processEvent(String event, Instant happenedAt, @Nullable String dim .setDimensions(dimensionsJson) .insert()); } - } diff --git a/application/src/test/java/org/togetherjava/tjbot/features/analytics/MetricsTests.java b/application/src/test/java/org/togetherjava/tjbot/features/analytics/MetricsTests.java new file mode 100644 index 0000000000..8c50971a90 --- /dev/null +++ b/application/src/test/java/org/togetherjava/tjbot/features/analytics/MetricsTests.java @@ -0,0 +1,57 @@ +package org.togetherjava.tjbot.features.analytics; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.togetherjava.tjbot.db.Database; +import org.togetherjava.tjbot.db.generated.tables.MetricEvents; +import org.togetherjava.tjbot.db.generated.tables.records.MetricEventsRecord; + +import java.time.Duration; +import java.time.Instant; +import java.util.Map; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class MetricsTests { + private Database database; + private Metrics metrics; + + @BeforeEach + void setUp() { + database = Database.createMemoryDatabase(MetricEvents.METRIC_EVENTS); + metrics = new Metrics(database); + } + + @Test + void countWithDoAsyncFalsePersists() { + + String expectedEvent = "test-event"; + + Instant expectedHappenedAt = Instant.now(); + + metrics.count(expectedEvent, Map.of(), false); + + MetricEventsRecord eventRecord = Objects.requireNonNull( + database.read(context -> context.selectFrom(MetricEvents.METRIC_EVENTS).fetchOne()), + "event not found"); + + Instant actualHappenedAt = eventRecord.getHappenedAt(); + + assertEquals(expectedEvent, eventRecord.getEvent()); + assertCloseEnough(expectedHappenedAt, actualHappenedAt); + } + + private void assertCloseEnough(Instant expectedHappenedAt, Instant actualHappenedAt) { + + Duration thresholdDuration = Duration.ofMinutes(1); + + Duration actualDuration = Duration.between(expectedHappenedAt, actualHappenedAt); + + boolean isBelowThreshold = actualDuration.compareTo(thresholdDuration) < 0; + + assertTrue(isBelowThreshold); + } +}