Skip to content
Snippets Groups Projects
Commit e0836d64 authored by Krzysztof Witukiewicz's avatar Krzysztof Witukiewicz
Browse files

OZG-7811 OZG-8099 Working registration of loaders

parent 9f5452af
Branches
Tags
1 merge request!23Ozg 7811 vorgaenge zur landesebene schicken
Showing
with 283 additions and 41 deletions
......@@ -23,7 +23,11 @@
*/
package de.ozgcloud.aggregation;
import de.ozgcloud.aggregation.transformation.AggregationMapping;
public interface AggregationDataLoader {
void loadIntoTarget(Aggregation aggregation);
AggregationMapping.Scope getScope();
}
......@@ -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);
}
}
}
......@@ -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);
}
}
......@@ -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;
......
......@@ -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;
}
}
......@@ -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")
......
......@@ -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;
}
}
......@@ -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) {
}
}
}
......@@ -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);
}
}
}
/*
* 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();
}
}
......@@ -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);
}
}
}
/*
* 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();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment