diff --git a/alfa-server/pom.xml b/alfa-server/pom.xml index c8ccccc16b2b77c2fef591921e0a388c1d8a7be7..c718c96e7fabdd9c9fac8638ea217b14f20b0521 100644 --- a/alfa-server/pom.xml +++ b/alfa-server/pom.xml @@ -32,7 +32,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.22.0-SNAPSHOT</version> + <version>2.23.0-SNAPSHOT</version> </parent> <artifactId>alfa-server</artifactId> diff --git a/alfa-server/src/main/resources/application.yml b/alfa-server/src/main/resources/application.yml index 7e2109ed1ed9d0638cd233a78efcc2bfb7d32938..71412abf77bd3d0db0233b6dd7802fc91edf7e18 100644 --- a/alfa-server/src/main/resources/application.yml +++ b/alfa-server/src/main/resources/application.yml @@ -1,7 +1,7 @@ logging: level: ROOT: WARN - '[de.ozgcloud]': INFO, + '[de.ozgcloud]': INFO '[org.springframework.security]': WARN spring: @@ -51,7 +51,7 @@ management: probes: enabled: true prometheus: - enabled: true + access: READ_ONLY endpoints: web: exposure: diff --git a/alfa-service/pom.xml b/alfa-service/pom.xml index 9c330c6d5115105e166ac8e68d09fcf36f0fc569..018f05fe9afd18e0caf68868801cdf940be47239 100644 --- a/alfa-service/pom.xml +++ b/alfa-service/pom.xml @@ -33,7 +33,7 @@ <parent> <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.22.0-SNAPSHOT</version> + <version>2.23.0-SNAPSHOT</version> </parent> <artifactId>alfa-service</artifactId> diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/RootViewLinkHandler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/RootViewLinkHandler.java index a8af53edb791331f6beb006f0b95bd89d8758f8d..6b1eddb06bbdde10b0b234ace883e20d2978bc65 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/RootViewLinkHandler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/RootViewLinkHandler.java @@ -27,12 +27,14 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.server.LinkBuilder; import org.springframework.stereotype.Component; +import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserId; import de.ozgcloud.alfa.common.user.UserRole; @@ -68,11 +70,12 @@ class RootViewLinkHandler { static final int PAGE_SIZE = 100; + private static final List<VorgangStatus> STATUS_LIST = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, + VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN, VorgangStatus.WEITERGELEITET); + private final CurrentUserService currentUserService; private final SystemStatusService systemStatusService; - - private final List<VorgangStatus> statusList = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, - VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN); + private final FeatureToggleProperties featureToggleProperties; public void addViewLinks(EntityModel<Root> model, Optional<UserId> userId) { model.add(buildGetAllVorgaengeLink()); @@ -158,7 +161,7 @@ class RootViewLinkHandler { } void addVorgangStatusLinks(EntityModel<Root> model, String linkRelTemplate) { - statusList.forEach(status -> model.add(buildGetAllVorgaengeByStatus(status, linkRelTemplate))); + getVorgangStatuses().forEach(status -> model.add(buildGetAllVorgaengeByStatus(status, linkRelTemplate))); } Link buildGetAllVorgaengeByStatus(VorgangStatus status, String linkRelTemplate) { @@ -175,7 +178,11 @@ class RootViewLinkHandler { } List<Link> buildVorgangStatusLinks(String linkRelTemplate, UserId userId) { - return statusList.stream().map(status -> buildGetAllVorgaengeByAssignedToAndStatus(status, linkRelTemplate, userId)).toList(); + return getVorgangStatuses().map(status -> buildGetAllVorgaengeByAssignedToAndStatus(status, linkRelTemplate, userId)).toList(); + } + + Stream<VorgangStatus> getVorgangStatuses() { + return STATUS_LIST.stream().filter(status -> status != VorgangStatus.WEITERGELEITET || featureToggleProperties.isForwardByOzgCloudEnabled()); } Link buildGetAllVorgaengeByAssignedToAndStatus(VorgangStatus status, String linkRelTemplate, UserId userId) { @@ -214,4 +221,5 @@ class RootViewLinkHandler { .getAllByAssignedToAndUngeleseneNachrichten(0, PAGE_SIZE, userId, VorgangController.PARAM_NACHRICHTEN_UNGELESENE)) .withRel(linkRel); } + } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/Anschrift.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/Anschrift.java index 231090b96c06be539ba68fe68213163869d49202..96b2d58709780761d8bbc82baa57bd18d781e928 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/Anschrift.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/Anschrift.java @@ -28,7 +28,7 @@ import lombok.Getter; @Builder @Getter -class Anschrift { +public class Anschrift { private String strasse; private String hausnummer; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessor.java index 2f75114f05193871bb4c78621f119d912075065f..919b64ed9a0281c46620de22ff49c1c1761de22d 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessor.java @@ -25,7 +25,6 @@ package de.ozgcloud.alfa.collaboration; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; -import java.util.List; import java.util.Objects; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; @@ -47,7 +46,6 @@ import lombok.RequiredArgsConstructor; class CollaborationVorgangProcessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { static final LinkRelation REL_COLLABORATIONS = LinkRelation.of("collaborations"); - static final LinkRelation REL_SEARCH_ORGANISATIONS_EINHEIT = LinkRelation.of("searchOrganisationsEinheit"); static final LinkRelation REL_SEARCH_FACHSTELLE = LinkRelation.of("searchFachstelle"); private final CurrentUserService currentUserService; @@ -60,17 +58,11 @@ class CollaborationVorgangProcessor implements RepresentationModelProcessor<Enti if (Objects.isNull(vorgang) || !currentUserService.hasRole(UserRole.VERWALTUNG_USER)) { return model; } - model.addAllIf(!collaborationService.hasCollaboration(vorgang.getId()), - () -> List.of(buildSearchOrganisationsEinheitLink(), buildSearchFachstelleLink())) + model.addIf(!collaborationService.hasCollaboration(vorgang.getId()), this::buildSearchFachstelleLink) .add(linkTo(methodOn(CollaborationByVorgangController.class).getAllByVorgangId(vorgang.getId())).withRel(REL_COLLABORATIONS)); return model; } - private Link buildSearchOrganisationsEinheitLink() { - return linkTo(methodOn(OrganisationsEinheitController.class).search(null)).withRel( - REL_SEARCH_ORGANISATIONS_EINHEIT); - } - private Link buildSearchFachstelleLink() { return linkTo(methodOn(FachstelleController.class).search(null)).withRel(REL_SEARCH_FACHSTELLE); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheit.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheit.java index aeccb1786c7795171c5a65a07674a5cb36484512..56e5510b8ada0b536e60a9f8e215f5d509124843 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheit.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheit.java @@ -30,7 +30,7 @@ import lombok.extern.jackson.Jacksonized; @Builder @Getter @Jacksonized -class OrganisationsEinheit { +public class OrganisationsEinheit { private String id; private XzufiId xzufiId; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitController.java index 911717902889dfc59197573842a89151a6792c83..a67353b79b2ba863b703a37775fe7c647edfd917 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitController.java @@ -40,7 +40,7 @@ import lombok.RequiredArgsConstructor; @RestController @RequestMapping(OrganisationsEinheitController.PATH) @RequiredArgsConstructor -class OrganisationsEinheitController { +public class OrganisationsEinheitController { static final String PATH = "/api/organisationseinheits"; // NOSONAR static final String SEARCH_BY_PARAM = "searchBy"; @@ -59,4 +59,8 @@ class OrganisationsEinheitController { public CollectionModel<EntityModel<OrganisationsEinheitHeader>> search(@RequestParam String searchBy) { return headerModelAssembler.toCollectionModel(service.searchOrganisationsEinheiten(searchBy).toList()); } + + public OrganisationsEinheit getOrganisationEinheit(String organisationEinheitId) { + return service.getById(organisationEinheitId); + } } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitHeader.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitHeader.java index b62a4788a8fd963365a2e7e3833572917297a816..ab2b3b4c6a2e8e3efcbbcf636cc11ef7946e6f4d 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitHeader.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitHeader.java @@ -30,7 +30,7 @@ import lombok.Getter; @Builder @Getter -class OrganisationsEinheitHeader { +public class OrganisationsEinheitHeader { @JsonIgnore private String id; diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..d5a371ede44244bef75c221ce645fd50b296b742 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessor.java @@ -0,0 +1,57 @@ +package de.ozgcloud.alfa.collaboration; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import java.util.Optional; + +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; +import org.springframework.hateoas.LinkRelation; +import org.springframework.hateoas.server.RepresentationModelProcessor; +import org.springframework.stereotype.Component; + +import de.ozgcloud.alfa.common.FeatureToggleProperties; +import de.ozgcloud.alfa.common.user.CurrentUserService; +import de.ozgcloud.alfa.common.user.UserRole; +import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +class OrganisationsEinheitVorgangWithEingangProcessor implements RepresentationModelProcessor<EntityModel<VorgangWithEingang>> { + + static final LinkRelation REL_SEARCH_ORGANISATIONS_EINHEIT = LinkRelation.of("searchOrganisationsEinheit"); + + private final FeatureToggleProperties featureToggleProperties; + private final CurrentUserService userService; + + @Override + public EntityModel<VorgangWithEingang> process(EntityModel<VorgangWithEingang> model) { + Optional.ofNullable(model.getContent()) + .ifPresent(vorgang -> model.addIf(isSearchNeeded(vorgang), + this::buildSearchOrganisationsEinheitLink)); + return model; + } + + boolean isSearchNeeded(VorgangWithEingang vorgang) { + return this.isForwardable(vorgang) || this.isCollaborationCreateable(); + } + + boolean isForwardable(VorgangWithEingang vorgang) { + return featureToggleProperties.isForwardByOzgCloudEnabled() && isStatusNeu(vorgang); + } + + boolean isCollaborationCreateable() { + return featureToggleProperties.isCollaborationEnabled() && this.userService.hasRole(UserRole.VERWALTUNG_USER); + } + + private boolean isStatusNeu(VorgangWithEingang vorgang) { + return vorgang.getStatus() == VorgangStatus.NEU; + } + + private Link buildSearchOrganisationsEinheitLink() { + return linkTo(methodOn(OrganisationsEinheitController.class).search(null)).withRel( + REL_SEARCH_ORGANISATIONS_EINHEIT); + } +} \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeService.java index 3920a077b01adce74965354f6bac185112d3fc3c..109c3cc16ff77ccd10019a80caf011c3cb1fbb7a 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeService.java @@ -37,4 +37,7 @@ public class ClientAttributeService { remoteService.setBooleanReadOnlyClientAttribute(vorgangId, ClientAttribute.HAS_NEW_POSTFACH_NACHRICHT, false); } + public void setHasNewPostfachNachricht(String vorgangId) { + remoteService.setBooleanReadOnlyClientAttribute(vorgangId, ClientAttribute.HAS_NEW_POSTFACH_NACHRICHT, true); + } } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java index 46db2e9b657a09f9d98f4a25c77d800a07cdba1b..1f288483dc782a92baade909ff64f582c2ae53f9 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandBody.java @@ -30,6 +30,7 @@ import de.ozgcloud.alfa.aktenzeichen.AktenzeichenCommandBody; import de.ozgcloud.alfa.bescheid.Bescheid; import de.ozgcloud.alfa.bescheid.BescheidDocumentFromFileBody; import de.ozgcloud.alfa.collaboration.CollaborationCommandBody; +import de.ozgcloud.alfa.forwarding.ForwardVorgangCommandBody; import de.ozgcloud.alfa.forwarding.RedirectRequest; import de.ozgcloud.alfa.kommentar.Kommentar; import de.ozgcloud.alfa.loeschanforderung.DeleteLoeschAnforderung; @@ -57,7 +58,8 @@ import de.ozgcloud.alfa.wiedervorlage.Wiedervorlage; @Type(value = AktenzeichenCommandBody.class, name = "SET_AKTENZEICHEN"), @Type(value = BescheidDocumentFromFileBody.class, name = "CREATE_BESCHEID_DOCUMENT_FROM_FILE"), @Type(value = CollaborationCommandBody.class, name = "CREATE_COLLABORATION_REQUEST"), - @Type(value = EmptyCommandBody.class, name = "ARCHIVE_VORGANG") + @Type(value = EmptyCommandBody.class, name = "ARCHIVE_VORGANG"), + @Type(value = ForwardVorgangCommandBody.class, name = "FORWARD_VORGANG") }) public interface CommandBody { } 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 94f0209dfd9324b206466fe52e0ae806d26fbb16..74c0bcb2df4a2abeae92823d91b7c51a1c15ebb5 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; @@ -41,10 +42,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,14 +59,29 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent 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 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) { @@ -73,14 +90,13 @@ class CommandModelAssembler implements RepresentationModelAssembler<Command, Ent 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()); + default -> throw new IllegalArgumentException("Unknown CommandOrder: " + entity.getOrder()); }; return linkBuilder.withRel(REL_EFFECTED_RESOURCE); diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandOrder.java b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandOrder.java index 5e97b02673c16a2ada39fd7c79ee931507668d46..16b13bd448c11814c6c41e8db031de579b1d9897 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandOrder.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandOrder.java @@ -73,6 +73,8 @@ public enum CommandOrder { PROCESS_VORGANG(false, Type.VORGANG), + FORWARD_VORGANG(false, Type.VORGANG), + CREATE_COLLABORATION_REQUEST(false, Type.COLLABORATION), UNBEKANNT(false, Type.NONE); 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 0000000000000000000000000000000000000000..77d8d40bf056a8936f15b04788e1d3ec33354a4c --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/common/command/CommandProcessor.java @@ -0,0 +1,34 @@ +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 diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandController.java new file mode 100644 index 0000000000000000000000000000000000000000..046d5308e00596afca7a6383ec77b80a5455fea2 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandController.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import de.ozgcloud.alfa.collaboration.OrganisationsEinheitController; +import de.ozgcloud.alfa.common.command.CommandController; +import de.ozgcloud.alfa.common.command.CommandService; +import de.ozgcloud.alfa.common.command.CreateCommand; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping(ForwardByVorgangCommandController.PATH) +@RequiredArgsConstructor +class ForwardByVorgangCommandController { + static final String PATH = "/api/vorgangs/{vorgangId}/{vorgangVersion}/forwarding"; + + private final CommandService commandService; + private final OrganisationsEinheitController organisationsEinheitController; + private final ForwardVorgangCommandBodyMapper bodyMapper; + + @PostMapping + public ResponseEntity<Void> createCommand(@PathVariable String vorgangId, @PathVariable long vorgangVersion, @RequestBody CreateCommand command) { + var completeBody = addOrganisationEinheitData((ForwardVorgangCommandBody) command.getBody()); + var enrichedCommand = command.toBuilder() + .vorgangId(vorgangId) + .relationId(vorgangId) + .body(completeBody) + .build(); + var created = commandService.createCommand(enrichedCommand, vorgangVersion); + return ResponseEntity.created(linkTo(CommandController.class).slash(created.getId()).toUri()).build(); + } + + + private ForwardVorgangCommandBody addOrganisationEinheitData(ForwardVorgangCommandBody body) { + var organisationEinheit = organisationsEinheitController.getOrganisationEinheit(body.getOrganisationEinheitId()); + return bodyMapper.updateFromOrganisationEinheit(organisationEinheit, body); + } +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBody.java b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBody.java new file mode 100644 index 0000000000000000000000000000000000000000..7770b4333db7c66abdccac117a2fc155a78c0997 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBody.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import de.ozgcloud.alfa.common.LinkedResourceDeserializer; +import de.ozgcloud.alfa.common.command.CommandBody; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder(toBuilder = true) +@AllArgsConstructor +@NoArgsConstructor +public class ForwardVorgangCommandBody implements CommandBody { + + @JsonDeserialize(using = LinkedResourceDeserializer.class) + private String organisationEinheitId; + + private String name; + private String strasse; + private String hausnummer; + private String plz; + private String ort; +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapper.java b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..d6d0cf677bbeda5357f5cd92bb1ed5e77e0641c8 --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; + +import de.ozgcloud.alfa.collaboration.OrganisationsEinheit; + +@Mapper +interface ForwardVorgangCommandBodyMapper { + + @Mapping(target = "organisationEinheitId", source = "xzufiId.id") + @Mapping(target = ".", source = "anschrift") + ForwardVorgangCommandBody updateFromOrganisationEinheit(OrganisationsEinheit organisationsEinheit, @MappingTarget ForwardVorgangCommandBody commandBody); +} diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessor.java b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessor.java index 714dc343d3a87d3ea38fa781a544b79121f254f5..fdb1d9cf0266b2d0ff8ca9f3594dd4164f9b9139 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessor.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessor.java @@ -34,7 +34,6 @@ import org.springframework.hateoas.server.RepresentationModelProcessor; import org.springframework.stereotype.Component; import de.ozgcloud.alfa.common.FeatureToggleProperties; -import de.ozgcloud.alfa.common.command.CommandController; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserRole; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; @@ -62,19 +61,15 @@ class ForwardingVorgangWithEingangProcessor implements RepresentationModelProces } boolean isForwardableByOzgCloud(VorgangWithEingang vorgang) { - return featureToggleProperties.isForwardByOzgCloudEnabled() && isStatusNeu(vorgang) && isVerwaltungUser(); + return featureToggleProperties.isForwardByOzgCloudEnabled() && isStatusNeu(vorgang); } private boolean isStatusNeu(VorgangWithEingang vorgang) { return vorgang.getStatus() == VorgangStatus.NEU; } - private boolean isVerwaltungUser() { - return userService.hasRole(UserRole.VERWALTUNG_USER); - } - private Link buildForwardByOzgCloudLink(VorgangWithEingang vorgang) { - return linkTo(methodOn(CommandController.CommandByRelationController.class).createCommand(vorgang.getId(), vorgang.getId(), + return linkTo(methodOn(ForwardByVorgangCommandController.class).createCommand(vorgang.getId(), vorgang.getVersion(), null)).withRel(REL_FORWARD_BY_OZGCLOUD); } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssembler.java b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssembler.java index d1f3b17742b04f08c6f30d4dac6ef11159425724..7809011ca6be8b3ce7de3fcbb35506b177a818dc 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssembler.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssembler.java @@ -25,6 +25,7 @@ package de.ozgcloud.alfa.postfach; import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*; +import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; @@ -58,6 +59,7 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac public static final String REL_SEND = "send"; public static final String REL_RESEND_POSTFACH_MAIL = "resendPostfachMail"; public static final String REL_RESET_NEW_POSTFACH_MAIL = "resetHasNewPostfachNachricht"; + public static final String REL_SET_HAS_NEW_POSTFACH_MAIL = "setHasNewPostfachNachricht"; public static final String REL_ATTACHMENTS = "attachments"; public static final String REL_EDIT = "edit"; static final String REL_UPLOAD_ATTACHMENT = "uploadAttachment"; @@ -67,6 +69,7 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac private static final String FILE_PATH = "file"; private static final Predicate<PostfachMail> IS_OUTGOING = postfachNachricht -> postfachNachricht.getDirection() == Direction.OUT; + private static final Predicate<PostfachMail> IS_INCOMING = postfachNachricht -> postfachNachricht.getDirection() == Direction.IN; private static final Predicate<PostfachMail> SENT_FAILED = postfachMail -> Objects.nonNull(postfachMail.getSentAt()) && BooleanUtils.isFalse(postfachMail.getSentSuccessful()); @@ -83,11 +86,11 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac public RepresentationModel<EntityModel<PostfachSettings>> toCollectionModel(Stream<PostfachMail> postfachMails, VorgangWithEingang vorgang, PostfachSettings postfachSettings) { - - var model = buildHalRepresentationModel(postfachMails, vorgang, postfachSettings); + var postfachMailsList = postfachMails.toList(); + var model = buildHalRepresentationModel(postfachMailsList.stream(), vorgang, postfachSettings); if (hasServiceKonto(vorgang)) { - addPostfachNachrichtLinks(model, vorgang); + addPostfachNachrichtLinks(model, buildVorgangMailInfo(postfachMailsList, vorgang)); } return model; } @@ -105,10 +108,18 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac return Optional.ofNullable(vorgang.getHeader()).map(VorgangHead::getServiceKonto).isPresent(); } - void addPostfachNachrichtLinks(RepresentationModel<EntityModel<PostfachSettings>> model, VorgangWithEingang vorgang) { - var vorgangId = vorgang.getId(); + VorgangMailInfo buildVorgangMailInfo(List<PostfachMail> postfachMails, VorgangWithEingang vorgang) { + return new VorgangMailInfo(vorgang, hasIncomingMails(postfachMails)); + } + + boolean hasIncomingMails(List<PostfachMail> postfachMails) { + return postfachMails.stream().anyMatch(IS_INCOMING); + } + + void addPostfachNachrichtLinks(RepresentationModel<EntityModel<PostfachSettings>> model, VorgangMailInfo vorgangMailInfo) { + var vorgangId = vorgangMailInfo.vorgang.getId(); - if (vorgangController.isEditable(vorgang)) { + if (vorgangController.isEditable(vorgangMailInfo.vorgang)) { model.add(linkTo(methodOn(PostfachMailCommandByVorgangController.class).sendPostfachMail(vorgangId, null)) .withRel(REL_SEND_POSTFACH_MAIL)); } @@ -116,8 +127,10 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac model.add(linkTo(BinaryFileController.class).slash(vorgangId).slash(POSTFACH_NACHRICHT_ATTACHMENT_FIELD).slash(FILE_PATH) .withRel(REL_UPLOAD_ATTACHMENT)); - if (vorgang.isHasNewPostfachNachricht()) { + if (vorgangMailInfo.vorgang.isHasNewPostfachNachricht()) { model.add(linkTo(VorgangController.class).slash(vorgangId).slash(HAS_NEW_POSTFACH_NACHRICHT_FIELD).withRel(REL_RESET_NEW_POSTFACH_MAIL)); + } else if (vorgangMailInfo.hasIncomingMails()) { + model.add(linkTo(VorgangController.class).slash(vorgangId).slash(HAS_NEW_POSTFACH_NACHRICHT_FIELD).withRel(REL_SET_HAS_NEW_POSTFACH_MAIL)); } } @@ -141,4 +154,5 @@ class PostfachMailModelAssembler implements RepresentationModelAssembler<Postfac () -> Link.of(String.format(userManagerUrlProvider.getUserProfileTemplate(), postfachMail.getCreatedBy()), REL_CREATED_BY)); } + record VorgangMailInfo(VorgangWithEingang vorgang, boolean hasIncomingMails) {} } \ 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 0000000000000000000000000000000000000000..358f358b4726a24daab1a0917e8a5e4ed58ce21d --- /dev/null +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/postfach/PostfachNachrichtCommandProcessor.java @@ -0,0 +1,46 @@ +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 diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/ByStatus.java b/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/ByStatus.java index 6f0a4929e00ed92387cf4cf0d7394ce698a2dd07..852c8cfc857519ad795c68c9c81930f6728e4800 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/ByStatus.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/ByStatus.java @@ -39,4 +39,5 @@ class ByStatus { private int abgeschlossen; private int verworfen; private int zuLoeschen; + private int weitergeleitet; } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/StatisticService.java b/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/StatisticService.java index 9e8cbd8c31459b379a51b42b5334c9babb2bcf07..57b9c2e7c56f6c53b3027d44972243c109c7ab39 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/StatisticService.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/statistic/StatisticService.java @@ -28,26 +28,27 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserRole; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import lombok.RequiredArgsConstructor; @Service +@RequiredArgsConstructor class StatisticService { - static final List<VorgangStatus> COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, - VorgangStatus.IN_BEARBEITUNG, VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN); - static final List<VorgangStatus> COUNT_BY_VORGANG_STATUS_POSTSTELLE_USER = Collections.emptyList(); - static final List<VorgangStatus> COUNT_BY_VORGANG_EINHEITLICHER_ANSPRECHPARTNER = Collections.emptyList(); + private static final List<VorgangStatus> COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, + VorgangStatus.IN_BEARBEITUNG, VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN, + VorgangStatus.WEITERGELEITET); + private static final List<VorgangStatus> COUNT_BY_VORGANG_STATUS_POSTSTELLE_USER = Collections.emptyList(); + private static final List<VorgangStatus> COUNT_BY_VORGANG_EINHEITLICHER_ANSPRECHPARTNER = Collections.emptyList(); - @Autowired - private StatisticRemoteService remoteService; - - @Autowired - private CurrentUserService currentUserService; + private final StatisticRemoteService remoteService; + private final CurrentUserService currentUserService; + private final FeatureToggleProperties featureToggleProperties; public Statistic getVorgaengeStatistic() { var response = remoteService.getVorgaengeStatistics(getCountByVorgangStatusList()); @@ -56,7 +57,7 @@ class StatisticService { List<VorgangStatus> getCountByVorgangStatusList() { if (currentUserService.hasRole(UserRole.VERWALTUNG_USER)) { - return COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER; + return getVerwaltungUserStatuses(); } if (currentUserService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)) { return COUNT_BY_VORGANG_STATUS_POSTSTELLE_USER; @@ -80,6 +81,7 @@ class StatisticService { .verworfen(getIntResult(response, remoteService.buildCountByStatusResultName(VorgangStatus.VERWORFEN))) .inBearbeitung(getIntResult(response, remoteService.buildCountByStatusResultName(VorgangStatus.IN_BEARBEITUNG))) .zuLoeschen(getIntResult(response, remoteService.buildCountByStatusResultName(VorgangStatus.ZU_LOESCHEN))) + .weitergeleitet(getIntResult(response, remoteService.buildCountByStatusResultName(VorgangStatus.WEITERGELEITET))) .build()) .build(); } @@ -91,4 +93,10 @@ class StatisticService { private boolean getBooleanResult(Map<String, StatisticResult> response, String resultName) { return Optional.ofNullable(response.get(resultName)).map(StatisticResult::getBoolValue).orElse(false); } + + List<VorgangStatus> getVerwaltungUserStatuses() { + return COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER.stream() + .filter(status -> status != VorgangStatus.WEITERGELEITET || featureToggleProperties.isForwardByOzgCloudEnabled()) + .toList(); + } } \ No newline at end of file diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/ResetNewPostfachNachrichtBody.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/SetNewPostfachNachrichtBody.java similarity index 96% rename from alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/ResetNewPostfachNachrichtBody.java rename to alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/SetNewPostfachNachrichtBody.java index 70ec293220e4217bec94d7da43727e48306bad85..15f2a5c2a15dd5d3ed9935a269a98e19ac4d8035 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/ResetNewPostfachNachrichtBody.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/SetNewPostfachNachrichtBody.java @@ -28,7 +28,7 @@ import lombok.Setter; @Getter @Setter -class ResetNewPostfachNachrichtBody { +public class SetNewPostfachNachrichtBody { private boolean hasNewPostfachNachricht; } diff --git a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java index 865d83fb59e4bad69534ca0a5ef52a2e686ef193..997800dcf6773183e235c82517eaf0b9695d9991 100644 --- a/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java +++ b/alfa-service/src/main/java/de/ozgcloud/alfa/vorgang/VorgangController.java @@ -25,8 +25,6 @@ package de.ozgcloud.alfa.vorgang; import java.util.Optional; -import jakarta.servlet.http.HttpServletResponse; - import org.apache.commons.lang3.StringUtils; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.RepresentationModel; @@ -45,6 +43,7 @@ import de.ozgcloud.alfa.common.clientattribute.ClientAttributeService; import de.ozgcloud.alfa.common.user.UserId; import de.ozgcloud.alfa.statistic.StatisticController; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; @RestController @@ -178,14 +177,13 @@ public class VorgangController { } @PutMapping(path = "/{vorgangId}/hasNewPostfachNachricht", consumes = MediaType.APPLICATION_JSON_VALUE) - public void resetNewPostfachNachricht(@PathVariable String vorgangId, @RequestBody ResetNewPostfachNachrichtBody body, + public void setNewPostfachNachricht(@PathVariable String vorgangId, @RequestBody SetNewPostfachNachrichtBody body, HttpServletResponse response) { if (body.isHasNewPostfachNachricht()) { - response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value()); - return; + clientAttributeService.setHasNewPostfachNachricht(vorgangId); + } else { + clientAttributeService.resetPostfachNachricht(vorgangId); } - - clientAttributeService.resetPostfachNachricht(vorgangId); response.setStatus(HttpStatus.NO_CONTENT.value()); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/LinkTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/LinkTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..011dd9db09c4aa8690dee42d024244373bebf9b5 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/LinkTestFactory.java @@ -0,0 +1,19 @@ +package de.ozgcloud.alfa; + +import java.util.List; +import java.util.stream.Stream; + +import org.springframework.hateoas.Link; + +import com.thedeanda.lorem.LoremIpsum; + +public class LinkTestFactory { + + public static Link create() { + return Link.of(LoremIpsum.getInstance().getUrl()); + } + + public static List<Link> createList(int size) { + return Stream.iterate(0, i -> i + 1).limit(size).map(i -> create()).toList(); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/RootViewLinkHandlerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/RootViewLinkHandlerTest.java index 6f0e2b655828b4283ba6947fbc81dedeb0783ffd..02d0843e8e8ebdd8673b51a9205435e1c09e1ca4 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/RootViewLinkHandlerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/RootViewLinkHandlerTest.java @@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -36,6 +37,7 @@ 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.AdditionalAnswers; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; @@ -45,6 +47,7 @@ import org.springframework.hateoas.LinkRelation; import com.thedeanda.lorem.LoremIpsum; +import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserId; import de.ozgcloud.alfa.common.user.UserProfileTestFactory; @@ -61,6 +64,8 @@ class RootViewLinkHandlerTest { private CurrentUserService currentUserService; @Mock private SystemStatusService systemStatusService; + @Mock + private FeatureToggleProperties featureToggleProperties; @DisplayName("Add view links") @Nested @@ -211,7 +216,7 @@ class RootViewLinkHandlerTest { } @Test - void shouldCallAddVorgangStatusLinks() { + void shouldCallAddAllVorgangStatusLinks() { viewLinkHandler.addViewLinksForVerwaltungUser(model, Optional.of(UserProfileTestFactory.ID)); verify(viewLinkHandler).addAllVorgangStatusLinks(model); @@ -433,29 +438,76 @@ class RootViewLinkHandlerTest { class TestBuildVorgangStatusLinks { private final UserId userId = UserProfileTestFactory.ID; + private final List<VorgangStatus> statuses = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN); + + @BeforeEach + void mock() { + doReturn(statuses.stream()).when(viewLinkHandler).getVorgangStatuses(); + } @Test - void shouldCallBuildVorgangListByPageLink() { + void shouldCallGetVorgangStatuses() { viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID); - verify(viewLinkHandler, times(7)).buildGetAllVorgaengeByAssignedToAndStatus( - any(VorgangStatus.class), - eq(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE), - eq(userId)); + verify(viewLinkHandler).getVorgangStatuses(); } @Test - void shouldCallFormatLinkRelTemplate() { + void shouldCallBuildVorgangListByPageLink() { viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID); - verify(viewLinkHandler, times(7)).formatLinkRelTemplate(eq(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE), any(VorgangStatus.class)); + statuses.forEach(status -> verify(viewLinkHandler).buildGetAllVorgaengeByAssignedToAndStatus(status, + RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, userId)); } @Test - void shouldBuildLinks() { + void shouldReturnLinks() { + var expectedLinks = LinkTestFactory.createList(statuses.size()); + doAnswer(AdditionalAnswers.returnsElementsOf(expectedLinks)).when(viewLinkHandler).buildGetAllVorgaengeByAssignedToAndStatus(any(), + any(), any()); + var links = viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID); - assertThat(links).hasSize(7); + assertThat(links).containsExactlyInAnyOrderElementsOf(expectedLinks); + } + } + + @DisplayName("build get all vorgaenge by assignedTo and status") + @Nested + class TestBuildGetAllVorgaengeByAssignedToAndStatus { + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", + "ZU_LOESCHEN", "WEITERGELEITET" }) + void shouldCallFormatLinkRelTemplate(VorgangStatus status) { + viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, + UserProfileTestFactory.ID); + + verify(viewLinkHandler).formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, status); + } + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", + "ZU_LOESCHEN", "WEITERGELEITET" }) + void shouldAddByAssignedToAndStatus(VorgangStatus status) { + var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, + UserProfileTestFactory.ID); + + assertThat(link.getHref()).isEqualTo( + "/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID.toString() + "&status=" + status.toString()); + } + + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", + "ZU_LOESCHEN", "WEITERGELEITET" }) + void shouldHaveFormattedRelation(VorgangStatus status) { + var expectedRelation = LoremIpsum.getInstance().getWords(1); + doReturn(expectedRelation).when(viewLinkHandler).formatLinkRelTemplate(any(), any()); + + var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, + UserProfileTestFactory.ID); + + assertThat(link.getRel()).isEqualTo(LinkRelation.of(expectedRelation)); } } @@ -465,7 +517,7 @@ class RootViewLinkHandlerTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) + "ZU_LOESCHEN", "WEITERGELEITET" }) void shouldCallFormatLinkRelTemplateWithAllVorgaengeTemplate(VorgangStatus status) { var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, status); @@ -474,7 +526,7 @@ class RootViewLinkHandlerTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) + "ZU_LOESCHEN", "WEITERGELEITET" }) void shouldCallFormatLinkRelTemplateWithMyVorgaengeTemplate(VorgangStatus status) { var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, status); @@ -483,7 +535,7 @@ class RootViewLinkHandlerTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) + "ZU_LOESCHEN", "WEITERGELEITET" }) void shouldCallFormatLinkRelTemplateWithUnassignedVorgaengeTemplate(VorgangStatus status) { var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_UNASSIGNED_REL_TEMPLATE, status); @@ -493,33 +545,38 @@ class RootViewLinkHandlerTest { @DisplayName("build get all vorgaenge by status") @Nested - class TestBuildGetAllByStatus { + class TestBuildGetAllVorgaengeByStatus { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) + "ZU_LOESCHEN", "WEITERGELEITET" }) void shouldAddByStatus(VorgangStatus status) { var link = viewLinkHandler.buildGetAllVorgaengeByStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE); assertThat(link.getHref()).isEqualTo("/api/vorgangs?page=0&limit=100&status=" + status.toString()); } - } + @ParameterizedTest + @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", + "ZU_LOESCHEN", "WEITERGELEITET" }) + void shouldCallFormatLinkRelTemplate(VorgangStatus status) { + viewLinkHandler.buildGetAllVorgaengeByStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE); - @DisplayName("build get all vorgaenge by assignedTo and status") - @Nested - class TestBuildGetAllVorgaengeByAssignedToAndStatus { + verify(viewLinkHandler).formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, status); + } @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) - void shouldAddByAssignedToAndStatus(VorgangStatus status) { - var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, - UserProfileTestFactory.ID); + "ZU_LOESCHEN", "WEITERGELEITET" }) + void shouldHaveFormattedRelation(VorgangStatus status) { + var expectedRelation = LoremIpsum.getInstance().getWords(1); + doReturn(expectedRelation).when(viewLinkHandler).formatLinkRelTemplate(any(), any()); - assertThat(link.getHref()).isEqualTo( - "/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID.toString() + "&status=" + status.toString()); + var link = viewLinkHandler.buildGetAllVorgaengeByStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE); + + assertThat(link.getRel()).isEqualTo(LinkRelation.of(expectedRelation)); } + } @DisplayName("build get all unassigned vorgaenge by status") @@ -528,7 +585,7 @@ class RootViewLinkHandlerTest { @ParameterizedTest @EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN", - "ZU_LOESCHEN" }) + "ZU_LOESCHEN", "WEITERGELEITET" }) void shouldAddByUnassignedToAndStatus(VorgangStatus status) { var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, UserId.empty()); @@ -706,4 +763,90 @@ class RootViewLinkHandlerTest { } } + + @Nested + class TestAddVorgangStatusLinks { + + private final EntityModel<Root> model = EntityModel.of(RootTestFactory.create()); + private final List<VorgangStatus> vorgangStatuses = List.of(VorgangStatus.NEU, VorgangStatus.BESCHIEDEN); + private final List<Link> links = LinkTestFactory.createList(vorgangStatuses.size()); + + @BeforeEach + void mock() { + doReturn(vorgangStatuses.stream()).when(viewLinkHandler).getVorgangStatuses(); + doAnswer(AdditionalAnswers.returnsElementsOf(links)).when(viewLinkHandler).buildGetAllVorgaengeByStatus(any(), any()); + } + + @Test + void shouldCallGetVorgangStatuses() { + viewLinkHandler.addVorgangStatusLinks(model, RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE); + + verify(viewLinkHandler).getVorgangStatuses(); + } + + @Test + void shouldCallBuildGetAllVorgaengeByStatus() { + viewLinkHandler.addVorgangStatusLinks(model, RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE); + + vorgangStatuses + .forEach(status -> verify(viewLinkHandler).buildGetAllVorgaengeByStatus(status, RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE)); + } + + @Test + void shouldAddLinksToModel() { + viewLinkHandler.addVorgangStatusLinks(model, RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE); + + assertThat(model.getLinks()).containsExactlyInAnyOrderElementsOf(links); + } + } + + @Nested + class TestGetVorgangStatuses { + + @Test + void shouldCallFeatureToggleProperties() { + viewLinkHandler.getVorgangStatuses().toList(); + + verify(featureToggleProperties).isForwardByOzgCloudEnabled(); + } + + @Nested + class TestOnIsForwardByOzgCloudEnabled { + + @BeforeEach + void mock() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(true); + } + + @Test + void shouldReturnStream() { + var expectedStatuses = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, + VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN, + VorgangStatus.WEITERGELEITET); + + var stream = viewLinkHandler.getVorgangStatuses(); + + assertThat(stream).containsExactlyInAnyOrderElementsOf(expectedStatuses); + } + } + + @Nested + class TestOnIsNotForwardByOzgCloudEnabled { + + @BeforeEach + void mock() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(false); + } + + @Test + void shouldReturnStream() { + var expectedStatuses = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, + VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN); + + var stream = viewLinkHandler.getVorgangStatuses(); + + assertThat(stream).containsExactlyInAnyOrderElementsOf(expectedStatuses); + } + } + } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java index 97079a883a639e1844028ab2356403866462f7f5..9f245c3c6a9c4b31c29e3459c148862d467bc1a7 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/CollaborationVorgangProcessorTest.java @@ -103,20 +103,6 @@ class CollaborationVorgangProcessorTest { when(collaborationService.hasCollaboration(VorgangHeaderTestFactory.ID)).thenReturn(true); } - @Test - void shouldHaveOneLink() { - var model = callProcessor(); - - assertThat(model.getLinks()).hasSize(1); - } - - @Test - void shouldNotAddSearchOrganisationsEinheitLink() { - var model = callProcessor(); - - assertThat(model.getLink(CollaborationVorgangProcessor.REL_SEARCH_ORGANISATIONS_EINHEIT)).isEmpty(); - } - @Test void shouldAddCollaborationsLink() { var model = callProcessor(); @@ -135,26 +121,6 @@ class CollaborationVorgangProcessorTest { when(collaborationService.hasCollaboration(VorgangHeaderTestFactory.ID)).thenReturn(false); } - @Test - void shouldHaveThreeLinks() { - var model = callProcessor(); - - assertThat(model.getLinks()).hasSize(3); - } - - @Test - void shouldAddSearchOrganisationsEinheitLink() { - var expectedHref = UriComponentsBuilder.fromUriString(OrganisationsEinheitController.PATH) - .queryParam(OrganisationsEinheitController.SEARCH_BY_PARAM, "{" + OrganisationsEinheitController.SEARCH_BY_PARAM + "}") - .build().toString(); - - var model = callProcessor(); - - assertThat(model.getLink(CollaborationVorgangProcessor.REL_SEARCH_ORGANISATIONS_EINHEIT)).get() - .extracting(Link::getHref) - .isEqualTo(expectedHref); - } - @Test void shouldAddSearchFachstelleLink() { var expectedHref = UriComponentsBuilder.fromUriString(FachstelleController.PATH) diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitControllerTest.java index 8f1efd6b991fc2f5649f58359da0cf0d6941c2fb..32662710a920068da9e44cb896f4b47fe9f5f2bc 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitControllerTest.java @@ -23,6 +23,7 @@ */ package de.ozgcloud.alfa.collaboration; +import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -256,4 +257,24 @@ class OrganisationsEinheitControllerTest { } } + @Nested + class TestGetOrganisationEinheit { + + @Test + void shouldCallService() { + controller.getOrganisationEinheit(OrganisationsEinheitTestFactory.ID); + + verify(service).getById(OrganisationsEinheitTestFactory.ID); + } + + @Test + void shouldReturnOrganisationEinheit() { + var organisationEinheitFromService = OrganisationsEinheitTestFactory.create(); + when(service.getById(any())).thenReturn(organisationEinheitFromService); + + var organisationsEinheit = controller.getOrganisationEinheit(OrganisationsEinheitTestFactory.ID); + + assertThat(organisationsEinheit).isSameAs(organisationEinheitFromService); + } + } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b6f7a6c040ee3e0890480ac79bf403bcbc8e6032 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/collaboration/OrganisationsEinheitVorgangWithEingangProcessorTest.java @@ -0,0 +1,211 @@ +package de.ozgcloud.alfa.collaboration; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +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.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.EnumSource.Mode; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.hateoas.EntityModel; +import org.springframework.hateoas.Link; + +import de.ozgcloud.alfa.common.FeatureToggleProperties; +import de.ozgcloud.alfa.common.user.CurrentUserService; +import de.ozgcloud.alfa.common.user.UserRole; +import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; +import de.ozgcloud.alfa.vorgang.VorgangWithEingang; +import de.ozgcloud.alfa.vorgang.VorgangWithEingangTestFactory; + +public class OrganisationsEinheitVorgangWithEingangProcessorTest { + + @InjectMocks + @Spy + private OrganisationsEinheitVorgangWithEingangProcessor processor; + + @Mock + private CurrentUserService userService; + @Mock + private FeatureToggleProperties featureToggleProperties; + + @DisplayName("Process") + @Nested + class TestProcess { + + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + private final EntityModel<VorgangWithEingang> model = EntityModel.of(vorgang); + + @Test + void shouldReturnSameModel() { + var processedModel = processor.process(model); + + assertThat(processedModel).isSameAs(model); + } + + @SuppressWarnings("unchecked") + @Test + void shouldReturnSameModelOnNullContent() { + var entityModel = mock(EntityModel.class); + when(entityModel.getContent()).thenReturn(null); + + var processedModel = processor.process(entityModel); + + assertThat(processedModel).isSameAs(entityModel); + } + + @DisplayName("entity model links") + @Nested + class TestEntityModelLinks { + + @Test + void shouldContainsSearchLinkIfNeeded() { + doReturn(true).when(processor).isSearchNeeded(any()); + + var processedModel = processor.process(model); + + assertThat(processedModel.getLink(OrganisationsEinheitVorgangWithEingangProcessor.REL_SEARCH_ORGANISATIONS_EINHEIT)).get() + .extracting(Link::getHref) + .isEqualTo(OrganisationsEinheitController.PATH + "?searchBy={searchBy}"); + } + } + } + + @DisplayName("is search needed") + @Nested + class TestIsSearchNeeded { + + private VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @Test + void shouldReturnTrueIfForwardable() { + doReturn(true).when(processor).isForwardable(any()); + + var isSearchNeeded = processor.isSearchNeeded(vorgang); + + assertThat(isSearchNeeded).isTrue(); + } + + @Test + void shouldReturnFalseIfCollaborationCreateable() { + doReturn(true).when(processor).isCollaborationCreateable(); + + var isSearchNeeded = processor.isSearchNeeded(vorgang); + + assertThat(isSearchNeeded).isTrue(); + } + } + + @DisplayName("is forwardable") + @Nested + class TestIsForwardable { + + @DisplayName("on feature disabled") + @Nested + class TestOnFeatureDisabled { + + @BeforeEach + void mockFeatureToggle() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(false); + } + + @ParameterizedTest + @EnumSource + void shouldReturnFalse(VorgangStatus status) { + var vorgang = VorgangWithEingangTestFactory.createBuilder().status(status).build(); + + var forwardableByOzgCloud = processor.isForwardable(vorgang); + + assertThat(forwardableByOzgCloud).isFalse(); + } + } + + @DisplayName("on feature enabled") + @Nested + class TestOnFeatureEnabled { + + @BeforeEach + void mockFeatureToggle() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(true); + } + + @ParameterizedTest(name = "{0}") + @EnumSource(mode = Mode.EXCLUDE, names = { "NEU" }) + void shouldReturnFalseOnVorgangStatus(VorgangStatus status) { + var forwardableByOzgCloud = processor.isForwardable(VorgangWithEingangTestFactory.createBuilder().status(status).build()); + + assertThat(forwardableByOzgCloud).isFalse(); + } + + @Test + void shouldReturnTrueOnVorgangStatusNeu() { + var forwardable = processor.isForwardable(VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.NEU).build()); + + assertThat(forwardable).isTrue(); + } + } + } + + @DisplayName("is collaboration createable") + @Nested + class TestIsCollaborationCreateable { + + @DisplayName("on feature disabled") + @Nested + class TestOnFeatureDisabled { + + @BeforeEach + void mockFeatureToggle() { + when(featureToggleProperties.isCollaborationEnabled()).thenReturn(false); + } + + void shouldReturnFalse() { + var isCollaborationEnabled = processor.isCollaborationCreateable(); + + assertThat(isCollaborationEnabled).isFalse(); + } + } + + @DisplayName("on feature enabled") + @Nested + class TestOnFeatureEnabled { + + @BeforeEach + void mockFeatureToggle() { + when(featureToggleProperties.isCollaborationEnabled()).thenReturn(true); + } + + @Test + void shouldVerifyRole() { + processor.isCollaborationCreateable(); + + verify(userService).hasRole(UserRole.VERWALTUNG_USER); + } + + @Test + void shouldReturnFalseMissingRole() { + when(userService.hasRole(any())).thenReturn(false); + + var isCollaborationEnabled = processor.isCollaborationCreateable(); + + assertThat(isCollaborationEnabled).isFalse(); + } + + @Test + void shouldReturnTrueOnAssignedRole() { + when(userService.hasRole(any())).thenReturn(true); + + var isCollaborationEnabled = processor.isCollaborationCreateable(); + + assertThat(isCollaborationEnabled).isTrue(); + } + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeServiceTest.java index 59bd03ddbb440d66be7e8fb1c262cdaa0976692d..25866dae17d71b3b5c68a9c1f71485efffe1df87 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/clientattribute/ClientAttributeServiceTest.java @@ -42,13 +42,23 @@ class ClientAttributeServiceTest { @Nested class TestResetNewPostfachNachricht { - private final ClientAttribute clientAttribute = ClientAttribute.HAS_NEW_POSTFACH_NACHRICHT; @Test void shouldCallRemoteService() { service.resetPostfachNachricht(VorgangHeaderTestFactory.ID); - verify(remoteService).setBooleanReadOnlyClientAttribute(VorgangHeaderTestFactory.ID, clientAttribute, false); + verify(remoteService).setBooleanReadOnlyClientAttribute(VorgangHeaderTestFactory.ID, ClientAttribute.HAS_NEW_POSTFACH_NACHRICHT, false); + } + } + + @Nested + class TestSetHasNewPostfachNachricht { + + @Test + void shouldCallRemoteService() { + service.setHasNewPostfachNachricht(VorgangHeaderTestFactory.ID); + + verify(remoteService).setBooleanReadOnlyClientAttribute(VorgangHeaderTestFactory.ID, ClientAttribute.HAS_NEW_POSTFACH_NACHRICHT, true); } } } 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 cdc84e3c2bb10c6620376542e304399ff8ea0cfb..cfaba07c471a901ee5a18863c7555b6fc362f53b 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()); @@ -226,7 +243,7 @@ class CommandModelAssemblerTest { "UPDATE_ATTACHED_ITEM", "PATCH_ATTACHED_ITEM", "RECEIVE_POSTFACH_NACHRICHT", "VORGANG_LOESCHEN", "DELETE_ATTACHED_ITEM", "VORGANG_ZUM_LOESCHEN_MARKIEREN", "LOESCH_ANFORDERUNG_ZURUECKNEHMEN", "CREATE_BESCHEID", "PROCESS_VORGANG", "SET_AKTENZEICHEN", "DELETE_BESCHEID", "UPDATE_BESCHEID", "CREATE_BESCHEID_DOCUMENT_FROM_FILE", "CREATE_BESCHEID_DOCUMENT", "SEND_BESCHEID", - "UNBEKANNT", "CREATE_COLLABORATION_REQUEST" }) + "UNBEKANNT", "CREATE_COLLABORATION_REQUEST", "FORWARD_VORGANG" }) void shouldBePresentOnOrder(CommandOrder order) { var model = toModelWithOrder(order.name()); @@ -250,7 +267,7 @@ class CommandModelAssemblerTest { @ParameterizedTest @EnumSource(mode = Mode.EXCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN", "VORGANG_BESCHEIDEN", "VORGANG_ZURUECKSTELLEN", "VORGANG_ABSCHLIESSEN", "VORGANG_WIEDEREROEFFNEN", "VORGANG_ZUM_LOESCHEN_MARKIEREN", - "LOESCH_ANFORDERUNG_ZURUECKNEHMEN" }) + "LOESCH_ANFORDERUNG_ZURUECKNEHMEN", "FORWARD_VORGANG" }) void shouldNOTBePresentOnOrder(CommandOrder order) { var model = toModelWithOrder(order.name()); 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 0000000000000000000000000000000000000000..edc832bafccb29ef6053a64fefda68e44955ec6a --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/common/command/CommandProcessorTest.java @@ -0,0 +1,146 @@ +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 diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/CreateForwardVorgangCommandTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/CreateForwardVorgangCommandTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..077816660e80ee50cc565e12efcc0c6215071f66 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/CreateForwardVorgangCommandTestFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CreateCommand; +import de.ozgcloud.alfa.common.command.CreateCommand.CreateCommandBuilder; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; + +class CreateForwardVorgangCommandTestFactory { + + static CreateCommand create() { + return createBuilder().build(); + } + + static CreateCommandBuilder createBuilder() { + return CreateCommand.builder() + .order(CommandOrder.FORWARD_VORGANG.name()) + .vorgangId(VorgangHeaderTestFactory.ID) + .relationId(VorgangHeaderTestFactory.ID) + .body(ForwardVorgangCommandBodyTestFactory.create()); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..09eb98b0ab8cb795ca8b6a73595913edde158cc3 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardByVorgangCommandControllerTest.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import static de.ozgcloud.alfa.common.command.CommandController.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.BeforeEach; +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.mockito.Spy; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import de.ozgcloud.alfa.collaboration.OrganisationsEinheit; +import de.ozgcloud.alfa.collaboration.OrganisationsEinheitController; +import de.ozgcloud.alfa.collaboration.OrganisationsEinheitTestFactory; +import de.ozgcloud.alfa.common.command.CommandOrder; +import de.ozgcloud.alfa.common.command.CommandService; +import de.ozgcloud.alfa.common.command.CommandTestFactory; +import de.ozgcloud.alfa.common.command.CreateCommand; +import de.ozgcloud.alfa.common.errorhandling.ExceptionController; +import de.ozgcloud.alfa.common.errorhandling.ProblemDetailMapper; +import de.ozgcloud.alfa.vorgang.VorgangHeaderTestFactory; +import lombok.SneakyThrows; + +class ForwardByVorgangCommandControllerTest { + + @Mock + private CommandService commandService; + @Mock + private OrganisationsEinheitController organisationsEinheitController; + @Mock + private ForwardVorgangCommandBodyMapper bodyMapper; + @Mock + private ProblemDetailMapper problemDetailMapper; + @Spy + @InjectMocks + private ForwardByVorgangCommandController controller; + + private MockMvc mockMvc; + + @BeforeEach + void init() { + mockMvc = MockMvcBuilders.standaloneSetup(controller).setControllerAdvice(new ExceptionController(problemDetailMapper)).build(); + } + + @Nested + class TestCreateCommand { + + private final OrganisationsEinheit organisationsEinheit = OrganisationsEinheitTestFactory.create(); + @Captor + private ArgumentCaptor<CreateCommand> commandCaptor; + @Captor + private ArgumentCaptor<ForwardVorgangCommandBody> commandBodyCaptor; + + @BeforeEach + void init() { + when(organisationsEinheitController.getOrganisationEinheit(any())).thenReturn(organisationsEinheit); + when(commandService.createCommand(any(), anyLong())).thenReturn(CommandTestFactory.create()); + when(bodyMapper.updateFromOrganisationEinheit(any(), any())).thenReturn(ForwardVorgangCommandBodyTestFactory.create()); + } + + @Test + void shouldCallOrganisationsEinheitController() { + doRequest(); + + verify(organisationsEinheitController).getOrganisationEinheit(OrganisationsEinheitTestFactory.ID); + } + + @Test + void shouldAddOrganisationsEinheitData() { + doRequest(); + + verify(bodyMapper).updateFromOrganisationEinheit(same(organisationsEinheit), commandBodyCaptor.capture()); + assertThat(commandBodyCaptor.getValue()).usingRecursiveComparison() + .isEqualTo(ForwardVorgangCommandBody.builder().organisationEinheitId(OrganisationsEinheitTestFactory.ID).build()); + } + + @Test + void shouldCallCommandService() { + doRequest(); + + verify(commandService).createCommand(commandCaptor.capture(), eq(VorgangHeaderTestFactory.VERSION)); + assertThat(commandCaptor.getValue()).usingRecursiveComparison().isEqualTo(CreateForwardVorgangCommandTestFactory.create()); + } + + @SneakyThrows + @Test + void shouldReturnCreated() { + var response = doRequest(); + + response.andExpect(status().isCreated()); + } + + @SneakyThrows + @Test + void shouldReturnLinkToCreatedCommand() { + var response = doRequest(); + + response.andExpect(header().stringValues("location", "http://localhost" + COMMANDS_PATH + "/" + CommandTestFactory.ID)); + } + + @SneakyThrows + private ResultActions doRequest() { + return mockMvc.perform(post(ForwardByVorgangCommandController.PATH, VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION) + .contentType(MediaType.APPLICATION_JSON) + .content(createContent())); + } + + private String createContent() { + return CommandTestFactory.buildCreateCommandWithBodyContent( + CommandOrder.FORWARD_VORGANG, + "{\"organisationEinheitId\":\"%s\"}".formatted(OrganisationsEinheitTestFactory.ID)); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapperTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d92662563790f1538d5f41ffbd17108319d05a74 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyMapperTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import static org.assertj.core.api.Assertions.*; + +import java.util.UUID; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +import de.ozgcloud.alfa.collaboration.OrganisationsEinheitTestFactory; + +class ForwardVorgangCommandBodyMapperTest { + + private final ForwardVorgangCommandBodyMapper mapper = Mappers.getMapper(ForwardVorgangCommandBodyMapper.class); + + @Nested + class TestUpdateFromOrganisationEinheit { + + private static final String ORGANISATIONS_EINHEIT_ID = UUID.randomUUID().toString(); + + private final ForwardVorgangCommandBody body = ForwardVorgangCommandBody.builder() + .organisationEinheitId(ORGANISATIONS_EINHEIT_ID).build(); + + @Test + void shouldUpdateFromOrganisationEinheit() { + mapper.updateFromOrganisationEinheit(OrganisationsEinheitTestFactory.create(), body); + + assertThat(body).usingRecursiveComparison().isEqualTo(ForwardVorgangCommandBodyTestFactory.create()); + } + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyTestFactory.java b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..de7f68117af6d88ac69da1311cd9a4ea9e0e3a96 --- /dev/null +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardVorgangCommandBodyTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den + * Ministerpräsidenten des Landes Schleswig-Holstein + * Staatskanzlei + * Abteilung Digitalisierung und zentrales IT-Management der Landesregierung + * + * Lizenziert unter der EUPL, Version 1.2 oder - sobald + * diese von der Europäischen Kommission genehmigt wurden - + * Folgeversionen der EUPL ("Lizenz"); + * Sie dürfen dieses Werk ausschließlich gemäß + * dieser Lizenz nutzen. + * Eine Kopie der Lizenz finden Sie hier: + * + * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + * + * Sofern nicht durch anwendbare Rechtsvorschriften + * gefordert oder in schriftlicher Form vereinbart, wird + * die unter der Lizenz verbreitete Software "so wie sie + * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - + * ausdrücklich oder stillschweigend - verbreitet. + * Die sprachspezifischen Genehmigungen und Beschränkungen + * unter der Lizenz sind dem Lizenztext zu entnehmen. + */ +package de.ozgcloud.alfa.forwarding; + +import de.ozgcloud.alfa.collaboration.AnschriftTestFactory; +import de.ozgcloud.alfa.collaboration.OrganisationsEinheitTestFactory; +import de.ozgcloud.alfa.forwarding.ForwardVorgangCommandBody.ForwardVorgangCommandBodyBuilder; + +class ForwardVorgangCommandBodyTestFactory { + + static ForwardVorgangCommandBody create() { + return createBuilder().build(); + } + + static ForwardVorgangCommandBodyBuilder createBuilder() { + return ForwardVorgangCommandBody.builder() + .organisationEinheitId(OrganisationsEinheitTestFactory.XZUFI_ID.getId()) + .name(OrganisationsEinheitTestFactory.NAME) + .strasse(AnschriftTestFactory.STRASSE) + .ort(AnschriftTestFactory.ORT) + .hausnummer(AnschriftTestFactory.HAUSNUMMER) + .plz(AnschriftTestFactory.PLZ); + } +} diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessorTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessorTest.java index e1da1b36f786f0186bbf35ed53b12cbb398b94a8..551f863ef0902690c89c3068ffcbe1612b71adb6 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessorTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/forwarding/ForwardingVorgangWithEingangProcessorTest.java @@ -42,7 +42,6 @@ import org.springframework.hateoas.Link; import org.springframework.web.util.UriComponentsBuilder; import de.ozgcloud.alfa.common.FeatureToggleProperties; -import de.ozgcloud.alfa.common.command.CommandController.CommandByRelationController; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserRole; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; @@ -122,8 +121,8 @@ class ForwardingVorgangWithEingangProcessorTest { @Test void shouldAddForwardByOzgCloudLink() { - var expectedHref = UriComponentsBuilder.fromUriString(CommandByRelationController.COMMAND_BY_RELATION_PATH) - .buildAndExpand(VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION).toString(); + var expectedHref = UriComponentsBuilder.fromUriString(ForwardByVorgangCommandController.PATH) + .buildAndExpand(VorgangHeaderTestFactory.ID, VorgangHeaderTestFactory.VERSION).toString(); var model = processor.process(EntityModel.of(vorgang)); @@ -205,16 +204,6 @@ class ForwardingVorgangWithEingangProcessorTest { assertThat(forwardableByOzgCloud).isFalse(); } - - @ParameterizedTest - @EnumSource - void shouldNotCallUserService(VorgangStatus status) { - var vorgang = VorgangWithEingangTestFactory.createBuilder().status(status).build(); - - processor.isForwardableByOzgCloud(vorgang); - - verify(userService, never()).hasRole(any()); - } } @Nested @@ -236,16 +225,6 @@ class ForwardingVorgangWithEingangProcessorTest { assertThat(forwardableByOzgCloud).isFalse(); } - - @ParameterizedTest - @EnumSource(mode = Mode.EXCLUDE, names = { "NEU" }) - void shouldNotCallUserService(VorgangStatus status) { - var vorgang = VorgangWithEingangTestFactory.createBuilder().status(status).build(); - - processor.isForwardableByOzgCloud(vorgang); - - verify(userService, never()).hasRole(any()); - } } @Nested @@ -254,20 +233,10 @@ class ForwardingVorgangWithEingangProcessorTest { private final VorgangWithEingang newVorgang = VorgangWithEingangTestFactory.createBuilder().status(VorgangStatus.NEU).build(); @Test - void shouldCheckUserRole() { - processor.isForwardableByOzgCloud(newVorgang); - - verify(userService).hasRole(UserRole.VERWALTUNG_USER); - } - - @ParameterizedTest - @ValueSource(booleans = { true, false }) - void shouldReturnValueOfHasVerwaltungUserRole(boolean isVerwaltungUser) { - when(userService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(isVerwaltungUser); - + void shouldReturnTrue() { var forwardableByOzgCloud = processor.isForwardableByOzgCloud(newVorgang); - assertThat(forwardableByOzgCloud).isEqualTo(isVerwaltungUser); + assertThat(forwardableByOzgCloud).isTrue(); } } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java index 41dbd9daa892b5f82555b700afdeed0c1f8071ce..7d7e6fedf88b1ad49a771a64dbfa06936e7e09de 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/historie/ChangeHistoryBuilderTest.java @@ -262,7 +262,7 @@ public class ChangeHistoryBuilderTest { void shouldReturnEmptyIfPropertyIsNotPresentInBody() { var command = previousCommand.toBuilder().body(Map.of("a", "b")).build(); - var value = builder.getValueFromCommandBody(PROPERTY_NAME, command); + var value = builder.getValueFromCommandBody("notExists", command); assertThat(value).isEmpty(); } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssemblerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssemblerTest.java index 091797403adabd9734b63866c28185033da4e0b7..da9a28e8d99613687b9d6315c3c515d40c4d8281 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssemblerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/postfach/PostfachMailModelAssemblerTest.java @@ -27,6 +27,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.List; import java.util.UUID; import java.util.stream.Stream; @@ -34,6 +35,10 @@ 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.junit.jupiter.params.ParameterizedTest; +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; @@ -53,6 +58,7 @@ import de.ozgcloud.alfa.common.user.UserId; import de.ozgcloud.alfa.common.user.UserManagerUrlProvider; import de.ozgcloud.alfa.postfach.PostfachMail.Direction; import de.ozgcloud.alfa.postfach.PostfachMailController.PostfachMailCommandByVorgangController; +import de.ozgcloud.alfa.postfach.PostfachMailModelAssembler.VorgangMailInfo; import de.ozgcloud.alfa.vorgang.ServiceKontoTestFactory; import de.ozgcloud.alfa.vorgang.VorgangController; import de.ozgcloud.alfa.vorgang.VorgangHeadTestFactory; @@ -291,40 +297,59 @@ class PostfachMailModelAssemblerTest { @Nested class TestToCollectionModel { - private final Stream<PostfachMail> mails = Stream.of(PostfachMailTestFactory.create()); + private final List<PostfachMail> mails = List.of(PostfachMailTestFactory.create()); private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); private final PostfachSettings postfachSettings = PostfachSettingsTestFactory.create(); @Mock private RepresentationModel<EntityModel<PostfachSettings>> model; + @Captor + private ArgumentCaptor<Stream<PostfachMail>> postfachMailsCaptor; @BeforeEach void setUpMocks() { - doReturn(model).when(modelAssembler).buildHalRepresentationModel(mails, vorgang, postfachSettings); + doReturn(model).when(modelAssembler).buildHalRepresentationModel(any(), any(), any()); } @Test void shouldBuildHalRepresentationModel() { + doReturn(false).when(modelAssembler).hasServiceKonto(vorgang); + callModelAssembler(); - verify(modelAssembler).buildHalRepresentationModel(mails, vorgang, postfachSettings); + verify(modelAssembler).buildHalRepresentationModel(postfachMailsCaptor.capture(), same(vorgang), same(postfachSettings)); + assertThat(postfachMailsCaptor.getValue()).containsExactlyElementsOf(mails); } @Nested class OnHasServiceKonto { + private final VorgangMailInfo vorgangMailInfo = new VorgangMailInfo(vorgang, true); + + @BeforeEach + void init() { + doReturn(vorgangMailInfo).when(modelAssembler).buildVorgangMailInfo(any(), any()); + } + + @Test + void shouldBuildVorgangMailInfo() { + callModelAssembler(); + + verify(modelAssembler).buildVorgangMailInfo(mails, vorgang); + } + @Test void shouldAddPostfachNachrichtLinks() { doReturn(true).when(modelAssembler).hasServiceKonto(vorgang); callModelAssembler(); - verify(modelAssembler).addPostfachNachrichtLinks(model, vorgang); + verify(modelAssembler).addPostfachNachrichtLinks(model, vorgangMailInfo); } } @Nested - class OnHasNotServiceKonto { + class OnHasNoServiceKonto { @Test void shouldAddPostfachNachrichtLinks() { @@ -338,13 +363,15 @@ class PostfachMailModelAssemblerTest { @Test void shouldReturnModel() { + doReturn(false).when(modelAssembler).hasServiceKonto(vorgang); + var returnedModel = callModelAssembler(); assertThat(returnedModel).isEqualTo(model); } private RepresentationModel<EntityModel<PostfachSettings>> callModelAssembler() { - return modelAssembler.toCollectionModel(mails, vorgang, postfachSettings); + return modelAssembler.toCollectionModel(mails.stream(), vorgang, postfachSettings); } } @@ -410,6 +437,37 @@ class PostfachMailModelAssemblerTest { } + @Nested + class TestBuildVorgangMailInfo { + + private final List<PostfachMail> mails = List.of(PostfachMailTestFactory.create()); + private final VorgangWithEingang vorgang = VorgangWithEingangTestFactory.create(); + + @Test + void shouldCallHasIncomingMails() { + modelAssembler.buildVorgangMailInfo(mails, vorgang); + + verify(modelAssembler).hasIncomingMails(mails); + } + + @ParameterizedTest + @ValueSource(booleans = { true, false }) + void shouldHaveHasIncomingMails(boolean hasIncomingMails) { + doReturn(hasIncomingMails).when(modelAssembler).hasIncomingMails(any()); + + var vorgangMailInfo = modelAssembler.buildVorgangMailInfo(mails, vorgang); + + assertThat(vorgangMailInfo.hasIncomingMails()).isEqualTo(hasIncomingMails); + } + + @Test + void shouldHaveVorgang() { + var vorgangMailInfo = modelAssembler.buildVorgangMailInfo(mails, vorgang); + + assertThat(vorgangMailInfo.vorgang()).isEqualTo(vorgang); + } + } + @Nested class TestHasServiceKonto { @@ -438,6 +496,33 @@ class PostfachMailModelAssemblerTest { } } + @Nested + class TestHasIncomingMails { + + @Test + void shouldReturnFalseIfListIsEmpty() { + var hasIncomingMails = modelAssembler.hasIncomingMails(List.of()); + + assertThat(hasIncomingMails).isFalse(); + } + + @Test + void shouldReturnFalseIfNoIncomingMails() { + var hasIncomingMails = modelAssembler.hasIncomingMails(List.of(PostfachMailTestFactory.createBuilder().direction(Direction.OUT).build())); + + assertThat(hasIncomingMails).isFalse(); + } + + @Test + void shouldReturnTrueIfIncomingMailsArePresent() { + var hasIncomingMails = modelAssembler.hasIncomingMails(List.of( + PostfachMailTestFactory.createBuilder().direction(Direction.OUT).build(), + PostfachMailTestFactory.createBuilder().direction(Direction.IN).build())); + + assertThat(hasIncomingMails).isTrue(); + } + } + @Nested class TestAddPostfachNachrichtLinks { @@ -449,6 +534,7 @@ class PostfachMailModelAssemblerTest { @Nested class ResetNewPostfachNachrichtLink { + @Test void shouldBePresent() { callModelAssembler(VorgangWithEingangTestFactory.create()); @@ -469,6 +555,55 @@ class PostfachMailModelAssemblerTest { } } + @Nested + class SetHasNewPostfachNachrichtLink { + + @Nested + class OnHasNewPostfachNachrichtSet { + + @Test + void shouldNotBePresent() { + callModelAssembler(); + + var link = model.getLink(PostfachMailModelAssembler.REL_SET_HAS_NEW_POSTFACH_MAIL); + assertThat(link).isNotPresent(); + } + + private void callModelAssembler() { + modelAssembler.addPostfachNachrichtLinks(model, + new VorgangMailInfo(VorgangWithEingangTestFactory.createBuilder().hasNewPostfachNachricht(true).build(), true)); + } + } + + @Nested + class OnHasNewPostfachNachrichtUnset { + + @Test + void shouldBePresentWhenHasIncomingMails() { + callModelAssembler(true); + + var link = model.getLink(PostfachMailModelAssembler.REL_SET_HAS_NEW_POSTFACH_MAIL); + assertThat(link).isPresent().get().extracting(Link::getHref) + .isEqualTo(UriComponentsBuilder.fromUriString("/api/vorgangs") + .pathSegment(VorgangHeaderTestFactory.ID, "hasNewPostfachNachricht") + .build().toString()); + } + + @Test + void shouldNotBePresentWhenHasNoIncomingMails() { + callModelAssembler(false); + + var link = model.getLink(PostfachMailModelAssembler.REL_SET_HAS_NEW_POSTFACH_MAIL); + assertThat(link).isNotPresent(); + } + + private void callModelAssembler(boolean hasIncomingMails) { + modelAssembler.addPostfachNachrichtLinks(model, + new VorgangMailInfo(VorgangWithEingangTestFactory.createBuilder().hasNewPostfachNachricht(false).build(), hasIncomingMails)); + } + } + } + @DisplayName("send postfach mail") @Nested class SendPostfachMailLink { @@ -521,7 +656,7 @@ class PostfachMailModelAssemblerTest { } private void callModelAssembler(VorgangWithEingang vorgang) { - modelAssembler.addPostfachNachrichtLinks(model, vorgang); + modelAssembler.addPostfachNachrichtLinks(model, new VorgangMailInfo(vorgang, false)); } } } 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 0000000000000000000000000000000000000000..5b2cbca51b4b6d9a2373c7ef399aa513e5eb5bb4 --- /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.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 diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/statistic/StatisticServiceTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/statistic/StatisticServiceTest.java index 61204730a5b571ba703774b95289d9e18a67ff7c..58ef88e6ca343dbdb02a510919ccb304058def9e 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/statistic/StatisticServiceTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/statistic/StatisticServiceTest.java @@ -24,12 +24,12 @@ package de.ozgcloud.alfa.statistic; import static de.ozgcloud.alfa.statistic.StatisticRemoteService.*; -import static de.ozgcloud.alfa.statistic.StatisticService.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.util.Collections; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -39,6 +39,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import de.ozgcloud.alfa.common.FeatureToggleProperties; import de.ozgcloud.alfa.common.user.CurrentUserService; import de.ozgcloud.alfa.common.user.UserRole; import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus; @@ -52,6 +53,8 @@ class StatisticServiceTest { private StatisticRemoteService remoteService; @Mock private CurrentUserService currentUserService; + @Mock + private FeatureToggleProperties featureToggleProperties; @Nested class TestBuildGetVorgaengeStatisticResult { @@ -155,6 +158,15 @@ class StatisticServiceTest { assertThat(statistic.getByStatus().getZuLoeschen()).isEqualTo(StatisticResultTestFactory.INT_VALUE); } + @Test + void shouldContainCountByStatusWeitergeleitet() { + var statistic = service.buildGetVorgaengeStatisticResult(Map.of( + String.format(COUNT_VORGANG_STATUS_RESULT_NAME_FORMAT, VorgangStatus.WEITERGELEITET.name()), + StatisticResultTestFactory.create())); + + assertThat(statistic.getByStatus().getWeitergeleitet()).isEqualTo(StatisticResultTestFactory.INT_VALUE); + } + @Test void shouldContainDefaultCountByStatus() { var statistic = service.buildGetVorgaengeStatisticResult(Map.of()); @@ -174,13 +186,25 @@ class StatisticServiceTest { @Nested class TestGetCountByVorgangStatusList { + private static final List<VorgangStatus> VORGANG_STATUS_VERWALTUNG_USER = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN); + + @Test + void shouldCallGetVerwaltungUserStatuses() { + when(currentUserService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(true); + + service.getCountByVorgangStatusList(); + + verify(service).getVerwaltungUserStatuses(); + } + @Test - void shouldGetForVerwalungUser() { + void shouldReturnVerwaltungUserStatuses() { when(currentUserService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(true); + doReturn(VORGANG_STATUS_VERWALTUNG_USER).when(service).getVerwaltungUserStatuses(); var result = service.getCountByVorgangStatusList(); - assertThat(result).containsAll(COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER); + assertThat(result).containsAll(VORGANG_STATUS_VERWALTUNG_USER); } @Test @@ -190,7 +214,7 @@ class StatisticServiceTest { var result = service.getCountByVorgangStatusList(); - assertThat(result).containsAll(COUNT_BY_VORGANG_STATUS_POSTSTELLE_USER); + assertThat(result).containsAll(Collections.emptyList()); } @Test @@ -201,7 +225,7 @@ class StatisticServiceTest { var result = service.getCountByVorgangStatusList(); - assertThat(result).containsAll(COUNT_BY_VORGANG_EINHEITLICHER_ANSPRECHPARTNER); + assertThat(result).containsAll(Collections.emptyList()); } @Test @@ -222,6 +246,7 @@ class StatisticServiceTest { private final Statistic statistic = StatisticTestFactory.create(); private final Map<String, StatisticResult> getVorgangStatisticsResponse = Map.of(COUNT_WIEDERVORLAGE_NEXT_FRIST_RESULT_NAME, StatisticResultTestFactory.create()); + private static final List<VorgangStatus> COUNT_BY_VORGANG_STATUS_VERWALTUNG_USER = List.of(VorgangStatus.NEU); @BeforeEach void beforeEach() { @@ -258,4 +283,54 @@ class StatisticServiceTest { assertThat(result).isEqualTo(statistic); } } + + @Nested + class TestGetVorgangStatuses { + + @Test + void shouldCallFeatureToggleProperties() { + service.getVerwaltungUserStatuses(); + + verify(featureToggleProperties).isForwardByOzgCloudEnabled(); + } + + @Nested + class TestOnIsForwardByOzgCloudEnabled { + + @BeforeEach + void mock() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(true); + } + + @Test + void shouldReturnStream() { + var expectedStatuses = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, + VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN, + VorgangStatus.WEITERGELEITET); + + var stream = service.getVerwaltungUserStatuses(); + + assertThat(stream).containsExactlyInAnyOrderElementsOf(expectedStatuses); + } + } + + @Nested + class TestOnIsNotForwardByOzgCloudEnabled { + + @BeforeEach + void mock() { + when(featureToggleProperties.isForwardByOzgCloudEnabled()).thenReturn(false); + } + + @Test + void shouldReturnStream() { + var expectedStatuses = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG, + VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN); + + var stream = service.getVerwaltungUserStatuses(); + + assertThat(stream).containsExactlyInAnyOrderElementsOf(expectedStatuses); + } + } + } } diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java index 257f019b7ffc2ba3f4a0b10f74139f3ed49cc4b5..36d0bc99fb051803fe2996fed0ffab28355bf81c 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerITCase.java @@ -39,6 +39,8 @@ 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.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -281,24 +283,16 @@ class VorgangControllerITCase { @Nested class TestClientAttribute { - @Test - void shouldReturnNoContent() throws Exception { - var content = VorgangHeaderTestFactory.createHasNewPostfachNachrichtContent(false); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void shouldReturnNoContent(boolean hasNewPostfachNachricht) throws Exception { + var content = VorgangHeaderTestFactory.createHasNewPostfachNachrichtContent(hasNewPostfachNachricht); var response = doRequest(content); response.andExpect(status().isNoContent()); } - @Test - void shouldReturnUnprocessableEntity() throws Exception { - var content = VorgangHeaderTestFactory.createHasNewPostfachNachrichtContent(true); - - var response = doRequest(content); - - response.andExpect(status().isUnprocessableEntity()); - } - private ResultActions doRequest(String content) throws Exception { return mockMvc.perform(put("/api/vorgangs/{0}/hasNewPostfachNachricht", VorgangHeaderTestFactory.ID).with(csrf()) .contentType(MediaType.APPLICATION_JSON) diff --git a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerTest.java b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerTest.java index 609ea5ccf1b83f33ecb2db97b2f4ff90652d2470..5f641cbf4302f0b27e863bf82f0634379a8cda6b 100644 --- a/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerTest.java +++ b/alfa-service/src/test/java/de/ozgcloud/alfa/vorgang/VorgangControllerTest.java @@ -869,22 +869,21 @@ class VorgangControllerTest { } } - @DisplayName("Reset new postfachNachricht") @Nested - class TestResetNewPostfachNachricht { + class TestSetNewPostfachNachricht { @Test - void resetNewPostfachNachricht() { + void shouldResetNewPostfachNachricht() { performRequest("{\"hasNewPostfachNachricht\":false}").assertThat().hasStatus(HttpStatus.NO_CONTENT); verify(clientAttributeService).resetPostfachNachricht(VorgangHeaderTestFactory.ID); } @Test - void shouldReturnUnprocessableEntity() { - performRequest("{\"hasNewPostfachNachricht\":true}").assertThat().hasStatus(HttpStatus.UNPROCESSABLE_ENTITY); + void shouldSetHasNewPostfachNachricht() { + performRequest("{\"hasNewPostfachNachricht\":true}").assertThat().hasStatus(HttpStatus.NO_CONTENT); - verify(clientAttributeService, never()).resetPostfachNachricht(VorgangHeaderTestFactory.ID); + verify(clientAttributeService).setHasNewPostfachNachricht(VorgangHeaderTestFactory.ID); } private MockMvcRequestBuilder performRequest(String body) { diff --git a/alfa-service/src/test/resources/application.yml b/alfa-service/src/test/resources/application.yml index ea81cad0839dc0d94a6ee477c178dd42c66b44fe..1fb7f34662fc94fee5c429cb655a81aa99f53efa 100644 --- a/alfa-service/src/test/resources/application.yml +++ b/alfa-service/src/test/resources/application.yml @@ -1,7 +1,7 @@ logging: level: ROOT: WARN - '[de.ozgcloud]': INFO, + '[de.ozgcloud]': INFO '[org.springframework.security]': WARN ozgcloud: diff --git a/pom.xml b/pom.xml index ceee27ef0d7b698ffbfd9f4678bf0b3a4c66b1e6..79db55f35ab10c7f34e14500e243e86676558d0f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ <groupId>de.ozgcloud.alfa</groupId> <artifactId>alfa</artifactId> - <version>2.22.0-SNAPSHOT</version> + <version>2.23.0-SNAPSHOT</version> <name>Alfa Parent</name> <packaging>pom</packaging> <inceptionYear>2020</inceptionYear> diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index ca99396fb145aef959cab90037aa14d0df91b5ed..a0c14d08bdd36bd36cdf19a84c36ffb31281e8ef 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -100,6 +100,14 @@ spec: - name: ozgcloud_user-assistance_documentation_url value: {{ .Values.ozgcloud.user_assistance.documentation.url }} {{- end }} + {{- if (((.Values.ozgcloud).upload).maxFileSize).postfachNachrichtAttachment }} + - name: ozgcloud_upload_maxFileSize_postfachNachrichtAttachment + value: {{ .Values.ozgcloud.upload.maxFileSize.postfachNachrichtAttachment }} + {{- end }} + {{- if (((.Values.ozgcloud).upload).contentTypes).postfachNachrichtAttachment }} + - name: ozgcloud_upload_contentTypes_postfachNachrichtAttachment + value: {{ .Values.ozgcloud.upload.contentTypes.postfachNachrichtAttachment }} + {{- end }} {{- with include "app.getCustomList" . }} {{ . | indent 8 }} {{- end }} diff --git a/src/test/helm/deployment_63_char_test.yaml b/src/test/helm/deployment_63_char_test.yaml index 28fa4858c874ef7f6a783a6d5ecadb9666ab797a..e4f54aa4043f30d2e29d3b0bd43af18ce39491b7 100644 --- a/src/test/helm/deployment_63_char_test.yaml +++ b/src/test/helm/deployment_63_char_test.yaml @@ -44,21 +44,21 @@ set: imagePullSecret: image-pull-secret tests: + - it: should not fail on .Release.Namespace length less than 63 characters + asserts: + - notFailedTemplate: { } - it: should fail on .Release.Namespace length longer than 63 characters release: namespace: test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 asserts: - failedTemplate: errorMessage: .Release.Namespace test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen) - - it: should not fail on .Release.Namespace length less than 63 characters + - it: should not fail on .Chart.Name-.Chart.Version length less than 63 characters asserts: - - notFailedTemplate: {} + - notFailedTemplate: { } - it: should fail on .Chart.Name-.Chart.Version length longer than 63 characters chart: version: 1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 asserts: - failedTemplate: errorMessage: .Chart.Name-.Chart.Version alfa-1.0-test1234567890123123456789012345678901234567890123456789012345678901234567890123456789012345678904567890 ist zu lang (max. 63 Zeichen) - - it: should not fail on .Chart.Name-.Chart.Version length less than 63 characters - asserts: - - notFailedTemplate: {} diff --git a/src/test/helm/deployment_upload_env_test.yaml b/src/test/helm/deployment_upload_env_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b0791698b17b5ca94efebbcb2b831d2fe163626a --- /dev/null +++ b/src/test/helm/deployment_upload_env_test.yaml @@ -0,0 +1,72 @@ +# +# Copyright (C) 2025 Das Land Schleswig-Holstein vertreten durch den +# Ministerpräsidenten des Landes Schleswig-Holstein +# Staatskanzlei +# Abteilung Digitalisierung und zentrales IT-Management der Landesregierung +# +# Lizenziert unter der EUPL, Version 1.2 oder - sobald +# diese von der Europäischen Kommission genehmigt wurden - +# Folgeversionen der EUPL ("Lizenz"); +# Sie dürfen dieses Werk ausschließlich gemäß +# dieser Lizenz nutzen. +# Eine Kopie der Lizenz finden Sie hier: +# +# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 +# +# Sofern nicht durch anwendbare Rechtsvorschriften +# gefordert oder in schriftlicher Form vereinbart, wird +# die unter der Lizenz verbreitete Software "so wie sie +# ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - +# ausdrücklich oder stillschweigend - verbreitet. +# Die sprachspezifischen Genehmigungen und Beschränkungen +# unter der Lizenz sind dem Lizenztext zu entnehmen. +# + +suite: deployment upload env +release: + name: alfa + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + baseUrl: test.company.local + ozgcloud: + environment: test + bundesland: sh + bezeichner: helm + barrierefreiheitUrl: http://barrierefreiheit.test.url + sso: + serverUrl: https://sso.company.local + imagePullSecret: image-pull-secret +tests: + - it: should set upload values + set: + ozgcloud: + upload: + maxFileSize: + postfachNachrichtAttachment: "50MB" + contentTypes: + postfachNachrichtAttachment: "text/plain,application/pdf" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_upload_maxFileSize_postfachNachrichtAttachment + value: "50MB" + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_upload_contentTypes_postfachNachrichtAttachment + value: "text/plain,application/pdf" + - it: should not set upload values when missing + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_upload_maxFileSize_postfachNachrichtAttachment + any: true + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_upload_contentTypes_postfachNachrichtAttachment + any: true \ No newline at end of file