Skip to content
Snippets Groups Projects
Commit 49ef936b authored by Martin Küster's avatar Martin Küster
Browse files

Merge branch 'OZG-7872-ErneutVersenden' into 'main'

OZG-7872 OZG-8039 add related resource link on postfach

See merge request !26
parents eb77b5c2 17669612
Branches
Tags
1 merge request!26OZG-7872 OZG-8039 add related resource link on postfach
...@@ -25,6 +25,7 @@ package de.ozgcloud.alfa.common.command; ...@@ -25,6 +25,7 @@ package de.ozgcloud.alfa.common.command;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Stream; import java.util.stream.Stream;
...@@ -41,10 +42,11 @@ import de.ozgcloud.alfa.bescheid.DocumentController; ...@@ -41,10 +42,11 @@ import de.ozgcloud.alfa.bescheid.DocumentController;
import de.ozgcloud.alfa.collaboration.CollaborationController.CollaborationByVorgangController; import de.ozgcloud.alfa.collaboration.CollaborationController.CollaborationByVorgangController;
import de.ozgcloud.alfa.forwarding.ForwardingController; import de.ozgcloud.alfa.forwarding.ForwardingController;
import de.ozgcloud.alfa.kommentar.KommentarController; import de.ozgcloud.alfa.kommentar.KommentarController;
import de.ozgcloud.alfa.postfach.PostfachMailController;
import de.ozgcloud.alfa.vorgang.VorgangController; import de.ozgcloud.alfa.vorgang.VorgangController;
import de.ozgcloud.alfa.wiedervorlage.WiedervorlageController; import de.ozgcloud.alfa.wiedervorlage.WiedervorlageController;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Component @Component
class CommandModelAssembler implements RepresentationModelAssembler<Command, EntityModel<Command>> { class CommandModelAssembler implements RepresentationModelAssembler<Command, EntityModel<Command>> {
...@@ -57,14 +59,29 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent ...@@ -57,14 +59,29 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent
static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT; static final Predicate<Command> HAS_KNOWN_COMMAND_ORDER = command -> command.getCommandOrder() != CommandOrder.UNBEKANNT;
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;
@Override @Override
public EntityModel<Command> toModel(Command command) { public EntityModel<Command> toModel(Command command) {
return EntityModel.of(command) var entityModel = EntityModel.of(command)
.add(linkTo(CommandController.class).slash(command.getId()).withSelfRel()) .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(CommandHelper.IS_PENDING.test(command), () -> linkTo(CommandController.class).slash(command.getId()).withRel(REL_UPDATE))
.addIf(IS_NOT_LOESCH_ANFORDERUNG_AND_REVOKEABLE.test(command), .addIf(IS_NOT_LOESCH_ANFORDERUNG_AND_REVOKEABLE.test(command),
() -> linkTo(methodOn(CommandController.class).revoke(command.getId(), null)).withRel(REL_REVOKE)); () -> linkTo(methodOn(CommandController.class).revoke(command.getId(), null)).withRel(REL_REVOKE));
processors.forEach(processor -> processor.process(entityModel));
return entityModel;
} }
Link effectedResourceLinkByOrderType(Command entity) { Link effectedResourceLinkByOrderType(Command entity) {
...@@ -73,14 +90,13 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent ...@@ -73,14 +90,13 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent
WebMvcLinkBuilder linkBuilder = switch (type) { WebMvcLinkBuilder linkBuilder = switch (type) {
case FORWARDING -> linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId())); case FORWARDING -> linkTo(methodOn(ForwardingController.class).findByVorgangId(entity.getVorgangId()));
case KOMMENTAR -> linkTo(KommentarController.class).slash(entity.getRelationId()); 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 -> linkTo(VorgangController.class).slash(entity.getRelationId());
case VORGANG_LIST -> linkTo(VorgangController.class); case VORGANG_LIST -> linkTo(VorgangController.class);
case WIEDERVORLAGE -> linkTo(WiedervorlageController.class).slash(entity.getRelationId()); case WIEDERVORLAGE -> linkTo(WiedervorlageController.class).slash(entity.getRelationId());
case BESCHEID -> linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId())); case BESCHEID -> linkTo(methodOn(BescheidController.class).getDraft(entity.getVorgangId()));
case DOCUMENT -> linkTo(DocumentController.class).slash(entity.getCreatedResource()); case DOCUMENT -> linkTo(DocumentController.class).slash(entity.getCreatedResource());
case COLLABORATION -> linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId())); case COLLABORATION -> linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(entity.getVorgangId()));
case NONE -> throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); default -> throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder());
}; };
return linkBuilder.withRel(REL_EFFECTED_RESOURCE); return linkBuilder.withRel(REL_EFFECTED_RESOURCE);
......
package de.ozgcloud.alfa.common.command;
import java.util.Objects;
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");
public void process(EntityModel<Command> model) {
var command = model.getContent();
if (Objects.isNull(command)) {
return;
}
model.addIf(CommandHelper.IS_DONE.test(command) && isResponsibleForEffectedResource(command.getCommandOrder()),
() -> createEffectedResourceLinkBuilder(command).withRel(REL_EFFECTED_RESOURCE));
model.addIf(isResponsibleForRelatedResource(command.getCommandOrder()),
() -> createRelatedResourceLinkBuilder(command).withRel(REL_RELATED_RESOURCE));
}
public abstract boolean isResponsibleForEffectedResource(CommandOrder order);
public abstract WebMvcLinkBuilder createEffectedResourceLinkBuilder(Command command);
public abstract boolean isResponsibleForRelatedResource(CommandOrder order);
public abstract WebMvcLinkBuilder createRelatedResourceLinkBuilder(Command command);
}
\ No newline at end of file
package de.ozgcloud.alfa.postfach;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
import java.util.List;
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<CommandOrder> RESPONSIBLE_ORDER = List.of(
CommandOrder.RECEIVE_POSTFACH_NACHRICHT,
CommandOrder.RESEND_POSTFACH_MAIL);
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 isResponsibleForRelatedResource(CommandOrder order) {
return RESPONSIBLE_ORDER.contains(order);
}
@Override
public WebMvcLinkBuilder createRelatedResourceLinkBuilder(Command command) {
return linkTo(PostfachMailController.class).slash(command.getRelationId());
}
@Override
public boolean isResponsibleForEffectedResource(CommandOrder order) {
return RESPONSIBLE_EFFECTED_RESOURCE_ORDER.contains(order);
}
@Override
public WebMvcLinkBuilder createEffectedResourceLinkBuilder(Command command) {
return linkTo(methodOn(PostfachMailController.class).getAll(command.getVorgangId()));
}
}
\ No newline at end of file
...@@ -27,7 +27,12 @@ import static de.ozgcloud.alfa.common.command.CommandModelAssembler.*; ...@@ -27,7 +27,12 @@ import static de.ozgcloud.alfa.common.command.CommandModelAssembler.*;
import static de.ozgcloud.alfa.common.command.CommandTestFactory.*; import static de.ozgcloud.alfa.common.command.CommandTestFactory.*;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.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.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
...@@ -35,7 +40,11 @@ import org.junit.jupiter.params.ParameterizedTest; ...@@ -35,7 +40,11 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode; import org.junit.jupiter.params.provider.EnumSource.Mode;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.EntityModel;
import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.IanaLinkRelations;
import org.springframework.hateoas.Link; import org.springframework.hateoas.Link;
...@@ -49,6 +58,19 @@ class CommandModelAssemblerTest { ...@@ -49,6 +58,19 @@ class CommandModelAssemblerTest {
@InjectMocks @InjectMocks
private CommandModelAssembler modelAssembler; 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 @Test
void shouldHaveSelfLink() { void shouldHaveSelfLink() {
var model = modelAssembler.toModel(CommandTestFactory.create()); var model = modelAssembler.toModel(CommandTestFactory.create());
...@@ -56,6 +78,16 @@ class CommandModelAssemblerTest { ...@@ -56,6 +78,16 @@ class CommandModelAssemblerTest {
assertThat(model.getLink(IanaLinkRelations.SELF)).isPresent().get().extracting(Link::getHref).isEqualTo(COMMAND_SINGLE_PATH); 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 @Nested
@DisplayName("Link to effected Resource") @DisplayName("Link to effected Resource")
class TestEffectedResourceLink { class TestEffectedResourceLink {
...@@ -103,22 +135,6 @@ class CommandModelAssemblerTest { ...@@ -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") @DisplayName("on bescheid")
@Nested @Nested
class TestOnBescheid { class TestOnBescheid {
...@@ -196,7 +212,8 @@ class CommandModelAssemblerTest { ...@@ -196,7 +212,8 @@ class CommandModelAssemblerTest {
} }
@ParameterizedTest @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) { void shouldBeAbleToBuildLinkForEveryOrder(CommandOrder order) {
var link = modelAssembler.effectedResourceLinkByOrderType(CommandTestFactory.createBuilder().order(order.name()).build()); var link = modelAssembler.effectedResourceLinkByOrderType(CommandTestFactory.createBuilder().order(order.name()).build());
......
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 {
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();
assertDoesNotThrow(() -> processor.process(entityModel));
}
@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 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() {
when(processor.isResponsibleForEffectedResource(any())).thenReturn(true);
doReturn(linkBuilder).when(processor).createEffectedResourceLinkBuilder(any());
processor.process(model);
verify(processor).createEffectedResourceLinkBuilder(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");
}
@Test
void shouldNotBeAddedIfCommandNotDone() {
var pendingCommand = CommandTestFactory.createBuilder().status(CommandStatus.PENDING).build();
var entityModel = EntityModel.of(pendingCommand);
processor.process(entityModel);
assertThat(entityModel.getLink(CommandProcessor.REL_EFFECTED_RESOURCE)).isNotPresent();
}
}
}
@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 shouldCallCreateRelatedResourceLinkBuilder() {
when(processor.isResponsibleForRelatedResource(any())).thenReturn(true);
doReturn(linkBuilder).when(processor).createRelatedResourceLinkBuilder(any());
processor.process(model);
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 {
@Test
void shouldBeAddedIfResponsible() {
doReturn(true).when(processor).isResponsibleForRelatedResource(any());
doReturn(linkBuilder).when(processor).createRelatedResourceLinkBuilder(any());
processor.process(model);
assertThat(model.getLink(CommandProcessor.REL_RELATED_RESOURCE).get().getHref()).isEqualTo("/api/commands/id");
}
}
}
}
}
\ No newline at end of file
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.isResponsibleForRelatedResource(order);
assertThat(isResponsible).isTrue();
}
@EnumSource(mode = Mode.EXCLUDE, names = { "RECEIVE_POSTFACH_NACHRICHT", "RESEND_POSTFACH_MAIL" })
@ParameterizedTest(name = "{0}")
void shouldReturnFalseOnOrder(CommandOrder order) {
var isResponsible = processor.isResponsibleForRelatedResource(order);
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);
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);
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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment