Skip to content
Snippets Groups Projects
Commit c5ff7952 authored by OZGCloud's avatar OZGCloud
Browse files

OZG-6335 wait for wiedervorlage commands

parent a951f148
Branches
Tags
No related merge requests found
Showing
with 355 additions and 42 deletions
......@@ -74,4 +74,14 @@ public class Command {
public CommandOrder getCommandOrder() {
return CommandOrder.fromOrder(order);
}
@JsonIgnore
public boolean isDoneSuccessfully() {
return status == CommandStatus.FINISHED;
}
@JsonIgnore
public boolean isNotDone() {
return status.isNotDone();
}
}
\ No newline at end of file
......@@ -23,6 +23,7 @@
*/
package de.ozgcloud.alfa.common.command;
import java.util.Calendar;
import java.util.Optional;
import java.util.stream.Stream;
......@@ -41,6 +42,8 @@ import lombok.NonNull;
public class CommandService {
static final long NO_RELATION_VERSION = -1;
private static final int WAIT_TIME_MS = 500;
private static final int COMMAND_REQUEST_THRESHOLD_MILLIS = 10000;
@Autowired
private CommandRemoteService remoteService;
......@@ -111,4 +114,25 @@ public class CommandService {
return remoteService.findCommands(vorgangId, Optional.of(CommandStatus.FINISHED), Optional.empty());
}
public Command waitUntilDone(Command commandToWaitFor) {
var command = commandToWaitFor;
var calendar = Calendar.getInstance();
var timeout = calendar.getTimeInMillis() + COMMAND_REQUEST_THRESHOLD_MILLIS;
while (command.isNotDone() && calendar.getTimeInMillis() < timeout) {
synchronized (this) {
try {
wait(WAIT_TIME_MS);
command = reloadCommand(command.getId());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
return command;
}
public Command reloadCommand(String commandId) {
return remoteService.getCommand(commandId);
}
}
\ No newline at end of file
......@@ -23,6 +23,14 @@
*/
package de.ozgcloud.alfa.common.command;
import java.util.Set;
public enum CommandStatus {
PENDING, FINISHED, ERROR, REVOKE_PENDING, REVOKED;
private static final Set<CommandStatus> FINAL_STATES = Set.of(FINISHED, ERROR, REVOKED);
public boolean isNotDone() {
return !FINAL_STATES.contains(this);
}
}
......@@ -53,10 +53,9 @@ public class WiedervorlageCommandController {
@PathVariable long wiedervorlageVersion) {
var wiedervorlage = service.getById(wiedervorlageId);
var createdCommand = createCommand(wiedervorlage, command);
var doneCommand = service.updateNextFristOnSuccessfullyDoneCommand(createdCommand, wiedervorlage.getVorgangId());
service.updateNextFrist(wiedervorlage.getVorgangId());
return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build();
return ResponseEntity.created(linkTo(CommandController.class).slash(doneCommand.getId()).toUri()).build();
}
Command createCommand(Wiedervorlage wiedervorlage, CreateCommand command) {
......@@ -97,10 +96,9 @@ public class WiedervorlageCommandController {
@PostMapping
public ResponseEntity<Void> createWiedervorlage(@RequestBody CreateCommand command, @PathVariable String vorgangId) {
var createdCommand = service.createWiedervorlage((Wiedervorlage) command.getBody(), vorgangId);
var doneCommand = service.updateNextFristOnSuccessfullyDoneCommand(createdCommand, createdCommand.getVorgangId());
service.updateNextFrist(vorgangId);
return ResponseEntity.created(linkTo(CommandController.class).slash(createdCommand.getId()).toUri()).build();
return ResponseEntity.created(linkTo(CommandController.class).slash(doneCommand.getId()).toUri()).build();
}
}
}
\ No newline at end of file
......@@ -33,27 +33,27 @@ import java.util.stream.Stream;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import de.ozgcloud.alfa.common.attacheditem.VorgangAttachedItemService;
import de.ozgcloud.alfa.common.command.Command;
import de.ozgcloud.alfa.common.command.CommandService;
import de.ozgcloud.alfa.common.user.CurrentUserService;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Validated
@Service
class WiedervorlageService {
private static final Predicate<Wiedervorlage> IS_NOT_DONE = wiedervorlage -> !wiedervorlage.isDone();
@Autowired
private WiedervorlageRemoteService remoteService;
@Autowired
private VorgangAttachedItemService vorgangAttachedItemService;
@Autowired
private CurrentUserService currentUserService;
private final WiedervorlageRemoteService remoteService;
private final VorgangAttachedItemService vorgangAttachedItemService;
private final CurrentUserService currentUserService;
private final CommandService commandService;
public Command createWiedervorlage(@Valid Wiedervorlage wiedervorlage, String vorgangId) {
return vorgangAttachedItemService.createNewWiedervorlage(addCreated(wiedervorlage), vorgangId);
......@@ -81,6 +81,14 @@ class WiedervorlageService {
remoteService.updateNextFrist(vorgangId, calculateNextFrist(allWiedervorlagen));
}
Command updateNextFristOnSuccessfullyDoneCommand(Command command, String vorgangId) {
var doneCommand = commandService.waitUntilDone(command);
if (doneCommand.isDoneSuccessfully()) {
updateNextFrist(vorgangId);
}
return doneCommand;
}
Optional<LocalDate> calculateNextFrist(Stream<Wiedervorlage> wiedervorlagen) {
return wiedervorlagen
.filter(Objects::nonNull)
......
......@@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.Calendar;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
......@@ -39,6 +40,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Spy;
import de.ozgcloud.alfa.loeschanforderung.DeleteLoeschAnforderung;
......@@ -297,4 +299,86 @@ class CommandServiceTest {
verify(remoteService).findCommands(VorgangHeaderTestFactory.ID, Optional.of(CommandStatus.FINISHED), Optional.empty());
}
}
@Nested
class TestWaitUntilDone {
private final Command pendingCommand = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build();
private final Command finishedCommand = pendingCommand.toBuilder().status(CommandStatus.FINISHED).build();
@Nested
class OnFinishedCommand {
@Test
void shouldReturnDoneCommand() {
var resultCommand = service.waitUntilDone(finishedCommand);
assertThat(resultCommand).isEqualTo(finishedCommand);
}
@Test
void shouldNotReloadCommand() {
service.waitUntilDone(finishedCommand);
verify(service, never()).reloadCommand(any());
}
}
@Nested
class OnPendingCommand {
@BeforeEach
void setUp() {
doReturn(finishedCommand).when(service).reloadCommand(pendingCommand.getId());
}
@Test
void shouldReloadCommand() {
service.waitUntilDone(pendingCommand);
verify(service, times(1)).reloadCommand(pendingCommand.getId());
}
@Test
void shouldReturnDoneCommand() {
var resultCommand = service.waitUntilDone(pendingCommand);
assertThat(resultCommand).isEqualTo(finishedCommand);
}
}
@Nested
class OnTimeoutExceeded {
@Mock
private Calendar calendar;
@Test
void shouldReturnPendingCommand() {
try (MockedStatic<Calendar> calendarMockedStatic = mockStatic(Calendar.class)) {
calendarMockedStatic.when(Calendar::getInstance).thenReturn(calendar);
when(calendar.getTimeInMillis()).thenReturn(0L, 15000L);
var resultCommand = service.waitUntilDone(pendingCommand);
assertThat(resultCommand).isEqualTo(pendingCommand);
}
}
@Test
void shouldReloadOnceAndReturnPendingCommand() {
doReturn(pendingCommand).when(service).reloadCommand(pendingCommand.getId());
try (MockedStatic<Calendar> calendarMockedStatic = mockStatic(Calendar.class)) {
calendarMockedStatic.when(Calendar::getInstance).thenReturn(calendar);
when(calendar.getTimeInMillis()).thenReturn(0L, 0L, 15000L);
var resultCommand = service.waitUntilDone(pendingCommand);
verify(service, times(1)).reloadCommand(pendingCommand.getId());
assertThat(resultCommand).isEqualTo(pendingCommand);
}
}
}
}
}
\ No newline at end of file
package de.ozgcloud.alfa.common.command;
import static org.assertj.core.api.Assertions.*;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode;
class CommandStatusTest {
@Nested
class TestIsNotDone {
@ParameterizedTest
@EnumSource(names = { "PENDING", "REVOKE_PENDING" })
public void shouldReturnTrue(CommandStatus status) {
var istNotDone = status.isNotDone();
assertThat(istNotDone).isTrue();
}
@ParameterizedTest
@EnumSource(names = { "PENDING", "REVOKE_PENDING" }, mode = Mode.EXCLUDE)
public void shouldReturnFalse(CommandStatus status) {
var istNotDone = status.isNotDone();
assertThat(istNotDone).isFalse();
}
}
}
\ No newline at end of file
package de.ozgcloud.alfa.common.command;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode;
import org.mockito.Mock;
class CommandTest {
@Nested
class TestIsDoneSuccessfully {
@ParameterizedTest
@EnumSource(names = "FINISHED")
public void shouldReturnTrue(CommandStatus commandStatus) {
var command = CommandTestFactory.createBuilder().status(commandStatus).build();
var isDoneSuccessfully = command.isDoneSuccessfully();
assertThat(isDoneSuccessfully).isTrue();
}
@ParameterizedTest
@EnumSource(names = "FINISHED", mode = Mode.EXCLUDE)
public void shouldReturnFalse(CommandStatus commandStatus) {
var command = CommandTestFactory.createBuilder().status(commandStatus).build();
var isDoneSuccessfully = command.isDoneSuccessfully();
assertThat(isDoneSuccessfully).isFalse();
}
}
@Nested
class TestIsNotDone {
@Mock
private CommandStatus commandStatus;
private Command command;
@BeforeEach
void setUp() {
command = CommandTestFactory.createBuilder().status(commandStatus).build();
}
@Test
public void shouldCallIsNotDone() {
command.isNotDone();
verify(commandStatus).isNotDone();
}
@Test
public void shouldReturnIsNotDone() {
when(commandStatus.isNotDone()).thenReturn(true);
var isNotDone = command.isNotDone();
assertThat(isNotDone).isTrue();
}
}
}
\ No newline at end of file
......@@ -44,7 +44,9 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import de.ozgcloud.alfa.common.binaryfile.BinaryFileTestFactory;
import de.ozgcloud.alfa.common.command.Command;
import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController;
import de.ozgcloud.alfa.common.command.CommandStatus;
import de.ozgcloud.alfa.common.command.CommandTestFactory;
import de.ozgcloud.alfa.common.command.CreateCommand;
import de.ozgcloud.alfa.common.command.LegacyOrder;
......@@ -80,10 +82,15 @@ class WiedervorlageCommandByVorgangControllerTest {
@Captor
private ArgumentCaptor<Wiedervorlage> wiedervorlageCaptor;
private Command createCommand;
private Command doneCommand;
@BeforeEach
void mockUserService() {
when(service.createWiedervorlage(any(), any())).thenReturn(CommandTestFactory.create());
createCommand = CommandTestFactory.create();
doneCommand = createCommand.toBuilder().status(CommandStatus.FINISHED).build();
when(service.createWiedervorlage(any(), any())).thenReturn(createCommand);
when(service.updateNextFristOnSuccessfullyDoneCommand(createCommand, VorgangHeaderTestFactory.ID)).thenReturn(doneCommand);
}
@Nested
......@@ -98,10 +105,10 @@ class WiedervorlageCommandByVorgangControllerTest {
}
@Test
void shouldCallServiceToUpdateNextFrist() {
void shouldUpdateNextFristOnSuccessfullyDoneCommand() {
doRequest();
verify(service).updateNextFrist(VorgangHeaderTestFactory.ID);
verify(service).updateNextFristOnSuccessfullyDoneCommand(createCommand, VorgangHeaderTestFactory.ID);
}
@SneakyThrows
......
......@@ -46,7 +46,7 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import de.ozgcloud.alfa.common.binaryfile.FileId;
import de.ozgcloud.alfa.common.command.Command;
import de.ozgcloud.alfa.common.command.CommandOrder;
import de.ozgcloud.alfa.common.command.CommandService;
import de.ozgcloud.alfa.common.command.CommandStatus;
import de.ozgcloud.alfa.common.command.CommandTestFactory;
import de.ozgcloud.alfa.common.command.CreateCommand;
import de.ozgcloud.alfa.common.command.LegacyOrder;
......@@ -60,8 +60,6 @@ class WiedervorlageCommandControllerTest {
@InjectMocks
private WiedervorlageCommandController controller;
@Mock
private CommandService commandService;
@Mock
private WiedervorlageService service;
private MockMvc mockMvc;
......@@ -80,12 +78,18 @@ class WiedervorlageCommandControllerTest {
@Nested
class ControllerMethods {
private Command createCommand;
private Command doneCommand;
@BeforeEach
void init() {
when(service.getById(any())).thenReturn(WiedervorlageTestFactory.create());
when(service.editWiedervorlage(any(), any(), anyLong())).thenReturn(CommandTestFactory.createBuilder()
createCommand = CommandTestFactory.createBuilder()
.order(CommandOrder.UPDATE_ATTACHED_ITEM.name())
.body(WiedervorlageTestFactory.createAsMap()).build());
.body(WiedervorlageTestFactory.createAsMap()).build();
doneCommand = createCommand.toBuilder().status(CommandStatus.FINISHED).build();
when(service.editWiedervorlage(any(), any(), anyLong())).thenReturn(createCommand);
when(service.updateNextFristOnSuccessfullyDoneCommand(createCommand, VorgangHeaderTestFactory.ID)).thenReturn(doneCommand);
}
@SneakyThrows
......@@ -97,10 +101,10 @@ class WiedervorlageCommandControllerTest {
}
@Test
void shouldCallServiceUpdateNextFrist() {
void shouldUpdateNextFristOnSuccessfullyDoneCommand() {
doRequest();
verify(service).updateNextFrist(VorgangHeaderTestFactory.ID);
verify(service).updateNextFristOnSuccessfullyDoneCommand(createCommand, VorgangHeaderTestFactory.ID);
}
@SneakyThrows
......
......@@ -47,6 +47,7 @@ import org.mockito.Spy;
import de.ozgcloud.alfa.common.attacheditem.VorgangAttachedItemService;
import de.ozgcloud.alfa.common.command.Command;
import de.ozgcloud.alfa.common.command.CommandService;
import de.ozgcloud.alfa.common.command.CommandStatus;
import de.ozgcloud.alfa.common.command.CommandTestFactory;
import de.ozgcloud.alfa.common.user.CurrentUserService;
import de.ozgcloud.alfa.common.user.UserProfileTestFactory;
......@@ -107,14 +108,14 @@ class WiedervorlageServiceTest {
}
@Test
void shouldSetCreatedAt() throws Exception {
void shouldSetCreatedAt() {
var wiedervorlage = callAddCreated();
assertThat(wiedervorlage.getCreatedAt()).isNotNull().isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS));
}
@Test
void shouldSetCreatedBy() throws Exception {
void shouldSetCreatedBy() {
var wiedervorlage = callAddCreated();
assertThat(wiedervorlage.getCreatedBy()).isEqualTo(UserProfileTestFactory.ID.toString());
......@@ -254,4 +255,68 @@ class WiedervorlageServiceTest {
}
}
}
@Nested
class TestUpdateNextFristOnSuccessfullyDoneCommand {
@Test
public void shouldWaitUntilCommandDone() {
var pendingCommand = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build();
var command = CommandTestFactory.create();
when(commandService.waitUntilDone(command)).thenReturn(pendingCommand);
service.updateNextFristOnSuccessfullyDoneCommand(command, VorgangHeaderTestFactory.ID);
verify(commandService).waitUntilDone(command);
}
@Test
void shouldReturnDoneCommand() {
var errorCommand = CommandTestFactory.createBuilder().status(CommandStatus.ERROR).build();
var command = CommandTestFactory.create();
when(commandService.waitUntilDone(command)).thenReturn(errorCommand);
var result = service.updateNextFristOnSuccessfullyDoneCommand(command, VorgangHeaderTestFactory.ID);
assertThat(result).isEqualTo(errorCommand);
}
@Nested
class OnDoneSuccessfullyCommand {
private final Command command = CommandTestFactory.create();
private final Command doneCommand = CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).build();
@BeforeEach
void setUp() {
when(commandService.waitUntilDone(command)).thenReturn(doneCommand);
}
@Test
public void shouldUpdateNextFrist() {
service.updateNextFristOnSuccessfullyDoneCommand(command, VorgangHeaderTestFactory.ID);
verify(service).updateNextFrist(VorgangHeaderTestFactory.ID);
}
}
@Nested
class OnNotDoneSuccessfullyCommand {
private final Command command = CommandTestFactory.create();
private final Command pendingCommand = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build();
@BeforeEach
void setUp() {
when(commandService.waitUntilDone(command)).thenReturn(pendingCommand);
}
@Test
public void shouldNotUpdateNextFrist() {
service.updateNextFristOnSuccessfullyDoneCommand(command, VorgangHeaderTestFactory.ID);
verify(service, never()).updateNextFrist(VorgangHeaderTestFactory.ID);
}
}
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment