From 9b6c9a565bdee02513b10c341eb7e56fe9cf8636 Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Mon, 31 Mar 2025 10:31:58 +0200 Subject: [PATCH 1/4] OZG-7872 OZG-8039 add related resource link on postfach --- .../common/command/CommandModelAssembler.java | 65 ++++++++++---- .../alfa/common/command/CommandProcessor.java | 37 ++++++++ .../PostfachNachrichtCommandProcessor.java | 47 ++++++++++ .../command/CommandModelAssemblerTest.java | 51 +++++++---- .../common/command/CommandProcessorTest.java | 77 ++++++++++++++++ ...PostfachNachrichtCommandProcessorTest.java | 88 +++++++++++++++++++ 6 files changed, 330 insertions(+), 35 deletions(-) create mode 100644 alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java create mode 100644 alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java create mode 100644 alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java create mode 100644 alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java index 94f0209dfd..467466ef0b 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java @@ -25,6 +25,7 @@ package de.ozgcloud.alfa.common.command; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import java.util.List; import java.util.function.Predicate; import java.util.stream.Stream; @@ -33,7 +34,6 @@ import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.server.RepresentationModelAssembler; -import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.bescheid.BescheidController; @@ -41,10 +41,11 @@ import de.ozgcloud.alfa.bescheid.DocumentController; import de.ozgcloud.alfa.collaboration.CollaborationController.CollaborationByVorgangController; import de.ozgcloud.alfa.forwarding.ForwardingController; import de.ozgcloud.alfa.kommentar.KommentarController; -import de.ozgcloud.alfa.postfach.PostfachMailController; import de.ozgcloud.alfa.vorgang.VorgangController; import de.ozgcloud.alfa.wiedervorlage.WiedervorlageController; +import lombok.RequiredArgsConstructor; +@RequiredArgsConstructor @Component class CommandModelAssembler implements RepresentationModelAssembler<Command, EntityModel<Command>> { @@ -57,33 +58,61 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT; + private static final List<String> NOT_PROCESSABLE_ORDER = List.of( + CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name(), + CommandOrder.RESEND_POSTFACH_MAIL.name(), + CommandOrder.SEND_POSTFACH_MAIL.name(), + CommandOrder.SEND_POSTFACH_NACHRICHT.name(), + CommandOrder.RESEND_POSTFACH_MAIL.name(), + CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name()); + + static final Predicate<Command> IS_PROCESSABLE_ORDER = command -> !NOT_PROCESSABLE_ORDER.contains(command.getOrder()); + + private final List<CommandProcessor> processors; + @Override public EntityModel<Command> toModel(Command command) { - return EntityModel.of(command) + var entityModel = EntityModel.of(command) .add(linkTo(CommandController.class).slash(command.getId()).withSelfRel()) - .addIf(CommandHelper.IS_DONE.and(HAS_KNOWN_COMMAND_ORDER).test(command), () -> effectedResourceLinkByOrderType(command)) + .addIf(CommandHelper.IS_DONE.and(HAS_KNOWN_COMMAND_ORDER).and(IS_PROCESSABLE_ORDER).test(command), + () -> effectedResourceLinkByOrderType(command)) .addIf(CommandHelper.IS_PENDING.test(command), () -> linkTo(CommandController.class).slash(command.getId()).withRel(REL_UPDATE)) .addIf(IS_NOT_LOESCH_ANFORDERUNG_AND_REVOKEABLE.test(command), () -> linkTo(methodOn(CommandController.class).revoke(command.getId(), null)).withRel(REL_REVOKE)); + + processors.forEach(processor -> processor.process(entityModel)); + + return entityModel; } Link effectedResourceLinkByOrderType(Command entity) { var type = entity.getCommandOrder().getType(); - WebMvcLinkBuilder linkBuilder = switch (type) { - case FORWARDING -> linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())); - case KOMMENTAR -> linkTo(KommentarController.class).slash(entity.getRelationId()); - case POSTFACH -> linkTo(methodOn(PostfachMailController.class).getAll(entity.getVorgangId())); - case VORGANG -> linkTo(VorgangController.class).slash(entity.getRelationId()); - case VORGANG_LIST -> linkTo(VorgangController.class); - case WIEDERVORLAGE -> linkTo(WiedervorlageController.class).slash(entity.getRelationId()); - case BESCHEID -> linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId())); - case DOCUMENT -> linkTo(DocumentController.class).slash(entity.getCreatedResource()); - case COLLABORATION -> linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId())); - case NONE -> throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); - }; - - return linkBuilder.withRel(REL_EFFECTED_RESOURCE); + if (type == CommandOrder.Type.FORWARDING) { + return linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.KOMMENTAR) { + return linkTo(KommentarController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.VORGANG) { + return linkTo(VorgangController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.VORGANG_LIST) { + return linkTo(VorgangController.class).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.WIEDERVORLAGE) { + return linkTo(WiedervorlageController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.BESCHEID) { + return linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.DOCUMENT) { + return linkTo(DocumentController.class).slash(entity.getCreatedResource()).withRel(REL_EFFECTED_RESOURCE); + } + if (type == CommandOrder.Type.COLLABORATION) { + return linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); + } + throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); } public CollectionModel<EntityModel<Command>> toCollectionModel(Stream<Command> entities) { diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java new file mode 100644 index 0000000000..a7343b9a3f --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java @@ -0,0 +1,37 @@ +package de.ozgcloud.alfa.common.command; + +import java.util.Objects; +import java.util.function.Predicate; + +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; + +public abstract class CommandProcessor { + + static final LinkRelation REL_EFFECTED_RESOURCE = LinkRelation.of("effected_resource"); + static final LinkRelation REL_RELATED_RESOURCE = LinkRelation.of("related_resource"); + + static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT; + + public void process(EntityModel<Command> model) { + var command = model.getContent(); + + if (Objects.isNull(command)) { + return; + } + + model.addIf(CommandHelper.IS_DONE.test(command) && isResponsibleForEffectedResource(command.getOrder()), + () -> createEffectedResourceLinkBuilder(command).withRel(REL_EFFECTED_RESOURCE)); + model.addIf(isResponsible(command.getOrder()), + () -> createRelatedResourceLinkBuilder(command).withRel(REL_RELATED_RESOURCE)); + } + + public abstract boolean isResponsibleForEffectedResource(String order); + + public abstract WebMvcLinkBuilder createEffectedResourceLinkBuilder(Command command); + + public abstract boolean isResponsible(String order); + + public abstract WebMvcLinkBuilder createRelatedResourceLinkBuilder(Command command); +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java new file mode 100644 index 0000000000..72f871b053 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java @@ -0,0 +1,47 @@ +package de.ozgcloud.alfa.postfach; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.List; +import java.util.Optional; + +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; +import org.springframework.stereotype.Component; + +import de.ozgcloud.alfa.common.command.Command; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandProcessor; + +@Component +public class PostfachNachrichtCommandProcessor extends CommandProcessor { + + private static final List<String> RESPONSIBLE_ORDER = List.of( + CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name(), + CommandOrder.RESEND_POSTFACH_MAIL.name()); + + private static final List<String> RESPONSIBLE_EFFECTED_RESOURCE_ORDER = List.of( + CommandOrder.SEND_POSTFACH_MAIL.name(), + CommandOrder.SEND_POSTFACH_NACHRICHT.name(), + CommandOrder.RESEND_POSTFACH_MAIL.name(), + CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name()); + + @Override + public boolean isResponsible(String order) { + return Optional.ofNullable(order).map(RESPONSIBLE_ORDER::contains).orElse(false); + } + + @Override + public WebMvcLinkBuilder createRelatedResourceLinkBuilder(Command command) { + return linkTo(PostfachMailController.class).slash(command.getRelationId()); + } + + @Override + public boolean isResponsibleForEffectedResource(String order) { + return Optional.ofNullable(order).map(RESPONSIBLE_EFFECTED_RESOURCE_ORDER::contains).orElse(false); + } + + @Override + public WebMvcLinkBuilder createEffectedResourceLinkBuilder(Command command) { + return linkTo(methodOn(PostfachMailController.class).getAll(command.getVorgangId())); + } +} \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandModelAssemblerTest.java index bd98a6b027..cfaba07c47 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandModelAssemblerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandModelAssemblerTest.java @@ -27,7 +27,12 @@ import static de.ozgcloud.alfa.common.command.CommandModelAssembler.*; import static de.ozgcloud.alfa.common.command.CommandTestFactory.*; import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -35,7 +40,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource.Mode; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; @@ -49,6 +58,19 @@ class CommandModelAssemblerTest { @InjectMocks private CommandModelAssembler modelAssembler; + @Spy + private List<CommandProcessor> processors = new ArrayList<>(); + @Mock + private CommandProcessor processor; + + @Captor + private ArgumentCaptor<EntityModel<Command>> commandEntityModelCaptor; + + @BeforeEach + void setProcessorMocks() { + processors.add(processor); + } + @Test void shouldHaveSelfLink() { var model = modelAssembler.toModel(CommandTestFactory.create()); @@ -56,6 +78,16 @@ class CommandModelAssemblerTest { assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); } + @Test + void shouldCallProcessor() { + var command = CommandTestFactory.create(); + + modelAssembler.toModel(command); + + verify(processor).process(commandEntityModelCaptor.capture()); + assertThat(commandEntityModelCaptor.getValue().getContent()).isEqualTo(command); + } + @Nested @DisplayName("Link to effected Resource") class TestEffectedResourceLink { @@ -103,22 +135,6 @@ class CommandModelAssemblerTest { } } - @DisplayName("on postfach") - @Nested - class TestOnPostfach { - - private static final String POSTFACH_URL = "/api/postfachMails?vorgangId=" + VorgangHeaderTestFactory.ID; - - @ParameterizedTest - @ValueSource(strings = { "SEND_POSTFACH_MAIL" }) - void shouldHaveLinkToPostfach(String order) { - var model = toModelWithOrder(order); - - assertThat(model.getLink(CommandModelAssembler.REL_EFFECTED_RESOURCE)).isPresent().get() - .extracting(Link::getHref).isEqualTo(POSTFACH_URL); - } - } - @DisplayName("on bescheid") @Nested class TestOnBescheid { @@ -196,7 +212,8 @@ class CommandModelAssemblerTest { } @ParameterizedTest - @EnumSource(mode = Mode.EXCLUDE, names = { "UNBEKANNT" }) + @EnumSource(mode = Mode.EXCLUDE, names = { "UNBEKANNT", "SEND_POSTFACH_MAIL", "SEND_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL", + "RECEIVE_POSTFACH_NACHRICHT" }) void shouldBeAbleToBuildLinkForEveryOrder(CommandOrder order) { var link = modelAssembler.effectedResourceLinkByOrderType(CommandTestFactory.createBuilder().order(order.name()).build()); diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java new file mode 100644 index 0000000000..224152f96e --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java @@ -0,0 +1,77 @@ +package de.ozgcloud.alfa.common.command; + +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.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Spy; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; + +class CommandProcessorTest { + + @Spy + private CommandProcessor processor; + + @DisplayName("Process") + @Nested + class TestProcess { + + @Test + void shouldNotThrowExceptionsOnNullContent() { + EntityModel<Command> entityModel = when(mock(EntityModel.class).getContent()).thenReturn(null).getMock(); + + assertDoesNotThrow(() -> processor.process(entityModel)); + } + + @DisplayName("effected resource link") + @Nested + class TestEffectedResource { + + @Test + void shouldBeAddedIfResponsible() { + var linkBuilder = WebMvcLinkBuilder.linkTo(CommandController.class).slash("id"); + doReturn(true).when(processor).isResponsibleForEffectedResource(any()); + doReturn(linkBuilder).when(processor).createEffectedResourceLinkBuilder(any()); + var command = CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).build(); + var entityModel = EntityModel.of(command); + + processor.process(entityModel); + + assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + } + + @Test + void shouldNotBeAddedIfCommandNotDone() { + var command = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build(); + var entityModel = EntityModel.of(command); + + processor.process(entityModel); + + assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE)).isNotPresent(); + } + } + + @DisplayName("related resource link") + @Nested + class TestRelatedResource { + + @Test + void shouldBeAddedIfResponsible() { + var linkBuilder = WebMvcLinkBuilder.linkTo(CommandController.class).slash("id"); + doReturn(true).when(processor).isResponsible(any()); + doReturn(linkBuilder).when(processor).createRelatedResourceLinkBuilder(any()); + var command = CommandTestFactory.create(); + var entityModel = EntityModel.of(command); + + processor.process(entityModel); + + assertThat(entityModel.getLink(CommandProcessor.REL_RELATED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + } + } + } +} \ No newline at end of file diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java new file mode 100644 index 0000000000..a4d6b2272e --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java @@ -0,0 +1,88 @@ +package de.ozgcloud.alfa.postfach; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +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.Spy; + +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandTestFactory; + +class PostfachNachrichtCommandProcessorTest { + + @Spy + private PostfachNachrichtCommandProcessor processor; + + @DisplayName("Related resource") + @Nested + class TestRelatedResource { + + @DisplayName("is responsible") + @Nested + class TestIsResponsible { + + @EnumSource(mode = Mode.INCLUDE, names = { "RECEIVE_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL" }) + @ParameterizedTest(name = "{0}") + void shouldReturnTrueOnOrder(CommandOrder order) { + var isResponsible = processor.isResponsible(order.name()); + + assertThat(isResponsible).isTrue(); + } + + @EnumSource(mode = Mode.EXCLUDE, names = { "RECEIVE_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL" }) + @ParameterizedTest(name = "{0}") + void shouldReturnFalseOnOrder(CommandOrder order) { + var isResponsible = processor.isResponsible(order.name()); + + assertThat(isResponsible).isFalse(); + } + } + + @Test + void shouldCreateLinkBuilder() { + var linkBuilder = processor.createRelatedResourceLinkBuilder(CommandTestFactory.create()); + + assertThat(linkBuilder).hasToString(PostfachMailController.PATH + "/" + CommandTestFactory.RELATION_ID); + } + } + + @DisplayName("Effected resource") + @Nested + class TestEffectedResource { + + @DisplayName("is responsible") + @Nested + class TestIsResponsible { + + @EnumSource(mode = Mode.INCLUDE, names = { "SEND_POSTFACH_MAIL", "SEND_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL", + "RECEIVE_POSTFACH_NACHRICHT" }) + @ParameterizedTest(name = "{0}") + void shouldReturnTrueOnOrder(CommandOrder order) { + var isResponsible = processor.isResponsibleForEffectedResource(order.name()); + + assertThat(isResponsible).isTrue(); + } + + @EnumSource(mode = Mode.EXCLUDE, names = { "SEND_POSTFACH_MAIL", "SEND_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL", + "RECEIVE_POSTFACH_NACHRICHT" }) + @ParameterizedTest(name = "{0}") + void shouldReturnFalseOnOrder(CommandOrder order) { + var isResponsible = processor.isResponsibleForEffectedResource(order.name()); + + assertThat(isResponsible).isFalse(); + } + } + + @Test + void shouldCreateLinkBuilder() { + var linkBuilder = processor.createEffectedResourceLinkBuilder(CommandTestFactory.create()); + + assertThat(linkBuilder).hasToString(PostfachMailController.PATH + "?vorgangId=" + CommandTestFactory.VORGANG_ID); + } + } +} \ No newline at end of file -- GitLab From b84b32e08b6f89fbfed53e334f870298a43e6d2a Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 15:29:00 +0200 Subject: [PATCH 2/4] OZG-7872 use enum instead of string; adjust naming; remove redundant order --- .../common/command/CommandModelAssembler.java | 16 ++-- .../alfa/common/command/CommandProcessor.java | 11 +-- .../PostfachNachrichtCommandProcessor.java | 25 +++-- .../common/command/CommandProcessorTest.java | 93 ++++++++++++++----- ...PostfachNachrichtCommandProcessorTest.java | 8 +- 5 files changed, 98 insertions(+), 55 deletions(-) diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java index 467466ef0b..eb69a13aeb 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java @@ -58,15 +58,13 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT; - private static final List<String> NOT_PROCESSABLE_ORDER = List.of( - CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name(), - CommandOrder.RESEND_POSTFACH_MAIL.name(), - CommandOrder.SEND_POSTFACH_MAIL.name(), - CommandOrder.SEND_POSTFACH_NACHRICHT.name(), - CommandOrder.RESEND_POSTFACH_MAIL.name(), - CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name()); - - static final Predicate<Command> IS_PROCESSABLE_ORDER = command -> !NOT_PROCESSABLE_ORDER.contains(command.getOrder()); + private static final List<CommandOrder> ORDER_PROCESSED_SEPERATELY = List.of( + CommandOrder.RECEIVE_POSTFACH_NACHRICHT, + CommandOrder.RESEND_POSTFACH_MAIL, + CommandOrder.SEND_POSTFACH_MAIL, + CommandOrder.SEND_POSTFACH_NACHRICHT); + + static final Predicate<Command> IS_PROCESSABLE_ORDER = command -> !ORDER_PROCESSED_SEPERATELY.contains(command.getCommandOrder()); private final List<CommandProcessor> processors; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java index a7343b9a3f..77d8d40bf0 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java @@ -1,7 +1,6 @@ package de.ozgcloud.alfa.common.command; import java.util.Objects; -import java.util.function.Predicate; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.LinkRelation; @@ -12,8 +11,6 @@ public abstract class CommandProcessor { static final LinkRelation REL_EFFECTED_RESOURCE = LinkRelation.of("effected_resource"); static final LinkRelation REL_RELATED_RESOURCE = LinkRelation.of("related_resource"); - static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT; - public void process(EntityModel<Command> model) { var command = model.getContent(); @@ -21,17 +18,17 @@ public abstract class CommandProcessor { return; } - model.addIf(CommandHelper.IS_DONE.test(command) && isResponsibleForEffectedResource(command.getOrder()), + model.addIf(CommandHelper.IS_DONE.test(command) && isResponsibleForEffectedResource(command.getCommandOrder()), () -> createEffectedResourceLinkBuilder(command).withRel(REL_EFFECTED_RESOURCE)); - model.addIf(isResponsible(command.getOrder()), + model.addIf(isResponsibleForRelatedResource(command.getCommandOrder()), () -> createRelatedResourceLinkBuilder(command).withRel(REL_RELATED_RESOURCE)); } - public abstract boolean isResponsibleForEffectedResource(String order); + public abstract boolean isResponsibleForEffectedResource(CommandOrder order); public abstract WebMvcLinkBuilder createEffectedResourceLinkBuilder(Command command); - public abstract boolean isResponsible(String order); + public abstract boolean isResponsibleForRelatedResource(CommandOrder order); public abstract WebMvcLinkBuilder createRelatedResourceLinkBuilder(Command command); } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java index 72f871b053..358f358b47 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java @@ -3,7 +3,6 @@ package de.ozgcloud.alfa.postfach; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; import java.util.List; -import java.util.Optional; import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.stereotype.Component; @@ -15,19 +14,19 @@ import de.ozgcloud.alfa.common.command.CommandProcessor; @Component public class PostfachNachrichtCommandProcessor extends CommandProcessor { - private static final List<String> RESPONSIBLE_ORDER = List.of( - CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name(), - CommandOrder.RESEND_POSTFACH_MAIL.name()); + private static final List<CommandOrder> RESPONSIBLE_ORDER = List.of( + CommandOrder.RECEIVE_POSTFACH_NACHRICHT, + CommandOrder.RESEND_POSTFACH_MAIL); - private static final List<String> RESPONSIBLE_EFFECTED_RESOURCE_ORDER = List.of( - CommandOrder.SEND_POSTFACH_MAIL.name(), - CommandOrder.SEND_POSTFACH_NACHRICHT.name(), - CommandOrder.RESEND_POSTFACH_MAIL.name(), - CommandOrder.RECEIVE_POSTFACH_NACHRICHT.name()); + private static final List<CommandOrder> RESPONSIBLE_EFFECTED_RESOURCE_ORDER = List.of( + CommandOrder.SEND_POSTFACH_MAIL, + CommandOrder.SEND_POSTFACH_NACHRICHT, + CommandOrder.RESEND_POSTFACH_MAIL, + CommandOrder.RECEIVE_POSTFACH_NACHRICHT); @Override - public boolean isResponsible(String order) { - return Optional.ofNullable(order).map(RESPONSIBLE_ORDER::contains).orElse(false); + public boolean isResponsibleForRelatedResource(CommandOrder order) { + return RESPONSIBLE_ORDER.contains(order); } @Override @@ -36,8 +35,8 @@ public class PostfachNachrichtCommandProcessor extends CommandProcessor { } @Override - public boolean isResponsibleForEffectedResource(String order) { - return Optional.ofNullable(order).map(RESPONSIBLE_EFFECTED_RESOURCE_ORDER::contains).orElse(false); + public boolean isResponsibleForEffectedResource(CommandOrder order) { + return RESPONSIBLE_EFFECTED_RESOURCE_ORDER.contains(order); } @Override diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java index 224152f96e..d62ad2e600 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java @@ -21,6 +21,11 @@ class CommandProcessorTest { @Nested class TestProcess { + private final Command command = CommandTestFactory.create(); + private final EntityModel<Command> model = EntityModel.of(command); + + private final WebMvcLinkBuilder linkBuilder = WebMvcLinkBuilder.linkTo(CommandController.class).slash("id"); + @Test void shouldNotThrowExceptionsOnNullContent() { EntityModel<Command> entityModel = when(mock(EntityModel.class).getContent()).thenReturn(null).getMock(); @@ -28,49 +33,93 @@ class CommandProcessorTest { assertDoesNotThrow(() -> processor.process(entityModel)); } - @DisplayName("effected resource link") + @DisplayName("effected resource") @Nested class TestEffectedResource { + @DisplayName("should verify if responsibility is related to the order") + @Test + void shouldCallIsResponsibleForEffectedResource() { + processor.process(model); + + verify(processor).isResponsibleForEffectedResource(CommandOrder.fromOrder(CommandTestFactory.ORDER)); + } + + @DisplayName("should create link builder for effected resource if the responsibility matches") @Test - void shouldBeAddedIfResponsible() { - var linkBuilder = WebMvcLinkBuilder.linkTo(CommandController.class).slash("id"); - doReturn(true).when(processor).isResponsibleForEffectedResource(any()); + void shouldCallCreateEffectedResourceLinkBuilder() { + when(processor.isResponsibleForEffectedResource(any())).thenReturn(true); doReturn(linkBuilder).when(processor).createEffectedResourceLinkBuilder(any()); - var command = CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).build(); - var entityModel = EntityModel.of(command); - processor.process(entityModel); + processor.process(model); - assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + verify(processor).createEffectedResourceLinkBuilder(command); } - @Test - void shouldNotBeAddedIfCommandNotDone() { - var command = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build(); - var entityModel = EntityModel.of(command); + @DisplayName("link") + @Nested + class TestEffectedResourceLink { + + @Test + void shouldBeAddedIfResponsible() { + doReturn(true).when(processor).isResponsibleForEffectedResource(any()); + doReturn(linkBuilder).when(processor).createEffectedResourceLinkBuilder(any()); + var finishedCommand = CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).build(); + var entityModel = EntityModel.of(finishedCommand); + + processor.process(entityModel); + + assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + } - processor.process(entityModel); + @Test + void shouldNotBeAddedIfCommandNotDone() { + var pendingCommand = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build(); + var entityModel = EntityModel.of(pendingCommand); - assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE)).isNotPresent(); + processor.process(entityModel); + + assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE)).isNotPresent(); + } } } - @DisplayName("related resource link") + @DisplayName("related resource") @Nested class TestRelatedResource { + @DisplayName("should verify if responsibility is related to the order") + @Test + void shouldCallIsResponsibleForRelatedResource() { + processor.process(model); + + verify(processor).isResponsibleForRelatedResource(CommandOrder.fromOrder(CommandTestFactory.ORDER)); + } + + @DisplayName("should create link builder for related resource if the responsibility matches") @Test - void shouldBeAddedIfResponsible() { - var linkBuilder = WebMvcLinkBuilder.linkTo(CommandController.class).slash("id"); - doReturn(true).when(processor).isResponsible(any()); + void shouldCallCreateRelatedResourceLinkBuilder() { + when(processor.isResponsibleForRelatedResource(any())).thenReturn(true); doReturn(linkBuilder).when(processor).createRelatedResourceLinkBuilder(any()); - var command = CommandTestFactory.create(); - var entityModel = EntityModel.of(command); - processor.process(entityModel); + processor.process(model); + + verify(processor).createRelatedResourceLinkBuilder(command); + } + + @DisplayName("link") + @Nested + class TestRelatedResourceLink { + + @Test + void shouldBeAddedIfResponsible() { + doReturn(true).when(processor).isResponsibleForRelatedResource(any()); + doReturn(linkBuilder).when(processor).createRelatedResourceLinkBuilder(any()); + + processor.process(model); - assertThat(entityModel.getLink(CommandProcessor.REL_RELATED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + assertThat(model.getLink(CommandProcessor.REL_RELATED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id"); + } } } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java index a4d6b2272e..5b2cbca51b 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessorTest.java @@ -29,7 +29,7 @@ class PostfachNachrichtCommandProcessorTest { @EnumSource(mode = Mode.INCLUDE, names = { "RECEIVE_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL" }) @ParameterizedTest(name = "{0}") void shouldReturnTrueOnOrder(CommandOrder order) { - var isResponsible = processor.isResponsible(order.name()); + var isResponsible = processor.isResponsibleForRelatedResource(order); assertThat(isResponsible).isTrue(); } @@ -37,7 +37,7 @@ class PostfachNachrichtCommandProcessorTest { @EnumSource(mode = Mode.EXCLUDE, names = { "RECEIVE_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL" }) @ParameterizedTest(name = "{0}") void shouldReturnFalseOnOrder(CommandOrder order) { - var isResponsible = processor.isResponsible(order.name()); + var isResponsible = processor.isResponsibleForRelatedResource(order); assertThat(isResponsible).isFalse(); } @@ -63,7 +63,7 @@ class PostfachNachrichtCommandProcessorTest { "RECEIVE_POSTFACH_NACHRICHT" }) @ParameterizedTest(name = "{0}") void shouldReturnTrueOnOrder(CommandOrder order) { - var isResponsible = processor.isResponsibleForEffectedResource(order.name()); + var isResponsible = processor.isResponsibleForEffectedResource(order); assertThat(isResponsible).isTrue(); } @@ -72,7 +72,7 @@ class PostfachNachrichtCommandProcessorTest { "RECEIVE_POSTFACH_NACHRICHT" }) @ParameterizedTest(name = "{0}") void shouldReturnFalseOnOrder(CommandOrder order) { - var isResponsible = processor.isResponsibleForEffectedResource(order.name()); + var isResponsible = processor.isResponsibleForEffectedResource(order); assertThat(isResponsible).isFalse(); } -- GitLab From 28a05fb394e51a37bf1d7f78e84fc1dfe6c03b9c Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 16:28:39 +0200 Subject: [PATCH 3/4] OZG-7872 add safety tests --- .../common/command/CommandProcessorTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java index d62ad2e600..edc832bafc 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java @@ -45,6 +45,16 @@ class CommandProcessorTest { verify(processor).isResponsibleForEffectedResource(CommandOrder.fromOrder(CommandTestFactory.ORDER)); } + @DisplayName("should NOT create link builder for related resource if the responsibility matches") + @Test + void shouldNOTCallCreateEffectedResourceLinkBuilder() { + when(processor.isResponsibleForEffectedResource(any())).thenReturn(false); + + processor.process(model); + + verify(processor, never()).createEffectedResourceLinkBuilder(command); + } + @DisplayName("should create link builder for effected resource if the responsibility matches") @Test void shouldCallCreateEffectedResourceLinkBuilder() { @@ -107,6 +117,16 @@ class CommandProcessorTest { verify(processor).createRelatedResourceLinkBuilder(command); } + @DisplayName("should NOT create link builder for related resource if the responsibility matches") + @Test + void shouldNOTCallCreateRelatedResourceLinkBuilder() { + when(processor.isResponsibleForRelatedResource(any())).thenReturn(false); + + processor.process(model); + + verify(processor, never()).createRelatedResourceLinkBuilder(command); + } + @DisplayName("link") @Nested class TestRelatedResourceLink { -- GitLab From 17669612e79f16181180335eb460fbd03b4b06bb Mon Sep 17 00:00:00 2001 From: Martin <git@mail.de> Date: Thu, 3 Apr 2025 16:28:48 +0200 Subject: [PATCH 4/4] OZG-7872 revert switch case --- .../common/command/CommandModelAssembler.java | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java index eb69a13aeb..74c0bcb2df 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandModelAssembler.java @@ -34,6 +34,7 @@ import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.server.RepresentationModelAssembler; +import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.bescheid.BescheidController; @@ -86,31 +87,19 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent Link effectedResourceLinkByOrderType(Command entity) { var type = entity.getCommandOrder().getType(); - if (type == CommandOrder.Type.FORWARDING) { - return linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.KOMMENTAR) { - return linkTo(KommentarController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.VORGANG) { - return linkTo(VorgangController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.VORGANG_LIST) { - return linkTo(VorgangController.class).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.WIEDERVORLAGE) { - return linkTo(WiedervorlageController.class).slash(entity.getRelationId()).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.BESCHEID) { - return linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.DOCUMENT) { - return linkTo(DocumentController.class).slash(entity.getCreatedResource()).withRel(REL_EFFECTED_RESOURCE); - } - if (type == CommandOrder.Type.COLLABORATION) { - return linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId())).withRel(REL_EFFECTED_RESOURCE); - } - throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); + WebMvcLinkBuilder linkBuilder = switch (type) { + case FORWARDING -> linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())); + case KOMMENTAR -> linkTo(KommentarController.class).slash(entity.getRelationId()); + case VORGANG -> linkTo(VorgangController.class).slash(entity.getRelationId()); + case VORGANG_LIST -> linkTo(VorgangController.class); + case WIEDERVORLAGE -> linkTo(WiedervorlageController.class).slash(entity.getRelationId()); + case BESCHEID -> linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId())); + case DOCUMENT -> linkTo(DocumentController.class).slash(entity.getCreatedResource()); + case COLLABORATION -> linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId())); + default -> throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); + }; + + return linkBuilder.withRel(REL_EFFECTED_RESOURCE); } public CollectionModel<EntityModel<Command>> toCollectionModel(Stream<Command> entities) { -- GitLab