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

Merge pull request 'OZG-4527-aktenzeichen-manuell-eingeben' (#251) from...

Merge pull request 'OZG-4527-aktenzeichen-manuell-eingeben' (#251) from OZG-4527-aktenzeichen-manuell-eingeben into master

Reviewed-on: https://git.ozg-sh.de/ozgcloud-app/vorgang-manager/pulls/251


Reviewed-by: default avatarOZGCloud <ozgcloud@mgm-tp.com>
parents 83ccbca6 9dfdfa32
Branches
Tags
No related merge requests found
Showing
with 378 additions and 6 deletions
......@@ -40,6 +40,8 @@ public enum Order {
ASSIGN_USER,
SET_AKTENZEICHEN,
REDIRECT_VORGANG,
FORWARD_SUCCESSFULL,
FORWARD_FAILED,
......
package de.ozgcloud.vorgang.vorgang;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
public class SetAktenzeichenCompletedEvent extends CommandExecutedEvent {
public SetAktenzeichenCompletedEvent(Command command) {
super(command);
}
}
......@@ -23,9 +23,9 @@
*/
package de.ozgcloud.vorgang.vorgang;
import java.util.Collections;
import java.util.function.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
......@@ -35,6 +35,7 @@ import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandCreatedEvent;
import de.ozgcloud.command.CommandExecutedEvent;
import de.ozgcloud.command.CommandFailedEvent;
import de.ozgcloud.vorgang.command.CommandService;
import de.ozgcloud.vorgang.command.Order;
import de.ozgcloud.vorgang.files.FileService;
import de.ozgcloud.vorgang.status.StatusService;
......@@ -48,16 +49,21 @@ public class VorgangEventListener {
private static final String IS_ASSIGN_COMMAND_CONDITION = "{T(de.ozgcloud.vorgang.vorgang.VorgangEventListener).IS_ASSIGN_COMMAND.test(event.getSource())}";
private static final String IS_LOESCHEN_COMMAND_CONDITION = "{T(de.ozgcloud.vorgang.vorgang.VorgangEventListener).IS_LOESCHEN_COMMAND.test(event.getSource())}";
private static final String IS_SET_AKTENZEICHEN_COMMAND_CONDITION = "{T(de.ozgcloud.vorgang.vorgang.VorgangEventListener).IS_SET_AKTENZEICHEN_COMMAND.test(event.getSource())}";
public static final Predicate<Command> IS_ASSIGN_COMMAND = command -> Order.ASSIGN_USER.isMeant(command.getOrder());
public static final Predicate<Command> IS_LOESCHEN_COMMAND = command -> StringUtils.equals(command.getOrder(), "VORGANG_LOESCHEN");
public static final Predicate<Command> IS_LOESCHEN_COMMAND = command -> Order.VORGANG_LOESCHEN.isMeant(command.getOrder());
public static final Predicate<Command> IS_SET_AKTENZEICHEN_COMMAND = command -> Order.SET_AKTENZEICHEN.isMeant(command.getOrder());
@Autowired
private StatusService statusService;
@Autowired
private VorgangService vorgangService;
@Autowired
private VorgangHeaderService vorgangHeaderService;
@Autowired
private FileService fileService;
@Autowired
private CommandService commandService;
@Autowired
private ApplicationEventPublisher publisher;
......@@ -92,8 +98,30 @@ public class VorgangEventListener {
}
}
@EventListener(condition = IS_SET_AKTENZEICHEN_COMMAND_CONDITION)
public void onSetAktenzeichen(CommandCreatedEvent event) {
var command = event.getSource();
try {
updatePreviousState(command);
vorgangService.setAktenzeichen(command);
publisher.publishEvent(new SetAktenzeichenCompletedEvent(command));
} catch (RuntimeException e) {
LOG.error("Unexpected Error setting Aktenzeichen.", e);
publisher.publishEvent(new CommandFailedEvent(command.getId(), e.getMessage()));
}
}
void publishEvent(CommandExecutedEvent event) {
LOG.debug("Command {} executed", event.getSource());
publisher.publishEvent(event);
}
private void updatePreviousState(Command command) {
commandService.setPreviousState(command.getId(),
Collections.singletonMap(VorgangService.BODY_OBJECT_AKTENZEICHEN, getAktenzeichen(command.getVorgangId())));
}
private String getAktenzeichen(String vorgangId) {
return vorgangHeaderService.getById(vorgangId).getAktenzeichen();
}
}
\ No newline at end of file
......@@ -24,12 +24,14 @@
package de.ozgcloud.vorgang.vorgang;
import java.util.Objects;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.vorgang.common.search.SearchService;
@Service
......@@ -64,4 +66,12 @@ class VorgangHeaderService {
.clientAttributes(vorgangService.removeUnpermitted(vorgangHeader.getClientAttributes()))
.build();
}
public Optional<VorgangHeader> findById(String vorgangId) {
return repository.findById(vorgangId);
}
public VorgangHeader getById(String vorgangId) {
return findById(vorgangId).orElseThrow(() -> new TechnicalException(vorgangId + " could not be found"));
}
}
\ No newline at end of file
......@@ -31,12 +31,17 @@ import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import com.mongodb.client.result.UpdateResult;
import de.ozgcloud.vorgang.common.db.CollisionVerifier;
import de.ozgcloud.vorgang.common.db.CriteriaUtil;
@Repository
......@@ -45,6 +50,13 @@ class VorgangRepository {
@Autowired
private MongoOperations mongoOperations;
private CollisionVerifier collisionVerifier;
@PostConstruct
void init() {
collisionVerifier = new CollisionVerifier(this::exists);
}
public Optional<Vorgang> findById(String vorgangId) {
return Optional.ofNullable(findOne(CriteriaUtil.isId(vorgangId)));
}
......@@ -76,14 +88,23 @@ class VorgangRepository {
updateFirst(whereIdAndVersion(vorgangId, version), update);
}
private void updateFirst(Criteria queryObj, Update update) {
mongoOperations.updateFirst(query(queryObj), update, Vorgang.class);
public void setAktenzeichen(String vorgangId, long version, String akteneinsicht) {
var updateResult = updateFirst(whereIdAndVersion(vorgangId, version), new Update().set(Vorgang.FIELD_AKTENZEICHEN, akteneinsicht));
collisionVerifier.verify(updateResult, vorgangId);
}
private UpdateResult updateFirst(Criteria queryObj, Update update) {
return mongoOperations.updateFirst(query(queryObj), update, Vorgang.class);
}
private Criteria whereIdAndVersion(String vorgangId, long version) {
return CriteriaUtil.isId(vorgangId).and(Vorgang.MONGODB_FIELDNAME_VERSION).is(version);
}
public boolean exists(String vorgangId) {
return mongoOperations.exists(query(isId(vorgangId)), Vorgang.class);
}
public boolean exists(String vorgangId, Collection<String> organisationEinheitenIds) {
return mongoOperations.exists(
query(isId(vorgangId)
......
......@@ -30,6 +30,8 @@ import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
......@@ -49,6 +51,8 @@ public class VorgangService {
private static final String BODY_ASSIGNED_TO_FIELD = "assignedTo";
static final String BODY_OBJECT_AKTENZEICHEN = "aktenzeichen";
@Autowired
private VorgangAuthorizationService vorgangAuthenticationService;
@Autowired
......@@ -144,6 +148,11 @@ public class VorgangService {
return addLabels(loaded);
}
public void setAktenzeichen(Command command) {
var aktenzeichen = StringUtils.trimToNull(MapUtils.getString(command.getBodyObject(), BODY_OBJECT_AKTENZEICHEN));
repository.setAktenzeichen(command.getVorgangId(), command.getRelationVersion(), aktenzeichen);
}
Vorgang removeUnpermittedClientAttributes(Vorgang vorgang) {
return vorgang.toBuilder().clientAttributes(removeUnpermitted(vorgang.getClientAttributes())).build();
}
......
package de.ozgcloud.vorgang.vorgang;
import static de.ozgcloud.vorgang.vorgang.VorgangService.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
......@@ -13,6 +16,8 @@ import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.context.ApplicationEventPublisher;
import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandCreatedEvent;
import de.ozgcloud.command.VorgangCreatedEvent;
import de.ozgcloud.common.test.ITCase;
import de.ozgcloud.notification.antragsteller.AntragstellerNotificationEventListener;
......@@ -20,7 +25,9 @@ import de.ozgcloud.notification.user.UserNotificationEventListener;
import de.ozgcloud.processor.processor.ProcessorService;
import de.ozgcloud.processor.vorgang.ProcessorVorgangMapper;
import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory;
import de.ozgcloud.vorgang.command.CommandService;
import de.ozgcloud.vorgang.command.CommandTestFactory;
import de.ozgcloud.vorgang.command.Order;
import de.ozgcloud.vorgang.files.FileService;
@ITCase
......@@ -45,7 +52,11 @@ class VorgangEventListenerITCase {
@MockBean
private VorgangService service;
@MockBean
private VorgangHeaderService vorgangHeaderService;
@MockBean
private FileService fileService;
@Autowired
private CommandService commandService;
@MockBean
private ProcessorService vorgagnProcessorService;
......@@ -88,4 +99,31 @@ class VorgangEventListenerITCase {
verify(vorgagnProcessorService, timeout(500)).processVorgang(any());
}
}
@Nested
class TestOnSetAktenzeichen {
private static final Command SET_AKTENZEICHEN_COMMAND = CommandTestFactory.createBuilder().order(Order.SET_AKTENZEICHEN.name())
.bodyObject(Map.of(BODY_OBJECT_AKTENZEICHEN, VorgangTestFactory.AKTENZEICHEN)).build();
private static final CommandCreatedEvent SET_AKTENZEICHEN_EVENT = CommandCreatedEventTestFactory.create(SET_AKTENZEICHEN_COMMAND);
@BeforeEach
void init() {
doNothing().when(listener).onSetAktenzeichen(any());
}
@Test
void shouldCallOnSetAktenzeichen() {
publisher.publishEvent(SET_AKTENZEICHEN_EVENT);
verify(listener, timeout(500)).onSetAktenzeichen(SET_AKTENZEICHEN_EVENT);
}
@Test
void shouldNotCallOnSetAktenzeichen() {
publisher.publishEvent(CommandCreatedEventTestFactory.create(CommandTestFactory.createBuilder().order("NOT_SET_AKTENZEICHEN").build()));
verify(listener, never()).onSetAktenzeichen(any());
}
}
}
......@@ -23,18 +23,34 @@
*/
package de.ozgcloud.vorgang.vorgang;
import static de.ozgcloud.vorgang.vorgang.VorgangService.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.Map;
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 org.springframework.context.ApplicationEventPublisher;
import com.thedeanda.lorem.LoremIpsum;
import de.ozgcloud.command.Command;
import de.ozgcloud.command.CommandExecutedEvent;
import de.ozgcloud.command.CommandFailedEvent;
import de.ozgcloud.common.errorhandling.TechnicalException;
import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory;
import de.ozgcloud.vorgang.command.CommandService;
import de.ozgcloud.vorgang.command.CommandTestFactory;
import de.ozgcloud.vorgang.command.Order;
import de.ozgcloud.vorgang.common.errorhandling.FunctionalException;
import de.ozgcloud.vorgang.files.FileService;
import de.ozgcloud.vorgang.status.StatusService;
......@@ -49,9 +65,13 @@ class VorgangEventListenerTest {
@Mock
private VorgangService service;
@Mock
private VorgangHeaderService vorgangHeaderService;
@Mock
private StatusService statusService;
@Mock
private FileService fileService;
@Mock
private CommandService commandService;
@Mock
private ApplicationEventPublisher publisher;
......@@ -114,4 +134,101 @@ class VorgangEventListenerTest {
}
}
@Nested
class TestSetAktenzeichen {
private static final Command SET_AKTENZEICHEN_COMMAND = CommandTestFactory.createBuilder().order(Order.SET_AKTENZEICHEN.name())
.bodyObject(Map.of(BODY_OBJECT_AKTENZEICHEN, VorgangTestFactory.AKTENZEICHEN)).build();
private static final String PREVIOUS_AKTENZEICHEN = VorgangTestFactory.ID + "-prev";
@BeforeEach
void init() {
when(vorgangHeaderService.getById(VorgangTestFactory.ID)).thenReturn(
VorgangHeaderTestFactory.createBuilder().aktenzeichen(PREVIOUS_AKTENZEICHEN).build());
}
@Test
void shouldCallGetById() {
callListener();
verify(vorgangHeaderService).getById(VorgangTestFactory.ID);
}
@Test
void shouldFailIfVorgangNotFound() {
when(vorgangHeaderService.getById(VorgangTestFactory.ID)).thenThrow(TechnicalException.class);
callListener();
verify(publisher).publishEvent(any(CommandFailedEvent.class));
}
@Test
void shouldSetPreviousAktenzeichen() {
callListener();
verify(commandService).setPreviousState(CommandTestFactory.ID,
Map.of(VorgangService.BODY_OBJECT_AKTENZEICHEN, PREVIOUS_AKTENZEICHEN));
}
@Test
@DisplayName("should store a null in previousState, if Aktenzeichen is null")
void shouldHandleNullAktenzeichen() {
when(vorgangHeaderService.getById(VorgangTestFactory.ID)).thenReturn(VorgangHeaderTestFactory.createBuilder().aktenzeichen(null).build());
callListener();
verify(commandService).setPreviousState(CommandTestFactory.ID, Collections.singletonMap(VorgangService.BODY_OBJECT_AKTENZEICHEN, null));
}
@Test
void shouldCallVorgangService() {
callListener();
verify(service).setAktenzeichen(SET_AKTENZEICHEN_COMMAND);
}
@Test
void shouldPublishEvent() {
callListener();
verify(publisher).publishEvent(any(SetAktenzeichenCompletedEvent.class));
}
@Nested
class TestFailedEvent {
private final String ERROR_MESSAGE = LoremIpsum.getInstance().getWords(3);
@Captor
private ArgumentCaptor<CommandFailedEvent> event;
@BeforeEach
void init() {
doThrow(new RuntimeException(ERROR_MESSAGE)).when(service).setAktenzeichen(any());
}
@Test
void shouldContainCommandId() {
callListener();
verify(publisher).publishEvent(event.capture());
assertThat(event.getValue().getSource()).isEqualTo(CommandTestFactory.ID);
assertThat(event.getValue().getErrorMessage()).isEqualTo(ERROR_MESSAGE);
}
@Test
void shouldContainMessage() {
callListener();
verify(publisher).publishEvent(event.capture());
assertThat(event.getValue().getErrorMessage()).isEqualTo(ERROR_MESSAGE);
}
}
private void callListener() {
listener.onSetAktenzeichen(CommandCreatedEventTestFactory.create(SET_AKTENZEICHEN_COMMAND));
}
}
}
\ No newline at end of file
......@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeEach;
......@@ -38,12 +39,16 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import de.ozgcloud.common.errorhandling.TechnicalException;
class VorgangHeaderServiceTest {
@InjectMocks
@Spy
private VorgangHeaderService service;
@Mock
private VorgangHeaderRepository repository;
......@@ -113,4 +118,58 @@ class VorgangHeaderServiceTest {
.build();
}
}
@Nested
class TestFindById {
@Test
void shouldCallRepository() {
service.findById(VorgangTestFactory.ID);
verify(repository).findById(VorgangTestFactory.ID);
}
@Test
void shouldReturnRepositoryResult() {
var repositoryResult = Optional.of(VorgangHeaderTestFactory.create());
when(repository.findById(VorgangTestFactory.ID)).thenReturn(repositoryResult);
var serviceResult = service.findById(VorgangTestFactory.ID);
assertThat(serviceResult).isEqualTo(repositoryResult);
}
}
@Nested
class TestGEtById {
@Test
void shouldCallFindById() {
doReturn(Optional.of(VorgangHeaderTestFactory.create())).when(service).findById(VorgangTestFactory.ID);
service.getById(VorgangTestFactory.ID);
verify(service).findById(VorgangTestFactory.ID);
}
@Test
void shouldReturnVorgangHeader() {
var expectedVorgangHeader = VorgangHeaderTestFactory.create();
doReturn(Optional.of(expectedVorgangHeader)).when(service).findById(VorgangTestFactory.ID);
var vorgangHeader = service.getById(VorgangTestFactory.ID);
assertThat(vorgangHeader).isEqualTo(expectedVorgangHeader);
}
@Test
@DisplayName("should throw exception if VorgangHeader could not be found")
void shouldThrowException() {
doReturn(Optional.empty()).when(service).findById(VorgangTestFactory.ID);
assertThatThrownBy(() -> service.getById(VorgangTestFactory.ID))
.isInstanceOf(TechnicalException.class)
.hasMessageContaining(VorgangTestFactory.ID + " could not be found");
}
}
}
\ No newline at end of file
......@@ -24,10 +24,12 @@
package de.ozgcloud.vorgang.vorgang;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
......@@ -305,4 +307,57 @@ class VorgangRepositoryITCase {
}
}
@Nested
class TestSetAktenzeichen {
@Test
void shouldSetAktenzeichen() {
mongoOperations.save(VorgangTestFactory.createBuilder().aktenzeichen(null).build());
repository.setAktenzeichen(VorgangTestFactory.ID, VorgangTestFactory.VERSION, VorgangTestFactory.AKTENZEICHEN);
assertThat(getVorgang().getAktenzeichen()).isEqualTo(VorgangTestFactory.AKTENZEICHEN);
}
@Test
void shouldUpdateAktenzeichen() {
mongoOperations.save(VorgangTestFactory.create());
var newAktenzeichen = "newAktenzeichen";
repository.setAktenzeichen(VorgangTestFactory.ID, VorgangTestFactory.VERSION, newAktenzeichen);
assertThat(getVorgang().getAktenzeichen()).isEqualTo(newAktenzeichen);
}
@Test
void shouldResetAktenzeichen() {
mongoOperations.save(VorgangTestFactory.create());
repository.setAktenzeichen(VorgangTestFactory.ID, VorgangTestFactory.VERSION, null);
assertThat(getVorgang().getAktenzeichen()).isNull();
}
@Test
void should() {
assertThat(mongoOperations.findAll(Vorgang.class)).isEmpty();
repository.setAktenzeichen(VorgangTestFactory.ID, VorgangTestFactory.VERSION, VorgangTestFactory.AKTENZEICHEN);
assertThat(getVorgang()).isNull();
}
@Test
void shouldThrowExceptionByCollision() {
mongoOperations.save(VorgangTestFactory.createBuilder().aktenzeichen(null).build());
assertThrows(ConcurrentModificationException.class,
() -> repository.setAktenzeichen(VorgangTestFactory.ID, VorgangTestFactory.VERSION + 1, VorgangTestFactory.AKTENZEICHEN));
}
private Vorgang getVorgang() {
return mongoOperations.findById(VorgangTestFactory.ID, Vorgang.class);
}
}
}
\ No newline at end of file
......@@ -419,4 +419,26 @@ class VorgangServiceTest {
verify(repository).saveStub(stub);
}
}
@Nested
class TestSetAktenzeichen {
@Test
void shouldCallRepository() {
var command = CommandTestFactory.createBuilder().bodyObject(Map.of(VorgangService.BODY_OBJECT_AKTENZEICHEN, VorgangTestFactory.AKTENZEICHEN)).build();
service.setAktenzeichen(command);
verify(repository).setAktenzeichen(VorgangTestFactory.ID, CommandTestFactory.RELATION_VERSION, VorgangTestFactory.AKTENZEICHEN);
}
@Test
void shouldTrimEmptyStringToNull() {
var command = CommandTestFactory.createBuilder().bodyObject(Map.of(VorgangService.BODY_OBJECT_AKTENZEICHEN, "")).build();
service.setAktenzeichen(command);
verify(repository).setAktenzeichen(VorgangTestFactory.ID, CommandTestFactory.RELATION_VERSION, null);
}
}
}
\ 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