From 439a946b0da90c0c883f1899baa1986b849dc371 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Wed, 16 Apr 2025 15:49:54 +0200 Subject: [PATCH 01/14] OZG-7811 OZG-8098 Rename scopes --- .../aggregation/transformation/AggregationMapping.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java index f32b019..71d00b3 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/transformation/AggregationMapping.java @@ -28,7 +28,6 @@ import java.util.List; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; - import lombok.Builder; import lombok.Getter; import lombok.Singular; @@ -57,7 +56,7 @@ public class AggregationMapping { } public enum Scope { - LAND, MANDANT; + EXTERN, INTERN; } @Getter -- GitLab From f5c4a429851caa4f1fe4077ab8430507d103407f Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Thu, 17 Apr 2025 18:45:57 +0200 Subject: [PATCH 02/14] OZG-7811 OZG-8099 Create AggregationDataRemoteService and call stub --- aggregation-manager-job/pom.xml | 4 + .../AggregationManagerProperties.java | 43 ++++++ .../extern/AggregationDataRemoteService.java | 94 +++++++++++++ .../extern/GrpcAggregationDataMapper.java | 38 ++++++ .../AggregationDataRemoteServiceTest.java | 128 ++++++++++++++++++ .../extern/AggregationTestFactory.java | 47 +++++++ .../extern/GrpcAggregationDataMapperTest.java | 77 +++++++++++ .../GrpcAggregationDataTestFactory.java | 43 ++++++ 8 files changed, 474 insertions(+) create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapperTest.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataTestFactory.java diff --git a/aggregation-manager-job/pom.xml b/aggregation-manager-job/pom.xml index f36bfda..3ec206f 100644 --- a/aggregation-manager-job/pom.xml +++ b/aggregation-manager-job/pom.xml @@ -50,6 +50,10 @@ <artifactId>ozg-cloud-spring-boot-starter</artifactId> <groupId>de.ozgcloud.api-lib</groupId> </dependency> + <dependency> + <groupId>de.ozgcloud.aggregation</groupId> + <artifactId>aggregation-manager-interface</artifactId> + </dependency> <!-- Spring --> <dependency> <groupId>org.springframework.boot</groupId> diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java new file mode 100644 index 0000000..4ace2f5 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.annotation.Validated; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.log4j.Log4j2; + +@ConfigurationProperties(prefix = "ozgcloud") +@Configuration +@Validated +@Getter +@Setter +@Log4j2 +public class AggregationManagerProperties { + + private String mandant; +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java new file mode 100644 index 0000000..25d4979 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import java.util.Iterator; +import java.util.stream.Stream; + +import de.ozgcloud.aggregation.AggregationManagerProperties; +import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; +import de.ozgcloud.aggregation.data.GrpcAggregationData; +import de.ozgcloud.aggregation.data.GrpcSendAggregationDataRequest; +import de.ozgcloud.aggregation.data.GrpcSendAggregationDataResponse; +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import io.grpc.stub.ClientCallStreamObserver; +import io.grpc.stub.ClientResponseObserver; +import lombok.Builder; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +class AggregationDataRemoteService { + + private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; + private final AggregationManagerProperties properties; + private final GrpcAggregationDataMapper grpcAggregationDataMapper; + + public void sendAggregationData(Aggregation aggregation) { + var requestObserver = serviceStub.sendAggregationData(new SendAggregationDataResponseObserver( + new RequestData(properties.getMandant(), aggregation.aggregationName, + toGrpcAggregationDataStream(aggregation.documentEntries).iterator()))); + } + + Stream<GrpcAggregationData> toGrpcAggregationDataStream(Stream<DocumentEntry> documentEntries) { + return documentEntries.map(grpcAggregationDataMapper::toGrpcAggregationData); + } + + @RequiredArgsConstructor + static class SendAggregationDataResponseObserver + implements ClientResponseObserver<GrpcSendAggregationDataRequest, GrpcSendAggregationDataResponse> { + + private final RequestData requestData; + + @Override + public void beforeStart(ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver) { + requestObserver.setOnReadyHandler(() -> { + while (requestObserver.isReady() && requestData.aggregationDataIterator.hasNext()) { + // call onNext()? + } + }); + } + + @Override + public void onNext(GrpcSendAggregationDataResponse value) { + + } + + @Override + public void onError(Throwable t) { + + } + + @Override + public void onCompleted() { + + } + } + + @Builder + public record Aggregation(String aggregationName, Stream<DocumentEntry> documentEntries) { + } + + record RequestData(String mandant, String aggregationName, Iterator<GrpcAggregationData> aggregationDataIterator) { + } +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java new file mode 100644 index 0000000..d111d82 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import de.ozgcloud.aggregation.data.GrpcAggregationData; +import de.ozgcloud.aggregation.warehouse.DocumentEntry; + +@Mapper +interface GrpcAggregationDataMapper { + + @Mapping(target = "eingangDatum", source = "eingangsdatum") + @Mapping(target = "vorgangName", source = "vorgangsname") + GrpcAggregationData toGrpcAggregationData(DocumentEntry documentEntry); +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java new file mode 100644 index 0000000..46e411f --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.apache.commons.collections.IteratorUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.aggregation.AggregationManagerProperties; +import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; +import de.ozgcloud.aggregation.extern.AggregationDataRemoteService.SendAggregationDataResponseObserver; +import de.ozgcloud.common.test.ReflectionTestUtils; + +class AggregationDataRemoteServiceTest { + + @Mock + private AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; + @Mock + private AggregationManagerProperties properties; + @Mock + private GrpcAggregationDataMapper grpcAggregationDataMapper; + @InjectMocks + @Spy + private AggregationDataRemoteService service; + + @Nested + class TestSendAggregationData { + + private static final String MANDANT = LoremIpsum.getInstance().getWords(1); + + @Captor + private ArgumentCaptor<SendAggregationDataResponseObserver> observerCaptor; + + @BeforeEach + void init() { + when(properties.getMandant()).thenReturn(MANDANT); + // only called, after stream was consumed (is lazy) + lenient().when(grpcAggregationDataMapper.toGrpcAggregationData(AggregationTestFactory.DOCUMENT_ENTRY)).thenReturn( + GrpcAggregationDataTestFactory.create()); + } + + @Test + void shouldGetMandant() { + sendAggregationData(); + + verify(properties).getMandant(); + } + + @Test + void shouldCallServiceStub() { + sendAggregationData(); + + verify(serviceStub).sendAggregationData(any(SendAggregationDataResponseObserver.class)); + } + + @Test + void shouldResponseObserverHaveMandant() { + sendAggregationData(); + + assertThat(getRequestDataFromCapturedResponseObserver().mandant()).isEqualTo(MANDANT); + } + + @Test + void shouldResponseObserverHaveAggregationName() { + sendAggregationData(); + + assertThat(getRequestDataFromCapturedResponseObserver().aggregationName()).isEqualTo(AggregationTestFactory.AGGREGATION_NAME); + } + + @Test + void shouldMapDocumentEntry() { + sendAggregationData(); + + IteratorUtils.toList(getRequestDataFromCapturedResponseObserver().aggregationDataIterator()); + verify(grpcAggregationDataMapper).toGrpcAggregationData(AggregationTestFactory.DOCUMENT_ENTRY); + } + + @Test + @SuppressWarnings("unchecked") + void shouldHaveGrpcAggregationData() { + sendAggregationData(); + + var grpcAggregationDataList = IteratorUtils.toList(getRequestDataFromCapturedResponseObserver().aggregationDataIterator()); + assertThat(grpcAggregationDataList).containsExactly(GrpcAggregationDataTestFactory.create()); + } + + private void sendAggregationData() { + service.sendAggregationData(AggregationTestFactory.create()); + } + + private AggregationDataRemoteService.RequestData getRequestDataFromCapturedResponseObserver() { + verify(serviceStub).sendAggregationData(observerCaptor.capture()); + return ReflectionTestUtils.getField(observerCaptor.getValue(), "requestData", AggregationDataRemoteService.RequestData.class); + } + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java new file mode 100644 index 0000000..6d167dd --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import java.util.stream.Stream; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; + +public class AggregationTestFactory { + + public static final String AGGREGATION_NAME = LoremIpsum.getInstance().getWords(1); + public static final DocumentEntry DOCUMENT_ENTRY = DocumentEntryTestFactory.create(); + + public static AggregationDataRemoteService.Aggregation create() { + return createBuilder().build(); + } + + public static AggregationDataRemoteService.Aggregation.AggregationBuilder createBuilder() { + return AggregationDataRemoteService.Aggregation.builder() + .aggregationName(AGGREGATION_NAME) + .documentEntries(Stream.of(DOCUMENT_ENTRY)); + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapperTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapperTest.java new file mode 100644 index 0000000..7f552a7 --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapperTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import de.ozgcloud.aggregation.data.GrpcObject; +import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; + +class GrpcAggregationDataMapperTest { + + private final GrpcAggregationDataMapper mapper = Mappers.getMapper(GrpcAggregationDataMapper.class); + + @Nested + class TestToGrpcAggregationData { + + @Test + void shouldMapId() { + var grpcData = mapper.toGrpcAggregationData(DocumentEntryTestFactory.create()); + + assertThat(grpcData.getId()).isEqualTo(DocumentEntryTestFactory.ID); + } + + @Test + void shouldMapStatus() { + var grpcData = mapper.toGrpcAggregationData(DocumentEntryTestFactory.create()); + + assertThat(grpcData.getStatus()).isEqualTo(DocumentEntryTestFactory.STATUS); + } + + @Test + void shouldMapEingangDatum() { + var grpcData = mapper.toGrpcAggregationData(DocumentEntryTestFactory.create()); + + assertThat(grpcData.getEingangDatum()).isEqualTo(DocumentEntryTestFactory.EINGANGSDATUM.toString()); + } + + @Test + void shouldMapVorgangName() { + var grpcData = mapper.toGrpcAggregationData(DocumentEntryTestFactory.create()); + + assertThat(grpcData.getVorgangName()).isEqualTo(DocumentEntryTestFactory.VORGANG_NAME); + } + + @Test + void shouldHaveDefaultPayload() { + var grpcData = mapper.toGrpcAggregationData(DocumentEntryTestFactory.create()); + + assertThat(grpcData.getPayload()).isEqualTo(GrpcObject.getDefaultInstance()); + } + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataTestFactory.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataTestFactory.java new file mode 100644 index 0000000..d3ba13a --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataTestFactory.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import de.ozgcloud.aggregation.data.GrpcAggregationData; +import de.ozgcloud.aggregation.data.GrpcAggregationData.Builder; +import de.ozgcloud.aggregation.warehouse.DocumentEntryTestFactory; + +public class GrpcAggregationDataTestFactory { + + public static GrpcAggregationData create() { + return createBuilder().build(); + } + + public static Builder createBuilder() { + return GrpcAggregationData.newBuilder() + .setId(DocumentEntryTestFactory.ID) + .setStatus(DocumentEntryTestFactory.STATUS) + .setEingangDatum(DocumentEntryTestFactory.EINGANGSDATUM.toString()) + .setVorgangName(DocumentEntryTestFactory.VORGANG_NAME); + } +} -- GitLab From 5c7df16827d3cc5f809f5945c900e4ae9b5356c8 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Wed, 23 Apr 2025 19:20:27 +0200 Subject: [PATCH 03/14] OZG-7811 OZG-8099 Implement sendAggregationData --- .../extern/AggregationDataRemoteService.java | 67 ++- .../AggregationDataRemoteServiceTest.java | 383 ++++++++++++++++-- 2 files changed, 412 insertions(+), 38 deletions(-) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index 25d4979..547475d 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -24,6 +24,8 @@ package de.ozgcloud.aggregation.extern; import java.util.Iterator; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.stream.Stream; import de.ozgcloud.aggregation.AggregationManagerProperties; @@ -35,19 +37,28 @@ import de.ozgcloud.aggregation.warehouse.DocumentEntry; import io.grpc.stub.ClientCallStreamObserver; import io.grpc.stub.ClientResponseObserver; import lombok.Builder; +import lombok.Getter; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class AggregationDataRemoteService { + static final int BATCH_SIZE = 100; + private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; private final AggregationManagerProperties properties; private final GrpcAggregationDataMapper grpcAggregationDataMapper; - public void sendAggregationData(Aggregation aggregation) { - var requestObserver = serviceStub.sendAggregationData(new SendAggregationDataResponseObserver( - new RequestData(properties.getMandant(), aggregation.aggregationName, - toGrpcAggregationDataStream(aggregation.documentEntries).iterator()))); + public Future<Void> sendAggregationData(Aggregation aggregation) { + var responseObserver = buildSendAggregationDataResponseObserver(aggregation); + serviceStub.sendAggregationData(responseObserver); + return responseObserver.getResponseFuture(); + } + + SendAggregationDataResponseObserver buildSendAggregationDataResponseObserver(Aggregation aggregation) { + var requestData = new RequestData(properties.getMandant(), aggregation.aggregationName, + toGrpcAggregationDataStream(aggregation.documentEntries).iterator()); + return new SendAggregationDataResponseObserver(requestData); } Stream<GrpcAggregationData> toGrpcAggregationDataStream(Stream<DocumentEntry> documentEntries) { @@ -59,14 +70,12 @@ class AggregationDataRemoteService { implements ClientResponseObserver<GrpcSendAggregationDataRequest, GrpcSendAggregationDataResponse> { private final RequestData requestData; + @Getter + private final CompletableFuture<Void> responseFuture = new CompletableFuture<>(); @Override public void beforeStart(ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver) { - requestObserver.setOnReadyHandler(() -> { - while (requestObserver.isReady() && requestData.aggregationDataIterator.hasNext()) { - // call onNext()? - } - }); + requestObserver.setOnReadyHandler(buildOnReadyHandler(requestObserver)); } @Override @@ -76,12 +85,49 @@ class AggregationDataRemoteService { @Override public void onError(Throwable t) { - + responseFuture.completeExceptionally(t); } @Override public void onCompleted() { + responseFuture.complete(null); + } + + SendAggregationDataOnReadyHandler buildOnReadyHandler(ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver) { + return new SendAggregationDataOnReadyHandler(requestObserver, BATCH_SIZE, requestData); + } + } + + @RequiredArgsConstructor + static class SendAggregationDataOnReadyHandler implements Runnable { + + private final ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver; + private final int batchSize; + private final RequestData requestData; + @Override + public void run() { + while (requestObserver.isReady() && requestData.aggregationDataIterator.hasNext()) { + requestObserver.onNext(buildRequest()); + } + if (!requestData.aggregationDataIterator.hasNext()) { + requestObserver.onCompleted(); + } + } + + GrpcSendAggregationDataRequest buildRequest() { + var builder = GrpcSendAggregationDataRequest.newBuilder() + .setName(requestData.aggregationName()) + .setMandant(requestData.mandant()); + addAggregationData(builder); + return builder.build(); + } + + private void addAggregationData(GrpcSendAggregationDataRequest.Builder builder) { + var elementsAdded = 0; + while (requestData.aggregationDataIterator.hasNext() && elementsAdded++ < batchSize) { + builder.addAggregationData(requestData.aggregationDataIterator.next()); + } } } @@ -89,6 +135,7 @@ class AggregationDataRemoteService { public record Aggregation(String aggregationName, Stream<DocumentEntry> documentEntries) { } + @Builder record RequestData(String mandant, String aggregationName, Iterator<GrpcAggregationData> aggregationDataIterator) { } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java index 46e411f..7836cdf 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java @@ -26,6 +26,12 @@ package de.ozgcloud.aggregation.extern; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import java.util.stream.IntStream; +import java.util.stream.Stream; + import org.apache.commons.collections.IteratorUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -40,8 +46,13 @@ import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; +import de.ozgcloud.aggregation.data.GrpcAggregationData; +import de.ozgcloud.aggregation.data.GrpcSendAggregationDataRequest; import de.ozgcloud.aggregation.extern.AggregationDataRemoteService.SendAggregationDataResponseObserver; +import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.common.test.ReflectionTestUtils; +import io.grpc.stub.ClientCallStreamObserver; +import lombok.SneakyThrows; class AggregationDataRemoteServiceTest { @@ -58,71 +69,387 @@ class AggregationDataRemoteServiceTest { @Nested class TestSendAggregationData { - private static final String MANDANT = LoremIpsum.getInstance().getWords(1); + private final AggregationDataRemoteService.Aggregation aggregation = AggregationTestFactory.create(); - @Captor - private ArgumentCaptor<SendAggregationDataResponseObserver> observerCaptor; + @Mock + private SendAggregationDataResponseObserver responseObserver; + @Mock + private CompletableFuture<Void> responseFuture; @BeforeEach void init() { - when(properties.getMandant()).thenReturn(MANDANT); - // only called, after stream was consumed (is lazy) - lenient().when(grpcAggregationDataMapper.toGrpcAggregationData(AggregationTestFactory.DOCUMENT_ENTRY)).thenReturn( - GrpcAggregationDataTestFactory.create()); + doReturn(responseObserver).when(service).buildSendAggregationDataResponseObserver(any()); + when(responseObserver.getResponseFuture()).thenReturn(responseFuture); } @Test - void shouldGetMandant() { + void shouldBuildResponseObserver() { sendAggregationData(); - verify(properties).getMandant(); + verify(service).buildSendAggregationDataResponseObserver(aggregation); } @Test void shouldCallServiceStub() { sendAggregationData(); - verify(serviceStub).sendAggregationData(any(SendAggregationDataResponseObserver.class)); + verify(serviceStub).sendAggregationData(responseObserver); } @Test - void shouldResponseObserverHaveMandant() { - sendAggregationData(); + void shouldReturnFutureFromResponseObserver() { + var future = sendAggregationData(); - assertThat(getRequestDataFromCapturedResponseObserver().mandant()).isEqualTo(MANDANT); + assertThat(future).isSameAs(responseFuture); + } + + private Future<Void> sendAggregationData() { + return service.sendAggregationData(aggregation); + } + } + + @Nested + class TestBuildSendAggregationDataResponseObserver { + + @Captor + private ArgumentCaptor<Stream<DocumentEntry>> documentEntriesCaptor; + + @BeforeEach + void init() { + doReturn(Stream.of(GrpcAggregationDataTestFactory.create())).when(service).toGrpcAggregationDataStream(any()); } @Test - void shouldResponseObserverHaveAggregationName() { - sendAggregationData(); + void shouldGetMandant() { + service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + verify(properties).getMandant(); + } + + @Test + void shouldCreateGrpcAggregationDataStream() { + service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); - assertThat(getRequestDataFromCapturedResponseObserver().aggregationName()).isEqualTo(AggregationTestFactory.AGGREGATION_NAME); + verify(service).toGrpcAggregationDataStream(documentEntriesCaptor.capture()); + assertThat(documentEntriesCaptor.getValue()).containsExactly(AggregationTestFactory.DOCUMENT_ENTRY); + } + + @Test + void shouldReturnResponseObserver() { + var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + assertThat(builtResponseObserver).isNotNull(); + } + + @Nested + class TestBuiltResponseObserver { + + private static final String MANDANT = LoremIpsum.getInstance().getWords(1); + + @BeforeEach + void init() { + when(properties.getMandant()).thenReturn(MANDANT); + } + + @Test + void shouldHaveMandant() { + var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + assertThat(getRequestData(builtResponseObserver).mandant()).isEqualTo(MANDANT); + } + + @Test + void shouldHaveAggregationName() { + var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + assertThat(getRequestData(builtResponseObserver).aggregationName()).isEqualTo(AggregationTestFactory.AGGREGATION_NAME); + } + + @Test + @SuppressWarnings("unchecked") + void shouldHaveGrpcAggregationData() { + var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + var aggregationData = IteratorUtils.toList(getRequestData(builtResponseObserver).aggregationDataIterator()); + assertThat(aggregationData).containsExactly(GrpcAggregationDataTestFactory.create()); + } + + private AggregationDataRemoteService.RequestData getRequestData(SendAggregationDataResponseObserver responseObserver) { + return ReflectionTestUtils.getField(responseObserver, "requestData", AggregationDataRemoteService.RequestData.class); + } + } + } + + @Nested + class TestToGrpcAggregationDataStream { + + @BeforeEach + void init() { + when(grpcAggregationDataMapper.toGrpcAggregationData(AggregationTestFactory.DOCUMENT_ENTRY)).thenReturn( + GrpcAggregationDataTestFactory.create()); } @Test void shouldMapDocumentEntry() { - sendAggregationData(); + service.toGrpcAggregationDataStream(Stream.of(AggregationTestFactory.DOCUMENT_ENTRY)).toList(); - IteratorUtils.toList(getRequestDataFromCapturedResponseObserver().aggregationDataIterator()); verify(grpcAggregationDataMapper).toGrpcAggregationData(AggregationTestFactory.DOCUMENT_ENTRY); } @Test - @SuppressWarnings("unchecked") - void shouldHaveGrpcAggregationData() { - sendAggregationData(); + void shouldReturnMappedDocumentEntries() { + var mappedDocumentEntries = service.toGrpcAggregationDataStream(Stream.of(AggregationTestFactory.DOCUMENT_ENTRY)); + + assertThat(mappedDocumentEntries.toList()).containsExactly(GrpcAggregationDataTestFactory.create()); + } + } + + @Nested + class SendAggregationDataResponseObserverTest { + + @Mock + private AggregationDataRemoteService.RequestData requestData; + @Spy + @InjectMocks + private SendAggregationDataResponseObserver responseObserver; + + @Nested + class TestBeforeStart { + + @Mock + private ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver; + @Mock + private AggregationDataRemoteService.SendAggregationDataOnReadyHandler onReadyHandler; + + @BeforeEach + void init() { + doReturn(onReadyHandler).when(responseObserver).buildOnReadyHandler(any()); + } + + @Test + void shouldBuildOnReadyHandler() { + responseObserver.beforeStart(requestObserver); + + verify(responseObserver).buildOnReadyHandler(requestObserver); + } + + @Test + void shouldSetOnReadyHandler() { + responseObserver.beforeStart(requestObserver); + + verify(requestObserver).setOnReadyHandler(onReadyHandler); + } + } + + @Nested + class TestOnError { + + @Test + @SneakyThrows + void shouldCompleteExceptionally() { + var error = new Throwable(); + + responseObserver.onError(error); + + assertThatException().isThrownBy(() -> responseObserver.getResponseFuture().get()).withCause(error); + } + } + + @Nested + class TestOnCompleted { + + @Test + void shouldCompleteFuture() { + responseObserver.onCompleted(); + + assertThat(responseObserver.getResponseFuture().isDone()).isTrue(); + } + } + } + + @Nested + class SendAggregationDataOnReadyHandlerTest { + + private static final int BATCH_SIZE = 2; + private static final String MANDANT = LoremIpsum.getInstance().getWords(1); + + @Mock + private ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver; + + @Nested + class TestRun { + + @Mock + private GrpcSendAggregationDataRequest request; + + @Test + void shouldCheckIfStreamIsReady() { + createOnReadyHandler(createAggregationData(1)).run(); + + verify(requestObserver).isReady(); + } + + @Test + void shouldNotCallOnNextWhenStreamIsNotReady() { + when(requestObserver.isReady()).thenReturn(false); + + createOnReadyHandler(createAggregationData(1)).run(); + + verify(requestObserver, never()).onNext(any()); + } + + @Test + void shouldNotCallOnNextWhenHasNoMoreData() { + when(requestObserver.isReady()).thenReturn(true); + + createOnReadyHandler(createAggregationData(0)).run(); + + verify(requestObserver, never()).onNext(any()); + } + + @Test + void shouldBuildRequest() { + when(requestObserver.isReady()).thenReturn(true); + var onReadyHandler = spy(createOnReadyHandler(createAggregationData(1))); + + onReadyHandler.run(); + + verify(onReadyHandler).buildRequest(); + } + + @Test + void shouldCallOnNextWithBuiltRequest() { + when(requestObserver.isReady()).thenReturn(true).thenReturn(false); + var onReadyHandler = spy(createOnReadyHandler(createAggregationData(1))); + doReturn(request).when(onReadyHandler).buildRequest(); + + onReadyHandler.run(); + + verify(requestObserver).onNext(request); + } + + @Test + void shouldCallOnNextUntilAllDataWasSent() { + when(requestObserver.isReady()).thenReturn(true); + + createOnReadyHandler(createAggregationData(BATCH_SIZE + 1)).run(); + + verify(requestObserver, times(2)).onNext(any()); + } + + @Test + void shouldCompleteRequest() { + when(requestObserver.isReady()).thenReturn(true); + + createOnReadyHandler(createAggregationData(BATCH_SIZE + 1)).run(); + + verify(requestObserver).onCompleted(); + } + + @Test + void shouldNotCompleteRequestIfNotAllDataWasSent() { + when(requestObserver.isReady()).thenReturn(true).thenReturn(false); + + createOnReadyHandler(createAggregationData(BATCH_SIZE + 1)).run(); + + verify(requestObserver, never()).onCompleted(); + } + } + + @Nested + class TestBuildRequest { + + @Test + void shouldSetName() { + var request = createOnReadyHandler().buildRequest(); + + assertThat(request.getName()).isEqualTo(AggregationTestFactory.AGGREGATION_NAME); + } + + @Test + void shouldSetMandant() { + var request = createOnReadyHandler().buildRequest(); + + assertThat(request.getMandant()).isEqualTo(MANDANT); + } + + @Nested + class OnHasNoAggregationData { + + @Test + void shouldAggregationDataBeEmpty() { + var request = createOnReadyHandler(List.of()).buildRequest(); + + assertThat(request.getAggregationDataCount()).isEqualTo(0); + } + } + + @Nested + class OnHasLessAggregationDataThenBatchSize { + + @Test + void shouldAddAllAggregationData() { + var aggregationData = createAggregationData(BATCH_SIZE - 1); + + var request = createOnReadyHandler(aggregationData).buildRequest(); + + assertThat(request.getAggregationDataList()).hasSameElementsAs(aggregationData); + } + } + + @Nested + class OnHasBatchSizeOfAggregationData { + + @Test + void shouldAddAllAggregationData() { + var aggregationData = createAggregationData(BATCH_SIZE); + + var request = createOnReadyHandler(aggregationData).buildRequest(); + + assertThat(request.getAggregationDataList()).hasSameElementsAs(aggregationData); + } + } + + @Nested + class OnHasMoreAggregationDataThenBatchSize { + + @Test + void shouldHaveExactlyBatchSizeOfAggregationData() { + var aggregationData = createAggregationData(BATCH_SIZE + 1); + + var request = createOnReadyHandler(aggregationData).buildRequest(); + + assertThat(request.getAggregationDataCount()).isEqualTo(BATCH_SIZE); + } + + @Test + void shouldContainOnlyFirstBatchSizeOfElements() { + var aggregationData = createAggregationData(BATCH_SIZE + 1); + + var request = createOnReadyHandler(aggregationData).buildRequest(); + + assertThat(request.getAggregationDataList()).containsExactlyElementsOf(aggregationData.subList(0, BATCH_SIZE)); + } + } + } - var grpcAggregationDataList = IteratorUtils.toList(getRequestDataFromCapturedResponseObserver().aggregationDataIterator()); - assertThat(grpcAggregationDataList).containsExactly(GrpcAggregationDataTestFactory.create()); + private List<GrpcAggregationData> createAggregationData(int count) { + return IntStream.range(1, count + 1) + .mapToObj(idx -> GrpcAggregationDataTestFactory.createBuilder().setVorgangName("vorgang " + idx).build()) + .toList(); } - private void sendAggregationData() { - service.sendAggregationData(AggregationTestFactory.create()); + private AggregationDataRemoteService.SendAggregationDataOnReadyHandler createOnReadyHandler() { + return createOnReadyHandler(List.of()); } - private AggregationDataRemoteService.RequestData getRequestDataFromCapturedResponseObserver() { - verify(serviceStub).sendAggregationData(observerCaptor.capture()); - return ReflectionTestUtils.getField(observerCaptor.getValue(), "requestData", AggregationDataRemoteService.RequestData.class); + private AggregationDataRemoteService.SendAggregationDataOnReadyHandler createOnReadyHandler(List<GrpcAggregationData> aggregationData) { + return new AggregationDataRemoteService.SendAggregationDataOnReadyHandler(requestObserver, BATCH_SIZE, + AggregationDataRemoteService.RequestData.builder() + .mandant(MANDANT) + .aggregationName(AggregationTestFactory.AGGREGATION_NAME) + .aggregationDataIterator(aggregationData.iterator()) + .build()); } } } -- GitLab From 65efb02609c4ded027921fd9c9882cf8fddfd4ae Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Thu, 24 Apr 2025 09:20:59 +0200 Subject: [PATCH 04/14] OZG-7811 OZG-8099 AggregationDataRemoteService conditional on property --- .../aggregation/extern/AggregationDataRemoteService.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index 547475d..c10dbbb 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -28,6 +28,9 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.stream.Stream; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; import de.ozgcloud.aggregation.data.GrpcAggregationData; @@ -39,12 +42,16 @@ import io.grpc.stub.ClientResponseObserver; import lombok.Builder; import lombok.Getter; import lombok.RequiredArgsConstructor; +import net.devh.boot.grpc.client.inject.GrpcClient; +@Service +@ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor class AggregationDataRemoteService { static final int BATCH_SIZE = 100; + @GrpcClient("aggregation-manager") private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; private final AggregationManagerProperties properties; private final GrpcAggregationDataMapper grpcAggregationDataMapper; -- GitLab From 81f57c65ce3a4b5f6df6255ffd0d5c8a1448b0d2 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Thu, 24 Apr 2025 14:56:00 +0200 Subject: [PATCH 05/14] OZG-7811 OZG-8099 Read batch size from configuration --- .../extern/AggregationDataRemoteService.java | 9 +++--- .../AggregationDataRemoteServiceTest.java | 29 +++++++++++++++++-- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index c10dbbb..643bbeb 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -31,6 +31,7 @@ import java.util.stream.Stream; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; +import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; import de.ozgcloud.aggregation.data.GrpcAggregationData; @@ -49,11 +50,10 @@ import net.devh.boot.grpc.client.inject.GrpcClient; @RequiredArgsConstructor class AggregationDataRemoteService { - static final int BATCH_SIZE = 100; - @GrpcClient("aggregation-manager") private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; private final AggregationManagerProperties properties; + private final AggregationManagerConfiguration configuration; private final GrpcAggregationDataMapper grpcAggregationDataMapper; public Future<Void> sendAggregationData(Aggregation aggregation) { @@ -65,7 +65,7 @@ class AggregationDataRemoteService { SendAggregationDataResponseObserver buildSendAggregationDataResponseObserver(Aggregation aggregation) { var requestData = new RequestData(properties.getMandant(), aggregation.aggregationName, toGrpcAggregationDataStream(aggregation.documentEntries).iterator()); - return new SendAggregationDataResponseObserver(requestData); + return new SendAggregationDataResponseObserver(configuration.getFetchingBatchSize(), requestData); } Stream<GrpcAggregationData> toGrpcAggregationDataStream(Stream<DocumentEntry> documentEntries) { @@ -76,6 +76,7 @@ class AggregationDataRemoteService { static class SendAggregationDataResponseObserver implements ClientResponseObserver<GrpcSendAggregationDataRequest, GrpcSendAggregationDataResponse> { + private final int batchSize; private final RequestData requestData; @Getter private final CompletableFuture<Void> responseFuture = new CompletableFuture<>(); @@ -101,7 +102,7 @@ class AggregationDataRemoteService { } SendAggregationDataOnReadyHandler buildOnReadyHandler(ClientCallStreamObserver<GrpcSendAggregationDataRequest> requestObserver) { - return new SendAggregationDataOnReadyHandler(requestObserver, BATCH_SIZE, requestData); + return new SendAggregationDataOnReadyHandler(requestObserver, batchSize, requestData); } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java index 7836cdf..2023e0c 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java @@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import java.util.List; +import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.stream.IntStream; @@ -44,6 +45,7 @@ import org.mockito.Spy; import com.thedeanda.lorem.LoremIpsum; +import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; import de.ozgcloud.aggregation.data.GrpcAggregationData; @@ -61,6 +63,8 @@ class AggregationDataRemoteServiceTest { @Mock private AggregationManagerProperties properties; @Mock + private AggregationManagerConfiguration configuration; + @Mock private GrpcAggregationDataMapper grpcAggregationDataMapper; @InjectMocks @Spy @@ -111,12 +115,14 @@ class AggregationDataRemoteServiceTest { @Nested class TestBuildSendAggregationDataResponseObserver { + private static final int BATCH_SIZE = new Random().nextInt(100); @Captor private ArgumentCaptor<Stream<DocumentEntry>> documentEntriesCaptor; @BeforeEach void init() { doReturn(Stream.of(GrpcAggregationDataTestFactory.create())).when(service).toGrpcAggregationDataStream(any()); + when(configuration.getFetchingBatchSize()).thenReturn(BATCH_SIZE); } @Test @@ -134,6 +140,13 @@ class AggregationDataRemoteServiceTest { assertThat(documentEntriesCaptor.getValue()).containsExactly(AggregationTestFactory.DOCUMENT_ENTRY); } + @Test + void shouldGetBatchSize() { + service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + verify(configuration).getFetchingBatchSize(); + } + @Test void shouldReturnResponseObserver() { var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); @@ -151,6 +164,13 @@ class AggregationDataRemoteServiceTest { when(properties.getMandant()).thenReturn(MANDANT); } + @Test + void shouldHaveBatchSize() { + var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); + + assertThat(getBatchSize(builtResponseObserver)).isEqualTo(BATCH_SIZE); + } + @Test void shouldHaveMandant() { var builtResponseObserver = service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); @@ -174,6 +194,10 @@ class AggregationDataRemoteServiceTest { assertThat(aggregationData).containsExactly(GrpcAggregationDataTestFactory.create()); } + private int getBatchSize(SendAggregationDataResponseObserver responseObserver) { + return ReflectionTestUtils.getField(responseObserver, "batchSize", Integer.class); + } + private AggregationDataRemoteService.RequestData getRequestData(SendAggregationDataResponseObserver responseObserver) { return ReflectionTestUtils.getField(responseObserver, "requestData", AggregationDataRemoteService.RequestData.class); } @@ -207,11 +231,12 @@ class AggregationDataRemoteServiceTest { @Nested class SendAggregationDataResponseObserverTest { + private static final int BATCH_SIZE = 2; + @Mock private AggregationDataRemoteService.RequestData requestData; @Spy - @InjectMocks - private SendAggregationDataResponseObserver responseObserver; + private SendAggregationDataResponseObserver responseObserver = new SendAggregationDataResponseObserver(BATCH_SIZE, requestData); @Nested class TestBeforeStart { -- GitLab From 39eece3af9e00603b135d530dc5e12a85b6ae2a9 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Thu, 24 Apr 2025 19:09:57 +0200 Subject: [PATCH 06/14] OZG-7811 OZG-8099 Use loaders in Aggregator --- .../de/ozgcloud/aggregation/Aggregation.java | 33 +++++ .../aggregation/AggregationDataLoader.java | 29 ++++ .../AggregationWarehouseDataLoader.java | 53 ++++++++ .../de/ozgcloud/aggregation/Aggregator.java | 28 ++-- .../extern/AggregationDataRemoteService.java | 9 +- .../extern/AggregationRemoteDataLoader.java | 56 ++++++++ .../warehouse/CustomWarehouseRepository.java | 4 +- .../CustomWarehouseRepositoryImpl.java | 7 +- .../{extern => }/AggregationTestFactory.java | 8 +- .../AggregationWarehouseDataLoaderTest.java | 110 +++++++++++++++ .../ozgcloud/aggregation/AggregatorTest.java | 87 ++++++------ .../AggregationDataRemoteServiceTest.java | 4 +- .../AggregationRemoteDataLoaderTest.java | 127 ++++++++++++++++++ .../CustomWarehouseRepositoryImplTest.java | 13 +- 14 files changed, 485 insertions(+), 83 deletions(-) create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregation.java create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java rename aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/{extern => }/AggregationTestFactory.java (85%) create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregation.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregation.java new file mode 100644 index 0000000..50368c6 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregation.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import java.util.stream.Stream; + +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import lombok.Builder; + +@Builder +public record Aggregation(String aggregationName, Stream<DocumentEntry> documentEntries) { +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java new file mode 100644 index 0000000..0bd3bd6 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +public interface AggregationDataLoader { + + void loadIntoTarget(Aggregation aggregation); +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java new file mode 100644 index 0000000..1873800 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.WarehouseRepository; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Component +@Scope("prototype") +@RequiredArgsConstructor +@Log4j2 +class AggregationWarehouseDataLoader implements AggregationDataLoader { + + private final WarehouseRepository repository; + + @Override + public void loadIntoTarget(Aggregation aggregation) { + var collectionName = getCollectionName(aggregation); + repository.clearCollection(collectionName); + repository.saveAllInCollection(aggregation.documentEntries(), collectionName); + } + + String getCollectionName(Aggregation aggregation) { + return StringUtils.isNotBlank(aggregation.aggregationName()) ? aggregation.aggregationName() : DocumentEntry.COLLECTION; + } +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregator.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregator.java index 79cf481..3049875 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregator.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/Aggregator.java @@ -22,7 +22,6 @@ import de.ozgcloud.aggregation.transformation.AggregationMapping.FormIdentifier; import de.ozgcloud.aggregation.transformation.TransformationException; import de.ozgcloud.aggregation.transformation.VorgangMapper; import de.ozgcloud.aggregation.warehouse.DocumentEntry; -import de.ozgcloud.aggregation.warehouse.WarehouseRepository; import de.ozgcloud.apilib.vorgang.OzgCloudVorgang; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangQuery.FormIdentification; @@ -42,13 +41,13 @@ class Aggregator { private static final Predicate<Batch> HAS_NEXT_BATCH = x -> !x.items.isEmpty(); private final OzgCloudVorgangService vorgangService; - private final WarehouseRepository repository; private final VorgangMapper vorgangMapper; private Execution execution; private FormIdentifier formIdentifier; - private String collectionName = DocumentEntry.COLLECTION; + private String aggregationName; private int batchSize = 100; + private AggregationDataLoader loader; public Aggregator withExecution(Execution execution) { this.execution = execution; @@ -58,7 +57,7 @@ class Aggregator { public Aggregator withAggregationMapping(AggregationMapping aggregationMapping) { if (Objects.nonNull(aggregationMapping)) { this.formIdentifier = aggregationMapping.getFormIdentifier(); - this.collectionName = aggregationMapping.getName(); + this.aggregationName = aggregationMapping.getName(); } return this; } @@ -68,8 +67,13 @@ class Aggregator { return this; } + public Aggregator withLoader(AggregationDataLoader loader) { + this.loader = loader; + return this; + } + public void aggregate() { - loadVorgaengeIntoRepository(Stream.concat(extractBatchesOfVorgaengeFromDataSource(), extractBatchesOfDeletedVorgaengeFromDataSource())); + loadVorgaenge(Stream.concat(extractBatchesOfVorgaengeFromDataSource(), extractBatchesOfDeletedVorgaengeFromDataSource())); } Stream<Batch> extractBatchesOfVorgaengeFromDataSource() { @@ -129,13 +133,12 @@ class Aggregator { return UUID.randomUUID(); } - void loadVorgaengeIntoRepository(Stream<Batch> batches) { - repository.clearCollection(collectionName); - batches.map(this::transformBatchToDocumentEntries).forEach(this::loadDocumentEntriesIntoRepository); + void loadVorgaenge(Stream<Batch> batches) { + loader.loadIntoTarget(new Aggregation(aggregationName, batches.flatMap(this::transformBatchToDocumentEntries))); } - List<DocumentEntry> transformBatchToDocumentEntries(Batch batch) { - return batch.items.stream().map(vorgang -> transformWithinBatch(batch, vorgang)).filter(Objects::nonNull).toList(); + Stream<DocumentEntry> transformBatchToDocumentEntries(Batch batch) { + return batch.items.stream().map(vorgang -> transformWithinBatch(batch, vorgang)).filter(Objects::nonNull); } DocumentEntry transformWithinBatch(Batch batch, OzgCloudVorgang vorgang) { @@ -149,11 +152,6 @@ class Aggregator { } } - void loadDocumentEntriesIntoRepository(List<DocumentEntry> entries) { - LOG.atDebug().log("store documents: {}", () -> entries.stream().map(DocumentEntry::getId).toList()); - repository.saveAllInCollection(entries, collectionName); - } - record Batch(Execution execution, UUID id, Page page, List<OzgCloudVorgang> items) { } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index 643bbeb..564df21 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -31,6 +31,7 @@ import java.util.stream.Stream; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; +import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; @@ -63,8 +64,8 @@ class AggregationDataRemoteService { } SendAggregationDataResponseObserver buildSendAggregationDataResponseObserver(Aggregation aggregation) { - var requestData = new RequestData(properties.getMandant(), aggregation.aggregationName, - toGrpcAggregationDataStream(aggregation.documentEntries).iterator()); + var requestData = new RequestData(properties.getMandant(), aggregation.aggregationName(), + toGrpcAggregationDataStream(aggregation.documentEntries()).iterator()); return new SendAggregationDataResponseObserver(configuration.getFetchingBatchSize(), requestData); } @@ -139,10 +140,6 @@ class AggregationDataRemoteService { } } - @Builder - public record Aggregation(String aggregationName, Stream<DocumentEntry> documentEntries) { - } - @Builder record RequestData(String mandant, String aggregationName, Iterator<GrpcAggregationData> aggregationDataIterator) { } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java new file mode 100644 index 0000000..92b7b99 --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import java.util.concurrent.ExecutionException; + +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import de.ozgcloud.aggregation.Aggregation; +import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; + +@Component +@Scope("prototype") +@RequiredArgsConstructor +@Log4j2 +class AggregationRemoteDataLoader implements AggregationDataLoader { + + private final AggregationDataRemoteService service; + + @Override + public void loadIntoTarget(Aggregation aggregation) { + try { + service.sendAggregationData(aggregation).get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TechnicalException("Waiting for sending aggregation data to complete was interrupted.", e); + } catch (ExecutionException e) { + throw new TechnicalException("Error on sending aggregation data.", e); + } + } +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java index 0a31216..a2abb1d 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepository.java @@ -1,12 +1,12 @@ package de.ozgcloud.aggregation.warehouse; -import java.util.List; +import java.util.stream.Stream; interface CustomWarehouseRepository { DocumentEntry saveInCollection(DocumentEntry documentEntry, String collectionName); - List<DocumentEntry> saveAllInCollection(Iterable<DocumentEntry> documentEntries, String collectionName); + void saveAllInCollection(Stream<DocumentEntry> documentEntries, String collectionName); void clearCollection(String collectionName); } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java index a5169d6..f2ce884 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImpl.java @@ -1,7 +1,6 @@ package de.ozgcloud.aggregation.warehouse; -import java.util.List; -import java.util.stream.StreamSupport; +import java.util.stream.Stream; import org.springframework.data.mongodb.core.MongoTemplate; @@ -13,8 +12,8 @@ class CustomWarehouseRepositoryImpl implements CustomWarehouseRepository { private final MongoTemplate mongoTemplate; @Override - public List<DocumentEntry> saveAllInCollection(Iterable<DocumentEntry> documentEntries, String collectionName) { - return StreamSupport.stream(documentEntries.spliterator(), false).map(entry -> saveInCollection(entry, collectionName)).toList(); + public void saveAllInCollection(Stream<DocumentEntry> documentEntries, String collectionName) { + documentEntries.forEach(entry -> saveInCollection(entry, collectionName)); } @Override diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationTestFactory.java similarity index 85% rename from aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java rename to aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationTestFactory.java index 6d167dd..4f99f08 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationTestFactory.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationTestFactory.java @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.aggregation.extern; +package de.ozgcloud.aggregation; import java.util.stream.Stream; @@ -35,12 +35,12 @@ public class AggregationTestFactory { public static final String AGGREGATION_NAME = LoremIpsum.getInstance().getWords(1); public static final DocumentEntry DOCUMENT_ENTRY = DocumentEntryTestFactory.create(); - public static AggregationDataRemoteService.Aggregation create() { + public static Aggregation create() { return createBuilder().build(); } - public static AggregationDataRemoteService.Aggregation.AggregationBuilder createBuilder() { - return AggregationDataRemoteService.Aggregation.builder() + public static Aggregation.AggregationBuilder createBuilder() { + return Aggregation.builder() .aggregationName(AGGREGATION_NAME) .documentEntries(Stream.of(DOCUMENT_ENTRY)); } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java new file mode 100644 index 0000000..5af0267 --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.stream.Stream; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.aggregation.warehouse.DocumentEntry; +import de.ozgcloud.aggregation.warehouse.WarehouseRepository; + +class AggregationWarehouseDataLoaderTest { + + @Mock + private WarehouseRepository repository; + @Spy + @InjectMocks + private AggregationWarehouseDataLoader loader; + + @Nested + class TestLoadIntoTarget { + + private static final String COLLECTION_NAME = LoremIpsum.getInstance().getWords(1); + private final Aggregation aggregation = AggregationTestFactory.create(); + + @Captor + private ArgumentCaptor<Stream<DocumentEntry>> documentEntriesCaptor; + + @BeforeEach + void init() { + doReturn(COLLECTION_NAME).when(loader).getCollectionName(aggregation); + } + + @Test + void shouldGetCollectionName() { + loader.loadIntoTarget(aggregation); + + verify(loader).getCollectionName(aggregation); + } + + @Test + void shouldClearCollection() { + loader.loadIntoTarget(aggregation); + + verify(repository).clearCollection(COLLECTION_NAME); + } + + @Test + void shouldSaveDocumentEntriesInCollection() { + loader.loadIntoTarget(aggregation); + + verify(repository).saveAllInCollection(documentEntriesCaptor.capture(), eq(COLLECTION_NAME)); + assertThat(documentEntriesCaptor.getValue()).containsExactly(AggregationTestFactory.DOCUMENT_ENTRY); + } + } + + @Nested + class TestGetCollectionName { + + @Test + void shouldReturnAggregationName() { + var collectionName = loader.getCollectionName(AggregationTestFactory.create()); + + assertThat(collectionName).isEqualTo(AggregationTestFactory.AGGREGATION_NAME); + } + + @ParameterizedTest + @NullAndEmptySource + void shouldReturnCollectionNameFromDocumentEntry(String aggregationName) { + var collectionName = loader.getCollectionName(AggregationTestFactory.createBuilder().aggregationName(aggregationName).build()); + + assertThat(collectionName).isEqualTo(DocumentEntry.COLLECTION); + } + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregatorTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregatorTest.java index 2d66f74..684c5df 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregatorTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregatorTest.java @@ -82,7 +82,7 @@ class AggregatorTest { private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); private final FormIdentifier formIdentifier = AggregationMappingTestFactory.FORM_IDENTIFIER; - private final String collectionName = AggregationMappingTestFactory.NAME; + private final String aggregationName = AggregationMappingTestFactory.NAME; @Test void shouldReturnSelf() { @@ -99,10 +99,10 @@ class AggregatorTest { } @Test - void shouldSetCollectionName() { + void shouldSetAggregationName() { var result = aggregator.withAggregationMapping(aggregationMapping); - assertThat(ReflectionTestUtils.getField(result, "collectionName")).isEqualTo(collectionName); + assertThat(ReflectionTestUtils.getField(result, "aggregationName")).isEqualTo(aggregationName); } } @@ -124,10 +124,10 @@ class AggregatorTest { } @Test - void shouldNotSetCollectionName() { + void shouldNotSetAggregationName() { var result = aggregator.withAggregationMapping(null); - assertThat(ReflectionTestUtils.getField(result, "collectionName")).isEqualTo(DocumentEntry.COLLECTION); + assertThat(ReflectionTestUtils.getField(result, "aggregationName")).isNull(); } } } @@ -152,6 +152,27 @@ class AggregatorTest { } } + @Nested + class TestWithLoader { + + @Mock + private AggregationDataLoader loader; + + @Test + void shouldReturnSelf() { + var result = aggregator.withLoader(loader); + + assertThat(result).isSameAs(aggregator); + } + + @Test + void shouldSetLoader() { + aggregator.withLoader(loader); + + assertThat(ReflectionTestUtils.getField(aggregator, "loader")).isSameAs(loader); + } + } + @Nested class TestAggregate { @Mock @@ -165,7 +186,7 @@ class AggregatorTest { void setUp() { doReturn(Stream.of(batchOfVorgaenge)).when(aggregator).extractBatchesOfVorgaengeFromDataSource(); doReturn(Stream.of(batchOfDeletedVorgaenge)).when(aggregator).extractBatchesOfDeletedVorgaengeFromDataSource(); - doNothing().when(aggregator).loadVorgaengeIntoRepository(any()); + doNothing().when(aggregator).loadVorgaenge(any()); } @Test @@ -186,7 +207,7 @@ class AggregatorTest { void shouldLoadVorgaengeIntoRepository() { aggregator.aggregate(); - verify(aggregator).loadVorgaengeIntoRepository(batchStreamCaptor.capture()); + verify(aggregator).loadVorgaenge(batchStreamCaptor.capture()); assertThat(batchStreamCaptor.getValue()).containsExactly(batchOfVorgaenge, batchOfDeletedVorgaenge); } } @@ -485,67 +506,55 @@ class AggregatorTest { } @Nested - class TestLoadVorgaengeIntoRepository { + class TestLoadVorgaenge { @Mock private Execution execution; @Mock private Batch batch; + @Mock + private AggregationDataLoader loader; private final DocumentEntry documentEntry = DocumentEntryTestFactory.create(); private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); @Captor - private ArgumentCaptor<List<DocumentEntry>> documentEntriesCaptor; + private ArgumentCaptor<Aggregation> aggregationCaptor; @BeforeEach void init() { - aggregator = aggregator.withAggregationMapping(aggregationMapping); - doReturn(List.of(documentEntry)).when(aggregator).transformBatchToDocumentEntries(any()); - doNothing().when(aggregator).loadDocumentEntriesIntoRepository(any()); + aggregator = aggregator.withAggregationMapping(aggregationMapping).withLoader(loader); } @Test - void shouldDropCollection() { - loadVorgaengeIntoRepository(); + void shouldLoadIntoTarget() { + loadVorgaenge(); - verify(repository).clearCollection(AggregationMappingTestFactory.NAME); + verify(loader).loadIntoTarget(any(Aggregation.class)); } @Test void shouldTransform() { - loadVorgaengeIntoRepository(); + doReturn(Stream.of(documentEntry)).when(aggregator).transformBatchToDocumentEntries(any()); + loadVorgaenge(); + + getArgumentOfLoadIntoTarget().documentEntries().toList(); verify(aggregator).transformBatchToDocumentEntries(batch); } @Test - void shouldLoadIntoRepository() { - loadVorgaengeIntoRepository(); + void shouldSetAggregationName() { + loadVorgaenge(); - verify(aggregator).loadDocumentEntriesIntoRepository(documentEntriesCaptor.capture()); - assertThat(documentEntriesCaptor.getValue()).containsExactly(documentEntry); + assertThat(getArgumentOfLoadIntoTarget().aggregationName()).isEqualTo(AggregationMappingTestFactory.NAME); } - private void loadVorgaengeIntoRepository() { - aggregator.loadVorgaengeIntoRepository(Stream.of(batch)); - } - } - - @Nested - class TestLoadDocumentEntriesIntoRepository { - - private final List<DocumentEntry> documentEntries = List.of(DocumentEntryTestFactory.create()); - private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); - - @BeforeEach - void init() { - aggregator = aggregator.withAggregationMapping(aggregationMapping); + private Aggregation getArgumentOfLoadIntoTarget() { + verify(loader).loadIntoTarget(aggregationCaptor.capture()); + return aggregationCaptor.getValue(); } - @Test - void shouldSaveDocumentEntriesInCollection() { - aggregator.loadDocumentEntriesIntoRepository(documentEntries); - - verify(repository).saveAllInCollection(documentEntries, AggregationMappingTestFactory.NAME); + private void loadVorgaenge() { + aggregator.loadVorgaenge(Stream.of(batch)); } } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java index 2023e0c..c3711cc 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java @@ -45,8 +45,10 @@ import org.mockito.Spy; import com.thedeanda.lorem.LoremIpsum; +import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; +import de.ozgcloud.aggregation.AggregationTestFactory; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; import de.ozgcloud.aggregation.data.GrpcAggregationData; import de.ozgcloud.aggregation.data.GrpcSendAggregationDataRequest; @@ -73,7 +75,7 @@ class AggregationDataRemoteServiceTest { @Nested class TestSendAggregationData { - private final AggregationDataRemoteService.Aggregation aggregation = AggregationTestFactory.create(); + private final Aggregation aggregation = AggregationTestFactory.create(); @Mock private SendAggregationDataResponseObserver responseObserver; diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java new file mode 100644 index 0000000..e6db338 --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.aggregation.Aggregation; +import de.ozgcloud.aggregation.AggregationTestFactory; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.SneakyThrows; + +class AggregationRemoteDataLoaderTest { + + @Mock + private AggregationDataRemoteService remoteService; + @Spy + @InjectMocks + private AggregationRemoteDataLoader loader; + + @Nested + class TestLoadIntoTarget { + + private final Aggregation aggregation = AggregationTestFactory.create(); + @Mock + private Future<Void> future; + + @BeforeEach + void init() { + when(remoteService.sendAggregationData(any())).thenReturn(future); + } + + @Test + @SneakyThrows + void shouldSendAggregationData() { + loader.loadIntoTarget(aggregation); + + verify(remoteService).sendAggregationData(aggregation); + } + + @Test + @SneakyThrows + void shouldGetFromFuture() { + loader.loadIntoTarget(aggregation); + + verify(future).get(); + } + + @Nested + class TestOnInterruptedException { + + private final InterruptedException exception = new InterruptedException(); + + @BeforeEach + @SneakyThrows + void mock() { + when(future.get()).thenThrow(exception); + } + + @Test + void shouldThrowTechnicalException() { + assertThrows(TechnicalException.class, () -> loader.loadIntoTarget(aggregation)); + } + + @Test + void shouldInterruptThread() { + try { + loader.loadIntoTarget(aggregation); + } catch (TechnicalException e) { + // expected + } + + assertThat(Thread.currentThread().isInterrupted()).isTrue(); + } + } + + @Nested + class TestOnExecutionException { + + private final ExecutionException exception = new ExecutionException(new Exception()); + + @BeforeEach + @SneakyThrows + void mock() { + when(future.get()).thenThrow(exception); + } + + @Test + void shouldThrowTechnicalException() { + assertThrows(TechnicalException.class, () -> loader.loadIntoTarget(aggregation)); + } + } + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java index 194a372..171257c 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/CustomWarehouseRepositoryImplTest.java @@ -1,7 +1,6 @@ package de.ozgcloud.aggregation.warehouse; import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.List; @@ -33,20 +32,10 @@ class CustomWarehouseRepositoryImplTest { @Test void shouldCallSaveInCollection() { - repository.saveAllInCollection(documentEntries, collectionName); + repository.saveAllInCollection(documentEntries.stream(), collectionName); verify(repository).saveInCollection(documentEntry, collectionName); } - - @Test - void shouldReturnSavedDocumentEntry() { - var savedDocumentEntry = DocumentEntryTestFactory.create(); - doReturn(savedDocumentEntry).when(repository).saveInCollection(any(), any()); - - var returnedDocumentEntries = repository.saveAllInCollection(documentEntries, collectionName); - - assertThat(returnedDocumentEntries).containsExactly(savedDocumentEntry); - } } @Nested -- GitLab From 1863b99a21582959dce003a99e5dfb63cb39c017 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Fri, 25 Apr 2025 18:32:06 +0200 Subject: [PATCH 07/14] OZG-7811 OZG-8099 runWithDefaultTransformation pro scope --- .../aggregation/AggregationManagerRunner.java | 31 +-- .../aggregation/TransformationProperties.java | 6 +- .../AggregationManagerRunnerTest.java | 199 +++++++++++------- .../AggregationMappingTestFactory.java | 4 +- 4 files changed, 147 insertions(+), 93 deletions(-) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java index 3d5596e..f3b4730 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java @@ -23,8 +23,9 @@ */ package de.ozgcloud.aggregation; -import java.util.Objects; +import java.util.Arrays; import java.util.UUID; +import java.util.stream.Stream; import org.apache.logging.log4j.ThreadContext; import org.springframework.beans.factory.annotation.Lookup; @@ -54,19 +55,25 @@ public abstract class AggregationManagerRunner implements CommandLineRunner { @Override public void run(String... args) throws TransformationException { - var identifier = transformationProperties.getIdentifier(); - var aggregationMappings = transformationProperties.getAggregationMappings(); - if (Objects.isNull(aggregationMappings) || aggregationMappings.isEmpty()) { - runWithDefaultTransformation(transformationService.load(identifier, null)); - } else { - aggregationMappings.stream() - .forEach(aggregationMapping -> runWithTransformation(transformationService.load(identifier, aggregationMapping), - aggregationMapping)); - } + getScopesWithoutConfiguredTransformations().forEach(this::runWithDefaultTransformation); + runWithConfiguredTransformations(); + } + + Stream<AggregationMapping.Scope> getScopesWithoutConfiguredTransformations() { + return Arrays.stream(AggregationMapping.Scope.values()).filter(this::hasNoConfiguredTransformationsWithScope); + } + + boolean hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope scope) { + return transformationProperties.getAggregationMappings().stream().map(AggregationMapping::getScope).noneMatch(scope::equals); + } + + void runWithDefaultTransformation(AggregationMapping.Scope scope) throws TransformationException { + runWithTransformation(transformationService.load(transformationProperties.getIdentifier(), null), null); } - void runWithDefaultTransformation(Transformation transformation) { - runWithTransformation(transformation, null); + void runWithConfiguredTransformations() { + transformationProperties.getAggregationMappings().forEach(aggregationMapping -> + runWithTransformation(transformationService.load(transformationProperties.getIdentifier(), aggregationMapping), aggregationMapping)); } void runWithTransformation(Transformation transformation, AggregationMapping aggregationMapping) { diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java index aadc1ca..25c70ca 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/TransformationProperties.java @@ -23,15 +23,15 @@ */ package de.ozgcloud.aggregation; +import java.util.ArrayList; import java.util.List; -import jakarta.validation.Valid; - import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.validation.annotation.Validated; import de.ozgcloud.aggregation.transformation.AggregationMapping; +import jakarta.validation.Valid; import lombok.Getter; import lombok.Setter; import lombok.extern.log4j.Log4j2; @@ -49,7 +49,7 @@ public class TransformationProperties { * field mappings should be applied */ @Valid - private List<AggregationMapping> aggregationMappings; + private List<AggregationMapping> aggregationMappings = new ArrayList<>(); /* * mapping definition for the entry id diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java index ef2a1f2..b4c365f 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java @@ -27,13 +27,16 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.util.Collections; import java.util.List; +import java.util.stream.Stream; import org.apache.commons.lang3.RandomUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.AdditionalMatchers; import org.mockito.ArgumentMatcher; import org.mockito.InjectMocks; import org.mockito.Mock; @@ -72,129 +75,171 @@ class AggregationManagerRunnerTest { @Nested class TestRun { - private final String identifier = LoremIpsum.getInstance().getWords(1); @BeforeEach - void mock() { - when(transformationProperties.getIdentifier()).thenReturn(identifier); - doNothing().when(runner).runWithTransformation(any(), any()); + void init() { + doNothing().when(runner).runWithDefaultTransformation(any()); + doNothing().when(runner).runWithConfiguredTransformations(); } @Test - void shouldGetAggregationMappings() { + void shouldGetScopesWithoutConfiguredTransformations() { runner.run(); - verify(transformationProperties).getAggregationMappings(); + verify(runner).getScopesWithoutConfiguredTransformations(); } - @Test - void shouldGetIdentifier() { + @ParameterizedTest + @EnumSource + void shouldRunWithDefaultTransformationForScopesWithoutConfiguredOnes(AggregationMapping.Scope scope) { + doReturn(Stream.of(scope)).when(runner).getScopesWithoutConfiguredTransformations(); + runner.run(); - verify(transformationProperties).getIdentifier(); + verify(runner).runWithDefaultTransformation(scope); } - @Nested - class TestOnAggregationMappingConfigured { - private final List<AggregationMapping> aggregationMappings = List.of(AggregationMappingTestFactory.create(), - AggregationMappingTestFactory.create()); - - @Mock - private Transformation firstTransformation; - @Mock - private Transformation secondTransformation; - - @BeforeEach - void mock() { - when(transformationProperties.getAggregationMappings()).thenReturn(aggregationMappings); - when(transformationService.load(any(), any())).thenReturn(firstTransformation, secondTransformation); - } + @Test + void shouldRunWithConfiguredTransformations() { + runner.run(); - @Test - void shouldLoadTransformationForEachAggregationMapping() { - runner.run(); + verify(runner).runWithConfiguredTransformations(); + } + } - aggregationMappings.forEach(mapping -> verify(transformationService).load(identifier, mapping)); - } + @Nested + class TestGetScopesWithoutConfiguredTransformations { - @Test - void shouldRunWithTransformationForEachTransformation() { - runner.run(); + @ParameterizedTest + @EnumSource + void shouldCheckForConfiguredTransformationsWithScope(AggregationMapping.Scope scope) { + runner.getScopesWithoutConfiguredTransformations().toList(); - verify(runner).runWithTransformation(firstTransformation, aggregationMappings.get(0)); - verify(runner).runWithTransformation(secondTransformation, aggregationMappings.get(1)); - } + verify(runner).hasNoConfiguredTransformationsWithScope(scope); } - @Nested - class TestOnAggregationMappingsNull { - @Mock - private Transformation transformation; + @ParameterizedTest + @EnumSource + void shouldReturnScopes(AggregationMapping.Scope scope) { + doReturn(true).when(runner).hasNoConfiguredTransformationsWithScope(scope); + doReturn(false).when(runner).hasNoConfiguredTransformationsWithScope(AdditionalMatchers.not(eq(scope))); - @BeforeEach - void mock() { - when(transformationProperties.getAggregationMappings()).thenReturn(null); - when(transformationService.load(any(), any())).thenReturn(transformation); - } + var scopes = runner.getScopesWithoutConfiguredTransformations(); - @Test - void shouldLoadTransformation() { - runner.run(); + assertThat(scopes).containsExactly(scope); + } + } - verify(transformationService).load(identifier, null); - } + @Nested + class TestHasNoConfiguredTransformationsWithScope { - @Test - void shouldCallRunWithDefaultTransformation() { - runner.run(); + @Test + void shouldGetAggregationMappings() { + runner.hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope.INTERN); - verify(runner).runWithDefaultTransformation(transformation); - } + verify(transformationProperties).getAggregationMappings(); } - @Nested - class TestOnAggregationMappingsEmpty { - @Mock - private Transformation transformation; + @Test + void shouldReturnFalseIfThereAreConfiguredTransformations() { + when(transformationProperties.getAggregationMappings()).thenReturn(List.of(AggregationMappingTestFactory.create())); - @BeforeEach - void mock() { - when(transformationProperties.getAggregationMappings()).thenReturn(Collections.emptyList()); - when(transformationService.load(any(), any())).thenReturn(transformation); - } + var hasNoTransformations = runner.hasNoConfiguredTransformationsWithScope(AggregationMappingTestFactory.SCOPE); - @Test - void shouldLoadTransformation() { - runner.run(); + assertThat(hasNoTransformations).isFalse(); + } - verify(transformationService).load(identifier, null); - } + @Test + void shouldReturnTrueIfThereAreNoConfiguredTransformations() { + when(transformationProperties.getAggregationMappings()).thenReturn(List.of(AggregationMappingTestFactory.createBuilder().scope( + AggregationMapping.Scope.INTERN).build())); - @Test - void shouldCallRunWithDefaultTransformation() { - runner.run(); + var hasNoTransformations = runner.hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope.EXTERN); - verify(runner).runWithDefaultTransformation(transformation); - } + assertThat(hasNoTransformations).isTrue(); } } @Nested class TestRunWithDefaultTransformation { + private final String identifier = LoremIpsum.getInstance().getWords(1); + @Mock private Transformation transformation; - @Test - void shouldCallRunWithTransformation() { + @BeforeEach + void init() { + when(transformationProperties.getIdentifier()).thenReturn(identifier); + when(transformationService.load(any(), any())).thenReturn(transformation); doNothing().when(runner).runWithTransformation(any(), any()); + } + + @Test + void shouldGetIdentifier() { + runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); - runner.runWithDefaultTransformation(transformation); + verify(transformationProperties).getIdentifier(); + } + + @Test + void shouldLoadTransformation() { + runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); + + verify(transformationService).load(identifier, null); + } + + @Test + void shouldRunWithTransformation() { + runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); verify(runner).runWithTransformation(eq(transformation), isNull()); } } + @Nested + class TestRunWithConfiguredTransformations { + + private final String identifier = LoremIpsum.getInstance().getWords(1); + private final List<AggregationMapping> aggregationMappings = List.of(AggregationMappingTestFactory.create(), + AggregationMappingTestFactory.create()); + + @Mock + private Transformation firstTransformation; + @Mock + private Transformation secondTransformation; + + @BeforeEach + void mock() { + when(transformationProperties.getAggregationMappings()).thenReturn(aggregationMappings); + when(transformationProperties.getIdentifier()).thenReturn(identifier); + when(transformationService.load(any(), any())).thenReturn(firstTransformation, secondTransformation); + doNothing().when(runner).runWithTransformation(any(), any()); + } + + @Test + void shouldGetIdentifier() { + runner.runWithConfiguredTransformations(); + + verify(transformationProperties, times(2)).getIdentifier(); + } + + @Test + void shouldLoadTransformationForEachAggregationMapping() { + runner.runWithConfiguredTransformations(); + + aggregationMappings.forEach(mapping -> verify(transformationService).load(identifier, mapping)); + } + + @Test + void shouldRunWithTransformationForEachTransformation() { + runner.runWithConfiguredTransformations(); + + verify(runner).runWithTransformation(firstTransformation, aggregationMappings.get(0)); + verify(runner).runWithTransformation(secondTransformation, aggregationMappings.get(1)); + } + } + @Nested class TestRunWithTransformation { diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java index 44a5c25..8986c79 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/transformation/AggregationMappingTestFactory.java @@ -34,6 +34,7 @@ public class AggregationMappingTestFactory { public static final FormIdentifier FORM_IDENTIFIER = FormIdentifierTestFactory.create(); public static final FieldMapping MAPPING = FieldMappingTestFactory.create(); public static final String NAME = LoremIpsum.getInstance().getWords(1); + public static final AggregationMapping.Scope SCOPE = AggregationMapping.Scope.INTERN; public static AggregationMapping create() { return createBuilder().build(); @@ -43,6 +44,7 @@ public class AggregationMappingTestFactory { return AggregationMapping.builder() .formIdentifier(FORM_IDENTIFIER) .fieldMapping(MAPPING) - .name(NAME); + .name(NAME) + .scope(SCOPE); } } -- GitLab From 2ae5aca28fcdf7d87bb355be34213268961f9bdc Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Mon, 28 Apr 2025 15:01:55 +0200 Subject: [PATCH 08/14] OZG-7811 OZG-8099 Pass Loader to Aggregator --- .../aggregation/AggregationDataLoader.java | 4 + .../AggregationDataLoaderRegistry.java | 63 +++++++ .../aggregation/AggregationManagerRunner.java | 25 +-- .../AggregationWarehouseDataLoader.java | 10 +- .../extern/AggregationRemoteDataLoader.java | 11 +- .../AggregationDataLoaderRegistryTest.java | 126 ++++++++++++++ .../AggregationManagerRunnerTest.java | 158 ++++++++++++------ .../AggregationWarehouseDataLoaderTest.java | 21 +++ .../AggregationRemoteDataLoaderTest.java | 22 +++ 9 files changed, 369 insertions(+), 71 deletions(-) create mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java index 0bd3bd6..f116b96 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java @@ -23,7 +23,11 @@ */ package de.ozgcloud.aggregation; +import de.ozgcloud.aggregation.transformation.AggregationMapping; + public interface AggregationDataLoader { void loadIntoTarget(Aggregation aggregation); + + boolean supportsScope(AggregationMapping.Scope scope); } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java new file mode 100644 index 0000000..363333b --- /dev/null +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import javax.annotation.PostConstruct; + +import org.springframework.stereotype.Component; + +import de.ozgcloud.aggregation.transformation.AggregationMapping; +import de.ozgcloud.common.errorhandling.TechnicalException; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +class AggregationDataLoaderRegistry { + + private final List<AggregationDataLoader> loaders; + private final Map<AggregationMapping.Scope, AggregationDataLoader> scopeToLoader = new HashMap<>(); + + @PostConstruct + void buildLoadersMap() { + + } + + public boolean hasLoader(AggregationMapping.Scope scope) { + return loaders.stream().anyMatch(supportsScope(scope)); + } + + public AggregationDataLoader getLoader(AggregationMapping.Scope scope) { + return loaders.stream().filter(supportsScope(scope)).findFirst() + .orElseThrow(() -> new TechnicalException("No loader found for scope " + scope)); + } + + private Predicate<AggregationDataLoader> supportsScope(AggregationMapping.Scope scope) { + return loader -> loader.supportsScope(scope); + } +} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java index f3b4730..9debd13 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java @@ -52,6 +52,7 @@ public abstract class AggregationManagerRunner implements CommandLineRunner { private final AggregationManagerConfiguration config; private final TransformationProperties transformationProperties; private final TransformationService transformationService; + private final AggregationDataLoaderRegistry loaderRegistry; @Override public void run(String... args) throws TransformationException { @@ -60,36 +61,38 @@ public abstract class AggregationManagerRunner implements CommandLineRunner { } Stream<AggregationMapping.Scope> getScopesWithoutConfiguredTransformations() { - return Arrays.stream(AggregationMapping.Scope.values()).filter(this::hasNoConfiguredTransformationsWithScope); + return Arrays.stream(AggregationMapping.Scope.values()).filter(loaderRegistry::hasLoader).filter(this::hasNoConfiguredTransformations); } - boolean hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope scope) { + boolean hasNoConfiguredTransformations(AggregationMapping.Scope scope) { return transformationProperties.getAggregationMappings().stream().map(AggregationMapping::getScope).noneMatch(scope::equals); } void runWithDefaultTransformation(AggregationMapping.Scope scope) throws TransformationException { - runWithTransformation(transformationService.load(transformationProperties.getIdentifier(), null), null); + runWithTransformation( + transformationService.load(transformationProperties.getIdentifier(), null), + createAggregator(scope)); } void runWithConfiguredTransformations() { - transformationProperties.getAggregationMappings().forEach(aggregationMapping -> - runWithTransformation(transformationService.load(transformationProperties.getIdentifier(), aggregationMapping), aggregationMapping)); + transformationProperties.getAggregationMappings().forEach(aggregationMapping -> runWithTransformation( + transformationService.load(transformationProperties.getIdentifier(), aggregationMapping), + createAggregator(aggregationMapping.getScope()).withAggregationMapping(aggregationMapping))); } - void runWithTransformation(Transformation transformation, AggregationMapping aggregationMapping) { + void runWithTransformation(Transformation transformation, Aggregator aggregator) { try (Execution execution = new Execution(transformation)) { ThreadContext.put(MDC_EXECUTION, execution.id.toString()); - prepareAggregator(execution, aggregationMapping).aggregate(); + aggregator.withExecution(execution).aggregate(); } finally { ThreadContext.remove(MDC_EXECUTION); } } - Aggregator prepareAggregator(Execution execution, AggregationMapping aggregationMapping) { + Aggregator createAggregator(AggregationMapping.Scope scope) { return createAggregator() - .withExecution(execution) - .withAggregationMapping(aggregationMapping) - .withBatchSize(config.getFetchingBatchSize()); + .withBatchSize(config.getFetchingBatchSize()) + .withLoader(loaderRegistry.getLoader(scope)); } @Lookup diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java index 1873800..5694975 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java @@ -24,19 +24,18 @@ package de.ozgcloud.aggregation; import org.apache.commons.lang3.StringUtils; -import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component -@Scope("prototype") @RequiredArgsConstructor @Log4j2 -class AggregationWarehouseDataLoader implements AggregationDataLoader { +public class AggregationWarehouseDataLoader implements AggregationDataLoader { private final WarehouseRepository repository; @@ -50,4 +49,9 @@ class AggregationWarehouseDataLoader implements AggregationDataLoader { String getCollectionName(Aggregation aggregation) { return StringUtils.isNotBlank(aggregation.aggregationName()) ? aggregation.aggregationName() : DocumentEntry.COLLECTION; } + + @Override + public boolean supportsScope(AggregationMapping.Scope scope) { + return AggregationMapping.Scope.INTERN.equals(scope); + } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java index 92b7b99..29ed0db 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java @@ -25,20 +25,21 @@ package de.ozgcloud.aggregation.extern; import java.util.concurrent.ExecutionException; -import org.springframework.context.annotation.Scope; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component -@Scope("prototype") +@ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor @Log4j2 -class AggregationRemoteDataLoader implements AggregationDataLoader { +public class AggregationRemoteDataLoader implements AggregationDataLoader { private final AggregationDataRemoteService service; @@ -53,4 +54,8 @@ class AggregationRemoteDataLoader implements AggregationDataLoader { throw new TechnicalException("Error on sending aggregation data.", e); } } + + public boolean supportsScope(AggregationMapping.Scope scope) { + return AggregationMapping.Scope.EXTERN.equals(scope); + } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java new file mode 100644 index 0000000..cd7127d --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; + +import de.ozgcloud.aggregation.transformation.AggregationMapping; +import de.ozgcloud.common.errorhandling.TechnicalException; +import de.ozgcloud.common.test.ReflectionTestUtils; + +class AggregationDataLoaderRegistryTest { + + @Mock + private AggregationDataLoader loader; + private AggregationDataLoaderRegistry registry; + + @BeforeEach + void init() { + registry = spy(new AggregationDataLoaderRegistry(List.of(loader))); + } + + @Nested + class TestBuildLoadersMap { + + @Test + void shouldAddLoader() { + } + + @SuppressWarnings("unchecked") + private Map<AggregationMapping.Scope, AggregationDataLoader> getScopeToLoader() { + return ReflectionTestUtils.getField(registry, "scopeToLoader", Map.class); + } + } + + @Nested + class TestHasLoader { + + @Test + void shouldCheckIfLoaderSupportsScope() { + var scope = AggregationMapping.Scope.INTERN; + + registry.hasLoader(scope); + + verify(loader).supportsScope(scope); + } + + @Test + void shouldReturnFalseIfNoLoaderRegisteredForScope() { + when(loader.supportsScope(any())).thenReturn(false); + + var hasLoader = registry.hasLoader(AggregationMapping.Scope.INTERN); + + assertThat(hasLoader).isFalse(); + } + + @Test + void shouldReturnTrueIfLoaderRegisteredForScope() { + when(loader.supportsScope(any())).thenReturn(true); + + var hasLoader = registry.hasLoader(AggregationMapping.Scope.INTERN); + + assertThat(hasLoader).isTrue(); + } + } + + @Nested + class TestGetLoader { + + private final AggregationMapping.Scope scope = AggregationMapping.Scope.INTERN; + + @Test + void shouldCheckIfLoaderSupportsScope() { + when(loader.supportsScope(any())).thenReturn(true); + + registry.getLoader(scope); + + verify(loader).supportsScope(scope); + } + + @Test + void shouldReturnLoaderThatSupportsScope() { + when(loader.supportsScope(any())).thenReturn(true); + + var loaderSupportingScope = registry.getLoader(scope); + + assertThat(loaderSupportingScope).isSameAs(loader); + } + + @Test + void shouldThrowExceptionIfNoLoaderRegisteredForScope() { + when(loader.supportsScope(any())).thenReturn(false); + + assertThatThrownBy(() -> registry.getLoader(scope)).isInstanceOf(TechnicalException.class); + } + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java index b4c365f..a494e09 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java @@ -49,14 +49,9 @@ import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.aggregation.transformation.AggregationMappingTestFactory; import de.ozgcloud.aggregation.transformation.Transformation; import de.ozgcloud.aggregation.transformation.TransformationService; -import de.ozgcloud.aggregation.transformation.VorgangMapper; -import de.ozgcloud.aggregation.warehouse.WarehouseRepository; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; class AggregationManagerRunnerTest { - @Mock - private OzgCloudVorgangService vorgangService; @Mock private AggregationManagerConfiguration config; @Mock @@ -64,9 +59,7 @@ class AggregationManagerRunnerTest { @Mock private TransformationService transformationService; @Mock - private WarehouseRepository repository; - @Mock - private VorgangMapper vorgangMapper; + private AggregationDataLoaderRegistry loaderRegistry; @Mock private static Aggregator aggregator; @Spy @@ -78,7 +71,6 @@ class AggregationManagerRunnerTest { @BeforeEach void init() { - doNothing().when(runner).runWithDefaultTransformation(any()); doNothing().when(runner).runWithConfiguredTransformations(); } @@ -93,6 +85,7 @@ class AggregationManagerRunnerTest { @EnumSource void shouldRunWithDefaultTransformationForScopesWithoutConfiguredOnes(AggregationMapping.Scope scope) { doReturn(Stream.of(scope)).when(runner).getScopesWithoutConfiguredTransformations(); + doNothing().when(runner).runWithDefaultTransformation(any()); runner.run(); @@ -110,32 +103,53 @@ class AggregationManagerRunnerTest { @Nested class TestGetScopesWithoutConfiguredTransformations { + @ParameterizedTest + @EnumSource + void shouldCheckForLoader(AggregationMapping.Scope scope) { + runner.getScopesWithoutConfiguredTransformations().toList(); + + verify(loaderRegistry).hasLoader(scope); + } + @ParameterizedTest @EnumSource void shouldCheckForConfiguredTransformationsWithScope(AggregationMapping.Scope scope) { + when(loaderRegistry.hasLoader(any())).thenReturn(true); + runner.getScopesWithoutConfiguredTransformations().toList(); - verify(runner).hasNoConfiguredTransformationsWithScope(scope); + verify(runner).hasNoConfiguredTransformations(scope); } @ParameterizedTest @EnumSource - void shouldReturnScopes(AggregationMapping.Scope scope) { - doReturn(true).when(runner).hasNoConfiguredTransformationsWithScope(scope); - doReturn(false).when(runner).hasNoConfiguredTransformationsWithScope(AdditionalMatchers.not(eq(scope))); + void shouldReturnScopesWithoutTransformations(AggregationMapping.Scope scope) { + doReturn(true).when(runner).hasNoConfiguredTransformations(scope); + doReturn(false).when(runner).hasNoConfiguredTransformations(AdditionalMatchers.not(eq(scope))); + when(loaderRegistry.hasLoader(any())).thenReturn(true); var scopes = runner.getScopesWithoutConfiguredTransformations(); assertThat(scopes).containsExactly(scope); } + + @Test + void shouldOmitScopesWithoutLoaders() { + lenient().doReturn(true).when(runner).hasNoConfiguredTransformations(any()); + when(loaderRegistry.hasLoader(any())).thenReturn(false); + + var scopes = runner.getScopesWithoutConfiguredTransformations(); + + assertThat(scopes).isEmpty(); + } } @Nested - class TestHasNoConfiguredTransformationsWithScope { + class TestHasNoConfiguredTransformations { @Test void shouldGetAggregationMappings() { - runner.hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope.INTERN); + runner.hasNoConfiguredTransformations(AggregationMapping.Scope.INTERN); verify(transformationProperties).getAggregationMappings(); } @@ -144,7 +158,7 @@ class AggregationManagerRunnerTest { void shouldReturnFalseIfThereAreConfiguredTransformations() { when(transformationProperties.getAggregationMappings()).thenReturn(List.of(AggregationMappingTestFactory.create())); - var hasNoTransformations = runner.hasNoConfiguredTransformationsWithScope(AggregationMappingTestFactory.SCOPE); + var hasNoTransformations = runner.hasNoConfiguredTransformations(AggregationMappingTestFactory.SCOPE); assertThat(hasNoTransformations).isFalse(); } @@ -154,7 +168,7 @@ class AggregationManagerRunnerTest { when(transformationProperties.getAggregationMappings()).thenReturn(List.of(AggregationMappingTestFactory.createBuilder().scope( AggregationMapping.Scope.INTERN).build())); - var hasNoTransformations = runner.hasNoConfiguredTransformationsWithScope(AggregationMapping.Scope.EXTERN); + var hasNoTransformations = runner.hasNoConfiguredTransformations(AggregationMapping.Scope.EXTERN); assertThat(hasNoTransformations).isTrue(); } @@ -163,6 +177,7 @@ class AggregationManagerRunnerTest { @Nested class TestRunWithDefaultTransformation { + private final AggregationMapping.Scope scope = AggregationMapping.Scope.INTERN; private final String identifier = LoremIpsum.getInstance().getWords(1); @Mock @@ -172,42 +187,58 @@ class AggregationManagerRunnerTest { void init() { when(transformationProperties.getIdentifier()).thenReturn(identifier); when(transformationService.load(any(), any())).thenReturn(transformation); + doReturn(aggregator).when(runner).createAggregator(any()); doNothing().when(runner).runWithTransformation(any(), any()); } @Test void shouldGetIdentifier() { - runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); + runner.runWithDefaultTransformation(scope); verify(transformationProperties).getIdentifier(); } @Test void shouldLoadTransformation() { - runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); + runner.runWithDefaultTransformation(scope); verify(transformationService).load(identifier, null); } + @Test + void shouldCreateAggregatorForScope() { + runner.runWithDefaultTransformation(scope); + + verify(runner).createAggregator(scope); + } + @Test void shouldRunWithTransformation() { - runner.runWithDefaultTransformation(AggregationMapping.Scope.INTERN); + runner.runWithDefaultTransformation(scope); - verify(runner).runWithTransformation(eq(transformation), isNull()); + verify(runner).runWithTransformation(transformation, aggregator); } } @Nested class TestRunWithConfiguredTransformations { - private final String identifier = LoremIpsum.getInstance().getWords(1); - private final List<AggregationMapping> aggregationMappings = List.of(AggregationMappingTestFactory.create(), - AggregationMappingTestFactory.create()); - + private final AggregationMapping.Scope firstScope = AggregationMapping.Scope.INTERN; @Mock private Transformation firstTransformation; @Mock + private Aggregator firstAggregator; + + private final AggregationMapping.Scope secondScope = AggregationMapping.Scope.EXTERN; + @Mock private Transformation secondTransformation; + @Mock + private Aggregator secondAggregator; + + private final String identifier = LoremIpsum.getInstance().getWords(1); + private final List<AggregationMapping> aggregationMappings = List.of( + AggregationMappingTestFactory.createBuilder().scope(firstScope).build(), + AggregationMappingTestFactory.createBuilder().scope(secondScope).build()); @BeforeEach void mock() { @@ -215,6 +246,10 @@ class AggregationManagerRunnerTest { when(transformationProperties.getIdentifier()).thenReturn(identifier); when(transformationService.load(any(), any())).thenReturn(firstTransformation, secondTransformation); doNothing().when(runner).runWithTransformation(any(), any()); + doReturn(firstAggregator).when(runner).createAggregator(firstScope); + doReturn(secondAggregator).when(runner).createAggregator(secondScope); + when(firstAggregator.withAggregationMapping(any())).thenReturn(firstAggregator); + when(secondAggregator.withAggregationMapping(any())).thenReturn(secondAggregator); } @Test @@ -231,12 +266,28 @@ class AggregationManagerRunnerTest { aggregationMappings.forEach(mapping -> verify(transformationService).load(identifier, mapping)); } + @Test + void shouldCreateAggregatorForScope() { + runner.runWithConfiguredTransformations(); + + verify(runner).createAggregator(firstScope); + verify(runner).createAggregator(secondScope); + } + + @Test + void shouldSetAggregationMappingInAggregator() { + runner.runWithConfiguredTransformations(); + + verify(firstAggregator).withAggregationMapping(aggregationMappings.getFirst()); + verify(secondAggregator).withAggregationMapping(aggregationMappings.getLast()); + } + @Test void shouldRunWithTransformationForEachTransformation() { runner.runWithConfiguredTransformations(); - verify(runner).runWithTransformation(firstTransformation, aggregationMappings.get(0)); - verify(runner).runWithTransformation(secondTransformation, aggregationMappings.get(1)); + verify(runner).runWithTransformation(firstTransformation, firstAggregator); + verify(runner).runWithTransformation(secondTransformation, secondAggregator); } } @@ -245,19 +296,18 @@ class AggregationManagerRunnerTest { @Mock private Transformation transformation; - private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); private final ArgumentMatcher<Execution> hasTransformation = execution -> execution.getTransformation().equals(transformation); @BeforeEach - void mock() { - doReturn(aggregator).when(runner).prepareAggregator(any(), any()); + void init() { + when(aggregator.withExecution(any())).thenReturn(aggregator); } @Test - void shouldCallPrepareAggregator() { + void shouldSetExecution() { runWithTransformation(); - verify(runner).prepareAggregator(argThat(hasTransformation), eq(aggregationMapping)); + verify(aggregator).withExecution(argThat(hasTransformation)); } @Test @@ -268,67 +318,67 @@ class AggregationManagerRunnerTest { } private void runWithTransformation() { - runner.runWithTransformation(transformation, aggregationMapping); + runner.runWithTransformation(transformation, aggregator); } } @Nested - class TestPrepareAggregator { + class TestCreateAggregator { - @Mock - private Execution execution; - private final AggregationMapping aggregationMapping = AggregationMappingTestFactory.create(); + private final AggregationMapping.Scope scope = AggregationMapping.Scope.INTERN; private final int batchSize = RandomUtils.insecure().randomInt(); + @Mock + private AggregationDataLoader loader; @BeforeEach - void mock() { + void init() { when(config.getFetchingBatchSize()).thenReturn(batchSize); - when(aggregator.withExecution(any())).thenReturn(aggregator); - when(aggregator.withAggregationMapping(any())).thenReturn(aggregator); + when(loaderRegistry.getLoader(any())).thenReturn(loader); when(aggregator.withBatchSize(anyInt())).thenReturn(aggregator); + when(aggregator.withLoader(any())).thenReturn(aggregator); } @Test void shouldGetBatchSize() { - runner.prepareAggregator(execution, aggregationMapping); + runner.createAggregator(scope); verify(config).getFetchingBatchSize(); } @Test - void shouldSetExecution() { - runner.prepareAggregator(execution, aggregationMapping); + void shouldSetBatchSize() { + runner.createAggregator(scope); - verify(aggregator).withExecution(execution); + verify(aggregator).withBatchSize(batchSize); } @Test - void shouldSetAggregationMapping() { - runner.prepareAggregator(execution, aggregationMapping); + void shouldGetLoaderFromRegistry() { + runner.createAggregator(scope); - verify(aggregator).withAggregationMapping(aggregationMapping); + verify(loaderRegistry).getLoader(scope); } @Test - void shouldSetBatchSize() { - runner.prepareAggregator(execution, aggregationMapping); + void shouldSetLoader() { + runner.createAggregator(scope); - verify(aggregator).withBatchSize(batchSize); + verify(aggregator).withLoader(loader); } @Test void shouldReturnAggregator() { - var result = runner.prepareAggregator(execution, aggregationMapping); + var created = runner.createAggregator(scope); - assertThat(result).isEqualTo(aggregator); + assertThat(created).isEqualTo(aggregator); } } static class AggregationManagerRunnerImpl extends AggregationManagerRunner { public AggregationManagerRunnerImpl(AggregationManagerConfiguration config, TransformationProperties transformationProperties, - TransformationService transformationService) { - super(config, transformationProperties, transformationService); + TransformationService transformationService, AggregationDataLoaderRegistry loaderRegistry) { + super(config, transformationProperties, transformationService, loaderRegistry); } @Override diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java index 5af0267..27ce28c 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java @@ -32,6 +32,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -41,6 +42,7 @@ import org.mockito.Spy; import com.thedeanda.lorem.LoremIpsum; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; @@ -107,4 +109,23 @@ class AggregationWarehouseDataLoaderTest { assertThat(collectionName).isEqualTo(DocumentEntry.COLLECTION); } } + + @Nested + class TestSupportsScope { + + @ParameterizedTest + @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "INTERN") + void shouldReturnFalse(AggregationMapping.Scope scope) { + var supported = loader.supportsScope(scope); + + assertThat(supported).isFalse(); + } + + @Test + void shouldReturnTrue() { + var supported = loader.supportsScope(AggregationMapping.Scope.INTERN); + + assertThat(supported).isTrue(); + } + } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java index e6db338..e76d39f 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java @@ -34,12 +34,15 @@ import java.util.concurrent.Future; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationTestFactory; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.SneakyThrows; @@ -124,4 +127,23 @@ class AggregationRemoteDataLoaderTest { } } } + + @Nested + class TestSupportsScope { + + @ParameterizedTest + @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "EXTERN") + void shouldReturnFalse(AggregationMapping.Scope scope) { + var supported = loader.supportsScope(scope); + + assertThat(supported).isFalse(); + } + + @Test + void shouldReturnTrue() { + var supported = loader.supportsScope(AggregationMapping.Scope.EXTERN); + + assertThat(supported).isTrue(); + } + } } -- GitLab From 9f5452affb743783d8eed833b1aa34aa998cea69 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Mon, 28 Apr 2025 16:11:21 +0200 Subject: [PATCH 09/14] OZG-7811 OZG-8099 Registration of loaders --- .../aggregation/AggregationDataLoader.java | 4 -- .../AggregationDataLoaderRegistry.java | 23 ++------ .../AggregationWarehouseDataLoader.java | 9 +-- .../extern/AggregationRemoteDataLoader.java | 8 +-- .../AggregationDataLoaderRegistryTest.java | 58 ++++--------------- .../AggregationWarehouseDataLoaderTest.java | 18 ++---- .../AggregationRemoteDataLoaderTest.java | 19 ++---- 7 files changed, 30 insertions(+), 109 deletions(-) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java index f116b96..0bd3bd6 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java @@ -23,11 +23,7 @@ */ package de.ozgcloud.aggregation; -import de.ozgcloud.aggregation.transformation.AggregationMapping; - public interface AggregationDataLoader { void loadIntoTarget(Aggregation aggregation); - - boolean supportsScope(AggregationMapping.Scope scope); } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java index 363333b..4d7e2ff 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java @@ -23,12 +23,8 @@ */ package de.ozgcloud.aggregation; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.function.Predicate; - -import javax.annotation.PostConstruct; +import java.util.Optional; import org.springframework.stereotype.Component; @@ -40,24 +36,13 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor class AggregationDataLoaderRegistry { - private final List<AggregationDataLoader> loaders; - private final Map<AggregationMapping.Scope, AggregationDataLoader> scopeToLoader = new HashMap<>(); - - @PostConstruct - void buildLoadersMap() { - - } + private final Map<String, AggregationDataLoader> loaders; public boolean hasLoader(AggregationMapping.Scope scope) { - return loaders.stream().anyMatch(supportsScope(scope)); + return loaders.containsKey(scope.name()); } public AggregationDataLoader getLoader(AggregationMapping.Scope scope) { - return loaders.stream().filter(supportsScope(scope)).findFirst() - .orElseThrow(() -> new TechnicalException("No loader found for scope " + scope)); - } - - private Predicate<AggregationDataLoader> supportsScope(AggregationMapping.Scope scope) { - return loader -> loader.supportsScope(scope); + return Optional.ofNullable(loaders.get(scope.name())).orElseThrow(() -> new TechnicalException("No data loader for scope " + scope)); } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java index 5694975..51ead0e 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java @@ -24,19 +24,21 @@ package de.ozgcloud.aggregation; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.aggregation.warehouse.DocumentEntry; import de.ozgcloud.aggregation.warehouse.WarehouseRepository; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component +@Qualifier(AggregationWarehouseDataLoader.SCOPE) @RequiredArgsConstructor @Log4j2 public class AggregationWarehouseDataLoader implements AggregationDataLoader { + static final String SCOPE = "INTERN"; private final WarehouseRepository repository; @Override @@ -49,9 +51,4 @@ public class AggregationWarehouseDataLoader implements AggregationDataLoader { String getCollectionName(Aggregation aggregation) { return StringUtils.isNotBlank(aggregation.aggregationName()) ? aggregation.aggregationName() : DocumentEntry.COLLECTION; } - - @Override - public boolean supportsScope(AggregationMapping.Scope scope) { - return AggregationMapping.Scope.INTERN.equals(scope); - } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java index 29ed0db..12a9e4d 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java @@ -25,22 +25,24 @@ package de.ozgcloud.aggregation.extern; import java.util.concurrent.ExecutionException; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationDataLoader; -import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component +@Qualifier(AggregationRemoteDataLoader.SCOPE) @ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor @Log4j2 public class AggregationRemoteDataLoader implements AggregationDataLoader { + static final String SCOPE = "EXTERN"; private final AggregationDataRemoteService service; @Override @@ -54,8 +56,4 @@ public class AggregationRemoteDataLoader implements AggregationDataLoader { throw new TechnicalException("Error on sending aggregation data.", e); } } - - public boolean supportsScope(AggregationMapping.Scope scope) { - return AggregationMapping.Scope.EXTERN.equals(scope); - } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java index cd7127d..02d5b4b 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java @@ -26,7 +26,6 @@ package de.ozgcloud.aggregation; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -36,58 +35,36 @@ import org.mockito.Mock; import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; -import de.ozgcloud.common.test.ReflectionTestUtils; class AggregationDataLoaderRegistryTest { + private static final String SCOPE_KEY = AggregationWarehouseDataLoader.SCOPE; + private final AggregationMapping.Scope registeredScope = AggregationMapping.Scope.INTERN; + private final AggregationMapping.Scope notRegisteredScope = AggregationMapping.Scope.EXTERN; + + @Mock private AggregationDataLoader loader; private AggregationDataLoaderRegistry registry; @BeforeEach void init() { - registry = spy(new AggregationDataLoaderRegistry(List.of(loader))); - } - - @Nested - class TestBuildLoadersMap { - - @Test - void shouldAddLoader() { - } - - @SuppressWarnings("unchecked") - private Map<AggregationMapping.Scope, AggregationDataLoader> getScopeToLoader() { - return ReflectionTestUtils.getField(registry, "scopeToLoader", Map.class); - } + registry = spy(new AggregationDataLoaderRegistry(Map.of(SCOPE_KEY, loader))); } @Nested class TestHasLoader { - @Test - void shouldCheckIfLoaderSupportsScope() { - var scope = AggregationMapping.Scope.INTERN; - - registry.hasLoader(scope); - - verify(loader).supportsScope(scope); - } - @Test void shouldReturnFalseIfNoLoaderRegisteredForScope() { - when(loader.supportsScope(any())).thenReturn(false); - - var hasLoader = registry.hasLoader(AggregationMapping.Scope.INTERN); + var hasLoader = registry.hasLoader(notRegisteredScope); assertThat(hasLoader).isFalse(); } @Test void shouldReturnTrueIfLoaderRegisteredForScope() { - when(loader.supportsScope(any())).thenReturn(true); - - var hasLoader = registry.hasLoader(AggregationMapping.Scope.INTERN); + var hasLoader = registry.hasLoader(registeredScope); assertThat(hasLoader).isTrue(); } @@ -96,31 +73,16 @@ class AggregationDataLoaderRegistryTest { @Nested class TestGetLoader { - private final AggregationMapping.Scope scope = AggregationMapping.Scope.INTERN; - - @Test - void shouldCheckIfLoaderSupportsScope() { - when(loader.supportsScope(any())).thenReturn(true); - - registry.getLoader(scope); - - verify(loader).supportsScope(scope); - } - @Test void shouldReturnLoaderThatSupportsScope() { - when(loader.supportsScope(any())).thenReturn(true); - - var loaderSupportingScope = registry.getLoader(scope); + var loaderSupportingScope = registry.getLoader(registeredScope); assertThat(loaderSupportingScope).isSameAs(loader); } @Test void shouldThrowExceptionIfNoLoaderRegisteredForScope() { - when(loader.supportsScope(any())).thenReturn(false); - - assertThatThrownBy(() -> registry.getLoader(scope)).isInstanceOf(TechnicalException.class); + assertThatThrownBy(() -> registry.getLoader(notRegisteredScope)).isInstanceOf(TechnicalException.class); } } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java index 27ce28c..9cd225c 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java @@ -32,13 +32,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import org.springframework.beans.factory.annotation.Qualifier; import com.thedeanda.lorem.LoremIpsum; @@ -111,21 +111,13 @@ class AggregationWarehouseDataLoaderTest { } @Nested - class TestSupportsScope { - - @ParameterizedTest - @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "INTERN") - void shouldReturnFalse(AggregationMapping.Scope scope) { - var supported = loader.supportsScope(scope); - - assertThat(supported).isFalse(); - } + class TestQualifier { @Test - void shouldReturnTrue() { - var supported = loader.supportsScope(AggregationMapping.Scope.INTERN); + void shouldBeIntern() { + var qualifierValue = AggregationWarehouseDataLoader.class.getAnnotation(Qualifier.class).value(); - assertThat(supported).isTrue(); + assertThat(qualifierValue).isEqualTo(AggregationMapping.Scope.INTERN.name()); } } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java index e76d39f..49e0b07 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java @@ -34,11 +34,10 @@ import java.util.concurrent.Future; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import org.springframework.beans.factory.annotation.Qualifier; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationTestFactory; @@ -129,21 +128,13 @@ class AggregationRemoteDataLoaderTest { } @Nested - class TestSupportsScope { - - @ParameterizedTest - @EnumSource(mode = EnumSource.Mode.EXCLUDE, names = "EXTERN") - void shouldReturnFalse(AggregationMapping.Scope scope) { - var supported = loader.supportsScope(scope); - - assertThat(supported).isFalse(); - } + class TestQualifier { @Test - void shouldReturnTrue() { - var supported = loader.supportsScope(AggregationMapping.Scope.EXTERN); + void shouldBeExtern() { + var qualifierValue = AggregationRemoteDataLoader.class.getAnnotation(Qualifier.class).value(); - assertThat(supported).isTrue(); + assertThat(qualifierValue).isEqualTo(AggregationMapping.Scope.EXTERN.name()); } } } -- GitLab From e0836d6439287859275533abec2e50fe0aa577de Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Tue, 29 Apr 2025 15:24:45 +0200 Subject: [PATCH 10/14] OZG-7811 OZG-8099 Working registration of loaders --- .../aggregation/AggregationDataLoader.java | 4 + .../AggregationDataLoaderRegistry.java | 25 ++++-- .../AggregationManagerGrpcConfiguration.java | 13 +++ .../extern/AggregationDataRemoteService.java | 7 +- .../extern/AggregationRemoteDataLoader.java | 10 ++- .../extern/GrpcAggregationDataMapper.java | 2 +- .../AggregationWarehouseDataLoader.java | 15 ++-- .../AggregationDataLoaderRegistryTest.java | 75 ++++++++++++++-- .../AggregationRemoteDataLoaderTest.java | 9 +- .../extern/SpringContextITCase.java | 88 +++++++++++++++++++ .../AggregationWarehouseDataLoaderTest.java | 15 ++-- .../warehouse/SpringContextITCase.java | 61 +++++++++++++ 12 files changed, 283 insertions(+), 41 deletions(-) rename aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/{ => warehouse}/AggregationWarehouseDataLoader.java (84%) create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java rename aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/{ => warehouse}/AggregationWarehouseDataLoaderTest.java (88%) create mode 100644 aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java index 0bd3bd6..454b0ad 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoader.java @@ -23,7 +23,11 @@ */ package de.ozgcloud.aggregation; +import de.ozgcloud.aggregation.transformation.AggregationMapping; + public interface AggregationDataLoader { void loadIntoTarget(Aggregation aggregation); + + AggregationMapping.Scope getScope(); } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java index 4d7e2ff..614e35a 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistry.java @@ -23,26 +23,39 @@ */ package de.ozgcloud.aggregation; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; import org.springframework.stereotype.Component; import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; -import lombok.RequiredArgsConstructor; @Component -@RequiredArgsConstructor -class AggregationDataLoaderRegistry { +public class AggregationDataLoaderRegistry { - private final Map<String, AggregationDataLoader> loaders; + private final Map<AggregationMapping.Scope, AggregationDataLoader> loadersByScope; + + AggregationDataLoaderRegistry(List<AggregationDataLoader> loaders) { + this.loadersByScope = toLoadersByScope(loaders); + } public boolean hasLoader(AggregationMapping.Scope scope) { - return loaders.containsKey(scope.name()); + return loadersByScope.containsKey(scope); } public AggregationDataLoader getLoader(AggregationMapping.Scope scope) { - return Optional.ofNullable(loaders.get(scope.name())).orElseThrow(() -> new TechnicalException("No data loader for scope " + scope)); + return Optional.ofNullable(loadersByScope.get(scope)).orElseThrow(() -> new TechnicalException("No data loader for scope " + scope)); + } + + static Map<AggregationMapping.Scope, AggregationDataLoader> toLoadersByScope(List<AggregationDataLoader> loaders) { + try { + return loaders.stream().collect(Collectors.toMap(AggregationDataLoader::getScope, Function.identity())); + } catch (IllegalStateException e) { + throw new TechnicalException("Multiple loaders exist for single scope.", e); + } } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java index eec32d5..a9b23c1 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java @@ -23,9 +23,13 @@ */ package de.ozgcloud.aggregation; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; +import de.ozgcloud.aggregation.extern.AggregationDataRemoteService; +import de.ozgcloud.aggregation.extern.GrpcAggregationDataMapper; import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; import de.ozgcloud.apilib.vorgang.grpc.GrpcOzgCloudVorgangService; import de.ozgcloud.apilib.vorgang.grpc.OzgCloudVorgangMapper; @@ -37,9 +41,12 @@ import net.devh.boot.grpc.client.inject.GrpcClientBean; @Configuration @GrpcClientBean(clazz = VorgangServiceGrpc.VorgangServiceBlockingStub.class, beanName = "vorgangServiceBlockingStub", client = @GrpcClient("vorgang-manager")) +@GrpcClientBean(clazz = AggregationDataServiceGrpc.AggregationDataServiceStub.class, beanName = "aggregationDataServiceStub", client = @GrpcClient("aggregation-manager")) public class AggregationManagerGrpcConfiguration { @GrpcClient("vorgang-manager") VorgangServiceGrpc.VorgangServiceBlockingStub vorgangServiceBlockingStub; + @GrpcClient("aggregation-manager") + AggregationDataServiceGrpc.AggregationDataServiceStub aggregationDataServiceStub; @Bean OzgCloudVorgangService grpcOzgCloudVorgangService(OzgCloudVorgangMapper vorgangMapper, @@ -49,4 +56,10 @@ public class AggregationManagerGrpcConfiguration { aggregationCallContext); } + @Bean + @ConditionalOnProperty("grpc.client.aggregation-manager.address") + AggregationDataRemoteService aggregationDataRemoteService(AggregationManagerProperties properties, + AggregationManagerConfiguration configuration, GrpcAggregationDataMapper grpcAggregationDataMapper) { + return new AggregationDataRemoteService(aggregationDataServiceStub, properties, configuration, grpcAggregationDataMapper); + } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index 564df21..531a02c 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -28,9 +28,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Future; import java.util.stream.Stream; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.stereotype.Service; - import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; @@ -46,10 +43,8 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import net.devh.boot.grpc.client.inject.GrpcClient; -@Service -@ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor -class AggregationDataRemoteService { +public class AggregationDataRemoteService { @GrpcClient("aggregation-manager") private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java index 12a9e4d..845c203 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java @@ -25,24 +25,22 @@ package de.ozgcloud.aggregation.extern; import java.util.concurrent.ExecutionException; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component -@Qualifier(AggregationRemoteDataLoader.SCOPE) @ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor @Log4j2 public class AggregationRemoteDataLoader implements AggregationDataLoader { - static final String SCOPE = "EXTERN"; private final AggregationDataRemoteService service; @Override @@ -56,4 +54,10 @@ public class AggregationRemoteDataLoader implements AggregationDataLoader { throw new TechnicalException("Error on sending aggregation data.", e); } } + + @Override + public AggregationMapping.Scope getScope() { + return AggregationMapping.Scope.EXTERN; + } + } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java index d111d82..33bdee4 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/GrpcAggregationDataMapper.java @@ -30,7 +30,7 @@ import de.ozgcloud.aggregation.data.GrpcAggregationData; import de.ozgcloud.aggregation.warehouse.DocumentEntry; @Mapper -interface GrpcAggregationDataMapper { +public interface GrpcAggregationDataMapper { @Mapping(target = "eingangDatum", source = "eingangsdatum") @Mapping(target = "vorgangName", source = "vorgangsname") diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java similarity index 84% rename from aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java rename to aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java index 51ead0e..3e319bf 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java @@ -21,24 +21,22 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.aggregation; +package de.ozgcloud.aggregation.warehouse; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; -import de.ozgcloud.aggregation.warehouse.DocumentEntry; -import de.ozgcloud.aggregation.warehouse.WarehouseRepository; +import de.ozgcloud.aggregation.Aggregation; +import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.aggregation.transformation.AggregationMapping; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component -@Qualifier(AggregationWarehouseDataLoader.SCOPE) @RequiredArgsConstructor @Log4j2 public class AggregationWarehouseDataLoader implements AggregationDataLoader { - static final String SCOPE = "INTERN"; private final WarehouseRepository repository; @Override @@ -51,4 +49,9 @@ public class AggregationWarehouseDataLoader implements AggregationDataLoader { String getCollectionName(Aggregation aggregation) { return StringUtils.isNotBlank(aggregation.aggregationName()) ? aggregation.aggregationName() : DocumentEntry.COLLECTION; } + + @Override + public AggregationMapping.Scope getScope() { + return AggregationMapping.Scope.INTERN; + } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java index 02d5b4b..92cde4b 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationDataLoaderRegistryTest.java @@ -26,30 +26,61 @@ package de.ozgcloud.aggregation; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.Mock; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.mockito.MockedStatic; import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; +import de.ozgcloud.common.test.ReflectionTestUtils; +import lombok.Getter; +import lombok.RequiredArgsConstructor; class AggregationDataLoaderRegistryTest { - private static final String SCOPE_KEY = AggregationWarehouseDataLoader.SCOPE; private final AggregationMapping.Scope registeredScope = AggregationMapping.Scope.INTERN; private final AggregationMapping.Scope notRegisteredScope = AggregationMapping.Scope.EXTERN; - - @Mock - private AggregationDataLoader loader; + private final AggregationDataLoader loader = new TestLoader(registeredScope); private AggregationDataLoaderRegistry registry; @BeforeEach void init() { - registry = spy(new AggregationDataLoaderRegistry(Map.of(SCOPE_KEY, loader))); + registry = spy(new AggregationDataLoaderRegistry(List.of(loader))); + } + + @Nested + class TestConstructor { + + private final List<AggregationDataLoader> loaders = List.of(new TestLoader(AggregationMapping.Scope.INTERN)); + + @Test + void shouldConvertToLoadersByScope() { + try (MockedStatic<AggregationDataLoaderRegistry> mocked = mockStatic(AggregationDataLoaderRegistry.class)) { + new AggregationDataLoaderRegistry(loaders); + mocked.verify(() -> AggregationDataLoaderRegistry.toLoadersByScope(loaders)); + } + } + + @Test + @SuppressWarnings("unchecked") + void setLoadersByScope() { + var loadersByScope = Map.of(AggregationMapping.Scope.INTERN, new TestLoader(AggregationMapping.Scope.INTERN)); + try (MockedStatic<AggregationDataLoaderRegistry> mocked = mockStatic(AggregationDataLoaderRegistry.class)) { + mocked.when(() -> AggregationDataLoaderRegistry.toLoadersByScope(loaders)).thenReturn(loadersByScope); + + AggregationDataLoaderRegistry registry = new AggregationDataLoaderRegistry(loaders); + + var loadersByScopeFieldValue = ReflectionTestUtils.getField(registry, "loadersByScope", Map.class); + assertThat(loadersByScopeFieldValue).isEqualTo(loadersByScope); + } + } } @Nested @@ -85,4 +116,36 @@ class AggregationDataLoaderRegistryTest { assertThatThrownBy(() -> registry.getLoader(notRegisteredScope)).isInstanceOf(TechnicalException.class); } } + + @Nested + class TestToLoadersByScope { + + @ParameterizedTest + @EnumSource + void shouldAddLoader(AggregationMapping.Scope scope) { + var loader = new TestLoader(scope); + var loadersByScope = AggregationDataLoaderRegistry.toLoadersByScope(List.of(loader)); + + assertThat(loadersByScope).containsEntry(scope, loader); + } + + @Test + void shouldThrowExceptionIfMultipleLoadersHaveSameScope() { + var scope = AggregationMapping.Scope.EXTERN; + assertThatThrownBy(() -> AggregationDataLoaderRegistry.toLoadersByScope(List.of(new TestLoader(scope), new TestLoader(scope)))) + .isInstanceOf(TechnicalException.class); + } + } + + @RequiredArgsConstructor + static class TestLoader implements AggregationDataLoader { + + @Getter + private final AggregationMapping.Scope scope; + + @Override + public void loadIntoTarget(Aggregation aggregation) { + + } + } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java index 49e0b07..045febe 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java @@ -37,7 +37,6 @@ import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.springframework.beans.factory.annotation.Qualifier; import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationTestFactory; @@ -128,13 +127,13 @@ class AggregationRemoteDataLoaderTest { } @Nested - class TestQualifier { + class TestGetScope { @Test - void shouldBeExtern() { - var qualifierValue = AggregationRemoteDataLoader.class.getAnnotation(Qualifier.class).value(); + void shouldReturnExternScope() { + var scope = loader.getScope(); - assertThat(qualifierValue).isEqualTo(AggregationMapping.Scope.EXTERN.name()); + assertThat(scope).isEqualTo(AggregationMapping.Scope.EXTERN); } } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java new file mode 100644 index 0000000..2c7e93e --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.extern; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; + +import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.aggregation.AggregationDataLoaderRegistry; +import de.ozgcloud.aggregation.transformation.AggregationMapping; +import de.ozgcloud.common.test.ITCase; + +class SpringContextITCase { + + @Nested + @ITCase + @TestPropertySource(properties = { "grpc.client.aggregation-manager.address=static://127.0.0.1:9090" }) + class OnAggregationManagerAddressSet { + + @Autowired + private List<AggregationDataLoader> loaders; + @Autowired + private AggregationDataLoaderRegistry registry; + + @Test + void shouldHaveOneLoader() { + assertThat(getLoadersWithScopeExtern(loaders)).singleElement().isInstanceOf(AggregationRemoteDataLoader.class); + } + + @Test + void shouldRegister() { + assertThat(registry.hasLoader(AggregationMapping.Scope.EXTERN)).isTrue(); + } + } + + @Nested + @ITCase + class OnAggregationManagerAddressNotSet { + + @Autowired + private List<AggregationDataLoader> loaders; + @Autowired + private AggregationDataLoaderRegistry registry; + + @Test + void shouldNotHaveLoader() { + assertThat(getLoadersWithScopeExtern(loaders)).isEmpty(); + } + + @Test + void shouldNotRegister() { + assertThat(registry.hasLoader(AggregationMapping.Scope.EXTERN)).isFalse(); + } + } + + private List<AggregationDataLoader> getLoadersWithScopeExtern(List<AggregationDataLoader> loaders) { + return loaders.stream() + .filter(loader -> loader.getScope() == AggregationMapping.Scope.EXTERN) + .toList(); + } +} diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java similarity index 88% rename from aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java rename to aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java index 9cd225c..ed86396 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationWarehouseDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java @@ -21,7 +21,7 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.aggregation; +package de.ozgcloud.aggregation.warehouse; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; @@ -38,13 +38,12 @@ import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import org.springframework.beans.factory.annotation.Qualifier; import com.thedeanda.lorem.LoremIpsum; +import de.ozgcloud.aggregation.Aggregation; +import de.ozgcloud.aggregation.AggregationTestFactory; import de.ozgcloud.aggregation.transformation.AggregationMapping; -import de.ozgcloud.aggregation.warehouse.DocumentEntry; -import de.ozgcloud.aggregation.warehouse.WarehouseRepository; class AggregationWarehouseDataLoaderTest { @@ -111,13 +110,13 @@ class AggregationWarehouseDataLoaderTest { } @Nested - class TestQualifier { + class TestGetScope { @Test - void shouldBeIntern() { - var qualifierValue = AggregationWarehouseDataLoader.class.getAnnotation(Qualifier.class).value(); + void shouldReturnInternScope() { + var scope = loader.getScope(); - assertThat(qualifierValue).isEqualTo(AggregationMapping.Scope.INTERN.name()); + assertThat(scope).isEqualTo(AggregationMapping.Scope.INTERN); } } } diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java new file mode 100644 index 0000000..ed88f4f --- /dev/null +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.aggregation.warehouse; + +import static org.assertj.core.api.Assertions.*; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import de.ozgcloud.aggregation.AggregationDataLoader; +import de.ozgcloud.aggregation.AggregationDataLoaderRegistry; +import de.ozgcloud.aggregation.transformation.AggregationMapping; +import de.ozgcloud.common.test.ITCase; + +@ITCase +class SpringContextITCase { + + @Autowired + private List<AggregationDataLoader> loaders; + @Autowired + private AggregationDataLoaderRegistry registry; + + @Test + void shouldHaveLoader() { + assertThat(getLoadersWithScopeIntern(loaders)).singleElement().isInstanceOf(AggregationWarehouseDataLoader.class); + } + + @Test + void shouldRegister() { + assertThat(registry.hasLoader(AggregationMapping.Scope.INTERN)).isTrue(); + } + + private List<AggregationDataLoader> getLoadersWithScopeIntern(List<AggregationDataLoader> loaders) { + return loaders.stream() + .filter(loader -> loader.getScope() == AggregationMapping.Scope.INTERN) + .toList(); + } +} -- GitLab From b6c33db163d85fade99a225cf85c9abe853eb2c7 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Tue, 29 Apr 2025 16:14:11 +0200 Subject: [PATCH 11/14] OZG-7811 OZG-8114 Adjust properties --- .../AggregationManagerConfiguration.java | 38 ------------------- .../AggregationManagerGrpcConfiguration.java | 6 +-- .../AggregationManagerProperties.java | 1 + .../aggregation/AggregationManagerRunner.java | 4 +- .../extern/AggregationDataRemoteService.java | 4 +- .../src/main/resources/application-local.yml | 8 +--- .../src/main/resources/application.yml | 5 ++- .../AggregationManagerRunnerTest.java | 10 ++--- .../AggregationDataRemoteServiceTest.java | 7 +--- .../src/test/resources/application-itcase.yml | 9 ++--- 10 files changed, 22 insertions(+), 70 deletions(-) delete mode 100644 aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerConfiguration.java diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerConfiguration.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerConfiguration.java deleted file mode 100644 index 0054187..0000000 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerConfiguration.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den - * Ministerpräsidenten des Landes Schleswig-Holstein - * Staatskanzlei - * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.aggregation; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -import lombok.Getter; -import lombok.Setter; - -@ConfigurationProperties(prefix = "aggregation-manager") -@Getter -@Setter -public class AggregationManagerConfiguration { - - private int fetchingBatchSize; - -} diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java index a9b23c1..3994100 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerGrpcConfiguration.java @@ -43,6 +43,7 @@ import net.devh.boot.grpc.client.inject.GrpcClientBean; @GrpcClientBean(clazz = VorgangServiceGrpc.VorgangServiceBlockingStub.class, beanName = "vorgangServiceBlockingStub", client = @GrpcClient("vorgang-manager")) @GrpcClientBean(clazz = AggregationDataServiceGrpc.AggregationDataServiceStub.class, beanName = "aggregationDataServiceStub", client = @GrpcClient("aggregation-manager")) public class AggregationManagerGrpcConfiguration { + @GrpcClient("vorgang-manager") VorgangServiceGrpc.VorgangServiceBlockingStub vorgangServiceBlockingStub; @GrpcClient("aggregation-manager") @@ -58,8 +59,7 @@ public class AggregationManagerGrpcConfiguration { @Bean @ConditionalOnProperty("grpc.client.aggregation-manager.address") - AggregationDataRemoteService aggregationDataRemoteService(AggregationManagerProperties properties, - AggregationManagerConfiguration configuration, GrpcAggregationDataMapper grpcAggregationDataMapper) { - return new AggregationDataRemoteService(aggregationDataServiceStub, properties, configuration, grpcAggregationDataMapper); + AggregationDataRemoteService aggregationDataRemoteService(AggregationManagerProperties properties, GrpcAggregationDataMapper grpcAggregationDataMapper) { + return new AggregationDataRemoteService(aggregationDataServiceStub, properties, grpcAggregationDataMapper); } } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java index 4ace2f5..c54be5d 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerProperties.java @@ -39,5 +39,6 @@ import lombok.extern.log4j.Log4j2; @Log4j2 public class AggregationManagerProperties { + private int fetchingBatchSize; private String mandant; } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java index 9debd13..8b0544e 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/AggregationManagerRunner.java @@ -49,7 +49,7 @@ public abstract class AggregationManagerRunner implements CommandLineRunner { private static final String MDC_EXECUTION = "execution"; - private final AggregationManagerConfiguration config; + private final AggregationManagerProperties aggregationManagerProperties; private final TransformationProperties transformationProperties; private final TransformationService transformationService; private final AggregationDataLoaderRegistry loaderRegistry; @@ -91,7 +91,7 @@ public abstract class AggregationManagerRunner implements CommandLineRunner { Aggregator createAggregator(AggregationMapping.Scope scope) { return createAggregator() - .withBatchSize(config.getFetchingBatchSize()) + .withBatchSize(aggregationManagerProperties.getFetchingBatchSize()) .withLoader(loaderRegistry.getLoader(scope)); } diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java index 531a02c..b0b91dc 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteService.java @@ -29,7 +29,6 @@ import java.util.concurrent.Future; import java.util.stream.Stream; import de.ozgcloud.aggregation.Aggregation; -import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; import de.ozgcloud.aggregation.data.GrpcAggregationData; @@ -49,7 +48,6 @@ public class AggregationDataRemoteService { @GrpcClient("aggregation-manager") private final AggregationDataServiceGrpc.AggregationDataServiceStub serviceStub; private final AggregationManagerProperties properties; - private final AggregationManagerConfiguration configuration; private final GrpcAggregationDataMapper grpcAggregationDataMapper; public Future<Void> sendAggregationData(Aggregation aggregation) { @@ -61,7 +59,7 @@ public class AggregationDataRemoteService { SendAggregationDataResponseObserver buildSendAggregationDataResponseObserver(Aggregation aggregation) { var requestData = new RequestData(properties.getMandant(), aggregation.aggregationName(), toGrpcAggregationDataStream(aggregation.documentEntries()).iterator()); - return new SendAggregationDataResponseObserver(configuration.getFetchingBatchSize(), requestData); + return new SendAggregationDataResponseObserver(properties.getFetchingBatchSize(), requestData); } Stream<GrpcAggregationData> toGrpcAggregationDataStream(Stream<DocumentEntry> documentEntries) { diff --git a/aggregation-manager-job/src/main/resources/application-local.yml b/aggregation-manager-job/src/main/resources/application-local.yml index f971794..a038ff2 100644 --- a/aggregation-manager-job/src/main/resources/application-local.yml +++ b/aggregation-manager-job/src/main/resources/application-local.yml @@ -14,10 +14,6 @@ spring: database: aggregation-manager-job ozgcloud: - vorgang-manager: - address: static://127.0.0.1:9090 - negotiationType: PLAINTEXT - -aggregation-manager: - fetching-batch-size: 5 + fetching-batch-size: 5 + mandant: "Landeshauptstadt Kiel" diff --git a/aggregation-manager-job/src/main/resources/application.yml b/aggregation-manager-job/src/main/resources/application.yml index 10ba491..8799164 100644 --- a/aggregation-manager-job/src/main/resources/application.yml +++ b/aggregation-manager-job/src/main/resources/application.yml @@ -39,6 +39,7 @@ spring: application: name: OzgCloud_AggregationManager -aggregation-manager: +ozgcloud: fetching-batch-size: 100 - identifier: "id.value" + aggregation: + identifier: "id.value" diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java index a494e09..7048356 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/AggregationManagerRunnerTest.java @@ -53,7 +53,7 @@ import de.ozgcloud.aggregation.transformation.TransformationService; class AggregationManagerRunnerTest { @Mock - private AggregationManagerConfiguration config; + private AggregationManagerProperties aggregationManagerProperties; @Mock private TransformationProperties transformationProperties; @Mock @@ -332,7 +332,7 @@ class AggregationManagerRunnerTest { @BeforeEach void init() { - when(config.getFetchingBatchSize()).thenReturn(batchSize); + when(aggregationManagerProperties.getFetchingBatchSize()).thenReturn(batchSize); when(loaderRegistry.getLoader(any())).thenReturn(loader); when(aggregator.withBatchSize(anyInt())).thenReturn(aggregator); when(aggregator.withLoader(any())).thenReturn(aggregator); @@ -342,7 +342,7 @@ class AggregationManagerRunnerTest { void shouldGetBatchSize() { runner.createAggregator(scope); - verify(config).getFetchingBatchSize(); + verify(aggregationManagerProperties).getFetchingBatchSize(); } @Test @@ -376,9 +376,9 @@ class AggregationManagerRunnerTest { static class AggregationManagerRunnerImpl extends AggregationManagerRunner { - public AggregationManagerRunnerImpl(AggregationManagerConfiguration config, TransformationProperties transformationProperties, + public AggregationManagerRunnerImpl(AggregationManagerProperties aggregationManagerProperties, TransformationProperties transformationProperties, TransformationService transformationService, AggregationDataLoaderRegistry loaderRegistry) { - super(config, transformationProperties, transformationService, loaderRegistry); + super(aggregationManagerProperties, transformationProperties, transformationService, loaderRegistry); } @Override diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java index c3711cc..8d34461 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteServiceTest.java @@ -46,7 +46,6 @@ import org.mockito.Spy; import com.thedeanda.lorem.LoremIpsum; import de.ozgcloud.aggregation.Aggregation; -import de.ozgcloud.aggregation.AggregationManagerConfiguration; import de.ozgcloud.aggregation.AggregationManagerProperties; import de.ozgcloud.aggregation.AggregationTestFactory; import de.ozgcloud.aggregation.data.AggregationDataServiceGrpc; @@ -65,8 +64,6 @@ class AggregationDataRemoteServiceTest { @Mock private AggregationManagerProperties properties; @Mock - private AggregationManagerConfiguration configuration; - @Mock private GrpcAggregationDataMapper grpcAggregationDataMapper; @InjectMocks @Spy @@ -124,7 +121,7 @@ class AggregationDataRemoteServiceTest { @BeforeEach void init() { doReturn(Stream.of(GrpcAggregationDataTestFactory.create())).when(service).toGrpcAggregationDataStream(any()); - when(configuration.getFetchingBatchSize()).thenReturn(BATCH_SIZE); + when(properties.getFetchingBatchSize()).thenReturn(BATCH_SIZE); } @Test @@ -146,7 +143,7 @@ class AggregationDataRemoteServiceTest { void shouldGetBatchSize() { service.buildSendAggregationDataResponseObserver(AggregationTestFactory.create()); - verify(configuration).getFetchingBatchSize(); + verify(properties).getFetchingBatchSize(); } @Test diff --git a/aggregation-manager-job/src/test/resources/application-itcase.yml b/aggregation-manager-job/src/test/resources/application-itcase.yml index b370e11..c8eef97 100644 --- a/aggregation-manager-job/src/test/resources/application-itcase.yml +++ b/aggregation-manager-job/src/test/resources/application-itcase.yml @@ -10,14 +10,11 @@ spring: banner-mode: "off" ozgcloud: - vorgang-manager: - address: static://127.0.0.1:9090 - negotiationType: PLAINTEXT command: line: runner: enabled: false - -aggregation-manager: + mandant: "Landeshauptstadt Kiel" fetching-batch-size: 2 - identifier: "id.value" + aggregation: + identifier: "id.value" -- GitLab From 3a0d2ee0b5971f60b278596afb1067d7ce8011b1 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Tue, 29 Apr 2025 17:29:04 +0200 Subject: [PATCH 12/14] OZG-7811 OZG-8114 Support mandant-value in helm-template --- .../src/main/helm/templates/cronjob.yaml | 4 ++++ .../src/test/helm/cronjob_env_test.yaml | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/aggregation-manager-job/src/main/helm/templates/cronjob.yaml b/aggregation-manager-job/src/main/helm/templates/cronjob.yaml index f241604..e11eafe 100644 --- a/aggregation-manager-job/src/main/helm/templates/cronjob.yaml +++ b/aggregation-manager-job/src/main/helm/templates/cronjob.yaml @@ -75,6 +75,10 @@ spec: - name: ozgcloud_administration_address value: {{ include "app.getOzgcloudAdministrationAddress" . }} {{- end }} + {{- if .Values.mandant }} + - name: ozgcloud_mandant + value: {{ .Values.mandant }} + {{- end }} {{- with include "app.getCustomList" . }} {{ . | indent 14 }} {{- end }} diff --git a/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml b/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml index 14aad4d..a7aab06 100644 --- a/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml +++ b/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml @@ -129,6 +129,28 @@ tests: name: ozgcloud_administration_address value: http://test-administration:8080 + - it: should not set mandant per default + set: + ozgcloud: + environment: dev + asserts: + - notContains: + path: spec.jobTemplate.spec.template.spec.containers[0].env + content: + name: ozgcloud_mandant + any: true + - it: should set mandant + set: + mandant: dummy mandant value + ozgcloud: + environment: dev + asserts: + - contains: + path: spec.jobTemplate.spec.template.spec.containers[0].env + content: + name: ozgcloud_mandant + value: "dummy mandant value" + - it: should fail template when env not set set: asserts: -- GitLab From 2f8ec313a4459e2279955f471c891609f34a4157 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Wed, 30 Apr 2025 14:22:40 +0200 Subject: [PATCH 13/14] Revert "OZG-7811 OZG-8114 Support mandant-value in helm-template" This reverts commit 3a0d2ee0b5971f60b278596afb1067d7ce8011b1. --- .../src/main/helm/templates/cronjob.yaml | 4 ---- .../src/test/helm/cronjob_env_test.yaml | 22 ------------------- 2 files changed, 26 deletions(-) diff --git a/aggregation-manager-job/src/main/helm/templates/cronjob.yaml b/aggregation-manager-job/src/main/helm/templates/cronjob.yaml index e11eafe..f241604 100644 --- a/aggregation-manager-job/src/main/helm/templates/cronjob.yaml +++ b/aggregation-manager-job/src/main/helm/templates/cronjob.yaml @@ -75,10 +75,6 @@ spec: - name: ozgcloud_administration_address value: {{ include "app.getOzgcloudAdministrationAddress" . }} {{- end }} - {{- if .Values.mandant }} - - name: ozgcloud_mandant - value: {{ .Values.mandant }} - {{- end }} {{- with include "app.getCustomList" . }} {{ . | indent 14 }} {{- end }} diff --git a/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml b/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml index a7aab06..14aad4d 100644 --- a/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml +++ b/aggregation-manager-job/src/test/helm/cronjob_env_test.yaml @@ -129,28 +129,6 @@ tests: name: ozgcloud_administration_address value: http://test-administration:8080 - - it: should not set mandant per default - set: - ozgcloud: - environment: dev - asserts: - - notContains: - path: spec.jobTemplate.spec.template.spec.containers[0].env - content: - name: ozgcloud_mandant - any: true - - it: should set mandant - set: - mandant: dummy mandant value - ozgcloud: - environment: dev - asserts: - - contains: - path: spec.jobTemplate.spec.template.spec.containers[0].env - content: - name: ozgcloud_mandant - value: "dummy mandant value" - - it: should fail template when env not set set: asserts: -- GitLab From cf7b2a4adc606620b190cffc20d42e1c0e646bd0 Mon Sep 17 00:00:00 2001 From: Krzysztof <krzysztof.witukiewicz@mgm-tp.com> Date: Wed, 30 Apr 2025 14:38:28 +0200 Subject: [PATCH 14/14] OZG-7811 OZG-8099 Rename loaders --- ...RemoteDataLoader.java => AggregationDataRemoteLoader.java} | 2 +- ...useDataLoader.java => AggregationDataWarehouseLoader.java} | 2 +- ...taLoaderTest.java => AggregationDataRemoteLoaderTest.java} | 4 ++-- .../de/ozgcloud/aggregation/extern/SpringContextITCase.java | 2 +- ...oaderTest.java => AggregationDataWarehouseLoaderTest.java} | 4 ++-- .../ozgcloud/aggregation/warehouse/SpringContextITCase.java | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/{AggregationRemoteDataLoader.java => AggregationDataRemoteLoader.java} (97%) rename aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/{AggregationWarehouseDataLoader.java => AggregationDataWarehouseLoader.java} (96%) rename aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/{AggregationRemoteDataLoaderTest.java => AggregationDataRemoteLoaderTest.java} (97%) rename aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/{AggregationWarehouseDataLoaderTest.java => AggregationDataWarehouseLoaderTest.java} (97%) diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoader.java similarity index 97% rename from aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java rename to aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoader.java index 845c203..81701ad 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoader.java @@ -39,7 +39,7 @@ import lombok.extern.log4j.Log4j2; @ConditionalOnProperty("grpc.client.aggregation-manager.address") @RequiredArgsConstructor @Log4j2 -public class AggregationRemoteDataLoader implements AggregationDataLoader { +public class AggregationDataRemoteLoader implements AggregationDataLoader { private final AggregationDataRemoteService service; diff --git a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoader.java similarity index 96% rename from aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java rename to aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoader.java index 3e319bf..3ef137a 100644 --- a/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoader.java +++ b/aggregation-manager-job/src/main/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoader.java @@ -35,7 +35,7 @@ import lombok.extern.log4j.Log4j2; @Component @RequiredArgsConstructor @Log4j2 -public class AggregationWarehouseDataLoader implements AggregationDataLoader { +public class AggregationDataWarehouseLoader implements AggregationDataLoader { private final WarehouseRepository repository; diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoaderTest.java similarity index 97% rename from aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java rename to aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoaderTest.java index 045febe..3c7b0b0 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationRemoteDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/AggregationDataRemoteLoaderTest.java @@ -44,13 +44,13 @@ import de.ozgcloud.aggregation.transformation.AggregationMapping; import de.ozgcloud.common.errorhandling.TechnicalException; import lombok.SneakyThrows; -class AggregationRemoteDataLoaderTest { +class AggregationDataRemoteLoaderTest { @Mock private AggregationDataRemoteService remoteService; @Spy @InjectMocks - private AggregationRemoteDataLoader loader; + private AggregationDataRemoteLoader loader; @Nested class TestLoadIntoTarget { diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java index 2c7e93e..b5cd918 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/extern/SpringContextITCase.java @@ -51,7 +51,7 @@ class SpringContextITCase { @Test void shouldHaveOneLoader() { - assertThat(getLoadersWithScopeExtern(loaders)).singleElement().isInstanceOf(AggregationRemoteDataLoader.class); + assertThat(getLoadersWithScopeExtern(loaders)).singleElement().isInstanceOf(AggregationDataRemoteLoader.class); } @Test diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoaderTest.java similarity index 97% rename from aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java rename to aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoaderTest.java index ed86396..1348836 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationWarehouseDataLoaderTest.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/AggregationDataWarehouseLoaderTest.java @@ -45,13 +45,13 @@ import de.ozgcloud.aggregation.Aggregation; import de.ozgcloud.aggregation.AggregationTestFactory; import de.ozgcloud.aggregation.transformation.AggregationMapping; -class AggregationWarehouseDataLoaderTest { +class AggregationDataWarehouseLoaderTest { @Mock private WarehouseRepository repository; @Spy @InjectMocks - private AggregationWarehouseDataLoader loader; + private AggregationDataWarehouseLoader loader; @Nested class TestLoadIntoTarget { diff --git a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java index ed88f4f..5504e78 100644 --- a/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java +++ b/aggregation-manager-job/src/test/java/de/ozgcloud/aggregation/warehouse/SpringContextITCase.java @@ -45,7 +45,7 @@ class SpringContextITCase { @Test void shouldHaveLoader() { - assertThat(getLoadersWithScopeIntern(loaders)).singleElement().isInstanceOf(AggregationWarehouseDataLoader.class); + assertThat(getLoadersWithScopeIntern(loaders)).singleElement().isInstanceOf(AggregationDataWarehouseLoader.class); } @Test -- GitLab