diff --git a/apps/institution-send-mail-scheduler/Dockerfile b/apps/institution-send-mail-scheduler/Dockerfile new file mode 100644 index 0000000..22b22da --- /dev/null +++ b/apps/institution-send-mail-scheduler/Dockerfile @@ -0,0 +1,53 @@ +# syntax=docker/dockerfile:1.6@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021 +FROM maven:3-eclipse-temurin-17@sha256:0d328fa6843bb26b60cf44d69833f241ffe96218fb29fa19df7a6603863eaae7 AS builder + +WORKDIR /src +COPY --link pom.xml . + +WORKDIR /src/test-coverage +COPY --link ./test-coverage/pom.xml . + +WORKDIR /src/apps +COPY --link ./apps/pom.xml . + +WORKDIR /src/apps/institution-send-mail-scheduler +COPY --link ./apps/institution-send-mail-scheduler/pom.xml . +COPY ./apps/institution-send-mail-scheduler/src/main/ ./src/main/ + +WORKDIR /src + +RUN echo "\n" \ + "\n" \ + "\n" \ + "\${repositoryId}\n" \ + "\${repoLogin}\n" \ + "\${repoPwd}\n" \ + "\n" \ + "\n" \ + "\n" > settings.xml + +ARG REPO_ONBOARDING +ARG REPO_USERNAME +ARG REPO_PASSWORD + +RUN mvn --global-settings settings.xml --projects :institution-send-mail-scheduler -DrepositoryId=${REPO_ONBOARDING} -DrepoLogin=${REPO_USERNAME} -DrepoPwd=${REPO_PASSWORD} --also-make clean package -DskipTests + +FROM openjdk:17-jdk@sha256:528707081fdb9562eb819128a9f85ae7fe000e2fbaeaf9f87662e7b3f38cb7d8 AS runtime + +ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' +ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager" + +WORKDIR /app + +COPY --from=builder /src/apps/institution-send-mail-scheduler/target/quarkus-app/lib/ ./lib/ +COPY --from=builder /src/apps/institution-send-mail-scheduler/target/quarkus-app/*.jar ./ +COPY --from=builder /src/apps/institution-send-mail-scheduler/target/quarkus-app/app/ ./app/ +COPY --from=builder /src/apps/institution-send-mail-scheduler/target/quarkus-app/quarkus/ ./quarkus/ + +ADD https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.2.11/applicationinsights-agent-3.2.11.jar ./applicationinsights-agent.jar +RUN chmod 755 ./applicationinsights-agent.jar + +EXPOSE 8080 +USER 1001 + +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTIONS -jar /app/quarkus-run.jar"] diff --git a/apps/institution-send-mail-scheduler/pom.xml b/apps/institution-send-mail-scheduler/pom.xml index 1708b6c..7520c90 100644 --- a/apps/institution-send-mail-scheduler/pom.xml +++ b/apps/institution-send-mail-scheduler/pom.xml @@ -24,7 +24,7 @@ 3.11.3 true 3.1.2 - 0.1.16 + 0.1.14 2.4.2 @@ -129,6 +129,11 @@ 4.13.1 test + + com.azure + azure-storage-blob + 12.14.0 + diff --git a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/config/InstitutionSendMailConfig.java b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/config/InstitutionSendMailConfig.java index 1af3092..cbf2e3d 100644 --- a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/config/InstitutionSendMailConfig.java +++ b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/config/InstitutionSendMailConfig.java @@ -6,28 +6,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import io.quarkus.runtime.StartupEvent; -import io.vertx.core.impl.logging.Logger; -import io.vertx.core.impl.logging.LoggerFactory; import io.vertx.core.json.jackson.DatabindCodec; import it.pagopa.selfcare.azurestorage.AzureBlobClient; import it.pagopa.selfcare.azurestorage.AzureBlobClientDefault; -import it.pagopa.selfcare.institution.repository.PecNotificationsRepository; import it.pagopa.selfcare.product.service.ProductService; import it.pagopa.selfcare.product.service.ProductServiceDefault; import jakarta.enterprise.context.ApplicationScoped; -import jakarta.enterprise.event.Observes; import jakarta.enterprise.inject.Produces; @ApplicationScoped public class InstitutionSendMailConfig { - private static final Logger log = LoggerFactory.getLogger(InstitutionSendMailConfig.class); - - void onStart(@Observes StartupEvent ev, PecNotificationsRepository repository) { - log.info(String.format("Database %s is starting...", repository.mongoDatabase().getName())); - } - @Produces public ObjectMapper objectMapper(){ ObjectMapper mapper = DatabindCodec.mapper(); diff --git a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/entity/PecNotification.java b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/entity/PecNotification.java index 54210a9..4386fad 100644 --- a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/entity/PecNotification.java +++ b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/entity/PecNotification.java @@ -1,48 +1,22 @@ package it.pagopa.selfcare.institution.entity; import io.quarkus.mongodb.panache.common.MongoEntity; -import lombok.Getter; -import org.bson.codecs.pojo.annotations.BsonId; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.FieldNameConstants; import org.bson.types.ObjectId; -@Getter -@MongoEntity(collection="pecNotification") -public class PecNotification { - @BsonId +@EqualsAndHashCode(callSuper = true) +@Data +@FieldNameConstants(asEnum = true) +@MongoEntity(collection="PecNotification") +public class PecNotification extends ReactivePanacheMongoEntity{ + private ObjectId id; private Integer moduleDayOfTheEpoch; private String productId; private String institutionId; private String institutionMail; - - public void setInstitutionMail(String institutionMail) { - this.institutionMail = institutionMail; - } - - public void setId(ObjectId id) { - this.id = id; - } - - public void setModuleDayOfTheEpoch(Integer moduleDayOfTheEpoch) { - this.moduleDayOfTheEpoch = moduleDayOfTheEpoch; - } - - public void setProductId(String productId) { - this.productId = productId; - } - - public void setInstitutionId(String institutionId) { - this.institutionId = institutionId; - } - - @Override - public String toString() { - return "PecNotification{" + - "id=" + id + - ", moduleDayOfTheEpoch=" + moduleDayOfTheEpoch + - ", productId='" + productId + '\'' + - ", institutionId='" + institutionId + '\'' + - ", institutionMail='" + institutionMail + '\'' + - '}'; - } + private String createdAt; } diff --git a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/repository/PecNotificationsRepository.java b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/repository/PecNotificationsRepository.java deleted file mode 100644 index fe85fe7..0000000 --- a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/repository/PecNotificationsRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package it.pagopa.selfcare.institution.repository; - -import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase; -import it.pagopa.selfcare.institution.entity.PecNotification; -import jakarta.enterprise.context.ApplicationScoped; - -@ApplicationScoped -public class PecNotificationsRepository implements PanacheMongoRepositoryBase { -} diff --git a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImpl.java b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImpl.java index e762bce..3ffd5de 100644 --- a/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImpl.java +++ b/apps/institution-send-mail-scheduler/src/main/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImpl.java @@ -1,12 +1,11 @@ package it.pagopa.selfcare.institution.service; -import io.quarkus.mongodb.panache.PanacheQuery; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoEntityBase; import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import io.vertx.core.impl.logging.Logger; import io.vertx.core.impl.logging.LoggerFactory; import it.pagopa.selfcare.institution.entity.PecNotification; -import it.pagopa.selfcare.institution.repository.PecNotificationsRepository; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.service.ProductService; import jakarta.enterprise.context.ApplicationScoped; @@ -24,7 +23,6 @@ public class InstitutionSendMailScheduledServiceImpl implements InstitutionSendM private static final Logger log = LoggerFactory.getLogger(InstitutionSendMailScheduledServiceImpl.class); private static final String PRODUCT_PLACEHOLDER = "nome_prodotto"; - private final PecNotificationsRepository pecNotificationsRepository; private final MailServiceImpl mailService; private final String templateMail; private final ProductService productService; @@ -32,14 +30,12 @@ public class InstitutionSendMailScheduledServiceImpl implements InstitutionSendM private final Integer sendingFrequency; private final Integer querySize; - public InstitutionSendMailScheduledServiceImpl(PecNotificationsRepository pecNotificationsRepository, - MailServiceImpl mailService, + public InstitutionSendMailScheduledServiceImpl(MailServiceImpl mailService, @ConfigProperty(name = "institution-send-mail.notification-path") String templateMail, @ConfigProperty(name = "institution-send-mail.notification-sending-frequency") Integer sendingFrequency, @ConfigProperty(name = "institution-send-mail.notification-start-date") String startDate, ProductService productService, @ConfigProperty(name = "institution-send-mail.notification-query-size") Integer querySize) { - this.pecNotificationsRepository = pecNotificationsRepository; this.mailService = mailService; this.templateMail = templateMail; this.productService = productService; @@ -57,24 +53,33 @@ public Uni retrieveInstitutionFromPecNotificationAndSendMail() { public Uni retrieveFilteredAndPaginatedPecNotification(Long moduleDayOfTheEpoch, int page, int size) { log.info("Retrieving page " + page + " of PecNotification"); - PanacheQuery query = pecNotificationsRepository.find("moduleDayOfTheEpoch", moduleDayOfTheEpoch) + + var pecNotificationPage = PecNotification.find(PecNotification.Fields.moduleDayOfTheEpoch.name(), moduleDayOfTheEpoch) .page(page, size); - if(query.hasNextPage()) { - return retrievePecNotificationListAndSendMail(query) - .onItem().transformToUni(unused -> retrieveFilteredAndPaginatedPecNotification(moduleDayOfTheEpoch, page + 1, size)) - .replaceWith(0); - }else{ - return retrievePecNotificationListAndSendMail(query) - .replaceWith(0); - } + + return Uni.combine().all() + .unis(pecNotificationPage.hasNextPage(), pecNotificationPage.list()) + .asTuple() + .onItem().transformToUni(tuple -> { + if(tuple.getItem1()) { + return retrievePecNotificationListAndSendMail(tuple.getItem2()) + .onItem().transformToUni(unused -> retrieveFilteredAndPaginatedPecNotification(moduleDayOfTheEpoch, page + 1, size)) + .replaceWith(0); + }else{ + return retrievePecNotificationListAndSendMail(tuple.getItem2()) + .replaceWith(0); + } + }) + .onFailure().invoke(throwable -> log.error("Error during send scheduled mail", throwable)); + } - private Uni retrievePecNotificationListAndSendMail(PanacheQuery query) { - return Multi.createFrom().iterable(query.list()) + private Uni retrievePecNotificationListAndSendMail(List query) { + return Multi.createFrom().iterable(query) + .onItem().transform(entityBase -> (PecNotification) entityBase) .onItem().transformToUniAndMerge(this::constructAndSendMail) .onFailure().invoke(throwable -> log.error("Error during send scheduled mail", throwable)) .collect().asList() - .onItem().invoke(list -> log.info("Mail sent to institutions")) .replaceWith(Uni.createFrom().voidItem()); } @@ -82,6 +87,7 @@ private Uni constructAndSendMail(PecNotification pecNotification) { Product product = productService.getProduct(pecNotification.getProductId()); Map mailParameters = retrieveMailParameters(product.getTitle()); mailService.sendMailWithFile(List.of(pecNotification.getInstitutionMail()), templateMail, mailParameters, null, null); + log.info("Mail sent to institutions"); return Uni.createFrom().voidItem(); } diff --git a/apps/institution-send-mail-scheduler/src/main/resources/application.properties b/apps/institution-send-mail-scheduler/src/main/resources/application.properties index 86707b8..0c327d9 100644 --- a/apps/institution-send-mail-scheduler/src/main/resources/application.properties +++ b/apps/institution-send-mail-scheduler/src/main/resources/application.properties @@ -1,15 +1,16 @@ -quarkus.mongodb.database=pecNotification - +institution-send-mail.connection-string = ${MONGODB-CONNECTION-STRING:http://localhost:8080} +quarkus.mongodb.connection-string = ${MONGODB-CONNECTION-STRING} +quarkus.mongodb.database = selcMsCore ## MAIL institution-send-mail.sender-mail = ${MAIL_SENDER_ADDRESS:test@test.it} ## In PROD env must be false for sending real mail -institution-send-mail.destination-mail = ${MAIL_DESTINATION_TEST:true} +institution-send-mail.destination-mail = ${MAIL_DESTINATION_TEST:false} ## If MAIL_DESTINATION_TEST is true, app send mail to this address institution-send-mail.destination-mail-test-address = ${MAIL_DESTINATION_TEST_ADDRESS:test@test.it} institution-send-mail.notification-path= ${MAIL_TEMPLATE_NOTIFICATION_PATH:test.json} -institution-send-mail.notification-query-size = 1000 +institution-send-mail.notification-query-size = 1 institution-send-mail.notification-sending-frequency = 30 institution-send-mail.notification-start-date = 2024-01-01 diff --git a/apps/institution-send-mail-scheduler/src/test/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImplTest.java b/apps/institution-send-mail-scheduler/src/test/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImplTest.java index 5073503..821eaa3 100644 --- a/apps/institution-send-mail-scheduler/src/test/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImplTest.java +++ b/apps/institution-send-mail-scheduler/src/test/java/it/pagopa/selfcare/institution/service/InstitutionSendMailScheduledServiceImplTest.java @@ -1,12 +1,13 @@ package it.pagopa.selfcare.institution.service; -import io.quarkus.mongodb.panache.PanacheQuery; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoEntityBase; +import io.quarkus.mongodb.panache.reactive.ReactivePanacheQuery; +import io.quarkus.panache.mock.PanacheMock; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import it.pagopa.selfcare.institution.entity.PecNotification; -import it.pagopa.selfcare.institution.repository.PecNotificationsRepository; import it.pagopa.selfcare.product.entity.Product; import it.pagopa.selfcare.product.service.ProductService; import jakarta.inject.Inject; @@ -22,9 +23,6 @@ @QuarkusTest class InstitutionSendMailScheduledServiceImplTest { - @InjectMock - PecNotificationsRepository pecNotificationsRepository; - @InjectMock MailServiceImpl mailService; @@ -37,15 +35,20 @@ class InstitutionSendMailScheduledServiceImplTest { @Test void shouldSendMailToAllPecNotificationsForCurrentModuleDay() { List notifications = getPecNotifications(); - PanacheQuery query = Mockito.mock(PanacheQuery.class); - PanacheQuery query2 = Mockito.mock(PanacheQuery.class); - when(pecNotificationsRepository.find(any(), any(Object.class))).thenReturn(query); - when(query.hasNextPage()).thenReturn(true); - when(query2.hasNextPage()).thenReturn(false); - when(query.page(0, 500)).thenReturn(query); - when(query.page(1, 500)).thenReturn(query2); - when(query.list()).thenReturn(List.of(notifications.get(0), notifications.get(1))); - when(query2.list()).thenReturn(List.of(notifications.get(2), notifications.get(3))); + PanacheMock.mock(PecNotification.class); + ReactivePanacheQuery query = Mockito.mock(ReactivePanacheQuery.class); + when(PecNotification.find(any(), any(Object.class))) + .thenReturn(query); + when(query.page(0, 1000)).thenReturn(query); + + ReactivePanacheQuery query2 = Mockito.mock(ReactivePanacheQuery.class); + + + when(query.page(1, 1000)).thenReturn(query2); + when(query.hasNextPage()).thenReturn(Uni.createFrom().item(true)); + when(query2.hasNextPage()).thenReturn(Uni.createFrom().item(false)); + when(query.list()).thenReturn(Uni.createFrom().item(List.of(notifications.get(0), notifications.get(1)))); + when(query2.list()).thenReturn(Uni.createFrom().item(List.of(notifications.get(2), notifications.get(3)))); Product product = new Product(); product.setTitle("prod-io"); when(productService.getProduct("product-id")).thenReturn(product); @@ -54,7 +57,7 @@ void shouldSendMailToAllPecNotificationsForCurrentModuleDay() { UniAssertSubscriber subscriber = result.subscribe().withSubscriber(UniAssertSubscriber.create()); subscriber.assertItem(0).assertCompleted(); Mockito.verify(mailService, Mockito.times(4)) - .sendMailWithFile(Mockito.anyList(), Mockito.anyString(), Mockito.anyMap(), Mockito.anyString(), Mockito.isNull()); + .sendMailWithFile(Mockito.anyList(), Mockito.anyString(), Mockito.anyMap(), Mockito.isNull(), Mockito.isNull()); } private static List getPecNotifications() { @@ -84,33 +87,35 @@ void shouldLogErrorAndContinueOnMailSendFailure() { notification1.setProductId("product-id"); notification1.setInstitutionMail("test@test.it"); notification1.setModuleDayOfTheEpoch(1); - PanacheQuery query = Mockito.mock(PanacheQuery.class); - when(pecNotificationsRepository.find(any(), any(Object.class))).thenReturn(query); - when(query.page(Mockito.anyInt(), Mockito.anyInt())).thenReturn(query); - when(query.hasNextPage()).thenReturn(true); - when(query.list()).thenReturn(List.of(notification1)); + PanacheMock.mock(PecNotification.class); + ReactivePanacheQuery query = Mockito.mock(ReactivePanacheQuery.class); + when(PecNotification.find(any(), any(Object.class))) + .thenReturn(query); + when(query.page(0, 1000)).thenReturn(query); + when(query.hasNextPage()).thenReturn(Uni.createFrom().item(false)); + when(query.list()).thenReturn(Uni.createFrom().item(List.of(notification1))); Product product = new Product(); product.setTitle("prod-io"); when(productService.getProduct("product-id")).thenReturn(product); - Mockito.doThrow(new RuntimeException("Mail send failed")).when(mailService).sendMailWithFile(Mockito.anyList(), Mockito.anyString(), Mockito.anyMap(), Mockito.anyString(), Mockito.isNull()); + Mockito.doThrow(new RuntimeException("Mail send failed")).when(mailService).sendMailWithFile(Mockito.anyList(), Mockito.anyString(), Mockito.anyMap(), Mockito.isNull(), Mockito.isNull()); // Execute Uni result = service.retrieveInstitutionFromPecNotificationAndSendMail(); UniAssertSubscriber subscriber = result.subscribe().withSubscriber(UniAssertSubscriber.create()); // Verify - Mockito.verify(mailService).sendMailWithFile(Mockito.anyList(), Mockito.anyString(), Mockito.anyMap(), Mockito.anyString(), Mockito.isNull()); subscriber.assertFailedWith(RuntimeException.class, "Mail send failed"); } @Test void shouldHandleNoPecNotificationsForCurrentModuleDay() { - // Setup mocks - PanacheQuery query = Mockito.mock(PanacheQuery.class); - when(pecNotificationsRepository.find(any(), any(Object.class))).thenReturn(query); - when(query.page(Mockito.anyInt(), Mockito.anyInt())).thenReturn(query); - when(query.hasNextPage()).thenReturn(false); - when(query.list()).thenReturn(Collections.emptyList()); + PanacheMock.mock(PecNotification.class); + ReactivePanacheQuery query = Mockito.mock(ReactivePanacheQuery.class); + when(PecNotification.find(any(), any(Object.class))) + .thenReturn(query); + when(query.page(0, 1000)).thenReturn(query); + when(query.hasNextPage()).thenReturn(Uni.createFrom().item(false)); + when(query.list()).thenReturn(Uni.createFrom().item(Collections.emptyList())); // Execute Uni result = service.retrieveInstitutionFromPecNotificationAndSendMail(); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..330f52f --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.8' + +services: + + user-ms: + image: user-ms:latest + container_name: user-ms + restart: on-failure + build: + context: . + dockerfile: ./apps/institution-send-mail-scheduler/Dockerfile + ports: + - "8080:8080" + environment: + - JWT_TOKEN_PUBLIC_KEY=asdf