From b424fd935987bebc66932b76f4ad5d87e85a1470 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominique=20Eifl=C3=A4nder?= Date: Mon, 11 Dec 2023 17:24:51 +0100 Subject: [PATCH] RED-5223: Enabled tracing, upgrade spring, use logstash-logback-encoder for json logs --- publish-custom-docker-img.sh | 32 ++++++------------- .../build.gradle.kts | 32 +++++++++++-------- .../server/controller/ControllerAdvice.java | 2 +- .../server/exception/NotFoundException.java | 15 +++++++++ .../v1/server/service/PlaceholderService.java | 2 +- .../src/main/resources/application.yml | 15 +++++++++ .../src/main/resources/log4j2.xml | 23 ------------- .../src/main/resources/logback-spring.xml | 17 ++++++++++ .../report/v1/server/PlaceholderTest.java | 4 --- .../RedactionReportIntegrationTest.java | 22 ++++++------- .../RedactionReportV2IntegrationTest.java | 29 +++++++++-------- 11 files changed, 102 insertions(+), 91 deletions(-) create mode 100644 redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/exception/NotFoundException.java delete mode 100644 redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/log4j2.xml create mode 100644 redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/logback-spring.xml diff --git a/publish-custom-docker-img.sh b/publish-custom-docker-img.sh index 4b70fde..8014ba0 100755 --- a/publish-custom-docker-img.sh +++ b/publish-custom-docker-img.sh @@ -1,27 +1,15 @@ #!/bin/bash - -# This script compiles the project, builds a docker image with the tag - and pushes it to our nexus. - -# Set the Nexus repository URL -NEXUS_REPO="nexus.knecon.com:5001" -# Set the image name -IMAGE_NAME="red/redaction-report-service-server-v1" -# path to image repo -IMAGE_REPO="redaction-report-service-image-v1" - -echo "Running build" -mvn clean install -Pquickbuild +dir=${PWD##*/} +gradle assemble # Get the current Git branch -GIT_BRANCH=$(git symbolic-ref --short HEAD) -# Get the first 5 characters of the commit hash -GIT_COMMIT_HASH=$(git rev-parse --short=5 HEAD) -# Create the image tag by combining branch and commit hash -IMAGE_TAG="${GIT_BRANCH}-${GIT_COMMIT_HASH}" -IMAGE_NAME="$NEXUS_REPO/$IMAGE_NAME:$IMAGE_TAG" +branch=$(git rev-parse --abbrev-ref HEAD) -echo "Building docker image: {$IMAGE_NAME}" -# Build the Docker image with the specified name and tag and push to nexus -mvn -f $IMAGE_REPO docker:build docker:push -Ddocker.image.version=$IMAGE_TAG +# Get the short commit hash (first 5 characters) +commit_hash=$(git rev-parse --short=5 HEAD) -echo "Docker image '$IMAGE_NAME' has been built and pushed to Nexus." +# Combine branch and commit hash +buildName="${USER}-${branch}-${commit_hash}" + +gradle bootBuildImage --cleanCache --publishImage -PbuildbootDockerHostNetwork=true -Pversion=$buildName +echo "nexus.knecon.com:5001/red/${dir}-server-v1:$buildName" \ No newline at end of file diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/build.gradle.kts b/redaction-report-service-v1/redaction-report-service-server-v1/build.gradle.kts index 5485805..73a202d 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/build.gradle.kts +++ b/redaction-report-service-v1/redaction-report-service-server-v1/build.gradle.kts @@ -3,7 +3,7 @@ import org.springframework.boot.gradle.tasks.bundling.BootBuildImage plugins { application id("com.iqser.red.service.java-conventions") - id("org.springframework.boot") version "3.0.4" + id("org.springframework.boot") version "3.1.5" id("io.spring.dependency-management") version "1.1.0" id("org.sonarqube") version "4.2.1.3168" id("io.freefair.lombok") version "8.1.0" @@ -11,16 +11,19 @@ plugins { description = "redaction-service-server-v1" -val tenantCommonVersion = "0.10.0" +val tenantCommonVersion = "0.14.0" val springCommonsVersion = "2.1.0" -val storageCommonsVersion = "2.27.0" +val storageCommonsVersion = "2.45.0" val poiVersion = "5.2.3" val metricCommonsVersion = "2.1.0" val persistenceServiceVersion = "2.239.0" +val springBootStarterVersion = "3.1.5" configurations { all { - exclude(group = "org.springframework.boot", module = "spring-boot-starter-logging") + exclude(group = "commons-logging", module = "commons-logging") + exclude(group = "org.springframework.boot", module = "spring-boot-starter-log4j2") + exclude(group = "com.iqser.red.commons", module = "logging-commons") } } @@ -28,26 +31,29 @@ description = "redaction-report-service-server-v1" dependencies { implementation(project(":redaction-report-service-api-v1")) - implementation("com.knecon.fforesight:tenant-commons:${tenantCommonVersion}") - implementation("com.iqser.red:platform-commons-dependency:2.7.0") + implementation("com.iqser.red.service:persistence-service-internal-api-v1:${persistenceServiceVersion}") + implementation("com.knecon.fforesight:tenant-commons:${tenantCommonVersion}") implementation("com.iqser.red.commons:storage-commons:${storageCommonsVersion}") implementation("com.iqser.red.commons:spring-commons:${springCommonsVersion}") implementation("com.iqser.red.commons:metric-commons:${metricCommonsVersion}") - implementation("com.iqser.red.service:persistence-service-internal-api-v1:${persistenceServiceVersion}") + implementation("com.knecon.fforesight:tracing:0.5.0") implementation("org.apache.poi:poi:${poiVersion}") implementation("org.apache.poi:poi-ooxml:${poiVersion}") implementation("org.apache.poi:poi-scratchpad:${poiVersion}") - implementation("org.springframework.boot:spring-boot-starter-amqp:3.0.4") - implementation("org.springframework.boot:spring-boot-starter-test:3.0.4") { - exclude("org.springframework.boot:spring-boot-starter-tomcat:3.0.4") - } - implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.0.1") - + implementation("org.springframework.boot:spring-boot-starter-amqp:${springBootStarterVersion}") + implementation("org.springframework.cloud:spring-cloud-starter-openfeign:4.0.4") implementation("org.apache.commons:commons-lang3:3.12.0") + implementation("net.logstash.logback:logstash-logback-encoder:7.4") + implementation("org.springframework.boot:spring-boot-starter-logging") + implementation("ch.qos.logback:logback-classic") + + testImplementation("org.springframework.boot:spring-boot-starter-test:${springBootStarterVersion}") { + exclude("org.springframework.boot:spring-boot-starter-tomcat:3.0.4") + } testImplementation("com.iqser.red.commons:test-commons:2.1.0") testImplementation("org.springframework.amqp:spring-rabbit-test:3.0.6") } diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/controller/ControllerAdvice.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/controller/ControllerAdvice.java index 6b18489..22f220a 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/controller/ControllerAdvice.java +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/controller/ControllerAdvice.java @@ -8,8 +8,8 @@ import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; -import com.amazonaws.services.kms.model.NotFoundException; import com.iqser.red.commons.spring.ErrorMessage; +import com.iqser.red.service.redaction.report.v1.server.exception.NotFoundException; import lombok.extern.slf4j.Slf4j; diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/exception/NotFoundException.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/exception/NotFoundException.java new file mode 100644 index 0000000..bfe95ff --- /dev/null +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/exception/NotFoundException.java @@ -0,0 +1,15 @@ +package com.iqser.red.service.redaction.report.v1.server.exception; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + + super(message); + } + + public NotFoundException(String message, Throwable t) { + + super(message, t); + } + +} diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/service/PlaceholderService.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/service/PlaceholderService.java index fb3c79e..d33d6e3 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/service/PlaceholderService.java +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/java/com/iqser/red/service/redaction/report/v1/server/service/PlaceholderService.java @@ -21,11 +21,11 @@ import org.apache.poi.xwpf.usermodel.XWPFTableCell; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.springframework.stereotype.Service; -import com.amazonaws.services.kms.model.NotFoundException; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate; import com.iqser.red.service.redaction.report.v1.server.client.DossierAttributesConfigClient; import com.iqser.red.service.redaction.report.v1.server.client.FileAttributesConfigClient; import com.iqser.red.service.redaction.report.v1.server.client.ReportTemplateClient; +import com.iqser.red.service.redaction.report.v1.server.exception.NotFoundException; import com.iqser.red.service.redaction.report.v1.server.storage.ReportStorageService; import com.iqser.red.storage.commons.exception.StorageObjectDoesNotExist; diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/application.yml b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/application.yml index 2dbf3ac..0b6fe3b 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/application.yml +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/application.yml @@ -8,7 +8,15 @@ fforesight.tenants.remote: true server: port: 8080 +logging.pattern.level: "%5p [${spring.application.name},%X{traceId:-},%X{spanId:-}]" + +logging.type: ${LOGGING_TYPE:CONSOLE} +kubernetes.namespace: ${NAMESPACE:default} +project.version: 1.0-SNAPSHOT + spring: + application: + name: redaction-report-service main: allow-circular-references: true # FIXME profiles: @@ -35,6 +43,13 @@ management: health.enabled: true endpoints.web.exposure.include: prometheus, health metrics.export.prometheus.enabled: ${monitoring.enabled:false} + tracing: + enabled: ${TRACING_ENABLED:false} + sampling: + probability: ${TRACING_PROBABILITY:1.0} + otlp: + tracing: + endpoint: ${OTLP_ENDPOINT:http://otel-collector-opentelemetry-collector.otel-collector:4318/v1/traces} storage: backend: 's3' diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/log4j2.xml b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/log4j2.xml deleted file mode 100644 index c36f74e..0000000 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/log4j2.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - ${env:LOGGING_TYPE} - - - - - - - - - - - - - - - - - - diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/logback-spring.xml b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..33b2cef --- /dev/null +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/main/resources/logback-spring.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/PlaceholderTest.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/PlaceholderTest.java index 1aab64c..5c06be6 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/PlaceholderTest.java +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/PlaceholderTest.java @@ -21,7 +21,6 @@ import org.springframework.context.annotation.FilterType; import org.springframework.core.io.ClassPathResource; import org.springframework.test.context.junit.jupiter.SpringExtension; -import com.amazonaws.services.s3.AmazonS3; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate; import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierAttributeType; @@ -53,9 +52,6 @@ public class PlaceholderTest { @MockBean private RabbitTemplate rabbitTemplate; - @MockBean - private AmazonS3 s3Client; - @MockBean private ReportTemplateClient reportTemplateClient; diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportIntegrationTest.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportIntegrationTest.java index 09ee246..36ca190 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportIntegrationTest.java +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportIntegrationTest.java @@ -45,7 +45,6 @@ import org.springframework.context.annotation.FilterType; import org.springframework.core.io.ClassPathResource; import org.springframework.test.context.junit.jupiter.SpringExtension; -import com.amazonaws.services.s3.AmazonS3; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.componentlog.ComponentLog; @@ -105,9 +104,6 @@ public class RedactionReportIntegrationTest { @MockBean private FileAttributesConfigClient fileAttributesConfigClient; - @MockBean - private AmazonS3 s3Client; - @Autowired private WordReportGenerationService wordReportGenerationService; @@ -332,9 +328,7 @@ public class RedactionReportIntegrationTest { FileModel fileModelSecondFile = FileModel.builder().filename("secondFile").build(); EntityLog entityLogSecondFile = objectMapper.readValue(new ClassPathResource("files/entityLogWithManualRedactions.json").getInputStream(), EntityLog.class); - List reportEntriesSecondFile = entityLogConverterService.convertAndSort(entityLogSecondFile, - legalBasisMapping, - new HashMap<>()); + List reportEntriesSecondFile = entityLogConverterService.convertAndSort(entityLogSecondFile, legalBasisMapping, new HashMap<>()); ClassPathResource templateResource = new ClassPathResource("templates/Seeds-NewJustificationForm.docx"); XWPFDocument doc = new XWPFDocument(templateResource.getInputStream()); @@ -443,9 +437,7 @@ public class RedactionReportIntegrationTest { excelTemplateReportGenerationService.generateExcelReport(reportEntries, placeholders, "test", writeWorkbook, "dossierName", fileModel, excelModel, false); EntityLog entityLogSecondFile = objectMapper.readValue(new ClassPathResource("files/entityLog.json").getInputStream(), EntityLog.class); - List reportEntriesSecondFile = entityLogConverterService.convertAndSort(entityLogSecondFile, - legalBasisMapping, - mapOfEntityDisplayName); + List reportEntriesSecondFile = entityLogConverterService.convertAndSort(entityLogSecondFile, legalBasisMapping, mapOfEntityDisplayName); FileModel fileModelSecondFile = FileModel.builder().filename("secondFile").build(); excelTemplateReportGenerationService.generateExcelReport(reportEntriesSecondFile, placeholders, @@ -495,21 +487,25 @@ public class RedactionReportIntegrationTest { } } + @Test @SneakyThrows public void testScmReport() { Dossier dossier = prepareDossier(); - FileModel fileModel = FileModel.builder().filename("filename") + FileModel fileModel = FileModel.builder() + .filename("filename") .dossierId(dossier.getDossierId()) .dossierTemplateId(dossier.getDossierTemplateId()) .fileAttributes(Map.of("TestAttribute", "Lorem Ipsum", "1b5ebe26-34f2-4c3b-983d-c0f0a010302c", "402")) .build(); EntityLog entityLog = objectMapper.readValue(new ClassPathResource("files/scm/entityLog.json").getInputStream(), EntityLog.class); - List legalBasisMapping = objectMapper.readValue(new ClassPathResource("files/scm/legalBasisMapping.json").getInputStream(), new TypeReference<>() {}); + List legalBasisMapping = objectMapper.readValue(new ClassPathResource("files/scm/legalBasisMapping.json").getInputStream(), new TypeReference<>() { + }); ComponentLog componentLog = objectMapper.readValue(new ClassPathResource("files/scm/componentLog.json").getInputStream(), ComponentLog.class); - List fileAttributeConfigs = objectMapper.readValue(new ClassPathResource("files/scm/fileAttributes.json").getInputStream(), new TypeReference<>() {}); + List fileAttributeConfigs = objectMapper.readValue(new ClassPathResource("files/scm/fileAttributes.json").getInputStream(), new TypeReference<>() { + }); Map mapOfEntityDisplayName = createEntityDisplayNames(entityLog); List reportEntries = entityLogConverterService.convertAndSort(entityLog, legalBasisMapping, mapOfEntityDisplayName); diff --git a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportV2IntegrationTest.java b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportV2IntegrationTest.java index 9304d04..a628020 100644 --- a/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportV2IntegrationTest.java +++ b/redaction-report-service-v1/redaction-report-service-server-v1/src/test/java/com/iqser/red/service/redaction/report/v1/server/RedactionReportV2IntegrationTest.java @@ -2,7 +2,6 @@ package com.iqser.red.service.redaction.report.v1.server; import static com.iqser.red.service.redaction.report.v1.server.utils.OsUtils.getTemporaryDirectory; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @@ -21,7 +20,6 @@ import java.util.stream.IntStream; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -38,7 +36,6 @@ import org.springframework.context.annotation.Primary; import org.springframework.core.io.ClassPathResource; import org.springframework.test.context.junit.jupiter.SpringExtension; -import com.amazonaws.services.s3.AmazonS3; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.iqser.red.service.persistence.service.v1.api.shared.model.analysislog.entitylog.EntityLog; @@ -99,9 +96,6 @@ public class RedactionReportV2IntegrationTest { @MockBean private FileAttributesConfigClient fileAttributesConfigClient; - @MockBean - private AmazonS3 s3Client; - @MockBean private MessagingConfiguration messagingConfiguration; @@ -180,7 +174,7 @@ public class RedactionReportV2IntegrationTest { var templateIds = new HashSet(); for (var template : templates) { - var templateId = Base64.getEncoder().encodeToString(template.getBytes(StandardCharsets.UTF_8)); + var templateId = Base64.getEncoder().encodeToString(template.getBytes(StandardCharsets.UTF_8)); templateIds.add(templateId); log.info("Assigned template Id {} to file {}", templateId, template); ReportTemplate testTemplate = new ReportTemplate(); @@ -234,11 +228,12 @@ public class RedactionReportV2IntegrationTest { } } + @SneakyThrows private void prepareStorage(int id) { - var entityLog = objectMapper.readValue(new ClassPathResource("files/entityLog.json").getInputStream(), EntityLog.class); - fileSystemBackedStorageService.storeJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId("dossierId", "fileId" + id, FileType.ENTITY_LOG), entityLog); + var entityLog = objectMapper.readValue(new ClassPathResource("files/entityLog.json").getInputStream(), EntityLog.class); + fileSystemBackedStorageService.storeJSONObject(TenantContext.getTenantId(), StorageIdUtils.getStorageId("dossierId", "fileId" + id, FileType.ENTITY_LOG), entityLog); } @@ -288,14 +283,16 @@ public class RedactionReportV2IntegrationTest { MetricValidationUtils.validateMetric(prometheusMeterRegistry, "redactmanager_getReportEntries", 1, null); } + @Test @SneakyThrows public void testIncludeUnprocessed() { var reportRequestMessage = prepareFlow(2, "templates/Excel QC (incl. Skipped Redactions).xlsx"); reportRequestMessage.setIncludeUnprocessed(true); - when(entityLogClient.getEntityLog(anyString(), anyString(), any(), eq(true))).thenReturn(new EntityLog( - 1, 1, List.of(EntityLogEntry.builder() + when(entityLogClient.getEntityLog(anyString(), anyString(), any(), eq(true))).thenReturn(new EntityLog(1, + 1, + List.of(EntityLogEntry.builder() .state(EntryState.APPLIED) .positions(List.of(new Position(1, 1, 1, 1, 1))) .id("123") @@ -306,8 +303,12 @@ public class RedactionReportV2IntegrationTest { .endOffset(10) .color(new float[]{0, 0, 0}) .section("section") - .build()), Collections.emptyList(), 1, 1, 1, 1) - ); + .build()), + Collections.emptyList(), + 1, + 1, + 1, + 1)); processRequest(reportRequestMessage, ".xlsx"); MetricValidationUtils.validateMetric(prometheusMeterRegistry, "redactmanager_generateReports", 1, null); @@ -327,7 +328,7 @@ public class RedactionReportV2IntegrationTest { @Configuration @EnableAutoConfiguration(exclude = {/*StorageAutoConfiguration.class, */RabbitAutoConfiguration.class}) - @ComponentScan(excludeFilters={@ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE, value=StorageAutoConfiguration.class)}) + @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = StorageAutoConfiguration.class)}) @ComponentScan("com.iqser.red.service.persistence") public static class TestConfiguration {