diff --git a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandService.java b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandService.java index 2506c223cc17f257ada388b6772728011b1ce830..4a3d26f0263a05d2d693123851831442f2793a23 100644 --- a/api-lib-core/src/main/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandService.java +++ b/api-lib-core/src/main/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandService.java @@ -1,5 +1,6 @@ package de.ozgcloud.apilib.common.command.grpc; +import de.itvsh.kop.common.errorhandling.TechnicalException; import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; import de.itvsh.ozg.pluto.grpc.command.GrpcGetCommandRequest; import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextAttachingInterceptor; @@ -7,6 +8,7 @@ import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; import de.ozgcloud.apilib.common.command.OzgCloudCommand; import de.ozgcloud.apilib.common.command.OzgCloudCommandId; import de.ozgcloud.apilib.common.command.OzgCloudCommandService; +import de.ozgcloud.apilib.common.command.OzgCloudCommandStatus; import lombok.RequiredArgsConstructor; import net.devh.boot.grpc.client.inject.GrpcClient; @@ -14,6 +16,7 @@ import net.devh.boot.grpc.client.inject.GrpcClient; public class GrpcOzgCloudCommandService implements OzgCloudCommandService { private static final int WAIT_TIME_MS = 500; + public static final int DEFAULT_COMMAND_REQUEST_THRESHOLD_MILLIS = 10000; @GrpcClient("command-manager") private final CommandServiceBlockingStub commandServiceStub; @@ -22,6 +25,8 @@ public class GrpcOzgCloudCommandService implements OzgCloudCommandService { private final OzgCloudCallContextProvider contextProvider; + private final int commandRequestThresholdMillis; + @Override public OzgCloudCommand createAndWaitUntilDone(OzgCloudCommand commandToCreate) { return waitUntilDone(create(commandToCreate)); @@ -34,8 +39,8 @@ public class GrpcOzgCloudCommandService implements OzgCloudCommandService { OzgCloudCommand waitUntilDone(OzgCloudCommand commandToWaitFor) { var command = commandToWaitFor; -// TODO timeout - while (!command.getStatus().isFinalState()) { + var timeout = System.currentTimeMillis() + commandRequestThresholdMillis; + while (!command.getStatus().isFinalState() && System.currentTimeMillis() < timeout) { synchronized (this) { try { wait(WAIT_TIME_MS); @@ -45,10 +50,11 @@ public class GrpcOzgCloudCommandService implements OzgCloudCommandService { } } } + verifyCommand(command); return command; } - private OzgCloudCommand reloadCommand(OzgCloudCommandId commandId) { + OzgCloudCommand reloadCommand(OzgCloudCommandId commandId) { GrpcGetCommandRequest request = GrpcGetCommandRequest.newBuilder().setId(commandId.toString()).build(); return mapper.fromGrpc(getCommandServiceStub().getCommand(request)); } @@ -56,4 +62,10 @@ public class GrpcOzgCloudCommandService implements OzgCloudCommandService { CommandServiceBlockingStub getCommandServiceStub() { return commandServiceStub.withInterceptors(new OzgCloudCallContextAttachingInterceptor(contextProvider)); } + + void verifyCommand(OzgCloudCommand command) { + if (command.getStatus() == OzgCloudCommandStatus.ERROR) { + throw new TechnicalException("Command (id=%s) failed: %s".formatted(command.getId(), command.getErrorMessage())); + } + } } diff --git a/api-lib-core/src/test/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandServiceTest.java b/api-lib-core/src/test/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandServiceTest.java index 8cdc3f055d216c2859f07ed4b5d29b3169dcf094..f032df3ffb8ef830387313695aacbb669ecf841a 100644 --- a/api-lib-core/src/test/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandServiceTest.java +++ b/api-lib-core/src/test/java/de/ozgcloud/apilib/common/command/grpc/GrpcOzgCloudCommandServiceTest.java @@ -1,29 +1,38 @@ package de.ozgcloud.apilib.common.command.grpc; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; -import org.mockito.Spy; +import org.mockito.Mockito; +import de.itvsh.kop.common.errorhandling.TechnicalException; import de.itvsh.ozg.pluto.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; +import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; import de.ozgcloud.apilib.common.command.OzgCloudCommand; +import de.ozgcloud.apilib.common.command.OzgCloudCommandStatus; class GrpcOzgCloudCommandServiceTest { - @Spy - @InjectMocks private GrpcOzgCloudCommandService service; @Mock private CommandServiceBlockingStub serviceStub; @Mock private CommandMapper mapper; + @Mock + private OzgCloudCallContextProvider contextProvider; + + @BeforeEach + void init() { + service = spy(new GrpcOzgCloudCommandService(serviceStub, mapper, contextProvider, 600)); + } @Nested class TestCreateAndWaitUntilDone { @@ -49,6 +58,23 @@ class GrpcOzgCloudCommandServiceTest { verify(service).waitUntilDone(created); } + @Test + void shouldTerminateByTimeout() { + var command = OzgCloudCommandTestFactory.createBuilder().status(OzgCloudCommandStatus.PENDING).build(); + doReturn(command).when(service).reloadCommand(any()); + + service.waitUntilDone(command); + + Mockito.verify(service, Mockito.times(2)).reloadCommand(command.getId()); + } + + @Test + void shouldCallVerifyCommand() { + service.waitUntilDone(command); + + verify(service).verifyCommand(command); + } + @Nested class Create { @@ -94,4 +120,15 @@ class GrpcOzgCloudCommandServiceTest { } } -} + @Nested + class TestStatus { + + @Test + void shouldThrowException() { + var command = OzgCloudCommandTestFactory.createBuilder().status(OzgCloudCommandStatus.ERROR).build(); + + assertThrows(TechnicalException.class, () -> service.verifyCommand(command)); + } + + } +} \ No newline at end of file diff --git a/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java b/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java index 8337636fdde90589577bbba49531c95eae6bcc6a..de90768eab0aaaf249f37c15984fd4f19a08290d 100644 --- a/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java +++ b/ozg-cloud-spring-boot-starter/src/main/java/de/ozgcloud/client/autoconfigure/OzgCloudClientAutoConfiguration.java @@ -56,10 +56,10 @@ public class OzgCloudClientAutoConfiguration { @Autowired private OzgCloudCommandManagerProperties commandManagerProperties; -// @Bean -// @ConditionalOnProperty("ozgcloud.vorgang-manager.address") + // @Bean + // @ConditionalOnProperty("ozgcloud.vorgang-manager.address") GrpcChannelsProperties channelProperties(GrpcChannelsProperties properties) { -// var properties = new GrpcChannelsProperties(); + // var properties = new GrpcChannelsProperties(); var clientMap = properties.getClient(); addVorgangManager(clientMap); @@ -111,8 +111,8 @@ public class OzgCloudClientAutoConfiguration { @Bean("ozgCloudVorgangService") @ConditionalOnProperty("ozgcloud.vorgang-manager.address") OzgCloudVorgangService grpcOzgCloudVorgangService(VorgangServiceBlockingStub vorgangServiceStub, - OzgCloudVorgangMapper mapper, - OzgCloudVorgangStubMapper stubMapper, OzgCloudCallContextProvider contextProvider) { + OzgCloudVorgangMapper mapper, + OzgCloudVorgangStubMapper stubMapper, OzgCloudCallContextProvider contextProvider) { return new GrpcOzgCloudVorgangService(vorgangServiceStub, mapper, stubMapper, contextProvider); } @@ -141,8 +141,9 @@ public class OzgCloudClientAutoConfiguration { @Bean @ConditionalOnProperty("ozgcloud.command-manager.address") OzgCloudCommandService grpcOzgCloudCommandService(@GrpcClient("command-manager") CommandServiceBlockingStub commandServiceStub, - CommandMapper commandMapper, OzgCloudCallContextProvider contextProvider) { - return new GrpcOzgCloudCommandService(commandServiceStub, commandMapper, contextProvider); + CommandMapper commandMapper, OzgCloudCallContextProvider contextProvider) { + return new GrpcOzgCloudCommandService(commandServiceStub, commandMapper, contextProvider, + GrpcOzgCloudCommandService.DEFAULT_COMMAND_REQUEST_THRESHOLD_MILLIS); } @Bean