diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..8826150 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,38 @@ +name: "CI Develop" + +on: + push: + branches: + - '**' + - '!main' + - '!master' + paths: + - '**' + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: normalize branch name for tagging + run: | + NORM_TAG=$(echo "$GITHUB_REF_NAME" | tr -s "/" "-") + echo "NORM_TAG=$NORM_TAG" >> $GITHUB_ENV + - name: Log in to the Container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and push on GitHub packages + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile + push: true + tags: | + ghcr.io/${{ github.repository }}:${{ env.NORM_TAG }} \ No newline at end of file diff --git a/.github/workflows/build_prod-uat.yaml b/.github/workflows/build_prod-uat.yaml new file mode 100644 index 0000000..e35d325 --- /dev/null +++ b/.github/workflows/build_prod-uat.yaml @@ -0,0 +1,36 @@ +name: "CI RCs - PROD" + +on: + push: + tags: + - 'v*' + - '*-rc' + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: normalize branch name for tagging + run: | + NORM_TAG=$(echo "$GITHUB_REF_NAME" | tr -s "/" "-") + echo "NORM_TAG=$NORM_TAG" >> $GITHUB_ENV + - name: Log in to the Container registry + uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push on GitHub packages + uses: docker/build-push-action@v2 + with: + context: . + file: Dockerfile + push: true + tags: | + ghcr.io/${{ github.repository }}:${{ env.NORM_TAG }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..073c893 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +## BUILD ## +FROM maven:3.8.3-openjdk-17 AS build + +WORKDIR /app + +COPY . . + +RUN mvn -q clean package -Dmaven.test.skip=true + + +## RUN ## +FROM openjdk:17-alpine + +COPY --from=build /app/target/*.jar /app/app.jar + +ENTRYPOINT ["java", "-jar", "/app/app.jar"] \ No newline at end of file diff --git a/config/application.properties b/config/application.properties new file mode 100644 index 0000000..44cb202 --- /dev/null +++ b/config/application.properties @@ -0,0 +1,2 @@ +spring.profiles.active=dev +server.port=8082 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..d6658a4 --- /dev/null +++ b/pom.xml @@ -0,0 +1,228 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.5.2 + + + it.pagopa.interop + signalhub-history-cleanup + 0.0.1-SNAPSHOT + signalhub history cleanup + Signal history cleanup + + 17 + 0.00 + UTF-8 + 5.6.15.Final + 2.11.0 + 2.8.0 + 0.2.1 + 2.3.1 + 1.18.24 + 0.2.0 + + 1.5.1.Final + 2.13.4 + + + + + io.awspring.cloud + spring-cloud-aws-dependencies + 3.0.1 + pom + import + + + + + + com.amazonaws + aws-xray-recorder-sdk-apache-http + ${com.amazonaws.version} + + + + com.amazonaws + aws-xray-recorder-sdk-spring + ${com.amazonaws.version} + + + javax.xml.bind + jaxb-api + ${javax.xml.bind.version} + + + org.openapitools + jackson-databind-nullable + ${org.openapitools.version} + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-data-r2dbc + + + org.springframework.boot + spring-boot-starter-data-jdbc + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-reactor-netty + + + org.projectlombok + lombok + ${org.projectlombok.version} + + + org.projectlombok + lombok-mapstruct-binding + ${org.projectlombok.mapstruct.version} + + + org.apache.commons + commons-lang3 + + + org.mapstruct + mapstruct + ${org.mapstruct.version} + + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + io.projectreactor + reactor-test + test + + + com.auth0 + java-jwt + 4.3.0 + + + com.auth0 + jwks-rsa + 0.22.1 + + + org.springframework.boot + spring-boot-starter-security + + + com.h2database + h2 + runtime + + + + io.r2dbc + r2dbc-h2 + runtime + + + + io.r2dbc + r2dbc-postgresql + runtime + + + org.postgresql + postgresql + runtime + + + + org.testcontainers + localstack + 1.17.6 + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 3.1.2 + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + **/config/** + **/execution/** + **/utility/** + **/SignalHubHistoryCleanupApplication.class + + + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + 17 + + + org.projectlombok + lombok + 1.18.24 + + + org.mapstruct + mapstruct-processor + 1.5.1.Final + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + + + + + diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/SignalHubHistoryCleanupApplication.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/SignalHubHistoryCleanupApplication.java new file mode 100644 index 0000000..d8e0ff9 --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/SignalHubHistoryCleanupApplication.java @@ -0,0 +1,16 @@ +package it.pagopa.interop.signalhub.history.cleanup; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; + +@SpringBootApplication +@EnableR2dbcAuditing +public class SignalHubHistoryCleanupApplication { + + public static void main(String[] args) { + SpringApplication.run(SignalHubHistoryCleanupApplication.class, args); + } + +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfig.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfig.java new file mode 100644 index 0000000..8e7809c --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfig.java @@ -0,0 +1,15 @@ +package it.pagopa.interop.signalhub.history.cleanup.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties(prefix = "pdnd.history-cleanup") +@Getter +@Setter +public class AppConfig { + + private String delayHours; +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceController.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceController.java new file mode 100644 index 0000000..53136be --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceController.java @@ -0,0 +1,31 @@ +package it.pagopa.interop.signalhub.history.cleanup.controller; + +import it.pagopa.interop.signalhub.history.cleanup.exception.PDNDBatchAlreadyExistException; +import it.pagopa.interop.signalhub.history.cleanup.service.SignalService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Slf4j +@Component +@AllArgsConstructor +public class SignalServiceController { + + private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + private final SignalService signalService; + + public void cleanSignal(){ + log.info("CleanSignal Started: {}", dateTimeFormatter.format(LocalDateTime.now())); + try { + signalService.cleanSignal(); + } catch (PDNDBatchAlreadyExistException ex) { + log.error("An error occurred ", ex); + } + log.info("CleanSignal Ended: {}", dateTimeFormatter.format(LocalDateTime.now())); + } + + +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/entities/Signal.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/entities/Signal.java new file mode 100644 index 0000000..c210ce2 --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/entities/Signal.java @@ -0,0 +1,33 @@ +package it.pagopa.interop.signalhub.history.cleanup.entities; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Id; +import org.springframework.data.relational.core.mapping.Column; +import org.springframework.data.relational.core.mapping.Table; + +import java.io.Serializable; +import java.time.Instant; + +@Getter +@Setter +@Table("SIGNAL") +public class Signal implements Serializable { + @Id + @Column("id") + private Long id; + @Column("signal_id") + private Long signalId; + @Column("object_id") + private String objectId; + @Column("eservice_id") + private String eserviceId; + @Column("object_type") + private String objectType; + @Column("signal_type") + private String signalType; + @CreatedDate + @Column("tmst_insert") + private Instant tmstInsert; +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/exception/PDNDBatchAlreadyExistException.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/exception/PDNDBatchAlreadyExistException.java new file mode 100644 index 0000000..b6fa567 --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/exception/PDNDBatchAlreadyExistException.java @@ -0,0 +1,8 @@ +package it.pagopa.interop.signalhub.history.cleanup.exception; + +public class PDNDBatchAlreadyExistException extends RuntimeException { + + public PDNDBatchAlreadyExistException() { + super("Batch already in running"); + } +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/execution/BatchTaskExecutor.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/execution/BatchTaskExecutor.java new file mode 100644 index 0000000..1f7d6af --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/execution/BatchTaskExecutor.java @@ -0,0 +1,19 @@ +package it.pagopa.interop.signalhub.history.cleanup.execution; + +import it.pagopa.interop.signalhub.history.cleanup.controller.SignalServiceController; +import lombok.AllArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Component; + +@Profile("!test") +@Component +@AllArgsConstructor +public class BatchTaskExecutor implements CommandLineRunner { + private final SignalServiceController signalServiceController; + + @Override + public void run(String... args) { + signalServiceController.cleanSignal(); + } +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/repository/SignalRepository.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/repository/SignalRepository.java new file mode 100644 index 0000000..9ed5976 --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/repository/SignalRepository.java @@ -0,0 +1,18 @@ +package it.pagopa.interop.signalhub.history.cleanup.repository; + +import it.pagopa.interop.signalhub.history.cleanup.entities.Signal; +import org.springframework.data.r2dbc.repository.Query; +import org.springframework.data.repository.reactive.ReactiveCrudRepository; +import org.springframework.stereotype.Repository; +import reactor.core.publisher.Mono; + +import java.time.Instant; +import java.time.LocalDate; + +@Repository +public interface SignalRepository extends ReactiveCrudRepository { + + @Query("delete from SIGNAL where tmst_Insert < :date") + Mono deleteByDate(Instant date ); + +} \ No newline at end of file diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/SignalService.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/SignalService.java new file mode 100644 index 0000000..cea6217 --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/SignalService.java @@ -0,0 +1,7 @@ +package it.pagopa.interop.signalhub.history.cleanup.service; + +public interface SignalService { + + void cleanSignal(); + +} diff --git a/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImpl.java b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImpl.java new file mode 100644 index 0000000..fd5afce --- /dev/null +++ b/src/main/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImpl.java @@ -0,0 +1,30 @@ +package it.pagopa.interop.signalhub.history.cleanup.service.impl; + +import it.pagopa.interop.signalhub.history.cleanup.config.AppConfig; +import it.pagopa.interop.signalhub.history.cleanup.repository.SignalRepository; +import it.pagopa.interop.signalhub.history.cleanup.service.SignalService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +@Slf4j +@AllArgsConstructor +@Service +public class SignalServiceImpl implements SignalService { + private final SignalRepository signalRepository; + private final AppConfig appConfig; + + public void cleanSignal() { + Instant pastDate = Instant.now().minus(Long.parseLong(appConfig.getDelayHours()), ChronoUnit.HOURS); + signalRepository.deleteByDate(pastDate) + .doOnSuccess(x -> log.info("clean complete with success")) + .doOnError(ex -> log.error("Error on signal cleanup", ex)) + .subscribe(); + } + +} + + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..9a8d864 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,19 @@ +logging.level.root=INFO +logging.pattern.level=%2p [%X{traceId:-}] + +database.name=${DB_NAME:signal-hub} +database.host=${DATABASE_WRITER_HOST:localhost} +database.port=${DB_PORT:5432} +database.username=${DB_USER:postgres} +database.password=${DB_PASSWORD:postgres} + +#R2DBC - Postgres +spring.r2dbc.url=r2dbc:postgresql://${database.host}:${database.port}/${database.name} +spring.r2dbc.username=${database.username} +spring.r2dbc.password=${database.password} +spring.r2dbc.pool.max-size=20 +spring.r2dbc.pool.initial-size=5 +spring.r2dbc.pool.enabled=true +spring.data.r2dbc.repositories.enabled=true + +pdnd.history-cleanup.delay-hours=30 \ No newline at end of file diff --git a/src/test/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfigTest.java b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfigTest.java new file mode 100644 index 0000000..2839879 --- /dev/null +++ b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/config/AppConfigTest.java @@ -0,0 +1,21 @@ +package it.pagopa.interop.signalhub.history.cleanup.config; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@ExtendWith(MockitoExtension.class) +class AppConfigTest { + + @InjectMocks + AppConfig appConfig; + + @Test + void testDelayDays() { + appConfig.setDelayHours("10"); + assertEquals("10", appConfig.getDelayHours()); + } +} diff --git a/src/test/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceControllerTest.java b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceControllerTest.java new file mode 100644 index 0000000..8c29117 --- /dev/null +++ b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/controller/SignalServiceControllerTest.java @@ -0,0 +1,49 @@ +package it.pagopa.interop.signalhub.history.cleanup.controller; + + +import it.pagopa.interop.signalhub.history.cleanup.exception.PDNDBatchAlreadyExistException; +import it.pagopa.interop.signalhub.history.cleanup.service.SignalService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class SignalServiceControllerTest { + + @Mock + private SignalService signalService; + + @InjectMocks + private SignalServiceController signalServiceController; + + + @Test + void whenCleanSignalOk(){ + + Mockito.doNothing().when(signalService).cleanSignal(); + + this.signalServiceController.cleanSignal(); + + Mockito.verify(signalService, Mockito.timeout(1000).times(1)).cleanSignal(); + + } + + @Test + void whenCleanSignalThrowPDNBatchAlreadyExistException(){ + + Mockito.doThrow(new PDNDBatchAlreadyExistException()) + .when(signalService).cleanSignal(); + + this.signalServiceController.cleanSignal(); + + Mockito.verify(signalService, Mockito.timeout(1000).times(1)).cleanSignal(); + + } + + + + +} diff --git a/src/test/java/it/pagopa/interop/signalhub/history/cleanup/entity/SignalTest.java b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/entity/SignalTest.java new file mode 100644 index 0000000..5677df0 --- /dev/null +++ b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/entity/SignalTest.java @@ -0,0 +1,72 @@ +package it.pagopa.interop.signalhub.history.cleanup.entity; + +import it.pagopa.interop.signalhub.history.cleanup.entities.Signal; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +@ExtendWith(MockitoExtension.class) +class SignalTest { + + @InjectMocks + private Signal signal; + + @BeforeEach + void setUp() { + signal = new Signal(); + } + + @Test + void testId() { + Long id = 1L; + signal.setId(id); + assertEquals(id, signal.getId()); + } + @Test + void testSignalId() { + Long signalId = 123L; + signal.setSignalId(signalId); + assertEquals(signalId, signal.getSignalId()); + } + @Test + void testObjectId() { + String objectId = "testObject"; + signal.setObjectId(objectId); + assertEquals(objectId, signal.getObjectId()); + } + + @Test + void testEserviceId() { + String eserviceId = "testEservice"; + signal.setEserviceId(eserviceId); + assertEquals(eserviceId, signal.getEserviceId()); + } + @Test + void testObjectType() { + String objectType = "testType"; + signal.setObjectType(objectType); + assertEquals(objectType, signal.getObjectType()); + } + + @Test + void testSignalType() { + String signalType = "testSignal"; + signal.setSignalType(signalType); + assertEquals(signalType, signal.getSignalType()); + } + + @Test + void testTmstInsert() { + Instant tmstInsert = Instant.now(); + signal.setTmstInsert(tmstInsert); + assertEquals(tmstInsert, signal.getTmstInsert()); + } + +} diff --git a/src/test/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImplTest.java b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImplTest.java new file mode 100644 index 0000000..c030ff5 --- /dev/null +++ b/src/test/java/it/pagopa/interop/signalhub/history/cleanup/service/impl/SignalServiceImplTest.java @@ -0,0 +1,31 @@ +package it.pagopa.interop.signalhub.history.cleanup.service.impl; + +import it.pagopa.interop.signalhub.history.cleanup.config.AppConfig; +import it.pagopa.interop.signalhub.history.cleanup.repository.SignalRepository; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +class SignalServiceImplTest { + @InjectMocks + private SignalServiceImpl signalService; + + @Mock + private SignalRepository signalRepository; + + @Mock + private AppConfig appConfig; + + @Test + void testDeleteSignal() { + Mockito.when(appConfig.getDelayHours()).thenReturn("30"); + Mockito.when(signalRepository.deleteByDate(Mockito.any())).thenReturn(Mono.empty()); + signalService.cleanSignal(); + Mockito.verify(signalRepository, Mockito.times(1)).deleteByDate(Mockito.any()); + } +}