diff --git a/common/src/main/java/de/ozgcloud/eingang/Application.java b/common/src/main/java/de/ozgcloud/eingang/Application.java index f03fc325e5c9db727db8020425efdd1e7d2ea0f3..b4ea3d5e2681ff558140f3d8b26a298118f7d87b 100644 --- a/common/src/main/java/de/ozgcloud/eingang/Application.java +++ b/common/src/main/java/de/ozgcloud/eingang/Application.java @@ -33,6 +33,8 @@ import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling public class Application { + public static final String ZUFI_MANAGER_GRPC_CLIENT = "zufi-manager"; + public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); SpringApplication.run(Application.class, args); diff --git a/formsolutions-adapter/src/test/java/de/ozgcloud/eingang/formsolutions/FormsolutionsITCase.java b/formsolutions-adapter/src/test/java/de/ozgcloud/eingang/formsolutions/FormsolutionsITCase.java index 8a2f3c291eb730fa9a0f93b6291bdd2e15dc9555..1024f573ab906e15942513d187b3afda11c8af1e 100644 --- a/formsolutions-adapter/src/test/java/de/ozgcloud/eingang/formsolutions/FormsolutionsITCase.java +++ b/formsolutions-adapter/src/test/java/de/ozgcloud/eingang/formsolutions/FormsolutionsITCase.java @@ -21,6 +21,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.util.ReflectionTestUtils; import de.ozgcloud.common.test.TestUtils; +import de.ozgcloud.eingang.router.ManagableStub; import de.ozgcloud.eingang.router.VorgangManagerServerResolver; import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileRequest; @@ -49,17 +50,23 @@ public class FormsolutionsITCase { @Mock private VorgangServiceBlockingStub blockingStub; @Mock + private ManagableStub<VorgangServiceBlockingStub> managableVorgangServiceBlockingStub; + @Mock private BinaryFileServiceStub fileStub; @Mock + private ManagableStub<BinaryFileServiceStub> managableBinaryFileStub; + @Mock private CallStreamObserver<GrpcUploadBinaryFileRequest> fileStreamObserver; @BeforeEach void initVorgangManagerResolver() { - when(resolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(any())).thenReturn(blockingStub); - when(resolver.resolveBinaryFileServiceStubByOrganisationsEinheitId(any())).thenReturn(fileStub); - + when(resolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(any())).thenReturn(managableVorgangServiceBlockingStub); + when(managableVorgangServiceBlockingStub.get()).thenReturn(blockingStub); + when(resolver.resolveBinaryFileServiceStubByOrganisationsEinheitId(any())).thenReturn(managableBinaryFileStub); + when(managableBinaryFileStub.get()).thenReturn(fileStub); + Channel mockChannel = mock(Channel.class); - when(blockingStub.getChannel()).thenReturn(mockChannel ); + when(blockingStub.getChannel()).thenReturn(mockChannel); when(blockingStub.startCreation(any())).thenReturn(GrpcCreateVorgangResponse.newBuilder().setVorgangId("42").build()); when(fileStub.uploadBinaryFileAsStream(any())).thenReturn(fileStreamObserver); diff --git a/pom.xml b/pom.xml index f171a23df4dd53f4266bb0db5a7c224a560546c9..fc1c3a6522eee55b25c01c4eebb1fd8b8e347e06 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ <properties> <vorgang-manager.version>2.10.0</vorgang-manager.version> + <zufi-manager.version>1.2.0-SNAPSHOT</zufi-manager.version> <jsoup.version>1.14.3</jsoup.version> <xmlschema.version>2.3.0</xmlschema.version> @@ -100,6 +101,11 @@ <artifactId>vorgang-manager-utils</artifactId> <version>${vorgang-manager.version}</version> </dependency> + <dependency> + <groupId>de.ozgcloud.zufi</groupId> + <artifactId>zufi-manager-interface</artifactId> + <version>${zufi-manager.version}</version> + </dependency> <dependency> <groupId>org.jsoup</groupId> diff --git a/router/pom.xml b/router/pom.xml index d8c7fdd6b4307c52a640f0c403e9cfae05e1e78c..bbd0a1666fdd99839c708b2622668fa50910ea08 100644 --- a/router/pom.xml +++ b/router/pom.xml @@ -50,6 +50,10 @@ <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager-utils</artifactId> </dependency> + <dependency> + <groupId>de.ozgcloud.zufi</groupId> + <artifactId>zufi-manager-interface</artifactId> + </dependency> <!-- spring --> <dependency> diff --git a/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheit.java b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheit.java new file mode 100644 index 0000000000000000000000000000000000000000..6b02d54577a0066a14537c931e37eefeecd36591 --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheit.java @@ -0,0 +1,16 @@ +package de.ozgcloud.eingang.common.zufi; + +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +@Builder +class OrganisationsEinheit { + + private String id; + private String name; + private String synonyme; + private String vorgangManagerAddress; +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapper.java b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..ad048c2cdbca5a22ab34c67a678abc50642b4764 --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapper.java @@ -0,0 +1,11 @@ +package de.ozgcloud.eingang.common.zufi; + +import org.mapstruct.Mapper; + +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheit; + +@Mapper +interface OrganisationsEinheitMapper { + + OrganisationsEinheit fromGrpc(GrpcOrganisationsEinheit organisationsEinheit); +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteService.java b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteService.java new file mode 100644 index 0000000000000000000000000000000000000000..d7ec3990817a1649dd2e10c08fd5fd021daa24ea --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteService.java @@ -0,0 +1,24 @@ +package de.ozgcloud.eingang.common.zufi; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import de.ozgcloud.eingang.Application; +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheitGetRequest; +import de.ozgcloud.zufi.grpc.organisationseinheit.OrganisationsEinheitServiceGrpc.OrganisationsEinheitServiceBlockingStub; +import net.devh.boot.grpc.client.inject.GrpcClient; + +@Service +class OrganisationsEinheitRemoteService { + + @GrpcClient(Application.ZUFI_MANAGER_GRPC_CLIENT) + private OrganisationsEinheitServiceBlockingStub serviceStub; + @Autowired + private OrganisationsEinheitMapper mapper; + + public OrganisationsEinheit getById(String id) { + var response = serviceStub.getById(GrpcOrganisationsEinheitGetRequest.newBuilder().setId(id).build()); + + return mapper.fromGrpc(response.getOrganisationsEinheit()); + } +} diff --git a/router/src/main/java/de/ozgcloud/eingang/common/zufi/ZufiService.java b/router/src/main/java/de/ozgcloud/eingang/common/zufi/ZufiService.java new file mode 100644 index 0000000000000000000000000000000000000000..7992033c7b2a7bea338bc8e5a9a68014839cc66d --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/common/zufi/ZufiService.java @@ -0,0 +1,16 @@ +package de.ozgcloud.eingang.common.zufi; + +import org.springframework.stereotype.Service; + +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Service +public class ZufiService { + + private final OrganisationsEinheitRemoteService remoteService; + + public String getVorgangManagerUrl(String organisationsEinheitId) { + return remoteService.getById(organisationsEinheitId).getVorgangManagerAddress(); + } +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/router/ClosableStub.java b/router/src/main/java/de/ozgcloud/eingang/router/ClosableStub.java new file mode 100644 index 0000000000000000000000000000000000000000..ba15eaff4ce83299153a6a766d38c23f42deadc4 --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/router/ClosableStub.java @@ -0,0 +1,44 @@ +package de.ozgcloud.eingang.router; + +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +import de.ozgcloud.eingang.common.errorhandling.TechnicalException; +import io.grpc.ManagedChannel; +import io.grpc.stub.AbstractStub; +import lombok.Builder; + +@Builder +class ClosableStub<T extends AbstractStub<?>> implements ManagableStub<T> { + + private static final int SHUTDOWN_TIME_IN_SEC = 3; + + private T stub; + private ManagedChannel channel; + + @Override + public T get() { + return stub; + } + + public void close() { + if (Objects.nonNull(stub)) { + shutdownChannel(); + stub = null; + } + } + + private void shutdownChannel() { + try { + channel.shutdown().awaitTermination(ClosableStub.SHUTDOWN_TIME_IN_SEC, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new TechnicalException("Error shutting down grpc channel.", e); + } + } + + @Override + public boolean isShutdownable() { + return true; + } +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/router/ConsistentStub.java b/router/src/main/java/de/ozgcloud/eingang/router/ConsistentStub.java new file mode 100644 index 0000000000000000000000000000000000000000..25c543504781b57791c2877b016b09e1d688ad9e --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/router/ConsistentStub.java @@ -0,0 +1,20 @@ +package de.ozgcloud.eingang.router; + +import io.grpc.stub.AbstractStub; +import lombok.Builder; + +@Builder +class ConsistentStub<T extends AbstractStub<?>> implements ManagableStub<T> { + + private T stub; + + @Override + public T get() { + return stub; + } + + @Override + public boolean isShutdownable() { + return false; + } +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/router/GrpcClientsProperties.java b/router/src/main/java/de/ozgcloud/eingang/router/GrpcClientsProperties.java index 3f308dd3d9173e02b78ec94966f615ea9398bd98..376bdc14c6f6a70064f78e64e860b3642404c3e7 100644 --- a/router/src/main/java/de/ozgcloud/eingang/router/GrpcClientsProperties.java +++ b/router/src/main/java/de/ozgcloud/eingang/router/GrpcClientsProperties.java @@ -48,7 +48,7 @@ public class GrpcClientsProperties { static class ClientProperty { @NotEmpty private String address; - private String negotationType = "TLS"; + private String negotiationType = "TLS"; } } diff --git a/router/src/main/java/de/ozgcloud/eingang/router/ManagableStub.java b/router/src/main/java/de/ozgcloud/eingang/router/ManagableStub.java new file mode 100644 index 0000000000000000000000000000000000000000..bce38696c16e1ac7ccf385b21deb6e1e0c1548f0 --- /dev/null +++ b/router/src/main/java/de/ozgcloud/eingang/router/ManagableStub.java @@ -0,0 +1,8 @@ +package de.ozgcloud.eingang.router; + +public interface ManagableStub<T> { + + T get(); + + boolean isShutdownable(); +} \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerListProperties.java b/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerListProperties.java index 8ca85a7d7f35009e149d6a2e77f382f6c841df19..7d615b5dfe7d4fba58e2bb6c1b1d8d38fe56f2f5 100644 --- a/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerListProperties.java +++ b/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerListProperties.java @@ -46,6 +46,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import org.springframework.validation.annotation.Validated; +import de.ozgcloud.eingang.Application; import de.ozgcloud.eingang.router.VorgangManagerListProperties.VorgangManagerListPropertiesConstraint; import lombok.AllArgsConstructor; import lombok.Getter; @@ -64,7 +65,7 @@ class VorgangManagerListProperties { } enum RoutingStrategy { - MULTI, SINGLE + MULTI, SINGLE, ZUFI } @Autowired(required = false) @@ -92,20 +93,28 @@ class VorgangManagerListProperties { } @AllArgsConstructor - public static class VorgangManagerListPropertiesValidator implements ConstraintValidator<VorgangManagerListPropertiesConstraint, VorgangManagerListProperties> { + public static class VorgangManagerListPropertiesValidator + implements ConstraintValidator<VorgangManagerListPropertiesConstraint, VorgangManagerListProperties> { - private static final Predicate<VorgangManagerListProperties> IS_SINGLE_ROUTING = props -> props.getRoutingStrategy() == RoutingStrategy.SINGLE; + private static final Predicate<VorgangManagerListProperties> IS_SINGLE_ROUTING = props -> props + .getRoutingStrategy() == RoutingStrategy.SINGLE; private static final Predicate<VorgangManagerListProperties> HAS_TARGET_NAME = props -> props.getTargetVorgangManagerName().isPresent(); private static final Predicate<VorgangManagerListProperties> IS_MULTI_ROUTING = props -> props.getRoutingStrategy() == RoutingStrategy.MULTI; private static final Predicate<VorgangManagerListProperties> HAS_FALLBACK_STRATEGY = props -> Objects.nonNull(props.getFallbackStrategy()); + private static final Predicate<VorgangManagerListProperties> IS_ZUFI_ROUTING = props -> props.getRoutingStrategy() == RoutingStrategy.ZUFI; + private static final Predicate<VorgangManagerListProperties> IS_FALLBACK_TO_FUNDSTELLE = props -> props.getFallbackStrategy() .map(strategy -> strategy == FallbackStrategy.FUNDSTELLE).orElse(false); private static final Predicate<VorgangManagerListProperties> HAS_FUNDSTELLE = props -> props.getFundstelleVorgangManagerName().isPresent(); @Override public boolean isValid(VorgangManagerListProperties value, ConstraintValidatorContext context) { + return isVorgangManagerRoutingValid(value) || isZufiManagerRoutingValid(value); + } + + private boolean isVorgangManagerRoutingValid(VorgangManagerListProperties value) { return IS_SINGLE_ROUTING.and(HAS_TARGET_NAME) .or(IS_MULTI_ROUTING.and(HAS_FALLBACK_STRATEGY)) .and(IS_FALLBACK_TO_FUNDSTELLE.negate().or(HAS_FUNDSTELLE)) @@ -113,14 +122,26 @@ class VorgangManagerListProperties { .test(value); } + private boolean isZufiManagerRoutingValid(VorgangManagerListProperties value) { + return IS_ZUFI_ROUTING.and(this::isZufiConfigured).test(value); + } + private boolean hasAllVorgangManagersConfigured(VorgangManagerListProperties props) { var clientNames = props.getClientProperties() .map(ps -> ps.getClient()) .map(Map::keySet) .orElse(Collections.emptySet()); - return props.getOrganisationseinheiten().values().stream().map(organisationseinheitName -> clientNames.contains("vorgang-manager-" + organisationseinheitName)) + return props.getOrganisationseinheiten().values().stream() + .map(organisationseinheitName -> clientNames.contains("vorgang-manager-" + organisationseinheitName)) .collect(Collectors.reducing(true, Boolean::logicalAnd)); } + + private boolean isZufiConfigured(VorgangManagerListProperties props) { + return props.getClientProperties() + .map(ps -> ps.getClient()) + .map(client -> client.get(Application.ZUFI_MANAGER_GRPC_CLIENT)) + .isPresent(); + } } } diff --git a/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerServerResolver.java b/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerServerResolver.java index a227052a1c5c58ed3baf19d946243b91b20c3ef6..6108c9ceb3d999e7349e1a7e588b69e1bb693abd 100644 --- a/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerServerResolver.java +++ b/router/src/main/java/de/ozgcloud/eingang/router/VorgangManagerServerResolver.java @@ -33,6 +33,8 @@ import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import de.ozgcloud.common.errorhandling.TechnicalException; +import de.ozgcloud.eingang.common.zufi.ZufiService; import de.ozgcloud.eingang.router.VorgangManagerListProperties.FallbackStrategy; import de.ozgcloud.eingang.router.VorgangManagerListProperties.RoutingStrategy; import de.ozgcloud.eingang.router.errorhandling.AdapterConfigurationException; @@ -40,6 +42,8 @@ import de.ozgcloud.eingang.router.errorhandling.UnknownOrganisationseinheitExcep import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; import de.ozgcloud.vorgang.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub; import io.grpc.Channel; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; import io.grpc.stub.AbstractStub; import lombok.NonNull; import lombok.extern.log4j.Log4j2; @@ -47,8 +51,8 @@ import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory; import net.devh.boot.grpc.client.inject.StubTransformer; import net.devh.boot.grpc.client.stubfactory.StubFactory; -@Component @Log4j2 +@Component public class VorgangManagerServerResolver { static final String CHANNEL_NAME_PREFIX = "vorgang-manager-"; @@ -64,13 +68,16 @@ public class VorgangManagerServerResolver { @Autowired(required = false) private Collection<StubTransformer> stubTransformers = Collections.emptyList(); - private StubFactory vorgangStubFactory; - private StubFactory binaryFileAsynStubFactory; + private StubFactory vorgangBlockingStubFactory; + private StubFactory binaryFileStubFactory; + + @Autowired + private ZufiService zufiService; @PostConstruct void findApplicableStubFactories() { - vorgangStubFactory = findStubFactory(VorgangServiceBlockingStub.class); - binaryFileAsynStubFactory = findStubFactory(BinaryFileServiceStub.class); + vorgangBlockingStubFactory = findStubFactory(VorgangServiceBlockingStub.class); + binaryFileStubFactory = findStubFactory(BinaryFileServiceStub.class); } StubFactory findStubFactory(Class<? extends AbstractStub<?>> stubClass) { @@ -79,38 +86,77 @@ public class VorgangManagerServerResolver { .findFirst().orElseThrow(() -> new AdapterConfigurationException("Cannot find Stub-Factory for GRPC-" + stubClass)); } - public VorgangServiceBlockingStub resolveVorgangServiceBlockingStubByOrganisationseinheitenId(Optional<String> organisationsEinheitId) { - return (VorgangServiceBlockingStub) createStub(organisationsEinheitId, vorgangStubFactory, VorgangServiceBlockingStub.class); + public ManagableStub<VorgangServiceBlockingStub> resolveVorgangServiceBlockingStubByOrganisationseinheitenId( + Optional<String> organisationsEinheitId) { + return createStub(organisationsEinheitId, vorgangBlockingStubFactory, VorgangServiceBlockingStub.class); } - public BinaryFileServiceStub resolveBinaryFileServiceStubByOrganisationsEinheitId(Optional<String> organisationsEinheitId) { - return (BinaryFileServiceStub) createStub(organisationsEinheitId, binaryFileAsynStubFactory, BinaryFileServiceStub.class); + public ManagableStub<BinaryFileServiceStub> resolveBinaryFileServiceStubByOrganisationsEinheitId(Optional<String> organisationsEinheitId) { + return createStub(organisationsEinheitId, binaryFileStubFactory, BinaryFileServiceStub.class); } - AbstractStub<?> createStub(Optional<String> organisationsEinheitId, StubFactory stubFactory, Class<? extends AbstractStub<?>> stubClass) {// NOSONAR - var channelName = getChannelName(organisationsEinheitId); - var stub = stubFactory.createStub(stubClass, createChannelByName(channelName)); + <T extends AbstractStub<T>> ManagableStub<T> createStub(Optional<String> organisationsEinheitId, StubFactory stubFactory, Class<T> stubClass) { + if (isZufiStrategy()) { + return createCloseableStub(organisationsEinheitId, stubFactory, stubClass); + } + return createStubByConfiguredChannels(organisationsEinheitId, stubFactory, stubClass); + } - return applyStubTransformers(stub, channelName); + private boolean isZufiStrategy() { + return properties.getRoutingStrategy() == RoutingStrategy.ZUFI; } - AbstractStub<?> applyStubTransformers(AbstractStub<?> stub, String channelName) { // NOSONAR wildcard given by StubTransformer - for (var transformer : stubTransformers) { - stub = transformer.transform(channelName, stub); + <T extends AbstractStub<T>> ManagableStub<T> createCloseableStub(Optional<String> organisationsEinheitId, StubFactory stubFactory, + Class<T> stubClass) { + var channelName = getVorgangManagerAddress(organisationsEinheitId); + var channel = createChannel(channelName); + var stub = stubFactory.createStub(stubClass, channel); + stub = applyStubTransformers(stub, channelName); + return buildClosableStub(stub, channel); + } + + @SuppressWarnings("unchecked") + private <T extends AbstractStub<?>> ClosableStub<T> buildClosableStub(AbstractStub<?> stub, ManagedChannel channel) { + return ClosableStub.<T>builder().stub((T) stub).channel(channel).build(); + } + + ManagedChannel createChannel(String channelName) { + return ManagedChannelBuilder.forTarget(channelName).usePlaintext().build(); + } + + String getVorgangManagerAddress(Optional<String> organisationsEinheitId) { + if (organisationsEinheitId.isEmpty()) { + throw new TechnicalException("No organisationsEinheitId exists, can not build connection to vorgang-manager."); } - return stub; + return zufiService.getVorgangManagerUrl(organisationsEinheitId.get()); + } + + <T extends AbstractStub<T>> ManagableStub<T> createStubByConfiguredChannels(Optional<String> organisationsEinheitId, StubFactory stubFactory, + Class<T> stubClass) { + var channelName = getChannelName(organisationsEinheitId); + var stub = stubFactory.createStub(stubClass, createChannelByName(channelName)); + stub = applyStubTransformers(stub, channelName); + return buildConsistentStub(stub); + } + + @SuppressWarnings("unchecked") + private <T extends AbstractStub<?>> ConsistentStub<T> buildConsistentStub(AbstractStub<?> stub) { + return ConsistentStub.<T>builder().stub((T) stub).build(); } String getChannelName(Optional<String> organisationsEinheitId) { - Optional<String> target; + return getChannelTarget(organisationsEinheitId).map(this::addChannelPrefix).orElseGet(this::getFundstelleChannelName); + } - if (properties.getRoutingStrategy() == RoutingStrategy.SINGLE) { - target = properties.getTargetVorgangManagerName(); - } else { - target = organisationsEinheitId.map(properties.getOrganisationseinheiten()::get); - } + private Optional<String> getChannelTarget(Optional<String> organisationsEinheitId) { + return isSingleRoutingStrategy() + ? properties.getTargetVorgangManagerName() + : organisationsEinheitId.map(properties.getOrganisationseinheiten()::get); - return target.map(this::addChannelPrefix).orElseGet(this::getFundstelleChannelName); + } + + private boolean isSingleRoutingStrategy() { + return properties.getRoutingStrategy() == RoutingStrategy.SINGLE; } private String addChannelPrefix(@NonNull String name) { @@ -134,4 +180,11 @@ public class VorgangManagerServerResolver { Channel createChannelByName(String name) { return grpcChannelFactory.createChannel(name); } + + AbstractStub<?> applyStubTransformers(AbstractStub<?> stub, String channelName) { // NOSONAR wildcard given by StubTransformer + for (var transformer : stubTransformers) { + stub = transformer.transform(channelName, stub); + } + return stub; + } } \ No newline at end of file diff --git a/router/src/main/java/de/ozgcloud/eingang/router/VorgangRemoteService.java b/router/src/main/java/de/ozgcloud/eingang/router/VorgangRemoteService.java index 54ee2075fe8827984a34b0dfec99c4a9cbec410f..e359c1c58d2b85fc2326c74ac848ef4287f5d972 100644 --- a/router/src/main/java/de/ozgcloud/eingang/router/VorgangRemoteService.java +++ b/router/src/main/java/de/ozgcloud/eingang/router/VorgangRemoteService.java @@ -67,21 +67,40 @@ public class VorgangRemoteService { private VorgangManagerServerResolver vorgangManagerServerResolver; public String createVorgang(FormData formData, GrpcEingang eingang, Optional<String> organisationsEinheitenId) { - var vorgangManagerStub = vorgangManagerServerResolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(organisationsEinheitenId); - LOG.info("Connecting to vorgang-manager server " + vorgangManagerStub.getChannel().authority() + "; OrganisationsEinheitId: " + organisationsEinheitenId); + var vorgangServiceStub = getVorgangServiceStub(organisationsEinheitenId); + var binaryFileServiceStub = getBinaryFileServiceStub(organisationsEinheitenId); - return createVorgang(formData, eingang, vorgangManagerStub, getBinaryFileServiceStub(organisationsEinheitenId)); + logConnection(organisationsEinheitenId, vorgangServiceStub.get()); + + try { + return createVorgang(formData, eingang, vorgangServiceStub.get(), binaryFileServiceStub.get()); + } finally { + finishStubConnections(List.of(vorgangServiceStub, binaryFileServiceStub)); + } } - public String createVorgang(FormData formData, GrpcEingang eingang, VorgangServiceBlockingStub remoteStub, - BinaryFileServiceStub remoteAsyncStub) { - return new VorgangCreator(formData, eingang, remoteStub, remoteAsyncStub).create(); + void logConnection(Optional<String> organisationsEinheitenId, VorgangServiceBlockingStub vorgangStub) { + LOG.info("Connecting to vorgang-manager {}; OrganisationsEinheitId: {}.", vorgangStub.getChannel().authority(), + organisationsEinheitenId); } - private BinaryFileServiceStub getBinaryFileServiceStub(Optional<String> organisationsEinheitenId) { + private ManagableStub<VorgangServiceBlockingStub> getVorgangServiceStub(Optional<String> organisationsEinheitenId) { + return vorgangManagerServerResolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(organisationsEinheitenId); + } + + private ManagableStub<BinaryFileServiceStub> getBinaryFileServiceStub(Optional<String> organisationsEinheitenId) { return vorgangManagerServerResolver.resolveBinaryFileServiceStubByOrganisationsEinheitId(organisationsEinheitenId); } + public String createVorgang(FormData formData, GrpcEingang eingang, VorgangServiceBlockingStub vorgangStub, + BinaryFileServiceStub binaryFileStub) { + return new VorgangCreator(formData, eingang, vorgangStub, binaryFileStub).create(); + } + + void finishStubConnections(List<ManagableStub<?>> stubs) { + stubs.stream().filter(ManagableStub::isShutdownable).map(ClosableStub.class::cast).forEach(ClosableStub::close); + } + @RequiredArgsConstructor public class VorgangCreator { @@ -124,14 +143,17 @@ public class VorgangRemoteService { } private IncomingFileGroup uploadAttachment(IncomingFileGroup attachment) { - var filesWithId = attachment.getFiles().stream().map(file -> file.toBuilder().id(uploadIncomingFile(file)).build()).toList(); + var filesWithId = attachment.getFiles().stream().map(this::addIncomingFileId).toList(); return IncomingFileGroup.builder().name(attachment.getName()).files(filesWithId).build(); } List<IncomingFile> uploadRepresentations() { - return formData.getRepresentations().stream() - .map(representation -> representation.toBuilder().id(uploadIncomingFile(representation)).build()).toList(); + return formData.getRepresentations().stream().map(this::addIncomingFileId).toList(); + } + + private IncomingFile addIncomingFileId(IncomingFile file) { + return file.toBuilder().id(uploadIncomingFile(file)).build(); } String uploadIncomingFile(IncomingFile incomingFile) { diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitGetResponseTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitGetResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..57f78b5d4db7f58699db379118409fce1d0e457c --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitGetResponseTestFactory.java @@ -0,0 +1,15 @@ +package de.ozgcloud.eingang.common.zufi; + +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheitGetResponse; + +public class GrpcOrganisationsEinheitGetResponseTestFactory { + + public static GrpcOrganisationsEinheitGetResponse create() { + return createBuilder().build(); + } + + public static GrpcOrganisationsEinheitGetResponse.Builder createBuilder() { + return GrpcOrganisationsEinheitGetResponse.newBuilder() + .setOrganisationsEinheit(GrpcOrganisationsEinheitTestFactory.create()); + } +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..49c01b101aa0caf4a3e866f329730238d3603cd4 --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/GrpcOrganisationsEinheitTestFactory.java @@ -0,0 +1,18 @@ +package de.ozgcloud.eingang.common.zufi; + +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheit; + +public class GrpcOrganisationsEinheitTestFactory { + + public static GrpcOrganisationsEinheit create() { + return createBuilder().build(); + } + + public static GrpcOrganisationsEinheit.Builder createBuilder() { + return GrpcOrganisationsEinheit.newBuilder() + .setId(OrganisationsEinheitTestFactory.ID) + .setName(OrganisationsEinheitTestFactory.NAME) + .setSynonyme(OrganisationsEinheitTestFactory.SYNONYME) + .setVorgangManagerAddress(OrganisationsEinheitTestFactory.VORGANG_MANAGER_ADDRESS); + } +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapperTest.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3086fd4fa410fc82cfae8a518c84c58c172b913e --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitMapperTest.java @@ -0,0 +1,20 @@ +package de.ozgcloud.eingang.common.zufi; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import de.ozgcloud.eingang.common.zufi.OrganisationsEinheitMapper; + +class OrganisationsEinheitMapperTest { + + private final OrganisationsEinheitMapper mapper = Mappers.getMapper(OrganisationsEinheitMapper.class); + + @Test + void shouldMapFromGrpc() { + var organisationsEinheit = mapper.fromGrpc(GrpcOrganisationsEinheitTestFactory.create()); + + assertThat(organisationsEinheit).usingRecursiveComparison().isEqualTo(OrganisationsEinheitTestFactory.create()); + } +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteServiceTest.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..320217632313129236581f6a5d61c46acf7801c9 --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitRemoteServiceTest.java @@ -0,0 +1,70 @@ +package de.ozgcloud.eingang.common.zufi; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +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 de.ozgcloud.eingang.common.zufi.OrganisationsEinheit; +import de.ozgcloud.eingang.common.zufi.OrganisationsEinheitMapper; +import de.ozgcloud.eingang.common.zufi.OrganisationsEinheitRemoteService; +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheitGetRequest; +import de.ozgcloud.zufi.grpc.organisationseinheit.GrpcOrganisationsEinheitGetResponse; +import de.ozgcloud.zufi.grpc.organisationseinheit.OrganisationsEinheitServiceGrpc.OrganisationsEinheitServiceBlockingStub; + +class OrganisationsEinheitRemoteServiceTest { + + @InjectMocks + private OrganisationsEinheitRemoteService service; + @Mock + private OrganisationsEinheitServiceBlockingStub stub; + @Mock + private OrganisationsEinheitMapper mapper; + + @DisplayName("Get by id") + @Nested + class TestGetById { + + private final GrpcOrganisationsEinheitGetResponse response = GrpcOrganisationsEinheitGetResponseTestFactory.create(); + private final OrganisationsEinheit mappedOrganisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @BeforeEach + void mock() { + when(stub.getById(any())).thenReturn(response); + when(mapper.fromGrpc(any())).thenReturn(mappedOrganisationsEinheit); + } + + @Captor + private ArgumentCaptor<GrpcOrganisationsEinheitGetRequest> requestCaptor; + + @Test + void shouldCallRemoteService() { + service.getById(OrganisationsEinheitTestFactory.ID); + + verify(stub).getById(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getId()).isEqualTo(OrganisationsEinheitTestFactory.ID); + } + + @Test + void shouldCallMapper() { + service.getById(OrganisationsEinheitTestFactory.ID); + + verify(mapper).fromGrpc(response.getOrganisationsEinheit()); + } + + @Test + void shouldReturnValue() { + var organisationsEinheit = service.getById(OrganisationsEinheitTestFactory.ID); + + assertThat(organisationsEinheit).isEqualTo(mappedOrganisationsEinheit); + } + } +} diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2be652a78b45cc8c7d75dfa16badab8c4825606f --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/OrganisationsEinheitTestFactory.java @@ -0,0 +1,27 @@ +package de.ozgcloud.eingang.common.zufi; + +import java.util.UUID; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.eingang.common.zufi.OrganisationsEinheit; + +public class OrganisationsEinheitTestFactory { + + public final static String ID = UUID.randomUUID().toString(); + public final static String NAME = LoremIpsum.getInstance().getName(); + public final static String SYNONYME = LoremIpsum.getInstance().getName(); + public final static String VORGANG_MANAGER_ADDRESS = LoremIpsum.getInstance().getUrl(); + + public static OrganisationsEinheit create() { + return createBuilder().build(); + } + + public static OrganisationsEinheit.OrganisationsEinheitBuilder createBuilder() { + return OrganisationsEinheit.builder() + .id(ID) + .name(NAME) + .synonyme(SYNONYME) + .vorgangManagerAddress(VORGANG_MANAGER_ADDRESS); + } +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/common/zufi/ZufiServiceTest.java b/router/src/test/java/de/ozgcloud/eingang/common/zufi/ZufiServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..04a03ee5e2a61c631f5f32c51c59df7cfd289f1b --- /dev/null +++ b/router/src/test/java/de/ozgcloud/eingang/common/zufi/ZufiServiceTest.java @@ -0,0 +1,46 @@ +package de.ozgcloud.eingang.common.zufi; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +class ZufiServiceTest { + + @InjectMocks + private ZufiService service; + @Mock + private OrganisationsEinheitRemoteService remoteService; + + @DisplayName("Get vorgangManager url") + @Nested + class TestGetVorgangManagerUrl { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + + @BeforeEach + void mock() { + when(remoteService.getById(any())).thenReturn(organisationsEinheit); + } + + @Test + void shouldCallRemoteService() { + service.getVorgangManagerUrl(OrganisationsEinheitTestFactory.ID); + + verify(remoteService).getById(OrganisationsEinheitTestFactory.ID); + } + + @Test + void shouldReturnValue() { + var vorgangManagerAddress = service.getVorgangManagerUrl(OrganisationsEinheitTestFactory.ID); + + assertThat(vorgangManagerAddress).isEqualTo(OrganisationsEinheitTestFactory.VORGANG_MANAGER_ADDRESS); + } + } +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTest.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTest.java index c470ff21d1a78c47a5a06cf963b9452d8cb0580a..b6295122f887a083f7af32445c503cfd2b80040b 100644 --- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTest.java +++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTest.java @@ -29,13 +29,17 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import jakarta.validation.ValidatorFactory; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.eingang.Application; import de.ozgcloud.eingang.router.GrpcClientsProperties.ClientProperty; -import jakarta.validation.Validation; -import jakarta.validation.Validator; -import jakarta.validation.ValidatorFactory; class VorgangManagerListPropertiesTest { @@ -113,6 +117,37 @@ class VorgangManagerListPropertiesTest { } } + @Nested + class TestZufiRouting { + + @Test + void shouldBeValid() { + var props = VorgangManagerListPropertiesTestFactory.createForZufiRouting(); + props.setClientProperties(Optional.of(createZufiClientProperties())); + + var violations = getValidator().validate(props); + + assertThat(violations).isEmpty(); + } + + @Test + void shouldViolateMissingAddress() { + var props = VorgangManagerListPropertiesTestFactory.createForZufiRouting(); + + var violations = getValidator().validate(props); + + assertThat(violations).isNotEmpty(); + } + + static GrpcClientsProperties createZufiClientProperties() { + var property = new ClientProperty(); + property.setAddress(LoremIpsum.getInstance().getUrl()); + var properties = new GrpcClientsProperties(); + properties.setClient(Map.of(Application.ZUFI_MANAGER_GRPC_CLIENT, property)); + return properties; + } + } + @Nested class TestClientProperties { private VorgangManagerListProperties props = VorgangManagerListPropertiesTestFactory.createForMultiRouting(); diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTestFactory.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTestFactory.java index 99ed3490b7a1f664d5209441d450924b4a28a942..8425acf8fa316f549d61c7fbdf5971a92a2a48ef 100644 --- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTestFactory.java +++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerListPropertiesTestFactory.java @@ -68,6 +68,14 @@ class VorgangManagerListPropertiesTestFactory { return props; } + static VorgangManagerListProperties createForZufiRouting() { + var props = new VorgangManagerListProperties(); + props.setRoutingStrategy(RoutingStrategy.ZUFI); + props.setFallbackStrategy(Optional.of(FallbackStrategy.DENY)); + + return props; + } + static GrpcClientsProperties createClientProperties() { var property = new ClientProperty(); property.setAddress(ADDRESS); diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverITCase.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverITCase.java index 62d86f63fde8fc6d93c79b7510f097413ae55f40..cf37814049f350dfe2173ba518af58e012050b97 100644 --- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverITCase.java +++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverITCase.java @@ -45,9 +45,10 @@ class VorgangManagerServerResolverITCase { @Test void shouldReturnVorgangServiceBlockingStub() { var created = resolver - .resolveVorgangServiceBlockingStubByOrganisationseinheitenId(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID)); + .resolveVorgangServiceBlockingStubByOrganisationseinheitenId( + Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID)); - assertThat(created).isNotNull().isInstanceOf(VorgangServiceBlockingStub.class); + assertThat(created.get()).isNotNull().isInstanceOf(VorgangServiceBlockingStub.class); } @Test @@ -55,6 +56,6 @@ class VorgangManagerServerResolverITCase { var created = resolver .resolveBinaryFileServiceStubByOrganisationsEinheitId(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID)); - assertThat(created).isNotNull().isInstanceOf(BinaryFileServiceStub.class); + assertThat(created.get()).isNotNull().isInstanceOf(BinaryFileServiceStub.class); } } diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverTest.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverTest.java index b8678c338c2e676054099816056f7ff38196ba1e..7bcc1d1f45488a6b5832662ce87f7e29bc6a79df 100644 --- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverTest.java +++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangManagerServerResolverTest.java @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -40,16 +41,22 @@ import org.mockito.Mock; import org.mockito.Spy; import org.springframework.test.util.ReflectionTestUtils; +import de.ozgcloud.common.errorhandling.TechnicalException; +import de.ozgcloud.eingang.common.formdata.ZustaendigeStelleTestFactory; +import de.ozgcloud.eingang.common.zufi.ZufiService; import de.ozgcloud.eingang.router.errorhandling.AdapterConfigurationException; import de.ozgcloud.eingang.router.errorhandling.UnknownOrganisationseinheitException; +import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; import de.ozgcloud.vorgang.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub; import io.grpc.Channel; +import io.grpc.ManagedChannel; import io.grpc.stub.AbstractStub; import net.devh.boot.grpc.client.channelfactory.GrpcChannelFactory; import net.devh.boot.grpc.client.inject.StubTransformer; import net.devh.boot.grpc.client.stubfactory.StubFactory; class VorgangManagerServerResolverTest { + @Spy @InjectMocks private VorgangManagerServerResolver resolver; @@ -59,121 +66,281 @@ class VorgangManagerServerResolverTest { @Mock private StubFactory stubFactory; + @Mock + private ZufiService zufiService; + @Nested - class TestCreateChannel { + class TestFindStubFactory { + + @Mock + private StubFactory stubFactory; @Test - void shouldCallChannelFactory() { - resolver.createChannelByName(VorgangManagerListPropertiesTestFactory.VORGANG_MANAGER_NAME); + void shouldSetApplicableFactory() { + when(stubFactory.isApplicable(any())).thenReturn(true); + setStubFactories(stubFactory, stubFactory); - verify(channelFactory).createChannel(VorgangManagerListPropertiesTestFactory.VORGANG_MANAGER_NAME); + resolver.findApplicableStubFactories(); + + assertThat(ReflectionTestUtils.getField(resolver, "vorgangBlockingStubFactory")).isSameAs(stubFactory); + } + + @Test + void shouldThrowExceptionIfNotFound() { + setStubFactories(stubFactory); + + assertThrows(AdapterConfigurationException.class, () -> resolver.findApplicableStubFactories()); + } + + private void setStubFactories(StubFactory... factories) { + ReflectionTestUtils.setField(resolver, "stubFactories", Arrays.asList(factories)); } } + @DisplayName("Resolve vorgangManager service by organisationsEinheitenId") @Nested - class TestGetFundstellenChannelName { + class TestResolveVorgangManagerServiceStubByOrganisationsEinheitenId { + + @Mock + private ManagableStub<VorgangServiceBlockingStub> stub; + + private final Optional<String> organisationsEinheitenId = Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + + @BeforeEach + void mock() { + ReflectionTestUtils.setField(resolver, "vorgangBlockingStubFactory", stubFactory); + + doReturn(stub).when(resolver).createStub(any(), any(), any()); + } + + @Test + void shouldCallCreateStub() { + resolveStub(); + + verify(resolver).createStub(organisationsEinheitenId, stubFactory, VorgangServiceBlockingStub.class); + } + + @Test + void shouldReturnStub() { + var createdStub = resolveStub(); + + assertThat(createdStub).isEqualTo(stub); + } + + private ManagableStub<VorgangServiceBlockingStub> resolveStub() { + return resolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(organisationsEinheitenId); + } + } + + @DisplayName("Resolve binaryFile service by organisationsEinheitenId") + @Nested + class TestResolveBinaryFileServiceStubByOrganisationsEinheitenId { + + @Mock + private ManagableStub<BinaryFileServiceStub> stub; + + private final Optional<String> organisationsEinheitenId = Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + + @BeforeEach + void mock() { + ReflectionTestUtils.setField(resolver, "binaryFileStubFactory", stubFactory); + + doReturn(stub).when(resolver).createStub(any(), any(), any()); + } + + @Test + void shouldCallCreateStub() { + resolveStub(); + + verify(resolver).createStub(organisationsEinheitenId, stubFactory, BinaryFileServiceStub.class); + } + @Test + void shouldReturnStub() { + var createdStub = resolveStub(); + + assertThat(createdStub).isEqualTo(stub); + } + + private ManagableStub<BinaryFileServiceStub> resolveStub() { + return resolver.resolveBinaryFileServiceStubByOrganisationsEinheitId(organisationsEinheitenId); + } + } + + @DisplayName("Create stub") + @Nested + class TestCreateStub { + + @Mock + private ManagableStub<?> stub; + + private final Optional<String> organisationsEinheitenId = Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + + @DisplayName("on zufi strategy") @Nested - class TestStrategyFundstelle { + class TestOnZufiRoutingStrategy { - @Test - void shouldCreateChannel() { - setProperties(VorgangManagerListPropertiesTestFactory.createWithFundstelle()); + @BeforeEach + void mock() { + doReturn(stub).when(resolver).createCloseableStub(any(), any(), any()); + setProperties(VorgangManagerListPropertiesTestFactory.createForZufiRouting()); + } - var channel = resolver.getFundstelleChannelName(); + @Test + void shouldCallCreateStub() { + createStub(); - assertThat(channel).isNotNull().isEqualTo(VorgangManagerListPropertiesTestFactory.FUNDSTELLE_CHANNEL_NAME); + verify(resolver).createStub(organisationsEinheitenId, stubFactory, VorgangServiceBlockingStub.class); } @Test - void shouldThrowExceptionIfFundstelleIsMissing() { - var props = VorgangManagerListPropertiesTestFactory.createWithFundstelle(); - props.setFundstelleVorgangManagerName(Optional.empty()); - setProperties(props); + void shouldReturnStub() { + var createdStub = createStub(); - assertThrows(AdapterConfigurationException.class, () -> resolver.getFundstelleChannelName()); + assertThat(createdStub).isEqualTo(stub); } } + @DisplayName("On other routing strategy") @Nested - class TestStrategyDeny { + class TestOnOtherRoutingStrategy { + + @BeforeEach + void mock() { + doReturn(stub).when(resolver).createStubByConfiguredChannels(any(), any(), any()); + setProperties(VorgangManagerListPropertiesTestFactory.createForSingleRouting()); + } @Test - void shouldThrowException() { - setProperties(VorgangManagerListPropertiesTestFactory.createForMultiRouting()); + void shouldCallCreateStub() { + createStub(); - assertThrows(UnknownOrganisationseinheitException.class, () -> resolver.getFundstelleChannelName()); + verify(resolver).createStubByConfiguredChannels(organisationsEinheitenId, stubFactory, VorgangServiceBlockingStub.class); + } + + @Test + void shouldReturnStub() { + var createdStub = createStub(); + + assertThat(createdStub).isEqualTo(stub); } } + + private ManagableStub<VorgangServiceBlockingStub> createStub() { + return resolver.createStub(organisationsEinheitenId, stubFactory, VorgangServiceBlockingStub.class); + } } + @DisplayName("Create closeable stub") @Nested - class TestGetChannelName { + class TestCreateCloseableStub { + + @Mock + private ManagableStub<?> manageableStub; + @Mock + private AbstractStub<?> stub; + @Mock + private ManagedChannel managedChannel; + + private Optional<String> organisationsEinheitenId = Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID); + private final String vorgangManagerAddress = "dummyVorgangManagerAddress"; + + @BeforeEach + void mock() { + doReturn(vorgangManagerAddress).when(resolver).getVorgangManagerAddress(any()); + doReturn(managedChannel).when(resolver).createChannel(any()); + doReturn(stub).when(resolver).applyStubTransformers(any(), any()); + } @Test - void shouldUseSingleName() { - setProperties(VorgangManagerListPropertiesTestFactory.createForSingleRouting()); + void shouldGetVorgangManagerAddress() { + createCloseableStub(); - var name = resolver.getChannelName(Optional.empty()); + verify(resolver).getVorgangManagerAddress(organisationsEinheitenId); + } - assertThat(name).contains(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME); + @Test + void shouldCreateChannel() { + createCloseableStub(); + + verify(resolver).createChannel(vorgangManagerAddress); } @Test - void shouldUseNameFromMap() { - setProperties(VorgangManagerListPropertiesTestFactory.createForMultiRouting()); + void shouldCreateStub() { + createCloseableStub(); - var name = resolver.getChannelName(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID)); + verify(stubFactory).createStub(VorgangServiceBlockingStub.class, managedChannel); + } - assertThat(name).contains(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME); + @Test + void shouldApplStubTransformers() { + createCloseableStub(); + + verify(resolver).applyStubTransformers(any(), eq(vorgangManagerAddress)); } @Test - void shouldGetFundstellenName() { - setProperties(VorgangManagerListPropertiesTestFactory.createWithFundstelle()); + void shouldReturnStub() { + var createdStub = (ClosableStub) createCloseableStub(); - var name = resolver.getChannelName(Optional.of("4711")); + assertThat(createdStub).isNotNull(); + assertThat(createdStub.get()).isEqualTo(stub); + } - verify(resolver).getFundstelleChannelName(); - assertThat(name).isEqualTo(VorgangManagerListPropertiesTestFactory.FUNDSTELLE_CHANNEL_NAME); + private ManagableStub<VorgangServiceBlockingStub> createCloseableStub() { + return resolver.createCloseableStub(organisationsEinheitenId, stubFactory, VorgangServiceBlockingStub.class); } } + @DisplayName("Get vorgangManager address") @Nested - class TestFindStubFactory { + class TestGetVorgangManagerAddress { - @Mock - private StubFactory stubFactory; + private final Optional<String> organisationsEinheitenId = Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID); + private final String vorgangManagerAddress = "DummyVorgangManagerAddress"; @Test - void shouldSetApplicableFactory() { - when(stubFactory.isApplicable(any())).thenReturn(true); - setStubFactories(stubFactory, stubFactory); + void shouldCallZufiService() { + when(zufiService.getVorgangManagerUrl(any())).thenReturn(vorgangManagerAddress); - resolver.findApplicableStubFactories(); + getVorgangManagerAddress(); - assertThat(ReflectionTestUtils.getField(resolver, "vorgangStubFactory")).isSameAs(stubFactory); + verify(zufiService).getVorgangManagerUrl(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID); } @Test - void shouldThrowExceptionIfNotFound() { - setStubFactories(stubFactory); + void shouldThrowExceptionIfOrganisationsEinheitIsNotPresent() { + var emptyOrganisationsEinheitId = Optional.<String>empty(); - assertThrows(AdapterConfigurationException.class, () -> resolver.findApplicableStubFactories()); + assertThatThrownBy(() -> resolver.getVorgangManagerAddress(emptyOrganisationsEinheitId)).isInstanceOf(TechnicalException.class); } - private void setStubFactories(StubFactory... factories) { - ReflectionTestUtils.setField(resolver, "stubFactories", Arrays.asList(factories)); + @Test + void shouldReturnAddress() { + when(zufiService.getVorgangManagerUrl(any())).thenReturn(vorgangManagerAddress); + + var address = getVorgangManagerAddress(); + + assertThat(address).isEqualTo(vorgangManagerAddress); + + } + + private String getVorgangManagerAddress() { + return resolver.getVorgangManagerAddress(organisationsEinheitenId); } } + @DisplayName("Create stub by configured channels") @Nested - class TestCreateStub { + class TestCreateStubByConfiguredChannels { @Mock private Channel channel; @Mock private StubFactory stubFactory; + @Mock + private AbstractStub<?> createdStub; private Class<? extends AbstractStub<?>> stubClass = VorgangServiceBlockingStub.class; @@ -181,31 +348,121 @@ class VorgangManagerServerResolverTest { void initTest() { doReturn(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME).when(resolver).getChannelName(any()); doReturn(channel).when(resolver).createChannelByName(any()); + setProperties(VorgangManagerListPropertiesTestFactory.createForSingleRouting()); + doReturn(createdStub).when(resolver).applyStubTransformers(any(), any()); } @Test void shouldGetChannel() { - createStub(); + createStubByConfiguredChannels(); verify(resolver).createChannelByName(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME); } @Test void shouldApplyTransformers() { - createStub(); + createStubByConfiguredChannels(); verify(resolver).applyStubTransformers(any(), any()); } @Test void shouldCreateStubByFactory() { - createStub(); + createStubByConfiguredChannels(); verify(stubFactory).createStub(eq(stubClass), any()); } - private AbstractStub<?> createStub() { - return resolver.createStub(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID), stubFactory, stubClass); + @Test + void shouldReturnStub() { + var stub = createStubByConfiguredChannels(); + + assertThat(stub.get()).isEqualTo(createdStub); + } + + private ManagableStub<?> createStubByConfiguredChannels() { + return resolver.createStubByConfiguredChannels(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID), stubFactory, + VorgangServiceBlockingStub.class); + } + } + + @Nested + class TestGetChannelName { + + @Test + void shouldUseSingleName() { + setProperties(VorgangManagerListPropertiesTestFactory.createForSingleRouting()); + + var name = resolver.getChannelName(Optional.empty()); + + assertThat(name).contains(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME); + } + + @Test + void shouldUseNameFromMap() { + setProperties(VorgangManagerListPropertiesTestFactory.createForMultiRouting()); + + var name = resolver.getChannelName(Optional.of(VorgangManagerListPropertiesTestFactory.ORGANISATIONSEINHEIT_ID)); + + assertThat(name).contains(VorgangManagerListPropertiesTestFactory.CHANNEL_NAME); + } + + @Test + void shouldGetFundstellenName() { + setProperties(VorgangManagerListPropertiesTestFactory.createWithFundstelle()); + + var name = resolver.getChannelName(Optional.of("4711")); + + verify(resolver).getFundstelleChannelName(); + assertThat(name).isEqualTo(VorgangManagerListPropertiesTestFactory.FUNDSTELLE_CHANNEL_NAME); + } + } + + @Nested + class TestGetFundstellenChannelName { + + @Nested + class TestStrategyFundstelle { + + @Test + void shouldCreateChannel() { + setProperties(VorgangManagerListPropertiesTestFactory.createWithFundstelle()); + + var channel = resolver.getFundstelleChannelName(); + + assertThat(channel).isNotNull().isEqualTo(VorgangManagerListPropertiesTestFactory.FUNDSTELLE_CHANNEL_NAME); + } + + @Test + void shouldThrowExceptionIfFundstelleIsMissing() { + var props = VorgangManagerListPropertiesTestFactory.createWithFundstelle(); + props.setFundstelleVorgangManagerName(Optional.empty()); + setProperties(props); + + assertThrows(AdapterConfigurationException.class, () -> resolver.getFundstelleChannelName()); + } + } + + @Nested + class TestStrategyDeny { + + @Test + void shouldThrowException() { + setProperties(VorgangManagerListPropertiesTestFactory.createForMultiRouting()); + + assertThrows(UnknownOrganisationseinheitException.class, () -> resolver.getFundstelleChannelName()); + } + } + } + + @Nested + class TestCreateChannelByName { + + @Test + void shouldCallChannelFactory() { + resolver.createChannelByName(VorgangManagerListPropertiesTestFactory.VORGANG_MANAGER_NAME); + + verify(channelFactory).createChannel(VorgangManagerListPropertiesTestFactory.VORGANG_MANAGER_NAME); } } @@ -233,4 +490,4 @@ class VorgangManagerServerResolverTest { private void setProperties(VorgangManagerListProperties properties) { ReflectionTestUtils.setField(resolver, "properties", properties); } -} +} \ No newline at end of file diff --git a/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java b/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java index 60206de34962325a9a4b66061f7f65617233cec3..465b61fcdbe88fc611f3990c2a6ec00d5073ab96 100644 --- a/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java +++ b/router/src/test/java/de/ozgcloud/eingang/router/VorgangRemoteServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.*; import java.io.InputStream; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -37,6 +38,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -45,6 +47,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import de.ozgcloud.common.binaryfile.GrpcFileUploadUtils.FileSender; import de.ozgcloud.common.errorhandling.TechnicalException; @@ -54,6 +57,7 @@ import de.ozgcloud.eingang.common.formdata.IncomingFile; import de.ozgcloud.eingang.common.formdata.IncomingFileGroup; import de.ozgcloud.eingang.common.formdata.IncomingFileGroupTestFactory; import de.ozgcloud.eingang.common.formdata.IncomingFileTestFactory; +import de.ozgcloud.eingang.common.formdata.ZustaendigeStelleTestFactory; import de.ozgcloud.eingang.router.VorgangRemoteService.VorgangCreator; import de.ozgcloud.vorgang.grpc.binaryFile.BinaryFileServiceGrpc.BinaryFileServiceStub; import de.ozgcloud.vorgang.grpc.binaryFile.GrpcUploadBinaryFileMetaData; @@ -70,14 +74,11 @@ import lombok.SneakyThrows; class VorgangRemoteServiceTest { + @Spy @InjectMocks private VorgangRemoteService remoteService; @Mock - private final VorgangServiceBlockingStub vorgangStub = VorgangManagerServerResolverTestFactory.createVorgangBlockingStub(); - @Mock - private final BinaryFileServiceStub binaryFileStub = VorgangManagerServerResolverTestFactory.createBinaryFileStub(); - @Mock - private GrpcEingangMapper eingangMapper; + private VorgangManagerServerResolver resolver; private VorgangCreator vorgangCreator; @@ -87,286 +88,434 @@ class VorgangRemoteServiceTest { .addRepresentations(GrpcIncomingFileTestFactory.create()) .build(); private final String vorgangId = UUID.randomUUID().toString(); - private String fileId = "42"; - - @BeforeEach - void init() { - vorgangCreator = spy(remoteService.new VorgangCreator(formData, eingang, vorgangStub, binaryFileStub)); - } + private final String fileId = "42"; + @DisplayName("Create vorgang") @Nested class TestCreateVorgang { @Mock - private CallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver; - @Captor - private ArgumentCaptor<GrpcCreateVorgangRequest> requestCaptor; - @Captor - private ArgumentCaptor<GrpcFinishCreationRequest> finishRequestCaptor; + private ManagableStub<VorgangServiceBlockingStub> managableVorgangServiceStub; + @Mock + private VorgangServiceBlockingStub vorgangServiceStub; + + @Mock + private ManagableStub<BinaryFileServiceStub> managableBinaryFileServiceStub; + @Mock + private BinaryFileServiceStub binaryFileServiceStub; - private final GrpcCreateVorgangResponse createVorgangResponse = GrpcCreateVorgangResponse.newBuilder().setVorgangId(vorgangId).build(); - private final GrpcFinishCreationResponse finishResponse = GrpcFinishCreationResponse.newBuilder().setMessage("OK").build(); + private final Optional<String> organisationsEinheitId = Optional.of(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + @SneakyThrows @BeforeEach - void init() { - when(vorgangStub.startCreation(any())).thenReturn(createVorgangResponse); - when(vorgangStub.finishCreation(any())).thenReturn(finishResponse); - doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); - doReturn(GrpcFinishCreationRequest.newBuilder().build()).when(vorgangCreator).buildFinishCreationRequest(); - } + void mock() { + when(resolver.resolveVorgangServiceBlockingStubByOrganisationseinheitenId(any())).thenReturn(managableVorgangServiceStub); + when(managableVorgangServiceStub.get()).thenReturn(vorgangServiceStub); - @Test - void shouldStartCreation() { - createVorgang(); + when(resolver.resolveBinaryFileServiceStubByOrganisationsEinheitId(any())).thenReturn(managableBinaryFileServiceStub); + when(managableBinaryFileServiceStub.get()).thenReturn(binaryFileServiceStub); - verify(vorgangStub).startCreation(any(GrpcCreateVorgangRequest.class)); + doNothing().when(remoteService).logConnection(any(), any()); + doNothing().when(remoteService).finishStubConnections(any()); } - @Test - void shouldStartCreationWithEmptyAttachmetns() { - createVorgang(); + @DisplayName("with no exception occuring") + @Nested + class TestWithNoException { - verify(vorgangStub).startCreation(requestCaptor.capture()); - assertThat(requestCaptor.getValue().getEingang().getAttachmentsList()).isEmpty(); - } + @SneakyThrows + @BeforeEach + void mock() { + doReturn(vorgangId).when(remoteService).createVorgang(any(), any(), any(), any()); + } - @Test - void shouldStartCreationWithEmptyRepresentations() { - createVorgang(); + @Test + void shouldGetVorgangService() { + createVorgang(); - verify(vorgangStub).startCreation(requestCaptor.capture()); - assertThat(requestCaptor.getValue().getEingang().getRepresentationsList()).isEmpty(); - } + verify(resolver).resolveVorgangServiceBlockingStubByOrganisationseinheitenId(organisationsEinheitId); + } - @Test - void shouldCallUploadAttachments() { - createVorgang(); + @Test + void shouldGetBinaryFileService() { + createVorgang(); - verify(vorgangCreator).uploadAttachments(); - } + verify(resolver).resolveBinaryFileServiceStubByOrganisationsEinheitId(organisationsEinheitId); + } - @Test - void shouldCallUploadRepresentations() { - createVorgang(); + @SneakyThrows + @Test + void shouldCreateVorgang() { + createVorgang(); - verify(vorgangCreator).uploadRepresentations(); - } + verify(remoteService).createVorgang(formData, eingang, vorgangServiceStub, binaryFileServiceStub); + } - @Test - void shouldFinishCreation() { - createVorgang(); + @Test + void shouldFinishStubConnection() { + createVorgang(); + + verify(remoteService).finishStubConnections(List.of(managableVorgangServiceStub, managableBinaryFileServiceStub)); + } - verify(vorgangStub).finishCreation(finishRequestCaptor.capture()); - assertThat(finishRequestCaptor.getValue()).isInstanceOf(GrpcFinishCreationRequest.class); + @Test + void shouldReturnVorgangId() { + var created = createVorgang(); + + assertThat(created).isEqualTo(vorgangId); + + } } - @Test - void shouldReturnVorgangId() { - var result = createVorgang(); + @DisplayName("on exception") + @Nested + class TestOnException { - assertThat(result).isEqualTo(vorgangId); + @SneakyThrows + @BeforeEach + void mock() { + doThrow(RuntimeException.class).when(remoteService).createVorgang(any(), any(), any(), any()); + } + + @SneakyThrows + @Test + void shouldFinishStubConnections() { + try { + createVorgang(); + } catch (Exception e) { + // Do nothing + } + + verify(remoteService).finishStubConnections(List.of(managableVorgangServiceStub, managableBinaryFileServiceStub)); + } } + @SneakyThrows private String createVorgang() { - return vorgangCreator.create(); + return remoteService.createVorgang(formData, eingang, organisationsEinheitId); } } + @DisplayName("Finish stub connections") @Nested - class TestUploadAttachments { + class TestFinishStubConnections { + + @Mock + private ClosableStub<VorgangServiceBlockingStub> closableStub; @BeforeEach - void mockFileId() { - doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); + void mock() { + when(closableStub.isShutdownable()).thenReturn(true); + doNothing().when(closableStub).close(); } @Test - void shouldCallUploadIncomingFile() { - vorgangCreator.uploadAttachments(); + void shouldCheckIfSubIsShutdownable() { + remoteService.finishStubConnections(List.of(closableStub)); - verify(vorgangCreator, times(2)).uploadIncomingFile(any(IncomingFile.class)); + verify(closableStub).isShutdownable(); } @Test - void shouldSetFileId() { - var uploadedAttachments = vorgangCreator.uploadAttachments(); + void shouldShutDownChannelForClosableStubs() { + remoteService.finishStubConnections(List.of(closableStub)); - assertThat(uploadedAttachments.get(0).getFiles().get(0).getId()).isEqualTo(fileId); + verify((ClosableStub) closableStub).close(); } } + @DisplayName("VorgangCreator") @Nested - class TestUploadRepresentations { + class TestVorgangCreator { + + @Mock + private final VorgangServiceBlockingStub vorgangStub = VorgangManagerServerResolverTestFactory.createVorgangBlockingStub(); + @Mock + private final BinaryFileServiceStub binaryFileStub = VorgangManagerServerResolverTestFactory.createBinaryFileStub(); + @Mock + private GrpcEingangMapper eingangMapper; @BeforeEach - void mockFileId() { - doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); + void init() { + vorgangCreator = spy(remoteService.new VorgangCreator(formData, eingang, vorgangStub, binaryFileStub)); } - @Test - void shouldCallUploadIncomingFile() { - vorgangCreator.uploadRepresentations(); + @Nested + class TestCreateVorgang { - verify(vorgangCreator).uploadIncomingFile(any(IncomingFile.class)); - } + @Mock + private CallStreamObserver<GrpcUploadBinaryFileRequest> requestObserver; + @Captor + private ArgumentCaptor<GrpcCreateVorgangRequest> requestCaptor; + @Captor + private ArgumentCaptor<GrpcFinishCreationRequest> finishRequestCaptor; - @Test - void shouldSetFileId() { - var uploadedRepresentations = vorgangCreator.uploadRepresentations(); + private final GrpcCreateVorgangResponse createVorgangResponse = GrpcCreateVorgangResponse.newBuilder().setVorgangId(vorgangId).build(); + private final GrpcFinishCreationResponse finishResponse = GrpcFinishCreationResponse.newBuilder().setMessage("OK").build(); - assertThat(uploadedRepresentations.get(0).getId()).isEqualTo(fileId); - } - } + @BeforeEach + void init() { + when(vorgangStub.startCreation(any())).thenReturn(createVorgangResponse); + when(vorgangStub.finishCreation(any())).thenReturn(finishResponse); + doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); + doReturn(GrpcFinishCreationRequest.newBuilder().build()).when(vorgangCreator).buildFinishCreationRequest(); + } - @Nested - class TestBuildMetaDataRequest { + @Test + void shouldStartCreation() { + createVorgang(); - @BeforeEach - void mockMapper() { - doReturn(vorgangId).when(vorgangCreator).getVorgangId(); - } + verify(vorgangStub).startCreation(any(GrpcCreateVorgangRequest.class)); + } - @Test - void shouldContainsContext() { - var metaData = buildMetaData(); + @Test + void shouldStartCreationWithEmptyAttachmetns() { + createVorgang(); - assertThat(metaData.getContext().getClient()).isEqualTo(VorgangRemoteService.VorgangCreator.CALL_CONTEXT_CLIENT); - } + verify(vorgangStub).startCreation(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getEingang().getAttachmentsList()).isEmpty(); + } - @Test - void shouldContainsVorgangId() { - var metaData = buildMetaData(); + @Test + void shouldStartCreationWithEmptyRepresentations() { + createVorgang(); - assertThat(metaData.getVorgangId()).isEqualTo(vorgangId); - } + verify(vorgangStub).startCreation(requestCaptor.capture()); + assertThat(requestCaptor.getValue().getEingang().getRepresentationsList()).isEmpty(); + } - @Test - void shouldContainsField() { - var metaData = buildMetaData(); + @Test + void shouldCallUploadAttachments() { + createVorgang(); - assertThat(metaData.getField()).isEqualTo(VorgangRemoteService.VorgangCreator.VORGANG_ATTACHMENT_FIELD); - } + verify(vorgangCreator).uploadAttachments(); + } - @Test - void shouldContainsContentType() { - var metaData = buildMetaData(); + @Test + void shouldCallUploadRepresentations() { + createVorgang(); - assertThat(metaData.getContentType()).isEqualTo(IncomingFileTestFactory.CONTENT_TYPE); - } + verify(vorgangCreator).uploadRepresentations(); + } - @Test - void shouldContainsSize() { - var metaData = buildMetaData(); + @Test + void shouldFinishCreation() { + createVorgang(); - assertThat(metaData.getSize()).isEqualTo(IncomingFileTestFactory.SIZE); - } + verify(vorgangStub).finishCreation(finishRequestCaptor.capture()); + assertThat(finishRequestCaptor.getValue()).isInstanceOf(GrpcFinishCreationRequest.class); + } - @Test - void shouldContainsFileName() { - var metaData = buildMetaData(); + @Test + void shouldReturnVorgangId() { + var result = createVorgang(); - assertThat(metaData.getFileName()).isEqualTo(IncomingFileTestFactory.NAME); - } + assertThat(result).isEqualTo(vorgangId); + } - private GrpcUploadBinaryFileMetaData buildMetaData() { - return vorgangCreator.buildMetaDataRequest(IncomingFileTestFactory.create()).getMetadata(); + @SneakyThrows + private String createVorgang() { + return vorgangCreator.create(); + } } - } - @Nested - class TestWaitUntilFutureToComplete { + @Nested + class TestUploadAttachments { - @Mock - private FileSender<GrpcUploadBinaryFileRequest, GrpcUploadBinaryFileResponse> sender; + @BeforeEach + void mockFileId() { + doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); + } - @Mock - private CompletableFuture<GrpcUploadBinaryFileResponse> streamFuture; + @Test + void shouldCallUploadIncomingFile() { + vorgangCreator.uploadAttachments(); - @Mock - private InputStream inputStream; + verify(vorgangCreator, times(2)).uploadIncomingFile(any(IncomingFile.class)); + } - @BeforeEach - void initSender() { - when(sender.getResultFuture()).thenReturn(streamFuture); - } + @Test + void shouldSetFileId() { + var uploadedAttachments = vorgangCreator.uploadAttachments(); - @Test - void shouldNotThrowException() { - assertDoesNotThrow(() -> vorgangCreator.waitUntilFutureToComplete(sender, inputStream)); + assertThat(uploadedAttachments.get(0).getFiles().get(0).getId()).isEqualTo(fileId); + } } - @ParameterizedTest - @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) - void shouldRethrowAsTechnicalException(Class<Exception> exception) - throws InterruptedException, ExecutionException, TimeoutException { - doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); + @Nested + class TestUploadRepresentations { - assertThrows(TechnicalException.class, () -> vorgangCreator.waitUntilFutureToComplete(sender, inputStream)); - } + @BeforeEach + void mockFileId() { + doReturn(fileId).when(vorgangCreator).uploadIncomingFile(any()); + } - @ParameterizedTest - @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) - @SneakyThrows - void shouldCloseFileContentStreamOnException(Class<Exception> exception) { - doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); + @Test + void shouldCallUploadIncomingFile() { + vorgangCreator.uploadRepresentations(); - try { - vorgangCreator.waitUntilFutureToComplete(sender, inputStream); - } catch (Exception e) { - // ignored + verify(vorgangCreator).uploadIncomingFile(any(IncomingFile.class)); + } + + @Test + void shouldSetFileId() { + var uploadedRepresentations = vorgangCreator.uploadRepresentations(); + + assertThat(uploadedRepresentations.get(0).getId()).isEqualTo(fileId); } - verify(inputStream).close(); } - @Test - @SneakyThrows - void shouldCloseFileContent() { - try { - vorgangCreator.waitUntilFutureToComplete(sender, inputStream); - } catch (Exception e) { - // ignored + @Nested + class TestBuildMetaDataRequest { + + @BeforeEach + void mockMapper() { + doReturn(vorgangId).when(vorgangCreator).getVorgangId(); } - verify(inputStream).close(); - } - } + @Test + void shouldContainsContext() { + var metaData = buildMetaData(); - @Nested - class TestBuildFinishCreationRequest { + assertThat(metaData.getContext().getClient()).isEqualTo(VorgangRemoteService.VorgangCreator.CALL_CONTEXT_CLIENT); + } - private final IncomingFileGroup attachment = IncomingFileGroupTestFactory.create(); - private final IncomingFile representation = IncomingFileTestFactory.create(); + @Test + void shouldContainsVorgangId() { + var metaData = buildMetaData(); - @BeforeEach - void mock() { - doReturn(vorgangId).when(vorgangCreator).getVorgangId(); - doReturn(List.of(attachment)).when(vorgangCreator).getUploadedAttachments(); - doReturn(List.of(representation)).when(vorgangCreator).getUploadedRepresentations(); - } + assertThat(metaData.getVorgangId()).isEqualTo(vorgangId); + } - @Test - void shouldContainsVorgangId() { - var request = buildFinishCreationRequest(); + @Test + void shouldContainsField() { + var metaData = buildMetaData(); - assertThat(request.getVorgangId()).isEqualTo(vorgangId); - } + assertThat(metaData.getField()).isEqualTo(VorgangRemoteService.VorgangCreator.VORGANG_ATTACHMENT_FIELD); + } - @Test - void shouldContainsAttachmentWithoutContent() { - var request = buildFinishCreationRequest(); + @Test + void shouldContainsContentType() { + var metaData = buildMetaData(); + + assertThat(metaData.getContentType()).isEqualTo(IncomingFileTestFactory.CONTENT_TYPE); + } + + @Test + void shouldContainsSize() { + var metaData = buildMetaData(); + + assertThat(metaData.getSize()).isEqualTo(IncomingFileTestFactory.SIZE); + } - assertThat(request.getAttachments(0).getFiles(0).getContent()).isEmpty(); + @Test + void shouldContainsFileName() { + var metaData = buildMetaData(); + + assertThat(metaData.getFileName()).isEqualTo(IncomingFileTestFactory.NAME); + } + + private GrpcUploadBinaryFileMetaData buildMetaData() { + return vorgangCreator.buildMetaDataRequest(IncomingFileTestFactory.create()).getMetadata(); + } } - @Test - void shouldContainsRepresentationsWithoutContent() { - var request = buildFinishCreationRequest(); + @Nested + class TestWaitUntilFutureToComplete { + + @Mock + private FileSender<GrpcUploadBinaryFileRequest, GrpcUploadBinaryFileResponse> sender; + + @Mock + private CompletableFuture<GrpcUploadBinaryFileResponse> streamFuture; + + @Mock + private InputStream inputStream; + + @BeforeEach + void initSender() { + when(sender.getResultFuture()).thenReturn(streamFuture); + } + + @Test + void shouldNotThrowException() { + assertDoesNotThrow(() -> vorgangCreator.waitUntilFutureToComplete(sender, inputStream)); + } + + @ParameterizedTest + @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) + void shouldRethrowAsTechnicalException(Class<Exception> exception) + throws InterruptedException, ExecutionException, TimeoutException { + doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); + + assertThrows(TechnicalException.class, () -> vorgangCreator.waitUntilFutureToComplete(sender, inputStream)); + } + + @ParameterizedTest + @ValueSource(classes = { InterruptedException.class, ExecutionException.class, TimeoutException.class }) + @SneakyThrows + void shouldCloseFileContentStreamOnException(Class<Exception> exception) { + doThrow(exception).when(streamFuture).get(anyLong(), any(TimeUnit.class)); + + waitUntilFutureToComplete(); + + verify(inputStream).close(); + } - assertThat(request.getRepresentations(0).getContent()).isEmpty(); + @Test + @SneakyThrows + void shouldCloseFileContent() { + waitUntilFutureToComplete(); + + verify(inputStream).close(); + } + + private void waitUntilFutureToComplete() { + try { + vorgangCreator.waitUntilFutureToComplete(sender, inputStream); + } catch (Exception e) { + // ignored + } + } } - private GrpcFinishCreationRequest buildFinishCreationRequest() { - return vorgangCreator.buildFinishCreationRequest(); + @Nested + class TestBuildFinishCreationRequest { + + private final IncomingFileGroup attachment = IncomingFileGroupTestFactory.create(); + private final IncomingFile representation = IncomingFileTestFactory.create(); + + @BeforeEach + void mock() { + doReturn(vorgangId).when(vorgangCreator).getVorgangId(); + doReturn(List.of(attachment)).when(vorgangCreator).getUploadedAttachments(); + doReturn(List.of(representation)).when(vorgangCreator).getUploadedRepresentations(); + } + + @Test + void shouldContainsVorgangId() { + var request = buildFinishCreationRequest(); + + assertThat(request.getVorgangId()).isEqualTo(vorgangId); + } + + @Test + void shouldContainsAttachmentWithoutContent() { + var request = buildFinishCreationRequest(); + + assertThat(request.getAttachments(0).getFiles(0).getContent()).isEmpty(); + } + + @Test + void shouldContainsRepresentationsWithoutContent() { + var request = buildFinishCreationRequest(); + + assertThat(request.getRepresentations(0).getContent()).isEmpty(); + } + + private GrpcFinishCreationRequest buildFinishCreationRequest() { + return vorgangCreator.buildFinishCreationRequest(); + } } } } \ No newline at end of file