From fa92304d34cd3610b1708ba5dc594e451a6597ae Mon Sep 17 00:00:00 2001 From: Sapotille Orange <15519-x-SaOrang@users.noreply.gitlab.inria.fr> Date: Wed, 24 Aug 2022 10:58:34 +0200 Subject: [PATCH 01/27] feat: update postgresql container version for integration tests to 13.7 --- .../gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java | 2 +- .../gouv/stopc/robert/pushnotif/server/ws/test/PsqlManager.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java index 685fe35..bec75e2 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java @@ -51,7 +51,7 @@ public class PsqlManager implements TestExecutionListener { } private static final JdbcDatabaseContainer POSTGRES = new PostgreSQLContainer( - DockerImageName.parse("postgres:9.6") + DockerImageName.parse("postgres:13.7") ); static { diff --git a/robert-push-notif-server-ws-rest/src/test/java/fr/gouv/stopc/robert/pushnotif/server/ws/test/PsqlManager.java b/robert-push-notif-server-ws-rest/src/test/java/fr/gouv/stopc/robert/pushnotif/server/ws/test/PsqlManager.java index 4108be4..f10f6a6 100644 --- a/robert-push-notif-server-ws-rest/src/test/java/fr/gouv/stopc/robert/pushnotif/server/ws/test/PsqlManager.java +++ b/robert-push-notif-server-ws-rest/src/test/java/fr/gouv/stopc/robert/pushnotif/server/ws/test/PsqlManager.java @@ -22,7 +22,7 @@ public class PsqlManager implements TestExecutionListener { private static JdbcTemplate jdbcTemplate; private static final JdbcDatabaseContainer POSTGRES = new PostgreSQLContainer( - DockerImageName.parse("postgres:9.6") + DockerImageName.parse("postgres:13.7") ); public static Instant defaultNextPlannedPushDate = LocalDateTime.now().toInstant(ZoneOffset.UTC); -- GitLab From 5a85dfd8b0fa1b43606627b51fd00f9bfe945c65 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 14:19:12 +0000 Subject: [PATCH 02/27] refactor(scheduler): reword NotificationHandler to ApnsNotificationHandler --- .../scheduler/apns/PushInfoNotificationHandler.java | 4 ++-- ...otificationHandler.java => ApnsNotificationHandler.java} | 2 +- .../pushnotif/scheduler/apns/template/ApnsOperations.java | 2 +- .../pushnotif/scheduler/apns/template/ApnsTemplate.java | 2 +- .../apns/template/DelegateNotificationHandler.java | 6 +++--- .../scheduler/apns/template/FailoverApnsTemplate.java | 4 ++-- .../scheduler/apns/template/MonitoringApnsTemplate.java | 2 +- .../scheduler/apns/template/RateLimitingApnsTemplate.java | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/{NotificationHandler.java => ApnsNotificationHandler.java} (97%) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java index 7aaef4e..0cb55b9 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java @@ -4,7 +4,7 @@ import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; -import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.NotificationHandler; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoDao; import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; import lombok.RequiredArgsConstructor; @@ -20,7 +20,7 @@ import static java.time.temporal.ChronoUnit.MINUTES; import static org.apache.commons.lang3.StringUtils.truncate; @RequiredArgsConstructor -public class PushInfoNotificationHandler implements NotificationHandler { +public class PushInfoNotificationHandler implements ApnsNotificationHandler { private final PushInfo notificationData; diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/NotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java similarity index 97% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/NotificationHandler.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java index f8e5d75..2f3b3fb 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/NotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java @@ -5,7 +5,7 @@ import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; -public interface NotificationHandler { +public interface ApnsNotificationHandler { /** * @return Apple push notification device token diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java index f68c386..0096fd5 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java @@ -4,7 +4,7 @@ import java.time.Duration; public interface ApnsOperations extends AutoCloseable { - void sendNotification(NotificationHandler handler); + void sendNotification(ApnsNotificationHandler handler); void waitUntilNoActivity(Duration toleranceDuration); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index b3069cf..d0ec2fc 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -23,7 +23,7 @@ public class ApnsTemplate implements ApnsOperations { private final ApnsClient apnsClient; - public void sendNotification(final NotificationHandler notificationHandler) { + public void sendNotification(final ApnsNotificationHandler notificationHandler) { pendingNotifications.incrementAndGet(); final var sendNotificationFuture = apnsClient.sendNotification(notificationHandler.buildNotification()); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java index 7c392e9..c7fb63a 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java @@ -5,12 +5,12 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import lombok.RequiredArgsConstructor; /** - * Base class for {@link NotificationHandler} decorators. + * Base class for {@link ApnsNotificationHandler} decorators. */ @RequiredArgsConstructor -public class DelegateNotificationHandler implements NotificationHandler { +public class DelegateNotificationHandler implements ApnsNotificationHandler { - private final NotificationHandler delegate; + private final ApnsNotificationHandler delegate; @Override public String getAppleToken() { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index f8f1fc1..f88f850 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -21,7 +21,7 @@ public class FailoverApnsTemplate implements ApnsOperations { private final List inactiveRejectionReasons; @Override - public void sendNotification(final NotificationHandler notificationHandler) { + public void sendNotification(final ApnsNotificationHandler notificationHandler) { final var apnsClientsQueue = new ConcurrentLinkedQueue<>(apnsDelegates); @@ -33,7 +33,7 @@ public class FailoverApnsTemplate implements ApnsOperations { apnsDelegates.parallelStream().forEach(it -> it.waitUntilNoActivity(toleranceDuration)); } - private void sendNotification(final NotificationHandler notificationHandler, + private void sendNotification(final ApnsNotificationHandler notificationHandler, final ConcurrentLinkedQueue queue) { final var client = queue.poll(); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java index c8be6e7..5bd1a56 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java @@ -91,7 +91,7 @@ public class MonitoringApnsTemplate implements ApnsOperations { } @Override - public void sendNotification(final NotificationHandler notificationHandler) { + public void sendNotification(final ApnsNotificationHandler notificationHandler) { pendingNotifications.incrementAndGet(); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java index c0ba74e..f40898f 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java @@ -44,7 +44,7 @@ public class RateLimitingApnsTemplate implements ApnsOperations { } @Override - public void sendNotification(final NotificationHandler notificationHandler) { + public void sendNotification(final ApnsNotificationHandler notificationHandler) { try { semaphore.acquire(); -- GitLab From 08c4b45e2267db87f595b597b88c7d86c118f712 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 14:25:49 +0000 Subject: [PATCH 03/27] refactor(scheduler): move notif construction out of NotificationHandler --- .../robert/pushnotif/scheduler/Scheduler.java | 26 ++++++++++++++++- .../apns/PushInfoNotificationHandler.java | 29 ------------------- .../template/ApnsNotificationHandler.java | 14 --------- .../apns/template/ApnsOperations.java | 4 ++- .../scheduler/apns/template/ApnsTemplate.java | 6 ++-- .../template/DelegateNotificationHandler.java | 11 ------- .../apns/template/FailoverApnsTemplate.java | 13 +++++---- .../apns/template/MonitoringApnsTemplate.java | 6 ++-- .../template/RateLimitingApnsTemplate.java | 6 ++-- 9 files changed, 48 insertions(+), 67 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 8219536..399c80d 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -1,5 +1,9 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; +import com.eatthepath.pushy.apns.DeliveryPriority; +import com.eatthepath.pushy.apns.PushType; +import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; +import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.PushInfoNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; @@ -18,6 +22,10 @@ import org.springframework.stereotype.Service; import java.sql.ResultSet; import java.sql.SQLException; import java.time.Duration; +import java.time.Instant; + +import static com.eatthepath.pushy.apns.util.SimpleApnsPushNotification.DEFAULT_EXPIRATION_PERIOD; +import static com.eatthepath.pushy.apns.util.TokenUtil.sanitizeTokenString; @Slf4j @Service @@ -68,7 +76,23 @@ public class Scheduler { handler.updateNextPlannedPushToRandomTomorrow(); - apnsTemplate.sendNotification(handler); + apnsTemplate.sendNotification(buildNotification(pushInfo.getToken()), handler); } } + + public SimpleApnsPushNotification buildNotification(final String apnsToken) { + final var payload = new SimpleApnsPayloadBuilder() + .setContentAvailable(true) + .setBadgeNumber(0) + .build(); + + return new SimpleApnsPushNotification( + sanitizeTokenString(apnsToken).toLowerCase(), + robertPushServerProperties.getApns().getTopic(), + payload, + Instant.now().plus(DEFAULT_EXPIRATION_PERIOD), + DeliveryPriority.IMMEDIATE, + PushType.BACKGROUND + ); + } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java index 0cb55b9..446da27 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java @@ -1,9 +1,5 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns; -import com.eatthepath.pushy.apns.DeliveryPriority; -import com.eatthepath.pushy.apns.PushType; -import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; -import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoDao; import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; @@ -14,8 +10,6 @@ import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.concurrent.ThreadLocalRandom; -import static com.eatthepath.pushy.apns.util.SimpleApnsPushNotification.DEFAULT_EXPIRATION_PERIOD; -import static com.eatthepath.pushy.apns.util.TokenUtil.sanitizeTokenString; import static java.time.temporal.ChronoUnit.MINUTES; import static org.apache.commons.lang3.StringUtils.truncate; @@ -32,11 +26,6 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { private final int maxPushHour; - @Override - public String getAppleToken() { - return notificationData.getToken(); - } - @Override public void onSuccess() { notificationData.setLastSuccessfulPush(Instant.now()); @@ -65,24 +54,6 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { notificationData.setActive(false); } - @Override - public SimpleApnsPushNotification buildNotification() { - - final String payload = new SimpleApnsPayloadBuilder() - .setContentAvailable(true) - .setBadgeNumber(0) - .build(); - - return new SimpleApnsPushNotification( - sanitizeTokenString(getAppleToken()).toLowerCase(), - apnsTopic, - payload, - Instant.now().plus(DEFAULT_EXPIRATION_PERIOD), - DeliveryPriority.IMMEDIATE, - PushType.BACKGROUND - ); - } - public void updateNextPlannedPushToRandomTomorrow() { notificationData.setNextPlannedPush(generateDateTomorrowBetweenBounds(notificationData.getTimezone())); pushInfoDao.updateNextPlannedPushDate(notificationData); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java index 2f3b3fb..5a82313 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java @@ -1,17 +1,10 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; -import com.eatthepath.pushy.apns.ApnsClient; -import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; public interface ApnsNotificationHandler { - /** - * @return Apple push notification device token - */ - String getAppleToken(); - /** * Called when the notification request is accepted */ @@ -42,11 +35,4 @@ public interface ApnsNotificationHandler { * @param rejectionMessage rejected push notification request response message */ void disableToken(); - - /** - * @param topic: Apple Push Notification topic - * @return Push Notification for Apple Push Notification service - * @see ApnsClient#sendNotification(ApnsPushNotification) - */ - ApnsPushNotification buildNotification(); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java index 0096fd5..5e7a65f 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java @@ -1,10 +1,12 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; +import com.eatthepath.pushy.apns.ApnsPushNotification; + import java.time.Duration; public interface ApnsOperations extends AutoCloseable { - void sendNotification(ApnsNotificationHandler handler); + void sendNotification(ApnsPushNotification notification, ApnsNotificationHandler handler); void waitUntilNoActivity(Duration toleranceDuration); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index d0ec2fc..42308cd 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -1,6 +1,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; import com.eatthepath.pushy.apns.ApnsClient; +import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,10 +24,11 @@ public class ApnsTemplate implements ApnsOperations { private final ApnsClient apnsClient; - public void sendNotification(final ApnsNotificationHandler notificationHandler) { + public void sendNotification(final ApnsPushNotification notification, + final ApnsNotificationHandler notificationHandler) { pendingNotifications.incrementAndGet(); - final var sendNotificationFuture = apnsClient.sendNotification(notificationHandler.buildNotification()); + final var sendNotificationFuture = apnsClient.sendNotification(notification); sendNotificationFuture.whenComplete((response, cause) -> { pendingNotifications.decrementAndGet(); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java index c7fb63a..86c16a2 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java @@ -1,6 +1,5 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; -import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import lombok.RequiredArgsConstructor; @@ -12,11 +11,6 @@ public class DelegateNotificationHandler implements ApnsNotificationHandler { private final ApnsNotificationHandler delegate; - @Override - public String getAppleToken() { - return delegate.getAppleToken(); - } - @Override public void onSuccess() { delegate.onSuccess(); @@ -36,9 +30,4 @@ public class DelegateNotificationHandler implements ApnsNotificationHandler { public void disableToken() { delegate.disableToken(); } - - @Override - public ApnsPushNotification buildNotification() { - return delegate.buildNotification(); - } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index f88f850..94976a1 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -1,5 +1,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; +import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,11 +22,12 @@ public class FailoverApnsTemplate implements ApnsOperations { private final List inactiveRejectionReasons; @Override - public void sendNotification(final ApnsNotificationHandler notificationHandler) { + public void sendNotification(final ApnsPushNotification notification, + final ApnsNotificationHandler notificationHandler) { final var apnsClientsQueue = new ConcurrentLinkedQueue<>(apnsDelegates); - sendNotification(notificationHandler, apnsClientsQueue); + sendNotification(notification, notificationHandler, apnsClientsQueue); } @Override @@ -33,12 +35,13 @@ public class FailoverApnsTemplate implements ApnsOperations { apnsDelegates.parallelStream().forEach(it -> it.waitUntilNoActivity(toleranceDuration)); } - private void sendNotification(final ApnsNotificationHandler notificationHandler, + private void sendNotification(final ApnsPushNotification notification, + final ApnsNotificationHandler notificationHandler, final ConcurrentLinkedQueue queue) { final var client = queue.poll(); if (client != null) { - client.sendNotification(new DelegateNotificationHandler(notificationHandler) { + client.sendNotification(notification, new DelegateNotificationHandler(notificationHandler) { @Override public void onRejection(final RejectionReason reason) { @@ -46,7 +49,7 @@ public class FailoverApnsTemplate implements ApnsOperations { // rejection reason means we must try on next APN server if (!queue.isEmpty()) { // try next apn client in the queue - sendNotification(notificationHandler, queue); + sendNotification(notification, notificationHandler, queue); } else { // notification was rejected on every client, then disable token super.disableToken(); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java index 5bd1a56..ea9e07d 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java @@ -1,5 +1,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; +import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import io.micrometer.core.instrument.Gauge; @@ -91,7 +92,8 @@ public class MonitoringApnsTemplate implements ApnsOperations { } @Override - public void sendNotification(final ApnsNotificationHandler notificationHandler) { + public void sendNotification(final ApnsPushNotification notification, + final ApnsNotificationHandler notificationHandler) { pendingNotifications.incrementAndGet(); @@ -122,7 +124,7 @@ public class MonitoringApnsTemplate implements ApnsOperations { } }; - delegate.sendNotification(measuringHandler); + delegate.sendNotification(notification, measuringHandler); } @Override diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java index f40898f..025405e 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java @@ -1,5 +1,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; +import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; @@ -44,7 +45,8 @@ public class RateLimitingApnsTemplate implements ApnsOperations { } @Override - public void sendNotification(final ApnsNotificationHandler notificationHandler) { + public void sendNotification(final ApnsPushNotification notification, + final ApnsNotificationHandler notificationHandler) { try { semaphore.acquire(); @@ -73,7 +75,7 @@ public class RateLimitingApnsTemplate implements ApnsOperations { super.onError(reason); } }; - delegate.sendNotification(limitedHandler); + delegate.sendNotification(notification, limitedHandler); } @Override -- GitLab From e860a1709b83734d7d3ea70d81f808893489f037 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 14:46:38 +0000 Subject: [PATCH 04/27] refactor(scheduler): reword SendNotification row callback handler --- .../stopc/robert/pushnotif/scheduler/Scheduler.java | 12 ++++-------- .../pushnotif/scheduler/data/PushInfoRowMapper.java | 2 ++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 399c80d..fd0afba 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -9,7 +9,6 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoDao; import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoRowMapper; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; import io.micrometer.core.annotation.Counted; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -34,8 +33,6 @@ public class Scheduler { private final JdbcTemplate jdbcTemplate; - private final PushInfoRowMapper rowMapper = new PushInfoRowMapper(); - private final PushInfoDao pushInfoDao; private final RobertPushServerProperties robertPushServerProperties; @@ -51,22 +48,21 @@ public class Scheduler { // basis. jdbcTemplate.query( "select * from push where active = true and deleted = false and next_planned_push <= now()", - new PushNotificationRowCallbackHandler() + new SendNotificationRowCallbackHandler() ); apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); } - @RequiredArgsConstructor - private class PushNotificationRowCallbackHandler implements RowCallbackHandler { + private class SendNotificationRowCallbackHandler implements RowCallbackHandler { @Override public void processRow(final ResultSet resultSet) throws SQLException { - PushInfo pushInfo = rowMapper.mapRow(resultSet, resultSet.getRow()); + final var pushInfo = PushInfoRowMapper.INSTANCE.mapRow(resultSet, resultSet.getRow()); // set the next planned push to be sure the notification could not be sent 2 // times the same day - PushInfoNotificationHandler handler = new PushInfoNotificationHandler( + final var handler = new PushInfoNotificationHandler( pushInfo, pushInfoDao, robertPushServerProperties.getApns().getTopic(), diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java index 0e46fd1..526a620 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java @@ -10,6 +10,8 @@ import static fr.gouv.stopc.robert.pushnotif.scheduler.data.InstantTimestampConv public class PushInfoRowMapper implements RowMapper { + public static final RowMapper INSTANCE = new PushInfoRowMapper(); + @Override public PushInfo mapRow(ResultSet resultSet, int i) throws SQLException { return PushInfo.builder() -- GitLab From 7c5c774732be66a16c33187532d9d3e53d99e04f Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 15:06:49 +0000 Subject: [PATCH 05/27] refactor(scheduler): rename data package to repository --- .../robert/pushnotif/scheduler/Scheduler.java | 8 ++++---- .../apns/PushInfoNotificationHandler.java | 14 +++++++------- .../InstantTimestampConverter.java | 2 +- .../PushInfoRepository.java} | 8 ++++---- .../{data => repository}/PushInfoRowMapper.java | 6 +++--- .../{data => repository}/model/PushInfo.java | 2 +- .../pushnotif/scheduler/SchedulerNominalTest.java | 2 +- .../scheduler/SchedulerVolumetryTest.java | 2 +- .../scheduler/SchedulerWithTwoApnsServerTest.java | 2 +- .../ratelimiting/SchedulerRateLimiting1sTest.java | 2 +- .../ratelimiting/SchedulerRateLimiting2sTest.java | 2 +- .../pushnotif/scheduler/test/PsqlManager.java | 8 ++++---- 12 files changed, 29 insertions(+), 29 deletions(-) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/{data => repository}/InstantTimestampConverter.java (87%) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/{data/PushInfoDao.java => repository/PushInfoRepository.java} (89%) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/{data => repository}/PushInfoRowMapper.java (84%) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/{data => repository}/model/PushInfo.java (88%) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index fd0afba..c3499ca 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -7,8 +7,8 @@ import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.PushInfoNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoDao; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoRowMapper; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRowMapper; import io.micrometer.core.annotation.Counted; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -33,7 +33,7 @@ public class Scheduler { private final JdbcTemplate jdbcTemplate; - private final PushInfoDao pushInfoDao; + private final PushInfoRepository pushInfoRepository; private final RobertPushServerProperties robertPushServerProperties; @@ -64,7 +64,7 @@ public class Scheduler { // times the same day final var handler = new PushInfoNotificationHandler( pushInfo, - pushInfoDao, + pushInfoRepository, robertPushServerProperties.getApns().getTopic(), robertPushServerProperties.getMinPushHour(), robertPushServerProperties.getMaxPushHour() diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java index 446da27..35fa73b 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java @@ -1,8 +1,8 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoDao; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; import java.time.Instant; @@ -18,7 +18,7 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { private final PushInfo notificationData; - private final PushInfoDao pushInfoDao; + private final PushInfoRepository pushInfoRepository; private final String apnsTopic; @@ -30,7 +30,7 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { public void onSuccess() { notificationData.setLastSuccessfulPush(Instant.now()); notificationData.setSuccessfulPushSent(notificationData.getSuccessfulPushSent() + 1); - pushInfoDao.updateSuccessFulPushedNotif(notificationData); + pushInfoRepository.updateSuccessFulPushedNotif(notificationData); } @Override @@ -38,7 +38,7 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { notificationData.setLastErrorCode(reason.getValue()); notificationData.setLastFailurePush(Instant.now()); notificationData.setFailedPushSent(notificationData.getFailedPushSent() + 1); - pushInfoDao.updateFailurePushedNotif(notificationData); + pushInfoRepository.updateFailurePushedNotif(notificationData); } @Override @@ -46,7 +46,7 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { notificationData.setLastErrorCode(truncate(cause.getMessage(), 255)); notificationData.setLastFailurePush(Instant.now()); notificationData.setFailedPushSent(notificationData.getFailedPushSent() + 1); - pushInfoDao.updateFailurePushedNotif(notificationData); + pushInfoRepository.updateFailurePushedNotif(notificationData); } @Override @@ -56,7 +56,7 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { public void updateNextPlannedPushToRandomTomorrow() { notificationData.setNextPlannedPush(generateDateTomorrowBetweenBounds(notificationData.getTimezone())); - pushInfoDao.updateNextPlannedPushDate(notificationData); + pushInfoRepository.updateNextPlannedPushDate(notificationData); } private Instant generateDateTomorrowBetweenBounds(final String timezone) { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/InstantTimestampConverter.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java similarity index 87% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/InstantTimestampConverter.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java index 7e320a3..db855ae 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/InstantTimestampConverter.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java @@ -1,4 +1,4 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.data; +package fr.gouv.stopc.robert.pushnotif.scheduler.repository; import lombok.experimental.UtilityClass; diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoDao.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java similarity index 89% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoDao.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java index 3df703d..1c3a2b7 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoDao.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java @@ -1,17 +1,17 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.data; +package fr.gouv.stopc.robert.pushnotif.scheduler.repository; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import static fr.gouv.stopc.robert.pushnotif.scheduler.data.InstantTimestampConverter.convertInstantToTimestamp; +import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertInstantToTimestamp; @Component @RequiredArgsConstructor -public class PushInfoDao { +public class PushInfoRepository { private final NamedParameterJdbcTemplate jdbcTemplate; diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java similarity index 84% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java index 526a620..44d7d7f 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/PushInfoRowMapper.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java @@ -1,12 +1,12 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.data; +package fr.gouv.stopc.robert.pushnotif.scheduler.repository; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import org.springframework.jdbc.core.RowMapper; import java.sql.ResultSet; import java.sql.SQLException; -import static fr.gouv.stopc.robert.pushnotif.scheduler.data.InstantTimestampConverter.convertTimestampToInstant; +import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertTimestampToInstant; public class PushInfoRowMapper implements RowMapper { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/model/PushInfo.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java similarity index 88% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/model/PushInfo.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java index 4f8d8a5..f5b6cb0 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/data/model/PushInfo.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java @@ -1,4 +1,4 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.data.model; +package fr.gouv.stopc.robert.pushnotif.scheduler.repository.model; import lombok.Builder; import lombok.Data; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index b94f213..6751ec7 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -3,7 +3,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java index 095b7cf..8517dfd 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java @@ -1,6 +1,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.awaitility.Awaitility; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index 574c4a5..9111af3 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -3,7 +3,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.awaitility.Awaitility; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java index 9366568..6c6696b 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java @@ -1,7 +1,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.ratelimiting; import fr.gouv.stopc.robert.pushnotif.scheduler.Scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.junit.jupiter.params.ParameterizedTest; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java index c7468e6..151f123 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java @@ -1,7 +1,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.ratelimiting; import fr.gouv.stopc.robert.pushnotif.scheduler.Scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.junit.jupiter.params.ParameterizedTest; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java index bec75e2..f67758e 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java @@ -1,8 +1,8 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.test; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.PushInfoRowMapper; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo; -import fr.gouv.stopc.robert.pushnotif.scheduler.data.model.PushInfo.PushInfoBuilder; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRowMapper; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo.PushInfoBuilder; import org.flywaydb.core.Flyway; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -20,7 +20,7 @@ import java.util.Map; import java.util.Random; import java.util.function.Function; -import static fr.gouv.stopc.robert.pushnotif.scheduler.data.InstantTimestampConverter.convertInstantToTimestamp; +import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertInstantToTimestamp; import static java.time.Instant.now; import static java.time.ZoneOffset.UTC; -- GitLab From 46c0606bd745f1ae0edd2feca95d4a2795f1afc7 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 15:16:31 +0000 Subject: [PATCH 06/27] refactor(scheduler): move SQL query to repository --- .../robert/pushnotif/scheduler/Scheduler.java | 46 ++++++------------- .../repository/PushInfoRepository.java | 22 +++++++-- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index c3499ca..075c87d 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -8,18 +8,14 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.apns.PushInfoNotificationHandler import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRowMapper; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import io.micrometer.core.annotation.Counted; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import java.sql.ResultSet; -import java.sql.SQLException; import java.time.Duration; import java.time.Instant; @@ -31,8 +27,6 @@ import static com.eatthepath.pushy.apns.util.TokenUtil.sanitizeTokenString; @RequiredArgsConstructor public class Scheduler { - private final JdbcTemplate jdbcTemplate; - private final PushInfoRepository pushInfoRepository; private final RobertPushServerProperties robertPushServerProperties; @@ -44,36 +38,24 @@ public class Scheduler { @Counted(value = "push.notifier.calls", description = "count each time the scheduler sending notifications is triggered") public void sendNotifications() { - // use a RowCallBackHandler in order to process a large resultset on a per-row - // basis. - jdbcTemplate.query( - "select * from push where active = true and deleted = false and next_planned_push <= now()", - new SendNotificationRowCallbackHandler() - ); + pushInfoRepository.forEachNotificationToBeSent(this::sendNotification); apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); } - private class SendNotificationRowCallbackHandler implements RowCallbackHandler { - - @Override - public void processRow(final ResultSet resultSet) throws SQLException { - final var pushInfo = PushInfoRowMapper.INSTANCE.mapRow(resultSet, resultSet.getRow()); - - // set the next planned push to be sure the notification could not be sent 2 - // times the same day - final var handler = new PushInfoNotificationHandler( - pushInfo, - pushInfoRepository, - robertPushServerProperties.getApns().getTopic(), - robertPushServerProperties.getMinPushHour(), - robertPushServerProperties.getMaxPushHour() - ); - - handler.updateNextPlannedPushToRandomTomorrow(); + private void sendNotification(final PushInfo pushInfo) { + final var handler = new PushInfoNotificationHandler( + pushInfo, + pushInfoRepository, + robertPushServerProperties.getApns().getTopic(), + robertPushServerProperties.getMinPushHour(), + robertPushServerProperties.getMaxPushHour() + ); + // set the next planned push to be sure the notification could not be sent 2 + // times the same day + handler.updateNextPlannedPushToRandomTomorrow(); - apnsTemplate.sendNotification(buildNotification(pushInfo.getToken()), handler); - } + apnsTemplate.sendNotification(buildNotification(pushInfo.getToken()), handler); } public SimpleApnsPushNotification buildNotification(final String apnsToken) { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java index 1c3a2b7..cb486af 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java @@ -4,18 +4,32 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; -import org.springframework.stereotype.Component; +import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.util.function.Consumer; + import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertInstantToTimestamp; +import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW; -@Component +@Repository @RequiredArgsConstructor public class PushInfoRepository { private final NamedParameterJdbcTemplate jdbcTemplate; @Transactional + public void forEachNotificationToBeSent(final Consumer pushInfoHandler) { + jdbcTemplate.query( + "select * from push where active = true and deleted = false and next_planned_push <= now()", + rs -> { + final var pushInfo = PushInfoRowMapper.INSTANCE.mapRow(rs, rs.getRow()); + pushInfoHandler.accept(pushInfo); + } + ); + } + + @Transactional(propagation = REQUIRES_NEW) public void updateNextPlannedPushDate(final PushInfo pushInfo) { final var params = new MapSqlParameterSource(); params.addValue("id", pushInfo.getId()); @@ -25,7 +39,7 @@ public class PushInfoRepository { jdbcTemplate.update("update push set next_planned_push = :nextPlannedPushDate where id = :id", params); } - @Transactional + @Transactional(propagation = REQUIRES_NEW) public void updateSuccessFulPushedNotif(final PushInfo pushInfo) { final var params = new MapSqlParameterSource(); params.addValue("id", pushInfo.getId()); @@ -43,7 +57,7 @@ public class PushInfoRepository { } - @Transactional + @Transactional(propagation = REQUIRES_NEW) public void updateFailurePushedNotif(final PushInfo pushInfo) { final var params = new MapSqlParameterSource(); params.addValue("id", pushInfo.getId()); -- GitLab From d96d7b32e3b51f2af07ad0b4ac6b89d40c50e0b4 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 14 Sep 2022 15:38:35 +0000 Subject: [PATCH 07/27] refactor(scheduler): move random push instant generation to data class --- .../robert/pushnotif/scheduler/Scheduler.java | 11 ++++--- .../apns/PushInfoNotificationHandler.java | 33 ------------------- .../scheduler/repository/model/PushInfo.java | 25 ++++++++++++++ 3 files changed, 32 insertions(+), 37 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 075c87d..fd1eb4c 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -47,13 +47,16 @@ public class Scheduler { final var handler = new PushInfoNotificationHandler( pushInfo, pushInfoRepository, - robertPushServerProperties.getApns().getTopic(), - robertPushServerProperties.getMinPushHour(), - robertPushServerProperties.getMaxPushHour() + robertPushServerProperties.getApns().getTopic() ); // set the next planned push to be sure the notification could not be sent 2 // times the same day - handler.updateNextPlannedPushToRandomTomorrow(); + pushInfoRepository.updateNextPlannedPushDate( + pushInfo.withPushDateTomorrowBetween( + robertPushServerProperties.getMinPushHour(), + robertPushServerProperties.getMaxPushHour() + ) + ); apnsTemplate.sendNotification(buildNotification(pushInfo.getToken()), handler); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java index 35fa73b..f7b14e8 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java @@ -6,11 +6,7 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.concurrent.ThreadLocalRandom; -import static java.time.temporal.ChronoUnit.MINUTES; import static org.apache.commons.lang3.StringUtils.truncate; @RequiredArgsConstructor @@ -22,10 +18,6 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { private final String apnsTopic; - private final int minPushHour; - - private final int maxPushHour; - @Override public void onSuccess() { notificationData.setLastSuccessfulPush(Instant.now()); @@ -53,29 +45,4 @@ public class PushInfoNotificationHandler implements ApnsNotificationHandler { public void disableToken() { notificationData.setActive(false); } - - public void updateNextPlannedPushToRandomTomorrow() { - notificationData.setNextPlannedPush(generateDateTomorrowBetweenBounds(notificationData.getTimezone())); - pushInfoRepository.updateNextPlannedPushDate(notificationData); - } - - private Instant generateDateTomorrowBetweenBounds(final String timezone) { - - final var random = ThreadLocalRandom.current(); - - final int durationBetweenHours; - // In case config requires "between 6pm and 4am" which translates in minPushHour - // = 18 and maxPushHour = 4 - if (maxPushHour < minPushHour) { - durationBetweenHours = 24 - minPushHour + maxPushHour; - } else { - durationBetweenHours = maxPushHour - minPushHour; - } - - return ZonedDateTime.now(ZoneId.of(timezone)).plusDays(1) - .withHour(random.nextInt(durationBetweenHours) + minPushHour % 24) - .withMinute(random.nextInt(60)) - .toInstant() - .truncatedTo(MINUTES); - } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java index f5b6cb0..b9e4dbf 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java @@ -4,6 +4,11 @@ import lombok.Builder; import lombok.Data; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.ThreadLocalRandom; + +import static java.time.temporal.ChronoUnit.MINUTES; @Data @Builder @@ -34,4 +39,24 @@ public class PushInfo { private boolean active; private boolean deleted; + + public PushInfo withPushDateTomorrowBetween(final int minPushHour, final int maxPushHour) { + final var random = ThreadLocalRandom.current(); + final int durationBetweenHours; + // In case config requires "between 6pm and 4am" which translates in minPushHour + // = 18 and maxPushHour = 4 + if (maxPushHour < minPushHour) { + durationBetweenHours = 24 - minPushHour + maxPushHour; + } else { + durationBetweenHours = maxPushHour - minPushHour; + } + final var nextPushInstant = ZonedDateTime.now(ZoneId.of(timezone)).plusDays(1) + .withHour(random.nextInt(durationBetweenHours) + minPushHour % 24) + .withMinute(random.nextInt(60)) + .toInstant() + .truncatedTo(MINUTES); + + setNextPlannedPush(nextPushInstant); + return this; + } } -- GitLab From d7a745255279f12665d3dfb50234d613ab18129e Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 07:44:31 +0000 Subject: [PATCH 08/27] test(scheduler): test next push date random generation --- .../repository/model/PushInfoTest.java | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java new file mode 100644 index 0000000..56b9f63 --- /dev/null +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java @@ -0,0 +1,58 @@ +package fr.gouv.stopc.robert.pushnotif.scheduler.repository.model; + +import org.junit.jupiter.api.RepeatedTest; +import org.junit.jupiter.api.Test; + +import java.time.ZoneId; + +import static java.time.ZoneOffset.UTC; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +class PushInfoTest { + + @RepeatedTest(100) + void a_random_push_date_for_timezone_GMT0_should_be_between_request_bounds() { + final var pushInfoUTC = PushInfo.builder() + .timezone("GMT") + .build(); + final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(10, 12) + .getNextPlannedPush() + .atZone(UTC); + assertThat("random hour should be between 10 (included) and 12 (excluded)", nextPush.getHour(), oneOf(10, 11)); + } + + @RepeatedTest(100) + void a_random_push_date_for_timezone_EuropeParis_should_be_between_request_bounds_plus_2() { + final var pushInfoUTC = PushInfo.builder() + .timezone("Europe/Paris") + .build(); + final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(10, 12) + .getNextPlannedPush() + .atZone(ZoneId.of("Europe/Paris")); + assertThat("random hour should be between 10 (included) and 12 (excluded)", nextPush.getHour(), oneOf(10, 11)); + } + + @RepeatedTest(100) + void can_generate_a_push_date_between_tonight_and_tomorrow_morning() { + final var pushInfoUTC = PushInfo.builder() + .timezone("Europe/Paris") + .build(); + final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(23, 2) + .getNextPlannedPush() + .atZone(ZoneId.of("Europe/Paris")); + assertThat( + "random hour should be between 23h (included) and 2h (excluded)", nextPush.getHour(), oneOf(23, 0, 1) + ); + } + + @Test + void cant_generate_a_meaningful_time_when_min_max_are_equals() { + final var pushInfoUTC = PushInfo.builder() + .timezone("Europe/Paris") + .build(); + assertThatThrownBy(() -> pushInfoUTC.withPushDateTomorrowBetween(10, 10)) + .hasMessage("bound must be positive"); + } +} -- GitLab From f15e84972dbfb0b23663aacaa8e61d10d4ee9f4e Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 07:45:12 +0000 Subject: [PATCH 09/27] fix(scheduler): next push date random generation when minPushHour > maxPushHour --- .../robert/pushnotif/scheduler/repository/model/PushInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java index b9e4dbf..e661ad2 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java @@ -51,7 +51,7 @@ public class PushInfo { durationBetweenHours = maxPushHour - minPushHour; } final var nextPushInstant = ZonedDateTime.now(ZoneId.of(timezone)).plusDays(1) - .withHour(random.nextInt(durationBetweenHours) + minPushHour % 24) + .withHour((random.nextInt(durationBetweenHours) + minPushHour) % 24) .withMinute(random.nextInt(60)) .toInstant() .truncatedTo(MINUTES); -- GitLab From 11513b6206e2809571d8eae4223710bbec4c4446 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 13:17:14 +0000 Subject: [PATCH 10/27] refactor(scheduler): tests uses a standalone jdbc repository --- .../scheduler/SchedulerNominalTest.java | 43 +++-- .../scheduler/SchedulerVolumetryTest.java | 20 +-- .../SchedulerWithTwoApnsServerTest.java | 48 +++--- .../SchedulerRateLimiting1sTest.java | 11 +- .../SchedulerRateLimiting2sTest.java | 11 +- .../pushnotif/scheduler/test/PsqlManager.java | 154 +++++++++++------- 6 files changed, 156 insertions(+), 131 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index 6751ec7..4a74a44 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -3,12 +3,9 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import io.micrometer.core.instrument.Tags; -import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; @@ -16,7 +13,7 @@ import org.springframework.test.context.ActiveProfiles; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.concurrent.TimeUnit; +import java.time.temporal.ChronoUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.ACCEPTED; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.REJECTED; @@ -24,12 +21,14 @@ import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.MetricsManager.assertCounterIncremented; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoWith; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; import static java.time.Instant.now; import static java.time.ZoneOffset.UTC; -import static java.time.temporal.ChronoUnit.SECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; +import static org.awaitility.Awaitility.await; @IntegrationTest @ActiveProfiles({ "dev", "one-apns-server" }) @@ -40,11 +39,10 @@ class SchedulerNominalTest { void should_correctly_update_push_status_when_send_notification_to_first_apn_server_with_successful_response() { // Given - givenPushInfoWith(b -> b.id(1L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); - givenPushInfoWith( - b -> b.id(2L) - .token("45f6aa01da5ddb387462c7eaf61bb78ad740f4707bebcf74f9b7c25d48e33589") - .nextPlannedPush(LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC)) + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); + givenPushInfoForTokenAndNextPlannedPush( + "45f6aa01da5ddb387462c7eaf61bb78ad740f4707bebcf74f9b7c25d48e33589", + LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) ); // When - triggering of the scheduled task @@ -52,7 +50,7 @@ class SchedulerNominalTest { // Then // Verify APNs servers - Awaitility.await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedOne(); assertThatMainServerRejectedNothing(); assertThat(APNsMockServersManager.getNotifsAcceptedByMainServer().get(0)) @@ -60,7 +58,7 @@ class SchedulerNominalTest { .satisfies( notif -> { assertThat(notif.getExpiration()) - .isCloseTo(now().plus(Duration.ofDays(1)), within(30, SECONDS)); + .isCloseTo(now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)); assertThat(notif.getPayload()) .isEqualTo("{\"aps\":{\"badge\":0,\"content-available\":1}}"); } @@ -76,7 +74,7 @@ class SchedulerNominalTest { ); // Verify Database - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as("Check the status of the notification that has been correctly sent to APNs server") .satisfies(pushInfo -> { assertThat(pushInfo.getLastSuccessfulPush()) @@ -96,7 +94,7 @@ class SchedulerNominalTest { ) .containsExactly(true, false, 0, null, null, 1); - assertThat(PsqlManager.findByToken("45f6aa01da5ddb387462c7eaf61bb78ad740f4707bebcf74f9b7c25d48e33589")) + assertThatPushInfo("45f6aa01da5ddb387462c7eaf61bb78ad740f4707bebcf74f9b7c25d48e33589") .as("This notification is not pushed because its planned date is in future") .extracting( PushInfo::isActive, PushInfo::isDeleted, PushInfo::getFailedPushSent, @@ -123,7 +121,7 @@ class SchedulerNominalTest { void should_deactivate_notification_when_apns_server_replies_with_is_invalid_token_reason() { // Given - givenPushInfoWith(b -> b.id(3L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); @@ -133,14 +131,11 @@ class SchedulerNominalTest { // Then // Verify servers - Awaitility.await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); // Verify database - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) - .as( - "Check the status of the notification that has been rejected by APNs server - notif is deactivated" - ) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .satisfies( pushInfoFromBase -> { assertThat(pushInfoFromBase.getLastFailurePush()) @@ -174,7 +169,7 @@ class SchedulerNominalTest { void should_not_deactivate_notification_when_apns_server_replies_with_no_invalid_token_reason() { // Given - givenPushInfoWith(b -> b.id(4L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_MESSAGE_ID ); @@ -184,12 +179,12 @@ class SchedulerNominalTest { // Then // Verify server - Awaitility.await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); // Verify database - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as( "Check the status of the notification that has been rejected by APNs server - notif is not deactivated" ) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java index 8517dfd..862cab9 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java @@ -1,20 +1,18 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; -import org.awaitility.Awaitility; +import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.PushInfo; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; -import java.util.concurrent.TimeUnit; - import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoWith; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatAllPushInfo; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; import static java.util.UUID.randomUUID; +import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.LongStream.rangeClosed; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; +import static org.awaitility.Awaitility.await; @IntegrationTest @ActiveProfiles({ "dev" }) @@ -23,22 +21,22 @@ class SchedulerVolumetryTest { private static final int PUSH_NOTIF_COUNT = 100; // This test class is useful to do test with volumetry - // @Disabled @Test void should_correctly_send_large_amount_of_notification_to_apns_servers() { // Given - rangeClosed(1, PUSH_NOTIF_COUNT).forEach(i -> givenPushInfoWith(b -> b.id(i).token(randomUUID().toString()))); + rangeClosed(1, PUSH_NOTIF_COUNT).forEach(i -> givenPushInfoForToken(randomUUID().toString())); // When -- triggering of the scheduled job // Then - Awaitility.await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAccepted(PUSH_NOTIF_COUNT); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedNothing(); - assertThat(PsqlManager.findAll()).hasSize(PUSH_NOTIF_COUNT) + assertThatAllPushInfo() + .hasSize(PUSH_NOTIF_COUNT) .extracting( PushInfo::isActive, PushInfo::isDeleted, diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index 9111af3..fe8f938 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -3,10 +3,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; -import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.springframework.test.context.ActiveProfiles; @@ -14,17 +11,18 @@ import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECOND; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoWith; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; import static java.time.ZoneOffset.UTC; -import static java.time.temporal.ChronoUnit.SECONDS; -import static java.util.concurrent.TimeUnit.of; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.await; @IntegrationTest @ActiveProfiles({ "dev" }) @@ -34,18 +32,18 @@ class SchedulerWithTwoApnsServerTest { void should_correctly_update_push_status_when_send_notification_to_first_apn_server_with_successful_response() { // Given - givenPushInfoWith(b -> b.id(1L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); // When // Then - Awaitility.await().atMost(40, of(SECONDS)).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedOne(); assertThatMainServerRejectedNothing(); assertThatSecondServerRejectedNothing(); assertThatSecondServerAcceptedNothing(); - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as("Check the status of the notification that has been correctly sent to main APNs server") .satisfies( p -> { @@ -71,7 +69,7 @@ class SchedulerWithTwoApnsServerTest { .as("Check the content of the notification received on the main APNs server side") .satisfies( notif -> assertThat(notif.getExpiration()) - .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, SECONDS)) + .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)) ).extracting( ApnsPushNotification::getPushType, ApnsPushNotification::getPriority, @@ -90,7 +88,7 @@ class SchedulerWithTwoApnsServerTest { void should_correctly_update_push_status_when_send_notification_to_first_apn_server_with_rejected_reason_other_than_invalid_token() { // Given - givenPushInfoWith(b -> b.id(1L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_TOPIC ); @@ -98,13 +96,13 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - Awaitility.await().atMost(40, of(SECONDS)).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedNothing(); - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as( "Check the status of the notification that has been rejected by main APNs server (reason other than invalid token)" ) @@ -133,7 +131,7 @@ class SchedulerWithTwoApnsServerTest { void should_send_notification_to_second_apns_server_when_first_replies_invalid_token_response() { // Given - givenPushInfoWith(b -> b.id(4L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); @@ -141,13 +139,13 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - Awaitility.await().atMost(40, of(SECONDS)).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedOne(); assertThatSecondServerRejectedNothing(); - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as("Check the status of the notification that has been correctly sent to secondary APNs server") .satisfies( pushInfo -> { @@ -174,7 +172,7 @@ class SchedulerWithTwoApnsServerTest { .as("Check the content of the notification received on the secondary APNs server side") .satisfies( notif -> assertThat(notif.getExpiration()) - .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, SECONDS)) + .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)) ).extracting( ApnsPushNotification::getPushType, ApnsPushNotification::getPriority, @@ -194,7 +192,7 @@ class SchedulerWithTwoApnsServerTest { void should_deactivate_notification_when_both_server_replies_invalid_token_response() { // Given - givenPushInfoWith(b -> b.id(3L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); @@ -206,14 +204,14 @@ class SchedulerWithTwoApnsServerTest { // Then - Awaitility.await().atMost(40, of(SECONDS)).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedOne(); - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as("Check the status of the notification that has been rejected by all APNs server") .satisfies( pushInfo -> { @@ -241,7 +239,7 @@ class SchedulerWithTwoApnsServerTest { void should_correctly_update_push_status_when_send_notification_to_second_apn_server_with_rejected_reason_other_than_invalid_token() { // Given - givenPushInfoWith(b -> b.id(1L).token("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")); + givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); @@ -252,13 +250,13 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - Awaitility.await().atMost(40, of(SECONDS)).untilAsserted(() -> { + await().atMost(40, SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedOne(); - assertThat(PsqlManager.findByToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad")) + assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .as( "Check the status of the notification that has been rejected by main APNs server (reason other than invalid token)" ) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java index 6c6696b..f53a58f 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java @@ -1,9 +1,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.ratelimiting; import fr.gouv.stopc.robert.pushnotif.scheduler.Scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; @@ -12,7 +10,9 @@ import org.springframework.test.context.TestPropertySource; import java.time.Duration; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoWith; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatAllPushInfo; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; import static java.time.Instant.now; import static java.util.UUID.randomUUID; import static java.util.stream.LongStream.rangeClosed; @@ -34,7 +34,7 @@ class SchedulerRateLimiting1sTest { // Given rangeClosed(1, notificationsNumber) - .forEach(i -> givenPushInfoWith(p -> p.id(i).token(randomUUID().toString()))); + .forEach(i -> givenPushInfoForToken(randomUUID().toString())); // When final var before = now(); @@ -42,7 +42,8 @@ class SchedulerRateLimiting1sTest { final var after = now(); // Then - assertThat(PsqlManager.findAll()).hasSize(notificationsNumber) + assertThatAllPushInfo() + .hasSize(notificationsNumber) .extracting( PushInfo::isActive, PushInfo::isDeleted, diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java index 151f123..d63a11f 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java @@ -1,9 +1,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.ratelimiting; import fr.gouv.stopc.robert.pushnotif.scheduler.Scheduler; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; @@ -12,7 +10,9 @@ import org.springframework.test.context.TestPropertySource; import java.time.Duration; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoWith; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatAllPushInfo; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; import static java.time.Instant.now; import static java.util.UUID.randomUUID; import static java.util.stream.LongStream.rangeClosed; @@ -34,7 +34,7 @@ class SchedulerRateLimiting2sTest { // Given rangeClosed(1, notificationsNumber) - .forEach(i -> givenPushInfoWith(p -> p.id(i).token(randomUUID().toString()))); + .forEach(i -> givenPushInfoForToken(randomUUID().toString())); // When final var before = now(); @@ -42,7 +42,8 @@ class SchedulerRateLimiting2sTest { final var after = now(); // Then - assertThat(PsqlManager.findAll()).hasSize(notificationsNumber) + assertThatAllPushInfo() + .hasSize(notificationsNumber) .extracting( PushInfo::isActive, PushInfo::isDeleted, diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java index f67758e..d7a5e5e 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/PsqlManager.java @@ -1,10 +1,11 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.test; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRowMapper; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo.PushInfoBuilder; +import lombok.Builder; +import lombok.Value; +import org.assertj.core.api.ListAssert; +import org.assertj.core.api.ObjectAssert; import org.flywaydb.core.Flyway; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.jdbc.core.simple.SimpleJdbcInsert; import org.springframework.test.context.TestContext; @@ -13,16 +14,17 @@ import org.testcontainers.containers.JdbcDatabaseContainer; import org.testcontainers.containers.PostgreSQLContainer; import org.testcontainers.utility.DockerImageName; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.List; import java.util.Map; import java.util.Random; -import java.util.function.Function; -import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertInstantToTimestamp; -import static java.time.Instant.now; import static java.time.ZoneOffset.UTC; +import static org.assertj.core.api.Assertions.assertThat; public class PsqlManager implements TestExecutionListener { @@ -30,26 +32,6 @@ public class PsqlManager implements TestExecutionListener { private static final PushInfoRowMapper pushInfoRowMapper = new PushInfoRowMapper(); - private static int insert(final PushInfo pushInfo) { - final MapSqlParameterSource parameters = new MapSqlParameterSource(); - parameters.addValue("id", pushInfo.getId()); - parameters.addValue("creation_date", convertInstantToTimestamp(pushInfo.getCreationDate())); - parameters.addValue("locale", pushInfo.getLocale()); - parameters.addValue("timezone", pushInfo.getTimezone()); - parameters.addValue("token", pushInfo.getToken()); - parameters.addValue("active", pushInfo.isActive()); - parameters.addValue("deleted", pushInfo.isDeleted()); - parameters.addValue("successful_push_sent", pushInfo.getSuccessfulPushSent()); - parameters.addValue("last_successful_push", convertInstantToTimestamp(pushInfo.getLastSuccessfulPush())); - parameters.addValue("failed_push_sent", pushInfo.getFailedPushSent()); - parameters.addValue("last_failure_push", convertInstantToTimestamp(pushInfo.getLastFailurePush())); - parameters.addValue("last_error_code", pushInfo.getLastErrorCode()); - parameters.addValue("next_planned_push", convertInstantToTimestamp(pushInfo.getNextPlannedPush())); - return new SimpleJdbcInsert(jdbcTemplate.getJdbcTemplate()) - .withTableName("push") - .execute(parameters); - } - private static final JdbcDatabaseContainer POSTGRES = new PostgreSQLContainer( DockerImageName.parse("postgres:13.7") ); @@ -74,44 +56,94 @@ public class PsqlManager implements TestExecutionListener { jdbcTemplate = testContext.getApplicationContext().getBean(NamedParameterJdbcTemplate.class); } - static PushInfoBuilder pushinfoBuilder = PushInfo.builder() - .id(10000000L) - .active(true) - .deleted(false) - .token("00000000") - .locale("fr-FR") - .creationDate(now()) - .successfulPushSent(0) - .failedPushSent(0) - .timezone("Europe/Paris"); - - public static void givenPushInfoWith(final Function testSpecificBuilder) { - insert( - testSpecificBuilder.apply( - pushinfoBuilder - /* - * Set next planned push date outside of static builder to have varying - * getRandomNumberInRange results but let test specific builder override it if - * needed - */ - .nextPlannedPush( - LocalDateTime.from( - LocalDate.now().atStartOfDay().plusHours(new Random().nextInt(24)) - .plusMinutes(new Random().nextInt(60)).minusDays(1) - ) - .toInstant(UTC) - ) - ).build() + public static void givenPushInfoForToken(String token) { + givenPushInfoForTokenAndNextPlannedPush( + token, + LocalDateTime.from( + LocalDate.now().atStartOfDay().plusHours(new Random().nextInt(24)) + .plusMinutes(new Random().nextInt(60)).minusDays(1) + ) + .toInstant(UTC) ); } - public static PushInfo findByToken(final String token) { - final var parameters = Map.of("token", token); - return jdbcTemplate.queryForObject("select * from push where token = :token", parameters, pushInfoRowMapper); + public static void givenPushInfoForTokenAndNextPlannedPush(String token, Instant nextPlannedPush) { + new SimpleJdbcInsert(jdbcTemplate.getJdbcTemplate()) + .withTableName("push") + .usingGeneratedKeyColumns("id") + .execute( + Map.of( + "creation_date", Timestamp.from(Instant.now()), + "locale", "fr-FR", + "timezone", "Europe/Paris", + "token", token, + "active", true, + "deleted", false, + "successful_push_sent", 0, + "failed_push_sent", 0, + "next_planned_push", Timestamp.from(nextPlannedPush) + ) + ); + } + + public static ListAssert assertThatAllPushInfo() { + return assertThat(jdbcTemplate.query("select * from push", Map.of(), PushInfoRowMapper.INSTANCE)) + .as("all push data stored"); } - public static List findAll() { - return jdbcTemplate.query("select * from push ", pushInfoRowMapper); + public static ObjectAssert assertThatPushInfo(final String token) { + final var push = jdbcTemplate.queryForObject( + "select * from push where token = :token", Map.of("token", token), + PushInfoRowMapper.INSTANCE + ); + return assertThat(push) + .describedAs("push data for token %s", token); } + @Value + @Builder + public static class PushInfo { + + String token; + + boolean active; + + boolean deleted; + + int successfulPushSent; + + Instant lastSuccessfulPush; + + int failedPushSent; + + Instant lastFailurePush; + + String lastErrorCode; + + Instant nextPlannedPush; + } + + private static class PushInfoRowMapper implements RowMapper { + + private static final PushInfoRowMapper INSTANCE = new PushInfoRowMapper(); + + @Override + public PushInfo mapRow(final ResultSet rs, final int rowNum) throws SQLException { + return PushInfo.builder() + .token(rs.getString("token")) + .active(rs.getBoolean("active")) + .deleted(rs.getBoolean("deleted")) + .successfulPushSent(rs.getInt("successful_push_sent")) + .lastSuccessfulPush(toInstant(rs.getTimestamp("last_successful_push"))) + .failedPushSent(rs.getInt("failed_push_sent")) + .lastFailurePush(toInstant(rs.getTimestamp("last_failure_push"))) + .lastErrorCode(rs.getString("last_error_code")) + .nextPlannedPush(toInstant(rs.getTimestamp("next_planned_push"))) + .build(); + } + + private static Instant toInstant(final Timestamp timestamp) { + return timestamp != null ? timestamp.toInstant() : null; + } + } } -- GitLab From ec2c641bb4fba7433ad84fb634b7673f191b1619 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 13:28:40 +0000 Subject: [PATCH 11/27] refactor(scheduler): remove "unused" PushInfo attributes, make it immutable --- .../robert/pushnotif/scheduler/Scheduler.java | 3 +- .../apns/PushInfoNotificationHandler.java | 24 ++---- .../repository/InstantTimestampConverter.java | 19 ----- .../repository/PushInfoRepository.java | 80 ++++++++++++------- .../repository/PushInfoRowMapper.java | 33 -------- .../scheduler/repository/model/PushInfo.java | 35 +++----- 6 files changed, 66 insertions(+), 128 deletions(-) delete mode 100644 robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java delete mode 100644 robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index fd1eb4c..c728474 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -46,8 +46,7 @@ public class Scheduler { private void sendNotification(final PushInfo pushInfo) { final var handler = new PushInfoNotificationHandler( pushInfo, - pushInfoRepository, - robertPushServerProperties.getApns().getTopic() + pushInfoRepository ); // set the next planned push to be sure the notification could not be sent 2 // times the same day diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java index f7b14e8..cc2953c 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java @@ -5,44 +5,30 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; -import java.time.Instant; - -import static org.apache.commons.lang3.StringUtils.truncate; - @RequiredArgsConstructor public class PushInfoNotificationHandler implements ApnsNotificationHandler { - private final PushInfo notificationData; + private final PushInfo pushInfo; private final PushInfoRepository pushInfoRepository; - private final String apnsTopic; - @Override public void onSuccess() { - notificationData.setLastSuccessfulPush(Instant.now()); - notificationData.setSuccessfulPushSent(notificationData.getSuccessfulPushSent() + 1); - pushInfoRepository.updateSuccessFulPushedNotif(notificationData); + pushInfoRepository.updateSuccessfulPushSent(pushInfo.getId()); } @Override public void onRejection(final RejectionReason reason) { - notificationData.setLastErrorCode(reason.getValue()); - notificationData.setLastFailurePush(Instant.now()); - notificationData.setFailedPushSent(notificationData.getFailedPushSent() + 1); - pushInfoRepository.updateFailurePushedNotif(notificationData); + pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); } @Override public void onError(final Throwable cause) { - notificationData.setLastErrorCode(truncate(cause.getMessage(), 255)); - notificationData.setLastFailurePush(Instant.now()); - notificationData.setFailedPushSent(notificationData.getFailedPushSent() + 1); - pushInfoRepository.updateFailurePushedNotif(notificationData); + pushInfoRepository.updateFailure(pushInfo.getId(), cause.getMessage()); } @Override public void disableToken() { - notificationData.setActive(false); + pushInfoRepository.disable(pushInfo.getId()); } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java deleted file mode 100644 index db855ae..0000000 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/InstantTimestampConverter.java +++ /dev/null @@ -1,19 +0,0 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.repository; - -import lombok.experimental.UtilityClass; - -import java.sql.Timestamp; -import java.time.Instant; - -@UtilityClass -public class InstantTimestampConverter { - - static Instant convertTimestampToInstant(final Timestamp timestamp) { - return timestamp != null ? timestamp.toInstant() : null; - } - - public static Timestamp convertInstantToTimestamp(Instant instant) { - return instant == null ? null : Timestamp.from(instant); - } - -} diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java index cb486af..03bebbd 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java @@ -2,16 +2,22 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.repository; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; -import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.Map; import java.util.function.Consumer; -import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertInstantToTimestamp; import static org.springframework.transaction.annotation.Propagation.REQUIRES_NEW; +@Slf4j @Repository @RequiredArgsConstructor public class PushInfoRepository { @@ -31,49 +37,65 @@ public class PushInfoRepository { @Transactional(propagation = REQUIRES_NEW) public void updateNextPlannedPushDate(final PushInfo pushInfo) { - final var params = new MapSqlParameterSource(); - params.addValue("id", pushInfo.getId()); - params.addValue( - "nextPlannedPushDate", convertInstantToTimestamp(pushInfo.getNextPlannedPush()) + jdbcTemplate.update( + "update push set next_planned_push = :nextPlannedPushDate where id = :id", Map.of( + "id", pushInfo.getId(), + "nextPlannedPushDate", Timestamp.from(pushInfo.getNextPlannedPush()) + ) ); - jdbcTemplate.update("update push set next_planned_push = :nextPlannedPushDate where id = :id", params); } @Transactional(propagation = REQUIRES_NEW) - public void updateSuccessFulPushedNotif(final PushInfo pushInfo) { - final var params = new MapSqlParameterSource(); - params.addValue("id", pushInfo.getId()); - params.addValue( - "lastSuccessfulPush", convertInstantToTimestamp(pushInfo.getLastSuccessfulPush()) - ); - params.addValue("successfulPushSent", pushInfo.getSuccessfulPushSent()); - + public void updateSuccessfulPushSent(final long id) { jdbcTemplate.update( "update push set last_successful_push = :lastSuccessfulPush, " + - "successful_push_sent = :successfulPushSent " + + "successful_push_sent = successful_push_sent + 1 " + "where id = :id", - params + Map.of( + "id", id, + "lastSuccessfulPush", Timestamp.from(Instant.now()) + ) ); } @Transactional(propagation = REQUIRES_NEW) - public void updateFailurePushedNotif(final PushInfo pushInfo) { - final var params = new MapSqlParameterSource(); - params.addValue("id", pushInfo.getId()); - params.addValue("active", pushInfo.isActive()); - params.addValue("lastFailurePush", convertInstantToTimestamp(pushInfo.getLastFailurePush())); - params.addValue("failedPushSent", pushInfo.getFailedPushSent()); - params.addValue("lastErrorCode", pushInfo.getLastErrorCode()); - + public void updateFailure(final long id, final String failureDescription) { jdbcTemplate.update( - "update push set active = :active, " + + "update push set " + "last_failure_push = :lastFailurePush, " + - "failed_push_sent = :failedPushSent, " + - "last_error_code = :lastErrorCode " + + "failed_push_sent = failed_push_sent + 1, " + + "last_error_code = :lastErrorCode::char(255) " + "where id = :id", - params + Map.of( + "id", id, + "lastFailurePush", Timestamp.from(Instant.now()), + "lastErrorCode", failureDescription + ) ); } + @Transactional(propagation = REQUIRES_NEW) + public void disable(final Long id) { + jdbcTemplate.update("update push set active = false where id = :id", Map.of("id", id)); + } + + private static class PushInfoRowMapper implements RowMapper { + + private static final PushInfoRowMapper INSTANCE = new PushInfoRowMapper(); + + @Override + public PushInfo mapRow(ResultSet rs, int rowNum) throws SQLException { + return PushInfo.builder() + .id(rs.getLong("id")) + .timezone(rs.getString("timezone")) + .token(rs.getString("token")) + .nextPlannedPush(toInstantNullSafe(rs.getTimestamp("next_planned_push"))) + .build(); + } + + private Instant toInstantNullSafe(Timestamp timestamp) { + return null == timestamp ? null : timestamp.toInstant(); + } + } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java deleted file mode 100644 index 44d7d7f..0000000 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRowMapper.java +++ /dev/null @@ -1,33 +0,0 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.repository; - -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; -import org.springframework.jdbc.core.RowMapper; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import static fr.gouv.stopc.robert.pushnotif.scheduler.repository.InstantTimestampConverter.convertTimestampToInstant; - -public class PushInfoRowMapper implements RowMapper { - - public static final RowMapper INSTANCE = new PushInfoRowMapper(); - - @Override - public PushInfo mapRow(ResultSet resultSet, int i) throws SQLException { - return PushInfo.builder() - .id(resultSet.getLong("id")) - .creationDate(convertTimestampToInstant(resultSet.getTimestamp("creation_date"))) - .locale(resultSet.getString("locale")) - .timezone(resultSet.getString("timezone")) - .token(resultSet.getString("token")) - .active(resultSet.getBoolean("active")) - .deleted(resultSet.getBoolean("deleted")) - .successfulPushSent(resultSet.getInt("successful_push_sent")) - .lastSuccessfulPush(convertTimestampToInstant(resultSet.getTimestamp("last_successful_push"))) - .failedPushSent(resultSet.getInt("failed_push_sent")) - .lastFailurePush(convertTimestampToInstant(resultSet.getTimestamp("last_failure_push"))) - .lastErrorCode(resultSet.getString("last_error_code")) - .nextPlannedPush(convertTimestampToInstant(resultSet.getTimestamp("next_planned_push"))) - .build(); - } -} diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java index e661ad2..5695c62 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java @@ -1,7 +1,8 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.repository.model; import lombok.Builder; -import lombok.Data; +import lombok.Value; +import lombok.With; import java.time.Instant; import java.time.ZoneId; @@ -10,35 +11,18 @@ import java.util.concurrent.ThreadLocalRandom; import static java.time.temporal.ChronoUnit.MINUTES; -@Data +@Value @Builder public class PushInfo { - private Long id; + Long id; - private String token; + String token; - private String timezone; + String timezone; - private String locale; - - private Instant nextPlannedPush; - - private Instant lastSuccessfulPush; - - private Instant lastFailurePush; - - private String lastErrorCode; - - private int successfulPushSent; - - private int failedPushSent; - - private Instant creationDate; - - private boolean active; - - private boolean deleted; + @With + Instant nextPlannedPush; public PushInfo withPushDateTomorrowBetween(final int minPushHour, final int maxPushHour) { final var random = ThreadLocalRandom.current(); @@ -56,7 +40,6 @@ public class PushInfo { .toInstant() .truncatedTo(MINUTES); - setNextPlannedPush(nextPushInstant); - return this; + return this.withNextPlannedPush(nextPushInstant); } } -- GitLab From 1c27d2d08f0a47ca737b2bd55d1aa5910351d07a Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 13:39:20 +0000 Subject: [PATCH 12/27] refactor(scheduler): move notif lifecycle business action to Scheduler --- .../robert/pushnotif/scheduler/Scheduler.java | 53 ++++++++++++------- .../apns/PushInfoNotificationHandler.java | 34 ------------ 2 files changed, 34 insertions(+), 53 deletions(-) delete mode 100644 robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index c728474..b0a66a5 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -4,11 +4,11 @@ import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; -import fr.gouv.stopc.robert.pushnotif.scheduler.apns.PushInfoNotificationHandler; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import io.micrometer.core.annotation.Counted; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -38,26 +38,41 @@ public class Scheduler { @Counted(value = "push.notifier.calls", description = "count each time the scheduler sending notifications is triggered") public void sendNotifications() { - pushInfoRepository.forEachNotificationToBeSent(this::sendNotification); + pushInfoRepository.forEachNotificationToBeSent(pushInfo -> { + // set the next planned push to be sure the notification could not be sent 2 + // times the same day + pushInfoRepository.updateNextPlannedPushDate( + pushInfo.withPushDateTomorrowBetween( + robertPushServerProperties.getMinPushHour(), + robertPushServerProperties.getMaxPushHour() + ) + ); + final var notification = buildNotification(pushInfo.getToken()); + apnsTemplate.sendNotification(notification, new ApnsNotificationHandler() { - apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); - } + @Override + public void onSuccess() { + pushInfoRepository.updateSuccessfulPushSent(pushInfo.getId()); + } - private void sendNotification(final PushInfo pushInfo) { - final var handler = new PushInfoNotificationHandler( - pushInfo, - pushInfoRepository - ); - // set the next planned push to be sure the notification could not be sent 2 - // times the same day - pushInfoRepository.updateNextPlannedPushDate( - pushInfo.withPushDateTomorrowBetween( - robertPushServerProperties.getMinPushHour(), - robertPushServerProperties.getMaxPushHour() - ) - ); + @Override + public void onRejection(final RejectionReason reason) { + pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); + } - apnsTemplate.sendNotification(buildNotification(pushInfo.getToken()), handler); + @Override + public void onError(final Throwable cause) { + pushInfoRepository.updateFailure(pushInfo.getId(), cause.getMessage()); + } + + @Override + public void disableToken() { + pushInfoRepository.disable(pushInfo.getId()); + } + }); + }); + + apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); } public SimpleApnsPushNotification buildNotification(final String apnsToken) { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java deleted file mode 100644 index cc2953c..0000000 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/PushInfoNotificationHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.apns; - -import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; -import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -public class PushInfoNotificationHandler implements ApnsNotificationHandler { - - private final PushInfo pushInfo; - - private final PushInfoRepository pushInfoRepository; - - @Override - public void onSuccess() { - pushInfoRepository.updateSuccessfulPushSent(pushInfo.getId()); - } - - @Override - public void onRejection(final RejectionReason reason) { - pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); - } - - @Override - public void onError(final Throwable cause) { - pushInfoRepository.updateFailure(pushInfo.getId(), cause.getMessage()); - } - - @Override - public void disableToken() { - pushInfoRepository.disable(pushInfo.getId()); - } -} -- GitLab From 3945b6c3b8a3d771692f6f86036d33194b1f7907 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 13:59:17 +0000 Subject: [PATCH 13/27] refactor(scheduler): move notif lifecycle business rules to Scheduler --- .../robert/pushnotif/scheduler/Scheduler.java | 110 +++++++++++++----- .../template/ApnsNotificationHandler.java | 1 + .../repository/PushInfoRepository.java | 34 ++---- .../scheduler/repository/model/PushInfo.java | 30 ----- ...=> SchedulerRandomDateGenerationTest.java} | 30 ++--- 5 files changed, 97 insertions(+), 108 deletions(-) rename robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/{repository/model/PushInfoTest.java => SchedulerRandomDateGenerationTest.java} (55%) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index b0a66a5..6930200 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -9,6 +9,7 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHa import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; +import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import io.micrometer.core.annotation.Counted; import io.micrometer.core.annotation.Timed; import lombok.RequiredArgsConstructor; @@ -18,9 +19,13 @@ import org.springframework.stereotype.Service; import java.time.Duration; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.concurrent.ThreadLocalRandom; import static com.eatthepath.pushy.apns.util.SimpleApnsPushNotification.DEFAULT_EXPIRATION_PERIOD; import static com.eatthepath.pushy.apns.util.TokenUtil.sanitizeTokenString; +import static java.time.temporal.ChronoUnit.MINUTES; @Slf4j @Service @@ -41,41 +46,31 @@ public class Scheduler { pushInfoRepository.forEachNotificationToBeSent(pushInfo -> { // set the next planned push to be sure the notification could not be sent 2 // times the same day - pushInfoRepository.updateNextPlannedPushDate( - pushInfo.withPushDateTomorrowBetween( - robertPushServerProperties.getMinPushHour(), - robertPushServerProperties.getMaxPushHour() - ) - ); - final var notification = buildNotification(pushInfo.getToken()); - apnsTemplate.sendNotification(notification, new ApnsNotificationHandler() { - - @Override - public void onSuccess() { - pushInfoRepository.updateSuccessfulPushSent(pushInfo.getId()); - } - - @Override - public void onRejection(final RejectionReason reason) { - pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); - } - - @Override - public void onError(final Throwable cause) { - pushInfoRepository.updateFailure(pushInfo.getId(), cause.getMessage()); - } - - @Override - public void disableToken() { - pushInfoRepository.disable(pushInfo.getId()); - } - }); + updateNextPlannedPush(pushInfo); + final var notification = buildWakeUpNotification(pushInfo.getToken()); + apnsTemplate.sendNotification(notification, new WakeUpDeviceNotificationHandler(pushInfo)); }); apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); } - public SimpleApnsPushNotification buildNotification(final String apnsToken) { + /** + * Updates the registered token with a new notification instant set to tomorrow. + */ + private void updateNextPlannedPush(final PushInfo pushInfo) { + final var nextPushDate = generatePushDateTomorrowBetween( + robertPushServerProperties.getMinPushHour(), + robertPushServerProperties.getMaxPushHour(), + ZoneId.of(pushInfo.getTimezone()) + ); + pushInfoRepository.updateNextPlannedPushDate(pushInfo.getId(), nextPushDate); + } + + /** + * Builds the {@link com.eatthepath.pushy.apns.ApnsPushNotification} to be sent + * to the Apple server. + */ + private SimpleApnsPushNotification buildWakeUpNotification(final String apnsToken) { final var payload = new SimpleApnsPayloadBuilder() .setContentAvailable(true) .setBadgeNumber(0) @@ -90,4 +85,59 @@ public class Scheduler { PushType.BACKGROUND ); } + + /** + * Generates a random instant tomorrow between the given hour bounds for the + * specified timezone. + *

+ * minPushHour can be greater than maxPushHour: its means notification period + * starts this evening and ends tommorrow. For instance min=20 and max=7 means + * notifications are send between today at 20:00 and tomorrow at 6:59. + */ + static Instant generatePushDateTomorrowBetween(final int minPushHour, final int maxPushHour, + final ZoneId timezone) { + final var random = ThreadLocalRandom.current(); + final int durationBetweenHours; + // In case config requires "between 6pm and 4am" which translates in minPushHour + // = 18 and maxPushHour = 4 + if (maxPushHour < minPushHour) { + durationBetweenHours = 24 - minPushHour + maxPushHour; + } else { + durationBetweenHours = maxPushHour - minPushHour; + } + return ZonedDateTime.now(timezone).plusDays(1) + .withHour((random.nextInt(durationBetweenHours) + minPushHour) % 24) + .withMinute(random.nextInt(60)) + .toInstant() + .truncatedTo(MINUTES); + } + + /** + * Handles notification request response. + */ + @RequiredArgsConstructor + private class WakeUpDeviceNotificationHandler implements ApnsNotificationHandler { + + private final PushInfo pushInfo; + + @Override + public void onSuccess() { + pushInfoRepository.updateSuccessfulPushSent(pushInfo.getId()); + } + + @Override + public void onRejection(final RejectionReason reason) { + pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); + } + + @Override + public void onError(final Throwable cause) { + pushInfoRepository.updateFailure(pushInfo.getId(), cause.getMessage()); + } + + @Override + public void disableToken() { + pushInfoRepository.disable(pushInfo.getId()); + } + } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java index 5a82313..8754683 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java @@ -34,5 +34,6 @@ public interface ApnsNotificationHandler { * APN server, this method is called. * @param rejectionMessage rejected push notification request response message */ + @Deprecated(since = "fixme: 'disableToken' is a business rule and should not be in this interface") void disableToken(); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java index 03bebbd..41f6310 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/PushInfoRepository.java @@ -3,13 +3,10 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.repository; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Timestamp; import java.time.Instant; import java.util.Map; @@ -29,18 +26,22 @@ public class PushInfoRepository { jdbcTemplate.query( "select * from push where active = true and deleted = false and next_planned_push <= now()", rs -> { - final var pushInfo = PushInfoRowMapper.INSTANCE.mapRow(rs, rs.getRow()); + final var pushInfo = PushInfo.builder() + .id(rs.getLong("id")) + .timezone(rs.getString("timezone")) + .token(rs.getString("token")) + .build(); pushInfoHandler.accept(pushInfo); } ); } @Transactional(propagation = REQUIRES_NEW) - public void updateNextPlannedPushDate(final PushInfo pushInfo) { + public void updateNextPlannedPushDate(final long id, final Instant nextPlannedPush) { jdbcTemplate.update( "update push set next_planned_push = :nextPlannedPushDate where id = :id", Map.of( - "id", pushInfo.getId(), - "nextPlannedPushDate", Timestamp.from(pushInfo.getNextPlannedPush()) + "id", id, + "nextPlannedPushDate", Timestamp.from(nextPlannedPush) ) ); } @@ -79,23 +80,4 @@ public class PushInfoRepository { public void disable(final Long id) { jdbcTemplate.update("update push set active = false where id = :id", Map.of("id", id)); } - - private static class PushInfoRowMapper implements RowMapper { - - private static final PushInfoRowMapper INSTANCE = new PushInfoRowMapper(); - - @Override - public PushInfo mapRow(ResultSet rs, int rowNum) throws SQLException { - return PushInfo.builder() - .id(rs.getLong("id")) - .timezone(rs.getString("timezone")) - .token(rs.getString("token")) - .nextPlannedPush(toInstantNullSafe(rs.getTimestamp("next_planned_push"))) - .build(); - } - - private Instant toInstantNullSafe(Timestamp timestamp) { - return null == timestamp ? null : timestamp.toInstant(); - } - } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java index 5695c62..1bd1863 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfo.java @@ -2,14 +2,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.repository.model; import lombok.Builder; import lombok.Value; -import lombok.With; - -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.concurrent.ThreadLocalRandom; - -import static java.time.temporal.ChronoUnit.MINUTES; @Value @Builder @@ -20,26 +12,4 @@ public class PushInfo { String token; String timezone; - - @With - Instant nextPlannedPush; - - public PushInfo withPushDateTomorrowBetween(final int minPushHour, final int maxPushHour) { - final var random = ThreadLocalRandom.current(); - final int durationBetweenHours; - // In case config requires "between 6pm and 4am" which translates in minPushHour - // = 18 and maxPushHour = 4 - if (maxPushHour < minPushHour) { - durationBetweenHours = 24 - minPushHour + maxPushHour; - } else { - durationBetweenHours = maxPushHour - minPushHour; - } - final var nextPushInstant = ZonedDateTime.now(ZoneId.of(timezone)).plusDays(1) - .withHour((random.nextInt(durationBetweenHours) + minPushHour) % 24) - .withMinute(random.nextInt(60)) - .toInstant() - .truncatedTo(MINUTES); - - return this.withNextPlannedPush(nextPushInstant); - } } diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerRandomDateGenerationTest.java similarity index 55% rename from robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java rename to robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerRandomDateGenerationTest.java index 56b9f63..b831474 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/repository/model/PushInfoTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerRandomDateGenerationTest.java @@ -1,46 +1,35 @@ -package fr.gouv.stopc.robert.pushnotif.scheduler.repository.model; +package fr.gouv.stopc.robert.pushnotif.scheduler; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import java.time.ZoneId; +import static fr.gouv.stopc.robert.pushnotif.scheduler.Scheduler.generatePushDateTomorrowBetween; import static java.time.ZoneOffset.UTC; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.oneOf; -class PushInfoTest { +class SchedulerRandomDateGenerationTest { @RepeatedTest(100) void a_random_push_date_for_timezone_GMT0_should_be_between_request_bounds() { - final var pushInfoUTC = PushInfo.builder() - .timezone("GMT") - .build(); - final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(10, 12) - .getNextPlannedPush() + final var nextPush = generatePushDateTomorrowBetween(10, 12, ZoneId.of("GMT")) .atZone(UTC); assertThat("random hour should be between 10 (included) and 12 (excluded)", nextPush.getHour(), oneOf(10, 11)); } @RepeatedTest(100) void a_random_push_date_for_timezone_EuropeParis_should_be_between_request_bounds_plus_2() { - final var pushInfoUTC = PushInfo.builder() - .timezone("Europe/Paris") - .build(); - final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(10, 12) - .getNextPlannedPush() + final var nextPush = generatePushDateTomorrowBetween(10, 12, ZoneId.of("Europe/Paris")) .atZone(ZoneId.of("Europe/Paris")); assertThat("random hour should be between 10 (included) and 12 (excluded)", nextPush.getHour(), oneOf(10, 11)); } @RepeatedTest(100) void can_generate_a_push_date_between_tonight_and_tomorrow_morning() { - final var pushInfoUTC = PushInfo.builder() - .timezone("Europe/Paris") - .build(); - final var nextPush = pushInfoUTC.withPushDateTomorrowBetween(23, 2) - .getNextPlannedPush() + final var nextPush = generatePushDateTomorrowBetween(23, 2, ZoneId.of("Europe/Paris")) .atZone(ZoneId.of("Europe/Paris")); assertThat( "random hour should be between 23h (included) and 2h (excluded)", nextPush.getHour(), oneOf(23, 0, 1) @@ -49,10 +38,7 @@ class PushInfoTest { @Test void cant_generate_a_meaningful_time_when_min_max_are_equals() { - final var pushInfoUTC = PushInfo.builder() - .timezone("Europe/Paris") - .build(); - assertThatThrownBy(() -> pushInfoUTC.withPushDateTomorrowBetween(10, 10)) + assertThatThrownBy(() -> generatePushDateTomorrowBetween(10, 10, ZoneId.of("Europe/Paris"))) .hasMessage("bound must be positive"); } } -- GitLab From ea62a55f7fd77f67fba5823e6c03a25df12f628b Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 15 Sep 2022 15:28:50 +0000 Subject: [PATCH 14/27] build(deps): upgrade dependencies and pom cleanup --- pom.xml | 54 +++---------------- robert-push-notif-server-scheduler/pom.xml | 53 ++++++++---------- .../src/test/resources/application-dev.yml | 6 +-- robert-push-notif-server-ws-rest/pom.xml | 34 +++--------- .../src/test/resources/application-dev.yml | 7 ++- 5 files changed, 42 insertions(+), 112 deletions(-) diff --git a/pom.xml b/pom.xml index b338704..a2036d2 100644 --- a/pom.xml +++ b/pom.xml @@ -9,10 +9,9 @@ org.springframework.boot spring-boot-starter-parent - 2.3.2.RELEASE + 2.3.12.RELEASE - fr.gouv.stopc robert-push-notif-server 0-SNAPSHOT @@ -26,17 +25,13 @@ - 11 - 1.18.24 - 3.1.1 - 2.1.1 - 2.0.1.Final - 42.2.12 3.11 - 1.15.3 - 2.17.0 - 5.2.20.RELEASE - Hoxton.SR5 + 11 + 2.17.1 + 42.5.0 + Hoxton.SR12 + 5.2.22.RELEASE + 1.17.3 @@ -94,36 +89,6 @@ pom import - - - javax.inject - javax.inject - 1 - - - - javax.ws.rs - javax.ws.rs-api - ${javax-rs.version} - - - - javax.validation - validation-api - ${javax.validation.version} - - - - org.postgresql - postgresql - ${postgresql.version} - - - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - @@ -224,11 +189,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - ${maven.jar.plugin.version} - diff --git a/robert-push-notif-server-scheduler/pom.xml b/robert-push-notif-server-scheduler/pom.xml index d8a03e5..3b2773b 100644 --- a/robert-push-notif-server-scheduler/pom.xml +++ b/robert-push-notif-server-scheduler/pom.xml @@ -13,67 +13,60 @@ robert-push-notif-server-scheduler + + 7.6.0 + 0.15.1 + + - org.springframework.cloud - spring-cloud-starter-vault-config + org.springframework.boot + spring-boot-starter-web - org.springframework.boot - spring-boot-starter-jdbc + spring-boot-starter-validation - org.springframework.boot - spring-boot-starter-validation + spring-boot-starter-actuator + + + io.micrometer + micrometer-registry-prometheus + + + org.springframework.cloud + spring-cloud-starter-vault-config + + + org.springframework.boot + spring-boot-starter-jdbc - org.springframework spring-jdbc - org.postgresql postgresql runtime - com.eatthepath pushy - 0.15.1 + ${pushy.version} - com.github.vladimir-bukhtoyarov bucket4j-core - 7.5.0 - - - - - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - io.micrometer - micrometer-registry-prometheus - runtime + ${bucket4j.version} org.apache.commons commons-lang3 - org.awaitility awaitility diff --git a/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml b/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml index 602c876..40f718f 100644 --- a/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml +++ b/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml @@ -1,8 +1,4 @@ -logging: - file: - name: ./target/logs/test.log - level: - fr.gouv.stopc: DEBUG +spring.cloud.vault.enabled: false robert.push.server: # Min/Max Push Notification Hours diff --git a/robert-push-notif-server-ws-rest/pom.xml b/robert-push-notif-server-ws-rest/pom.xml index 6d0563a..fd09a88 100644 --- a/robert-push-notif-server-ws-rest/pom.xml +++ b/robert-push-notif-server-ws-rest/pom.xml @@ -17,11 +17,6 @@ Rest Webservice Module - UTF-8 - - UTF-8 - - 1.17.1 5.4.0 @@ -32,22 +27,22 @@ org.springframework.boot spring-boot-starter-web - org.springframework.cloud - spring-cloud-starter-vault-config - - - - org.springframework.boot + spring-cloud-starter-vault-config + + + org.springframework.boot spring-boot-starter-validation - org.springframework.boot spring-boot-starter-actuator - + + io.micrometer + micrometer-registry-prometheus + org.postgresql postgresql @@ -63,12 +58,6 @@ rest-assured test - - - - io.micrometer - micrometer-registry-prometheus - @@ -119,13 +108,6 @@ org.springframework.boot spring-boot-maven-plugin - - - - build-info - - - diff --git a/robert-push-notif-server-ws-rest/src/test/resources/application-dev.yml b/robert-push-notif-server-ws-rest/src/test/resources/application-dev.yml index 0cdf5c6..6ea0b91 100644 --- a/robert-push-notif-server-ws-rest/src/test/resources/application-dev.yml +++ b/robert-push-notif-server-ws-rest/src/test/resources/application-dev.yml @@ -4,7 +4,6 @@ spring: hibernate: jdbc: time_zone: UTC -logging: - level: - org.flywaydb.core.internal: OFF - + cloud.vault.enabled: false +logging.level: + org.flywaydb.core.internal: OFF -- GitLab From 585122f9da0e566a0b9f6309411d6aad755a785e Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Fri, 16 Sep 2022 09:25:31 +0000 Subject: [PATCH 15/27] refactor(scheduler): reword 'dev' profile used in opaque tests to 'test' --- .../src/main/resources/application.yml | 3 +-- .../robert/pushnotif/scheduler/SchedulerNominalTest.java | 2 +- .../pushnotif/scheduler/SchedulerVolumetryTest.java | 2 -- .../scheduler/SchedulerWithTwoApnsServerTest.java | 2 -- .../ratelimiting/SchedulerRateLimiting1sTest.java | 8 ++++---- .../ratelimiting/SchedulerRateLimiting2sTest.java | 8 ++++---- .../robert/pushnotif/scheduler/test/IntegrationTest.java | 2 ++ .../{application-dev.yml => application-test.yml} | 7 +------ 8 files changed, 13 insertions(+), 21 deletions(-) rename robert-push-notif-server-scheduler/src/test/resources/{application-dev.yml => application-test.yml} (85%) diff --git a/robert-push-notif-server-scheduler/src/main/resources/application.yml b/robert-push-notif-server-scheduler/src/main/resources/application.yml index 8bad79a..e43b077 100644 --- a/robert-push-notif-server-scheduler/src/main/resources/application.yml +++ b/robert-push-notif-server-scheduler/src/main/resources/application.yml @@ -23,8 +23,7 @@ robert.push.server: min-push-hour: 8 max-push-hour: 20 - scheduler: - delay-in-ms: 30000 + scheduler.delay-in-ms: 30000 max-number-of-pending-notifications: 10000 max-notifications-per-second: 200 diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index 4a74a44..89e655f 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -31,7 +31,7 @@ import static org.assertj.core.api.Assertions.within; import static org.awaitility.Awaitility.await; @IntegrationTest -@ActiveProfiles({ "dev", "one-apns-server" }) +@ActiveProfiles({ "test", "one-apns-server" }) @DirtiesContext class SchedulerNominalTest { diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java index 862cab9..07f4188 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java @@ -3,7 +3,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.PushInfo; import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatAllPushInfo; @@ -15,7 +14,6 @@ import static org.assertj.core.api.Assertions.tuple; import static org.awaitility.Awaitility.await; @IntegrationTest -@ActiveProfiles({ "dev" }) class SchedulerVolumetryTest { private static final int PUSH_NOTIF_COUNT = 100; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index fe8f938..974898a 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -5,7 +5,6 @@ import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import org.junit.jupiter.api.Test; -import org.springframework.test.context.ActiveProfiles; import java.time.Duration; import java.time.Instant; @@ -25,7 +24,6 @@ import static org.assertj.core.api.Assertions.*; import static org.awaitility.Awaitility.await; @IntegrationTest -@ActiveProfiles({ "dev" }) class SchedulerWithTwoApnsServerTest { @Test diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java index f53a58f..f822fb5 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java @@ -5,7 +5,6 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import java.time.Duration; @@ -20,9 +19,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @IntegrationTest -@ActiveProfiles("dev") -@TestPropertySource(properties = { "robert.push.server.max-notifications-per-second=1", - "robert.push.server.scheduler.delay-in-ms=10000000000" }) +@TestPropertySource(properties = { + "robert.push.server.max-notifications-per-second=1", + "robert.push.server.scheduler.delay-in-ms=10000000000" +}) class SchedulerRateLimiting1sTest { @Autowired diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java index d63a11f..69976d5 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java @@ -5,7 +5,6 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestPropertySource; import java.time.Duration; @@ -20,9 +19,10 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; @IntegrationTest -@ActiveProfiles("dev") -@TestPropertySource(properties = { "robert.push.server.max-notifications-per-second=2", - "robert.push.server.scheduler.delay-in-ms=10000000000" }) +@TestPropertySource(properties = { + "robert.push.server.max-notifications-per-second=2", + "robert.push.server.scheduler.delay-in-ms=10000000000" +}) class SchedulerRateLimiting2sTest { @Autowired diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/IntegrationTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/IntegrationTest.java index 3e86d09..27ba97b 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/IntegrationTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/IntegrationTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.TestExecutionListeners; import java.lang.annotation.Retention; @@ -18,6 +19,7 @@ import static org.springframework.test.context.TestExecutionListeners.MergeMode. @DirtiesContext @Retention(RUNTIME) @SpringBootTest(webEnvironment = RANDOM_PORT) +@ActiveProfiles({ "test" }) @DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class) @TestExecutionListeners(listeners = { APNsMockServersManager.class, PsqlManager.class, MetricsManager.class }, mergeMode = MERGE_WITH_DEFAULTS) diff --git a/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml similarity index 85% rename from robert-push-notif-server-scheduler/src/test/resources/application-dev.yml rename to robert-push-notif-server-scheduler/src/test/resources/application-test.yml index 40f718f..3674f3a 100644 --- a/robert-push-notif-server-scheduler/src/test/resources/application-dev.yml +++ b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml @@ -1,9 +1,8 @@ -spring.cloud.vault.enabled: false - robert.push.server: # Min/Max Push Notification Hours min-push-hour: 8 max-push-hour: 10 + scheduler.delay-in-ms: 1000 apns: clients: - host: localhost @@ -15,8 +14,4 @@ robert.push.server: auth-key-id: key-id team-id: team-id topic: test - - #path to the trusted certificate chain trusted-client-certificate-chain: classpath:/apns/ca.pem - - scheduler.delay-in-ms: 1000 -- GitLab From 959d204dba322b1553f50b43803898495d92d923 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Tue, 20 Sep 2022 12:24:09 +0000 Subject: [PATCH 16/27] test(scheduler): verbose assertion errors --- robert-push-notif-server-scheduler/pom.xml | 6 + .../scheduler/SchedulerNominalTest.java | 153 +++++------- .../scheduler/SchedulerVolumetryTest.java | 3 +- .../SchedulerWithTwoApnsServerTest.java | 226 +++++++----------- 4 files changed, 143 insertions(+), 245 deletions(-) diff --git a/robert-push-notif-server-scheduler/pom.xml b/robert-push-notif-server-scheduler/pom.xml index 3b2773b..da585cb 100644 --- a/robert-push-notif-server-scheduler/pom.xml +++ b/robert-push-notif-server-scheduler/pom.xml @@ -15,6 +15,7 @@ 7.6.0 + 2.0.8 0.15.1 @@ -72,6 +73,11 @@ awaitility test + + org.exparity + hamcrest-date + ${hamcrest-date.version} + diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index 89e655f..bb8bc87 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -1,6 +1,5 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; -import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; import fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager; @@ -10,10 +9,9 @@ import org.junit.jupiter.api.Test; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ActiveProfiles; -import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.ACCEPTED; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.REJECTED; @@ -22,13 +20,17 @@ import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManag import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.MetricsManager.assertCounterIncremented; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; import static java.time.Instant.now; import static java.time.ZoneOffset.UTC; -import static java.util.concurrent.TimeUnit.SECONDS; +import static java.time.temporal.ChronoUnit.*; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; +import static org.assertj.core.api.HamcrestCondition.matching; import static org.awaitility.Awaitility.await; +import static org.exparity.hamcrest.date.InstantMatchers.after; +import static org.exparity.hamcrest.date.InstantMatchers.within; +import static org.hamcrest.Matchers.hasProperty; @IntegrationTest @ActiveProfiles({ "test", "one-apns-server" }) @@ -50,64 +52,43 @@ class SchedulerNominalTest { // Then // Verify APNs servers - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedOne(); assertThatMainServerRejectedNothing(); - assertThat(APNsMockServersManager.getNotifsAcceptedByMainServer().get(0)) - .as("Check the content of the notification received on the APNs server side") - .satisfies( - notif -> { - assertThat(notif.getExpiration()) - .isCloseTo(now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)); - assertThat(notif.getPayload()) - .isEqualTo("{\"aps\":{\"badge\":0,\"content-available\":1}}"); - } - ).extracting( - ApnsPushNotification::getPushType, - ApnsPushNotification::getPriority, - ApnsPushNotification::getToken, - ApnsPushNotification::getTopic + assertThat(APNsMockServersManager.getNotifsAcceptedByMainServer()) + .hasSize(1) + .first() + .hasFieldOrPropertyWithValue("pushType", PushType.BACKGROUND) + .hasFieldOrPropertyWithValue("priority", DeliveryPriority.IMMEDIATE) + .hasFieldOrPropertyWithValue( + "token", + "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad" ) - .containsExactly( - PushType.BACKGROUND, DeliveryPriority.IMMEDIATE, - "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", "test" - ); + .hasFieldOrPropertyWithValue("topic", "test") + .hasFieldOrPropertyWithValue("payload", "{\"aps\":{\"badge\":0,\"content-available\":1}}") + .is(matching(hasProperty("expiration", within(30, SECONDS, now().plus(1, DAYS))))); // Verify Database assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as("Check the status of the notification that has been correctly sent to APNs server") - .satisfies(pushInfo -> { - assertThat(pushInfo.getLastSuccessfulPush()) - .as("Last successful push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ).extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastFailurePush, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent - ) - .containsExactly(true, false, 0, null, null, 1); + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 0) + .hasFieldOrPropertyWithValue("lastFailurePush", null) + .hasFieldOrPropertyWithValue("lastErrorCode", null) + .hasFieldOrPropertyWithValue("successfulPushSent", 1) + .is(matching(hasProperty("lastSuccessfulPush", within(1, MINUTES, now())))) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); assertThatPushInfo("45f6aa01da5ddb387462c7eaf61bb78ad740f4707bebcf74f9b7c25d48e33589") - .as("This notification is not pushed because its planned date is in future") - .extracting( - PushInfo::isActive, PushInfo::isDeleted, PushInfo::getFailedPushSent, - PushInfo::getLastFailurePush, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush, - PushInfo::getNextPlannedPush - ) - .containsExactly( - true, false, 0, null, null, 0, null, LocalDateTime - .from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 0) + .hasFieldOrPropertyWithValue("lastFailurePush", null) + .hasFieldOrPropertyWithValue("lastErrorCode", null) + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .hasFieldOrPropertyWithValue("nextPlannedPush", now().plus(1, DAYS).truncatedTo(DAYS)); + // Verify counters assertCounterIncremented( "pushy.notifications.sent.timer", @@ -131,30 +112,19 @@ class SchedulerNominalTest { // Then // Verify servers - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); // Verify database assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .satisfies( - pushInfoFromBase -> { - assertThat(pushInfoFromBase.getLastFailurePush()) - .as("Last successful push should have been updated") - .isNotNull(); - assertThat(pushInfoFromBase.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)) - .toInstant(UTC) - ); - } - ).extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush - ) - .contains(false, false, 1, "BadDeviceToken", 0, null); + .hasFieldOrPropertyWithValue("active", false) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 1) + .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) + .hasFieldOrPropertyWithValue("lastErrorCode", "BadDeviceToken") + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); // Verify counters assertCounterIncremented( @@ -179,33 +149,20 @@ class SchedulerNominalTest { // Then // Verify server - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); // Verify database assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as( - "Check the status of the notification that has been rejected by APNs server - notif is not deactivated" - ) - .satisfies( - pushInfo -> { - assertThat(pushInfo.getLastFailurePush()) - .as("Last failure push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ).extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush - ) - .contains(true, false, 1, "BadMessageId", 0, null); + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 1) + .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) + .hasFieldOrPropertyWithValue("lastErrorCode", "BadMessageId") + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); // Verify counters assertCounterIncremented( diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java index 07f4188..db01cb2 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java @@ -5,8 +5,7 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.PushInfo; import org.junit.jupiter.api.Test; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatAllPushInfo; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; import static java.util.UUID.randomUUID; import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.stream.LongStream.rangeClosed; diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index 974898a..58f6725 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -1,27 +1,30 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; -import com.eatthepath.pushy.apns.ApnsPushNotification; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import org.junit.jupiter.api.Test; -import java.time.Duration; -import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.concurrent.TimeUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECOND; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; -import static java.time.ZoneOffset.UTC; -import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.Assertions.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; +import static java.time.Instant.now; +import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; +import static org.assertj.core.api.HamcrestCondition.matching; import static org.awaitility.Awaitility.await; +import static org.exparity.hamcrest.date.InstantMatchers.*; +import static org.exparity.hamcrest.date.InstantMatchers.after; +import static org.exparity.hamcrest.date.InstantMatchers.within; +import static org.hamcrest.Matchers.*; @IntegrationTest class SchedulerWithTwoApnsServerTest { @@ -35,49 +38,36 @@ class SchedulerWithTwoApnsServerTest { // When // Then - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedOne(); assertThatMainServerRejectedNothing(); assertThatSecondServerRejectedNothing(); assertThatSecondServerAcceptedNothing(); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as("Check the status of the notification that has been correctly sent to main APNs server") - .satisfies( - p -> { - assertThat(p.getLastSuccessfulPush()) - .as("Last successful push should have been updated") - .isNotNull(); - assertThat(p.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ) - .extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastFailurePush, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 0) + .hasFieldOrPropertyWithValue("lastFailurePush", null) + .hasFieldOrPropertyWithValue("lastErrorCode", null) + .hasFieldOrPropertyWithValue("successfulPushSent", 1) + .is(matching(hasProperty("lastSuccessfulPush", within(1, MINUTES, now())))) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); + + assertThat(getNotifsAcceptedByMainServer()) + .hasSize(1) + .first() + .hasFieldOrPropertyWithValue( + "token", + "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad" ) - .containsExactly(true, false, 0, null, null, 1); - - assertThat(getNotifsAcceptedByMainServer().get(0)) - .as("Check the content of the notification received on the main APNs server side") + .hasFieldOrPropertyWithValue("pushType", PushType.BACKGROUND) + .hasFieldOrPropertyWithValue("priority", DeliveryPriority.IMMEDIATE) + .hasFieldOrPropertyWithValue("topic", "test") + .hasFieldOrPropertyWithValue("payload", "{\"aps\":{\"badge\":0,\"content-available\":1}}") .satisfies( notif -> assertThat(notif.getExpiration()) - .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)) - ).extracting( - ApnsPushNotification::getPushType, - ApnsPushNotification::getPriority, - ApnsPushNotification::getToken, - ApnsPushNotification::getTopic, - ApnsPushNotification::getPayload - ).containsExactly( - PushType.BACKGROUND, DeliveryPriority.IMMEDIATE, - "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", "test", - "{\"aps\":{\"badge\":0,\"content-available\":1}}" + .isCloseTo(now().plus(1, DAYS), within(30, ChronoUnit.SECONDS)) ); }); } @@ -94,34 +84,21 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedNothing(); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as( - "Check the status of the notification that has been rejected by main APNs server (reason other than invalid token)" - ) - .satisfies( - pushInfo -> { - assertThat(pushInfo.getLastFailurePush()) - .as("Last failure push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ).extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush - ) - .containsExactly(true, false, 1, "BadTopic", 0, null); + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 1) + .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) + .hasFieldOrPropertyWithValue("lastErrorCode", "BadTopic") + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); }); } @@ -137,51 +114,36 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedOne(); assertThatSecondServerRejectedNothing(); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as("Check the status of the notification that has been correctly sent to secondary APNs server") - .satisfies( - pushInfo -> { - assertThat(pushInfo.getLastSuccessfulPush()) - .as("Last successful push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()) - .isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)) - .toInstant(UTC) - ); - } - ).extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastFailurePush, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 0) + .hasFieldOrPropertyWithValue("lastFailurePush", null) + .hasFieldOrPropertyWithValue("lastErrorCode", null) + .hasFieldOrPropertyWithValue("successfulPushSent", 1) + .is(matching(hasProperty("lastSuccessfulPush", within(1, MINUTES, now())))) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); + + assertThat(getNotifsAcceptedBySecondServer()) + .hasSize(1) + .first() + .hasFieldOrPropertyWithValue( + "token", + "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad" ) - .containsExactly(true, false, 0, null, null, 1); - - assertThat(getNotifsAcceptedBySecondServer().get(0)) - .as("Check the content of the notification received on the secondary APNs server side") + .hasFieldOrPropertyWithValue("pushType", PushType.BACKGROUND) + .hasFieldOrPropertyWithValue("priority", DeliveryPriority.IMMEDIATE) + .hasFieldOrPropertyWithValue("topic", "test") + .hasFieldOrPropertyWithValue("payload", "{\"aps\":{\"badge\":0,\"content-available\":1}}") .satisfies( notif -> assertThat(notif.getExpiration()) - .isCloseTo(Instant.now().plus(Duration.ofDays(1)), within(30, ChronoUnit.SECONDS)) - ).extracting( - ApnsPushNotification::getPushType, - ApnsPushNotification::getPriority, - ApnsPushNotification::getToken, - ApnsPushNotification::getTopic, - ApnsPushNotification::getPayload - ) - .containsExactly( - PushType.BACKGROUND, DeliveryPriority.IMMEDIATE, - "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", "test", - "{\"aps\":{\"badge\":0,\"content-available\":1}}" + .isCloseTo(now().plus(1, DAYS), within(30, ChronoUnit.SECONDS)) ); }); } @@ -202,7 +164,7 @@ class SchedulerWithTwoApnsServerTest { // Then - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); @@ -210,26 +172,14 @@ class SchedulerWithTwoApnsServerTest { assertThatSecondServerRejectedOne(); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as("Check the status of the notification that has been rejected by all APNs server") - .satisfies( - pushInfo -> { - assertThat(pushInfo.getLastFailurePush()) - .as("Last failure push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ) - .extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush - ) - .containsExactly(false, false, 1, "BadDeviceToken", 0, null); + .hasFieldOrPropertyWithValue("active", false) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 1) + .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) + .hasFieldOrPropertyWithValue("lastErrorCode", "BadDeviceToken") + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); }); } @@ -248,35 +198,21 @@ class SchedulerWithTwoApnsServerTest { // When -- triggering of the scheduled job // Then - await().atMost(40, SECONDS).untilAsserted(() -> { + await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatMainServerAcceptedNothing(); assertThatMainServerRejectedOne(); assertThatSecondServerAcceptedNothing(); assertThatSecondServerRejectedOne(); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") - .as( - "Check the status of the notification that has been rejected by main APNs server (reason other than invalid token)" - ) - .satisfies( - pushInfo -> { - assertThat(pushInfo.getLastFailurePush()) - .as("Last failure push should have been updated") - .isNotNull(); - assertThat(pushInfo.getNextPlannedPush()).isAfter( - LocalDateTime.from(LocalDate.now().atStartOfDay().plusDays(1)).toInstant(UTC) - ); - } - ) - .extracting( - PushInfo::isActive, - PushInfo::isDeleted, - PushInfo::getFailedPushSent, - PushInfo::getLastErrorCode, - PushInfo::getSuccessfulPushSent, - PushInfo::getLastSuccessfulPush - ) - .containsExactly(true, false, 1, "PayloadEmpty", 0, null); + .hasFieldOrPropertyWithValue("active", true) + .hasFieldOrPropertyWithValue("deleted", false) + .hasFieldOrPropertyWithValue("failedPushSent", 1) + .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) + .hasFieldOrPropertyWithValue("lastErrorCode", "PayloadEmpty") + .hasFieldOrPropertyWithValue("successfulPushSent", 0) + .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) + .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); }); } } -- GitLab From 318c041bbef265935fe75160aafa30efcf354d03 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Tue, 20 Sep 2022 13:43:56 +0000 Subject: [PATCH 17/27] test(scheduler): reword mock apns server ids --- .../scheduler/SchedulerNominalTest.java | 6 +-- .../SchedulerWithTwoApnsServerTest.java | 17 ++++----- .../test/APNsMockServersManager.java | 38 +++++++++---------- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index bb8bc87..6f1b363 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -17,7 +17,7 @@ import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.A import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.REJECTED; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.MetricsManager.assertCounterIncremented; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; import static java.time.Instant.now; @@ -104,7 +104,7 @@ class SchedulerNominalTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); // When - triggering of the scheduled task @@ -141,7 +141,7 @@ class SchedulerNominalTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_MESSAGE_ID + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_MESSAGE_ID ); // When - triggering of the scheduled task diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index 58f6725..5a68461 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -10,8 +10,8 @@ import java.util.concurrent.TimeUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECOND; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; import static java.time.Instant.now; @@ -21,7 +21,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.within; import static org.assertj.core.api.HamcrestCondition.matching; import static org.awaitility.Awaitility.await; -import static org.exparity.hamcrest.date.InstantMatchers.*; import static org.exparity.hamcrest.date.InstantMatchers.after; import static org.exparity.hamcrest.date.InstantMatchers.within; import static org.hamcrest.Matchers.*; @@ -78,7 +77,7 @@ class SchedulerWithTwoApnsServerTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_TOPIC + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_TOPIC ); // When -- triggering of the scheduled job @@ -108,7 +107,7 @@ class SchedulerWithTwoApnsServerTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); // When -- triggering of the scheduled job @@ -154,10 +153,10 @@ class SchedulerWithTwoApnsServerTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); givenApnsServerRejectsTokenIdWith( - SECOND, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + SECONDARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); // When -- triggering of the scheduled job @@ -189,10 +188,10 @@ class SchedulerWithTwoApnsServerTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - FIRST, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN ); givenApnsServerRejectsTokenIdWith( - SECOND, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", PAYLOAD_EMPTY + SECONDARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", PAYLOAD_EMPTY ); // When -- triggering of the scheduled job diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java index b836440..00d13eb 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java @@ -9,8 +9,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.FIRST; -import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECOND; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; import static org.assertj.core.api.Assertions.assertThat; /** @@ -20,8 +20,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class APNsMockServersManager implements TestExecutionListener { private static final Map servers = Map.of( - FIRST, new ApnsMockServerDecorator(2198), - SECOND, new ApnsMockServerDecorator(2197) + PRIMARY, new ApnsMockServerDecorator(2198), + SECONDARY, new ApnsMockServerDecorator(2197) ); public static void givenApnsServerRejectsTokenIdWith(final ServerId serverId, @@ -34,56 +34,56 @@ public class APNsMockServersManager implements TestExecutionListener { public void beforeTestExecution(final TestContext testContext) throws Exception { TestExecutionListener.super.beforeTestExecution(testContext); servers.values().forEach(ApnsMockServerDecorator::resetMock); - servers.get(FIRST).clear(); - servers.get(SECOND).clear(); + servers.get(PRIMARY).clear(); + servers.get(SECONDARY).clear(); } public static List getNotifsAcceptedBySecondServer() { - return new ArrayList<>(servers.get(SECOND).getAcceptedPushNotifications()); + return new ArrayList<>(servers.get(SECONDARY).getAcceptedPushNotifications()); } public static List getNotifsAcceptedByMainServer() { - return new ArrayList<>(servers.get(FIRST).getAcceptedPushNotifications()); + return new ArrayList<>(servers.get(PRIMARY).getAcceptedPushNotifications()); } public static void assertThatMainServerAcceptedOne() { - assertThat(servers.get(FIRST).getAcceptedPushNotifications()).hasSize(1); + assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).hasSize(1); } public static void assertThatMainServerAccepted(final int nbNotifications) { - assertThat(servers.get(FIRST).getAcceptedPushNotifications()).hasSize(nbNotifications); + assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).hasSize(nbNotifications); } public static void assertThatMainServerAcceptedNothing() { - assertThat(servers.get(FIRST).getAcceptedPushNotifications()).isEmpty(); + assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).isEmpty(); } public static void assertThatMainServerRejectedOne() { - assertThat(servers.get(FIRST).getRejectedPushNotifications()).hasSize(1); + assertThat(servers.get(PRIMARY).getRejectedPushNotifications()).hasSize(1); } public static void assertThatMainServerRejectedNothing() { - assertThat(servers.get(FIRST).getRejectedPushNotifications()).isEmpty(); + assertThat(servers.get(PRIMARY).getRejectedPushNotifications()).isEmpty(); } public static void assertThatSecondServerAcceptedOne() { - assertThat(servers.get(SECOND).getAcceptedPushNotifications()).hasSize(1); + assertThat(servers.get(SECONDARY).getAcceptedPushNotifications()).hasSize(1); } public static void assertThatSecondServerAcceptedNothing() { - assertThat(servers.get(SECOND).getAcceptedPushNotifications()).isEmpty(); + assertThat(servers.get(SECONDARY).getAcceptedPushNotifications()).isEmpty(); } public static void assertThatSecondServerRejectedOne() { - assertThat(servers.get(SECOND).getRejectedPushNotifications()).hasSize(1); + assertThat(servers.get(SECONDARY).getRejectedPushNotifications()).hasSize(1); } public static void assertThatSecondServerRejectedNothing() { - assertThat(servers.get(SECOND).getRejectedPushNotifications()).isEmpty(); + assertThat(servers.get(SECONDARY).getRejectedPushNotifications()).isEmpty(); } public enum ServerId { - FIRST, - SECOND + PRIMARY, + SECONDARY } } -- GitLab From d997748258814cee764353a61a83ba6694a6a1a3 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Tue, 20 Sep 2022 14:15:15 +0000 Subject: [PATCH 18/27] test(scheduler): mock assertion verbosity --- .../scheduler/SchedulerNominalTest.java | 22 +++---- .../scheduler/SchedulerVolumetryTest.java | 9 ++- .../SchedulerWithTwoApnsServerTest.java | 62 +++++++++---------- .../test/APNsMockServersManager.java | 60 ++++++------------ 4 files changed, 65 insertions(+), 88 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index 6f1b363..f1db189 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -2,7 +2,6 @@ package fr.gouv.stopc.robert.pushnotif.scheduler; import com.eatthepath.pushy.apns.DeliveryPriority; import com.eatthepath.pushy.apns.PushType; -import fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import io.micrometer.core.instrument.Tags; import org.junit.jupiter.api.Test; @@ -18,14 +17,12 @@ import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.ApnsRequestOutcome.R import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.MetricsManager.assertCounterIncremented; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; import static java.time.Instant.now; import static java.time.ZoneOffset.UTC; import static java.time.temporal.ChronoUnit.*; -import static java.time.temporal.ChronoUnit.DAYS; -import static java.time.temporal.ChronoUnit.MINUTES; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.HamcrestCondition.matching; import static org.awaitility.Awaitility.await; import static org.exparity.hamcrest.date.InstantMatchers.after; @@ -53,9 +50,7 @@ class SchedulerNominalTest { // Verify APNs servers await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedOne(); - assertThatMainServerRejectedNothing(); - assertThat(APNsMockServersManager.getNotifsAcceptedByMainServer()) + assertThatNotifsAcceptedBy(PRIMARY) .hasSize(1) .first() .hasFieldOrPropertyWithValue("pushType", PushType.BACKGROUND) @@ -113,8 +108,11 @@ class SchedulerNominalTest { // Verify servers await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); + // Verify database assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", false) @@ -150,8 +148,10 @@ class SchedulerNominalTest { // Verify server await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); // Verify database assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java index db01cb2..024439e 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerVolumetryTest.java @@ -5,6 +5,8 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.PushInfo; import org.junit.jupiter.api.Test; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.*; import static java.util.UUID.randomUUID; import static java.util.concurrent.TimeUnit.SECONDS; @@ -28,9 +30,10 @@ class SchedulerVolumetryTest { // Then await().atMost(40, SECONDS).untilAsserted(() -> { - assertThatMainServerAccepted(PUSH_NOTIF_COUNT); - assertThatSecondServerAcceptedNothing(); - assertThatSecondServerRejectedNothing(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(PUSH_NOTIF_COUNT); + assertThatNotifsRejectedBy(PRIMARY).hasSize(0); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); assertThatAllPushInfo() .hasSize(PUSH_NOTIF_COUNT) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index 5a68461..c575f4f 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -5,19 +5,17 @@ import com.eatthepath.pushy.apns.PushType; import fr.gouv.stopc.robert.pushnotif.scheduler.test.IntegrationTest; import org.junit.jupiter.api.Test; -import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.*; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; +import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.assertThatNotifsAcceptedBy; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.assertThatPushInfo; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.PsqlManager.givenPushInfoForToken; import static java.time.Instant.now; -import static java.time.temporal.ChronoUnit.DAYS; -import static java.time.temporal.ChronoUnit.MINUTES; -import static org.assertj.core.api.Assertions.assertThat; +import static java.time.temporal.ChronoUnit.*; import static org.assertj.core.api.Assertions.within; import static org.assertj.core.api.HamcrestCondition.matching; import static org.awaitility.Awaitility.await; @@ -38,10 +36,10 @@ class SchedulerWithTwoApnsServerTest { // Then await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedOne(); - assertThatMainServerRejectedNothing(); - assertThatSecondServerRejectedNothing(); - assertThatSecondServerAcceptedNothing(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(1); + assertThatNotifsRejectedBy(PRIMARY).hasSize(0); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", true) @@ -53,7 +51,7 @@ class SchedulerWithTwoApnsServerTest { .is(matching(hasProperty("lastSuccessfulPush", within(1, MINUTES, now())))) .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); - assertThat(getNotifsAcceptedByMainServer()) + assertThatNotifsAcceptedBy(PRIMARY) .hasSize(1) .first() .hasFieldOrPropertyWithValue( @@ -64,10 +62,7 @@ class SchedulerWithTwoApnsServerTest { .hasFieldOrPropertyWithValue("priority", DeliveryPriority.IMMEDIATE) .hasFieldOrPropertyWithValue("topic", "test") .hasFieldOrPropertyWithValue("payload", "{\"aps\":{\"badge\":0,\"content-available\":1}}") - .satisfies( - notif -> assertThat(notif.getExpiration()) - .isCloseTo(now().plus(1, DAYS), within(30, ChronoUnit.SECONDS)) - ); + .is(matching(hasProperty("expiration", within(30, SECONDS, now().plus(1, DAYS))))); }); } @@ -84,10 +79,10 @@ class SchedulerWithTwoApnsServerTest { // Then await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); - assertThatSecondServerAcceptedNothing(); - assertThatSecondServerRejectedNothing(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", true) @@ -114,10 +109,11 @@ class SchedulerWithTwoApnsServerTest { // Then await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); - assertThatSecondServerAcceptedOne(); - assertThatSecondServerRejectedNothing(); + + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(1); + assertThatNotifsRejectedBy(SECONDARY).hasSize(0); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", true) @@ -129,7 +125,7 @@ class SchedulerWithTwoApnsServerTest { .is(matching(hasProperty("lastSuccessfulPush", within(1, MINUTES, now())))) .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); - assertThat(getNotifsAcceptedBySecondServer()) + assertThatNotifsAcceptedBy(SECONDARY) .hasSize(1) .first() .hasFieldOrPropertyWithValue( @@ -140,10 +136,7 @@ class SchedulerWithTwoApnsServerTest { .hasFieldOrPropertyWithValue("priority", DeliveryPriority.IMMEDIATE) .hasFieldOrPropertyWithValue("topic", "test") .hasFieldOrPropertyWithValue("payload", "{\"aps\":{\"badge\":0,\"content-available\":1}}") - .satisfies( - notif -> assertThat(notif.getExpiration()) - .isCloseTo(now().plus(1, DAYS), within(30, ChronoUnit.SECONDS)) - ); + .is(matching(hasProperty("expiration", within(30, SECONDS, now().plus(1, DAYS))))); }); } @@ -165,10 +158,10 @@ class SchedulerWithTwoApnsServerTest { await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); - assertThatSecondServerAcceptedNothing(); - assertThatSecondServerRejectedOne(); + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(1); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", false) @@ -198,10 +191,11 @@ class SchedulerWithTwoApnsServerTest { // Then await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { - assertThatMainServerAcceptedNothing(); - assertThatMainServerRejectedOne(); - assertThatSecondServerAcceptedNothing(); - assertThatSecondServerRejectedOne(); + + assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); + assertThatNotifsRejectedBy(PRIMARY).hasSize(1); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); + assertThatNotifsRejectedBy(SECONDARY).hasSize(1); assertThatPushInfo("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad") .hasFieldOrPropertyWithValue("active", true) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java index 00d13eb..6eb93a3 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/APNsMockServersManager.java @@ -2,6 +2,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.test; import com.eatthepath.pushy.apns.ApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; +import org.assertj.core.api.ListAssert; import org.springframework.test.context.TestContext; import org.springframework.test.context.TestExecutionListener; @@ -11,6 +12,7 @@ import java.util.Map; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.PRIMARY; import static fr.gouv.stopc.robert.pushnotif.scheduler.test.APNsMockServersManager.ServerId.SECONDARY; +import static java.util.stream.Collectors.joining; import static org.assertj.core.api.Assertions.assertThat; /** @@ -34,52 +36,30 @@ public class APNsMockServersManager implements TestExecutionListener { public void beforeTestExecution(final TestContext testContext) throws Exception { TestExecutionListener.super.beforeTestExecution(testContext); servers.values().forEach(ApnsMockServerDecorator::resetMock); - servers.get(PRIMARY).clear(); - servers.get(SECONDARY).clear(); + servers.values().forEach(ApnsMockServerDecorator::clear); } - public static List getNotifsAcceptedBySecondServer() { - return new ArrayList<>(servers.get(SECONDARY).getAcceptedPushNotifications()); + public static ListAssert assertThatNotifsAcceptedBy(final ServerId apnsServerId) { + final var notifs = new ArrayList<>(servers.get(apnsServerId).getAcceptedPushNotifications()); + return assertThat(notifs) + .describedAs("Notifications accepted by APNS %s server:\n%s", apnsServerId, describe(notifs)); } - public static List getNotifsAcceptedByMainServer() { - return new ArrayList<>(servers.get(PRIMARY).getAcceptedPushNotifications()); + public static ListAssert assertThatNotifsRejectedBy(final ServerId apnsServerId) { + final var notifs = new ArrayList<>(servers.get(apnsServerId).getRejectedPushNotifications()); + return assertThat(notifs) + .describedAs("Notifications rejected by APNS %s server:\n%s", apnsServerId, describe(notifs)); } - public static void assertThatMainServerAcceptedOne() { - assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).hasSize(1); - } - - public static void assertThatMainServerAccepted(final int nbNotifications) { - assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).hasSize(nbNotifications); - } - - public static void assertThatMainServerAcceptedNothing() { - assertThat(servers.get(PRIMARY).getAcceptedPushNotifications()).isEmpty(); - } - - public static void assertThatMainServerRejectedOne() { - assertThat(servers.get(PRIMARY).getRejectedPushNotifications()).hasSize(1); - } - - public static void assertThatMainServerRejectedNothing() { - assertThat(servers.get(PRIMARY).getRejectedPushNotifications()).isEmpty(); - } - - public static void assertThatSecondServerAcceptedOne() { - assertThat(servers.get(SECONDARY).getAcceptedPushNotifications()).hasSize(1); - } - - public static void assertThatSecondServerAcceptedNothing() { - assertThat(servers.get(SECONDARY).getAcceptedPushNotifications()).isEmpty(); - } - - public static void assertThatSecondServerRejectedOne() { - assertThat(servers.get(SECONDARY).getRejectedPushNotifications()).hasSize(1); - } - - public static void assertThatSecondServerRejectedNothing() { - assertThat(servers.get(SECONDARY).getRejectedPushNotifications()).isEmpty(); + private static String describe(List notifs) { + return notifs.stream() + .map( + n -> String.format( + " - token=%s, topic=%s, pushType=%s, priority=%s, expiration=%s", n.getToken(), + n.getTopic(), n.getPushType(), n.getPriority(), n.getExpiration() + ) + ) + .collect(joining("\n")); } public enum ServerId { -- GitLab From cc3716337fff6e1c899e88189fe17b61e4ae5575 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Tue, 20 Sep 2022 14:19:28 +0000 Subject: [PATCH 19/27] test(scheduler): introduce parameter to control the grace time waiting for notifications to be sent in an execution --- .../fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java | 3 +-- .../scheduler/configuration/RobertPushServerProperties.java | 4 ++++ .../src/main/resources/application.yml | 1 + .../scheduler/ratelimiting/SchedulerRateLimiting1sTest.java | 3 ++- .../scheduler/ratelimiting/SchedulerRateLimiting2sTest.java | 3 ++- .../src/test/resources/application-test.yml | 1 + 6 files changed, 11 insertions(+), 4 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 6930200..4009469 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -17,7 +17,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; -import java.time.Duration; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -51,7 +50,7 @@ public class Scheduler { apnsTemplate.sendNotification(notification, new WakeUpDeviceNotificationHandler(pushInfo)); }); - apnsTemplate.waitUntilNoActivity(Duration.ofSeconds(10)); + apnsTemplate.waitUntilNoActivity(robertPushServerProperties.getBatchTerminationGraceTime()); } /** diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/RobertPushServerProperties.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/RobertPushServerProperties.java index 102422c..d46a79e 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/RobertPushServerProperties.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/RobertPushServerProperties.java @@ -13,6 +13,7 @@ import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Positive; +import java.time.Duration; import java.util.List; @Value @@ -35,6 +36,9 @@ public class RobertPushServerProperties { @Positive int maxNotificationsPerSecond; + @NotNull + Duration batchTerminationGraceTime; + @Valid RobertPushServerProperties.Apns apns; diff --git a/robert-push-notif-server-scheduler/src/main/resources/application.yml b/robert-push-notif-server-scheduler/src/main/resources/application.yml index e43b077..ef430bd 100644 --- a/robert-push-notif-server-scheduler/src/main/resources/application.yml +++ b/robert-push-notif-server-scheduler/src/main/resources/application.yml @@ -27,6 +27,7 @@ robert.push.server: max-number-of-pending-notifications: 10000 max-notifications-per-second: 200 + batch-termination-grace-time: 10s apns: inactive-rejection-reason: BadDeviceToken,DeviceTokenNotForTopic diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java index f822fb5..de54385 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java @@ -55,7 +55,8 @@ class SchedulerRateLimiting1sTest { ) .containsOnly(tuple(true, false, 0, null, null, 1, 0)); + final var expectedDuration = Duration.ofSeconds(notificationsNumber); assertThat(Duration.between(before, after)) - .isGreaterThanOrEqualTo(Duration.ofSeconds(notificationsNumber)); + .isBetween(expectedDuration.minusSeconds(1), expectedDuration.plusSeconds(1)); } } diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java index 69976d5..6b6bfe5 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java @@ -55,7 +55,8 @@ class SchedulerRateLimiting2sTest { ) .containsOnly(tuple(true, false, 0, null, null, 1, 0)); + final var expectedDuration = Duration.ofSeconds(notificationsNumber / 2); assertThat(Duration.between(before, after)) - .isGreaterThanOrEqualTo(Duration.ofSeconds(notificationsNumber / 2)); + .isBetween(expectedDuration.minusSeconds(1), expectedDuration.plusSeconds(1)); } } diff --git a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml index 3674f3a..3c4080a 100644 --- a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml +++ b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml @@ -3,6 +3,7 @@ robert.push.server: min-push-hour: 8 max-push-hour: 10 scheduler.delay-in-ms: 1000 + batch-termination-grace-time: 1s apns: clients: - host: localhost -- GitLab From 00cee4050d3534a39b3bff15c0e21ced2c563d00 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Tue, 20 Sep 2022 15:04:17 +0000 Subject: [PATCH 20/27] test(scheduler): use same reasons list as production --- .../pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java | 2 +- .../src/test/resources/application-test.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index c575f4f..a5443dd 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -146,7 +146,7 @@ class SchedulerWithTwoApnsServerTest { // Given givenPushInfoForToken("740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad"); givenApnsServerRejectsTokenIdWith( - PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN + PRIMARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", DEVICE_TOKEN_NOT_FOR_TOPIC ); givenApnsServerRejectsTokenIdWith( SECONDARY, "740f4707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bb78ad", BAD_DEVICE_TOKEN diff --git a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml index 3c4080a..4925424 100644 --- a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml +++ b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml @@ -10,7 +10,6 @@ robert.push.server: port: 2198 - host: localhost port: 2197 - inactive-rejection-reason: BadDeviceToken auth-token-file: classpath:/apns/token-auth-private-key.p8 auth-key-id: key-id team-id: team-id -- GitLab From 49b9a861b06da5b7d10fd6741d388c1ce09e00b1 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 08:45:43 +0000 Subject: [PATCH 21/27] refactor(scheduler): make ApnsTemplate able to signal an inactive token --- .../robert/pushnotif/scheduler/Scheduler.java | 3 ++- .../template/ApnsNotificationHandler.java | 18 ++++++------- .../scheduler/apns/template/ApnsTemplate.java | 16 ++++++++---- .../template/DelegateNotificationHandler.java | 4 +-- .../apns/template/FailoverApnsTemplate.java | 25 ++++++------------- .../apns/template/MonitoringApnsTemplate.java | 7 ++++++ .../ApnsClientConfiguration.java | 10 +++++--- 7 files changed, 42 insertions(+), 41 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 4009469..1039bdf 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -135,7 +135,8 @@ public class Scheduler { } @Override - public void disableToken() { + public void onInactive(final RejectionReason reason) { + pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); pushInfoRepository.disable(pushInfo.getId()); } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java index 8754683..308dc78 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java @@ -12,7 +12,7 @@ public interface ApnsNotificationHandler { /** * Called when the notification request is rejected - * + * * @param reason rejected push notification request response message */ void onRejection(final RejectionReason reason); @@ -25,15 +25,11 @@ public interface ApnsNotificationHandler { void onError(final Throwable reason); /** - * Called when the notification request is rejected on every configured APN - * server. In this app context, we have configured multiple APN servers. When a - * push notif request fails because of specific configured errors: - * - * @see RobertPushServerProperties.Apns#inactiveRejectionReason The app tries - * again on the next configured APN server. If the request fails on every - * APN server, this method is called. - * @param rejectionMessage rejected push notification request response message + * Called when the notification request is rejected because one of inactive + * rejection reasons. + * + * @param reason rejected push notification request response message + * @see RobertPushServerProperties.Apns#getInactiveRejectionReason() */ - @Deprecated(since = "fixme: 'disableToken' is a business rule and should not be in this interface") - void disableToken(); + void onInactive(RejectionReason reason); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index 42308cd..ef51f4e 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.time.Duration; +import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason.UNKNOWN; @@ -24,6 +25,8 @@ public class ApnsTemplate implements ApnsOperations { private final ApnsClient apnsClient; + private final List inactiveRejectionReasons; + public void sendNotification(final ApnsPushNotification notification, final ApnsNotificationHandler notificationHandler) { @@ -36,11 +39,14 @@ public class ApnsTemplate implements ApnsOperations { if (response.isAccepted()) { notificationHandler.onSuccess(); } else { - notificationHandler.onRejection( - response.getRejectionReason() - .map(RejectionReason::fromValue) - .orElse(UNKNOWN) - ); + final var rejection = response.getRejectionReason() + .map(RejectionReason::fromValue) + .orElse(UNKNOWN); + if (inactiveRejectionReasons.contains(rejection)) { + notificationHandler.onInactive(rejection); + } else { + notificationHandler.onRejection(rejection); + } } } else { // Something went wrong when trying to send the notification to the diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java index 86c16a2..0eb8acd 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java @@ -27,7 +27,7 @@ public class DelegateNotificationHandler implements ApnsNotificationHandler { } @Override - public void disableToken() { - delegate.disableToken(); + public void onInactive(RejectionReason reason) { + delegate.onInactive(reason); } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index 94976a1..f769fc0 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -19,8 +19,6 @@ public class FailoverApnsTemplate implements ApnsOperations { private final List apnsDelegates; - private final List inactiveRejectionReasons; - @Override public void sendNotification(final ApnsPushNotification notification, final ApnsNotificationHandler notificationHandler) { @@ -37,28 +35,19 @@ public class FailoverApnsTemplate implements ApnsOperations { private void sendNotification(final ApnsPushNotification notification, final ApnsNotificationHandler notificationHandler, - final ConcurrentLinkedQueue queue) { + final ConcurrentLinkedQueue apnsTemplates) { - final var client = queue.poll(); + final var client = apnsTemplates.poll(); if (client != null) { client.sendNotification(notification, new DelegateNotificationHandler(notificationHandler) { @Override - public void onRejection(final RejectionReason reason) { - if (inactiveRejectionReasons.contains(reason)) { - // rejection reason means we must try on next APN server - if (!queue.isEmpty()) { - // try next apn client in the queue - sendNotification(notification, notificationHandler, queue); - } else { - // notification was rejected on every client, then disable token - super.disableToken(); - super.onRejection(reason); - } + public void onInactive(RejectionReason reason) { + if (!apnsTemplates.isEmpty()) { + // try next apn client in the queue + sendNotification(notification, this, apnsTemplates); } else { - // rejection reason means the notification must not be attempted on next APN - // server - super.onRejection(reason); + super.onInactive(reason); } } }); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java index ea9e07d..119201a 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java @@ -122,6 +122,13 @@ public class MonitoringApnsTemplate implements ApnsOperations { log.warn("Push Notification sent by {} failed", this, cause); super.onError(cause); } + + @Override + public void onInactive(RejectionReason reason) { + pendingNotifications.decrementAndGet(); + sample.stop(getTimer(REJECTED, reason)); + super.onInactive(reason); + } }; delegate.sendNotification(notification, measuringHandler); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java index 296d537..0b58b3e 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java @@ -51,8 +51,12 @@ public class ApnsClientConfiguration { ); } + final var apnsTemplate = new ApnsTemplate( + apnsClientBuilder.build(), + robertPushServerProperties.getApns().getInactiveRejectionReason() + ); return new MonitoringApnsTemplate( - new ApnsTemplate(apnsClientBuilder.build()), + apnsTemplate, apnsClientProperties.getHost(), apnsClientProperties.getPort(), meterRegistry @@ -80,8 +84,6 @@ public class ApnsClientConfiguration { .map(this::buildRateLimitingTemplate) .collect(toUnmodifiableList()); - return new FailoverApnsTemplate( - measuredRateLimitedApnsTemplates, robertPushServerProperties.getApns().getInactiveRejectionReason() - ); + return new FailoverApnsTemplate(measuredRateLimitedApnsTemplates); } } -- GitLab From e505f702ae3c4d6d8905c3da3470f4e3a3712768 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 12:05:47 +0000 Subject: [PATCH 22/27] refactor(scheduler): reword NotificationHandler to ResponseHandler --- .../stopc/robert/pushnotif/scheduler/Scheduler.java | 6 +++--- .../scheduler/apns/template/ApnsOperations.java | 2 +- ...tificationHandler.java => ApnsResponseHandler.java} | 2 +- .../scheduler/apns/template/ApnsTemplate.java | 10 +++++----- ...onHandler.java => DelegateApnsResponseHandler.java} | 6 +++--- .../scheduler/apns/template/FailoverApnsTemplate.java | 8 ++++---- .../apns/template/MonitoringApnsTemplate.java | 4 ++-- .../apns/template/RateLimitingApnsTemplate.java | 4 ++-- 8 files changed, 21 insertions(+), 21 deletions(-) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/{ApnsNotificationHandler.java => ApnsResponseHandler.java} (95%) rename robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/{DelegateNotificationHandler.java => DelegateApnsResponseHandler.java} (76%) diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 1039bdf..2af9383 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -5,8 +5,8 @@ import com.eatthepath.pushy.apns.PushType; import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; -import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsNotificationHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsResponseHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; @@ -47,7 +47,7 @@ public class Scheduler { // times the same day updateNextPlannedPush(pushInfo); final var notification = buildWakeUpNotification(pushInfo.getToken()); - apnsTemplate.sendNotification(notification, new WakeUpDeviceNotificationHandler(pushInfo)); + apnsTemplate.sendNotification(notification, new WakeUpDeviceResponseHandler(pushInfo)); }); apnsTemplate.waitUntilNoActivity(robertPushServerProperties.getBatchTerminationGraceTime()); @@ -115,7 +115,7 @@ public class Scheduler { * Handles notification request response. */ @RequiredArgsConstructor - private class WakeUpDeviceNotificationHandler implements ApnsNotificationHandler { + private class WakeUpDeviceResponseHandler implements ApnsResponseHandler { private final PushInfo pushInfo; diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java index 5e7a65f..1325eec 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java @@ -6,7 +6,7 @@ import java.time.Duration; public interface ApnsOperations extends AutoCloseable { - void sendNotification(ApnsPushNotification notification, ApnsNotificationHandler handler); + void sendNotification(ApnsPushNotification notification, ApnsResponseHandler handler); void waitUntilNoActivity(Duration toleranceDuration); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java similarity index 95% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java index 308dc78..7e91de1 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java @@ -3,7 +3,7 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; -public interface ApnsNotificationHandler { +public interface ApnsResponseHandler { /** * Called when the notification request is accepted diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index ef51f4e..c262a68 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -28,7 +28,7 @@ public class ApnsTemplate implements ApnsOperations { private final List inactiveRejectionReasons; public void sendNotification(final ApnsPushNotification notification, - final ApnsNotificationHandler notificationHandler) { + final ApnsResponseHandler responseHandler) { pendingNotifications.incrementAndGet(); final var sendNotificationFuture = apnsClient.sendNotification(notification); @@ -37,15 +37,15 @@ public class ApnsTemplate implements ApnsOperations { pendingNotifications.decrementAndGet(); if (response != null) { if (response.isAccepted()) { - notificationHandler.onSuccess(); + responseHandler.onSuccess(); } else { final var rejection = response.getRejectionReason() .map(RejectionReason::fromValue) .orElse(UNKNOWN); if (inactiveRejectionReasons.contains(rejection)) { - notificationHandler.onInactive(rejection); + responseHandler.onInactive(rejection); } else { - notificationHandler.onRejection(rejection); + responseHandler.onRejection(rejection); } } } else { @@ -53,7 +53,7 @@ public class ApnsTemplate implements ApnsOperations { // APNs server. Note that this is distinct from a rejection from // the server, and indicates that something went wrong when actually // sending the notification or waiting for a reply. - notificationHandler.onError(cause); + responseHandler.onError(cause); } }).exceptionally(e -> { log.error("Unexpected error occurred", e); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateApnsResponseHandler.java similarity index 76% rename from robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java rename to robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateApnsResponseHandler.java index 0eb8acd..fcab3bd 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateNotificationHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/DelegateApnsResponseHandler.java @@ -4,12 +4,12 @@ import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import lombok.RequiredArgsConstructor; /** - * Base class for {@link ApnsNotificationHandler} decorators. + * Base class for {@link ApnsResponseHandler} decorators. */ @RequiredArgsConstructor -public class DelegateNotificationHandler implements ApnsNotificationHandler { +public class DelegateApnsResponseHandler implements ApnsResponseHandler { - private final ApnsNotificationHandler delegate; + private final ApnsResponseHandler delegate; @Override public void onSuccess() { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index f769fc0..1840439 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -21,11 +21,11 @@ public class FailoverApnsTemplate implements ApnsOperations { @Override public void sendNotification(final ApnsPushNotification notification, - final ApnsNotificationHandler notificationHandler) { + final ApnsResponseHandler responseHandler) { final var apnsClientsQueue = new ConcurrentLinkedQueue<>(apnsDelegates); - sendNotification(notification, notificationHandler, apnsClientsQueue); + sendNotification(notification, responseHandler, apnsClientsQueue); } @Override @@ -34,12 +34,12 @@ public class FailoverApnsTemplate implements ApnsOperations { } private void sendNotification(final ApnsPushNotification notification, - final ApnsNotificationHandler notificationHandler, + final ApnsResponseHandler responseHandler, final ConcurrentLinkedQueue apnsTemplates) { final var client = apnsTemplates.poll(); if (client != null) { - client.sendNotification(notification, new DelegateNotificationHandler(notificationHandler) { + client.sendNotification(notification, new DelegateApnsResponseHandler(responseHandler) { @Override public void onInactive(RejectionReason reason) { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java index 119201a..864be03 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java @@ -93,13 +93,13 @@ public class MonitoringApnsTemplate implements ApnsOperations { @Override public void sendNotification(final ApnsPushNotification notification, - final ApnsNotificationHandler notificationHandler) { + final ApnsResponseHandler responseHandler) { pendingNotifications.incrementAndGet(); final var sample = Timer.start(); - final var measuringHandler = new DelegateNotificationHandler(notificationHandler) { + final var measuringHandler = new DelegateApnsResponseHandler(responseHandler) { @Override public void onSuccess() { diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java index 025405e..8d98ffa 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java @@ -46,7 +46,7 @@ public class RateLimitingApnsTemplate implements ApnsOperations { @Override public void sendNotification(final ApnsPushNotification notification, - final ApnsNotificationHandler notificationHandler) { + final ApnsResponseHandler responseHandler) { try { semaphore.acquire(); @@ -55,7 +55,7 @@ public class RateLimitingApnsTemplate implements ApnsOperations { log.error("error during rate limiting process", e); return; } - final var limitedHandler = new DelegateNotificationHandler(notificationHandler) { + final var limitedHandler = new DelegateApnsResponseHandler(responseHandler) { @Override public void onSuccess() { -- GitLab From c9a2a405906bfdbffe390e76d1cab959dcca1bcf Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 13:25:13 +0000 Subject: [PATCH 23/27] feat(scheduler): log all rejections on failure/inactivation --- .../robert/pushnotif/scheduler/Scheduler.java | 21 +++-- .../apns/template/ApnsOperations.java | 4 +- .../apns/template/ApnsResponseHandler.java | 4 +- .../scheduler/apns/template/ApnsTemplate.java | 2 +- .../template/FailoverApnsResponseHandler.java | 37 +++++++++ .../apns/template/FailoverApnsTemplate.java | 81 ++++++++++++------- .../apns/template/MonitoringApnsTemplate.java | 4 +- .../template/RateLimitingApnsTemplate.java | 6 +- .../ApnsClientConfiguration.java | 8 +- .../SchedulerWithTwoApnsServerTest.java | 4 +- 10 files changed, 122 insertions(+), 49 deletions(-) create mode 100644 robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsResponseHandler.java diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 2af9383..705d059 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -6,7 +6,7 @@ import com.eatthepath.pushy.apns.util.SimpleApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsOperations; -import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsResponseHandler; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.FailoverApnsResponseHandler; import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.PushInfoRepository; import fr.gouv.stopc.robert.pushnotif.scheduler.repository.model.PushInfo; @@ -20,11 +20,13 @@ import org.springframework.stereotype.Service; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; +import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static com.eatthepath.pushy.apns.util.SimpleApnsPushNotification.DEFAULT_EXPIRATION_PERIOD; import static com.eatthepath.pushy.apns.util.TokenUtil.sanitizeTokenString; import static java.time.temporal.ChronoUnit.MINUTES; +import static java.util.stream.Collectors.joining; @Slf4j @Service @@ -115,7 +117,7 @@ public class Scheduler { * Handles notification request response. */ @RequiredArgsConstructor - private class WakeUpDeviceResponseHandler implements ApnsResponseHandler { + private class WakeUpDeviceResponseHandler implements FailoverApnsResponseHandler { private final PushInfo pushInfo; @@ -125,8 +127,9 @@ public class Scheduler { } @Override - public void onRejection(final RejectionReason reason) { - pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); + public void onRejection(final List reasons) { + + pushInfoRepository.updateFailure(pushInfo.getId(), concat(reasons)); } @Override @@ -135,9 +138,15 @@ public class Scheduler { } @Override - public void onInactive(final RejectionReason reason) { - pushInfoRepository.updateFailure(pushInfo.getId(), reason.getValue()); + public void onInactive(final List reasons) { + pushInfoRepository.updateFailure(pushInfo.getId(), concat(reasons)); pushInfoRepository.disable(pushInfo.getId()); } + + private String concat(List reasons) { + return reasons.stream() + .map(RejectionReason::getValue) + .collect(joining(",")); + } } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java index 1325eec..b68cfb9 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsOperations.java @@ -4,9 +4,9 @@ import com.eatthepath.pushy.apns.ApnsPushNotification; import java.time.Duration; -public interface ApnsOperations extends AutoCloseable { +public interface ApnsOperations extends AutoCloseable { - void sendNotification(ApnsPushNotification notification, ApnsResponseHandler handler); + void sendNotification(ApnsPushNotification notification, T handler); void waitUntilNoActivity(Duration toleranceDuration); } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java index 7e91de1..baf5ec2 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsResponseHandler.java @@ -20,9 +20,9 @@ public interface ApnsResponseHandler { /** * Called when the notification request fails before reaching Apple server. * - * @param reason error message + * @param cause error message */ - void onError(final Throwable reason); + void onError(final Throwable cause); /** * Called when the notification request is rejected because one of inactive diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index c262a68..751a046 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -19,7 +19,7 @@ import static java.util.concurrent.TimeUnit.SECONDS; */ @Slf4j @RequiredArgsConstructor -public class ApnsTemplate implements ApnsOperations { +public class ApnsTemplate implements ApnsOperations { private final AtomicInteger pendingNotifications = new AtomicInteger(0); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsResponseHandler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsResponseHandler.java new file mode 100644 index 0000000..8b1eb71 --- /dev/null +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsResponseHandler.java @@ -0,0 +1,37 @@ +package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; + +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.RejectionReason; +import fr.gouv.stopc.robert.pushnotif.scheduler.configuration.RobertPushServerProperties; + +import java.util.List; + +public interface FailoverApnsResponseHandler { + + /** + * Called when the notification request is accepted. + */ + void onSuccess(); + + /** + * Called when the notification request is rejected. + * + * @param reasons rejected push response messages + */ + void onRejection(List reasons); + + /** + * Called when the notification request fails before reaching Apple server. + * + * @param reason error message + */ + void onError(Throwable reason); + + /** + * Called when the notification request is rejected because one of inactive + * rejection reasons. + * + * @param reasons rejected push response messages + * @see RobertPushServerProperties.Apns#getInactiveRejectionReason() + */ + void onInactive(List reasons); +} diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index 1840439..11bf558 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; @@ -15,17 +16,22 @@ import java.util.concurrent.ConcurrentLinkedQueue; */ @Slf4j @RequiredArgsConstructor -public class FailoverApnsTemplate implements ApnsOperations { +public class FailoverApnsTemplate implements ApnsOperations { - private final List apnsDelegates; + private final List> apnsDelegates; @Override public void sendNotification(final ApnsPushNotification notification, - final ApnsResponseHandler responseHandler) { + final FailoverApnsResponseHandler responseHandler) { - final var apnsClientsQueue = new ConcurrentLinkedQueue<>(apnsDelegates); - - sendNotification(notification, responseHandler, apnsClientsQueue); + final var apnsTemplates = new ConcurrentLinkedQueue<>(apnsDelegates); + final var first = apnsTemplates.poll(); + if (first != null) { + first.sendNotification( + notification, + new TryOnNextServerAfterInactiveResponseHandler(notification, apnsTemplates, responseHandler) + ); + } } @Override @@ -33,35 +39,54 @@ public class FailoverApnsTemplate implements ApnsOperations { apnsDelegates.parallelStream().forEach(it -> it.waitUntilNoActivity(toleranceDuration)); } - private void sendNotification(final ApnsPushNotification notification, - final ApnsResponseHandler responseHandler, - final ConcurrentLinkedQueue apnsTemplates) { - - final var client = apnsTemplates.poll(); - if (client != null) { - client.sendNotification(notification, new DelegateApnsResponseHandler(responseHandler) { - - @Override - public void onInactive(RejectionReason reason) { - if (!apnsTemplates.isEmpty()) { - // try next apn client in the queue - sendNotification(notification, this, apnsTemplates); - } else { - super.onInactive(reason); - } - } - }); - } - } - @Override public void close() { apnsDelegates.parallelStream().forEach(delegate -> { try { delegate.close(); - } catch (Exception e) { + } catch (final Exception e) { log.error("Unable to close {} gracefully", delegate, e); } }); } + + @RequiredArgsConstructor + private static class TryOnNextServerAfterInactiveResponseHandler implements ApnsResponseHandler { + + private final List rejectionsHistory = new ArrayList<>(); + + private final ApnsPushNotification notification; + + private final ConcurrentLinkedQueue> apnsTemplates; + + private final FailoverApnsResponseHandler failoverResponseHandler; + + @Override + public void onSuccess() { + failoverResponseHandler.onSuccess(); + } + + @Override + public void onRejection(final RejectionReason reason) { + rejectionsHistory.add(reason); + failoverResponseHandler.onRejection(rejectionsHistory); + } + + @Override + public void onError(final Throwable cause) { + failoverResponseHandler.onError(cause); + } + + @Override + public void onInactive(final RejectionReason reason) { + rejectionsHistory.add(reason); + final var nextApnsTemplate = apnsTemplates.poll(); + if (null != nextApnsTemplate) { + // try next apns in the queue + nextApnsTemplate.sendNotification(notification, this); + } else { + failoverResponseHandler.onInactive(rejectionsHistory); + } + } + } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java index 864be03..3b46dfe 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/MonitoringApnsTemplate.java @@ -28,7 +28,7 @@ import static java.util.stream.Stream.concat; */ @Slf4j @ToString(onlyExplicitlyIncluded = true) -public class MonitoringApnsTemplate implements ApnsOperations { +public class MonitoringApnsTemplate implements ApnsOperations { private final AtomicInteger pendingNotifications = new AtomicInteger(0); @@ -38,7 +38,7 @@ public class MonitoringApnsTemplate implements ApnsOperations { private static final String REJECTION_REASON_TAG_KEY = "rejectionReason"; - private final ApnsOperations delegate; + private final ApnsOperations delegate; @ToString.Include private final String host; diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java index 8d98ffa..6290cc2 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/RateLimitingApnsTemplate.java @@ -15,18 +15,18 @@ import java.util.concurrent.Semaphore; * An APNS template decorator to limit notification rate. */ @Slf4j -public class RateLimitingApnsTemplate implements ApnsOperations { +public class RateLimitingApnsTemplate implements ApnsOperations { private final LocalBucket rateLimitingBucket; - private final ApnsOperations delegate; + private final ApnsOperations delegate; private final Semaphore semaphore; public RateLimitingApnsTemplate( final int maxNotificationsPerSecond, final int maxNumberOfPendingNotifications, - final ApnsOperations delegate) { + final ApnsOperations delegate) { this.delegate = delegate; this.semaphore = new Semaphore(maxNumberOfPendingNotifications); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java index 0b58b3e..4fa713c 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/configuration/ApnsClientConfiguration.java @@ -25,7 +25,8 @@ public class ApnsClientConfiguration { private final MeterRegistry meterRegistry; - private ApnsOperations buildMeasureApnsTemplate(final RobertPushServerProperties.ApnsClient apnsClientProperties) { + private ApnsOperations buildMeasureApnsTemplate( + final RobertPushServerProperties.ApnsClient apnsClientProperties) { final var listener = new MicrometerApnsClientMetricsListener( meterRegistry, @@ -69,7 +70,8 @@ public class ApnsClientConfiguration { } } - private ApnsOperations buildRateLimitingTemplate(final ApnsOperations apnsOperations) { + private ApnsOperations buildRateLimitingTemplate( + final ApnsOperations apnsOperations) { return new RateLimitingApnsTemplate( robertPushServerProperties.getMaxNotificationsPerSecond(), robertPushServerProperties.getMaxNumberOfPendingNotifications(), @@ -78,7 +80,7 @@ public class ApnsClientConfiguration { } @Bean - public ApnsOperations apnsTemplate() { + public ApnsOperations apnsTemplate() { final var measuredRateLimitedApnsTemplates = robertPushServerProperties.getApns().getClients().stream() .map(this::buildMeasureApnsTemplate) .map(this::buildRateLimitingTemplate) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java index a5443dd..0bdbaf5 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerWithTwoApnsServerTest.java @@ -168,7 +168,7 @@ class SchedulerWithTwoApnsServerTest { .hasFieldOrPropertyWithValue("deleted", false) .hasFieldOrPropertyWithValue("failedPushSent", 1) .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) - .hasFieldOrPropertyWithValue("lastErrorCode", "BadDeviceToken") + .hasFieldOrPropertyWithValue("lastErrorCode", "DeviceTokenNotForTopic,BadDeviceToken") .hasFieldOrPropertyWithValue("successfulPushSent", 0) .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); @@ -202,7 +202,7 @@ class SchedulerWithTwoApnsServerTest { .hasFieldOrPropertyWithValue("deleted", false) .hasFieldOrPropertyWithValue("failedPushSent", 1) .is(matching(hasProperty("lastFailurePush", within(1, MINUTES, now())))) - .hasFieldOrPropertyWithValue("lastErrorCode", "PayloadEmpty") + .hasFieldOrPropertyWithValue("lastErrorCode", "BadDeviceToken,PayloadEmpty") .hasFieldOrPropertyWithValue("successfulPushSent", 0) .hasFieldOrPropertyWithValue("lastSuccessfulPush", null) .is(matching(hasProperty("nextPlannedPush", after(now().plus(1, DAYS).truncatedTo(DAYS))))); -- GitLab From 6e72a60e7520948d5fa33b7ec91275b595c5ce81 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 14:21:19 +0000 Subject: [PATCH 24/27] test(scheduler): raise up time tolerance for rate limiting tests --- .../scheduler/ratelimiting/SchedulerRateLimiting1sTest.java | 2 +- .../scheduler/ratelimiting/SchedulerRateLimiting2sTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java index de54385..4cfb54e 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting1sTest.java @@ -57,6 +57,6 @@ class SchedulerRateLimiting1sTest { final var expectedDuration = Duration.ofSeconds(notificationsNumber); assertThat(Duration.between(before, after)) - .isBetween(expectedDuration.minusSeconds(1), expectedDuration.plusSeconds(1)); + .isGreaterThanOrEqualTo(expectedDuration.minusSeconds(1)); } } diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java index 6b6bfe5..17a7973 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/ratelimiting/SchedulerRateLimiting2sTest.java @@ -57,6 +57,6 @@ class SchedulerRateLimiting2sTest { final var expectedDuration = Duration.ofSeconds(notificationsNumber / 2); assertThat(Duration.between(before, after)) - .isBetween(expectedDuration.minusSeconds(1), expectedDuration.plusSeconds(1)); + .isGreaterThanOrEqualTo(expectedDuration.minusSeconds(1)); } } -- GitLab From 09172465ec67d21ce07a9475b5197962681df598 Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 13:56:12 +0000 Subject: [PATCH 25/27] refactor(scheduler): name apns templates in toString() methods for debugging purposes --- .../robert/pushnotif/scheduler/Scheduler.java | 3 +-- .../MicrometerApnsClientMetricsListener.java | 15 ++++++++------ .../apns/template/ApnsServerCoordinates.java | 16 +++++++++++++++ .../scheduler/apns/template/ApnsTemplate.java | 9 +++++++++ .../apns/template/FailoverApnsTemplate.java | 12 ++++++++++- .../apns/template/MonitoringApnsTemplate.java | 20 +++++++++---------- .../template/RateLimitingApnsTemplate.java | 7 ++++++- .../ApnsClientConfiguration.java | 12 +++++++---- .../src/test/resources/application-test.yml | 3 +++ 9 files changed, 72 insertions(+), 25 deletions(-) create mode 100644 robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsServerCoordinates.java diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java index 705d059..03d9e4e 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/Scheduler.java @@ -37,13 +37,12 @@ public class Scheduler { private final RobertPushServerProperties robertPushServerProperties; - private final ApnsOperations apnsTemplate; + private final ApnsOperations apnsTemplate; @Scheduled(fixedDelayString = "${robert.push.server.scheduler.delay-in-ms}") @Timed(value = "push.notifier.duration", description = "on going export duration", longTask = true) @Counted(value = "push.notifier.calls", description = "count each time the scheduler sending notifications is triggered") public void sendNotifications() { - pushInfoRepository.forEachNotificationToBeSent(pushInfo -> { // set the next planned push to be sure the notification could not be sent 2 // times the same day diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/MicrometerApnsClientMetricsListener.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/MicrometerApnsClientMetricsListener.java index 4f63b88..7f53a46 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/MicrometerApnsClientMetricsListener.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/MicrometerApnsClientMetricsListener.java @@ -2,9 +2,9 @@ package fr.gouv.stopc.robert.pushnotif.scheduler.apns; import com.eatthepath.pushy.apns.ApnsClient; import com.eatthepath.pushy.apns.ApnsClientMetricsListener; +import fr.gouv.stopc.robert.pushnotif.scheduler.apns.template.ApnsServerCoordinates; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Tags; import java.util.concurrent.atomic.AtomicInteger; @@ -69,13 +69,16 @@ public class MicrometerApnsClientMetricsListener implements ApnsClientMetricsLis * Constructs a new Micrometer metrics listener that adds metrics to the given * registry with the given list of tags. * - * @param meterRegistry the registry to which to add metrics - * @param host the apns server host - * @param port the apns server port + * @param meterRegistry the registry to which to add metrics + * @param serverCoordinates the apns server host and port */ - public MicrometerApnsClientMetricsListener(final MeterRegistry meterRegistry, String host, int port) { + public MicrometerApnsClientMetricsListener(final MeterRegistry meterRegistry, + final ApnsServerCoordinates serverCoordinates) { - Iterable tags = Tags.of("host", host, "port", "" + port); + final var tags = Tags.of( + "host", serverCoordinates.getHost(), + "port", String.valueOf(serverCoordinates.getPort()) + ); this.writeFailures = meterRegistry.counter(WRITE_FAILURES_COUNTER_NAME, tags); this.sentNotifications = meterRegistry.counter(SENT_NOTIFICATIONS_COUNTER_NAME, tags); diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsServerCoordinates.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsServerCoordinates.java new file mode 100644 index 0000000..9df0da6 --- /dev/null +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsServerCoordinates.java @@ -0,0 +1,16 @@ +package fr.gouv.stopc.robert.pushnotif.scheduler.apns.template; + +import lombok.Value; + +@Value +public class ApnsServerCoordinates { + + String host; + + int port; + + @Override + public String toString() { + return String.format("%s:%d", host, port); + } +} diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java index 751a046..7cffdd0 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/ApnsTemplate.java @@ -23,6 +23,8 @@ public class ApnsTemplate implements ApnsOperations { private final AtomicInteger pendingNotifications = new AtomicInteger(0); + private final ApnsServerCoordinates serverCoordinates; + private final ApnsClient apnsClient; private final List inactiveRejectionReasons; @@ -70,11 +72,18 @@ public class ApnsTemplate implements ApnsOperations { log.warn("Unable to wait until all notifications are sent", e); } } while (pendingNotifications.get() != 0); + log.info("{} has no more pending notifications"); } @Override public void close() throws Exception { log.info("Shutting down {}, gracefully waiting 1 minute", this); apnsClient.close().get(1, MINUTES); + log.info("{} is stopped", this); + } + + @Override + public String toString() { + return String.format("ApnsTemplate(%s)", serverCoordinates); } } diff --git a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java index 11bf558..d3986d8 100644 --- a/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java +++ b/robert-push-notif-server-scheduler/src/main/java/fr/gouv/stopc/robert/pushnotif/scheduler/apns/template/FailoverApnsTemplate.java @@ -10,6 +10,8 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; +import static java.util.stream.Collectors.joining; + /** * An APNS template able to defer notification sending to fallback servers * depending on the reason returned from the previous server. @@ -36,7 +38,7 @@ public class FailoverApnsTemplate implements ApnsOperations it.waitUntilNoActivity(toleranceDuration)); + apnsDelegates.forEach(apnsTemplate -> apnsTemplate.waitUntilNoActivity(toleranceDuration)); } @Override @@ -50,6 +52,14 @@ public class FailoverApnsTemplate implements ApnsOperations { private final AtomicInteger pendingNotifications = new AtomicInteger(0); @@ -40,19 +38,16 @@ public class MonitoringApnsTemplate implements ApnsOperations delegate; - @ToString.Include private final String host; - @ToString.Include private final Integer port; public MonitoringApnsTemplate(final ApnsTemplate delegate, - final String host, - final Integer port, + final ApnsServerCoordinates serverCoordinates, final MeterRegistry meterRegistry) { - this.host = host; - this.port = port; + this.host = serverCoordinates.getHost(); + this.port = serverCoordinates.getPort(); this.delegate = delegate; final var successTags = Stream.of( @@ -141,10 +136,14 @@ public class MonitoringApnsTemplate implements ApnsOperations shutting down delegate: {}", this, delegate); delegate.close(); } + @Override + public String toString() { + return String.format("Monitoring(%s)", delegate); + } + /** * returns the Timer matching various tags matching parameters. * @@ -155,8 +154,7 @@ public class MonitoringApnsTemplate implements ApnsOperations buildMeasureApnsTemplate( final RobertPushServerProperties.ApnsClient apnsClientProperties) { - final var listener = new MicrometerApnsClientMetricsListener( - meterRegistry, + final var apnsServerCoordinates = new ApnsServerCoordinates( apnsClientProperties.getHost(), apnsClientProperties.getPort() ); + final var listener = new MicrometerApnsClientMetricsListener( + meterRegistry, + apnsServerCoordinates + ); + try (final var authTokenFile = this.robertPushServerProperties.getApns().getAuthTokenFile().getInputStream()) { final var apnsClientBuilder = new ApnsClientBuilder() .setApnsServer(apnsClientProperties.getHost(), apnsClientProperties.getPort()) @@ -53,13 +57,13 @@ public class ApnsClientConfiguration { } final var apnsTemplate = new ApnsTemplate( + apnsServerCoordinates, apnsClientBuilder.build(), robertPushServerProperties.getApns().getInactiveRejectionReason() ); return new MonitoringApnsTemplate( apnsTemplate, - apnsClientProperties.getHost(), - apnsClientProperties.getPort(), + apnsServerCoordinates, meterRegistry ); diff --git a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml index 4925424..7b1f800 100644 --- a/robert-push-notif-server-scheduler/src/test/resources/application-test.yml +++ b/robert-push-notif-server-scheduler/src/test/resources/application-test.yml @@ -15,3 +15,6 @@ robert.push.server: team-id: team-id topic: test trusted-client-certificate-chain: classpath:/apns/ca.pem + +logging.level: + org.flywaydb.core.internal.command.DbMigrate: WARN -- GitLab From dd6208aa60e05d661e1c4fdf02a48b49a4a9e48e Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Wed, 21 Sep 2022 14:19:26 +0000 Subject: [PATCH 26/27] test(scheduler): verify gauge of pending notifs goes down to 0 between tests --- .../robert/pushnotif/scheduler/test/MetricsManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/MetricsManager.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/MetricsManager.java index 8c02227..43ed08b 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/MetricsManager.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/test/MetricsManager.java @@ -34,6 +34,15 @@ public class MetricsManager implements TestExecutionListener { .collect(Collectors.toMap(Timer::getId, Timer::count)); } + @Override + public void afterTestMethod(TestContext testContext) { + final var pendingNotifications = meterRegistry.get("pushy.notifications.pending") + .gauge(); + assertThat(pendingNotifications.value()) + .describedAs("'pushy.notifications.pending' gauge metric") + .isEqualTo(0.0); + } + public static AbstractLongAssert assertCounterIncremented(final String name, final long increment, final Tags tags) { final var timer = meterRegistry.timer(name, tags.and(SERVER_INFORMATION_TAGS)); -- GitLab From 6a6c5a8e3bd13778e4387f5361a0238b3ff1d21b Mon Sep 17 00:00:00 2001 From: Jujube Orange <13631-x-JOrang@users.noreply.gitlab.inria.fr> Date: Thu, 22 Sep 2022 09:11:32 +0000 Subject: [PATCH 27/27] test(scheduler): fix wrong apns mock verification --- .../stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java index f1db189..4ac1095 100644 --- a/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java +++ b/robert-push-notif-server-scheduler/src/test/java/fr/gouv/stopc/robert/pushnotif/scheduler/SchedulerNominalTest.java @@ -110,7 +110,7 @@ class SchedulerNominalTest { await().atMost(40, TimeUnit.SECONDS).untilAsserted(() -> { assertThatNotifsAcceptedBy(PRIMARY).hasSize(0); assertThatNotifsRejectedBy(PRIMARY).hasSize(1); - assertThatNotifsRejectedBy(SECONDARY).hasSize(0); + assertThatNotifsAcceptedBy(SECONDARY).hasSize(0); assertThatNotifsRejectedBy(SECONDARY).hasSize(0); // Verify database -- GitLab