diff --git a/Jenkinsfile b/Jenkinsfile index 81a74c97531aa991385a034402a13fbbb38786b6..68d5ff909746390e066b0536415c8385e7c263c4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -44,6 +44,32 @@ pipeline { } } } + + stage('Set Version') { + when { + not { + anyOf { + branch 'master' + branch 'release' + } + } + } + steps { + script { + FAILED_STAGE=env.STAGE_NAME + JAR_TAG = getPomVersion('pom.xml').replace("SNAPSHOT", "${env.BRANCH_NAME}-SNAPSHOT") + BESCHEID_MANAGER_TAG = getPomVersion('bescheid-manager/pom.xml').replace("SNAPSHOT", "${env.BRANCH_NAME}-SNAPSHOT") + updateBescheidManagerDependencyVersion(BESCHEID_MANAGER_TAG) + + } + configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { + sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${JAR_TAG} -DprocessAllModules=true" + dir('bescheid-manager') { + sh "mvn -s $MAVEN_SETTINGS versions:set -DnewVersion=${BESCHEID_MANAGER_TAG}" + } + } + } + } stage('Build VorgangManager') { steps { @@ -58,18 +84,13 @@ pipeline { } stage('Deploy to Nexus'){ - when { - anyOf { - branch 'master' - branch 'release' - } - } steps { script { FAILED_STAGE = env.STAGE_NAME } configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS -DskipTests deploy -Dmaven.wagon.http.retryHandler.count=3' + } } } @@ -84,6 +105,7 @@ pipeline { dir('vorgang-manager-server') { sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS spring-boot:build-image -DskipTests -Dmaven.wagon.http.retryHandler.count=3' } + sh 'mvn -s $MAVEN_SETTINGS versions:revert' } } } @@ -92,10 +114,11 @@ pipeline { steps { script { FAILED_STAGE=env.STAGE_NAME + IMAGE_TAG = generateImageTag('pom.xml') tagAndPushDockerImage('vorgang-manager', IMAGE_TAG) - + if (env.BRANCH_NAME == 'master') { tagAndPushDockerImage('vorgang-manager', 'snapshot-latest') tagAndPushDockerImage('vorgang-manager', 'e2e-test') @@ -160,12 +183,17 @@ pipeline { stage ('Deploy SBOM to DependencyTrack') { steps { - configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { - withCredentials([string(credentialsId: 'dependency-track-api-key', variable: 'API_KEY')]) { + script { + IMAGE_TAG = generateImageTag('pom.xml') - dir('vorgang-manager-server') { - - sh 'mvn --no-transfer-progress -s $MAVEN_SETTINGS io.github.pmckeown:dependency-track-maven-plugin:upload-bom -Ddependency-track.apiKey=$API_KEY -Ddependency-track.projectVersion=' + generateImageTag('pom.xml') + ' -Ddependency-track.dependencyTrackBaseUrl=https://dependency-track.ozg-sh.de' + configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { + withCredentials([string(credentialsId: 'dependency-track-api-key', variable: 'API_KEY')]) { + + dir('vorgang-manager-server') { + catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') { + sh "mvn --no-transfer-progress -s $MAVEN_SETTINGS io.github.pmckeown:dependency-track-maven-plugin:upload-bom -Ddependency-track.apiKey=$API_KEY -Ddependency-track.projectVersion=${IMAGE_TAG} -Ddependency-track.dependencyTrackBaseUrl=https://dependency-track.ozg-sh.de" + } + } } } } @@ -269,6 +297,14 @@ String getPomVersion(String pomFile){ return pom.version } +void updateBescheidManagerDependencyVersion(String snapshotVersion) { + def vorgangManagerServerPom = readMavenPom file: 'vorgang-manager-server/pom.xml' + if ( vorgangManagerServerPom.properties['bescheid-manager.version'] ==~ SNAPSHOT_REGEX ) { + vorgangManagerServerPom.properties['bescheid-manager.version'] = snapshotVersion + writeMavenPom model: vorgangManagerServerPom, file: 'vorgang-manager-server/pom.xml' + } +} + String generateImageTag(String pomFile) { def imageTag = "${env.BRANCH_NAME}-${getPomVersion(pomFile)}" @@ -344,7 +380,7 @@ Void sendFailureMessage() { "formatted_body":"VorgangManager: Build Failed. Stage: ${FAILED_STAGE} Build-ID: <a href='${BLUE_OCEAN_URL}'>${env.BUILD_NUMBER}</a>"}""" if (env.BRANCH_NAME == 'master') { - room = "!iQPAvQIiRwRpNOszjw:matrix.ozg-sh.de" + room = "!GjqhmouBtnDbwUkAjx:matrix.ozg-sh.de" } else if (env.BRANCH_NAME == 'release') { room = "!oWZpUGTFsxkJIYNfYg:matrix.ozg-sh.de" diff --git a/bescheid-manager/pom.xml b/bescheid-manager/pom.xml index 7a0bae459ad6ca5dea200d31d22a56b8ebcfc789..1ad2f48abe90b5e1adb825e420486d79b16df89c 100644 --- a/bescheid-manager/pom.xml +++ b/bescheid-manager/pom.xml @@ -5,19 +5,19 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-parent</artifactId> - <version>4.2.0</version> + <version>4.3.1</version> <relativePath /> </parent> <groupId>de.ozgcloud.bescheid</groupId> <artifactId>bescheid-manager</artifactId> <name>OZG-Cloud Bescheid Manager</name> - <version>1.14.0-SNAPSHOT</version> + <version>1.17.0-SNAPSHOT</version> <properties> <vorgang-manager.version>2.9.0</vorgang-manager.version> <nachrichten-manager.version>2.9.0</nachrichten-manager.version> - <api-lib.version>0.10.0</api-lib.version> + <api-lib.version>0.11.0</api-lib.version> </properties> <dependencies> diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java index 49fbce9beef117e017788ae682323792bdb7478e..ada68e5d7b3dcc83dd1aeacc8998ba32b8702820 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidEventListener.java @@ -23,6 +23,8 @@ */ package de.ozgcloud.bescheid; +import static java.util.Objects.*; + import java.time.LocalDate; import java.util.Arrays; import java.util.Optional; @@ -42,10 +44,10 @@ import de.ozgcloud.bescheid.attacheditem.AttachedItemService; import de.ozgcloud.bescheid.binaryfile.BinaryFileService; import de.ozgcloud.bescheid.common.callcontext.CurrentUserService; import de.ozgcloud.bescheid.common.user.UserProfileService; -import de.ozgcloud.bescheid.nachricht.NachrichtService; import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandCreatedEvent; +import de.ozgcloud.command.CommandExecutedEvent; import de.ozgcloud.command.CommandFailedEvent; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.document.BescheidDocumentCreatedEvent; @@ -78,8 +80,9 @@ class BescheidEventListener { public static final Predicate<Command> IS_CREATE_BESCHEID_DOCUMENT_COMMAND = command -> CREATE_BESCHEID_DOCUMENT_ORDER.equals(command.getOrder()); private static final String IS_CREATE_BESCHEID_DOCUMENT = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_CREATE_BESCHEID_DOCUMENT_COMMAND.test(event.getSource())}"; - public static final Predicate<Command> IS_SEND_BESCHEID_COMMAND = command -> SEND_BESCHEID_ORDER.equals(command.getOrder()); + public static final Predicate<Command> IS_SEND_BESCHEID_COMMAND = command -> nonNull(command) && SEND_BESCHEID_ORDER.equals(command.getOrder()); private static final String IS_SEND_BESCHEID = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_BESCHEID_COMMAND.test(event.getSource())}"; + private static final String IS_SEND_BESCHEID_ORDER = "{T(de.ozgcloud.bescheid.BescheidEventListener).IS_SEND_BESCHEID_COMMAND.test(event.getCommand())}"; static final String VORGANG_ID_BODY_KEY = "vorgangId"; static final String BESCHEID_VOM_BODY_KEY = "bescheidVom"; @@ -90,14 +93,12 @@ class BescheidEventListener { private final BescheidService service; private final BinaryFileService fileService; - private final NachrichtService nachrichtService; private final AttachedItemService attachedItemService; private final DocumentService documentService; private final ApplicationEventPublisher eventPublisher; private final CurrentUserService userService; private final UserProfileService userProfileService; - private final BescheidFeatureProperties bescheidFeatureProperties; @EventListener(condition = IS_CREATE_BESCHEID) public void onCreateBescheidCommand(CommandCreatedEvent event) { @@ -105,12 +106,6 @@ class BescheidEventListener { } void doCreateBescheid(Command command) { - if (bescheidFeatureProperties.isKielHackathonRoute()) { - var bescheid = doCreateBescheidBiz(command); - nachrichtService.createNachrichtDraft(bescheid); - eventPublisher.publishEvent(new BescheidCreatedEvent(command)); - return; - } var createdItemId = attachedItemService.createBescheidDraft(command); eventPublisher.publishEvent(new BescheidCreatedEvent(command, createdItemId)); } @@ -165,9 +160,9 @@ class BescheidEventListener { void doSendBescheid(Command command) { var bescheidItem = attachedItemService.getItem(command.getRelationId()); if (SendBy.MANUAL.hasValue(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY))) { - service.sendBescheidManually(bescheidItem, command.getRelationVersion()); + service.sendBescheidManually(bescheidItem, command); } else if (SendBy.NACHRICHT.hasValue(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY))) { - service.sendBescheidPostfachMail(bescheidItem, command.getRelationVersion()); + service.sendBescheidPostfachMail(bescheidItem, command); } else { throw new TechnicalException("Bescheid has unexpected sendBy value: '%s'. Allowed values are %s." .formatted(bescheidItem.getItem().get(Bescheid.FIELD_SEND_BY), @@ -190,6 +185,15 @@ class BescheidEventListener { return builder.build(); } + @EventListener(condition = IS_SEND_BESCHEID_ORDER) + public void onBescheidSent(CommandExecutedEvent event) { + runWithSecurityContext(event.getCommand(), this::setAntragBewilligung); + } + + void setAntragBewilligung(Command command) { + service.setAntragBewilligung(attachedItemService.getItem(command.getRelationId())); + } + void runWithSecurityContext(Command command, Consumer<Command> commandExecutor) { SecurityContext prevContext = null; try { diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java index ffc2ece833e690b658dd1689c38e5e28789d1689..fb010f5dff17f3406354455944fab3a4f168be2c 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidService.java @@ -1,25 +1,35 @@ package de.ozgcloud.bescheid; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.stream.Stream; import jakarta.annotation.PostConstruct; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.info.BuildProperties; import org.springframework.stereotype.Service; +import de.ozgcloud.apilib.common.command.OzgCloudCommand; +import de.ozgcloud.apilib.common.command.OzgCloudCommandService; +import de.ozgcloud.apilib.common.command.OzgCloudCreateSubCommandsRequest; +import de.ozgcloud.apilib.common.command.grpc.CommandMapper; import de.ozgcloud.bescheid.attacheditem.AttachedItem; import de.ozgcloud.bescheid.attacheditem.AttachedItemService; import de.ozgcloud.bescheid.attributes.ClientAttributeService; import de.ozgcloud.bescheid.common.user.UserProfileService; -import de.ozgcloud.bescheid.nachricht.NachrichtService; import de.ozgcloud.bescheid.vorgang.Vorgang; import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.bescheid.vorgang.VorgangService; +import de.ozgcloud.command.Command; import de.ozgcloud.common.binaryfile.FileId; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.document.Document; @@ -33,10 +43,24 @@ class BescheidService { private static final String ERROR_MESSAGE_NO_SERVICE = "No Bescheid Endpoint is configured."; + static final String VORGANG_BESCHEIDEN_ORDER = "VORGANG_BESCHEIDEN"; + static final String SUBCOMMANDS_EXECUTION_MODE = "PARALLEL"; + static final String SEND_POSTFACH_NACHRICHT_ORDER = "SEND_POSTFACH_NACHRICHT"; + + static final String FIELD_POSTFACH_ID = "postfachId"; + static final String FIELD_REPLY_OPTION = "replyOption"; + static final String REPLY_OPTION = "FORBIDDEN"; + static final String FIELD_SUBJECT = "subject"; + static final String FIELD_MAIL_BODY = "mailBody"; + static final String FIELD_ATTACHMENTS = "attachments"; + static final String SUBJECT = "Ihr Bescheid zum Antrag"; + private final VorgangService vorgangService; private final AttachedItemService attachedItemService; - private final NachrichtService nachrichtService; - private final UserProfileService currentUserService; + private final UserProfileService userProfileService; + @Qualifier(BescheidManagerConfiguration.COMMAND_SERVICE_NAME) + private final OzgCloudCommandService commandService; + private final CommandMapper commandMapper; private final ClientAttributeService bescheidClientAttributeService; private final BuildProperties buildProperties; private final Optional<BescheidRemoteService> remoteService; @@ -72,9 +96,10 @@ class BescheidService { } } - public void sendBescheidManually(AttachedItem bescheidItem, long version) { - validateBescheidSendManually(bescheidItem, version); - setBescheidSent(bescheidItem); + public void sendBescheidManually(AttachedItem bescheidItem, Command parentCommand) { + validateBescheidSendManually(bescheidItem, parentCommand.getRelationVersion()); + var vorgang = vorgangService.getById(VorgangId.from(bescheidItem.getVorgangId())); + addSubCommands(parentCommand.getId(), buildSetBescheidSentSubCommands(bescheidItem, vorgang, getUserId())); } void validateBescheidSendManually(AttachedItem bescheidItem, long version) { @@ -85,11 +110,13 @@ class BescheidService { } } - public void sendBescheidPostfachMail(AttachedItem bescheidItem, long version) { - validateBescheidSendPostfach(bescheidItem, version); + public void sendBescheidPostfachMail(AttachedItem bescheidItem, Command parentCommand) { + validateBescheidSendPostfach(bescheidItem, parentCommand.getRelationVersion()); var vorgang = vorgangService.getById(VorgangId.from(bescheidItem.getVorgangId())); - setBescheidSent(bescheidItem); - nachrichtService.sendNachricht(buildBescheid(bescheidItem, vorgang.getServiceKonto())); + var userId = getUserId(); + var subCommands = buildSetBescheidSentSubCommands(bescheidItem, vorgang, userId); + subCommands.add(buildSendPostfachNachrichtCommand(bescheidItem, vorgang.getServiceKonto(), userId)); + addSubCommands(parentCommand.getId(), subCommands); } public void validateBescheidSendPostfach(AttachedItem bescheidItem, long version) { @@ -98,10 +125,10 @@ class BescheidService { if (Bescheid.SendBy.NACHRICHT.notValue(sendBy)) { throw new TechnicalException("Bescheid has unexpected sendBy value: '%s'. Expected is %s".formatted(sendBy, Bescheid.SendBy.NACHRICHT)); } - if (StringUtils.isBlank(getNachrichtSubject(bescheidItem))) { + if (getNachrichtSubject(bescheidItem).isEmpty()) { throw new TechnicalException("Bescheid has no nachricht subject"); } - if (StringUtils.isBlank(getNachrichtText(bescheidItem))) { + if (getNachrichtText(bescheidItem).isEmpty()) { throw new TechnicalException("Bescheid has no nachricht text"); } } @@ -119,30 +146,93 @@ class BescheidService { } } - Bescheid buildBescheid(AttachedItem bescheidItem, Vorgang.ServiceKonto serviceKonto) { - return Bescheid.builder() - .vorgangId(VorgangId.from(bescheidItem.getVorgangId())) - .genehmigt(getBewilligt(bescheidItem)) - .bescheidFileId(getBescheidFileId(bescheidItem)) - .nachrichtSubject(Optional.ofNullable(getNachrichtSubject(bescheidItem))) - .nachrichtText(Optional.ofNullable(getNachrichtText(bescheidItem))) - .createdBy(currentUserService.getUserProfile().getId()) - .serviceKonto(serviceKonto) - .attachments(getAttachments(bescheidItem)) + Optional<String> getNachrichtSubject(AttachedItem bescheidItem) { + return Optional.ofNullable(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_SUBJECT)).map(StringUtils::trimToNull); + } + + Optional<String> getNachrichtText(AttachedItem bescheidItem) { + return Optional.ofNullable(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_TEXT)).map(StringUtils::trimToNull); + } + + String getUserId() { + return userProfileService.getUserProfile().getId().toString(); + } + + List<OzgCloudCommand> buildSetBescheidSentSubCommands(AttachedItem bescheidItem, Vorgang vorgang, String userId) { + var subCommands = new ArrayList<OzgCloudCommand>(); + subCommands.add(buildVorgangBescheidenCommand(vorgang)); + subCommands.add(buildSetBescheidSentStatusCommand(bescheidItem, userId)); + return subCommands; + } + + OzgCloudCommand buildVorgangBescheidenCommand(Vorgang vorgang) { + return OzgCloudCommand.builder() + .vorgangId(commandMapper.toOzgCloudVorgangId(vorgang.getId().toString())) + .relationId(commandMapper.mapRelationId(vorgang.getId().toString())) + .relationVersion(vorgang.getVersion()) + .order(VORGANG_BESCHEIDEN_ORDER) .build(); } - FileId getBescheidFileId(AttachedItem bescheidItem) { - var bescheidDocument = attachedItemService.getItem(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT)); - return FileId.from(MapUtils.getString(bescheidDocument.getItem(), Document.FIELD_DOCUMENT_FILE)); + OzgCloudCommand buildSetBescheidSentStatusCommand(AttachedItem bescheidItem, String userId) { + return OzgCloudCommand.builder() + .relationId(commandMapper.mapRelationId(bescheidItem.getId())) + .relationVersion(bescheidItem.getVersion()) + .order(AttachedItemService.PATCH_ATTACHED_ITEM) + .bodyObject(Map.of( + AttachedItem.PROPERTY_ID, bescheidItem.getId(), + AttachedItem.PROPERTY_ITEM, buildBescheidSentStatusItem(userId))) + .build(); + } + + Map<String, Object> buildBescheidSentStatusItem(String userId) { + return Map.of( + Bescheid.FIELD_STATUS, Bescheid.Status.SENT.name(), + Bescheid.FIELD_SENT_INFO, buildSentInfoMap(userId)); + } + + Map<String, Object> buildSentInfoMap(String userId) { + return Map.of( + Bescheid.FIELD_SENT_AT, ZonedDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME), + Bescheid.FIELD_SENT_BY, userId); + } + + OzgCloudCommand buildSendPostfachNachrichtCommand(AttachedItem bescheidItem, Vorgang.ServiceKonto serviceKonto, String userId) { + var bodyObject = buildSendNachrichtCommandBody(bescheidItem, buildPostfachAddress(serviceKonto.getPostfachAddresses().getFirst(), + serviceKonto.getType())); + return OzgCloudCommand.builder() + .vorgangId(commandMapper.toOzgCloudVorgangId(bescheidItem.getVorgangId())) + .relationId(commandMapper.mapRelationId(bescheidItem.getVorgangId())) + .order(SEND_POSTFACH_NACHRICHT_ORDER) + .createdBy(commandMapper.toOzgCloudUserId(userId)) + .bodyObject(bodyObject) + .build(); + } + + Map<String, Object> buildPostfachAddress(Vorgang.PostfachAddress postfachAddress, String serviceKontoType) { + return Map.of(Vorgang.PostfachAddress.FIELD_TYPE, postfachAddress.getType(), + Vorgang.PostfachAddress.FIELD_VERSION, postfachAddress.getVersion(), + Vorgang.PostfachAddress.FIELD_IDENTIFIER, postfachAddress.getIdentifier(), + Vorgang.ServiceKonto.FIELD_SERVICEKONTO_TYPE, serviceKontoType); + } + + Map<String, Object> buildSendNachrichtCommandBody(AttachedItem bescheidItem, Map<String, Object> postfachAddress) { + return Map.of( + FIELD_REPLY_OPTION, REPLY_OPTION, + FIELD_SUBJECT, getNachrichtSubject(bescheidItem).orElse(SUBJECT), + FIELD_MAIL_BODY, getNachrichtText(bescheidItem).orElse(StringUtils.EMPTY), + FIELD_ATTACHMENTS, buildAttachments(bescheidItem), + Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, postfachAddress); + } - String getNachrichtSubject(AttachedItem bescheidItem) { - return MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_SUBJECT); + List<String> buildAttachments(AttachedItem bescheidItem) { + return Stream.concat(Stream.of(getBescheidFileId(bescheidItem)), getAttachments(bescheidItem).stream()).map(FileId::toString).toList(); } - String getNachrichtText(AttachedItem bescheidItem) { - return MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_NACHRICHT_TEXT); + FileId getBescheidFileId(AttachedItem bescheidItem) { + var bescheidDocument = attachedItemService.getItem(MapUtils.getString(bescheidItem.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT)); + return FileId.from(MapUtils.getString(bescheidDocument.getItem(), Document.FIELD_DOCUMENT_FILE)); } List<FileId> getAttachments(AttachedItem bescheidItem) { @@ -160,18 +250,20 @@ class BescheidService { return Collections.emptyList(); } - void setBescheidSent(AttachedItem bescheidItem) { - attachedItemService.setBescheidSentStatus(bescheidItem.getId(), bescheidItem.getVersion()); - try { - vorgangService.bescheiden(bescheidItem.getVorgangId()); - } catch (Exception e) { - attachedItemService.revertBescheidSendStatus(bescheidItem.getId(), bescheidItem.getVersion()); - throw e; - } - setAntragBewilligung(bescheidItem); + void addSubCommands(String parentCommandId, List<OzgCloudCommand> subCommands) { + commandService.addSubCommands(buildCreateSubCommandsRequest(parentCommandId, subCommands)); + } + + OzgCloudCreateSubCommandsRequest buildCreateSubCommandsRequest(String parentCommandId, List<OzgCloudCommand> subCommands) { + return OzgCloudCreateSubCommandsRequest.builder() + .parentId(parentCommandId) + .executionMode(SUBCOMMANDS_EXECUTION_MODE) + .completedIfSubsCompleted(true) + .subCommands(subCommands) + .build(); } - void setAntragBewilligung(AttachedItem bescheidItem) { + public void setAntragBewilligung(AttachedItem bescheidItem) { try { bescheidClientAttributeService.setAntragResult(bescheidItem.getVorgangId(), getBewilligt(bescheidItem)); } catch (Exception e) { diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java index 4e2867be8883419b25ccbd4a055fd7de97f7eef2..24a67a215b953478b6bf2518432c37469eb94e42 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/attacheditem/AttachedItemService.java @@ -25,8 +25,6 @@ package de.ozgcloud.bescheid.attacheditem; import static java.util.Objects.*; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -37,7 +35,6 @@ import java.util.stream.Collectors; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; @@ -47,39 +44,33 @@ import de.ozgcloud.apilib.common.command.grpc.CommandMapper; import de.ozgcloud.bescheid.Bescheid; import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor; import de.ozgcloud.bescheid.BescheidManagerConfiguration; -import de.ozgcloud.bescheid.common.callcontext.CallContextUser; -import de.ozgcloud.bescheid.common.callcontext.CurrentUserService; import de.ozgcloud.bescheid.common.freemarker.TemplateHandler; import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.command.Command; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.document.Document; +import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Log4j2 @Service +@RequiredArgsConstructor public class AttachedItemService { public static final String BESCHEID_ITEM_NAME = "Bescheid"; + public static final String PATCH_ATTACHED_ITEM = "PATCH_ATTACHED_ITEM"; static final String CREATE_ATTACHED_ITEM_ORDER = "CREATE_ATTACHED_ITEM"; static final String UPDATE_ATTACHED_ITEM_ORDER = "UPDATE_ATTACHED_ITEM"; static final String DELETE_ATTACHED_ITEM = "DELETE_ATTACHED_ITEM"; - static final String PATCH_ATTACHED_ITEM = "PATCH_ATTACHED_ITEM"; static final String DEFAULT_SUBJECT = "Ihr Bescheid zum Antrag"; static final String DEFAULT_TEMPLATE_FILE = "bescheid.nachrichtTemplate.txt.ftlh"; @Qualifier(BescheidManagerConfiguration.COMMAND_SERVICE_NAME) - @Autowired - private OzgCloudCommandService commandService; - @Autowired - private VorgangAttachedItemRemoteService remoteService; - @Autowired - private CommandMapper commandMapper; - @Autowired - private CurrentUserService currentUserService; - @Autowired - private TemplateHandler templateHandler; + private final OzgCloudCommandService commandService; + private final VorgangAttachedItemRemoteService remoteService; + private final CommandMapper commandMapper; + private final TemplateHandler templateHandler; public Optional<AttachedItem> findBescheidItem(String vorgangId) { return remoteService.findBescheidDraft(vorgangId); @@ -121,7 +112,7 @@ public class AttachedItemService { String overrideAttachedItem(AttachedItem bescheidItem, Command command) { var attachedItem = buildAttachedItemAsMap(command, buildItemMapWithAllBescheidFields(command)); - var finishedOzgCloudCommand = commandService.createAndWaitUntilDone(buildUpdateAttachedItemCommand(bescheidItem, attachedItem)); + var finishedOzgCloudCommand = commandService.createAndWaitUntilDone(buildPatchAttachedItemCommand(bescheidItem, attachedItem)); return finishedOzgCloudCommand.getCreatedResource(); } @@ -146,7 +137,7 @@ public class AttachedItemService { } private OzgCloudCommand buildUpdateBescheidDraftCommand(Command command, Optional<Document> document, AttachedItem bescheidItem) { - return buildUpdateAttachedItemCommand(bescheidItem, buildAttachedItemAsMap(command, buildBescheidItemAsMap(command, document))); + return buildPatchAttachedItemCommand(bescheidItem, buildAttachedItemAsMap(command, buildBescheidItemAsMap(command, document))); } Map<String, Object> buildBescheidItemAsMap(Command command, Optional<Document> newDocument) { @@ -173,12 +164,12 @@ public class AttachedItemService { return MapUtils.getString(item.getItem(), Bescheid.FIELD_BESCHEID_DOCUMENT); } - OzgCloudCommand buildUpdateAttachedItemCommand(AttachedItem bescheidItem, Map<String, Object> bodyObject) { + OzgCloudCommand buildPatchAttachedItemCommand(AttachedItem bescheidItem, Map<String, Object> bodyObject) { return OzgCloudCommand.builder() .vorgangId(commandMapper.toOzgCloudVorgangId(bescheidItem.getVorgangId())) .relationId(commandMapper.mapRelationId(bescheidItem.getId())) .relationVersion(bescheidItem.getVersion()) - .order(UPDATE_ATTACHED_ITEM_ORDER) + .order(PATCH_ATTACHED_ITEM) .bodyObject(bodyObject) .build(); } @@ -267,65 +258,12 @@ public class AttachedItemService { .build(); } - public void setBescheidSentStatus(String id, long version) { - createPatchCommand(id, version, buildBescheidSentStatusBodyObject(id)); - } - - Map<String, Object> buildBescheidSentStatusBodyObject(String bescheidId) { - return Map.of(AttachedItem.PROPERTY_ID, bescheidId, - AttachedItem.PROPERTY_ITEM, buildBescheidSentStatusItem()); - } - - Map<String, Object> buildBescheidSentStatusItem() { - var itemMap = buildItemMap(Bescheid.Status.SENT); - itemMap.put(Bescheid.FIELD_SENT_INFO, buildSentInfoMap()); - return itemMap; - } - - Map<String, Object> buildSentInfoMap() { - return Map.of( - Bescheid.FIELD_SENT_AT, ZonedDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME), - Bescheid.FIELD_SENT_BY, getUserId()); - } - - String getUserId() { - return currentUserService.findUser().flatMap(CallContextUser::getUserId).orElse(StringUtils.EMPTY); - } - - public void revertBescheidSendStatus(String id, long version) { - createPatchCommand(id, version, buildRevertBescheidSentStatusBodyObject(id)); - } - - private void createPatchCommand(String id, long version, Map<String, Object> itemMap) { - commandService.createAndWaitUntilDone(buildPatchBescheidCommand(id, version, itemMap)); - } - - Map<String, Object> buildRevertBescheidSentStatusBodyObject(String bescheidId) { - return Map.of(AttachedItem.PROPERTY_ID, bescheidId, - AttachedItem.PROPERTY_ITEM, buildRevertBescheidSentStatusItem()); - } - - Map<String, Object> buildRevertBescheidSentStatusItem() { - var itemMap = buildItemMap(Bescheid.Status.DRAFT); - itemMap.put(Bescheid.FIELD_SENT_INFO, null); - return itemMap; - } - Map<String, Object> buildItemMap(Bescheid.Status status) { var itemMap = new HashMap<String, Object>(); itemMap.put(Bescheid.FIELD_STATUS, status.name()); return itemMap; } - OzgCloudCommand buildPatchBescheidCommand(String bescheidId, long bescheidVersion, Map<String, Object> bodyObject) { - return OzgCloudCommand.builder() - .relationId(commandMapper.mapRelationId(bescheidId)) - .relationVersion(bescheidVersion) - .order(PATCH_ATTACHED_ITEM) - .bodyObject(bodyObject) - .build(); - } - public Collection<AttachedItem> findAllBescheid(VorgangId vorgangId) { return remoteService.findAllBescheid(vorgangId); } diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/user/UserProfileTestFactory.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/user/UserProfileTestFactory.java index 8b20aa41c65568e456538a45f3c363d1a18cbded..60949bbe87cf4d3b6c2a366dcc529d4be45c99bf 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/user/UserProfileTestFactory.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/common/user/UserProfileTestFactory.java @@ -33,7 +33,8 @@ public class UserProfileTestFactory { private static final LoremIpsum LOREM_IPSUM = LoremIpsum.getInstance(); - public static final UserId ID = UserId.from(UUID.randomUUID().toString()); + public static final String ID_STR = UUID.randomUUID().toString(); + public static final UserId ID = UserId.from(ID_STR); public static final String FIRST_NAME = LOREM_IPSUM.getFirstName(); public static final String LAST_NAME = LOREM_IPSUM.getLastName(); diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/Nachricht.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/Nachricht.java deleted file mode 100644 index 1f352d1d9efab6714236d1fbd00b01b33c1ea0dc..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/Nachricht.java +++ /dev/null @@ -1,33 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import java.time.ZonedDateTime; - -import de.ozgcloud.bescheid.UserId; -import de.ozgcloud.bescheid.vorgang.Vorgang; -import de.ozgcloud.bescheid.vorgang.VorgangId; -import de.ozgcloud.common.binaryfile.FileId; -import lombok.Builder; -import lombok.Getter; -import lombok.NonNull; - -@Getter -@Builder -public class Nachricht { - - private NachrichtId id; - private VorgangId vorgangId; - - @NonNull - private String subject; - @NonNull - private String mailBody; - - private FileId bescheidFileId; - - @NonNull - private UserId createdBy; - @Builder.Default - private ZonedDateTime createdAt = ZonedDateTime.now(); - - private Vorgang.PostfachAddress postfachAddress; -} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtId.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtId.java deleted file mode 100644 index b8fd29a9345542580177104549207a731e2c7139..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtId.java +++ /dev/null @@ -1,14 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import de.ozgcloud.common.datatype.StringBasedValue; - -public class NachrichtId extends StringBasedValue { - - NachrichtId(String nachrichtId) { - super(nachrichtId); - } - - public static NachrichtId from(String nachrichtId) { - return new NachrichtId(nachrichtId); - } -} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtMapper.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtMapper.java deleted file mode 100644 index 45bba2af3b3d076f92ed3b114fccd601a52ea2b4..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtMapper.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import org.mapstruct.CollectionMappingStrategy; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.NullValueCheckStrategy; -import org.mapstruct.ReportingPolicy; - -import de.ozgcloud.bescheid.UserId; -import de.ozgcloud.bescheid.vorgang.Vorgang; -import de.ozgcloud.common.binaryfile.FileId; -import de.ozgcloud.nachrichten.postfach.GrpcPostfachNachricht; -import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper; -import de.ozgcloud.vorgang.vorgang.GrpcPostfachAddress; - -@Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED, // - nullValueCheckStrategy = NullValueCheckStrategy.ALWAYS, unmappedTargetPolicy = ReportingPolicy.ERROR, uses = { GrpcObjectMapper.class }) -public interface NachrichtMapper { - - @Mapping(target = "createdAtBytes", ignore = true) - @Mapping(target = "mergePostfachAddress", ignore = true) - @Mapping(target = "mergeFrom", ignore = true) - @Mapping(target = "clearField", ignore = true) - @Mapping(target = "clearOneof", ignore = true) - @Mapping(target = "mergeUnknownFields", ignore = true) - @Mapping(target = "idBytes", ignore = true) - @Mapping(target = "mailBodyBytes", ignore = true) - @Mapping(target = "replyOptionBytes", ignore = true) - @Mapping(target = "subjectBytes", ignore = true) - @Mapping(target = "unknownFields", ignore = true) - @Mapping(target = "allFields", ignore = true) - - @Mapping(target = "id", ignore = true) - @Mapping(target = "attachmentList", source = "bescheidFileId") - @Mapping(target = "replyOption", constant = "FORBIDDEN") - GrpcPostfachNachricht mapToGrpc(Nachricht nachricht); - - @Mapping(target = "mergeFrom", ignore = true) - @Mapping(target = "clearField", ignore = true) - @Mapping(target = "clearOneof", ignore = true) - @Mapping(target = "mergeIdentifier", ignore = true) - @Mapping(target = "mergeUnknownFields", ignore = true) - @Mapping(target = "unknownFields", ignore = true) - @Mapping(target = "versionBytes", ignore = true) - @Mapping(target = "allFields", ignore = true) - GrpcPostfachAddress mapAddress(Vorgang.PostfachAddress address); - - default String mapToString(FileId fileId) { - return fileId.toString(); - } - - default String mapToString(UserId userId) { - return userId.toString(); - } -} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteService.java deleted file mode 100644 index 594c6eb77ab84ffa026550f5fa4c0c65b1f1b698..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteService.java +++ /dev/null @@ -1,34 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import de.ozgcloud.nachrichten.postfach.GrpcSaveNachrichtDraftRequest; -import de.ozgcloud.nachrichten.postfach.PostfachServiceGrpc.PostfachServiceBlockingStub; -import io.grpc.ClientInterceptor; -import net.devh.boot.grpc.client.inject.GrpcClient; - -@Service -class NachrichtRemoteService { - - @GrpcClient("nachrichten-manager") - private PostfachServiceBlockingStub serviceStub; - - @Autowired - private NachrichtMapper mapper; - - @Autowired - private ClientInterceptor bescheidCallContextInterceptor; - - public void saveDraft(Nachricht nachricht) { - serviceStub.withInterceptors(bescheidCallContextInterceptor) - .saveNachrichtDraft(buildRequest(nachricht)); - } - - GrpcSaveNachrichtDraftRequest buildRequest(Nachricht nachricht) { - return GrpcSaveNachrichtDraftRequest.newBuilder() - .setVorgangId(nachricht.getVorgangId().toString()) - .setNachricht(mapper.mapToGrpc(nachricht)) - .build(); - } -} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java deleted file mode 100644 index ce261151ddaa6d682521e59cfbb806c21c033656..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/nachricht/NachrichtService.java +++ /dev/null @@ -1,116 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.stereotype.Service; - -import de.ozgcloud.apilib.common.command.OzgCloudCommand; -import de.ozgcloud.apilib.common.command.OzgCloudCommandService; -import de.ozgcloud.apilib.common.command.grpc.CommandMapper; -import de.ozgcloud.bescheid.Bescheid; -import de.ozgcloud.bescheid.common.freemarker.TemplateHandler; -import de.ozgcloud.bescheid.vorgang.Vorgang; -import de.ozgcloud.bescheid.vorgang.Vorgang.PostfachAddress; -import de.ozgcloud.common.binaryfile.FileId; -import de.ozgcloud.common.errorhandling.TechnicalException; -import lombok.extern.log4j.Log4j2; - -@Service -@Log4j2 -public class NachrichtService { - - public static final String SEND_POSTFACH_NACHRICHT_ORDER = "SEND_POSTFACH_NACHRICHT"; - public static final String FIELD_POSTFACH_ID = "postfachId"; - static final String FIELD_REPLY_OPTION = "replyOption"; - static final String REPLY_OPTION = "POSSIBLE"; - static final String FIELD_SUBJECT = "subject"; - static final String FIELD_MAIL_BODY = "mailBody"; - static final String FIELD_ATTACHMENTS = "attachments"; - - @Autowired - private NachrichtRemoteService remoteService; - @Autowired - @Qualifier("bescheid_OzgCloudCommandService") - private OzgCloudCommandService commandService; - @Autowired - private CommandMapper commandMapper; - @Autowired - private TemplateHandler templateHandler; - - static final String SUBJECT = "Ihr Bescheid zum Antrag"; - - private static final String TEMPLATE_FILE = "bescheid.nachrichtTemplate.txt.ftlh"; - - public void createNachrichtDraft(Bescheid bescheid) { - buildNachricht(bescheid).ifPresentOrElse(remoteService::saveDraft, () -> LOG.warn("No ServiceKonto given on Vorgang.")); - } - - Optional<Nachricht> buildNachricht(Bescheid bescheid) { - return getAddress(bescheid).map(address -> Nachricht.builder() - .vorgangId(bescheid.getVorgangId()) - .postfachAddress(address) - .subject(bescheid.getNachrichtSubject().orElse(SUBJECT)) - .mailBody(buildMessage(bescheid)) - .createdBy(bescheid.getCreatedBy()) - .bescheidFileId(bescheid.getBescheidFileId()) - .build()); - } - - String buildMessage(Bescheid bescheid) { - return bescheid.getNachrichtText() - .orElseGet(() -> templateHandler.fillTemplate(TEMPLATE_FILE, bescheid)); - } - - public void sendNachricht(Bescheid bescheid) { - commandService.createAndWaitUntilDone(buildSendBescheidCommand(bescheid)); - } - - OzgCloudCommand buildSendBescheidCommand(Bescheid bescheid) { - return OzgCloudCommand.builder() - .vorgangId(commandMapper.toOzgCloudVorgangId(bescheid.getVorgangId().toString())) - .relationId(commandMapper.mapRelationId(bescheid.getVorgangId().toString())) - .order(SEND_POSTFACH_NACHRICHT_ORDER) - .createdBy(commandMapper.toOzgCloudUserId(bescheid.getCreatedBy().toString())) - .bodyObject(buildSendNachrichtCommandBody(bescheid)) - .build(); - } - - Map<String, Object> buildSendNachrichtCommandBody(Bescheid bescheid) { - return Map.of( - FIELD_REPLY_OPTION, REPLY_OPTION, - FIELD_SUBJECT, bescheid.getNachrichtSubject().orElse(SUBJECT), - FIELD_MAIL_BODY, bescheid.getNachrichtText().orElse(StringUtils.EMPTY), - FIELD_ATTACHMENTS, buildAttachments(bescheid), - Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, buildPostfachAddress(bescheid)); - - } - - List<String> buildAttachments(Bescheid bescheid) { - return Stream.concat(Stream.of(bescheid.getBescheidFileId()), bescheid.getAttachments().stream()).map(FileId::toString).toList(); - } - - Map<String, Object> buildPostfachAddress(Bescheid bescheid) { - var postfachAddress = getAddress(bescheid).orElseThrow( - () -> new TechnicalException("No ServiceKonto on Vorgang (id: %s)".formatted(bescheid.getVorgangId()))); - return Map.of(PostfachAddress.FIELD_TYPE, postfachAddress.getType(), - PostfachAddress.FIELD_VERSION, postfachAddress.getVersion(), - PostfachAddress.FIELD_IDENTIFIER, postfachAddress.getIdentifier(), - Vorgang.ServiceKonto.FIELD_SERVICEKONTO_TYPE, bescheid.getServiceKonto().getType()); - } - - Optional<PostfachAddress> getAddress(Bescheid bescheid) { - var serviceKonto = bescheid.getServiceKonto(); - if (Objects.nonNull(serviceKonto)) { - return Optional.of(serviceKonto.getPostfachAddresses().get(0)); - } - return Optional.empty(); - } - -} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java index b3ce8a3e1a88daa5cff3e3a4b6af9f6b46ce2e98..651017599265902ee54a2e1c153ecfe5dd744f57 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteService.java @@ -20,6 +20,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.BodyExtractors; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; +import org.w3c.dom.Document; import org.w3c.dom.Text; import org.xml.sax.SAXException; @@ -55,6 +56,9 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService { private static final String FILE_TYPE_PDF = "PDF"; private static final String FILE_TYPE_XML = "XML"; + static final String NACHRICHTEN_TEXT_EXPRESSION = "/root/SmartDocument/Fields/NachrichtenText/text()"; // NOSONAR + static final String FIELD_TEMPLATE_EXPRESSION = "/root/SmartDocument/Fields/Field[@ID=\"Template.56E7AA0956C7486292E9A02114CB231C\"]/text()"; //NOSONAR + @SuppressWarnings("deprecation") // SD requires forced UTF-8 encoding private static final MediaType JSON_MEDIA_TYPE_FOR_SD = MediaType.APPLICATION_JSON_UTF8; @@ -116,31 +120,48 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService { } Optional<String> getNachrichtText(SmartDocumentsResponse response) { - return getXMLFile(response).flatMap(this::extractTextFormXmlFile); + return getXMLFile(response).flatMap(this::extractTextFromXmlFile); } - Optional<String> extractTextFormXmlFile(File xmlFile) { + Optional<String> extractTextFromXmlFile(File xmlFile) { try { - return doExtractText(xmlFile); - } catch (XPathExpressionException | SAXException | IOException | ParserConfigurationException e) { - LOG.warn("XML-Parsing error on extracting Nachricht-Text: {}", e.getMessage(), e); - } catch (ClassCastException e) { - LOG.warn("Error on extraction Nachricht-Text. XPath return unexpected Type.", e); + var document = parseXml(xmlFile); + if (document.isPresent()) { + return document.flatMap(this::getNachrichtenText).or(() -> getFieldTemplateText(document.get())).map(Text::getTextContent); + } } catch (RuntimeException e) { LOG.warn("Unexpected Error on extracting NachrichtText: {}", e.getMessage(), e); } return Optional.empty(); } - Optional<String> doExtractText(File xmlFile) - throws SAXException, IOException, ParserConfigurationException, XPathExpressionException { - var xPath = XPathFactory.newInstance().newXPath(); + Optional<Document> parseXml(File xmlFile) { + try { + return Optional.of(DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(xmlFile)); + } catch (SAXException | IOException | ParserConfigurationException e) { + LOG.warn("XML-Parsing error on extracting Nachricht-Text: {}", e.getMessage(), e); + } + return Optional.empty(); + } + + Optional<Text> getNachrichtenText(Document document) { + return evaluateXPath(document, NACHRICHTEN_TEXT_EXPRESSION); + } - var document = DocumentBuilderFactory.newDefaultInstance().newDocumentBuilder().parse(xmlFile); - var expr = xPath.compile("/root/SmartDocument/Fields/NachrichtenText/text()"); + Optional<Text> getFieldTemplateText(Document document) { + return evaluateXPath(document, FIELD_TEMPLATE_EXPRESSION); + } - return Optional.ofNullable((Text) expr.evaluate(document, XPathConstants.NODE)) - .map(Text::getTextContent); + Optional<Text> evaluateXPath(Document document, String xpathExpression) { + try { + var expr = XPathFactory.newInstance().newXPath().compile(xpathExpression); + return Optional.ofNullable(expr.evaluate(document, XPathConstants.NODE)).map(Text.class::cast); + } catch (ClassCastException e) { + LOG.warn("Error on extraction Nachricht-Text. XPath return unexpected Type.", e); + } catch (XPathExpressionException e) { + LOG.warn("Cannot evaluate XPath: {}", xpathExpression, e); + } + return Optional.empty(); } Optional<File> getXMLFile(SmartDocumentsResponse response) { @@ -178,9 +199,9 @@ class SmartDocumentsBescheidRemoteService implements BescheidRemoteService { private SmartDocumentsRequest.SmartDocument buildSDSection(Vorgang vorgang) { return SmartDocumentsRequest.SmartDocument.builder().selection(Selection.builder() - .templateGroup(properties.getTemplateGroup()) - .template(vorgang.getVorgangName()) - .build()) + .templateGroup(properties.getTemplateGroup()) + .template(vorgang.getVorgangName()) + .build()) .build(); } diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java index 09b366285262b009acc54f131611760025ef1c5d..98689c96ea25c66ffcfbe9cab54556b148b7fc65 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java +++ b/bescheid-manager/src/main/java/de/ozgcloud/bescheid/vorgang/VorgangService.java @@ -1,46 +1,18 @@ package de.ozgcloud.bescheid.vorgang; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Service; -import de.ozgcloud.apilib.common.command.OzgCloudCommand; -import de.ozgcloud.apilib.common.command.OzgCloudCommandService; -import de.ozgcloud.apilib.common.command.grpc.CommandMapper; import lombok.NonNull; +import lombok.RequiredArgsConstructor; @Service("bescheid_VorgangService") +@RequiredArgsConstructor public class VorgangService { - static final String VORGANG_BESCHEIDEN = "VORGANG_BESCHEIDEN"; - - @Autowired - private VorgangRemoteService remoteService; - @Autowired - @Qualifier("bescheid_OzgCloudCommandService") - private OzgCloudCommandService commandService; - @Autowired - private CommandMapper commandMapper; + private final VorgangRemoteService remoteService; public Vorgang getById(@NonNull VorgangId id) { return remoteService.getById(id); } - public void bescheiden(String vorgangId) { - bescheiden(getById(VorgangId.from(vorgangId))); - } - - private void bescheiden(Vorgang vorgang) { - commandService.createAndWaitUntilDone(buildBescheidenCommand(vorgang)); - } - - OzgCloudCommand buildBescheidenCommand(Vorgang vorgang) { - return OzgCloudCommand.builder() - .vorgangId(commandMapper.toOzgCloudVorgangId(vorgang.getId().toString())) - .relationId(commandMapper.mapRelationId(vorgang.getId().toString())) - .relationVersion(vorgang.getVersion()) - .order(VORGANG_BESCHEIDEN) - .build(); - } - } diff --git a/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh b/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh index a53a9cb966f6dad7b608d517f81938d3d611500a..368804ed6ea51f622c32a2e90a21d5f1dd00763f 100644 --- a/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh +++ b/bescheid-manager/src/main/resources/templates/bescheid.nachrichtTemplate.txt.ftlh @@ -1,6 +1,6 @@ Sehr geehrte/r Antragsteller/in, -im folgenden erhalten Sie Ihren Bescheid. +im Folgenden erhalten Sie Ihren Bescheid. Mit freundlichen Grüßen diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java index 96c5ef5dafcba70a2f79b18ae9d7aa8b24bf8670..a4c77b54a873cf167eb1ce027467114391322744 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.apilib.common.command.OzgCloudCommandService; @@ -19,16 +20,19 @@ import de.ozgcloud.bescheid.common.user.UserProfile; import de.ozgcloud.bescheid.common.user.UserProfileService; import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandCreatedEventTestFactory; +import de.ozgcloud.command.CommandExecutedEvent; import de.ozgcloud.command.CommandTestFactory; import de.ozgcloud.common.test.ITCase; @ITCase class BescheidEventListenerITCase { + private static final Command COMMAND_WITH_OTHER_ORDER = CommandTestFactory.createBuilder().order("OTHER").build(); + @Autowired private ApplicationEventPublisher publisher; - @Autowired - private BescheidFeatureProperties bescheidFeatureProperties; + @SpyBean + private BescheidEventListener listener; @MockBean private BescheidService service; @@ -56,21 +60,16 @@ class BescheidEventListenerITCase { private final Command command = CommandTestFactory.createBuilder().order(BescheidEventListener.CREATE_BESCHEID_ORDER).build(); - @BeforeEach - void init() { - bescheidFeatureProperties.setKielHackathonRoute(true); - } - @Test void shouldCallService() { publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(command)); - verify(service).createBescheid(any()); + verify(attachedItemService).createBescheidDraft(command); } @Test void shouldNotReactOnOtherOrder() { - publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(CommandTestFactory.createBuilder().order("OTHER").build())); + publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(COMMAND_WITH_OTHER_ORDER)); verifyNoInteractions(service); } @@ -90,7 +89,7 @@ class BescheidEventListenerITCase { @Test void shouldNotReactOnOtherOrder() { - publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(CommandTestFactory.createBuilder().order("OTHER").build())); + publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(COMMAND_WITH_OTHER_ORDER)); verifyNoInteractions(attachedItemService); } @@ -110,9 +109,29 @@ class BescheidEventListenerITCase { @Test void shouldNotReactOnOtherOrder() { - publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(CommandTestFactory.createBuilder().order("OTHER").build())); + publisher.publishEvent(CommandCreatedEventTestFactory.withCommand(COMMAND_WITH_OTHER_ORDER)); verifyNoInteractions(attachedItemService); } } + + @Nested + class TestBescheidSent { + + private static final Command COMMAND = CommandTestFactory.createBuilder().order(BescheidEventListener.SEND_BESCHEID_ORDER).build(); + + @Test + void shouldCallMethod() { + publisher.publishEvent(new CommandExecutedEvent(COMMAND)); + + verify(listener).setAntragBewilligung(COMMAND); + } + + @Test + void shouldNotReactOnOtherOrder() { + publisher.publishEvent(new CommandExecutedEvent(COMMAND_WITH_OTHER_ORDER)); + + verifyNoInteractions(listener); + } + } } diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java index 823d76cd45304b38173621fa521a6e3cbec6820e..ba29a266aeb99969217d43cee2a15a485b12718a 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerTest.java @@ -32,9 +32,9 @@ import de.ozgcloud.bescheid.common.freemarker.TemplateHandler; import de.ozgcloud.bescheid.common.user.UserProfile; import de.ozgcloud.bescheid.common.user.UserProfileService; import de.ozgcloud.bescheid.common.user.UserProfileTestFactory; -import de.ozgcloud.bescheid.nachricht.NachrichtService; import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandCreatedEventTestFactory; +import de.ozgcloud.command.CommandExecutedEvent; import de.ozgcloud.command.CommandFailedEvent; import de.ozgcloud.command.CommandTestFactory; import de.ozgcloud.common.errorhandling.TechnicalException; @@ -54,12 +54,8 @@ class BescheidEventListenerTest { @Mock private BinaryFileService fileService; @Mock - private NachrichtService nachrichtService; - @Mock private AttachedItemService attachedItemService; @Mock - private BescheidFeatureProperties featureProperties; - @Mock private DocumentService documentService; @Mock @@ -104,77 +100,36 @@ class BescheidEventListenerTest { @Captor private ArgumentCaptor<BescheidCreatedEvent> eventCaptor; - @Nested - class TestKielConfigured { - - @BeforeEach - void init() { - when(featureProperties.isKielHackathonRoute()).thenReturn(true); - } - - @Test - void shouldCallDoCreateBescheidBiz() { - listener.doCreateBescheid(COMMAND); - - verify(listener).doCreateBescheidBiz(COMMAND); - verify(attachedItemService, never()).createBescheidDraft(any()); - } - - @Test - void shouldCallNachrichtService() { - var bescheid = BescheidTestFactory.create(); - doReturn(bescheid).when(listener).doCreateBescheidBiz(any()); - - listener.doCreateBescheid(COMMAND); - - verify(nachrichtService).createNachrichtDraft(bescheid); - } - - @Test - void shouldPublishBescheidCreatedEvent() { - listener.doCreateBescheid(COMMAND); + @Test + void shouldCallCreateBescheidDraft() { + listener.doCreateBescheid(COMMAND); - verify(eventPublisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getSource()).isEqualTo(CommandTestFactory.ID); - } + verify(attachedItemService).createBescheidDraft(COMMAND); } - @Nested - class TestNotKielConfigured { - - @Test - void shouldCallCreateBescheidDraft() { - listener.doCreateBescheid(COMMAND); - - verify(attachedItemService).createBescheidDraft(COMMAND); - verify(listener, never()).doCreateBescheidBiz(any()); - } - - @Test - @DisplayName("should publish BescheidCreatedEvent after creating BescheidDraft") - void shouldPublishBescheidCreatedEventWithCommand() { - var createdResource = "item-id"; - when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource); + @Test + @DisplayName("should publish BescheidCreatedEvent after creating BescheidDraft") + void shouldPublishBescheidCreatedEventWithCommand() { + var createdResource = "item-id"; + when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource); - listener.doCreateBescheid(COMMAND); + listener.doCreateBescheid(COMMAND); - verify(eventPublisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getCommand()).isSameAs(COMMAND); - } + verify(eventPublisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue().getCommand()).isSameAs(COMMAND); + } - @Test - @DisplayName("should publish BescheidCreatedEvent with created resource") - void shouldPublishBescheidCreatedEventWithCreatedResource() { - var createdResource = "item-id"; - when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource); + @Test + @DisplayName("should publish BescheidCreatedEvent with created resource") + void shouldPublishBescheidCreatedEventWithCreatedResource() { + var createdResource = "item-id"; + when(attachedItemService.createBescheidDraft(any())).thenReturn(createdResource); - listener.doCreateBescheid(COMMAND); + listener.doCreateBescheid(COMMAND); - verify(eventPublisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getCreatedResource()).isEqualTo(createdResource); - } + verify(eventPublisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue().getCreatedResource()).isEqualTo(createdResource); } - } @Nested @@ -551,8 +506,8 @@ class BescheidEventListenerTest { void shouldCallSendBescheid() { listener.doSendBescheid(COMMAND); - verify(service).sendBescheidManually(bescheidItem, AttachedItemTestFactory.VERSION); - verify(service, never()).sendBescheidPostfachMail(any(), anyLong()); + verify(service).sendBescheidManually(bescheidItem, COMMAND); + verify(service, never()).sendBescheidPostfachMail(any(), any()); } @Test @@ -585,8 +540,8 @@ class BescheidEventListenerTest { void shouldCallBescheidService() { listener.doSendBescheid(COMMAND); - verify(service).sendBescheidPostfachMail(bescheidItem, AttachedItemTestFactory.VERSION); - verify(service, never()).sendBescheidManually(any(), anyLong()); + verify(service).sendBescheidPostfachMail(bescheidItem, COMMAND); + verify(service, never()).sendBescheidManually(any(), any()); } @Test @@ -599,6 +554,49 @@ class BescheidEventListenerTest { } } + @Nested + class TestOnBescheidSent { + + private static final Command COMMAND = CommandTestFactory.create(); + + @Test + void shouldCallRunWithSecurityContext() { + listener.onBescheidSent(new CommandExecutedEvent(COMMAND)); + + verify(listener).runWithSecurityContext(eq(COMMAND), any()); + } + + @Test + void shouldExecuteSetAntragBewilligung() { + listener.onBescheidSent(new CommandExecutedEvent(COMMAND)); + + verify(listener).setAntragBewilligung(COMMAND); + } + } + + @Nested + class TestSetAntragBewilligung { + + private static final Command COMMAND = CommandTestFactory.create(); + + @Test + void shouldCallGetItem() { + listener.setAntragBewilligung(COMMAND); + + verify(attachedItemService).getItem(CommandTestFactory.RELATION_ID); + } + + @Test + void shouldCallSetAntragBewilligung() { + var item = AttachedItemTestFactory.createBescheid(); + when(attachedItemService.getItem(any())).thenReturn(item); + + listener.setAntragBewilligung(COMMAND); + + verify(service).setAntragBewilligung(item); + } + } + @Nested class TestRunWithSecurityContext { diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java index 6697f5fe8687a7524464e3a2fb01b1f86ff4cd62..c810b402d1b51e1b1cea5017f3fcaf7530c1d690 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidServiceTest.java @@ -1,15 +1,21 @@ package de.ozgcloud.bescheid; import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.InstanceOfAssertFactories.*; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; +import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -26,20 +32,30 @@ import org.mockito.Spy; import org.springframework.boot.info.BuildProperties; import org.springframework.test.util.ReflectionTestUtils; +import de.ozgcloud.apilib.common.command.OzgCloudCommand; +import de.ozgcloud.apilib.common.command.OzgCloudCommandService; +import de.ozgcloud.apilib.common.command.OzgCloudCreateSubCommandsRequest; +import de.ozgcloud.apilib.common.command.grpc.CommandMapper; +import de.ozgcloud.apilib.common.datatypes.GenericId; +import de.ozgcloud.apilib.user.OzgCloudUserId; +import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; import de.ozgcloud.bescheid.attacheditem.AttachedItem; import de.ozgcloud.bescheid.attacheditem.AttachedItemService; import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory; import de.ozgcloud.bescheid.attributes.ClientAttributeService; import de.ozgcloud.bescheid.common.user.UserProfile; import de.ozgcloud.bescheid.common.user.UserProfileService; -import de.ozgcloud.bescheid.nachricht.NachrichtService; +import de.ozgcloud.bescheid.common.user.UserProfileTestFactory; +import de.ozgcloud.bescheid.vorgang.PostfachAddressTestFactory; import de.ozgcloud.bescheid.vorgang.ServiceKontoTestFactory; import de.ozgcloud.bescheid.vorgang.Vorgang; import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.bescheid.vorgang.VorgangService; import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; +import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandTestFactory; import de.ozgcloud.common.binaryfile.FileId; +import de.ozgcloud.common.datatype.StringBasedValue; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.document.DocumentTestFactory; @@ -52,19 +68,19 @@ class BescheidServiceTest { @Mock private VorgangService vorgangService; @Mock - private BescheidRemoteService remoteService; - @Mock private AttachedItemService attachedItemService; @Mock - private NachrichtService nachrichtService; - @Mock private UserProfileService userProfileService; @Mock - private UserProfile callContextUser; + private OzgCloudCommandService commandService; + @Mock + private CommandMapper commandMapper; @Mock private ClientAttributeService clientAttributeService; @Mock private BuildProperties buildProperties; + @Mock + private BescheidRemoteService remoteService; @BeforeEach void init() { @@ -110,11 +126,23 @@ class BescheidServiceTest { @Nested class TestSendBescheidManually { + private static final Vorgang VORGANG = VorgangTestFactory.create(); + + @Mock + private OzgCloudCommand subCommand; + + @Captor + private ArgumentCaptor<VorgangId> vorgangIdCaptor; + private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid(); @BeforeEach void init() { doNothing().when(service).validateBescheidSendManually(any(), anyLong()); + doNothing().when(service).addSubCommands(any(), any()); + doReturn(UserProfileTestFactory.ID_STR).when(service).getUserId(); + doReturn(List.of(subCommand)).when(service).buildSetBescheidSentSubCommands(any(), any(), any()); + when(vorgangService.getById(any())).thenReturn(VORGANG); } @Test @@ -125,14 +153,37 @@ class BescheidServiceTest { } @Test - void shouldCallSendBescheid() { + void shouldCallGetVorgang() { sendBescheid(); - verify(service).setBescheidSent(bescheidItem); + verify(vorgangService).getById(vorgangIdCaptor.capture()); + assertThat(vorgangIdCaptor.getValue()).hasToString(CommandTestFactory.VORGANG_ID); + } + + @Test + void shouldCallGetUserId() { + sendBescheid(); + + verify(service).getUserId(); + } + + @Test + void shouldCallBuildSetBescheidSentSubCommands() { + sendBescheid(); + + verify(service).buildSetBescheidSentSubCommands(bescheidItem, VORGANG, UserProfileTestFactory.ID_STR); + } + + @Test + void shouldCallAddSubCommands() { + sendBescheid(); + + verify(service).addSubCommands(CommandTestFactory.ID, List.of(subCommand)); } private void sendBescheid() { - service.sendBescheidManually(bescheidItem, AttachedItemTestFactory.VERSION); + var parentCommand = CommandTestFactory.createBuilder().relationVersion(AttachedItemTestFactory.VERSION).build(); + service.sendBescheidManually(bescheidItem, parentCommand); } } @@ -171,38 +222,42 @@ class BescheidServiceTest { @Nested class TestSendBescheidPostfachMail { - private AttachedItem bescheidItem; + private static final Command PARENT_COMMAND = CommandTestFactory.createBuilder().relationVersion(AttachedItemTestFactory.VERSION).build(); + private static final Vorgang.ServiceKonto SERVICE_KONTO = ServiceKontoTestFactory.create(); + private static final Vorgang VORGANG = VorgangTestFactory.createBuilder().serviceKonto(SERVICE_KONTO).build(); + + private static final AttachedItem BESCHEID_ITEM = AttachedItemTestFactory.createBescheid(); + @Mock - private AttachedItem bescheidSendItem; + private OzgCloudCommand setBescheidSentSubCommand; + @Mock + private OzgCloudCommand sendPostfachNachrichtSubCommand; @Captor private ArgumentCaptor<VorgangId> vorgangIdCaptor; + @Captor + private ArgumentCaptor<List<OzgCloudCommand>> subCommandsCaptor; @BeforeEach void init() { - var item = AttachedItemTestFactory.createBescheidItem(); - item.put(Bescheid.FIELD_SEND_BY, Bescheid.SendBy.NACHRICHT.name()); - bescheidItem = AttachedItemTestFactory.createBescheidBuilder().item(item).build(); - doNothing().when(service).validateBescheidSendPostfach(any(), anyLong()); - lenient().when(callContextUser.getId()).thenReturn(UserId.from("user-id")); - lenient().when(userProfileService.getUserProfile()).thenReturn(callContextUser); + doNothing().when(service).addSubCommands(any(), any()); + doReturn(UserProfileTestFactory.ID_STR).when(service).getUserId(); + var subCommands = new ArrayList<OzgCloudCommand>(); + subCommands.add(setBescheidSentSubCommand); + doReturn(subCommands).when(service).buildSetBescheidSentSubCommands(any(), any(), any()); + doReturn(sendPostfachNachrichtSubCommand).when(service).buildSendPostfachNachrichtCommand(any(), any(), any()); + when(vorgangService.getById(any())).thenReturn(VORGANG); } @Test void shouldCallValidateBescheid() { - doReturn(BescheidTestFactory.create()).when(service).buildBescheid(any(), any()); - when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); - sendBescheid(); - verify(service).validateBescheidSendPostfach(bescheidItem, AttachedItemTestFactory.VERSION); + verify(service).validateBescheidSendPostfach(BESCHEID_ITEM, AttachedItemTestFactory.VERSION); } @Test void shouldCallVorgangService() { - doReturn(BescheidTestFactory.create()).when(service).buildBescheid(any(), any()); - when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); - sendBescheid(); verify(vorgangService).getById(vorgangIdCaptor.capture()); @@ -210,40 +265,36 @@ class BescheidServiceTest { } @Test - void shouldCallBuildBescheid() { - doReturn(BescheidTestFactory.create()).when(service).buildBescheid(any(), any()); - var serviceKonto = ServiceKontoTestFactory.create(); - var vorgang = VorgangTestFactory.createBuilder().serviceKonto(serviceKonto).build(); - doReturn(vorgang).when(vorgangService).getById(any()); - + void shouldCallGetUserId() { sendBescheid(); - verify(service).buildBescheid(bescheidItem, serviceKonto); + verify(service).getUserId(); } @Test - void shouldCallNachrichtService() { - var bescheid = BescheidTestFactory.create(); - doReturn(bescheid).when(service).buildBescheid(any(), any()); - when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); - + void shouldCallBuildSetBescheidSentSubCommands() { sendBescheid(); - verify(nachrichtService).sendNachricht(bescheid); + verify(service).buildSetBescheidSentSubCommands(BESCHEID_ITEM, VORGANG, UserProfileTestFactory.ID_STR); } @Test - void shouldCallSendBescheid() { - doReturn(BescheidTestFactory.create()).when(service).buildBescheid(any(), any()); - when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); + void shouldCallBuildSendPostfachNachrichtCommand() { + sendBescheid(); + + verify(service).buildSendPostfachNachrichtCommand(BESCHEID_ITEM, SERVICE_KONTO, UserProfileTestFactory.ID_STR); + } + @Test + void shouldCallAddSubCommands() { sendBescheid(); - verify(service).setBescheidSent(bescheidItem); + verify(service).addSubCommands(eq(CommandTestFactory.ID), subCommandsCaptor.capture()); + assertThat(subCommandsCaptor.getValue()).containsExactly(setBescheidSentSubCommand, sendPostfachNachrichtSubCommand); } private void sendBescheid() { - service.sendBescheidPostfachMail(bescheidItem, AttachedItemTestFactory.VERSION); + service.sendBescheidPostfachMail(BESCHEID_ITEM, PARENT_COMMAND); } } @@ -352,114 +403,6 @@ class BescheidServiceTest { } } - @Nested - class TestBuildBescheid { - - private static final AttachedItem BESCHEID_ITEM = AttachedItemTestFactory.createBescheid(); - private static final Vorgang.ServiceKonto SERVICE_KONTO = ServiceKontoTestFactory.create(); - private static final String CREATED_BY = "user-id"; - - @BeforeEach - void init() { - when(callContextUser.getId()).thenReturn(UserId.from(CREATED_BY)); - when(userProfileService.getUserProfile()).thenReturn(callContextUser); - doReturn(FileId.from(DocumentTestFactory.DOCUMENT_FILE)).when(service).getBescheidFileId(any()); - } - - @Test - void shouldSetVorgangId() { - var result = buildBescheid(); - - assertThat(result.getVorgangId()).hasToString(CommandTestFactory.VORGANG_ID); - } - - @Test - void shouldCallGetBewilligt() { - buildBescheid(); - - verify(service).getBewilligt(BESCHEID_ITEM); - } - - @Test - void shouldSetGenehmigt() { - doReturn(true).when(service).getBewilligt(any()); - - var result = buildBescheid(); - - assertThat(result.isGenehmigt()).isTrue(); - } - - @Test - void shouldCallGetAttachments() { - buildBescheid(); - - verify(service).getAttachments(BESCHEID_ITEM); - } - - @Test - void shouldSetAttachments() { - var expectedAttachments = List.of(FileId.from(AttachedItemTestFactory.ATTACHMENT)); - doReturn(expectedAttachments).when(service).getAttachments(any()); - - var result = buildBescheid(); - - assertThat(result.getAttachments()).containsExactlyElementsOf(expectedAttachments); - } - - @Test - void shouldCallGetBescheidFileId() { - buildBescheid(); - - verify(service).getBescheidFileId(BESCHEID_ITEM); - } - - @Test - void shouldSetBescheidFileId() { - var result = buildBescheid(); - - assertThat(result.getBescheidFileId()).hasToString(DocumentTestFactory.DOCUMENT_FILE); - } - - @Test - void shouldSetNachrichtSubject() { - var result = buildBescheid(); - - assertThat(result.getNachrichtSubject()).contains(AttachedItemTestFactory.NACHRICHT_SUBJECT); - } - - @Test - void shouldSetNachrichtText() { - var result = buildBescheid(); - - assertThat(result.getNachrichtText()).contains(AttachedItemTestFactory.NACHRICHT_TEXT); - } - - @Test - void shouldCallCurrentUserService() { - buildBescheid(); - - verify(userProfileService).getUserProfile(); - } - - @Test - void shouldSetCreatedBy() { - var result = buildBescheid(); - - assertThat(result.getCreatedBy()).hasToString(CREATED_BY); - } - - @Test - void shouldSetServiceKonto() { - var result = buildBescheid(); - - assertThat(result.getServiceKonto()).usingRecursiveComparison().isEqualTo(SERVICE_KONTO); - } - - private Bescheid buildBescheid() { - return service.buildBescheid(BESCHEID_ITEM, SERVICE_KONTO); - } - } - @Nested class TestGetAttachments { @@ -540,6 +483,33 @@ class BescheidServiceTest { } } + @Nested + class TestAddSubCommands { + + @Mock + private OzgCloudCommand subCommand; + @Mock + private OzgCloudCreateSubCommandsRequest createSubCommandsRequest; + + @Test + void shouldCallBuildCreateSubCommandsRequest() { + var subCommands = List.of(subCommand); + + service.addSubCommands(CommandTestFactory.ID, subCommands); + + verify(service).buildCreateSubCommandsRequest(CommandTestFactory.ID, subCommands); + } + + @Test + void shouldCallCommandService() { + doReturn(createSubCommandsRequest).when(service).buildCreateSubCommandsRequest(any(), any()); + + service.addSubCommands(CommandTestFactory.ID, List.of(subCommand)); + + verify(commandService).addSubCommands(createSubCommandsRequest); + } + } + @Nested class TestGetBescheidFileId { @@ -573,7 +543,7 @@ class BescheidServiceTest { var result = service.getNachrichtSubject(bescheidItem); - assertThat(result).isEqualTo(AttachedItemTestFactory.NACHRICHT_SUBJECT); + assertThat(result).contains(AttachedItemTestFactory.NACHRICHT_SUBJECT); } } @@ -587,49 +557,537 @@ class BescheidServiceTest { var result = service.getNachrichtText(bescheidItem); - assertThat(result).isEqualTo(AttachedItemTestFactory.NACHRICHT_TEXT); + assertThat(result).contains(AttachedItemTestFactory.NACHRICHT_TEXT); } } @Nested - class TestSendBescheid { + class TestGetUserId { - private final AttachedItem bescheidItem = AttachedItemTestFactory.createBescheid(); @Mock - private AttachedItem bescheidSendItem; + private UserProfile userProfile; + + @BeforeEach + void init() { + when(userProfileService.getUserProfile()).thenReturn(userProfile); + when(userProfile.getId()).thenReturn(UserProfileTestFactory.ID); + + } @Test - void shouldCallSetBescheidStatusSent() { - sendBescheid(); + void shouldCallGetUserProfile() { + service.getUserId(); - verify(attachedItemService).setBescheidSentStatus(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION); + verify(userProfileService).getUserProfile(); } @Test - void shouldCallBescheiden() { - sendBescheid(); + void shouldReturnUserId() { + var result = service.getUserId(); + + assertThat(result).isEqualTo(UserProfileTestFactory.ID_STR); + } + } + + @Nested + class TestBuildSetBescheidSentCommands { + + @Mock + private AttachedItem bescheidItem; + @Mock + private Vorgang vorgang; + @Mock + private OzgCloudCommand vorgangBescheidenCommand; + @Mock + private OzgCloudCommand setBescheidSentStatusCommand; - verify(vorgangService).bescheiden(CommandTestFactory.VORGANG_ID); + @BeforeEach + void inti() { + doReturn(vorgangBescheidenCommand).when(service).buildVorgangBescheidenCommand(any()); + doReturn(setBescheidSentStatusCommand).when(service).buildSetBescheidSentStatusCommand(any(), any()); } @Test - void shouldCallRevertBescheidStatusSent() { - doThrow(new TechnicalException("error")).when(vorgangService).bescheiden(anyString()); + void shouldCallBuildVorgangBescheidenCommand() { + buildSetBescheidSentCommands(); - assertThrows(TechnicalException.class, this::sendBescheid); + verify(service).buildVorgangBescheidenCommand(vorgang); + } - verify(attachedItemService).revertBescheidSendStatus(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION); + @Test + void shouldCallBuildSetBescheidSentStatusCommand() { + buildSetBescheidSentCommands(); + + verify(service).buildSetBescheidSentStatusCommand(bescheidItem, UserProfileTestFactory.ID_STR); } @Test - void shouldCallSetAntragResult() { - sendBescheid(); + void shouldReturnResult() { + var result = buildSetBescheidSentCommands(); - verify(service).setAntragBewilligung(bescheidItem); + assertThat(result).containsExactly(vorgangBescheidenCommand, setBescheidSentStatusCommand); } - private void sendBescheid() { - service.setBescheidSent(bescheidItem); + private List<OzgCloudCommand> buildSetBescheidSentCommands() { + return service.buildSetBescheidSentSubCommands(bescheidItem, vorgang, UserProfileTestFactory.ID_STR); + } + } + + @Nested + class TestBuildVorgangBescheidenCommand { + + private static final OzgCloudVorgangId VORGANG_ID = OzgCloudVorgangId.from(VorgangTestFactory.ID.toString()); + private static final StringBasedValue RELATION_ID = GenericId.from(VorgangTestFactory.ID.toString()); + + @BeforeEach + void init() { + when(commandMapper.toOzgCloudVorgangId(anyString())).thenReturn(VORGANG_ID); + when(commandMapper.mapRelationId(anyString())).thenReturn(RELATION_ID); + } + + @Test + void shouldCallMapVorgangId() { + buildVorgangBescheidenCommand(); + + verify(commandMapper).toOzgCloudVorgangId(VorgangTestFactory.ID.toString()); + } + + @Test + void shouldSetVorgangId() { + var result = buildVorgangBescheidenCommand(); + + assertThat(result.getVorgangId()).isEqualTo(VORGANG_ID); + } + + @Test + void shouldCallMapRelationId() { + buildVorgangBescheidenCommand(); + + verify(commandMapper).mapRelationId(VorgangTestFactory.ID.toString()); + } + + @Test + void shouldSetRelationId() { + var result = buildVorgangBescheidenCommand(); + + assertThat(result.getRelationId()).isEqualTo(RELATION_ID); + } + + @Test + void shouldSetRelationVersion() { + var result = buildVorgangBescheidenCommand(); + + assertThat(result.getRelationVersion()).isEqualTo(VorgangTestFactory.VERSION); + } + + @Test + void shouldSetOrder() { + var result = buildVorgangBescheidenCommand(); + + assertThat(result.getOrder()).isEqualTo(BescheidService.VORGANG_BESCHEIDEN_ORDER); + } + + private OzgCloudCommand buildVorgangBescheidenCommand() { + return service.buildVorgangBescheidenCommand(VorgangTestFactory.create()); + } + } + + @Nested + class TestBuildSetBescheidSentStatusCommand { + + private static final StringBasedValue RELATION_ID = GenericId.from(VorgangTestFactory.ID.toString()); + private static final AttachedItem BESCHEID_ITEM = AttachedItemTestFactory.createBescheid(); + + @BeforeEach + void init() { + when(commandMapper.mapRelationId(any())).thenReturn(RELATION_ID); + } + + @Test + void shouldCallMapRelationId() { + buildSetBescheidSentStatusCommand(); + + verify(commandMapper).mapRelationId(AttachedItemTestFactory.ID); + } + + @Test + void shouldSetRelationId() { + var result = buildSetBescheidSentStatusCommand(); + + assertThat(result.getRelationId()).isEqualTo(RELATION_ID); + } + + @Test + void shouldSetRelationVersion() { + var result = buildSetBescheidSentStatusCommand(); + + assertThat(result.getRelationVersion()).isEqualTo(AttachedItemTestFactory.VERSION); + } + + @Test + void shouldSetOrder() { + var result = buildSetBescheidSentStatusCommand(); + + assertThat(result.getOrder()).isEqualTo(AttachedItemService.PATCH_ATTACHED_ITEM); + } + + @Test + void shouldSetPropertyId() { + var result = buildSetBescheidSentStatusCommand(); + + assertThat(result.getBodyObject()).containsEntry(AttachedItem.PROPERTY_ID, AttachedItemTestFactory.ID); + } + + @Test + void shouldCallBuildBescheidSentStatusItem() { + buildSetBescheidSentStatusCommand(); + + verify(service).buildBescheidSentStatusItem(UserProfileTestFactory.ID_STR); + } + + @Test + void shouldSetPropertyItem() { + var itemMap = Map.of("key", "value"); + doReturn(itemMap).when(service).buildBescheidSentStatusItem(any()); + + var result = buildSetBescheidSentStatusCommand(); + + assertThat(result.getBodyObject()).containsEntry(AttachedItem.PROPERTY_ITEM, itemMap); + } + + private OzgCloudCommand buildSetBescheidSentStatusCommand() { + return service.buildSetBescheidSentStatusCommand(BESCHEID_ITEM, UserProfileTestFactory.ID_STR); + } + } + + @Nested + class TestBuildBescheidSentStatusItem { + + @Test + void shouldSetStatus() { + var result = service.buildBescheidSentStatusItem(UserProfileTestFactory.ID_STR); + + assertThat(result).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SENT.name()); + } + + @Test + void shouldCallBuildSentInfoMap() { + service.buildBescheidSentStatusItem(UserProfileTestFactory.ID_STR); + + verify(service).buildSentInfoMap(UserProfileTestFactory.ID_STR); + } + + @Test + void shouldSetSentInfo() { + var sentInfoMap = Map.of("key", "value"); + doReturn(sentInfoMap).when(service).buildSentInfoMap(any()); + + var result = service.buildBescheidSentStatusItem(UserProfileTestFactory.ID_STR); + + assertThat(result).containsEntry(Bescheid.FIELD_SENT_INFO, sentInfoMap); + } + } + + @Nested + class TestBuildSentInfoMap { + + @Test + void shouldSetSentAt() { + var result = service.buildSentInfoMap(UserProfileTestFactory.ID_STR); + + assertThat(getSentAt(result)).isCloseTo(ZonedDateTime.now(), within(1, ChronoUnit.SECONDS)); + } + + @Test + void shouldSetSentBy() { + var result = service.buildSentInfoMap(UserProfileTestFactory.ID_STR); + + assertThat(result).containsEntry(Bescheid.FIELD_SENT_BY, UserProfileTestFactory.ID_STR); + } + + private ZonedDateTime getSentAt(Map<String, Object> sentInfoMap) { + return ZonedDateTime.parse((String) sentInfoMap.get(Bescheid.FIELD_SENT_AT), DateTimeFormatter.ISO_DATE_TIME); + } + } + + @Nested + class TestBuildSendPostfachNachrichtCommand { + + private static final AttachedItem BESCHEID_ITEM = AttachedItemTestFactory.createBescheid(); + private static final Vorgang.PostfachAddress POSTFACH_ADDRESS = PostfachAddressTestFactory.create(); + private static final Vorgang.ServiceKonto SERVICE_KONTO = ServiceKontoTestFactory.createBuilder().clearPostfachAddresses() + .postfachAddress(POSTFACH_ADDRESS).build(); + private static final OzgCloudUserId OZG_CLOUD_ID = OzgCloudUserId.from(UserProfileTestFactory.ID_STR); + private static final Map<String, Object> OBJECT_MAP = Map.of("key", "value"); + private static final OzgCloudVorgangId VORGANG_ID = OzgCloudVorgangId.from(VorgangTestFactory.ID.toString()); + private static final StringBasedValue RELATION_ID = GenericId.from(VorgangTestFactory.ID.toString()); + + @BeforeEach + void init() { + when(commandMapper.toOzgCloudVorgangId(anyString())).thenReturn(VORGANG_ID); + when(commandMapper.mapRelationId(anyString())).thenReturn(RELATION_ID); + when(commandMapper.toOzgCloudUserId(anyString())).thenReturn(OZG_CLOUD_ID); + doReturn(OBJECT_MAP).when(service).buildSendNachrichtCommandBody(any(), any()); + } + + @Test + void shouldCallBuildPostfachAddress() { + buildSendPostfachNachrichtCommand(); + + verify(service).buildPostfachAddress(POSTFACH_ADDRESS, ServiceKontoTestFactory.TYPE); + } + + @Test + void shouldCallBuildSenNachrichtCommandBody() { + var postfachAddressMap = Map.<String, Object>of("key", "value"); + doReturn(postfachAddressMap).when(service).buildPostfachAddress(any(), any()); + + buildSendPostfachNachrichtCommand(); + + verify(service).buildSendNachrichtCommandBody(BESCHEID_ITEM, postfachAddressMap); + } + + @Test + void shouldSetBodyObject() { + var result = buildSendPostfachNachrichtCommand(); + + assertThat(result.getBodyObject()).isSameAs(OBJECT_MAP); + } + + @Test + void shouldCallMapVorgangId() { + buildSendPostfachNachrichtCommand(); + + verify(commandMapper).toOzgCloudVorgangId(CommandTestFactory.VORGANG_ID); + } + + @Test + void shouldSetVorgangId() { + var result = buildSendPostfachNachrichtCommand(); + + assertThat(result.getVorgangId()).isEqualTo(VORGANG_ID); + } + + @Test + void shouldCallMapRelationId() { + buildSendPostfachNachrichtCommand(); + + verify(commandMapper).mapRelationId(CommandTestFactory.VORGANG_ID); + } + + @Test + void shouldSetRelationId() { + var result = buildSendPostfachNachrichtCommand(); + + assertThat(result.getRelationId()).isEqualTo(RELATION_ID); + } + + @Test + void shouldSetOrder() { + var result = buildSendPostfachNachrichtCommand(); + + assertThat(result.getOrder()).isEqualTo(BescheidService.SEND_POSTFACH_NACHRICHT_ORDER); + } + + @Test + void shouldCallMapToOzgCloudUserId() { + buildSendPostfachNachrichtCommand(); + + verify(commandMapper).toOzgCloudUserId(UserProfileTestFactory.ID_STR); + } + + @Test + void shouldSetCreatedBy() { + var result = buildSendPostfachNachrichtCommand(); + + assertThat(result.getCreatedBy()).isEqualTo(OZG_CLOUD_ID); + } + + private OzgCloudCommand buildSendPostfachNachrichtCommand() { + return service.buildSendPostfachNachrichtCommand(BESCHEID_ITEM, SERVICE_KONTO, UserProfileTestFactory.ID_STR); + } + } + + @Nested + class TestBuildPostfachAddress { + + @Test + void shouldSetType() { + var result = buildPostfachAddress(); + + assertThat(result).containsEntry(Vorgang.PostfachAddress.FIELD_TYPE, PostfachAddressTestFactory.TYPE); + } + + @Test + void shouldSetVersion() { + var result = buildPostfachAddress(); + + assertThat(result).containsEntry(Vorgang.PostfachAddress.FIELD_VERSION, PostfachAddressTestFactory.VERSION); + } + + @Test + void shouldSetIdentifier() { + var result = buildPostfachAddress(); + + assertThat(result).extracting(Vorgang.PostfachAddress.FIELD_IDENTIFIER, MAP) + .containsEntry(BescheidService.FIELD_POSTFACH_ID, PostfachAddressTestFactory.POSTFACH_ID); + } + + @Test + void shouldSetServicekontoType() { + var result = buildPostfachAddress(); + + assertThat(result).containsEntry(Vorgang.ServiceKonto.FIELD_SERVICEKONTO_TYPE, ServiceKontoTestFactory.TYPE); + } + + private Map<String, Object> buildPostfachAddress() { + return service.buildPostfachAddress(PostfachAddressTestFactory.create(), ServiceKontoTestFactory.TYPE); + } + } + + @Nested + class TestBuildSendNachrichtCommandBody { + + private static final Map<String, Object> SERVICE_KONTO_MAP = Map.of("key", "value"); + + @Mock + private AttachedItem bescheidItem; + + private Map<String, Object> buildSendNachrichtCommandBody() { + return service.buildSendNachrichtCommandBody(bescheidItem, SERVICE_KONTO_MAP); + } + + @Nested + class TestWithValuesFromBescheidItem { + + @BeforeEach + void init() { + doReturn(Optional.of(AttachedItemTestFactory.NACHRICHT_SUBJECT)).when(service).getNachrichtSubject(any()); + doReturn(Optional.of(AttachedItemTestFactory.NACHRICHT_TEXT)).when(service).getNachrichtText(any()); + doReturn(List.of(AttachedItemTestFactory.ATTACHMENT)).when(service).buildAttachments(any()); + } + + @Test + void shouldSetReplyOption() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(BescheidService.FIELD_REPLY_OPTION, BescheidService.REPLY_OPTION); + } + + @Test + void shouldCallGetNachrichtSubject() { + buildSendNachrichtCommandBody(); + + verify(service).getNachrichtSubject(bescheidItem); + } + + @Test + void shouldSetSubject() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(BescheidService.FIELD_SUBJECT, AttachedItemTestFactory.NACHRICHT_SUBJECT); + } + + @Test + void shouldCallGetNachrichtText() { + buildSendNachrichtCommandBody(); + + verify(service).getNachrichtText(bescheidItem); + } + + @Test + void shouldSetMailBody() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(BescheidService.FIELD_MAIL_BODY, AttachedItemTestFactory.NACHRICHT_TEXT); + } + + @Test + void shouldCallBuildAttachments() { + buildSendNachrichtCommandBody(); + + verify(service).buildAttachments(bescheidItem); + } + + @Test + void shouldSetAttachments() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).extracting(BescheidService.FIELD_ATTACHMENTS, LIST).containsExactly(AttachedItemTestFactory.ATTACHMENT); + } + + @Test + void shouldSetPostfachAddress() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, SERVICE_KONTO_MAP); + } + } + + @Nested + class TestWithDefaultValues { + + @BeforeEach + void init() { + doReturn(Optional.empty()).when(service).getNachrichtSubject(any()); + doReturn(Optional.empty()).when(service).getNachrichtText(any()); + doReturn(Collections.emptyList()).when(service).buildAttachments(any()); + } + + @Test + void shouldSetSubject() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(BescheidService.FIELD_SUBJECT, BescheidService.SUBJECT); + } + + @Test + void shouldSetMailBody() { + var result = buildSendNachrichtCommandBody(); + + assertThat(result).containsEntry(BescheidService.FIELD_MAIL_BODY, StringUtils.EMPTY); + } + } + } + + @Nested + class TestBuildAttachments { + + private static final String BESCHEID_FILE_ID = "bescheid-file-id"; + private static final String ATTACHMENT_FILE_ID = "attachment-file-id"; + + @Mock + private AttachedItem bescheidItem; + + @BeforeEach + void init() { + doReturn(FileId.from(BESCHEID_FILE_ID)).when(service).getBescheidFileId(any()); + doReturn(List.of(FileId.from(ATTACHMENT_FILE_ID))).when(service).getAttachments(any()); + } + + @Test + void shouldCallGetBescheidFileId() { + buildAttachments(); + + verify(service).getBescheidFileId(bescheidItem); + } + + @Test + void shouldCallGetAttachments() { + buildAttachments(); + + verify(service).getAttachments(bescheidItem); + } + + @Test + void shouldReturnAllAttachments() { + var result = buildAttachments(); + + assertThat(result).containsExactly(BESCHEID_FILE_ID, ATTACHMENT_FILE_ID); + } + + private List<String> buildAttachments() { + return service.buildAttachments(bescheidItem); } } diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestApplication.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestApplication.java index 28c3e88f5c362906b9288319b4062347daeea5f2..9bbaa5728dc689fe6d70fe510af8ae909b65ae7f 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestApplication.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/BescheidTestApplication.java @@ -1,6 +1,8 @@ package de.ozgcloud.bescheid; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.info.BuildProperties; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.security.authentication.AuthenticationTrustResolver; @@ -10,6 +12,9 @@ import org.springframework.security.authentication.AuthenticationTrustResolverIm @ComponentScan({ "de.ozgcloud.*" }) public class BescheidTestApplication { + @MockBean + private BuildProperties buildProperties; + @Bean AuthenticationTrustResolver trustResolver() { return new AuthenticationTrustResolverImpl(); diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java index b70261e5222ae98734dfe4960ac3a20f98bb0ff8..9f4272fed782bba1aef9ea7236c4430752002e88 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/attacheditem/AttachedItemServiceTest.java @@ -28,15 +28,12 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -61,8 +58,6 @@ import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; import de.ozgcloud.bescheid.Bescheid; import de.ozgcloud.bescheid.Bescheid.SendBy; import de.ozgcloud.bescheid.BescheidCallContextAttachingInterceptor; -import de.ozgcloud.bescheid.common.callcontext.CallContextUser; -import de.ozgcloud.bescheid.common.callcontext.CurrentUserService; import de.ozgcloud.bescheid.common.freemarker.TemplateHandler; import de.ozgcloud.bescheid.vorgang.VorgangId; import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; @@ -86,8 +81,6 @@ class AttachedItemServiceTest { @Mock private VorgangAttachedItemRemoteService remoteService; @Mock - private CurrentUserService currentUserService; - @Mock private TemplateHandler templateHandler; @Nested @@ -451,12 +444,12 @@ class AttachedItemServiceTest { doUpdateBescheidDraft(); - verify(service).buildUpdateAttachedItemCommand(bescheidItem, expectedBodyObject); + verify(service).buildPatchAttachedItemCommand(bescheidItem, expectedBodyObject); } @Test void shouldCallCommandService() { - doReturn(updateAttachedItemCommand).when(service).buildUpdateAttachedItemCommand(any(), any()); + doReturn(updateAttachedItemCommand).when(service).buildPatchAttachedItemCommand(any(), any()); doUpdateBescheidDraft(); @@ -672,12 +665,12 @@ class AttachedItemServiceTest { service.overrideAttachedItem(bescheidItem, command); - verify(service).buildUpdateAttachedItemCommand(bescheidItem, bodyObject); + verify(service).buildPatchAttachedItemCommand(bescheidItem, bodyObject); } @Test void shouldCallCommandService() { - doReturn(updateItemCommand).when(service).buildUpdateAttachedItemCommand(any(), any()); + doReturn(updateItemCommand).when(service).buildPatchAttachedItemCommand(any(), any()); service.overrideAttachedItem(AttachedItemTestFactory.createBescheid(), command); @@ -853,7 +846,7 @@ class AttachedItemServiceTest { @Test void shouldCallVorgangIdMapper() { - service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); verify(commandMapper).toOzgCloudVorgangId(CommandTestFactory.VORGANG_ID); } @@ -863,14 +856,14 @@ class AttachedItemServiceTest { var expectedVorgangId = OzgCloudVorgangId.from(CommandTestFactory.VORGANG_ID); when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedVorgangId); - var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); assertThat(result.getVorgangId()).isEqualTo(expectedVorgangId); } @Test void shouldCallRelationIdMapper() { - service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); verify(commandMapper).mapRelationId(AttachedItemTestFactory.ID); } @@ -880,30 +873,30 @@ class AttachedItemServiceTest { var expectedId = GenericId.from("relationId"); when(commandMapper.mapRelationId(any())).thenReturn(expectedId); - var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); assertThat(result.getRelationId()).isEqualTo(expectedId); } @Test void shouldSetRelationVersion() { - var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); assertThat(result.getRelationVersion()).isEqualTo(AttachedItemTestFactory.VERSION); } @Test void shouldSetOrder() { - var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); + var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), Map.of()); - assertThat(result.getOrder()).isEqualTo(AttachedItemService.UPDATE_ATTACHED_ITEM_ORDER); + assertThat(result.getOrder()).isEqualTo(AttachedItemService.PATCH_ATTACHED_ITEM); } @Test void shouldSetBodyObject() { var bodyObject = Map.<String, Object>of("key", "value"); - var result = service.buildUpdateAttachedItemCommand(AttachedItemTestFactory.createBescheid(), bodyObject); + var result = service.buildPatchAttachedItemCommand(AttachedItemTestFactory.createBescheid(), bodyObject); assertThat(result.getBodyObject()).containsAllEntriesOf(bodyObject); } @@ -1039,257 +1032,6 @@ class AttachedItemServiceTest { } } - @Nested - class TestSetBescheidSentStatus { - - private static final Map<String, Object> BODY_OBJECT_MAP = Map.of("key", "value"); - - @Mock - private OzgCloudCommand command; - - @BeforeEach - void init() { - doReturn(BODY_OBJECT_MAP).when(service).buildBescheidSentStatusBodyObject(any()); - } - - @Test - void shouldCallBuildPatchBescheidCommand() { - setBescheidStatusSent(); - - verify(service).buildPatchBescheidCommand(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION, BODY_OBJECT_MAP); - } - - @Test - void shouldCallCommandService() { - doReturn(command).when(service).buildPatchBescheidCommand(any(), anyLong(), any()); - - setBescheidStatusSent(); - - verify(commandService).createAndWaitUntilDone(command); - } - - private void setBescheidStatusSent() { - service.setBescheidSentStatus(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION); - } - } - - @Nested - class TestBuildBescheidSentStatusBodyObject { - - @Test - void shouldSetId() { - var result = service.buildBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - assertThat(result).containsEntry(AttachedItem.PROPERTY_ID, AttachedItemTestFactory.ID); - } - - @Test - void shouldCallBuildBescheidSentStatusItem() { - service.buildBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - verify(service).buildBescheidSentStatusItem(); - } - - @Test - void shouldSetBescheidBodyObject() { - var expectedItemMap = Map.of("key", "value"); - doReturn(expectedItemMap).when(service).buildBescheidSentStatusItem(); - - var result = service.buildBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - assertThat(result).containsEntry(AttachedItem.PROPERTY_ITEM, expectedItemMap); - } - - } - - @Nested - class TestBuildBescheidSentStatusItem { - - @Test - void shouldSetStatus() { - var result = service.buildBescheidSentStatusItem(); - - assertThat(result).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SENT.name()); - } - } - - @Nested - class TestBuildSentInfoMap { - - @Test - void shouldSetSentAt() { - var result = service.buildSentInfoMap(); - - assertThat(getSentAt(result)).isCloseTo(ZonedDateTime.now(), within(2, ChronoUnit.SECONDS)); - } - - @Test - void shouldCallGetUser() { - service.buildSentInfoMap(); - - verify(currentUserService).findUser(); - } - - @Test - void shouldSetSentBy() { - var userId = "user-id"; - doReturn(userId).when(service).getUserId(); - - var result = service.buildSentInfoMap(); - - assertThat(result).containsEntry(Bescheid.FIELD_SENT_BY, userId); - } - - private ZonedDateTime getSentAt(Map<String, Object> sentInfo) { - return ZonedDateTime.parse(MapUtils.getString(sentInfo, Bescheid.FIELD_SENT_AT)); - } - } - - @Nested - class TestGetUserId { - - @Mock - private CallContextUser callContextUser; - - @Test - void shouldCallFindUser() { - service.getUserId(); - - verify(currentUserService).findUser(); - } - - @Test - void shouldCallGetUserIdOnCallContexUser() { - when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser)); - - service.getUserId(); - - verify(callContextUser).getUserId(); - } - - @Test - void shouldReturnUserId() { - var expectedUserId = "user-id"; - when(callContextUser.getUserId()).thenReturn(Optional.of(expectedUserId)); - when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser)); - - var result = service.getUserId(); - - assertThat(result).isEqualTo(expectedUserId); - } - - @Test - void shouldReturnEmptyWhenNoUserFound() { - var result = service.getUserId(); - - assertThat(result).isEmpty(); - } - - @Test - void shouldReturnEmptyWhenNoUserIdFound() { - when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser)); - - var result = service.getUserId(); - - assertThat(result).isEmpty(); - } - } - - @Nested - class TestRevertBescheidStatusSent { - - private static final Map<String, Object> BODY_OBJECT_MAP = Map.of("key", "value"); - - @Mock - private OzgCloudCommand command; - - @Test - void shouldCallBuildRevertBescheidSentStatusBodyObject() { - revertBescheidStatusSent(); - - verify(service).buildRevertBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - } - - @Test - void shouldCallBuildPatchBescheidCommand() { - doReturn(BODY_OBJECT_MAP).when(service).buildRevertBescheidSentStatusBodyObject(any()); - - revertBescheidStatusSent(); - - verify(service).buildPatchBescheidCommand(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION, BODY_OBJECT_MAP); - } - - @Test - void shouldCallCommandService() { - doReturn(command).when(service).buildPatchBescheidCommand(any(), anyLong(), any()); - - revertBescheidStatusSent(); - - verify(commandService).createAndWaitUntilDone(command); - } - - private void revertBescheidStatusSent() { - service.revertBescheidSendStatus(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION); - } - - } - - @Nested - class TestBuildRevertBescheidSentStatusBodyObject { - - @Test - void shouldSetId() { - var result = service.buildRevertBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - assertThat(result).containsEntry(AttachedItem.PROPERTY_ID, AttachedItemTestFactory.ID); - } - - @Test - void shouldCallBuildRevertBescheidSentStatusItem() { - service.buildRevertBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - verify(service).buildRevertBescheidSentStatusItem(); - } - - @Test - void shouldSetBescheidBodyObject() { - var expectedItemMap = Map.of("key", "value"); - doReturn(expectedItemMap).when(service).buildRevertBescheidSentStatusItem(); - - var result = service.buildRevertBescheidSentStatusBodyObject(AttachedItemTestFactory.ID); - - assertThat(result).containsEntry(AttachedItem.PROPERTY_ITEM, expectedItemMap); - } - } - - @Nested - class TestBuildRevertBescheidSentStatusItem { - - @Test - void shouldCallBuildItemMap() { - service.buildRevertBescheidSentStatusItem(); - - verify(service).buildItemMap(Bescheid.Status.DRAFT); - } - - @Test - void shouldReturnResultsOfBuildItemMap() { - var expectedItemMap = new HashMap<String, Object>(Map.of("key", "value")); - doReturn(expectedItemMap).when(service).buildItemMap(any()); - - var result = service.buildRevertBescheidSentStatusItem(); - - assertThat(result).containsEntry("key", "value"); - } - - @Test - void shouldSetSentInfoNull() { - var result = service.buildRevertBescheidSentStatusItem(); - - assertThat(result).containsEntry(Bescheid.FIELD_SENT_INFO, null); - } - } - @Nested class TestBuildItemMap { @@ -1301,54 +1043,6 @@ class AttachedItemServiceTest { } } - @Nested - class TestBuildPatchBescheidCommand { - - private static final Map<String, Object> BODY_OBJECT_MAP = Map.of("key", "value"); - - @Test - void shouldCallRelationIdMapper() { - buildPatchBescheidCommand(); - - verify(commandMapper).mapRelationId(AttachedItemTestFactory.ID); - } - - @Test - void shouldSetRelationId() { - var expectedId = GenericId.from(AttachedItemTestFactory.ID); - when(commandMapper.mapRelationId(any())).thenReturn(expectedId); - - var result = buildPatchBescheidCommand(); - - assertThat(result.getRelationId()).isEqualTo(expectedId); - } - - @Test - void shouldSetRelationVersion() { - var result = buildPatchBescheidCommand(); - - assertThat(result.getRelationVersion()).isEqualTo(AttachedItemTestFactory.VERSION); - } - - @Test - void shouldSetOrder() { - var result = buildPatchBescheidCommand(); - - assertThat(result.getOrder()).isEqualTo(AttachedItemService.PATCH_ATTACHED_ITEM); - } - - @Test - void shouldSetBodyObject() { - var result = buildPatchBescheidCommand(); - - assertThat(result.getBodyObject()).isSameAs(BODY_OBJECT_MAP); - } - - private OzgCloudCommand buildPatchBescheidCommand() { - return service.buildPatchBescheidCommand(AttachedItemTestFactory.ID, AttachedItemTestFactory.VERSION, BODY_OBJECT_MAP); - } - } - @Nested class TestFindAllBescheid { diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java index 61873f64571613f97a8a55b58cb23c7d4751a2f4..071cb7d7ee00b57de1a217ccef7e4522df18a4c9 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/common/freemarker/TemplateHandlerITCase.java @@ -6,7 +6,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.info.BuildProperties; import org.springframework.boot.test.mock.mockito.MockBean; import de.ozgcloud.apilib.common.command.OzgCloudCommandService; @@ -21,8 +20,6 @@ class TemplateHandlerITCase { @Autowired private TemplateHandler handler; @MockBean - private BuildProperties properties; - @MockBean private OzgCloudCommandService commandService; @MockBean private CommandMapper commandMapper; diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/GrpcPostfachNachrichtTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/GrpcPostfachNachrichtTestFactory.java deleted file mode 100644 index c42e9fb32e68ea5a86de3e41e682927c87d77019..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/GrpcPostfachNachrichtTestFactory.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import de.ozgcloud.nachrichten.postfach.GrpcPostfachNachricht; - -public class GrpcPostfachNachrichtTestFactory { - - public static final GrpcPostfachNachricht create() { - return createBuilder().build(); - } - - public static final GrpcPostfachNachricht.Builder createBuilder() { - return GrpcPostfachNachricht.newBuilder() - .setCreatedAt(NachrichtTestFactory.CREATED_AT.toString()) - .setMailBody(NachrichtTestFactory.MAIL_BODY) - .setSubject(NachrichtTestFactory.SUBJECT); - } -} diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtMapperTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtMapperTest.java deleted file mode 100644 index ac7a466e044dcd7e0c5a95b3a0f1f6e1332e1230..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtMapperTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mapstruct.factory.Mappers; -import org.mockito.InjectMocks; - -class NachrichtMapperTest { - - @InjectMocks - private NachrichtMapper mapper = Mappers.getMapper(NachrichtMapper.class);; - - @Nested - class TestMapToGrpc { - @Test - void shouldMapNachricht() { - var mapped = mapper.mapToGrpc(NachrichtTestFactory.create()); - - assertThat(mapped).usingRecursiveComparison() - .ignoringFields("replyOption_", "memoizedHashCode") - .isEqualTo(GrpcPostfachNachrichtTestFactory.create()); - } - - @Test - void shouldIgnoreMissingFileId() { - var mapped = mapper.mapToGrpc(NachrichtTestFactory.createBuilder().bescheidFileId(null).build()); - - assertThat(mapped.getAttachmentList()).isEmpty(); - } - - @Test - void shouldSetReplyOption() { - var mapped = mapper.mapToGrpc(NachrichtTestFactory.create()); - - assertThat(mapped.getReplyOption()).isEqualTo("FORBIDDEN"); - } - - @Test - void shouldNotMapId() { - var mapped = mapper.mapToGrpc(NachrichtTestFactory.create()); - - assertThat(mapped.getId()).isEmpty(); - } - } - -} diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteServiceTest.java deleted file mode 100644 index fb796252a7f562a824b4f1b1e9dd1a323e7fc8ed..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtRemoteServiceTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -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.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; -import de.ozgcloud.nachrichten.postfach.GrpcPostfachNachricht; - -class NachrichtRemoteServiceTest { - - @InjectMocks - private NachrichtRemoteService remoteService; - - @Mock - private NachrichtMapper mapper; - - @Nested - class TestSaveDarft { - @Nested - class BuildRequest { - private Nachricht nachricht = NachrichtTestFactory.create(); - private GrpcPostfachNachricht grpcNachricht = GrpcPostfachNachrichtTestFactory.create(); - - @BeforeEach - void initMocks() { - when(mapper.mapToGrpc(any())).thenReturn(grpcNachricht); - } - - @Test - void shouldCallMapper() { - remoteService.buildRequest(nachricht); - - verify(mapper).mapToGrpc(nachricht); - } - - @Test - void shouldSetNachricht() { - var request = remoteService.buildRequest(nachricht); - - assertThat(request.getNachricht()).isSameAs(grpcNachricht); - } - - @Test - void shouldSetVorgangId() { - var request = remoteService.buildRequest(nachricht); - - assertThat(request.getVorgangId()).isEqualTo(VorgangTestFactory.ID.toString()); - } - } - } - -} diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java deleted file mode 100644 index e0280020426fe92f6dce27b0f223738f57e108b5..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceITCase.java +++ /dev/null @@ -1,39 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -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.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.info.BuildProperties; -import org.springframework.boot.test.mock.mockito.MockBean; - -import de.ozgcloud.apilib.common.command.OzgCloudCommandService; -import de.ozgcloud.apilib.common.command.grpc.CommandMapper; -import de.ozgcloud.bescheid.BescheidTestFactory; -import de.ozgcloud.common.test.ITCase; - -@ITCase -class NachrichtServiceITCase { - - @Autowired - private NachrichtService service; - @MockBean - private OzgCloudCommandService commandService; - @MockBean - private CommandMapper commandMapper; - @MockBean - private BuildProperties buildProperties; - - @DisplayName("Build message") - @Nested - class TestBuildMessage { - @Test - void shouldBuildMessage() { - var message = service.buildMessage(BescheidTestFactory.create()); - - assertThat(message).isNotBlank(); - } - } -} \ No newline at end of file diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java deleted file mode 100644 index f18ffbc9d6851d39b9dfe8676d98f7676013dbe5..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtServiceTest.java +++ /dev/null @@ -1,350 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -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 java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; - -import de.ozgcloud.apilib.common.command.OzgCloudCommand; -import de.ozgcloud.apilib.common.command.OzgCloudCommandService; -import de.ozgcloud.apilib.common.command.grpc.CommandMapper; -import de.ozgcloud.apilib.common.datatypes.GenericId; -import de.ozgcloud.apilib.user.OzgCloudUserId; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; -import de.ozgcloud.bescheid.Bescheid; -import de.ozgcloud.bescheid.BescheidTestFactory; -import de.ozgcloud.bescheid.attacheditem.AttachedItemTestFactory; -import de.ozgcloud.bescheid.common.freemarker.TemplateHandler; -import de.ozgcloud.bescheid.vorgang.PostfachAddressTestFactory; -import de.ozgcloud.bescheid.vorgang.ServiceKontoTestFactory; -import de.ozgcloud.bescheid.vorgang.Vorgang; -import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; -import de.ozgcloud.common.errorhandling.TechnicalException; - -class NachrichtServiceTest { - - @Spy - @InjectMocks - private NachrichtService service; - - @Mock - private NachrichtRemoteService remoteService; - @Mock - private OzgCloudCommandService commandService; - @Mock - private CommandMapper commandMapper; - @Mock - private TemplateHandler templateHandler; - - @Nested - class TestCreateNachrichtDraft { - - private final Nachricht nachricht = NachrichtTestFactory.create(); - - @Test - void shouldCallRemoteService() { - doReturn(Optional.of(nachricht)).when(service).buildNachricht(any()); - - service.createNachrichtDraft(BescheidTestFactory.create()); - - verify(remoteService).saveDraft(nachricht); - } - - @Nested - class BuildNachricht { - - @BeforeEach - void init() { - doReturn(NachrichtTestFactory.MAIL_BODY).when(service).buildMessage(any()); - } - - @Test - void shouldFillMailBody() { - var nachricht = service.buildNachricht(BescheidTestFactory.create()).get(); - - assertThat(nachricht.getMailBody()).isEqualTo(NachrichtTestFactory.MAIL_BODY); - } - - @Test - void shouldSetSubject() { - var nachricht = service.buildNachricht(BescheidTestFactory.create()).get(); - - assertThat(nachricht.getSubject()).isEqualTo(BescheidTestFactory.NACHRICHT_SUBJECT); - } - - @Test - void shouldSetDefaultSubject() { - var bescheid = BescheidTestFactory.createBuilder().nachrichtSubject(Optional.empty()).build(); - - var nachricht = service.buildNachricht(bescheid).get(); - - assertThat(nachricht.getSubject()).isEqualTo(NachrichtService.SUBJECT); - } - - @Test - void shouldSetUser() { - var nachricht = service.buildNachricht(BescheidTestFactory.create()).get(); - - assertThat(nachricht.getCreatedBy()).isEqualTo(BescheidTestFactory.CREATED_BY); - } - - @Test - void shouldSetVorgangId() { - var nachricht = service.buildNachricht(BescheidTestFactory.create()).get(); - - assertThat(nachricht.getVorgangId()).isEqualTo(VorgangTestFactory.ID); - } - } - } - - @Nested - class TestBuildMessage { - @Test - void shouldUseTextFromBescheid() { - var message = service.buildMessage(BescheidTestFactory.create()); - - assertThat(message).isEqualTo(BescheidTestFactory.NACHRICHT_TEXT); - } - - @Test - void shouldUseDefaultTemplate() { - doReturn("FROM_TEMPLATE").when(templateHandler).fillTemplate(any(), any()); - - var message = service.buildMessage(BescheidTestFactory.createBuilder().nachrichtText(Optional.empty()).build()); - - assertThat(message).isEqualTo("FROM_TEMPLATE"); - } - } - - @Nested - class TestSendNachricht { - - @Mock - private OzgCloudCommand ozgCloudCommand; - - @BeforeEach - void init() { - doReturn(ozgCloudCommand).when(service).buildSendBescheidCommand(any()); - } - - @Test - void shouldCallBuildBescheidCommand() { - var bescheid = BescheidTestFactory.create(); - - service.sendNachricht(bescheid); - - verify(service).buildSendBescheidCommand(bescheid); - } - - @Test - void shouldCallCommandService() { - service.sendNachricht(BescheidTestFactory.create()); - - verify(commandService).createAndWaitUntilDone(ozgCloudCommand); - } - - } - - @Nested - class TestBuildSendBescheidCommand { - - private static final Map<String, Object> bodyObject = Map.of("key", "value"); - private final Bescheid bescheid = BescheidTestFactory.create(); - - @BeforeEach - void init() { - doReturn(bodyObject).when(service).buildSendNachrichtCommandBody(any()); - } - - @Test - void shouldCallVorgangIdMapper() { - buildCommand(); - - verify(commandMapper).toOzgCloudVorgangId(VorgangTestFactory.ID.toString()); - } - - @Test - void shouldSetVorgangId() { - var vorgangId = OzgCloudVorgangId.from(VorgangTestFactory.ID.toString()); - when(commandMapper.toOzgCloudVorgangId(anyString())).thenReturn(vorgangId); - - var result = buildCommand(); - - assertThat(result.getVorgangId()).isEqualTo(vorgangId); - } - - @Test - void shouldCallMapRelationId() { - buildCommand(); - - verify(commandMapper).mapRelationId(VorgangTestFactory.ID.toString()); - } - - @Test - void shouldSetRelationId() { - var relationId = GenericId.from("relation-id"); - when(commandMapper.mapRelationId(anyString())).thenReturn(relationId); - - var result = buildCommand(); - - assertThat(result.getRelationId()).isEqualTo(relationId); - } - - @Test - void shouldSetOrder() { - var result = buildCommand(); - - assertThat(result.getOrder()).isEqualTo(NachrichtService.SEND_POSTFACH_NACHRICHT_ORDER); - } - - @Test - void shouldCallCreatedBy() { - buildCommand(); - - verify(commandMapper).toOzgCloudUserId(BescheidTestFactory.CREATED_BY.toString()); - } - - @Test - void shouldSetCreatedBy() { - var userId = OzgCloudUserId.from(BescheidTestFactory.CREATED_BY.toString()); - when(commandMapper.toOzgCloudUserId(anyString())).thenReturn(userId); - - var result = buildCommand(); - - assertThat(result.getCreatedBy()).isEqualTo(userId); - } - - @Test - void shouldCallBuildSendNachrichtCommandBody() { - buildCommand(); - - verify(service).buildSendNachrichtCommandBody(bescheid); - } - - @Test - void shouldSetBodyObject() { - var result = buildCommand(); - - assertThat(result.getBodyObject()).isEqualTo(bodyObject); - } - - private OzgCloudCommand buildCommand() { - return service.buildSendBescheidCommand(bescheid); - } - } - - @Nested - class TestBuildSendNachrichtCommandBody { - - private final Bescheid bescheid = BescheidTestFactory.create(); - - @Test - void shouldCallBuildPostfachAddress() { - service.buildSendNachrichtCommandBody(bescheid); - - verify(service).buildPostfachAddress(bescheid); - } - - @Test - void shouldSetPostfachAddress() { - var postfachAddressMap = Map.<String, Object>of("key", "value"); - doReturn(postfachAddressMap).when(service).buildPostfachAddress(any()); - - var result = service.buildSendNachrichtCommandBody(bescheid); - - assertThat(result).containsEntry(Vorgang.ServiceKonto.FIELD_POSTFACH_ADDRESS, postfachAddressMap); - } - - @Test - void shouldSetReplyOption() { - var result = service.buildSendNachrichtCommandBody(bescheid); - - assertThat(result).containsEntry(NachrichtService.FIELD_REPLY_OPTION, NachrichtService.REPLY_OPTION); - } - - @Test - void shouldSetSubject() { - var result = service.buildSendNachrichtCommandBody(bescheid); - - assertThat(result).containsEntry(NachrichtService.FIELD_SUBJECT, BescheidTestFactory.NACHRICHT_SUBJECT); - } - - @Test - void shouldSetMailBody() { - var result = service.buildSendNachrichtCommandBody(bescheid); - - assertThat(result).containsEntry(NachrichtService.FIELD_MAIL_BODY, BescheidTestFactory.NACHRICHT_TEXT); - } - - @Test - void shouldSetAttachments() { - var result = service.buildSendNachrichtCommandBody(bescheid); - - assertThat(result).containsEntry(NachrichtService.FIELD_ATTACHMENTS, - List.of(BescheidTestFactory.BESCHEID_FILE_ID, AttachedItemTestFactory.ATTACHMENT)); - } - } - - @Nested - class TestBuildPostfachAddress { - - @Test - void shouldCallGetAddress() { - var bescheid = BescheidTestFactory.create(); - - service.buildPostfachAddress(bescheid); - - verify(service).getAddress(bescheid); - } - - @Test - void shouldThrowExceptionIfNoServiceKonto() { - doReturn(Optional.empty()).when(service).getAddress(any()); - var bescheid = BescheidTestFactory.create(); - - assertThrows(TechnicalException.class, () -> service.buildPostfachAddress(bescheid)); - } - - @Test - void shouldSetAddressType() { - var result = service.buildPostfachAddress(BescheidTestFactory.create()); - - assertThat(result).containsEntry(Vorgang.PostfachAddress.FIELD_TYPE, PostfachAddressTestFactory.TYPE); - } - - @Test - void shouldSetAddressVersion() { - var result = service.buildPostfachAddress(BescheidTestFactory.create()); - - assertThat(result).containsEntry(Vorgang.PostfachAddress.FIELD_VERSION, PostfachAddressTestFactory.VERSION); - } - - @Test - void shouldSetAddressIdentifier() { - var result = service.buildPostfachAddress(BescheidTestFactory.create()); - - assertThat(result).extracting(Vorgang.PostfachAddress.FIELD_IDENTIFIER, MAP) - .containsEntry(NachrichtService.FIELD_POSTFACH_ID, PostfachAddressTestFactory.POSTFACH_ID); - } - - @Test - void shouldSetServiceKontoType() { - var result = service.buildPostfachAddress(BescheidTestFactory.create()); - - assertThat(result).containsEntry(Vorgang.ServiceKonto.FIELD_SERVICEKONTO_TYPE, ServiceKontoTestFactory.TYPE); - } - - } - -} diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtTestFactory.java deleted file mode 100644 index a82751dcc7ac14b7ba16a677d2fd2ad3476e73a9..0000000000000000000000000000000000000000 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/nachricht/NachrichtTestFactory.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.ozgcloud.bescheid.nachricht; - -import java.time.ZonedDateTime; -import java.util.UUID; - -import de.ozgcloud.bescheid.UserId; -import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; - -public class NachrichtTestFactory { - - public static final NachrichtId ID = NachrichtId.from(UUID.randomUUID().toString()); - - public static final String SUBJECT = "subject of message"; - public static final String MAIL_BODY = "Lorem ipsum"; - - public static final UserId CREATED_BY = UserId.from(UUID.randomUUID().toString()); - public static final ZonedDateTime CREATED_AT = ZonedDateTime.now().withNano(0); - - public static Nachricht create() { - return createBuilder().build(); - } - - public static Nachricht.NachrichtBuilder createBuilder() { - return Nachricht.builder() - .id(ID) - .vorgangId(VorgangTestFactory.ID) - .subject(SUBJECT) - .mailBody(MAIL_BODY) - .createdBy(CREATED_BY) - .createdAt(CREATED_AT); - } -} diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java index 3126ecfe5b5f98c41749c406cd426253a65b053e..764fc28f4882f0f160b07deef03f080c4c8b975d 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceITCase.java @@ -2,7 +2,10 @@ package de.ozgcloud.bescheid.smartdocuments; import static org.assertj.core.api.Assertions.*; +import java.io.File; + import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -14,9 +17,10 @@ import de.ozgcloud.apilib.common.command.grpc.CommandMapper; import de.ozgcloud.bescheid.BescheidRequestTestFactory; import de.ozgcloud.bescheid.BescheidTestApplication; import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; +import de.ozgcloud.common.binaryfile.TempFileUtils; import de.ozgcloud.common.test.ITCase; +import de.ozgcloud.common.test.TestUtils; -@Disabled @SpringBootTest(classes = BescheidTestApplication.class) @ITCase @ActiveProfiles({ "itcase", "local" }) @@ -29,6 +33,7 @@ class SmartDocumentsBescheidRemoteServiceITCase { @MockBean private CommandMapper commandMapper; + @Disabled("This test request SmartDocuments service") @Test void createBescheid() { var bescheid = remoteService.create(BescheidRequestTestFactory.create(), VorgangTestFactory.create()); @@ -38,4 +43,37 @@ class SmartDocumentsBescheidRemoteServiceITCase { assertThat(bescheid.getBescheidFileName()).isNotEmpty(); } + @Nested + class TestGetNachrichtenText { + + private static final String NACHRICHTEN_TEXT = "Nachrichtentext"; + private static final String FIELD_TEMPLATE_TEXT = "Nachrichten Field Text"; + + @Test + void shouldGetNachrichtenText() { + var nachrichtenText = remoteService.extractTextFromXmlFile(loadFile("SD_answer.xml")); + + assertThat(nachrichtenText).contains(NACHRICHTEN_TEXT); + } + + @Test + void shouldGetFieldTemplateText() { + var fieldTemplateText = remoteService.extractTextFromXmlFile(loadFile("SD_answer_field_template.xml")); + + assertThat(fieldTemplateText).contains(FIELD_TEMPLATE_TEXT); + } + + @Test + void shouldExpectMissingTextNode() { + File xmlFileWithoutText = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer_without_text.xml")); + + var text = remoteService.extractTextFromXmlFile(xmlFileWithoutText); + + assertThat(text).isEmpty(); + } + + private File loadFile(String fileName) { + return TempFileUtils.writeTmpFile(TestUtils.loadFile(fileName)); + } + } } diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java index 695e1cad6f1f865c19181e4b510d51ce03a12b96..4db4b0e81338307b7af8df53bf85667096e738e3 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/smartdocuments/SmartDocumentsBescheidRemoteServiceTest.java @@ -7,19 +7,20 @@ import static org.mockito.Mockito.*; import java.io.File; import java.util.Optional; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; +import org.w3c.dom.Document; +import org.w3c.dom.Text; import de.ozgcloud.bescheid.BescheidRequestTestFactory; import de.ozgcloud.bescheid.BescheidTestFactory; import de.ozgcloud.bescheid.common.user.UserProfileTestFactory; import de.ozgcloud.bescheid.smartdocuments.SmartDocumentsRequest.CustomerData.UserData; import de.ozgcloud.bescheid.vorgang.VorgangTestFactory; -import de.ozgcloud.common.binaryfile.TempFileUtils; -import de.ozgcloud.common.test.TestUtils; import lombok.SneakyThrows; class SmartDocumentsBescheidRemoteServiceTest { @@ -30,6 +31,10 @@ class SmartDocumentsBescheidRemoteServiceTest { @Mock private SmartDocumentsProperties properties; + @Mock + private Text xmlText; + @Mock + private File xmlFile; @Nested class TestBuildBescheid { @@ -57,7 +62,15 @@ class SmartDocumentsBescheidRemoteServiceTest { @Nested class TestGetNachrichtText { - private final File xmlFile = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer.xml")); + + @Test + void shouldCallGetXMLFile() { + var response = SmartDocumentsResponseTestFactory.create(); + + service.getNachrichtText(response); + + verify(service).getXMLFile(response); + } @Test void shouldCallExtractText() { @@ -65,36 +78,171 @@ class SmartDocumentsBescheidRemoteServiceTest { service.getNachrichtText(SmartDocumentsResponseTestFactory.create()); - verify(service).extractTextFormXmlFile(notNull()); + verify(service).extractTextFromXmlFile(xmlFile); + } + } + + @Nested + class TestExtractTextFromXmlFileSuccessfully { + + private static final String NACHRICHT_TEXT = "NachrichtText"; + + @Mock + private Document document; + + @SneakyThrows + @BeforeEach + void init() { + doReturn(Optional.of(document)).when(service).parseXml(any()); + } + + @SneakyThrows + @Test + void shouldCallParseXml() { + service.extractTextFromXmlFile(xmlFile); + + verify(service).parseXml(xmlFile); + } + + @Test + void shouldCallGetNachrichtenText() { + doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any()); + + service.extractTextFromXmlFile(xmlFile); + + verify(service).getNachrichtenText(document); + } + + @Test + void shouldReturnNachrichtenText() { + doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any()); + when(xmlText.getTextContent()).thenReturn(NACHRICHT_TEXT); + + var text = service.extractTextFromXmlFile(xmlFile); + + assertThat(text).contains(NACHRICHT_TEXT); + } + + @Test + void shouldNotCallGetFieldTemplateText() { + doReturn(Optional.of(xmlText)).when(service).getNachrichtenText(any()); + + service.extractTextFromXmlFile(xmlFile); + + verify(service, never()).getFieldTemplateText(document); } - @Nested - class ExtractingText { + @Test + void shouldCallGetFieldTemplateText() { + doReturn(Optional.empty()).when(service).getNachrichtenText(any()); + doReturn(Optional.of(xmlText)).when(service).getFieldTemplateText(any()); + + service.extractTextFromXmlFile(xmlFile); - @Test - void shouldReturnText() { - var text = service.extractTextFormXmlFile(xmlFile); + verify(service).getFieldTemplateText(document); + } - assertThat(text).isPresent().get().isNotNull(); - } + @Test + void shouldReturnFieldTemplateText() { + doReturn(Optional.empty()).when(service).getNachrichtenText(any()); + doReturn(Optional.of(xmlText)).when(service).getFieldTemplateText(any()); + when(xmlText.getTextContent()).thenReturn(NACHRICHT_TEXT); - @Test - void shouldHandleError() { - var text = service.extractTextFormXmlFile(null); + var text = service.extractTextFromXmlFile(xmlFile); - assertThat(text).isEmpty(); - } + assertThat(text).contains(NACHRICHT_TEXT); + } + } - @Test - @SneakyThrows - void shouldExpectMissingTextNode() { - File xmlFileWithoutText = TempFileUtils.writeTmpFile(TestUtils.loadFile("SD_answer_without_text.xml")); + @Nested + class TestExtractTextFormXmlFileFails { - var text = service.doExtractText(xmlFileWithoutText); + @Mock + private Document document; - assertThat(text).isEmpty(); - } + @SneakyThrows + @Test + void shouldHandleMissingXmlDocument() { + doReturn(Optional.empty()).when(service).parseXml(any()); + + var text = service.extractTextFromXmlFile(xmlFile); + + assertThat(text).isEmpty(); + } + + @Test + @SneakyThrows + void shouldExpectMissingTextNode() { + doReturn(Optional.of(document)).when(service).parseXml(any()); + doReturn(Optional.empty()).when(service).getFieldTemplateText(any()); + + var text = service.extractTextFromXmlFile(xmlFile); + + assertThat(text).isEmpty(); + } + } + + @Nested + class TestGetNachrichtenText { + + @Mock + private Text text; + @Mock + private Document document; + + @BeforeEach + void init() { + doReturn(Optional.of(text)).when(service).evaluateXPath(any(), any()); + } + + @Test + void shouldCallEvaluatePath() { + getNachrichtenText(); + + verify(service).evaluateXPath(document, SmartDocumentsBescheidRemoteService.NACHRICHTEN_TEXT_EXPRESSION); + } + + @Test + void shouldReturnText() { + var result = getNachrichtenText(); + + assertThat(result).contains(text); + } + + private Optional<Text> getNachrichtenText() { + return service.getNachrichtenText(document); + } + } + + @Nested + class TestGetFieldTemplateText { + + @Mock + private Text text; + @Mock + private Document document; + + @BeforeEach + void init() { + doReturn(Optional.of(text)).when(service).evaluateXPath(any(), any()); + } + + @Test + void shouldCallEvaluatePath() { + getFieldTemplateText(); + + verify(service).evaluateXPath(document, SmartDocumentsBescheidRemoteService.FIELD_TEMPLATE_EXPRESSION); + } + + @Test + void shouldReturnText() { + var result = getFieldTemplateText(); + + assertThat(result).contains(text); + } + private Optional<Text> getFieldTemplateText() { + return service.getFieldTemplateText(document); } } } diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/PostfachAddressTestFactory.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/PostfachAddressTestFactory.java index 7f7ef749025025cc565b4b04b6a5db8183eb3fb1..5d01a8b7573c16643a61e602ff06bc94aa0cd155 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/PostfachAddressTestFactory.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/PostfachAddressTestFactory.java @@ -26,12 +26,13 @@ package de.ozgcloud.bescheid.vorgang; import java.util.Map; import java.util.UUID; -import de.ozgcloud.bescheid.nachricht.NachrichtService; import de.ozgcloud.bescheid.vorgang.Vorgang.PostfachAddress.PostfachAddressBuilder; public class PostfachAddressTestFactory { public static final String POSTFACH_ID = UUID.randomUUID().toString(); + public static final String FIELD_POSTFACH_ID = "postfachId"; + public static final int TYPE = 1; public static final String VERSION = "1.0"; @@ -43,6 +44,6 @@ public class PostfachAddressTestFactory { return Vorgang.PostfachAddress.builder() .type(TYPE) .version(VERSION) - .identifier(Map.of(NachrichtService.FIELD_POSTFACH_ID, POSTFACH_ID)); + .identifier(Map.of(FIELD_POSTFACH_ID, POSTFACH_ID)); } } diff --git a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java index 424a4330012bc1fd605dede82c56eac40c435f67..cc93692c7831628dd87ada5bf0cf21f2250b5bb4 100644 --- a/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java +++ b/bescheid-manager/src/test/java/de/ozgcloud/bescheid/vorgang/VorgangServiceTest.java @@ -11,12 +11,6 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import de.ozgcloud.apilib.common.command.OzgCloudCommand; -import de.ozgcloud.apilib.common.command.OzgCloudCommandService; -import de.ozgcloud.apilib.common.command.grpc.CommandMapper; -import de.ozgcloud.apilib.common.datatypes.GenericId; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangId; - class VorgangServiceTest { @Spy @@ -25,10 +19,6 @@ class VorgangServiceTest { @Mock private VorgangRemoteService remoteService; - @Mock - private OzgCloudCommandService commandService; - @Mock - private CommandMapper commandMapper; @Nested class TestGetById { @@ -54,99 +44,4 @@ class VorgangServiceTest { assertThat(result).isEqualTo(vorgang); } } - - @Nested - class TestBescheiden { - - private final static Vorgang VORGANG = VorgangTestFactory.create(); - - @Mock - private OzgCloudCommand ozgCloudCommand; - - @BeforeEach - void init() { - doReturn(VORGANG).when(service).getById(any(VorgangId.class)); - } - - @Test - void shouldCallGetBeyId() { - service.bescheiden(VorgangTestFactory.ID.toString()); - - verify(service).getById(VorgangTestFactory.ID); - } - - @Test - void shouldCallBuildBescheidenCommand() { - service.bescheiden(VorgangTestFactory.ID.toString()); - - verify(service).buildBescheidenCommand(VORGANG); - } - - @Test - void shouldCallCommandService() { - doReturn(ozgCloudCommand).when(service).buildBescheidenCommand(any()); - - service.bescheiden(VorgangTestFactory.ID.toString()); - - verify(commandService).createAndWaitUntilDone(ozgCloudCommand); - } - } - - @Nested - class TestBuildBescheidenCommand { - - private final static Vorgang VORGANG = VorgangTestFactory.create(); - - @Test - void shouldCallCommandMapperToOzgCloudVorgangId() { - service.buildBescheidenCommand(VORGANG); - - verify(commandMapper).toOzgCloudVorgangId(VorgangTestFactory.ID.toString()); - } - - @Test - void shouldSetVorgangId() { - var expectedId = OzgCloudVorgangId.from(VorgangTestFactory.ID.toString()); - when(commandMapper.toOzgCloudVorgangId(any())).thenReturn(expectedId); - - var result = service.buildBescheidenCommand(VORGANG); - - assertThat(result.getVorgangId()).isEqualTo(expectedId); - } - - @Test - void shouldCallCommandMapperMapRelationId() { - service.buildBescheidenCommand(VORGANG); - - verify(commandMapper).mapRelationId(VorgangTestFactory.ID.toString()); - } - - @Test - void shouldSetRelationId() { - var expectedId = GenericId.from(VorgangTestFactory.ID.toString()); - when(commandMapper.mapRelationId(any())).thenReturn(expectedId); - - var result = service.buildBescheidenCommand(VORGANG); - - assertThat(result.getRelationId()).isEqualTo(expectedId); - } - - @Test - void shouldSetRelationVersion() { - var expectedVersion = VORGANG.getVersion(); - - var result = service.buildBescheidenCommand(VORGANG); - - assertThat(result.getRelationVersion()).isEqualTo(expectedVersion); - } - - @Test - void shouldSetOrder() { - var expectedOrder = VorgangService.VORGANG_BESCHEIDEN; - - var result = service.buildBescheidenCommand(VORGANG); - - assertThat(result.getOrder()).isEqualTo(expectedOrder); - } - } } diff --git a/bescheid-manager/src/test/resources/SD_answer.xml b/bescheid-manager/src/test/resources/SD_answer.xml index df9d905e472c9b0a077421064ad61a990389ede5..172f70cc754469dec0ee76b32714c0ea1b127c5c 100644 --- a/bescheid-manager/src/test/resources/SD_answer.xml +++ b/bescheid-manager/src/test/resources/SD_answer.xml @@ -258,13 +258,7 @@ <Field ID="FEC37A441C9D46189533DBD9CFD0E0E8"><![CDATA[STEUBER]]></Field> <Field ID="FECCC52ABB614390BAE2577AD8C44262"><![CDATA[24103]]></Field> <Field ID="root/CustomerData/Dateiname"><![CDATA[Fleet_9-Verk-AO_01-08_06.12.2023-06.12.2023]]></Field> - <NachrichtenText>Sehr geehrte/r Antragstellende, - -anliegend erhalten Sie die verkehrsrechtliche Anordnung für die Aufstellung der von Ihnen beantragten Beschilderung. Die Prüfung der Straßenverkehrsbehörde bezieht sich nur auf die Vollständigkeit des Antrages und die Plausibilität der Angaben. Die richtige Auswahl der aufzustellenden Verkehrszeichen oder Überschneidung mit anderen Anträgen wurde dabei nicht geprüft. - -BITTE DRINGEND BEACHTEN -Der Antrag einer mobilen Haltverbotszone ist MINDESTENS 1 Woche vor Aufstellung der Beschilderung zu stellen. Des Weiteren gilt die Beschilderung erst als angeordnet, wenn die Antragstellenden das von der Straßenverkehrsbehörde unterschriebene Anmeldeformular zurückerhalten haben (Fax, E-Mail, Post). Vorab sind keine Schilder aufzustellen!!! -</NachrichtenText> + <NachrichtenText>Nachrichtentext</NachrichtenText> </Fields> <DocumentProperties> <Guid>7ACEA5AE7C3642978ACCC48182EBCD6E</Guid> diff --git a/bescheid-manager/src/test/resources/SD_answer_field_template.xml b/bescheid-manager/src/test/resources/SD_answer_field_template.xml new file mode 100644 index 0000000000000000000000000000000000000000..7ad2b6b9bdd219cf10ac96c711d43fff926a4009 --- /dev/null +++ b/bescheid-manager/src/test/resources/SD_answer_field_template.xml @@ -0,0 +1,299 @@ +<?xml version='1.0' encoding='UTF-8'?><!-- + ~ Copyright (C) 2024 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. + --> +<root> + <SmartDocument Version="2.0"> + <Selection> + <TypistID>072D2DBF07C049858DFB716BDBAAE83A</TypistID> + <UserGroupID> + DA9D309EF1374D8A920CC9878DD3D629 + </UserGroupID> + <AuthorID>072D2DBF07C049858DFB716BDBAAE83A + </AuthorID> + <TemplateGroupID>94A1C1208DF0479CB58E3CFB0A6057BC</TemplateGroupID> + <Template> + KFAS_LIVE_KI_10_Haltverbot_befristet + </Template> + <TemplateID VersionID="87731D1F4AB74952979B58497D95EA86"> + 7ACEA5AE7C3642978ACCC48182EBCD6E + </TemplateID> + <Blocks> + <Block VersionID="15DCCB4DFE1A414796EBA44352C7AAFF"> + CC2A9C0E14164B9A967CEE8DCD41B3B5 + </Block> + <Placeholders> + <Placeholder ID="125E766AF0424D4992F8F04C806F75EB"> + CC2A9C0E14164B9A967CEE8DCD41B3B5 + </Placeholder> + </Placeholders> + </Blocks> + <FixedValues/> + </Selection> + <Variables/> + <QuestionAnswers> + <Question ID="07BE1279C0504852AEFBB264D165E139" Description="Tagesdatum" OriginalValue="2024-06-25"> + 25.06.2024 + </Question> + <Question ID="23BB945F7BA44321B09A0A984D541DD1" Description="Tage Halteverbot" + ContentId="23BB945F7BA44321B09A0A984D541DD1" IsFormatted="true" ContentType="XML"> + <Records> + <Record> + <Field Name="71F5F35C4FD94982A1F8321A73E9B593"> + <Value>nein</Value> + <QuestionAnswers> + <Question ID="58F6E456AE1F4E13907D78E158BF4847" Description="Freitag"> + <Answer ID="313DC774D89E45D8864FC33983C95923" Description="Nein" IsFormatted="true" + ContentId="313DC774D89E45D8864FC33983C95923"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="B66C9D21D31A4293852EEDCAB57C593B"> + <Value>ja</Value> + <QuestionAnswers> + <Question ID="EF33891FF01A42B0BEFB1E78E3FC023D" Description="Samstag"> + <Answer ID="F22F6FE074784CCD950430166F42B33F" Description="Ja" IsFormatted="true" + ContentId="F22F6FE074784CCD950430166F42B33F"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="67082AAB675E457180A7014531C46CB3"> + <Value>ja</Value> + <QuestionAnswers> + <Question ID="60C6CD1F29084A26BEC493A862583AAB" Description="Montag"> + <Answer ID="8EAE05F9D89C46AE8CCB08A24104CC17" Description="Ja" IsFormatted="true" + ContentId="8EAE05F9D89C46AE8CCB08A24104CC17"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="3FD23F8F98EF459A95B50448DEDFF20E"> + <Value>ja</Value> + <QuestionAnswers> + <Question ID="C8BC6BFFE8BE4E6BBDC4CBCD34377DB2" Description="Mittwoch"> + <Answer ID="97B676CAC9B74D00B574F319B2D20BB5" Description="Ja" IsFormatted="true" + ContentId="97B676CAC9B74D00B574F319B2D20BB5"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="09658B4DA4BB4295822A42C0AA8FC2EC"> + <Value>nein</Value> + <QuestionAnswers> + <Question ID="39BF39565D4043078ABEEC9601A091E7" Description="Donnerstag"> + <Answer ID="8D4A19B4D3AF448FB720E9160E7A40D2" Description="Nein" IsFormatted="true" + ContentId="8D4A19B4D3AF448FB720E9160E7A40D2"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="975867EBE97547ACA6B8D85F1D8D39C8"> + <Value>nein</Value> + <QuestionAnswers> + <Question ID="03906ABB93494C6EB087D87C4C70217C" Description="Dienstag"> + <Answer ID="B9ACD0E74C6B41BEA700C9EB8FCFFC25" Description="Nein" IsFormatted="true" + ContentId="B9ACD0E74C6B41BEA700C9EB8FCFFC25"/> + </Question> + </QuestionAnswers> + </Field> + <Field Name="AF89873144744EDC9A1B78776F3D25F5"> + <Value>nein</Value> + <QuestionAnswers> + <Question ID="06349259221C4DEFBB4FE9FD33D76403" Description="Sonntag"> + <Answer ID="853ED74B536C4BD8AF95193450F18201" Description="Nein" IsFormatted="true" + ContentId="853ED74B536C4BD8AF95193450F18201"/> + </Question> + </QuestionAnswers> + </Field> + </Record> + </Records> + </Question> + <Question ID="28CB094DAD1A4604B08CE7C5D2177C03" Description="Beschilderung"> + <Answer ID="605F18E32DE04FF2B058BD66F2DE9C06" Description="Nein" IsFormatted="true" + ContentId="605F18E32DE04FF2B058BD66F2DE9C06"/> + </Question> + <Question ID="736372FDB0A2407CBBFB15DE71FD1450" Description="Geschlecht Antragssteller"> + <Answer ID="CCFD78217BC84D339F582F4B5703A7B6" Description="keine Angabe" IsFormatted="true" + ContentId="CCFD78217BC84D339F582F4B5703A7B6"/> + </Question> + <Question ID="97E309BDBDB548F48D162D289056B313" Description="Uhrzeit Halteverbot"> + <Answer ID="E45BE1342D5F43D59E21986C377DA1B0" Description="ganztägig" IsFormatted="true" + ContentId="E45BE1342D5F43D59E21986C377DA1B0"/> + </Question> + <Question ID="992F241A1DF4497A8B4568E69B9C567C" Description="Vor Ort geltende Parkregelung"> + <Answer ID="9875A84F77164A9C88426FD6A01D784F" Description="Parken am Fahrbahnrand" IsFormatted="true" + ContentId="9875A84F77164A9C88426FD6A01D784F"/> + </Question> + <Question ID="9BD81FB0E5FF4235A094BE3D21544615" Description="Name Ansprechperson / Firma" + AlternativeParentID="736372FDB0A2407CBBFB15DE71FD1450"> + <Answer ID="FC9E396BAA894B52A475BCE1184230D9" Description="keine Angabe" IsFormatted="true" + ContentId="FC9E396BAA894B52A475BCE1184230D9"/> + </Question> + <Question ID="9CE081D9122E43B1B778863AE32AB29F" Description="Protokoll"> + <Answer ID="8490AF83642D4D26A408A2C431F52183" Description="Nein" IsFormatted="true" + ContentId="8490AF83642D4D26A408A2C431F52183"/> + </Question> + <Question ID="B651AC699A4F4B768DC34DE87BBD578C" Description="Gründe für Halteverbot" + ContentId="B651AC699A4F4B768DC34DE87BBD578C" IsFormatted="true" ContentType="XML"> + <Records/> + </Question> + <Question ID="C33A6D64E1B542CB97520C1F32D18C35" Description="Unterschrift"> + <Answer ID="68A02377AE9D473C9F6BAFF4EF150DE5" Description="Reusche" IsFormatted="true" + ContentId="68A02377AE9D473C9F6BAFF4EF150DE5"/> + </Question> + <Question ID="C7C9CCBF9633499EAB08D1BBB84BFD98" Description="Richtigkeit"> + <Answer ID="6DF6B7C6A88D4F2282B8366027B0032D" Description="Nein" IsFormatted="true" + ContentId="6DF6B7C6A88D4F2282B8366027B0032D"/> + </Question> + <Question ID="C7D1A20B17CD4C96BF55A2738B8A4631" Description="Kenntnisnahme"> + <Answer ID="50BBC8449BEB4AFEB774AC706F1D66B9" Description="Nein" IsFormatted="true" + ContentId="50BBC8449BEB4AFEB774AC706F1D66B9"/> + </Question> + </QuestionAnswers> + <Fields> + <Field ID="3C5C7920FB444A9DB2F5239339599BD7"/> + <Field ID="41237CC02584421A8D65990E7F78F833"/> + <Field ID="423CB6F4AEB54A44BC67F97626ADC06C"/> + <Field ID="442D48B946C64853B78DB99D9341AAC8">16:23</Field> + <Field ID="4D4178B6F808409391990233D33BF54C"/> + <Field ID="5AE5217E97F845B48D4274894BC2F043">03:25</Field> + <Field ID="635CEB0B41DE44EFB2207122CCB8EE1D"/> + <Field ID="6B06CD242FC64865BFC5B770BB03EAA4"/> + <Field ID="769E3AC926154741AABE870EB802E2AD"/> + <Field ID="7AF097E4E741455685CD543ABB7CE337"/> + <Field ID="8CA72E11F95F4352AE6FB8001BE79508">25.05.2023</Field> + <Field ID="8D38662ADBF2408B8C8DDB5294A5A5A9"/> + <Field ID="CA36A227CFEC4B89A40CA8B0CF4460C0">23</Field> + <Field ID="CC330D642D7D465FA611D029F6A06344">KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</Field> + <Field ID="D316B8AE41A3496495C058CCB3766F3A"/> + <Field ID="DB270EC247D5476A9D8BFA008E872D7D">Kiel</Field> + <Field ID="DE7F7E00C8C246AEA9FFDF7F89E2FB03">05.05.2023</Field> + <Field ID="F1826BE2F3D04E508F6401E8AA1D08C1"/> + <Field ID="FEC37A441C9D46189533DBD9CFD0E0E8"/> + <Field ID="FECCC52ABB614390BAE2577AD8C44262">24114</Field> + <Field ID="Template.56E7AA0956C7486292E9A02114CB231C">Nachrichten Field Text</Field> + <Field ID="root/CustomerData/Dateiname">_23-Verk-AO_05-23_05.05.2023-25.05.2023</Field> + </Fields> + <DocumentProperties> + <Guid>7ACEA5AE7C3642978ACCC48182EBCD6E</Guid> + <BuiltIn> + <creator>MGM</creator> + </BuiltIn> + <Extended/> + <Custom> + <Author name="Author" type="lpwstr" property="true" variable="false" xml="false">MGM</Author> + <Typist name="Typist" type="lpwstr" property="true" variable="false" xml="false">MGM</Typist> + <TemplateId name="TemplateId" type="lpwstr" property="true" variable="false" xml="false"> + 7ACEA5AE7C3642978ACCC48182EBCD6E + </TemplateId> + <Template name="Template" type="lpwstr" property="true" variable="false" xml="false"> + KFAS_LIVE_KI_10_Haltverbot_befristet + </Template> + </Custom> + </DocumentProperties> + </SmartDocument> + <CustomerData> + <bescheid> + <bescheidVom>2023-06-26</bescheidVom> + <genehmigt>true</genehmigt> + </bescheid> + <userData> + <firstName>Theo</firstName> + <lastName>Test</lastName> + </userData> + <vorgang> + <vorgangName>KFAS_LIVE_KI_10_Haltverbot_befristet</vorgangName> + <eingang> + <zustaendigeStelle> + <organisationseinheitenId>10363455</organisationseinheitenId> + <email/> + </zustaendigeStelle> + <antragsteller/> + <formData> + <Antrag_abschlie_en> + <Ich_best_tige__dass_die_Beschilderung_entsprechend_den_wichtigen_Hinweisen_durchgef_hrt_wird> + ja + </Ich_best_tige__dass_die_Beschilderung_entsprechend_den_wichtigen_Hinweisen_durchgef_hrt_wird> + <Ich_best_tige_hiermit__dass_ein_Protokoll_entsprechend_den_wichtigen_Hinweisen_erstellt_wird> + ja + </Ich_best_tige_hiermit__dass_ein_Protokoll_entsprechend_den_wichtigen_Hinweisen_erstellt_wird> + <Hiermit_best_tige_ich_die_Kenntnisnahme_des_Vorgehens_bei_parkenden_KFZ>ja + </Hiermit_best_tige_ich_die_Kenntnisnahme_des_Vorgehens_bei_parkenden_KFZ> + <Hiermit_best_tige_ich_die_Richtigkeit_der_gemachten_Angaben>ja + </Hiermit_best_tige_ich_die_Richtigkeit_der_gemachten_Angaben> + </Antrag_abschlie_en> + <Stammdaten__Antragstellende_Person_> + <Adresse_Kiel_vorbef_llt_mit_Kontaktdaten> + <AS_E-Mail>Bjoern.Reusche@kiel.de</AS_E-Mail> + <Postleitzahl>24114</Postleitzahl> + <AS_Telefon>015142536149</AS_Telefon> + <Wohnort>Kiel</Wohnort> + <Stra_e>Je?stra?e</Stra_e> + <Hausnummer>23</Hausnummer> + </Adresse_Kiel_vorbef_llt_mit_Kontaktdaten> + <Nachname>Reusche</Nachname> + <Angabe_zur_Person__Firma>m?nnlich</Angabe_zur_Person__Firma> + <Verantwortliche_Person_f_r_die_Aufstellung_der_Beschilderung/> + <Vorname>Bj?rn</Vorname> + </Stammdaten__Antragstellende_Person_> + <Angaben_zur_Parkregelung_und_zur_Beschilderung> + <Objektgruppe_Hinweis_kein_Abschleppen_m_glich/> + <Ort_des_Haltverbotes> + <Postleitzahl>24114</Postleitzahl> + <Wohnort>Kiel</Wohnort> + <Stra_e>Je?stra?e</Stra_e> + <Hausnummer>23</Hausnummer> + </Ort_des_Haltverbotes> + <L_nge_der_Fl_che_in_Metern>25</L_nge_der_Fl_che_in_Metern> + <Gr_nde> + <Container>nein</Container> + <Baufahrzeug>nein</Baufahrzeug> + <Materiallagerung>nein</Materiallagerung> + <Umzug>ja</Umzug> + <Hubsteiger__Kran>nein</Hubsteiger__Kran> + <Sonstiges>ja</Sonstiges> + </Gr_nde> + <Beginn_des_Haltverbotes>05.05.2023</Beginn_des_Haltverbotes> + <Bitte_erl_utern_Sie_Ihren_Grund>Verl?ngerung bisher ab 01.04.2023 + </Bitte_erl_utern_Sie_Ihren_Grund> + <Uhrzeit> + <Ende_des_Haltverbotes__Uhrzeit_>16:23</Ende_des_Haltverbotes__Uhrzeit_> + <Beginn_des_Haltverbotes__Uhrzeit_>03:25</Beginn_des_Haltverbotes__Uhrzeit_> + </Uhrzeit> + <Vor_Ort_geltende_Parkregelung>Parken am Fahrbahnrand</Vor_Ort_geltende_Parkregelung> + <Tage_Haltverbot> + <Freitag>nein</Freitag> + <Samstag>ja</Samstag> + <Montag>ja</Montag> + <Mittwoch>ja</Mittwoch> + <Donnerstag>nein</Donnerstag> + <Dienstag>nein</Dienstag> + <Sonntag>nein</Sonntag> + </Tage_Haltverbot> + <Ende_des_Haltverbotes>25.05.2023</Ende_des_Haltverbotes> + </Angaben_zur_Parkregelung_und_zur_Beschilderung> + <Informationen_und_Hinweise_zu_erforderlichen_Unterlagen/> + </formData> + </eingang> + <vorgangNummer>KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</vorgangNummer> + <aktenzeichen>KFAS_LIVE_KI_10_Haltverbot_befristet-JhQDdq2F</aktenzeichen> + </vorgang> + <Dateiname>_23-Verk-AO_05-23_05.05.2023-25.05.2023</Dateiname> + </CustomerData> +</root> diff --git a/lombok.config b/lombok.config index d07dd9b0e2b0281fbf514a968b9451cb6af62f93..a06fa130e8af26b659f2d3a0cb1114cd966a9b0e 100644 --- a/lombok.config +++ b/lombok.config @@ -27,4 +27,5 @@ lombok.log.slf4j.flagUsage = ERROR lombok.log.log4j.flagUsage = ERROR lombok.data.flagUsage = ERROR lombok.nonNull.exceptionType = IllegalArgumentException -lombok.addLombokGeneratedAnnotation = true \ No newline at end of file +lombok.addLombokGeneratedAnnotation = true +lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Qualifier \ No newline at end of file diff --git a/pom.xml b/pom.xml index b8aec14705c319a0b4710203f947b9e23d43df88..da655369cc9b17861fa4ba7ae0f7d566ec17a941 100644 --- a/pom.xml +++ b/pom.xml @@ -29,13 +29,13 @@ <modelVersion>4.0.0</modelVersion> <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <name>OZG-Cloud Vorgang Manager</name> <packaging>pom</packaging> <properties> - <ozgcloud.license.version>1.3.0</ozgcloud.license.version> + <ozgcloud.license.version>1.6.0</ozgcloud.license.version> </properties> <modules> diff --git a/src/main/helm/templates/_helpers.tpl b/src/main/helm/templates/_helpers.tpl index 7c94e6cfd9851532d35296171f68cff8ba0e46b2..1317f415cf1502d9f897172dfcfcfdf2b1c3a2ef 100644 --- a/src/main/helm/templates/_helpers.tpl +++ b/src/main/helm/templates/_helpers.tpl @@ -70,7 +70,7 @@ app.kubernetes.io/namespace: {{ include "app.namespace" . }} {{- end -}} {{- define "app.ozgcloud_vorgangmanager_address" -}} -{{ printf "dns://%s.%s:9090" .Values.vorgangmanagerName .Release.Namespace }} +{{ printf "%s.%s:9090" .Values.vorgangmanagerName .Release.Namespace }} {{- end -}} {{- define "app.databaseSecretName" -}} @@ -101,4 +101,29 @@ app.kubernetes.io/namespace: {{ include "app.namespace" . }} {{- $customList = append $customList (dict "name" $key "value" $value) }} {{- end -}} {{- $customList | toYaml -}} +{{- end -}} + + +{{- define "app.bayernidAbsenderName" -}} +{{- quote (required "ozgcloud.bayernid.absender.name must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).name) -}} +{{- end -}} + +{{- define "app.bayernidAbsenderMandant" -}} +{{- if (((.Values.ozgcloud).bayernid).absender).mandant -}} +{{ quote .Values.ozgcloud.bayernid.absender.mandant }} +{{- else -}} +{{ include "app.bayernidAbsenderName" . }} +{{- end -}} +{{- end -}} + +{{- define "app.bayernidAbsenderDienst" -}} +{{- if (((.Values.ozgcloud).bayernid).absender).dienst -}} +{{ quote .Values.ozgcloud.bayernid.absender.dienst }} +{{- else -}} +{{ include "app.bayernidAbsenderName" . }} +{{- end -}} +{{- end -}} + +{{- define "app.elsterTransferOperatorNamespace" -}} +{{- required "elsterTransferOperator.namespace must be set" (.Values.elsterTransferOperator).namespace -}} {{- end -}} \ No newline at end of file diff --git a/src/main/helm/templates/deployment.yaml b/src/main/helm/templates/deployment.yaml index 39186e8254794e5d1bbb1ce648979c54978aa33f..5965bbe2744f8d242346762159df225a29178047 100644 --- a/src/main/helm/templates/deployment.yaml +++ b/src/main/helm/templates/deployment.yaml @@ -139,6 +139,10 @@ spec: - name: spring_ssl_bundle_pem_es-root-ca_truststore_certificate value: "/bindings/ca-certificates/es-root-ca.pem" {{- end }} + {{- if ((.Values.ozgcloud).mongodbsearch).enabled }} + - name: ozgcloud_mongodbsearch_enabled + value: {{ quote .Values.ozgcloud.mongodbsearch.enabled }} + {{- end }} {{- with include "app.getCustomList" . }} {{ . | indent 10 }} {{- end }} @@ -174,28 +178,52 @@ spec: - name: grpc_client_bayern-id_negotiationType value: {{ (((.Values.ozgcloud).bayernid).proxy).negotiationType | default "PLAINTEXT" }} - name: ozgcloud_bayernid_absender_name - value: {{ quote (required "ozgcloud.bayernid.absender.name must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).name) }} + value: {{ include "app.bayernidAbsenderName" . }} - name: ozgcloud_bayernid_absender_anschrift value: {{ quote (required "ozgcloud.bayernid.absender.anschrift must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).anschrift) }} - name: ozgcloud_bayernid_absender_dienst - value: {{ quote (required "ozgcloud.bayernid.absender.dienst must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).dienst) }} + value: {{ include "app.bayernidAbsenderDienst" . }} - name: ozgcloud_bayernid_absender_mandant - value: {{ quote (required "ozgcloud.bayernid.absender.mandant must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).mandant) }} + value: {{ include "app.bayernidAbsenderMandant" . }} - name: ozgcloud_bayernid_absender_gemeindeSchluessel value: {{ quote (required "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled" (((.Values.ozgcloud).bayernid).absender).gemeindeSchluessel) }} {{- end }} + {{- if ((.Values.ozgcloud).muk).enabled }} + - name: ozgcloud_muk_sender + value: {{ quote (required "ozgcloud.muk.sender must be set if ozgcloud.muk is enabled" ((.Values.ozgcloud).muk).sender) }} + - name: ozgcloud_muk_server + value: {{ quote (required "ozgcloud.muk.server must be set if ozgcloud.muk is enabled" ((.Values.ozgcloud).muk).server) }} + - name: ozgcloud_muk_password + valueFrom: + secretKeyRef: + name: muk-user-secret + key: password + optional: false + - name: ozgcloud_muk_userName + valueFrom: + secretKeyRef: + name: muk-user-secret + key: login + optional: false + {{- end }} + + + + {{- if ((.Values.ozgcloud).antragraum).enabled }} - name: ozgcloud_antragraum_enabled value: {{ quote .Values.ozgcloud.antragraum.enabled }} - name: ozgcloud_antragraum_url value: {{ quote (required "ozgcloud.antragraum.url must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).url) }} - name: ozgcloud_antragraum_metadataUri - value: {{ quote (required "ozgcloud.antragraum.metadataUri must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).metadataUri) }} + value: "file:/keystore/bayernid/metadata.xml" - name: ozgcloud_antragraum_decryptionPrivateKey value: "file:/keystore/bayernid/bayern-id.key" - name: ozgcloud_antragraum_decryptionCertificate value: "file:/keystore/bayernid/bayern-id.crt" + - name: ozgcloud_antragraum_entityId + value: {{ quote (required "ozgcloud.antragraum.entityId must be set if ozgcloud.antragraum is enabled" ((.Values.ozgcloud).antragraum).entityId) }} {{- end }} {{- if (((.Values.ozgcloud).feature).bescheid).enableDummyDocumentProcessor }} - name: ozgcloud_feature_bescheid_enableDummyDocumentProcessor diff --git a/src/main/helm/templates/elstertransfer_user_cr.yaml b/src/main/helm/templates/elstertransfer_user_cr.yaml new file mode 100644 index 0000000000000000000000000000000000000000..622de75f0eb7c8ff34d7069732d7bd40da1bab8a --- /dev/null +++ b/src/main/helm/templates/elstertransfer_user_cr.yaml @@ -0,0 +1,34 @@ + +# +# Copyright (C) 2022 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. +# +{{- if (((.Values.ozgcloud).muk).enabled) }} +--- +apiVersion: operator.ozgcloud.de/v1 +kind: OzgCloudElsterTransferUser +metadata: + name: {{ include "app.namespace" . }}-etr-user + namespace: {{ include "app.namespace" $ }} +spec: + keep_after_delete: {{ (.Values.elsterTransferOperator).keep_after_delete | default false }} +{{- end -}} \ No newline at end of file diff --git a/src/main/helm/templates/network_policy.yaml b/src/main/helm/templates/network_policy.yaml index 833491d9cb2a70c0fe5ea422034caede5db33fda..c146d1f7ccd78c0f6e611d57d87dfd8610c8dbf6 100644 --- a/src/main/helm/templates/network_policy.yaml +++ b/src/main/helm/templates/network_policy.yaml @@ -119,6 +119,18 @@ spec: podSelector: matchLabels: component: info-manager +{{- end }} +{{- if (.Values.zufiManager).enabled }} + - to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ required "zufiManager.namespace must be set if zufiManager server is enabled" (.Values.zufiManager).namespace }} + podSelector: + matchLabels: + component: zufi-server + ports: + - port: 9090 + protocol: TCP {{- end }} - to: - namespaceSelector: diff --git a/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_create_role.yaml b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_create_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..14e66bc84f9808f7a1c044555305f4a4148e1bf2 --- /dev/null +++ b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_create_role.yaml @@ -0,0 +1,50 @@ +# +# Copyright (C) 2022 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. +# +{{- if ((.Values.ozgcloud).muk).enabled }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-create-role-binding + namespace: {{ include "app.namespace" . }} +subjects: + - kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: {{ include "app.elsterTransferOperatorNamespace" . }} +roleRef: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-create-role + apiGroup: rbac.authorization.k8s.io + +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-create-role + namespace: {{ include "app.namespace" . }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["create"] +{{- end }} diff --git a/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_read_role.yaml b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_read_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..561b89abc4a96334c80a54382887e323dde6fd2f --- /dev/null +++ b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_read_role.yaml @@ -0,0 +1,51 @@ +# +# Copyright (C) 2022 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. +# +{{- if ((.Values.ozgcloud).muk).enabled }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-read-role-binding + namespace: {{ include "app.namespace" . }} +subjects: + - kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: {{ include "app.elsterTransferOperatorNamespace" . }} +roleRef: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-read-role + apiGroup: rbac.authorization.k8s.io + +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-read-role + namespace: {{ include "app.namespace" . }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + resourceNames: ["muk-user-secret"] +{{- end -}} diff --git a/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_update_role.yaml b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_update_role.yaml new file mode 100644 index 0000000000000000000000000000000000000000..584400851f0b3b110cb2d32e619a4b1816cb899d --- /dev/null +++ b/src/main/helm/templates/ozgcloud_elstertransfer_operator_secret_update_role.yaml @@ -0,0 +1,51 @@ +# +# Copyright (C) 2022 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. +# +{{- if ((.Values.ozgcloud).muk).enabled }} +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-update-role-binding + namespace: {{ include "app.namespace" . }} +subjects: + - kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: {{ include "app.elsterTransferOperatorNamespace" . }} +roleRef: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-update-role + apiGroup: rbac.authorization.k8s.io + +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: ozgcloud-elster-transfer-operator-secret-update-role + namespace: {{ include "app.namespace" . }} +rules: + - apiGroups: [""] + resources: ["secrets"] + verbs: ["update"] + resourceNames: ["muk-user-secret"] +{{- end }} diff --git a/src/main/helm/values.yaml b/src/main/helm/values.yaml index 0030593cb94c8706f07c80e614e2d94af12155b7..b32c916cc99d575cd813c30100329f5df65c5f02 100644 --- a/src/main/helm/values.yaml +++ b/src/main/helm/values.yaml @@ -37,7 +37,7 @@ image: tag: latest # [default: latest] env: - # overrideSpringProfiles: "oc,prod" +# overrideSpringProfiles: "oc,prod" ozgcloudAktenzeichen: enabled: false # [default: false] value: de.ozgcloud.vorgang.vorgang.AktenzeichenProviderEA @@ -52,6 +52,7 @@ env: # password: sk9t2dfsadf20@. # username: sh-kiel-vorgang-manager-dev + vorgangmanagerName: vorgang-manager usermanagerName: user-manager @@ -60,4 +61,4 @@ zufiManager: address: dns://zufi-server.zufi:9090 elasticsearch: - certificateSecretName: elasticsearch-certificate + certificateSecretName: elasticsearch-certificate diff --git a/src/test/helm-linter-values.yaml b/src/test/helm-linter-values.yaml index 047b472e2225090f010e1f297a03a7a660062eb1..4ee02232ecd2a9c111ed1a6f0eaf7295f075700c 100644 --- a/src/test/helm-linter-values.yaml +++ b/src/test/helm-linter-values.yaml @@ -27,7 +27,12 @@ ozgcloud: infoManager: address: https://info-manager.my-wonderful-domain.local:9000 + networkPolicy: dnsServerNamespace: test-dns-namespace -imagePullSecret: test-image-pull-secret \ No newline at end of file +imagePullSecret: test-image-pull-secret + +elsterTransferOperator: + namespace: elster-transfer + \ No newline at end of file diff --git a/src/test/helm/deployment_antragraum_test.yaml b/src/test/helm/deployment_antragraum_test.yaml index 7905ec79487182bc1e11c667a1a85528bad162af..0def5958d58429a5889774f8dc0c3cd49daff8cd 100644 --- a/src/test/helm/deployment_antragraum_test.yaml +++ b/src/test/helm/deployment_antragraum_test.yaml @@ -33,13 +33,13 @@ set: ozgcloud: environment: dev tests: - - it: should set antragraum values + - it: should set antragsraum values set: ozgcloud: antragraum: enabled: true url: https://antragraum.address - metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml" + entityId: https://sso.dev.de/realms/by-antragsraum-idp asserts: - contains: path: spec.template.spec.containers[0].env @@ -55,7 +55,7 @@ tests: path: spec.template.spec.containers[0].env content: name: ozgcloud_antragraum_metadataUri - value: "classpath:/bayernid/metadata/bayernid-idp-infra.xml" + value: "file:/keystore/bayernid/metadata.xml" - contains: path: spec.template.spec.containers[0].env content: @@ -82,12 +82,12 @@ tests: ozgcloud: antragraum: enabled: true - metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml" + entityId: https://sso.dev.de/realms/by-antragsraum-idp asserts: - failedTemplate: errorMessage: "ozgcloud.antragraum.url must be set if ozgcloud.antragraum is enabled" - - it: should fail if metadataUri is not set + - it: should fail if entityId is not set set: ozgcloud: antragraum: @@ -95,7 +95,7 @@ tests: url: https://antragraum.address asserts: - failedTemplate: - errorMessage: "ozgcloud.antragraum.metadataUri must be set if ozgcloud.antragraum is enabled" + errorMessage: "ozgcloud.antragraum.entityId must be set if ozgcloud.antragraum is enabled" - it: should set volumeMounts @@ -104,7 +104,7 @@ tests: antragraum: enabled: true url: https://antragraum.address - metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml" + entityId: https://sso.dev.de/realms/by-antragsraum-idp asserts: - contains: path: spec.template.spec.containers[0].volumeMounts @@ -129,7 +129,7 @@ tests: antragraum: enabled: true url: https://antragraum.address - metadataUri: "classpath:/bayernid/metadata/bayernid-idp-infra.xml" + entityId: https://sso.dev.de/realms/by-antragsraum-idp asserts: - contains: path: spec.template.spec.volumes diff --git a/src/test/helm/deployment_bayernid_test.yaml b/src/test/helm/deployment_bayernid_test.yaml index 2a73c2761d442304840198966a1641a74081124a..5b4837870b547f1c80384011bbf8dce8305eacc4 100644 --- a/src/test/helm/deployment_bayernid_test.yaml +++ b/src/test/helm/deployment_bayernid_test.yaml @@ -42,10 +42,8 @@ tests: address: https://proxy.address.local absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" - dienst: "dienst" - mandant: "mandant" + name: "name" gemeindeSchluessel: "gemeindeSchluessel" asserts: - contains: @@ -66,18 +64,18 @@ tests: - contains: path: spec.template.spec.containers[0].env content: - name: ozgcloud_bayernid_absender_dienst - value: "dienst" + name: ozgcloud_bayernid_absender_gemeindeSchluessel + value: "gemeindeSchluessel" - contains: path: spec.template.spec.containers[0].env content: - name: ozgcloud_bayernid_absender_mandant - value: "mandant" + name: ozgcloud_bayernid_absender_dienst + value: "name" - contains: path: spec.template.spec.containers[0].env content: - name: ozgcloud_bayernid_absender_gemeindeSchluessel - value: "gemeindeSchluessel" + name: ozgcloud_bayernid_absender_mandant + value: "name" - contains: path: spec.template.spec.containers[0].env content: @@ -99,8 +97,6 @@ tests: absender: postkorbId: "postkorbId" anschrift: "anschrift" - dienst: "dienst" - mandant: "mandant" gemeindeSchluessel: "gemeindeSchluessel" asserts: - failedTemplate: @@ -115,15 +111,14 @@ tests: address: https://proxy.address.local absender: postkorbId: "postkorbId" - name: "name" - dienst: "dienst" - mandant: "mandant" + name: "name_dienst_mandant" gemeindeSchluessel: "gemeindeSchluessel" asserts: - failedTemplate: errorMessage: "ozgcloud.bayernid.absender.anschrift must be set if ozgcloud.bayernid is enabled" - - it: should fail if absender dienst is not set + + - it: should fail if absender gemeindeSchluessel is not set set: ozgcloud: bayernid: @@ -132,82 +127,86 @@ tests: address: https://proxy.address.local absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" - mandant: "mandant" - gemeindeSchluessel: "gemeindeSchluessel" + name: "name_dienst_mandant" asserts: - failedTemplate: - errorMessage: "ozgcloud.bayernid.absender.dienst must be set if ozgcloud.bayernid is enabled" + errorMessage: "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled" - - it: should fail if absender mandant is not set + - it: should fail if bayernid proxy is enabled but proxy address is not configured set: ozgcloud: bayernid: enabled: true - proxy: - address: https://proxy.address.local absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" - dienst: "dienst" + name: "name_dienst_mandant" gemeindeSchluessel: "gemeindeSchluessel" asserts: - failedTemplate: - errorMessage: "ozgcloud.bayernid.absender.mandant must be set if ozgcloud.bayernid is enabled" + errorMessage: "ozgcloud.bayernid.proxy.address must be set if ozgcloud.bayernid is enabled" - - it: should fail if absender gemeindeSchluessel is not set + - it: should set the bayernid proxy grpc negotiationType set: ozgcloud: bayernid: enabled: true proxy: address: https://proxy.address.local + negotiationType: NOT_DEFAULT absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" - dienst: "dienst" - mandant: "mandant" + name: "name_dienst_mandant" + gemeindeSchluessel: "gemeindeSchluessel" asserts: - - failedTemplate: - errorMessage: "ozgcloud.bayernid.absender.gemeindeSchluessel must be set if ozgcloud.bayernid is enabled" + - contains: + path: spec.template.spec.containers[0].env + content: + name: grpc_client_bayern-id_negotiationType + value: NOT_DEFAULT - - it: should fail if bayernid proxy is enabled but proxy address is not configured + - it: should overwrite with absender name if mandant is set set: ozgcloud: bayernid: enabled: true + proxy: + address: https://proxy.address.local absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" - dienst: "dienst" + name: "name" mandant: "mandant" gemeindeSchluessel: "gemeindeSchluessel" asserts: - - failedTemplate: - errorMessage: "ozgcloud.bayernid.proxy.address must be set if ozgcloud.bayernid is enabled" - - - it: should set the bayernid proxy grpc negotiationType + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_bayernid_enabled + value: "true" + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_bayernid_absender_mandant + value: "mandant" + - it: should overwrite with absender name if dienst is set set: ozgcloud: bayernid: enabled: true proxy: address: https://proxy.address.local - negotiationType: NOT_DEFAULT absender: postkorbId: "postkorbId" - name: "name" anschrift: "anschrift" + name: "name" dienst: "dienst" - mandant: "mandant" gemeindeSchluessel: "gemeindeSchluessel" asserts: - contains: path: spec.template.spec.containers[0].env content: - name: grpc_client_bayern-id_negotiationType - value: NOT_DEFAULT + name: ozgcloud_bayernid_absender_dienst + value: "dienst" diff --git a/src/test/helm/deployment_mongodb_test.yaml b/src/test/helm/deployment_mongodb_test.yaml index 7daf5d3196bd7c34413c8aff2741a9159c3a1fdd..ad8a4cb17c47cdca0b5cc89daf2055ec9a24109e 100644 --- a/src/test/helm/deployment_mongodb_test.yaml +++ b/src/test/helm/deployment_mongodb_test.yaml @@ -93,3 +93,21 @@ tests: content: name: spring_data_mongodb_database value: vorgang-manager-database + + - it: check mongodb full text search disabled + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_mongodbsearch_enabled + value: "true" + + - it: check mongodb full text search enabled + set: + ozgcloud.mongodbsearch.enabled: true + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_mongodbsearch_enabled + value: "true" diff --git a/src/test/helm/deployment_muk_test.yaml b/src/test/helm/deployment_muk_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..aeb0cd59e88df0196712fb836088d1faa2b468ff --- /dev/null +++ b/src/test/helm/deployment_muk_test.yaml @@ -0,0 +1,119 @@ +# +# Copyright (C) 2023 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 muk +release: + name: vorgang-manager + namespace: sh-helm-test +templates: + - templates/deployment.yaml +set: + ozgcloud: + environment: dev + imagePullSecret: test-image-pull-secret +tests: + - it: should set muk values + set: + ozgcloud: + muk: + enabled: true + server: muk.test.ozg.de + sender: "name" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_sender + value: "name" + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_server + value: muk.test.ozg.de + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_userName + valueFrom: + secretKeyRef: + name: muk-user-secret + key: login + optional: false + - contains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_password + valueFrom: + secretKeyRef: + name: muk-user-secret + key: password + optional: false + + - it: should not by default set muk values + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_sender + any: true + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_server + any: true + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_userName + any: true + - notContains: + path: spec.template.spec.containers[0].env + content: + name: ozgcloud_muk_password + any: true + + - it: should fail if sender name is not set + set: + ozgcloud: + muk: + enabled: true + server: muk.test.ozg.de + asserts: + - failedTemplate: + errorMessage: "ozgcloud.muk.sender must be set if ozgcloud.muk is enabled" + + + - it: should fail if muk server is not set + set: + ozgcloud: + muk: + enabled: true + sender: "name" + asserts: + - failedTemplate: + errorMessage: "ozgcloud.muk.server must be set if ozgcloud.muk is enabled" + + + + diff --git a/src/test/helm/deployment_nachrichten_manager_address_test.yaml b/src/test/helm/deployment_nachrichten_manager_address_test.yaml index c473b5dd34e7e75f4132b0ae359f9eb47a042f44..b91b3cc5fe1d814a525fc99464add26e746aa617 100644 --- a/src/test/helm/deployment_nachrichten_manager_address_test.yaml +++ b/src/test/helm/deployment_nachrichten_manager_address_test.yaml @@ -39,4 +39,4 @@ tests: path: spec.template.spec.containers[0].env content: name: ozgcloud_nachrichten-manager_address - value: dns://vorgang-manager.sh-helm-test:9090 + value: vorgang-manager.sh-helm-test:9090 diff --git a/src/test/helm/elster_transfer_user_cr_test.yaml b/src/test/helm/elster_transfer_user_cr_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..0587c112a56c4097ccba6dc6c06cd60349fc66f5 --- /dev/null +++ b/src/test/helm/elster_transfer_user_cr_test.yaml @@ -0,0 +1,103 @@ +# +# Copyright (C) 2022 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: elster transfer user cr +release: + name: vorgang-manager + namespace: by-helm-test +templates: + - templates/elstertransfer_user_cr.yaml +tests: + - it: should contain apiVersion + set: + ozgcloud: + muk: + enabled: true + asserts: + - isAPIVersion: + of: operator.ozgcloud.de/v1 + - it: should contain basic info + set: + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: OzgCloudElsterTransferUser + - isAPIVersion: + of: operator.ozgcloud.de/v1 + - it: should have metadata name + set: + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: metadata.name + value: by-helm-test-etr-user + - it: should have metadata namespace + set: + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: metadata.namespace + value: by-helm-test + - it: should have default spec + set: + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: spec.keep_after_delete + value: false + - it: should set spec correctly + set: + ozgcloud: + muk: + enabled: true + elsterTransferOperator: + keep_after_delete: true + asserts: + - equal: + path: spec.keep_after_delete + value: true + - it: should create cr if enabled + set: + ozgcloud: + muk: + enabled: true + asserts: + - hasDocuments: + count: 1 + + - it: should NOT create cr by default + asserts: + - hasDocuments: + count: 0 + + + \ No newline at end of file diff --git a/src/test/helm/network_policy_test.yaml b/src/test/helm/network_policy_test.yaml index e1fd3a808588f4694753f00501a8f1bb76edbbd3..a304549edc261dee07c46b4cf7579c7285d59095 100644 --- a/src/test/helm/network_policy_test.yaml +++ b/src/test/helm/network_policy_test.yaml @@ -27,21 +27,28 @@ release: namespace: by-helm-test templates: - templates/network_policy.yaml -set: - networkPolicy: - dnsServerNamespace: test-dns-namespace + tests: - it: should match apiVersion + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - isAPIVersion: of: networking.k8s.io/v1 - it: should match kind + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - isKind: of: NetworkPolicy - it: validate metadata + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - equal: path: metadata @@ -50,6 +57,9 @@ tests: namespace: by-helm-test - it: should set policy target matchLabel + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - equal: path: spec.podSelector @@ -59,18 +69,27 @@ tests: - it: should add policyType Egress + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.policyTypes content: Egress - it: should add policyType Ingress + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.policyTypes content: Ingress - it: should add ingress rule for eingangsmanager and alfa + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.ingress @@ -90,6 +109,8 @@ tests: - it: should add ingress rule for antragraum if antragraum is enabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: antragraum: enabled: true @@ -109,6 +130,8 @@ tests: - it: should not add ingress rule for antragraum if antragraum is disabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: antragraum: enabled: false @@ -126,6 +149,8 @@ tests: - it: should throw error if antragraum is enabled but antragraum namespace is not set set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: antragraum: enabled: true @@ -135,6 +160,9 @@ tests: - it: should add egress rule to elasticsearch + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.egress @@ -167,6 +195,9 @@ tests: protocol: TCP - it: should add egress rule to mongodb + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.egress @@ -180,6 +211,9 @@ tests: protocol: TCP - it: should add egress rule to user-manager + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.egress @@ -194,6 +228,8 @@ tests: - it: should add egress rule to nachrichten-bayernid-proxy if bayernid is enabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: bayernid: enabled: true @@ -216,6 +252,8 @@ tests: - it: should not add egress rule to bayernid-proxy if bayernid is disabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: bayernid: enabled: false @@ -236,6 +274,8 @@ tests: - it: should throw error if bayernid-proxy is enabled but bayernid namespace is not set set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: bayernid: enabled: true @@ -245,6 +285,8 @@ tests: - it: should add egress rule to info-manager if antragraum is enabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: antragraum: enabled: true @@ -263,6 +305,8 @@ tests: - it: should not add egress rule to info-manager if antragraum is disabled set: + networkPolicy: + dnsServerNamespace: test-dns-namespace ozgcloud: antragraum: enabled: false @@ -278,8 +322,83 @@ tests: matchLabels: component: info-manager + - it: should add egress rule to zufi server if zufi is enabled + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + zufiManager: + enabled: true + namespace: zufi + asserts: + - contains: + path: spec.egress + content: + to: + - podSelector: + matchLabels: + component: zufi-server + namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: zufi + ports: + - port: 9090 + protocol: TCP + + + - it: should not add egress rule to zufi server if zufi is disabled + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + zufiManager: + enabled: false + namespace: zufi + asserts: + - notContains: + path: spec.egress + content: + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: zufi + podSelector: + matchLabels: + component: zufi-server + any: true + + - it: should throw error if zufi is enabled but zufi namespace is not set + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace + zufiManager: + enabled: true + asserts: + - failedTemplate: + errorMessage: zufiManager.namespace must be set if zufiManager server is enabled + + - it: should not enable zufi netpol by default + set: + zufiManager: + namespace: zufi + networkPolicy: + dnsServerNamespace: test-dns-namespace + asserts: + - notContains: + path: spec.egress + content: + to: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: zufi + podSelector: + matchLabels: + component: zufi-server + any: true + - it: should add egress rule to dns service + set: + networkPolicy: + dnsServerNamespace: test-dns-namespace asserts: - contains: path: spec.egress @@ -301,6 +420,7 @@ tests: - it: add ingress rule local by values set: networkPolicy: + dnsServerNamespace: test-dns-namespace ssoPublicIp: 51.89.117.53/32 additionalIngressConfigGlobal: - from: @@ -318,6 +438,7 @@ tests: - it: add ingress rule global by values set: networkPolicy: + dnsServerNamespace: test-dns-namespace ssoPublicIp: 51.89.117.53/32 additionalIngressConfigLocal: - from: @@ -336,6 +457,7 @@ tests: - it: add egress rules local by values set: networkPolicy: + dnsServerNamespace: test-dns-namespace additionalEgressConfigGlobal: - to: - ipBlock: @@ -367,6 +489,7 @@ tests: - it: add egress rules global by values set: networkPolicy: + dnsServerNamespace: test-dns-namespace additionalEgressConfigLocal: - to: - ipBlock: @@ -396,6 +519,22 @@ tests: set: networkPolicy: disabled: false + dnsServerNamespace: test-dns-namespace + asserts: + - hasDocuments: + count: 1 + - it: test network policy dnsServerNamespace must be set message + set: + networkPolicy: + disabled: false + asserts: + - failedTemplate: + errorMessage: networkPolicy.dnsServerNamespace must be set + + - it: test network policy should be enabled by default + set: + networkPolicy: + dnsServerNamespace: test-dns-server-namespace asserts: - hasDocuments: count: 1 \ No newline at end of file diff --git a/src/test/helm/ozgcloud_elstertransfer_operator_secret_create_role_test.yaml b/src/test/helm/ozgcloud_elstertransfer_operator_secret_create_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..34a6109f7119929c515b06f0e1de9c85e440de55 --- /dev/null +++ b/src/test/helm/ozgcloud_elstertransfer_operator_secret_create_role_test.yaml @@ -0,0 +1,133 @@ +# +# Copyright (C) 2022 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: ElsterTransfer user secret rbac test +release: + name: ozgcloud-elstertransfer-operator + namespace: test-namespace +templates: + - templates/ozgcloud_elstertransfer_operator_secret_create_role.yaml + + +tests: + - it: test RoleBinding metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: RoleBinding + documentIndex: 0 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 0 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-create-role-binding + documentIndex: 0 + + - it: test RoleBinding subject + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: subjects + content: + kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: etr-operator + documentIndex: 0 + - it: test RoleBinding roleRef + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: roleRef + value: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-create-role + apiGroup: rbac.authorization.k8s.io + documentIndex: 0 + + - it: test Role metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: Role + documentIndex: 1 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 1 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-create-role + documentIndex: 1 + + - it: test RoleBinding rules + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: rules + content: + apiGroups: + - "" + resources: + - secrets + verbs: + - create + documentIndex: 1 + - it: test eltertransferOperator namespace must be set msg + set: + ozgcloud: + muk: + enabled: true + asserts: + - failedTemplate: + errorMessage: elsterTransferOperator.namespace must be set + + - it: RBAC not created by default + asserts: + - hasDocuments: + count: 0 \ No newline at end of file diff --git a/src/test/helm/ozgcloud_elstertransfer_operator_secret_read_role_test.yaml b/src/test/helm/ozgcloud_elstertransfer_operator_secret_read_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6d3bf2a10988847c5e425f13def889ea9ef8ea48 --- /dev/null +++ b/src/test/helm/ozgcloud_elstertransfer_operator_secret_read_role_test.yaml @@ -0,0 +1,138 @@ +# +# Copyright (C) 2022 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: ElsterTransfer user secret rbac test +release: + name: ozgcloud-elstertransfer-operator + namespace: test-namespace +templates: + - templates/ozgcloud_elstertransfer_operator_secret_read_role.yaml + + +tests: + - it: test RoleBinding metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: RoleBinding + documentIndex: 0 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 0 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-read-role-binding + documentIndex: 0 + + - it: test RoleBinding subject + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: subjects + content: + kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: etr-operator + documentIndex: 0 + - it: test RoleBinding roleRef + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: roleRef + value: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-read-role + apiGroup: rbac.authorization.k8s.io + documentIndex: 0 + + - it: test Role metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: Role + documentIndex: 1 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 1 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-read-role + documentIndex: 1 + + - it: test RoleBinding rules + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: rules + content: + apiGroups: + - "" + resourceNames: + - muk-user-secret + resources: + - secrets + verbs: + - get + - list + documentIndex: 1 + - it: test eltertransferOperator namespace must be set msg + set: + ozgcloud: + muk: + enabled: true + asserts: + - failedTemplate: + errorMessage: elsterTransferOperator.namespace must be set + documentIndex: 0 + + - it: RBAC not created by default + asserts: + - hasDocuments: + count: 0 + \ No newline at end of file diff --git a/src/test/helm/ozgcloud_elstertransfer_operator_secret_update_role_test.yaml b/src/test/helm/ozgcloud_elstertransfer_operator_secret_update_role_test.yaml new file mode 100644 index 0000000000000000000000000000000000000000..5f28b5cd2104687d7b40103ae741effa0418fda4 --- /dev/null +++ b/src/test/helm/ozgcloud_elstertransfer_operator_secret_update_role_test.yaml @@ -0,0 +1,136 @@ +# +# Copyright (C) 2022 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: ElsterTransfer user secret rbac test +release: + name: ozgcloud-elstertransfer-operator + namespace: test-namespace +templates: + - templates/ozgcloud_elstertransfer_operator_secret_update_role.yaml + + +tests: + - it: test RoleBinding metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: RoleBinding + documentIndex: 0 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 0 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-update-role-binding + documentIndex: 0 + + - it: test RoleBinding subject + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: subjects + content: + kind: ServiceAccount + name: ozgcloud-elster-transfer-operator-service-account + namespace: etr-operator + documentIndex: 0 + - it: test RoleBinding roleRef + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - equal: + path: roleRef + value: + kind: Role + name: ozgcloud-elster-transfer-operator-secret-update-role + apiGroup: rbac.authorization.k8s.io + documentIndex: 0 + + - it: test Role metadata + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - isKind: + of: Role + documentIndex: 1 + - isAPIVersion: + of: rbac.authorization.k8s.io/v1 + documentIndex: 1 + - equal: + path: metadata.name + value: ozgcloud-elster-transfer-operator-secret-update-role + documentIndex: 1 + + - it: test RoleBinding rules + set: + elsterTransferOperator: + namespace: etr-operator + ozgcloud: + muk: + enabled: true + asserts: + - contains: + path: rules + content: + apiGroups: + - "" + resources: + - secrets + verbs: + - update + resourceNames: + - muk-user-secret + documentIndex: 1 + - it: test eltertransferOperator namespace must be set msg + set: + ozgcloud: + muk: + enabled: true + asserts: + - failedTemplate: + errorMessage: elsterTransferOperator.namespace must be set + + - it: RBAC not created by default + asserts: + - hasDocuments: + count: 0 + \ No newline at end of file diff --git a/vorgang-manager-base/pom.xml b/vorgang-manager-base/pom.xml index 1d761fc80a6fe434e581a1c7086173c68a23d140..160b9cedaefd30134a39578102bb70668718cc5e 100644 --- a/vorgang-manager-base/pom.xml +++ b/vorgang-manager-base/pom.xml @@ -6,13 +6,13 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-parent</artifactId> - <version>4.2.0</version> + <version>4.3.1</version> <relativePath /> </parent> <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager-base</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <name>OZG-Cloud Vorgang Manager Base</name> diff --git a/vorgang-manager-command/pom.xml b/vorgang-manager-command/pom.xml index a925adb4f7fd6f221cb12c537a830c964746f155..72cf65259e2e9cf5abcc95c81af4524a39dababb 100644 --- a/vorgang-manager-command/pom.xml +++ b/vorgang-manager-command/pom.xml @@ -4,13 +4,13 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-dependencies</artifactId> - <version>4.2.0</version> + <version>4.3.1</version> <relativePath/> </parent> <groupId>de.ozgcloud.command</groupId> <artifactId>command-manager</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <name>OZG-Cloud Command Manager</name> <properties> diff --git a/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java b/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java index 926ca62da79c8ac64c56bc9fd4d9f0fc59e84aea..31bbfd577e335865cdebef9b37de0c5613c52f18 100644 --- a/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java +++ b/vorgang-manager-command/src/main/java/de/ozgcloud/command/CommandStatus.java @@ -24,5 +24,5 @@ package de.ozgcloud.command; public enum CommandStatus { - PENDING, FINISHED, ERROR, REVOKE_PENDING, REVOKED; + NEW, PENDING, CANCELED, FINISHED, ERROR, REVOKE_PENDING, REVOKED } \ No newline at end of file diff --git a/vorgang-manager-interface/pom.xml b/vorgang-manager-interface/pom.xml index 82b9fd15bf7446722b17111e93d1edb70509123e..69ffc5f94591a8bb502e02468c50e56076fae865 100644 --- a/vorgang-manager-interface/pom.xml +++ b/vorgang-manager-interface/pom.xml @@ -30,13 +30,13 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-dependencies</artifactId> - <version>4.2.0</version> + <version>4.3.1</version> <relativePath/> </parent> <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager-interface</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <name>OZG-Cloud Vorgang Manager gRPC Interface</name> <description>Interface (gRPC) for Vorgang Manager Server</description> diff --git a/vorgang-manager-interface/src/main/protobuf/vorgang.model.proto b/vorgang-manager-interface/src/main/protobuf/vorgang.model.proto index dc8cc1c5bc16b8277a61fd4b7741fc2fe921c924..76157a12e40b638a00ca020bfde221706b1cc3ef 100644 --- a/vorgang-manager-interface/src/main/protobuf/vorgang.model.proto +++ b/vorgang-manager-interface/src/main/protobuf/vorgang.model.proto @@ -197,3 +197,18 @@ message GrpcVorgangQueryExpression { } } +message GrpcCreateCollaborationVorgangRequest { + oneof request { + GrpcCollaborationRequest collaborationRequest = 1; + } +} + +message GrpcCollaborationRequest { + string vorgangId = 1; + int32 collaborationLevel = 2; + string zustaendigeStelle = 3; +} + +message GrpcCreateCollaborationVorgangResponse { + string vorgangId = 1; +} \ No newline at end of file diff --git a/vorgang-manager-interface/src/main/protobuf/vorgang.proto b/vorgang-manager-interface/src/main/protobuf/vorgang.proto index 03e9adb797364be086db096b01e345cbae88fb85..ff73c52cb2a072c011e318fd88000c79300dc48b 100644 --- a/vorgang-manager-interface/src/main/protobuf/vorgang.proto +++ b/vorgang-manager-interface/src/main/protobuf/vorgang.proto @@ -44,6 +44,9 @@ service VorgangService { rpc FinishCreation(GrpcFinishCreationRequest) returns (GrpcFinishCreationResponse) { } + + rpc CreateCollaborationVorgang(GrpcCreateCollaborationVorgangRequest) returns (GrpcCreateCollaborationVorgangResponse) { + } } message GrpcCreateVorgangRequest { diff --git a/vorgang-manager-server/pom.xml b/vorgang-manager-server/pom.xml index 8803d73a38204b69940da7542fdf439e99fe2990..232523b8ffcb6e5ed7960faeac4bcf117e9c6762 100644 --- a/vorgang-manager-server/pom.xml +++ b/vorgang-manager-server/pom.xml @@ -32,13 +32,13 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-parent</artifactId> - <version>4.3.0</version> + <version>4.3.1</version> <relativePath /> </parent> <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager-server</artifactId> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <name>OZG-Cloud Vorgang Manager Server</name> <description>Server Implementierung des VorgangManagers</description> @@ -50,14 +50,15 @@ <spring-boot.build-image.imageName>docker.ozg-sh.de/vorgang-manager:build-latest</spring-boot.build-image.imageName> <ozgcloud.license.version>1.3.0</ozgcloud.license.version> - <zufi-manager-interface.version>1.0.0-SNAPSHOT</zufi-manager-interface.version> + <zufi-manager-interface.version>1.0.0</zufi-manager-interface.version> <user-manager-interface.version>2.1.0</user-manager-interface.version> - <bescheid-manager.version>1.13.0</bescheid-manager.version> - <processor-manager.version>0.4.0</processor-manager.version> - <nachrichten-manager.version>2.9.0</nachrichten-manager.version> + <bescheid-manager.version>1.17.0-SNAPSHOT</bescheid-manager.version> + <processor-manager.version>0.4.1</processor-manager.version> + <nachrichten-manager.version>2.12.0-SNAPSHOT</nachrichten-manager.version> <ozgcloud-starter.version>0.10.0</ozgcloud-starter.version> - <notification-manager.version>2.8.0</notification-manager.version> + <notification-manager.version>2.10.0-SNAPSHOT</notification-manager.version> + <collaboration-manager.version>0.2.0-SNAPSHOT</collaboration-manager.version> <zip.version>2.11.1</zip.version> <jsoup.version>1.15.3</jsoup.version> @@ -151,6 +152,11 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>de.ozgcloud.collaboration</groupId> + <artifactId>collaboration-manager-server</artifactId> + <version>${collaboration-manager.version}</version> + </dependency> <!-- Spring --> <dependency> @@ -369,6 +375,7 @@ <env> <BPE_DELIM_JAVA_TOOL_OPTIONS xml:space="preserve"> </BPE_DELIM_JAVA_TOOL_OPTIONS> <BPE_APPEND_JAVA_TOOL_OPTIONS>-Dfile.encoding=UTF-8</BPE_APPEND_JAVA_TOOL_OPTIONS> + <BPE_APPEND_LC_ALL>en_US.UTF-8</BPE_APPEND_LC_ALL> </env> </image> <profiles> diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java index 62811097900c5e6daee5d06831962184579d63b8..3fe994644fae00480fdda6814cae0dcbec5abb5a 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangManagerCallContextProvider.java @@ -31,6 +31,7 @@ import org.springframework.stereotype.Component; import de.ozgcloud.apilib.common.callcontext.CallContext; import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; import de.ozgcloud.apilib.user.OzgCloudUserId; +import de.ozgcloud.vorgang.callcontext.CallContextUser; import de.ozgcloud.vorgang.callcontext.CurrentUserService; import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor; import lombok.RequiredArgsConstructor; @@ -45,11 +46,11 @@ public class VorgangManagerCallContextProvider implements OzgCloudCallContextPro @Override public CallContext provideContext() { var callContextBuilder = CallContext.builder().clientName(VorgangManagerClientCallContextAttachingInterceptor.VORGANG_MANAGER_CLIENT_NAME); - getUserId().ifPresent(callContextBuilder::userId); + findUserId().ifPresent(callContextBuilder::userId); return callContextBuilder.build(); } - Optional<OzgCloudUserId> getUserId() { - return currentUserService.getUser().getUserId().map(OzgCloudUserId::from); + Optional<OzgCloudUserId> findUserId() { + return currentUserService.findUser().flatMap(CallContextUser::getUserId).map(OzgCloudUserId::from); } } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java index 56d602a31ebc0a372e80cb958e604f85c5e7fef1..9fdab580da21c2a57572b28612b428f8a198fbd7 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/VorgangProcessorConfiguration.java @@ -7,29 +7,15 @@ import de.ozgcloud.apilib.common.callcontext.OzgCloudCallContextProvider; import de.ozgcloud.apilib.user.GrpcOzgCloudUserProfileService; import de.ozgcloud.apilib.user.OzgCloudUserProfileService; import de.ozgcloud.apilib.user.UserProfileMapper; -import de.ozgcloud.apilib.vorgang.OzgCloudVorgangService; -import de.ozgcloud.apilib.vorgang.grpc.GrpcOzgCloudVorgangService; -import de.ozgcloud.apilib.vorgang.grpc.OzgCloudVorgangMapper; -import de.ozgcloud.apilib.vorgang.grpc.OzgCloudVorgangStubMapper; import de.ozgcloud.user.grpc.userprofile.UserProfileServiceGrpc.UserProfileServiceBlockingStub; -import de.ozgcloud.vorgang.vorgang.VorgangServiceGrpc.VorgangServiceBlockingStub; import net.devh.boot.grpc.client.inject.GrpcClient; @Configuration class VorgangProcessorConfiguration { - @GrpcClient("vorgang-manager") - private VorgangServiceBlockingStub vorgangServiceStub; - @GrpcClient("user-manager") private UserProfileServiceBlockingStub userProfileServiceGrpc; - @Bean - OzgCloudVorgangService ozgCloudVorgangService(OzgCloudVorgangMapper mapper, OzgCloudVorgangStubMapper stubMapper, - OzgCloudCallContextProvider contextProvider) { - return new GrpcOzgCloudVorgangService(vorgangServiceStub, mapper, stubMapper, contextProvider); - } - @Bean OzgCloudUserProfileService ozgCloudUserProfileService(UserProfileMapper mapper, OzgCloudCallContextProvider contextProvider) { return new GrpcOzgCloudUserProfileService(userProfileServiceGrpc, mapper, contextProvider); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java index 60d9b646de80c056a7f4df16a06c69861ab571e8..0fcd73c898e729dc7d06ef4f9f862976e4f60676 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepository.java @@ -27,6 +27,10 @@ import java.util.Map; interface VorgangAttachedItemCustomRepository { + void update(String itemId, long version, Map<String, Object> properyMap); + + void forceUpdate(String itemId, Map<String, Object> propertyMap); + void patch(String itemId, long version, Map<String, Object> propertyMap); void forcePatch(String itemId, Map<String, Object> propertyMap); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java index 66c828d24149e338c647d9eff9d1f7e34347c033..21a29bfe93e5821cf99dceee801fbc234908a6a4 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemCustomRepositoryImpl.java @@ -60,23 +60,41 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI } boolean doVorgangAttachedItemExist(String itemId) { - return mongoOperations.exists(query(isId(itemId)), VorgangAttachedItem.COLLECTION_NAME); + return mongoOperations.exists(createQueryById(itemId), VorgangAttachedItem.class); + } + + @Override + public void update(String itemId, long version, Map<String, Object> properyMap) { + var updateResult = mongoOperations.updateFirst(query(byIdAndVersion(itemId, version)), buildUpdateForItemAndVersion(properyMap), + VorgangAttachedItem.class); + collisionVerifier.verify(updateResult, itemId); + } + + @Override + public void forceUpdate(String itemId, Map<String, Object> propertyMap) { + mongoOperations.updateFirst(createQueryById(itemId), buildUpdateForItemAndVersion(propertyMap), VorgangAttachedItem.class); + } + + private Update buildUpdateForItemAndVersion(Map<String, Object> propertyMap) { + var item = new Document(); + item.putAll(propertyMap); + return new Update().set(VorgangAttachedItem.FIELDNAME_ITEM, item).inc(VorgangAttachedItem.FIELDNAME_VERSION, 1); } @Override public void patch(String itemId, long version, Map<String, Object> propertyMap) { - mongoOperations.updateFirst( + var patchedResult = mongoOperations.updateFirst( query(byIdAndVersion(itemId, version)), - createUpdateForItemAndVersion(propertyMap), + createPatchUpdateForItemAndVersion(propertyMap), VorgangAttachedItem.class); - + collisionVerifier.verify(patchedResult, itemId); } @Override public void forcePatch(String itemId, Map<String, Object> propertiesMap) { mongoOperations.updateFirst( createQueryById(itemId), - createUpdateForItemAndVersion(propertiesMap), + createPatchUpdateForItemAndVersion(propertiesMap), VorgangAttachedItem.class); } @@ -100,12 +118,12 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI return where(VorgangAttachedItem.FIELDNAME_IS_DELETED).is(false); } - private Update createUpdateForItemAndVersion(Map<String, Object> propertyMap) { + private Update createPatchUpdateForItemAndVersion(Map<String, Object> propertyMap) { return Update.fromDocument(buildItemPatchDocument(propertyMap)).inc(VorgangAttachedItem.FIELDNAME_VERSION, 1); } private Document buildItemPatchDocument(Map<String, Object> propertiesMap) { - Map<String, Object> resultMap = new HashMap<>(propertiesMap.size()); + var resultMap = new HashMap<String, Object>(propertiesMap.size()); propertiesMap.forEach((k, v) -> resultMap.put("%s.%s".formatted(VorgangAttachedItem.FIELDNAME_ITEM, k), v)); return new Document(resultMap); } @@ -122,5 +140,4 @@ public class VorgangAttachedItemCustomRepositoryImpl implements VorgangAttachedI Update.update(VorgangAttachedItem.FIELDNAME_IS_DELETED, deleted), VorgangAttachedItem.COLLECTION_NAME); collisionVerifier.verify(updateResult, itemId); } - } \ No newline at end of file diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListener.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListener.java index b32e3bd29b0bcaa5dfd0378b1f1f1406092ceab0..cf991372cac1e53ef918bf360128a4939f9ccb6c 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListener.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListener.java @@ -23,28 +23,32 @@ */ package de.ozgcloud.vorgang.attached_item; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.Predicate; +import jakarta.validation.ValidationException; + import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; -import jakarta.validation.ValidationException; - import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandCreatedEvent; import de.ozgcloud.command.CommandFailedEvent; -import de.ozgcloud.command.CommandRevokeFailedEvent; -import de.ozgcloud.command.CommandRevokedEvent; import de.ozgcloud.command.RevokeCommandEvent; +import de.ozgcloud.vorgang.command.CommandService; import de.ozgcloud.vorgang.command.Order; +import de.ozgcloud.vorgang.command.PersistedCommand; import de.ozgcloud.vorgang.vorgang.VorgangDeletedEvent; +import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @Component +@RequiredArgsConstructor @Log4j2 class VorgangAttachedItemEventListener { @@ -58,42 +62,52 @@ class VorgangAttachedItemEventListener { public static final Predicate<Command> IS_PATCH_ITEM_ORDER = command -> Order.PATCH_ATTACHED_ITEM.isMeant(command.getOrder()); public static final Predicate<Command> IS_DELETE_ITEM_ORDER = command -> Order.DELETE_ATTACHED_ITEM.isMeant(command.getOrder()); - @Autowired - private VorgangAttachedItemService service; - @Autowired - private VorgangAttachedItemMapper mapper; - @Autowired - private ApplicationEventPublisher publisher; + private final VorgangAttachedItemService service; + private final VorgangAttachedItemMapper mapper; + + private final ApplicationEventPublisher publisher; + + private final CommandService commandService; @EventListener(condition = IS_CREATE_ITEM_ORDER_CONDITION) public void createItem(CommandCreatedEvent event) { var command = event.getSource(); validateCreateCommand(command); - var item = mapper.fill(command.getBodyObject()); - service.create(command.getId(), item.toBuilder().id(null).version(0).build()); + var item = mapper.fill(command.getBodyObject()).toBuilder().id(null).version(0).build(); + service.create(command.getId(), item); } - private void validateCreateCommand(Command command) { + void validateCreateCommand(Command command) { if (!StringUtils.equals(command.getRelationId(), command.getVorgangId())) { throw new ValidationException("Command invalid creation command: vorgangId and relationId must be equals"); } - } @EventListener(condition = IS_UPDATE_ITEM_ORDER_CONDITION) public void updateItem(CommandCreatedEvent event) { var command = event.getSource(); - var itemBuilder = mapper.fill(command.getBodyObject()) - .toBuilder().id(command.getRelationId()); + var filledItem = mapper.fill(command.getBodyObject()).toBuilder().id(command.getRelationId()).build(); + + setCommandPreviousState(command.getId(), getVorgangAttachedItem(command.getRelationId()).getItem()); - if (Objects.nonNull(command.getRelationVersion())) { - itemBuilder.version(command.getRelationVersion()); - service.update(command.getId(), itemBuilder.build()); + doUpdate(command, filledItem); + } + + private void doUpdate(Command command, VorgangAttachedItem filledItem) { + if (isVersionSet(command)) { + service.update(command.getId(), filledItem.toBuilder().version(command.getRelationVersion()).build()); } else { - service.forceUpdate(command.getId(), itemBuilder.build()); + service.forceUpdate(command.getId(), filledItem.getId(), filledItem.getItem()); } + } + + private boolean isVersionSet(Command command) { + return Objects.nonNull(command.getRelationVersion()) && command.getRelationVersion() > 0; + } + VorgangAttachedItem getVorgangAttachedItem(String id) { + return service.getById(id); } @EventListener(condition = IS_PATCH_ITEM_ORDER_CONDITION) @@ -101,9 +115,20 @@ class VorgangAttachedItemEventListener { var command = event.getSource(); var item = mapper.fill(command.getBodyObject()); + setCommandPreviousState(command.getId(), getExistingItemMapEntries(command.getRelationId(), item.getItem().keySet())); + service.patch(command, item.getItem()); } + Map<String, Object> getExistingItemMapEntries(String id, Set<String> keys) { + return getVorgangAttachedItem(id).getItem().entrySet().stream().filter(entry -> keys.contains(entry.getKey())) + .collect(HashMap::new, (itemMap, entry) -> itemMap.put(entry.getKey(), entry.getValue()), HashMap::putAll); + } + + void setCommandPreviousState(String commandId, Map<String, Object> itemMap) { + commandService.setPreviousState(commandId, itemMap); + } + @EventListener(condition = IS_DELETE_ITEM_ORDER_CONDITION) public void deleteItem(CommandCreatedEvent event) { var command = event.getSource(); @@ -129,16 +154,31 @@ class VorgangAttachedItemEventListener { } } + @EventListener(condition = IS_CREATE_ITEM_ORDER_CONDITION) + public void onRevokeCreateItem(RevokeCommandEvent event) { + service.revokeCreate(event.getSource()); + } + @EventListener(condition = IS_DELETE_ITEM_ORDER_CONDITION) public void onRevokeDeleteItem(RevokeCommandEvent event) { + service.revokeDelete(event.getSource()); + } + + @EventListener(condition = IS_UPDATE_ITEM_ORDER_CONDITION) + public void onRevokeUpdateItem(RevokeCommandEvent event) { var command = event.getSource(); - try { - service.unmarkAsDeleteByIdAndVersion(command.getRelationId(), command.getRelationVersion()); - publisher.publishEvent(new CommandRevokedEvent(command)); - } catch (RuntimeException e) { - LOG.error("Revoke of delete item command failed", e); - publisher.publishEvent(new CommandRevokeFailedEvent(command.getId(), e.getMessage())); - } + var persistedCommand = getPersistedCommand(command.getId()); + service.revokeUpdate(command, persistedCommand.getPreviousState()); + } + + @EventListener(condition = IS_PATCH_ITEM_ORDER_CONDITION) + public void onRevokePatchItem(RevokeCommandEvent event) { + var command = event.getSource(); + var persistedCommand = getPersistedCommand(command.getId()); + service.revokePatch(command, persistedCommand.getPreviousState()); } + private PersistedCommand getPersistedCommand(String commandId) { + return (PersistedCommand) commandService.getById(commandId); + } } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapper.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapper.java index b4ebb33584da5dd2745b959dbcee27d8a3fc3bdd..74edfabb0549cae70fba2d7443ba3d9ed3008c20 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapper.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapper.java @@ -24,7 +24,6 @@ package de.ozgcloud.vorgang.attached_item; import java.util.Map; -import java.util.Optional; import org.apache.commons.collections.MapUtils; import org.mapstruct.Mapper; @@ -41,25 +40,18 @@ public interface VorgangAttachedItemMapper { public static final String PROPERTY_ITEM_NAME = "itemName"; public static final String PROPERTY_ITEM = "item"; - @SuppressWarnings("unchecked") default VorgangAttachedItem fill(Map<String, Object> propertyMap) { - return VorgangAttachedItem.builder() - .id(extractIdFromItem(propertyMap)) .client(MapUtils.getString(propertyMap, PROPERTY_CLIENT)) .vorgangId(MapUtils.getString(propertyMap, PROPERTY_VORGANG_ID)) .itemName(MapUtils.getString(propertyMap, PROPERTY_ITEM_NAME)) - .item((Map<String, Object>) propertyMap.get(PROPERTY_ITEM)) + .item(getPropertyItem(propertyMap)) .version(MapUtils.getLong(propertyMap, PROPERTY_VERSION, 0L)) .build(); } - // TODO remove mapping of id from Map @SuppressWarnings("unchecked") - default String extractIdFromItem(Map<String, Object> propertyMap) { - return (String) Optional.ofNullable(propertyMap.get(PROPERTY_ID)) - .orElseGet(() -> ((Map<String, Object>) propertyMap.get(PROPERTY_ITEM)).get(PROPERTY_ID)); - + default Map<String, Object> getPropertyItem(Map<String, Object> propertyMap) { + return (Map<String, Object>) propertyMap.get(PROPERTY_ITEM); } - } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java index cf25401a6607aa4468ddad30e456b9f35924904f..06aafc83380e42ac5d8ab5bdbd82ac5ff5f9914f 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemService.java @@ -31,25 +31,28 @@ import java.util.stream.Stream; import jakarta.validation.Valid; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import de.ozgcloud.command.Command; +import de.ozgcloud.command.CommandRevokeFailedEvent; +import de.ozgcloud.command.CommandRevokedEvent; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; -@Service +@Log4j2 @Validated +@RequiredArgsConstructor +@Service public class VorgangAttachedItemService { static final boolean NOT_DELETED = false; - @Autowired - private VorgangAttachedItemRepository repository; - @Autowired - private ApplicationEventPublisher publisher; + private final VorgangAttachedItemRepository repository; + private final ApplicationEventPublisher publisher; public VorgangAttachedItem create(@NonNull String commandId, @Valid VorgangAttachedItem item) { var saved = repository.save(prepareForCreation(item)); @@ -74,13 +77,21 @@ public class VorgangAttachedItemService { } public void update(String commandId, VorgangAttachedItem item) { - repository.patch(item.getId(), item.getVersion(), item.getItem()); + doUpdate(item.getId(), item.getVersion(), item.getItem()); publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId)); } - public void forceUpdate(String commandId, VorgangAttachedItem item) { - repository.forcePatch(item.getId(), item.getItem()); + public void revokeUpdate(Command command, Map<String, Object> itemMap) { + handleRevoke(command, () -> doUpdate(command.getRelationId(), command.getRelationVersion() + 1, itemMap)); + } + + void doUpdate(String id, long version, Map<String, Object> body) { + repository.update(id, version, body); + } + + public void forceUpdate(String commandId, String itemId, Map<String, Object> itemMap) { + repository.forceUpdate(itemId, itemMap); publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(commandId)); } @@ -115,11 +126,19 @@ public class VorgangAttachedItemService { } public void patch(Command command, Map<String, Object> body) { - repository.patch(command.getRelationId(), command.getRelationVersion(), body); + doPatch(command, command.getRelationVersion(), body); publisher.publishEvent(new VorgangAttachedItemUpdatedEvent(command.getId())); } + public void revokePatch(Command command, Map<String, Object> itemMap) { + handleRevoke(command, () -> doPatch(command, command.getRelationVersion() + 1, itemMap)); + } + + void doPatch(Command command, Long relationVersion, Map<String, Object> body) { + repository.patch(command.getRelationId(), relationVersion, body); + } + public VorgangAttachedItem getById(String id) { var attachedItem = repository.findByIdAndDeleted(id, NOT_DELETED).orElseThrow(() -> new NotFoundException(VorgangAttachedItem.class, id)); @@ -136,12 +155,33 @@ public class VorgangAttachedItemService { repository.updateDeleted(true, itemId, version); } - public void unmarkAsDeleteByIdAndVersion(String itemId, long version) { - repository.updateDeleted(false, itemId, version); - } - public void deleteByVorgangId(String vorgangId) { repository.deleteByVorgangId(vorgangId); } + public void revokeCreate(Command command) { + handleRevoke(command, () -> delete(command.getRelationId(), command.getRelationVersion())); + } + + void delete(String id, long version) { + repository.delete(id, version); + } + + public void revokeDelete(Command command) { + handleRevoke(command, () -> unmarkAsDeleteByIdAndVersion(command.getRelationId(), command.getRelationVersion())); + } + + void unmarkAsDeleteByIdAndVersion(String itemId, long version) { + repository.updateDeleted(false, itemId, version); + } + + void handleRevoke(Command command, Runnable runnable) { + try { + runnable.run(); + publisher.publishEvent(new CommandRevokedEvent(command)); + } catch (RuntimeException e) { + LOG.error("Revoke item command failed(order: {})", command.getOrder(), e); + publisher.publishEvent(new CommandRevokeFailedEvent(command.getId(), e.getMessage())); + } + } } \ No newline at end of file diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..a4dcbf254763851b9dc6980bc0102d0600827840 --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationRepository.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.stereotype.Repository; + +import de.ozgcloud.vorgang.vorgang.Vorgang; +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +class CollaborationRepository { + + private final MongoOperations mongoOperations; + + public Vorgang save(Vorgang vorgang) { + return mongoOperations.save(vorgang); + } + +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationService.java new file mode 100644 index 0000000000000000000000000000000000000000..763856b36699f916425c90f13b512ffae5982bce --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CollaborationService.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import de.ozgcloud.vorgang.vorgang.Vorgang; +import de.ozgcloud.vorgang.vorgang.VorgangHead; +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class CollaborationService { + + private final CollaborationRepository collaborationRepository; + private final ApplicationEventPublisher publisher; + + public Vorgang createCollaborationVorgang(CreateCollaborationVorgangRequest request) { + var vorgang = collaborationRepository.save(buildCollaborationVorgang(request)); + // TODO: wieder aktivieren wenn OZG-6380 implementiert ist + // publisher.publishEvent(new VorgangCreatedEvent(vorgang.getId())); + return vorgang; + } + + Vorgang buildCollaborationVorgang(CreateCollaborationVorgangRequest request) { + var srcVorgang = request.getVorgang(); + return Vorgang.builder() + .name(srcVorgang.getName()) + .nummer(srcVorgang.getNummer()) + .formEngineName(srcVorgang.getFormEngineName()) + .aktenzeichen(srcVorgang.getAktenzeichen()) + .header(getHeader(request)) + .eingangs(srcVorgang.getEingangs()) + .build(); + } + + VorgangHead getHeader(CreateCollaborationVorgangRequest request) { + return request.getVorgang().getHeader().toBuilder() + .collaborationLevel(request.getCollaborationLevel()) + .organisationsEinheitId(request.getZustaendigeStelle()) + .build(); + } +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangBadRequestException.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangBadRequestException.java new file mode 100644 index 0000000000000000000000000000000000000000..b2d35ef9553325cc6b97b5be1deb09b0ef315b0d --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangBadRequestException.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import de.ozgcloud.vorgang.common.errorhandling.FunctionalException; + +public class CreateCollaborationVorgangBadRequestException extends FunctionalException { + + public CreateCollaborationVorgangBadRequestException(String message) { + super(message, () -> "CREATE_COLLABORATION_VORGANG.BAD_REQUEST"); + } + +} diff --git a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequest.java similarity index 71% rename from bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java rename to vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequest.java index 1b0011890ab72654176613f9dfd31ec1eb41fdc8..44f61fed6d4ea63527907176ab79f790fdc534d0 100644 --- a/bescheid-manager/src/main/java/de/ozgcloud/bescheid/BescheidFeatureProperties.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequest.java @@ -21,21 +21,17 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -package de.ozgcloud.bescheid; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; +package de.ozgcloud.vorgang.collaboration; +import de.ozgcloud.vorgang.vorgang.Vorgang; +import lombok.Builder; import lombok.Getter; -import lombok.Setter; -@Configuration -@ConfigurationProperties(prefix = "ozgcloud.feature.bescheid") +@Builder(toBuilder = true) @Getter -@Setter -public class BescheidFeatureProperties { - - private boolean storeAsDocument = false; +public class CreateCollaborationVorgangRequest { - private boolean kielHackathonRoute = false; + private Vorgang vorgang; + private int collaborationLevel; + private String zustaendigeStelle; } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapper.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..28c23c3d257a9bc371fcd54dd1eaed57cd2166a7 --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapper.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; + +import de.ozgcloud.vorgang.vorgang.GrpcCollaborationRequest; + +@Mapper(unmappedTargetPolicy = ReportingPolicy.WARN) +public interface CreateCollaborationVorgangRequestMapper { + + @Mapping(target = "vorgang", ignore = true) + CreateCollaborationVorgangRequest mapFrom(GrpcCollaborationRequest grpcCollaborationRequest); +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java index 6222d4569a6002727397d8df8c4a7b185f711219..a9f57bac1d65ca9aba3af9671ab6d2efb2948558 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandRepository.java @@ -35,15 +35,21 @@ import java.util.Optional; import java.util.stream.Stream; import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.ArrayUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.FindAndModifyOptions; import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.aggregation.Aggregation; +import org.springframework.data.mongodb.core.aggregation.ComparisonOperators.Eq; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch; +import org.springframework.data.mongodb.core.aggregation.ConditionalOperators.Switch.CaseOperator; +import org.springframework.data.mongodb.core.aggregation.SetOperation; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update; +import org.springframework.data.mongodb.core.query.UpdateDefinition; import org.springframework.stereotype.Repository; -import com.google.common.collect.ObjectArrays; - import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandStatus; @@ -51,7 +57,8 @@ import de.ozgcloud.command.CommandStatus; class CommandRepository { private static final String MONGODB_ID = "id"; - private static final String MONGODB_STATUS = "status"; + static final String MONGODB_STATUS = "status"; + private static final String MONGODB_REFERENCE_STATUS = "$status"; private static final String MONGODB_FINISHED_AT = "finishedAt"; private static final String MONGODB_RELATION_VERSION = "relationVersion"; private static final String MONGODB_ERROR_MSG = "errorMessage"; @@ -62,8 +69,8 @@ class CommandRepository { private static final String MONGODB_ASSIGNED_TO = "body.assignedTo"; private static final String MONGODB_CREATED_RESOURCE = "createdResource"; private static final String MONGODB_PARENT_ID = PersistedCommand.FIELD_BODY_OBJECT + "." + PersistedCommand.PROPERTY_PARENT_ID; - private static final String MONGODB_COMPLETE_IF_SUBS_COMPLETED = - PersistedCommand.FIELD_BODY_OBJECT + "." + PersistedCommand.PROPERTY_COMPLETE_IF_SUBS_COMPLETED; + private static final String MONGODB_COMPLETE_IF_SUBS_COMPLETED = PersistedCommand.FIELD_BODY_OBJECT + "." + + PersistedCommand.PROPERTY_COMPLETE_IF_SUBS_COMPLETED; private static final String MONGODB_CLASS = "_class"; @Autowired @@ -101,6 +108,10 @@ class CommandRepository { Command.class); } + public Command updateCommandStatusAndReturnPrevious(String commandId, CommandStatus status) { + return mongoOperations.findAndModify(queryById(commandId), new Update().set(MONGODB_STATUS, status), Command.class); + } + void setErrorMessage(String commandId, String errorMsg) { mongoOperations.updateFirst(queryById(commandId), new Update() @@ -119,7 +130,7 @@ class CommandRepository { Command.class); } - Optional<Command> getById(String commandId) { + Optional<Command> findById(String commandId) { return Optional.ofNullable(mongoOperations.findById(commandId, Command.class)); } @@ -195,12 +206,9 @@ class CommandRepository { mongoOperations.updateFirst(queryById(commandId), update, Command.class); } - public Optional<String> getNotFailedParentId(String commandId) { - return getParentId(commandId) - .map(this::queryNotFailedCommandById) - .map(this::includeIdAndClass) - .flatMap(this::findOne) - .map(Command::getId); + public boolean isCommandFailed(String commandId) { + return mongoOperations.exists(query(new Criteria().andOperator(criteriaById(commandId), where(MONGODB_STATUS).is(CommandStatus.ERROR))), + Command.class); } public Optional<String> getParentId(String commandId) { @@ -209,10 +217,6 @@ class CommandRepository { return findOne(query).map(Command::getBodyObject).map(this::getParentIdProperty); } - private Query queryById(String commandId) { - return query(criteriaById(commandId)); - } - private Criteria criteriaById(String commandId) { return where(MONGODB_ID).is(commandId); } @@ -221,12 +225,6 @@ class CommandRepository { return MapUtils.getString(body, PersistedCommand.PROPERTY_PARENT_ID); } - Query queryNotFailedCommandById(String commandId) { - return query(new Criteria().andOperator( - where(MONGODB_ID).is(commandId), - where(MONGODB_STATUS).ne(CommandStatus.ERROR))); - } - Optional<Command> findOne(Query query) { return Optional.ofNullable(mongoOperations.findOne(query, Command.class)); } @@ -243,19 +241,55 @@ class CommandRepository { return mongoOperations.exists(buildQueryNotInStatus(parentId, CommandStatus.FINISHED, CommandStatus.REVOKED), Command.class); } - public Stream<String> findNotFailedSubCommandIds(String parentId) { - return mongoOperations.stream(includeIdAndClass(buildQueryNotInStatus(parentId, CommandStatus.ERROR)), Command.class).map(Command::getId); - } - - private Query buildQueryNotInStatus(String parentId, CommandStatus status, CommandStatus... statuses) { - Object[] excludeStatuses = ObjectArrays.concat(status, statuses); + Query buildQueryNotInStatus(String parentId, CommandStatus status, CommandStatus... statuses) { + Object[] excludeStatuses = ArrayUtils.add(statuses, status); return query(new Criteria().andOperator( - where(MONGODB_PARENT_ID).is(parentId), + buildIsParentIdCriteria(parentId), where(MONGODB_STATUS).nin(excludeStatuses))); } + public Stream<String> findSubCommandIds(String parentId) { + return mongoOperations.stream(includeIdAndClass(query(buildIsParentIdCriteria(parentId))), Command.class).map(Command::getId); + } + + private Criteria buildIsParentIdCriteria(String parentId) { + return where(MONGODB_PARENT_ID).is(parentId); + } + Query includeIdAndClass(Query query) { query.fields().include(MONGODB_CLASS, MONGODB_ID); return query; } + + public Command setRevokeStatus(String id) { + return mongoOperations.findAndModify(queryById(id), buildUpdateStatusRevoke(), FindAndModifyOptions.options().returnNew(true), Command.class); + } + + private UpdateDefinition buildUpdateStatusRevoke() { + var switchOperation = Switch.switchCases( + CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.NEW)).then(CommandStatus.CANCELED), + CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.PENDING)).then(CommandStatus.REVOKE_PENDING), + CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.FINISHED)).then(CommandStatus.REVOKE_PENDING)) + .defaultTo(MONGODB_REFERENCE_STATUS); + return Aggregation.newUpdate(SetOperation.set(MONGODB_STATUS).toValue(switchOperation)); + } + + public Optional<Command> setRevokeStatusIfNotPending(String id) { + var updateCommand = mongoOperations.findAndModify(queryById(id), buildUpdateStatusRevokeIfNotPending(), + FindAndModifyOptions.options().returnNew(true), Command.class); + return Optional.ofNullable(updateCommand); + } + + private Query queryById(String commandId) { + return query(criteriaById(commandId)); + } + + private UpdateDefinition buildUpdateStatusRevokeIfNotPending() { + var switchOperation = Switch.switchCases( + CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.NEW)).then(CommandStatus.CANCELED), + CaseOperator.when(Eq.valueOf(MONGODB_STATUS).equalToValue(CommandStatus.FINISHED)).then(CommandStatus.REVOKE_PENDING) + ).defaultTo(MONGODB_REFERENCE_STATUS); + return Aggregation.newUpdate(SetOperation.set(MONGODB_STATUS).toValue(switchOperation)); + } + } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java index 9b2e19c7f3172cea9a1dcc813bcc4e37774f7793..2f661a37b47f0da8143fdd5332e8385da4ecb1e5 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/CommandService.java @@ -33,11 +33,9 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.UUID; -import java.util.function.Consumer; import java.util.stream.Stream; import org.apache.commons.collections4.MapUtils; -import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; @@ -55,12 +53,11 @@ import de.ozgcloud.vorgang.callcontext.CallContextUser; import de.ozgcloud.vorgang.callcontext.CurrentUserService; import de.ozgcloud.vorgang.callcontext.User; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; +import de.ozgcloud.vorgang.common.errorhandling.RevokeFailedException; import lombok.NonNull; -import lombok.extern.log4j.Log4j2; @Service @Validated -@Log4j2 public class CommandService { @Autowired @@ -109,10 +106,6 @@ public class CommandService { return command; } - public Optional<Command> findCommand(String commandId) { - return repository.getById(commandId); - } - public Stream<Command> findFinishedVorgangLoeschenCommandsOlderThen(@NonNull ZonedDateTime createdAfter) { return repository.findCommands(Order.VORGANG_LOESCHEN, CommandStatus.FINISHED, createdAfter); } @@ -122,62 +115,90 @@ public class CommandService { } public void setCommandFinished(String commandId, String createdResource) { - Optional.ofNullable(createdResource).ifPresentOrElse( - resource -> repository.finishCommand(commandId, resource), + var command = getById(commandId); + if (shouldRevoke(command)) { + repository.setRevokeStatus(commandId); + publishRevokeCommandEvent(command); + return; + } + Optional.ofNullable(createdResource).ifPresentOrElse(resource -> repository.finishCommand(commandId, resource), () -> repository.finishCommand(commandId)); - getCompletableParentId(commandId).filter(parentId -> !repository.existsNotFinishedSubCommands(parentId)) - .ifPresent(this::publishCommandExecutedEvent); + getCompletableParentId(command).ifPresent(this::publishCommandExecutedEvent); + } + + boolean shouldRevoke(Command command) { + return command.getStatus() == CommandStatus.REVOKE_PENDING || isParentCommandFailed(command); + } + + boolean isParentCommandFailed(Command command) { + return getParentId(command).filter(repository::isCommandFailed).isPresent(); + } + + Optional<String> getCompletableParentId(Command command) { + return getParentId(command).filter(repository::isCompleteIfSubsCompleted) + .filter(parentId -> !repository.existsNotFinishedSubCommands(parentId)); } - Optional<String> getCompletableParentId(String commandId) { - return repository.getParentId(commandId).filter(repository::isCompleteIfSubsCompleted); + Optional<String> getParentId(Command command) { + return Optional.ofNullable(command.getBodyObject()).map(body -> MapUtils.getString(body, PROPERTY_PARENT_ID)); } public void publishCommandExecutedEvent(String commandId) { - findCommand(commandId).ifPresent(command -> publisher.publishEvent(new CommandExecutedEvent(command))); + findById(commandId).ifPresent(command -> publisher.publishEvent(new CommandExecutedEvent(command))); } - public void setCommandError(String id, String errorMessage) { - repository.setErrorMessage(id, errorMessage); - var publishCommandFailedEvent = createCommandFailedEventPublisher("Command %s failed: %s".formatted(id, errorMessage)); - repository.findNotFailedSubCommandIds(id).filter(notFailedCommandId -> !StringUtils.equals(notFailedCommandId, id)) - .forEach(publishCommandFailedEvent); - repository.getNotFailedParentId(id).ifPresent(publishCommandFailedEvent); + public void setCommandError(String commandId, String errorMessage) { + repository.setErrorMessage(commandId, errorMessage); + repository.getParentId(commandId).map(this::setErrorStatus).filter(this::notErrorStatus) + .map(Command::getId).ifPresent(parentId -> handleCommandError(commandId, parentId)); } - Consumer<String> createCommandFailedEventPublisher(String errorMessage) { - return commandId -> publisher.publishEvent(new CommandFailedEvent(commandId, errorMessage)); + Command setErrorStatus(String commandId) { + return Optional.ofNullable(repository.updateCommandStatusAndReturnPrevious(commandId, CommandStatus.ERROR)) + .orElseThrow(() -> new NotFoundException(Command.class, commandId)); } - public void setCommandRevoked(String commandId) { - updateCommandStatus(commandId, CommandStatus.REVOKED); + boolean notErrorStatus(Command command) { + return command.getStatus() != CommandStatus.ERROR; } - public void setCommandRevokePending(final String commandId) { - updateCommandStatus(commandId, CommandStatus.REVOKE_PENDING); + void handleCommandError(String commandId, String parentId) { + publishCommandFailedEvent(parentId, "Command %s failed because subcommand %s failed".formatted(parentId, commandId)); + revokeSubCommands(parentId); + } - publishRevokeCommandEvent(commandId); + void publishCommandFailedEvent(String commandId, String errorMessage) { + publisher.publishEvent(new CommandFailedEvent(commandId, errorMessage)); } - void publishRevokeCommandEvent(final String commandId) { - var commandOptional = repository.getById(commandId); - commandOptional.ifPresentOrElse(command -> { - if (MapUtils.isEmpty(((PersistedCommand) command).getPreviousState())) { - handleError(commandId, "Command %s can not be revoked because it has no previous state."); - } - publisher.publishEvent(new RevokeCommandEvent(command)); - }, () -> handleError(commandId, "No Command with id %s found.")); + void revokeSubCommands(String parentId) { + repository.findSubCommandIds(parentId).forEach(this::revokeCommandSilent); } - private void handleError(String commandId, String messageTemplate) { - var message = messageTemplate.formatted(commandId); - LOG.error(message); - publisher.publishEvent(new CommandFailedEvent(commandId, message)); - throw new TechnicalException(message); + public void setCommandRevoked(String commandId) { + repository.updateCommandStatus(commandId, CommandStatus.REVOKED); } - void updateCommandStatus(String commandId, CommandStatus status) { - repository.updateCommandStatus(commandId, status); + public Command revokeCommand(String commandId) { + var updatedCommand = revokeCommandSilent(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId)); + if (!isRevokePending(updatedCommand)) { + throw new RevokeFailedException(commandId, "Unexpected status '%s'. (expected: REVOKE_PENDING)".formatted(updatedCommand.getStatus())); + } + return updatedCommand; + } + + Optional<Command> revokeCommandSilent(String commandId) { + var updatedCommand = repository.setRevokeStatusIfNotPending(commandId); + updatedCommand.filter(this::isRevokePending).ifPresent(this::publishRevokeCommandEvent); + return updatedCommand; + } + + boolean isRevokePending(Command command) { + return command.getStatus() == CommandStatus.REVOKE_PENDING; + } + + void publishRevokeCommandEvent(Command command) { + publisher.publishEvent(new RevokeCommandEvent(command)); } public boolean existsPendingCommands(String vorgangId) { @@ -205,20 +226,28 @@ public class CommandService { } public Stream<Command> createSubCommands(CreateSubCommandsRequest request) { - var parentCommand = getNotFailedCommand(request.getParentId()); + var parentCommand = getPendingCommand(request.getParentId()); repository.addSubCommands(parentCommand.getId(), buildSubCommandValues(request)); return request.getSubCommands().stream().map(subCommand -> buildCreateCommandRequest(subCommand, parentCommand)) .map(this::createCommand); } - Command getNotFailedCommand(String commandId) { - var command = findCommand(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId)); - if (command.getStatus() == CommandStatus.ERROR) { - throw new TechnicalException("Cannot add sub commands to failed command ('%s')".formatted(commandId)); + Command getPendingCommand(String commandId) { + var command = getById(commandId); + if (command.getStatus() != CommandStatus.PENDING) { + throw new TechnicalException("Cannot add sub commands to command '%s'. Parent command must have status PENDING".formatted(commandId)); } return command; } + public Command getById(String commandId) { + return findById(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId)); + } + + public Optional<Command> findById(String commandId) { + return repository.findById(commandId); + } + Map<String, Object> buildSubCommandValues(CreateSubCommandsRequest request) { var executionMode = request.getExecutionMode(); return Map.of( diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java index 4237a4c2f986161b0b76880b2aa50409e751fd68..5e83fe5ab0aa3c5f0c85b8fe4dc04bc51ec20a34 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/GrpcCommandService.java @@ -36,7 +36,6 @@ import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandExecutedEvent; import de.ozgcloud.command.CommandStatus; -import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.vorgang.command.CommandResponse.ResponseCode; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; import de.ozgcloud.vorgang.common.security.PolicyService; @@ -92,11 +91,6 @@ public class GrpcCommandService extends CommandServiceImplBase { responseObserver.onCompleted(); } - // TODO check - is this function still in use? - boolean isStatusChangeOrder(String order) { - return order.startsWith("VORGANG"); - } - @Override public void getCommand(GrpcGetCommandRequest request, StreamObserver<GrpcCommand> responseObserver) { policyService.checkPermissionByCommand(request.getId()); @@ -110,23 +104,13 @@ public class GrpcCommandService extends CommandServiceImplBase { @Override public void revokeCommand(GrpcRevokeCommandRequest request, StreamObserver<GrpcCommandResponse> responseObserver) { policyService.checkPermissionByCommand(request.getId()); - - String commandId = request.getId(); - - try { - commandService.setCommandRevokePending(commandId); - - Command updatedCommand = getCommand(commandId); - - responseObserver.onNext(mapToGrpcResponse(updatedCommand)); - responseObserver.onCompleted(); - } catch (TechnicalException e) { - responseObserver.onError(e); - } + var updatedCommand = commandService.revokeCommand(request.getId()); + responseObserver.onNext(mapToGrpcResponse(updatedCommand)); + responseObserver.onCompleted(); } Command getCommand(String commandId) { - return commandService.findCommand(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId)); + return commandService.findById(commandId).orElseThrow(() -> new NotFoundException(Command.class, commandId)); } private GrpcCommandResponse mapToGrpcResponse(Command response) { @@ -180,7 +164,7 @@ public class GrpcCommandService extends CommandServiceImplBase { @Override public void setCommandExecuted(GrpcSetCommandExecutedRequest request, StreamObserver<GrpcEmpty> responseObserver) { - commandService.findCommand(request.getCommandId()) + commandService.findById(request.getCommandId()) .map(command -> new CommandExecutedEvent(command, trimToNull(request.getCreatedResource()))) .ifPresentOrElse(publisher::publishEvent, () -> LOG.warn("Command with ID '%s' not found. Cannot mark as executed.".formatted(request.getCommandId()))); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java index b6360403db1c64d915838671a4a7ab390d540a21..4c7bb459c80b6c4e5a3aa39a88afcad0d0ebf9aa 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandService.java @@ -22,13 +22,8 @@ package de.ozgcloud.vorgang.command; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URLConnection; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; import java.util.Base64; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -37,21 +32,15 @@ import java.util.stream.Stream; import jakarta.activation.MimetypesFileTypeMap; -import org.apache.commons.lang3.StringUtils; import org.apache.http.entity.ContentType; import org.springframework.stereotype.Service; import de.ozgcloud.common.errorhandling.TechnicalException; import de.ozgcloud.nachrichten.postfach.AttachmentFile; import de.ozgcloud.nachrichten.postfach.PersistPostfachNachrichtService; -import de.ozgcloud.nachrichten.postfach.PostfachAddress; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; -import de.ozgcloud.nachrichten.postfach.StringBasedIdentifier; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; -import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemMapper; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemService; -import de.ozgcloud.vorgang.callcontext.CallContext; -import de.ozgcloud.vorgang.callcontext.User; import de.ozgcloud.vorgang.files.FileService; import de.ozgcloud.vorgang.files.OzgFile; import de.ozgcloud.vorgang.files.UploadedFilesReference; @@ -71,108 +60,12 @@ class PersistPostfachNachrichtByCommandService implements PersistPostfachNachric static final String ITEM_NAME = "PostfachMail"; static final String ATTACHMENT_NAME = "PostfachAttachment"; - private final CommandService service; private final VorgangAttachedItemService attachedItemService; private final FileService fileService; @Override public void persistNachricht(Optional<String> userId, PostfachNachricht nachricht) { - var builder = CreateCommandRequest.builder() - .callContext(buildCallContext(userId)) - .vorgangId(nachricht.getVorgangId()); - - var request = StringUtils.isBlank(nachricht.getId()) ? buildPersistCommand(builder, nachricht) : buildUpdateCommand(builder, nachricht); - - service.createCommand(request); - } - - CreateCommandRequest buildPersistCommand(CreateCommandRequest.CreateCommandRequestBuilder builder, PostfachNachricht nachricht) { - return builder.order(Order.CREATE_ATTACHED_ITEM.name()) - .relationId(nachricht.getVorgangId()) - .bodyObject(buildItem(nachricht)).build(); - } - - CreateCommandRequest buildUpdateCommand(CreateCommandRequest.CreateCommandRequestBuilder builder, PostfachNachricht nachricht) { - return builder.order(Order.UPDATE_ATTACHED_ITEM.name()) - .relationId(nachricht.getId()) - .bodyObject(buildUpdateItem(nachricht)).build(); - } - - private CallContext buildCallContext(Optional<String> userId) { - var builder = CallContext.builder(); - userId.map(id -> User.builder().id(id).build()).ifPresent(builder::user); - - return builder.build(); - } - - private Map<String, Object> buildItem(PostfachNachricht nachricht) { - return Map.of( - VorgangAttachedItemMapper.PROPERTY_CLIENT, CLIENT, - VorgangAttachedItemMapper.PROPERTY_VORGANG_ID, nachricht.getVorgangId(), - VorgangAttachedItemMapper.PROPERTY_ITEM_NAME, ITEM_NAME, - VorgangAttachedItemMapper.PROPERTY_ITEM, buildNachrichtMap(nachricht)); - - } - - private Map<String, Object> buildUpdateItem(PostfachNachricht nachricht) { - return Map.of( - VorgangAttachedItemMapper.PROPERTY_CLIENT, CLIENT, - VorgangAttachedItemMapper.PROPERTY_ITEM, buildNachrichtMap(nachricht)); - - } - - Map<String, Object> buildNachrichtMap(PostfachNachricht nachricht) { - var result = new HashMap<>(Map.of( - PostfachNachricht.FIELD_VORGANG_ID, nachricht.getVorgangId(), - PostfachNachricht.FIELD_CREATED_AT, nachricht.getCreatedAt().format(DateTimeFormatter.ISO_DATE_TIME), - PostfachNachricht.FIELD_DIRECTION, nachricht.getDirection().name(), - PostfachNachricht.FIELD_REPLY_OPTION, nachricht.getReplyOption().name(), - PostfachNachricht.FIELD_SUBJECT, nachricht.getSubject(), - PostfachNachricht.FIELD_MAIL_BODY, nachricht.getMailBody(), - PostfachNachricht.FIELD_ATTACHMENTS, nachricht.getAttachments())); - - putIfNonNull(result, PostfachNachricht.FIELD_POSTFACH_ID, nachricht.getPostfachId()); - putIfNonNull(result, PostfachNachricht.FIELD_MESSAGE_ID, nachricht.getMessageId()); - putIfNonNull(result, PostfachNachricht.FIELD_CREATED_BY, nachricht.getCreatedBy()); - putIfNonNull(result, PostfachNachricht.FIELD_SENT_SUCCESSFUL, nachricht.getSentSuccessful()); - putIfNonNull(result, PostfachNachricht.FIELD_MESSAGE_CODE, nachricht.getMessageCode()); - putIfNonNull(result, PostfachNachricht.REFERENCED_NACHRICHT_FIELD, nachricht.getReferencedNachricht()); - putDateIfNonNull(result, PostfachNachricht.FIELD_SENT_AT, nachricht.getSentAt()); - - Optional.ofNullable(nachricht.getPostfachAddress()).map(this::buildPostfachAddressMap) - .ifPresent(postfachMaiMap -> result.put(PostfachNachricht.POSTFACH_ADDRESS_FIELD, postfachMaiMap)); - - return Collections.unmodifiableMap(result); - } - - private Map<String, Object> putIfNonNull(Map<String, Object> mapIn, String key, Object value) { - if (Objects.nonNull(value)) { - mapIn.put(key, value); - } - return mapIn; - } - - private Map<String, Object> putDateIfNonNull(Map<String, Object> mapIn, String key, Object value) { - if (Objects.nonNull(value)) { - mapIn.put(key, ((ZonedDateTime) value).format(DateTimeFormatter.ISO_DATE_TIME)); - } - return mapIn; - } - - private Map<String, Object> buildPostfachAddressMap(PostfachAddress postfachAddress) { - var resultMap = new HashMap<String, Object>(); - resultMap.put(PostfachAddress.IDENTIFIER_FIELD, buildPostfachAddressIdentifierMap(postfachAddress)); - resultMap.put(PostfachAddress.VERSION_FIELD, postfachAddress.getVersion()); - resultMap.put(PostfachAddress.TYPE_FIELD, postfachAddress.getType()); - Optional.ofNullable(postfachAddress.getServiceKontoType()).ifPresentOrElse( - serviceKontoType -> resultMap.put(PostfachAddress.SERVICEKONTO_TYPE_FIELD, serviceKontoType), - () -> LOG.warn("ServiceKontoType is null")); - return Collections.unmodifiableMap(resultMap); - } - - private Map<String, Object> buildPostfachAddressIdentifierMap(PostfachAddress postfachAddress) { - var identifier = (StringBasedIdentifier) postfachAddress.getIdentifier(); - return Map.of(PostfachNachricht.FIELD_POSTFACH_ID, identifier.getPostfachId()); + throw new UnsupportedOperationException("Not implemented."); } @Override diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java index d5a1c169125ab7a84e1d734ad6e85f6adf5adccf..11318a0cf33d16cc95296cef55b61c1f08a878b8 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/ExceptionHandler.java @@ -41,11 +41,14 @@ import net.devh.boot.grpc.server.advice.GrpcExceptionHandler; @GrpcAdvice public class ExceptionHandler { + private static final String ERROR_MESSAGE = "Grpc internal server error"; + static final String KEY_ERROR_CODE = "ERROR_CODE"; static final String KEY_EXCEPTION_ID = "EXCEPTION_ID"; @GrpcExceptionHandler public StatusException handleNotFoundException(NotFoundException e) { + LOG.error(ERROR_MESSAGE, e); return createStatusException(buildNotFoundStatus(e), buildMetadata(e)); } @@ -55,6 +58,7 @@ public class ExceptionHandler { @GrpcExceptionHandler public StatusException handleFunctionalException(FunctionalException e) { + LOG.error(ERROR_MESSAGE, e); return createStatusException(buildInternalStatus(e), buildMetadata(e)); } @@ -77,6 +81,7 @@ public class ExceptionHandler { @GrpcExceptionHandler public StatusException handleTechnicalException(TechnicalException e) { + LOG.error(ERROR_MESSAGE, e); return createStatusException(buildInternalStatus(e), buildMetadata(e.getExceptionId())); } @@ -88,7 +93,7 @@ public class ExceptionHandler { public StatusException handleRuntimeException(RuntimeException e) { var exceptionId = createExceptionId(); var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); - LOG.error("Grpc internal server error: " + messageWithExceptionId, e); + LOG.error("Grpc internal server error: {}", messageWithExceptionId, e); return createStatusException(buildInternalStatus(e, messageWithExceptionId), buildMetadata(exceptionId)); } @@ -123,7 +128,7 @@ public class ExceptionHandler { public StatusException handleAccessDeniedException(AccessDeniedException e) { var exceptionId = createExceptionId(); var messageWithExceptionId = ExceptionUtil.formatMessageWithExceptionId(e.getMessage(), exceptionId); - LOG.error("Grpc permission denied error: " + messageWithExceptionId, e); + LOG.error("Grpc permission denied error: {}", messageWithExceptionId, e); return createStatusException(buildPermissionDeniedStatus(e, messageWithExceptionId), buildMetadata(exceptionId)); } @@ -134,6 +139,7 @@ public class ExceptionHandler { @GrpcExceptionHandler public StatusException handleSearchServiceUnavailableException(SearchServiceUnavailableException e) { + LOG.error(ERROR_MESSAGE, e); return createStatusException(buildUnavailableStatus(e), buildMetadata(e.getExceptionId())); } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/RevokeFailedException.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/RevokeFailedException.java new file mode 100644 index 0000000000000000000000000000000000000000..133b8970b815aaf02ad3284a24babd821ea13b0a --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/errorhandling/RevokeFailedException.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.vorgang.common.errorhandling; + +import java.util.Map; + +import de.ozgcloud.common.errorhandling.FunctionalErrorCode; + +public class RevokeFailedException extends FunctionalException { + + static final String KEY_COMMAND_ID = "Id"; + + private static final String MESSAGE_TEMPLATE = "Can not revoke command with id '%s'. %s"; + private static final FunctionalErrorCode ERROR_CODE = () -> "error.revoke_failed"; + + public RevokeFailedException(String commandId, String message) { + super(MESSAGE_TEMPLATE.formatted(commandId, message), ERROR_CODE, Map.of(KEY_COMMAND_ID, commandId)); + } +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java new file mode 100644 index 0000000000000000000000000000000000000000..172953ca2820282a18a978e7ac012e7379cd55eb --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgang.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 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.vorgang.common.migration; + +import org.bson.Document; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.index.CompoundIndexDefinition; +import org.springframework.data.mongodb.core.index.IndexDefinition; + +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; + +@ChangeUnit(id = "2024-06-12 17:00:00 OZG-5661", order = "M010", author = "ebardin") +public class M010_CreateIndexesInVorgang { // NOSONAR + + static final String COLLECTION_NAME = "vorgang"; + static final String COMPOUND_INDEX_NAME = "inCreation_OrgaId_assignedTo_status_createdAt"; + + static final String FIELD_IN_CREATION = "inCreation"; + static final String FIELD_STATUS = "status"; + static final String FIELD_CREATED_AT = "createdAt"; + static final String FIELD_ASSIGNED_TO = "assignedTo"; + static final String FIELD_EINGANGS = "eingangs"; + static final String FIELD_ORGANISATIONSEINHEITEN_ID = FIELD_EINGANGS + ".zustaendigeStelle.organisationseinheitenId"; + + @Execution + public void doMigration(MongoTemplate template) { + template.indexOps(COLLECTION_NAME).ensureIndex(buildCompoundIndexDefinition()); + } + + IndexDefinition buildCompoundIndexDefinition() { + var indexKeys = new Document(); + indexKeys.put(FIELD_IN_CREATION, 1); + indexKeys.put(FIELD_ORGANISATIONSEINHEITEN_ID, 1); + indexKeys.put(FIELD_ASSIGNED_TO, 1); + indexKeys.put(FIELD_STATUS, 1); + indexKeys.put(FIELD_CREATED_AT, 1); + return new CompoundIndexDefinition(indexKeys).named(COMPOUND_INDEX_NAME); + } + + @RollbackExecution + public void rollback() { + // kein rollback implementiert + } + +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevel.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevel.java new file mode 100644 index 0000000000000000000000000000000000000000..b87ca0ed026919f0c81ea26687e1eae8f82efdd5 --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevel.java @@ -0,0 +1,39 @@ +package de.ozgcloud.vorgang.common.migration; + +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.Update; + +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; + +@ChangeUnit(id = "2024-07-29 17:30:00 OZG-6259", order = "M011", author = "mkuester", runAlways = true) +public class M011_SetDefaultCollaborationLevel { // NOSONAR + + private static final String VORGANG_COLLECTION = "vorgang"; + + static final String HEADER_FIELD = "header"; + static final String COLLABORATION_LEVEL_FIELD = "collaborationLevel"; + static final String HEADER_COLLABORATION_LEVEL_FIELD = HEADER_FIELD + "." + COLLABORATION_LEVEL_FIELD; + private static final int DEFAULT_COLLABORATION_LEVEL = 0; + + @Execution + public void doMigration(MongoTemplate template) { + template.updateMulti(createFilterQuery(), createUpdate(), VORGANG_COLLECTION); + } + + private Query createFilterQuery() { + return new Query(Criteria.where(HEADER_COLLABORATION_LEVEL_FIELD).exists(false)); + } + + private Update createUpdate() { + return new Update().set(HEADER_COLLABORATION_LEVEL_FIELD, DEFAULT_COLLABORATION_LEVEL); + } + + @RollbackExecution + public void rollback() { + // kein rollback implementiert + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..105b8bdffbb95a96514c9fe65c0100da5820a12a --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializer.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 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.vorgang.common.search; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.index.TextIndexDefinition; +import org.springframework.stereotype.Component; + +import de.ozgcloud.vorgang.vorgang.Vorgang; +import lombok.RequiredArgsConstructor; + +@ConditionalOnProperty(prefix = "ozgcloud.mongodbsearch", name = "enabled", havingValue = "true") +@Component +@RequiredArgsConstructor +public class MongodbFullTextSearchIndexInitializer { + + static final String TEXT_INDEX_NAME = "textIndex"; + static final String TEXT_INDEX_LANGUAGE = "german"; + + private final MongoOperations mongoOperations; + + @EventListener(ContextRefreshedEvent.class) + public void initWildcardTextIndex() { + mongoOperations.indexOps(Vorgang.COLLECTION_NAME).ensureIndex(buildWildcardTextIndexDefinition()); + } + + TextIndexDefinition buildWildcardTextIndexDefinition() { + return new TextIndexDefinition.TextIndexDefinitionBuilder() + .named(TEXT_INDEX_NAME) + .onAllFields() + .withDefaultLanguage(TEXT_INDEX_LANGUAGE) + .build(); + } + +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchEventListener.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchEventListener.java index 6575b816370c075ac0ce7ef8c5251fa095dd4920..c844c3f36cc550abab150590103780d6b28bdc84 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchEventListener.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchEventListener.java @@ -74,7 +74,7 @@ public class SearchEventListener { } private void updateVorgangById(String commandId) { - commandService.findCommand(commandId).ifPresentOrElse(this::updateVorgang, () -> { + commandService.findById(commandId).ifPresentOrElse(this::updateVorgang, () -> { throw new IllegalStateException(MessageFormat.format("Kein Command mit der ID {0} gefunden", commandId)); }); } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java index 19533cbeee1904c697846e124744baf09a841c31..1d7a92f2e8d7be97c40a63f717454a23c3702942 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchService.java @@ -23,10 +23,12 @@ */ package de.ozgcloud.vorgang.common.search; +import static java.util.Objects.*; + import java.util.Collections; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.dao.DataAccessResourceFailureException; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; @@ -37,10 +39,10 @@ import de.ozgcloud.vorgang.vorgang.Vorgang; import de.ozgcloud.vorgang.vorgang.VorgangHeader; @Service -@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address") +@ConditionalOnExpression("!'${ozgcloud.elasticsearch.address:}'.isEmpty() || ${ozgcloud.mongodbsearch.enabled:false}") public class SearchService { - @Autowired + @Autowired(required = false) private SearchVorgangRepository repository; @Autowired @@ -49,16 +51,15 @@ public class SearchService { @Autowired private IndexedVorgangMapper mapper; + // TODO die Methode scheint überfüssig zu sein. Man könnte stattsie und ' updateVorgang' eine Methode z.B. 'addOrUpdateVorgang' nutzen. public void addVorgang(Vorgang vorgang) { - repository.save(mapper.fromVorgang(vorgang)); + updateVorgang(vorgang); } public void updateVorgang(Vorgang vorgang) { - repository.save(mapper.fromVorgang(vorgang)); - } - - public boolean existsById(String vorgangId) { - return repository.existsById(vorgangId); + if (nonNull(repository)) { + repository.save(mapper.fromVorgang(vorgang)); + } } public Page<VorgangHeader> find(FindVorgangRequest findVorgangRequest) { @@ -70,7 +71,9 @@ public class SearchService { } public void deleteVorgang(String vorgangId) { - repository.deleteById(vorgangId); + if (nonNull(repository)) { + repository.deleteById(vorgangId); + } } public void setAktenzeichen(String vorgangId, String aktenzeichen) { diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java index 7c1a2a19abae7c802e7054c3713d1dfdd6255879..3a58f8f74cfc1c23b8fdc5f217ddc64e488d4244 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepository.java @@ -25,13 +25,11 @@ package de.ozgcloud.vorgang.common.search; import java.util.Map; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import de.ozgcloud.vorgang.vorgang.FindVorgangRequest; import de.ozgcloud.vorgang.vorgang.VorgangHeader; -@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address") interface SearchVorgangCustomRepository { Page<VorgangHeader> searchBy(FindVorgangRequest request); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java index cfd983a2828f8edc7d56c2f0457292615becbaca..47fe7b83020c3fbd880df9c0abd3aceaeef23ccd 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryImpl.java @@ -39,6 +39,7 @@ import java.util.stream.Stream; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder; @@ -61,6 +62,7 @@ import de.ozgcloud.vorgang.vorgang.FindVorgangRequest; import de.ozgcloud.vorgang.vorgang.Vorgang; import de.ozgcloud.vorgang.vorgang.VorgangHeader; +@ConditionalOnProperty(prefix = "ozgcloud.elasticsearch", name = "address") @Repository class SearchVorgangCustomRepositoryImpl implements SearchVorgangCustomRepository { diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java new file mode 100644 index 0000000000000000000000000000000000000000..51570c813a3d09f2ba0373b598384429f736dbaa --- /dev/null +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongo.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2024 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.vorgang.common.search; + +import java.util.Arrays; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.data.mongodb.core.query.TextCriteria; +import org.springframework.data.mongodb.core.query.TextQuery; +import org.springframework.data.support.PageableExecutionUtils; +import org.springframework.stereotype.Repository; + +import de.ozgcloud.vorgang.vorgang.FindVorgangRequest; +import de.ozgcloud.vorgang.vorgang.VorgangHeader; +import lombok.RequiredArgsConstructor; + +@ConditionalOnProperty(prefix = "ozgcloud.mongodbsearch", name = "enabled", havingValue = "true") +@RequiredArgsConstructor +@Repository +public class SearchVorgangCustomRepositoryMongo implements SearchVorgangCustomRepository { + + private final MongoOperations mongoOperations; + + @Override + public Page<VorgangHeader> searchBy(final FindVorgangRequest request) { + var query = buildQuery(request); + var vorgangs = mongoOperations.stream(query, VorgangHeader.class).skip(request.getOffset()).limit(request.getLimit()).toList(); + return PageableExecutionUtils.getPage(vorgangs, buildPageable(request), vorgangs::size); + } + + private Query buildQuery(FindVorgangRequest request) { + return TextQuery.queryText(buildTextCriteria(request.getSearchBy())).sortByScore(); + } + + TextCriteria buildTextCriteria(String searchBy) { + var textCriteria = TextCriteria.forDefaultLanguage(); + Arrays.stream(searchBy.split(StringUtils.SPACE)).forEach(textCriteria::matchingPhrase); + return textCriteria; + } + + PageRequest buildPageable(FindVorgangRequest request) { + return PageRequest.of(request.getOffset() / request.getLimit(), request.getLimit()); + } + + @Override + public void update(final String vorangId, final Map<String, Object> patch) { + // update implemented in vorgang.vorgang.VorgangRepository + } +} diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/registry/ZufiRemoteService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/registry/ZufiRemoteService.java index 3aa2a888cfbd90f119e22290567d807bc636659b..bc436dedd25d7bd9ee4ef1ad95f7cfffb1612e8b 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/registry/ZufiRemoteService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/registry/ZufiRemoteService.java @@ -28,7 +28,7 @@ class ZufiRemoteService { } private GrpcVorgangManagerRegistrationRequest buildRequest(List<String> organistationsEinheitenIds, String address) { - return GrpcVorgangManagerRegistrationRequest.newBuilder().addAllOrganisationsEinheitenIds(organistationsEinheitenIds) + return GrpcVorgangManagerRegistrationRequest.newBuilder().addAllOrganisationsEinheitIds(organistationsEinheitenIds) .setServiceAddress(address).build(); } } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/servicekonto/ServiceKonto.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/servicekonto/ServiceKonto.java index dc55642a887b0d8a453a10ead82e0fb7de5970c8..c65e33018262555e9b38727cf434e221ec3ac4eb 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/servicekonto/ServiceKonto.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/servicekonto/ServiceKonto.java @@ -24,7 +24,7 @@ public class ServiceKonto { @Singular private List<PostfachAddress> postfachAddresses; - static class ServiceKontoBuilder { + public static class ServiceKontoBuilder { public ServiceKontoBuilder trustLevel(String trustLevel) { validateTrustLevel(trustLevel); diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/status/StatusEventListener.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/status/StatusEventListener.java index 4596de8fd1ea0477bd122b1af94e571076ee48ac..5c7db826fa425ebafb747b911bc8d6afbd3d5e7f 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/status/StatusEventListener.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/status/StatusEventListener.java @@ -120,7 +120,7 @@ class StatusEventListener { Command setPreviousState(Command command) { var vorgang = vorgangService.getById(command.getVorgangId()); commandService.setPreviousState(command.getId(), getPreviousStateMap(vorgang)); - return commandService.findCommand(command.getId()) + return commandService.findById(command.getId()) .orElseThrow(() -> new NoSuchElementException("No command with Id %s found".formatted(command.getId()))); } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangService.java index 56d7db937e4dc37c69cf806d51cce64a0aeb08f1..647303f1968fcf6ae80edad3dbe42e87497e6579 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangService.java @@ -23,33 +23,31 @@ */ package de.ozgcloud.vorgang.vorgang; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; +import de.ozgcloud.vorgang.collaboration.CollaborationService; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangBadRequestException; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequest; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequestMapper; import io.grpc.stub.StreamObserver; +import lombok.RequiredArgsConstructor; import net.devh.boot.grpc.server.service.GrpcService; @GrpcService +@RequiredArgsConstructor class GrpcVorgangService extends VorgangServiceGrpc.VorgangServiceImplBase { - @Autowired - private VorgangService vorgangService; - @Autowired - private VorgangHeaderService vorgangHeaderService; - @Autowired - private EingangMapper eingangMapper; - @Autowired - private VorgangHeaderMapper vorgangHeaderMapper; - @Autowired - private VorgangWithEingangMapper vorgangWithEingangMapper; - @Autowired - private FindVorgangRequestMapper findVorgangRequestMapper; - @Autowired - private FilterByMapper filterByMapper; - @Autowired - private IncomingFileMapper incomingFileMapper; - @Autowired - private IncomingFileGroupMapper incomingFileGroupMapper; + private final VorgangService vorgangService; + private final VorgangHeaderService vorgangHeaderService; + private final EingangMapper eingangMapper; + private final VorgangHeaderMapper vorgangHeaderMapper; + private final VorgangWithEingangMapper vorgangWithEingangMapper; + private final FindVorgangRequestMapper findVorgangRequestMapper; + private final FilterByMapper filterByMapper; + private final IncomingFileMapper incomingFileMapper; + private final IncomingFileGroupMapper incomingFileGroupMapper; + private final CollaborationService collaborationService; + private final CreateCollaborationVorgangRequestMapper createCollaborationVorgangRequestMapper; @Override public void startCreation(GrpcCreateVorgangRequest request, StreamObserver<GrpcCreateVorgangResponse> responseObserver) { @@ -113,4 +111,31 @@ class GrpcVorgangService extends VorgangServiceGrpc.VorgangServiceImplBase { .setVorgangWithEingang(vorgangWithEingangMapper.toVorgangWithEingang(vorgang)) .build(); } + + @Override + public void createCollaborationVorgang(GrpcCreateCollaborationVorgangRequest request, + StreamObserver<GrpcCreateCollaborationVorgangResponse> responseObserver) { + GrpcCreateCollaborationVorgangResponse response; + if (request.hasCollaborationRequest()) { + response = createCollaborationVorgang(request.getCollaborationRequest()); + } else { + throw new CreateCollaborationVorgangBadRequestException("Cannot create collaboration vorgang. Collaboration request is empty."); + } + responseObserver.onNext(response); + responseObserver.onCompleted(); + } + + GrpcCreateCollaborationVorgangResponse createCollaborationVorgang(GrpcCollaborationRequest request) { + var collaborationVorgang = collaborationService.createCollaborationVorgang(buildCreateCollaborationVorgangRequest(request)); + return buildCreateCollaborationVorgangResponse(collaborationVorgang); + } + + CreateCollaborationVorgangRequest buildCreateCollaborationVorgangRequest(GrpcCollaborationRequest request) { + var vorgang = vorgangService.getById(request.getVorgangId()); + return createCollaborationVorgangRequestMapper.mapFrom(request).toBuilder().vorgang(vorgang).build(); + } + + GrpcCreateCollaborationVorgangResponse buildCreateCollaborationVorgangResponse(Vorgang vorgang) { + return GrpcCreateCollaborationVorgangResponse.newBuilder().setVorgangId(vorgang.getId()).build(); + } } \ No newline at end of file diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangHead.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangHead.java index cc6c36f1f60d8aa55082ffd0304ef699f5cf1041..13e858035e377296843b70740e23c96ca3c3485e 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangHead.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangHead.java @@ -6,7 +6,7 @@ import de.ozgcloud.vorgang.servicekonto.ServiceKonto; import lombok.Builder; import lombok.Getter; -@Builder +@Builder(toBuilder = true) @Getter @TypeAlias("VorgangHeader") public class VorgangHead { @@ -14,4 +14,6 @@ public class VorgangHead { private ServiceKonto serviceKonto; private String organisationsEinheitId; + @Builder.Default + private int collaborationLevel = 0; } diff --git a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java index a6315672327389de9cc133226f0debe77e2a228c..3b69aa9e535b41598d1f2d76f9645436e3c8e75c 100644 --- a/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java +++ b/vorgang-manager-server/src/main/java/de/ozgcloud/vorgang/vorgang/VorgangService.java @@ -153,9 +153,8 @@ public class VorgangService { return addLabels(loaded); } - public void setAktenzeichen(Command command) { - var aktenzeichen = StringUtils.trimToNull(MapUtils.getString(command.getBodyObject(), BODY_OBJECT_AKTENZEICHEN)); - repository.setAktenzeichen(command.getVorgangId(), command.getRelationVersion(), aktenzeichen); + public Vorgang getOriginalVorgangById(String vorgangId) { + return removeUnpermittedClientAttributes(loadById(vorgangId)); } Vorgang removeUnpermittedClientAttributes(Vorgang vorgang) { @@ -182,6 +181,11 @@ public class VorgangService { return Pair.of(entry.getKey(), resultAttributeMap); } + public void setAktenzeichen(Command command) { + var aktenzeichen = StringUtils.trimToNull(MapUtils.getString(command.getBodyObject(), BODY_OBJECT_AKTENZEICHEN)); + repository.setAktenzeichen(command.getVorgangId(), command.getRelationVersion(), aktenzeichen); + } + public void assignToUser(Command command) { var patch = Map.<String, Object>of(Vorgang.MONGODB_FIELDNAME_ASSIGNED_TO, command.getBody().get(BODY_ASSIGNED_TO_FIELD)); diff --git a/vorgang-manager-server/src/main/resources/application-local.yml b/vorgang-manager-server/src/main/resources/application-local.yml index b1aa3707359cf0cc5a250ea5b76b2a86016d7c06..3c001700cf121f982f772d96606412189ad50db3 100644 --- a/vorgang-manager-server/src/main/resources/application-local.yml +++ b/vorgang-manager-server/src/main/resources/application-local.yml @@ -65,9 +65,6 @@ ozgcloud: user: cleanup: cron: 0 */5 * * * * - elasticsearch: - initEnabled: true - index: test-index feature: bescheid: enable-dummy-document-processor: true @@ -85,10 +82,14 @@ spring: - with-elastic ozgcloud: elasticsearch: + initEnabled: true + index: test-index address: localhost:9200 username: elastic password: password useSsl: false + mongodbsearch: + enabled: false --- @@ -126,3 +127,14 @@ grpc: bayern-id: address: static://127.0.0.1:9099 negotiationType: PLAINTEXT + +--- + +spring: + config: + activate: + on-profile: + - with-mongodbsearch +ozgcloud: + mongodbsearch: + enabled: true diff --git a/vorgang-manager-server/src/main/resources/application.yml b/vorgang-manager-server/src/main/resources/application.yml index 97831c912ce7c50b7a5bc66391a1b899841d5b78..6784f1d84c27c3a9745a4eb3f978d175cb440eeb 100644 --- a/vorgang-manager-server/src/main/resources/application.yml +++ b/vorgang-manager-server/src/main/resources/application.yml @@ -5,7 +5,7 @@ logging: mongock: runner-type: initializingbean - migration-scan-package: + migration-scan-package: - de.ozgcloud.vorgang.common.migration enabled: true transactionEnabled: false @@ -28,7 +28,7 @@ grpc: address: self:self negotiationType: TLS user-manager: - negotiationType: TLS + negotiationType: TLS email: address: self:self negotiationType: TLS @@ -83,9 +83,9 @@ ozgcloud: nachrichten-manager: address: self:self negotiation-type: plaintext - # vorgang-manager: - # address: self:self - # negotiation-type: plaintext +# vorgang-manager: +# address: self:self +# negotiation-type: plaintext file-manager: # address: ${ozgcloud.vorgang-manager.address} address: self:self @@ -96,6 +96,4 @@ ozgcloud: user-manager: address: ${grpc.client.user-manager.address:false} negotiation-type: ${grpc.client.user-manager.negotiationType} - antragraum: - entityId: https://antragsraum.ozgcloud.de/ - metadataUri: "classpath:/bayernid/bayernid-idp-infra.xml" + diff --git a/vorgang-manager-server/src/main/resources/bayernid/keycloak-saml-metadata.xml b/vorgang-manager-server/src/main/resources/bayernid/keycloak-saml-metadata.xml new file mode 100644 index 0000000000000000000000000000000000000000..b5746cf909d209910496f9ffef142f748c9378e9 --- /dev/null +++ b/vorgang-manager-server/src/main/resources/bayernid/keycloak-saml-metadata.xml @@ -0,0 +1,25 @@ +<md:EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" entityID="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp"> +<md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> +<md:KeyDescriptor use="signing"> +<ds:KeyInfo> +<ds:KeyName>qO7IAjV9WUVVahQd9b1LGC1jAn9zMWV8oiPeafCAfdM</ds:KeyName> +<ds:X509Data> +<ds:X509Certificate>MIICszCCAZsCBgGRLC9tAjANBgkqhkiG9w0BAQsFADAdMRswGQYDVQQDDBJieS1hbnRyYWdzcmF1bS1pZHAwHhcNMjQwODA3MDkzMTMxWhcNMzQwODA3MDkzMzExWjAdMRswGQYDVQQDDBJieS1hbnRyYWdzcmF1bS1pZHAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCo2RVcJYiqv000dk20VBv6952+FXjEa1BzSzlX1vs4IrpWCVNQfsmZ4ZAg7TBlwJiBHtx0zMtM6DyHQthTVf/E/aaZvBtobUlLt/A/hy/Q31SkzScUgLZy4Nm+kIfmwHRj5/Mo9/53NCYfHcRV0nmRISPj9XsGIjjU6UWEb8sjhQC3OJJg4rWeXKzGNQfGIaMeYmSu0dPfDGo1g08jTMVhgMbF2vystHv4W6dC1q2r6Sp7SrdfXIkkaK2WZcHaqJ1uWWzw6rZMA5vjVdm/djdUN/RHeKpw2d8lD9B/a9+swJJ59cccuk7izqIgXMP2OPBG8qroqOydkyG9huma9C9JAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAcQlpbNPvut1HjJ0Xlk8iyt6u43CvBhNpMPFdBcUPzdSlehiifmo214HOVIacBz7DAv+TzLXKDLsd+RNJkh3s+bEbA6C9lnYwOwi6jJy7Wp0Zsndks0Qf2b/jPYSBvUzFKx0v9w28kUS563ErQXF+TErqsin78kxQttMVRyfnjisC/3xSLcF7BngyVo20iggqMfcC4BDCGhEU2Th01I+Gk/Fs9UxL6laPfaf+CjJscCp9qJgA/TRMr+MRfPXb4QCLMTd+gMfv27fZN4YLvmhj0pWVIbqC76WJDlc0gzOdhxERWRwAtqei7WfqaEsRdcapm7t9clQQVyz2+WVlRSTxw=</ds:X509Certificate> +</ds:X509Data> +</ds:KeyInfo> +</md:KeyDescriptor> +<md:ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml/resolve" index="0"/> +<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat> +<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</md:NameIDFormat> +<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat> +<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat> +<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="https://sso.dev.by.ozg-cloud.de/realms/by-antragsraum-idp/protocol/saml"/> +</md:IDPSSODescriptor> +</md:EntityDescriptor> \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidITCase.java similarity index 91% rename from vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java rename to vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidITCase.java index 9e40a8290c82c77854dd2a9d502f46377f962b95..deabb903e2e37c99c2e45d41c8d88006e0077764 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidEventListenerITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/bescheid/BescheidITCase.java @@ -26,6 +26,7 @@ package de.ozgcloud.bescheid; import static de.ozgcloud.bescheid.BescheidEventListener.*; import static org.assertj.core.api.Assertions.*; import static org.awaitility.Awaitility.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; import java.time.ZonedDateTime; @@ -44,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; -import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -59,7 +59,6 @@ import de.ozgcloud.apilib.user.OzgCloudUserProfile; import de.ozgcloud.apilib.user.OzgCloudUserProfileService; import de.ozgcloud.bescheid.attacheditem.AttachedItemService; import de.ozgcloud.command.Command; -import de.ozgcloud.command.CommandCreatedEvent; import de.ozgcloud.command.CommandStatus; import de.ozgcloud.common.test.DataITCase; import de.ozgcloud.document.BescheidDocumentCreatedEvent; @@ -73,8 +72,8 @@ import de.ozgcloud.vorgang.VorgangManagerServerApplication; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory; import de.ozgcloud.vorgang.callcontext.CallContextAuthenticationTokenTestFactory; +import de.ozgcloud.vorgang.callcontext.WithMockCustomUser; import de.ozgcloud.vorgang.command.CommandService; -import de.ozgcloud.vorgang.command.CommandTestFactory; import de.ozgcloud.vorgang.command.CreateCommandRequest; import de.ozgcloud.vorgang.servicekonto.ServiceKontoTestFactory; import de.ozgcloud.vorgang.status.StatusChangedEvent; @@ -87,20 +86,18 @@ import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; "grpc.client.ozgcloud-command-manager.address=in-process:test", "grpc.client.vorgang-manager.address=in-process:test", "grpc.client.nachrichten-manager.address=in-process:test", - "grpc.client.pluto.address=in-process:test", "grpc.client.command-manager.address=in-process:test", "ozgcloud.feature.bescheid.enable-dummy-document-processor=true", }) @DataITCase @DirtiesContext -class BescheidEventListenerITCase { +@WithMockCustomUser +class BescheidITCase { private static final String USER_ID = "user-id"; @Autowired private CommandService commandService; - @Autowired - private ApplicationEventPublisher eventPublisher; @SpyBean private TestEventListener bescheiTestEventListener; @@ -133,7 +130,7 @@ class BescheidEventListenerITCase { var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId(); var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId(); - eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId)); + commandService.createCommand(buildCreatedCommandRequest(vorgangId, bescheidItemId)); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture()); @@ -146,7 +143,7 @@ class BescheidEventListenerITCase { var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId(); var bescheidItemId = mongoOperations.save(createBescheidAttachedItem(vorgangId)).getId(); - eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItemId)); + commandService.createCommand(buildCreatedCommandRequest(vorgangId, bescheidItemId)); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { verify(bescheiTestEventListener).onBescheidDocumentCreated(bescheidDocumentCreatedEventCaptor.capture()); @@ -164,15 +161,14 @@ class BescheidEventListenerITCase { .build(); } - private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, String bescheidItemId) { - var command = CommandTestFactory.createBuilder() + private CreateCommandRequest buildCreatedCommandRequest(String vorgangId, String bescheidItemId) { + return CreateCommandRequest.builder() .vorgangId(vorgangId) .relationId(bescheidItemId) .order(BescheidEventListener.CREATE_BESCHEID_DOCUMENT_ORDER) .bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01", GENEHMIGT_BODY_KEY, true)) .build(); - return new CommandCreatedEvent(command); } private GridFSFile getBescheidDocumentFile() { @@ -195,14 +191,21 @@ class BescheidEventListenerITCase { @Captor private ArgumentCaptor<StatusChangedEvent> statusChangedEventCaptor; + private VorgangAttachedItem bescheidItem;; + private String vorgangId; + + @BeforeEach + void mock() { + vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId(); + bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId)); + } + @Test void shouldSetBescheidStatusToSent() { - var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId(); - var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId)); - - eventPublisher.publishEvent(buildCommandCreatedEvent(vorgangId, bescheidItem)); + commandService.createCommand(buildCreateCommandRequest()); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(loadBescheid(bescheidItem.getId()).getVersion()).isEqualTo(2); verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture()); assertThat(loadBescheid(bescheidItem.getId()).getItem()).containsEntry(Bescheid.FIELD_STATUS, Bescheid.Status.SENT.name()); }); @@ -210,13 +213,10 @@ class BescheidEventListenerITCase { @Test void shouldSetVorgangStatusToBeschieden() { - var vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()).getId(); - var bescheidItem = mongoOperations.save(createBescheidAttachedItemWithDocument(vorgangId)); - var event = buildCommandCreatedEvent(vorgangId, bescheidItem); - - eventPublisher.publishEvent(event); + commandService.createCommand(buildCreateCommandRequest()); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(loadBescheid(bescheidItem.getId()).getVersion()).isEqualTo(2); verify(bescheiTestEventListener).onBescheidStatusChangedEvent(statusChangedEventCaptor.capture()); assertThat(loadVorgang(vorgangId).getStatus()).isEqualTo(Vorgang.Status.BESCHIEDEN); }); @@ -235,8 +235,8 @@ class BescheidEventListenerITCase { .build(); } - private CommandCreatedEvent buildCommandCreatedEvent(String vorgangId, VorgangAttachedItem bescheidItem) { - var command = CommandTestFactory.createBuilder() + private CreateCommandRequest buildCreateCommandRequest() { + return CreateCommandRequest.builder() .vorgangId(vorgangId) .relationId(bescheidItem.getId()) .relationVersion(bescheidItem.getVersion()) @@ -244,7 +244,6 @@ class BescheidEventListenerITCase { .bodyObject(Map.of(BESCHEID_VOM_BODY_KEY, "2024-01-01", GENEHMIGT_BODY_KEY, true)) .build(); - return new CommandCreatedEvent(command); } private VorgangAttachedItem loadBescheid(String bescheidId) { @@ -312,7 +311,7 @@ class BescheidEventListenerITCase { await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { verify(bescheiTestEventListener).onBescheidSentEvent(bescheidSentEventCaptor.capture()); var sentInfoMap = MapUtils.getMap(loadBescheid(bescheidItem.getId()).getItem(), Bescheid.FIELD_SENT_INFO); - assertThat(sentInfoMap.get(Bescheid.FIELD_SENT_BY)).asString().isNotBlank(); + assertThat(MapUtils.getString(sentInfoMap, Bescheid.FIELD_SENT_BY)).isNotBlank(); }); } @@ -339,6 +338,7 @@ class BescheidEventListenerITCase { }); } + @DirtiesContext @Test void shouldSendPostfachNachricht() { var expectedPostfachAddress = PostfachAddressTestFactory.createBuilder() diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..61fc27b309e3d269706f3a8bd3c1fd74da19ff6e --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/antragraum/AntragraumITCase.java @@ -0,0 +1,225 @@ +package de.ozgcloud.nachrichten.antragraum; + +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.apache.commons.lang3.StringUtils; +import org.apache.xml.security.stax.ext.XMLSecurityConstants.DIRECTION; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.annotation.DirtiesContext; + +import de.ozgcloud.common.test.DataITCase; +import de.ozgcloud.common.test.TestUtils; +import de.ozgcloud.nachrichten.postfach.PostfachAddress; +import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; +import de.ozgcloud.nachrichten.postfach.osi.ReplyOption; +import de.ozgcloud.vorgang.VorgangManagerServerApplication; +import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; +import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory; +import de.ozgcloud.vorgang.servicekonto.PostfachAddressTestFactory; +import de.ozgcloud.vorgang.servicekonto.ServiceKonto; +import de.ozgcloud.vorgang.servicekonto.ServiceKontoTestFactory; +import de.ozgcloud.vorgang.vorgang.Vorgang; +import de.ozgcloud.vorgang.vorgang.VorgangHeadTestFactory; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; +import io.grpc.stub.StreamObserver; + +@SpringBootTest(classes = { VorgangManagerServerApplication.class }, properties = { + "grpc.server.in-process-name=antragraumitcase", + "ozgcloud.antragraum.url=https://localhost/dummy/antragraum", + "ozgcloud.antragraum.entityId=dummy-id", + "ozgcloud.antragraum.metadataUri=http://localhost/dummy/antragraum/uri", + "ozgcloud.antragraum.decryptionPrivateKey=dummyPrivateKey", + "ozgcloud.antragraum.decryptionCertificate=dummyCertificate", + "grpc.client.ozgcloud-command-manager.address=in-process:antragraumitcase", + "grpc.client.command-manager.address=in-process:antragraumitcase", + "grpc.client.info-manager.address=in-process:antragraumitcase", + "grpc.client.vorgang-manager.address=in-process:antragraumitcase", +}) +@WithMockUser +@DataITCase +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS) +class AntragraumITCase { + + @Autowired + private AntragraumGrpcService grpcService; + @Autowired + private MongoOperations mongoOperations; + + @MockBean + private Saml2Decrypter decrypter; + @MockBean + private Saml2Verifier verifier; + + @BeforeEach + void prepareDatabase() { + mongoOperations.dropCollection(Vorgang.COLLECTION_NAME); + mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME); + } + + @DisplayName("Find rueckfragen") + @Nested + class TestFindRueckfragen { + + @Mock + private StreamObserver<GrpcFindRueckfragenResponse> responseObserver; + + @Captor + private ArgumentCaptor<GrpcFindRueckfragenResponse> captor; + + @BeforeEach + void mock() { + when(decrypter.decryptPostfachId(any())).thenReturn(PostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE); + when(verifier.verify(any())).thenReturn(Collections.emptyList()); + } + + @DisplayName("with nachrichten from different vorgaenge") + @Nested + class TestWithDifferentVorgangId { + + private Vorgang vorgangTrustLevel3; + private Vorgang vorgangTrustLevel2; + + @BeforeEach + void prepareDatabase() { + vorgangTrustLevel3 = mongoOperations.save(createVorgang(TrustLevel.LEVEL_3), Vorgang.COLLECTION_NAME); + mongoOperations.save(createPostfachNachrichtVorgangAttachedItem(vorgangTrustLevel3.getId()), VorgangAttachedItem.COLLECTION_NAME); + + vorgangTrustLevel2 = mongoOperations.save(createVorgang(TrustLevel.LEVEL_2), Vorgang.COLLECTION_NAME); + mongoOperations.save(createPostfachNachrichtVorgangAttachedItem(vorgangTrustLevel2.getId()), VorgangAttachedItem.COLLECTION_NAME); + } + + @Test + void shouldHaveTrustLevelForVorgang() { + when(decrypter.decryptTrustLevel(any())).thenReturn(TrustLevel.LEVEL_4.getValue()); + + var rueckfragen = findRueckfragen().stream() + .filter(rueckfrage -> StringUtils.equals(rueckfrage.getVorgangId(), vorgangTrustLevel3.getId())) + .toList(); + + assertThat(rueckfragen).isNotEmpty().hasSize(1); + assertThat(rueckfragen.get(0).getTrustLevel()).isEqualTo(TrustLevel.LEVEL_3.getValue()); + assertThat(rueckfragen.get(0).getAccessible()).isTrue(); + } + + @Test + void shouldHaveTrustLevelForOtherVorgang() { + when(decrypter.decryptTrustLevel(any())).thenReturn(TrustLevel.LEVEL_1.getValue()); + + var rueckfragen = findRueckfragen().stream() + .filter(rueckfrage -> StringUtils.equals(rueckfrage.getVorgangId(), vorgangTrustLevel2.getId())) + .toList(); + + assertThat(rueckfragen).isNotEmpty().hasSize(1); + assertThat(rueckfragen.get(0).getTrustLevel()).isEqualTo(TrustLevel.LEVEL_2.getValue()); + assertThat(rueckfragen.get(0).getAccessible()).isFalse(); + } + } + + @DisplayName("with nachrichten from one vorgang") + @Nested + class TestWithOneVorgangId { + + @BeforeEach + void prepareDatabase() { + var vorgang = mongoOperations.save(createVorgang(TrustLevel.LEVEL_3), Vorgang.COLLECTION_NAME); + mongoOperations.save(createPostfachNachrichtVorgangAttachedItem(vorgang.getId()), VorgangAttachedItem.COLLECTION_NAME); + } + + @Test + void shouldReturnOnMatchingTokenTrustLevel() { + when(decrypter.decryptTrustLevel(any())).thenReturn(TrustLevel.LEVEL_3.getValue()); + + var rueckfragen = findRueckfragen(); + + assertThat(rueckfragen).isNotEmpty().hasSize(1); + assertThat(rueckfragen.get(0).getTrustLevel()).isEqualTo(TrustLevel.LEVEL_3.getValue()); + assertThat(rueckfragen.get(0).getAccessible()).isTrue(); + } + + @Test + void shouldReturnOnLowerTokenTrustLevel() { + when(decrypter.decryptTrustLevel(any())).thenReturn(TrustLevel.LEVEL_1.getValue()); + + var rueckfragen = findRueckfragen(); + + assertThat(rueckfragen).isNotEmpty().hasSize(1); + assertThat(rueckfragen.get(0).getTrustLevel()).isEqualTo(TrustLevel.LEVEL_3.getValue()); + assertThat(rueckfragen.get(0).getAccessible()).isFalse(); + } + + @Test + void shouldReturnOnHigherTokenTrustLevel() { + when(decrypter.decryptTrustLevel(any())).thenReturn(TrustLevel.LEVEL_4.getValue()); + + var rueckfragen = findRueckfragen(); + + assertThat(rueckfragen).isNotEmpty().hasSize(1); + assertThat(rueckfragen.get(0).getTrustLevel()).isEqualTo(TrustLevel.LEVEL_3.getValue()); + assertThat(rueckfragen.get(0).getAccessible()).isTrue(); + } + } + + private List<GrpcRueckfrageHead> findRueckfragen() { + grpcService.findRueckfragen( + GrpcFindRueckfrageRequestTestFactory.createBuilder().setSamlToken(TestUtils.loadTextFile("SamlResponse.xml")).build(), + responseObserver); + verify(responseObserver).onNext(captor.capture()); + return captor.getValue().getRueckfrageHeadList(); + } + } + + private Vorgang createVorgang(TrustLevel trustLevel) { + return VorgangTestFactory.createBuilder() + .id(null) + .header(VorgangHeadTestFactory.createBuilder().serviceKonto(createBayernIdServiceKonto(trustLevel)).build()) + .build(); + } + + private ServiceKonto createBayernIdServiceKonto(TrustLevel trustLevel) { + return ServiceKontoTestFactory.createBuilder().type("BayernID").trustLevel(trustLevel.getValue()).build(); + } + + private VorgangAttachedItem createPostfachNachrichtVorgangAttachedItem(String vorgangId) { + return VorgangAttachedItemTestFactory.createBuilder() + .id(null) + .version(0) + .vorgangId(vorgangId) + .itemName("PostfachMail") + .client("OzgCloud_NachrichtenManager") + .item(createBayernIdPostfachNachrichtItem(vorgangId)) + .build(); + } + + private Map<String, Object> createBayernIdPostfachNachrichtItem(String vorgangId) { + var nachrichtItem = PostfachNachrichtTestFactory.asMap(); + nachrichtItem.put(PostfachNachricht.FIELD_DIRECTION, DIRECTION.OUT.name()); + nachrichtItem.put(PostfachNachricht.FIELD_REPLY_OPTION, ReplyOption.POSSIBLE); + nachrichtItem.put(PostfachNachricht.FIELD_VORGANG_ID, vorgangId); + nachrichtItem.put(PostfachNachricht.FIELD_POSTFACH_ID, PostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE); + nachrichtItem.put(PostfachNachricht.POSTFACH_ADDRESS_FIELD, Map.of( + PostfachAddress.TYPE_FIELD, 0, + PostfachAddress.VERSION_FIELD, 1, + PostfachAddress.IDENTIFIER_FIELD, + Map.of(PostfachNachricht.FIELD_POSTFACH_ID, PostfachAddressTestFactory.STRING_BASED_IDENTIFIER_POSTFACH_ID_VALUE), + PostfachAddress.SERVICEKONTO_TYPE_FIELD, "BayernID")); + return nachrichtItem; + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImplITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImplITCase.java index cbd18b8e88f7de97c722790032fab203b0bfeffc..fd8fec55986287b729195fe60ddd04e081334ad9 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImplITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PersistPostfachNachrichtServiceImplITCase.java @@ -24,14 +24,15 @@ package de.ozgcloud.nachrichten.postfach; import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.*; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; import java.util.concurrent.TimeUnit; import org.apache.commons.collections.MapUtils; -import org.awaitility.Awaitility; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -55,6 +56,7 @@ import lombok.SneakyThrows; "grpc.server.inProcessName=test", "grpc.client.vorgang-manager.address=in-process:test", "grpc.client.ozgcloud-command-manager.address=in-process:test", + "grpc.client.pluto.address=in-process:test", }) @DataITCase @DirtiesContext @@ -63,6 +65,8 @@ class PersistPostfachNachrichtServiceImplITCase { @Autowired private PersistPostfachNachrichtServiceImpl service; + @Autowired + private PostfachNachrichtMapper postfachNachrichtMapper; @Autowired private MongoOperations mongoOperations; @@ -75,6 +79,36 @@ class PersistPostfachNachrichtServiceImplITCase { vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build(), Vorgang.COLLECTION_NAME).getId(); } + @Nested + class TestPersistPostfachNachricht { + + @Test + void shouldPersistPostfachNachricht() { + var postfachNachricht = PostfachNachrichtTestFactory.createBuilder().id(null).vorgangId(vorgangId).build(); + service.persistNachricht(Optional.empty(), postfachNachricht); + + assertThat(loadPostfachNachrichts()).hasSize(1).first().usingRecursiveComparison().ignoringFields("id").isEqualTo(postfachNachricht); + } + + @Test + void shouldUpdatePersistedNachricht() { + var persistedNachrichtId = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0) + .itemName(PersistPostfachNachrichtServiceImpl.ITEM_NAME).item(PostfachNachrichtTestFactory.asMap()).build()).getId(); + var postfachNachricht = PostfachNachrichtTestFactory.createBuilder().id(persistedNachrichtId).vorgangId(vorgangId) + .subject("updated subject").mailBody("updated body").build(); + + service.persistNachricht(Optional.empty(), postfachNachricht); + + assertThat(loadPostfachNachrichts()).hasSize(1).first().usingRecursiveComparison().ignoringFields("id").isEqualTo(postfachNachricht); + } + + private List<PostfachNachricht> loadPostfachNachrichts() { + return await().atMost(60, TimeUnit.MINUTES) + .until(() -> mongoOperations.findAll(VorgangAttachedItem.class), list -> !list.isEmpty()).stream() + .map(VorgangAttachedItem::getItem).map(postfachNachrichtMapper::fromMapToPostfachMail).toList(); + } + } + @DisplayName("Find rueckfragen") @Nested class TestFindRueckfragen { @@ -101,7 +135,7 @@ class PersistPostfachNachrichtServiceImplITCase { void shouldPersistAnswer() { service.persistAnswer(referencedNachrichtId, createPostfachNachricht()); - Awaitility.await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { var persistedAnswers = findAllVorgangAttachedItems(); assertThat(persistedAnswers).hasSize(1).first() .extracting(VorgangAttachedItem::getItem) diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachEventListenerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachEventListenerITCase.java index 502e0829ebbc47f6cd630cd9030ee7222acc3b00..516b1ea807fd936a7d7c91b034f8bba505c02017 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachEventListenerITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachEventListenerITCase.java @@ -75,7 +75,7 @@ class PostfachEventListenerITCase { @BeforeEach void initServices() { - when(commandService.findCommand(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); } @Test diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java index cf6ab1233d729c31002070698f877d7e3d0c809b..8c657211a40fd0c72b8e13aeff1de606304a4601 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/nachrichten/postfach/PostfachMailITCase.java @@ -71,10 +71,14 @@ import de.ozgcloud.vorgang.files.OzgFile; import io.grpc.stub.StreamObserver; @SpringBootTest(classes = { VorgangManagerServerApplication.class, OsiPostfachProperties.class }, properties = { + "grpc.server.in-process-name=postfachitcase", "ozgcloud.osi.postfach.proxyapi.url=http://localhost/ApiProxy/V1/Message", "ozgcloud.osi.postfach.proxyapi.key=1234", "ozgcloud.osi.postfach.proxyapi.realm=test-realm", - "ozgcloud.osi.postfach.notification.mail-from=test@local.host" + "ozgcloud.osi.postfach.notification.mail-from=test@local.host", + "grpc.client.ozgcloud-command-manager.address=in-process:postfachitcase", + "grpc.client.command-manager.address=in-process:postfachitcase", + }) @WithMockUser @DataITCase @@ -93,8 +97,6 @@ class PostfachMailITCase { @MockBean private PolicyService policyService; - private MockRestServiceServer mockServer; - @Mock private StreamObserver<GrpcFindPostfachMailsResponse> responseObserver; @Captor @@ -105,6 +107,7 @@ class PostfachMailITCase { @SpyBean private CommandService commandService; + private MockRestServiceServer mockServer; @BeforeEach void prepareDatabase() { @@ -133,7 +136,7 @@ class PostfachMailITCase { callGrpcEndpoint(); - mockServer.verify(); + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> mockServer.verify()); } @Nested @@ -144,6 +147,7 @@ class PostfachMailITCase { mockServerSendSuccess(); callGrpcEndpoint(); + var mails = await().atMost(60, TimeUnit.SECONDS) .until(PostfachMailITCase.this::callGrpcListEndpoint, PostfachMailITCase.this::hasAtLeastOneElement); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/processor/ProcessorITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/processor/ProcessorITCase.java index 2688640366bfcfc09504f2db123a0dfba3fa12dc..495d3665e76fb0e5badd9cceaadb0e6b8a99dd18 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/processor/ProcessorITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/processor/ProcessorITCase.java @@ -79,7 +79,7 @@ class ProcessorITCase { var commandBuilder = PersistedCommand.builder().id("42").status(CommandStatus.PENDING).createdAt(ZonedDateTime.now()); when(commandService.createCommand(any())).thenReturn(commandBuilder.build()); - when(commandService.findCommand("42")) + when(commandService.findById("42")) .thenReturn(Optional.of(commandBuilder.status(CommandStatus.FINISHED).finishedAt(ZonedDateTime.now()).build())); } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1ab89f43444d623ec62d3cd493d4b2155e06c276 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/VorgangManagerCallContextProviderTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2024 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.vorgang; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Optional; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.apilib.user.OzgCloudUserId; +import de.ozgcloud.vorgang.callcontext.CallContextUser; +import de.ozgcloud.vorgang.callcontext.CurrentUserService; +import de.ozgcloud.vorgang.callcontext.VorgangManagerClientCallContextAttachingInterceptor; + +class VorgangManagerCallContextProviderTest { + + private static final String USER_ID = "userId"; + + @Spy + @InjectMocks + private VorgangManagerCallContextProvider contextProvider; + + @Mock + private CurrentUserService currentUserService; + + @Nested + class TestProvideContext { + + @Test + void shouldSetClientName() { + var result = contextProvider.provideContext(); + + assertThat(result.getClientName()).isEqualTo(VorgangManagerClientCallContextAttachingInterceptor.VORGANG_MANAGER_CLIENT_NAME); + } + + @Test + void shouldCallFindUserId() { + contextProvider.provideContext(); + + verify(contextProvider).findUserId(); + } + + @Test + void shouldSetUserId() { + var userId = OzgCloudUserId.from(USER_ID); + when(contextProvider.findUserId()).thenReturn(Optional.of(userId)); + + var result = contextProvider.provideContext(); + + assertThat(result.getUserId()).isEqualTo(userId); + } + } + + @Nested + class TestFindUserId { + + @Test + void shouldCallCurrentUserService() { + contextProvider.findUserId(); + + verify(currentUserService).findUser(); + } + + @Test + void shouldReturnUserId() { + var callContextUser = CallContextUser.builder().userId(Optional.of(USER_ID)).build(); + when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser)); + + var result = contextProvider.findUserId(); + + assertThat(result).contains(OzgCloudUserId.from(USER_ID)); + } + + @Test + void shouldReturnIfNoUser() { + var result = contextProvider.findUserId(); + + assertThat(result).isEmpty(); + } + + @Test + void shouldReturnIfNoUserId() { + var callContextUser = CallContextUser.builder().userId(Optional.empty()).build(); + when(currentUserService.findUser()).thenReturn(Optional.of(callContextUser)); + + var result = contextProvider.findUserId(); + + assertThat(result).isEmpty(); + } + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerITCase.java index 248ebc6edb9c5dcc677b60457a71151448a67089..1ef45402d363fe6e8a0a695dd349c88383f31773 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerITCase.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.*; import java.util.Map; import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -37,6 +38,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.context.ApplicationEventPublisher; +import de.ozgcloud.command.Command; import de.ozgcloud.command.RevokeCommandEvent; import de.ozgcloud.common.test.ITCase; import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory; @@ -64,6 +66,12 @@ class VorgangAttachedItemEventListenerITCase { @Nested class TestOnCreateItemCommand { + + @BeforeEach + void mock() { + when(service.create(any(), any())).thenReturn(VorgangAttachedItemTestFactory.create()); + } + @Test void shouldCallListener() { var command = CommandTestFactory.createBuilder().order(Order.CREATE_ATTACHED_ITEM.name()).relationId(VorgangTestFactory.ID) @@ -78,6 +86,12 @@ class VorgangAttachedItemEventListenerITCase { @Nested class TestOnUpdateItemCommand { + + @BeforeEach + void mock() { + when(service.getById(any())).thenReturn(VorgangAttachedItemTestFactory.create()); + } + @Test void shouldCallListener() { var command = CommandTestFactory.createBuilder().order(Order.UPDATE_ATTACHED_ITEM.name()) @@ -92,7 +106,14 @@ class VorgangAttachedItemEventListenerITCase { @Nested class TestOnPatchItemCommand { - private Map<String, Object> bodyMap = Map.of("id", VorgangAttachedItemTestFactory.ID, "item", Map.of("done", true)); + + private final Map<String, Object> bodyMap = Map.of(VorgangAttachedItem.FIELDNAME_ID, VorgangAttachedItemTestFactory.ID, + VorgangAttachedItem.FIELDNAME_ITEM, Map.of("done", true)); + + @BeforeEach + void mock() { + when(service.getById(any())).thenReturn(VorgangAttachedItemTestFactory.create()); + } @Test void shouldCallListener() { @@ -134,17 +155,13 @@ class VorgangAttachedItemEventListenerITCase { @Nested class TestOnRevokeDeleteItem { + private final Command command = CommandTestFactory.createBuilder().order(Order.DELETE_ATTACHED_ITEM.name()).build(); + @Test void shouldCallUnmarkAsDeleted() { - var command = CommandTestFactory.createBuilder().order(Order.DELETE_ATTACHED_ITEM.name()).build(); - var event = new RevokeCommandEvent(command); + publisher.publishEvent(new RevokeCommandEvent(command)); - publisher.publishEvent(event); - - await().atMost(5, TimeUnit.SECONDS).untilAsserted( - () -> verify(service).unmarkAsDeleteByIdAndVersion(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION)); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> verify(service).revokeDelete(command)); } - } - -} +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerTest.java index 7b83059e27150e6459fb4f4a08d72e672fd7dc5b..28611d93e880d5012906f5dc8da81aea4c5a2846 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemEventListenerTest.java @@ -27,14 +27,20 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; import jakarta.validation.ValidationException; +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.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.mapstruct.factory.Mappers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -44,9 +50,9 @@ import org.mockito.Spy; import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.command.Command; -import de.ozgcloud.command.CommandRevokeFailedEvent; import de.ozgcloud.command.RevokeCommandEvent; import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory; +import de.ozgcloud.vorgang.command.CommandService; import de.ozgcloud.vorgang.command.CommandTestFactory; import de.ozgcloud.vorgang.vorgang.VorgangDeletedEventTestFactory; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; @@ -61,26 +67,27 @@ class VorgangAttachedItemEventListenerTest { private VorgangAttachedItemService service; @Mock private ApplicationEventPublisher publisher; + @Mock + private CommandService commandService; @Spy private VorgangAttachedItemMapper mapper = Mappers.getMapper(VorgangAttachedItemMapper.class); + @DisplayName("Create item") @Nested class TestCreateItem { - private Map<String, Object> bodyMap = VorgangAttachedItemTestFactory.asMap(); + private final Map<String, Object> bodyMap = VorgangAttachedItemTestFactory.asMap(); - private Command command = CommandTestFactory.createBuilder().bodyObject(bodyMap).relationId(VorgangTestFactory.ID).build(); + private final Command command = CommandTestFactory.createBuilder().bodyObject(bodyMap).relationId(VorgangTestFactory.ID).build(); @Captor private ArgumentCaptor<VorgangAttachedItem> itemCaptor; + @Captor + private ArgumentCaptor<VorgangAttachedItemCreatedEvent> vorgangAttachedItemCreatedEventCaptor; - @Test - void shouldThrowExceptionOnIdDismatching() { - var command = CommandTestFactory.createBuilder().vorgangId("other").build(); - var event = CommandCreatedEventTestFactory.create(command); - - assertThatThrownBy(() -> listener.createItem(event)).isInstanceOf(ValidationException.class); - + @BeforeEach + void mock() { + when(service.create(any(), any())).thenReturn(VorgangAttachedItemTestFactory.create()); } @DisplayName("should call service") @@ -91,7 +98,7 @@ class VorgangAttachedItemEventListenerTest { void withItem() { listener.createItem(CommandCreatedEventTestFactory.create(command)); - verify(service).create(anyString(), itemCaptor.capture()); + verify(service).create(eq(CommandTestFactory.ID), itemCaptor.capture()); assertThat(itemCaptor.getValue()).usingRecursiveComparison() .ignoringFields(VorgangAttachedItem.FIELDNAME_ID, VorgangAttachedItem.FIELDNAME_VERSION) .isEqualTo(VorgangAttachedItemTestFactory.create()); @@ -101,7 +108,7 @@ class VorgangAttachedItemEventListenerTest { void withIdAndVersionAsNull() { listener.createItem(CommandCreatedEventTestFactory.create(command)); - verify(service).create(anyString(), itemCaptor.capture()); + verify(service).create(eq(CommandTestFactory.ID), itemCaptor.capture()); var item = itemCaptor.getValue(); assertThat(item.getId()).isNull(); @@ -112,37 +119,73 @@ class VorgangAttachedItemEventListenerTest { void withCommandId() { listener.createItem(CommandCreatedEventTestFactory.create(command)); - verify(service).create(eq(CommandTestFactory.ID), any()); + verify(service).create(any(), any()); } + } + } + @DisplayName("Validate create command") + @Nested + class TestValidateCreateCommand { + + @Test + void shouldThrowExceptionOnIdMismatching() { + var command = CommandTestFactory.createBuilder().vorgangId("other").build(); + + assertThatThrownBy(() -> listener.validateCreateCommand(command)).isInstanceOf(ValidationException.class); } } + @DisplayName("Update item") @Nested class TestUpdateItem { - private Map<String, Object> bodyMap = VorgangAttachedItemTestFactory.asMap(); + private final Map<String, Object> bodyMap = VorgangAttachedItemTestFactory.asMap(); private Command command = buildCommand(bodyMap); @Captor private ArgumentCaptor<VorgangAttachedItem> itemCaptor; + @Captor + private ArgumentCaptor<Map<String, Object>> itemMapCaptor; + @Captor + private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor; + + @BeforeEach + void mock() { + doReturn(VorgangAttachedItemTestFactory.create()).when(listener).getVorgangAttachedItem(any()); + } + + @Test + void shouldGetVorgangAttachedItem() { + doUpdateItem(); + + verify(listener).getVorgangAttachedItem(VorgangAttachedItemTestFactory.ID); + } + + @Test + void shouldCallSetCommandPreviousState() { + doUpdateItem(); + + verify(listener).setCommandPreviousState(CommandTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); + } @DisplayName("service Call") @Nested class TestCallService { + @Test void shouldHaveItem() { - listener.updateItem(CommandCreatedEventTestFactory.create(command)); + doUpdateItem(); - verify(service).update(anyString(), itemCaptor.capture()); + verify(service).update(eq(CommandTestFactory.ID), itemCaptor.capture()); assertThat(itemCaptor.getValue()).usingRecursiveComparison().isEqualTo(VorgangAttachedItemTestFactory.create()); } @Test void shouldHaveCommand() { - listener.updateItem(CommandCreatedEventTestFactory.create(command)); + doUpdateItem(); - verify(service).update(eq(CommandTestFactory.ID), any()); + verify(service).update(any(), any()); } @Test @@ -152,9 +195,27 @@ class VorgangAttachedItemEventListenerTest { listener.updateItem(CommandCreatedEventTestFactory.create(buildCommand(bMap))); - verify(service).update(anyString(), itemCaptor.capture()); + verify(service).update(eq(CommandTestFactory.ID), itemCaptor.capture()); assertThat(itemCaptor.getValue().getId()).isEqualTo(VorgangAttachedItemTestFactory.ID); } + + @DisplayName("should call forceUpdate when") + @ParameterizedTest(name = "relationVersion is {0}") + @NullSource + @ValueSource(longs = { -10, 0 }) + void shouldCallForceUpdate(Long relationVersion) { + command = CommandTestFactory.createBuilder().relationId(VorgangAttachedItemTestFactory.ID).relationVersion(relationVersion) + .bodyObject(bodyMap) + .build(); + + listener.updateItem(CommandCreatedEventTestFactory.create(command)); + + verify(service).forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); + } + } + + private void doUpdateItem() { + listener.updateItem(CommandCreatedEventTestFactory.create(command)); } private Command buildCommand(Map<String, Object> bodyMap) { @@ -163,11 +224,77 @@ class VorgangAttachedItemEventListenerTest { } } + @DisplayName("Get vorgangAttachedItem") + @Nested + class TestGetVorgangAttachedItem { + + @BeforeEach + void mock() { + when(service.getById(any())).thenReturn(VorgangAttachedItemTestFactory.create()); + } + + @Test + void shouldCallService() { + listener.getVorgangAttachedItem(VorgangAttachedItemTestFactory.ID); + + verify(service).getById(VorgangAttachedItemTestFactory.ID); + } + + @Test + void shouldReturnValue() { + var item = listener.getVorgangAttachedItem(VorgangAttachedItemTestFactory.ID); + + assertThat(item).usingRecursiveComparison().isEqualTo(VorgangAttachedItemTestFactory.create()); + } + } + + @DisplayName("Set command previous state") + @Nested + class TestSetCommandPreviousState { + + @Test + void shouldCallCommandService() { + listener.setCommandPreviousState(CommandTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); + + verify(commandService).setPreviousState(CommandTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); + } + } + + @DisplayName("Patch item") @Nested class TestPatchItem { - private Map<String, Object> bodyMap = Map.of("id", VorgangAttachedItemTestFactory.ID, "item", Map.of("number", "73")); - private Command command = buildCommand(bodyMap); + @Captor + private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor; + + private final Map<String, Object> itemMap = Map.of("number", "73"); + private final Set<String> itemKeys = Set.of("number"); + private final Map<String, Object> bodyMap = Map.of("id", VorgangAttachedItemTestFactory.ID, VorgangAttachedItem.FIELDNAME_ITEM, + itemMap); + + private final Command command = CommandTestFactory.createBuilder().relationId(VorgangAttachedItemTestFactory.ID) + .relationVersion(VorgangAttachedItemTestFactory.VERSION).bodyObject(bodyMap).build(); + + private final Map<String, Object> patchMap = Collections.emptyMap(); + + @BeforeEach + void mock() { + doReturn(patchMap).when(listener).getExistingItemMapEntries(any(), any()); + } + + @Test + void shouldBuildPatchMap() { + doPatchItem(); + + verify(listener).getExistingItemMapEntries(CommandTestFactory.RELATION_ID, itemKeys); + } + + @Test + void shouldCallSetCommandPreviousState() { + doPatchItem(); + + verify(listener).setCommandPreviousState(CommandTestFactory.ID, patchMap); + } @DisplayName("service Call") @Nested @@ -175,22 +302,66 @@ class VorgangAttachedItemEventListenerTest { @SuppressWarnings("unchecked") @Test void shouldHaveItem() { - listener.patchItem(CommandCreatedEventTestFactory.create(command)); + doPatchItem(); - verify(service).patch(any(), (Map<String, Object>) eq(bodyMap.get("item"))); + verify(service).patch(any(), (Map<String, Object>) eq(bodyMap.get(VorgangAttachedItem.FIELDNAME_ITEM))); } @Test void shouldHaveCommand() { - listener.patchItem(CommandCreatedEventTestFactory.create(command)); + doPatchItem(); verify(service).patch(eq(command), any()); } } - private Command buildCommand(Map<String, Object> bodyMap) { - return CommandTestFactory.createBuilder().relationId(VorgangAttachedItemTestFactory.ID) - .relationVersion(VorgangAttachedItemTestFactory.VERSION).bodyObject(bodyMap).build(); + private void doPatchItem() { + listener.patchItem(CommandCreatedEventTestFactory.create(command)); + } + } + + @DisplayName("Build patch map") + @Nested + class TestBuildPatchMap { + + private final Set<String> commandPatchKeySet = Set.of("KeyToPatch", "KeyWithNullValueToPatch"); + private final Map<String, Object> itemMap = buildItemMap(); + + private Map<String, Object> buildItemMap() { + var itemMap = new HashMap<String, Object>(); + itemMap.put("KeyToPatch", "ValueToPatch"); + itemMap.put("KeyToIgnore", "ValueToIgnore"); + itemMap.put("KeyWithNullValueToPatch", null); + return itemMap; + } + + private final VorgangAttachedItem vorgangAttachedItem = VorgangAttachedItemTestFactory.createBuilder().item(itemMap).build(); + + @BeforeEach + void mock() { + when(service.getById(any())).thenReturn(vorgangAttachedItem); + } + + @Test + void shouldCallGetVorgangAttachedItem() { + listener.getExistingItemMapEntries(VorgangAttachedItemTestFactory.ID, commandPatchKeySet); + + verify(service).getById(VorgangAttachedItemTestFactory.ID); + } + + @Test + void shouldReturnMapWithPatchEntry() { + var patchMap = listener.getExistingItemMapEntries(VorgangAttachedItemTestFactory.ID, commandPatchKeySet); + + assertThat(patchMap).hasSize(2).containsEntry("KeyToPatch", "ValueToPatch"); + } + + @Test + void shouldReturnMapWithNullPatchEntry() { + var patchMap = listener.getExistingItemMapEntries(VorgangAttachedItemTestFactory.ID, commandPatchKeySet); + + assertThat(patchMap).hasSize(2).containsKey("KeyWithNullValueToPatch"); + assertThat(patchMap.get("KeyWithNullValueToPatch")).isNull(); } } @@ -198,7 +369,7 @@ class VorgangAttachedItemEventListenerTest { @DisplayName("Delete item") class TestDeleteItem { - private Command command = CommandTestFactory.createBuilder().relationId(VorgangAttachedItemTestFactory.ID) + private final Command command = CommandTestFactory.createBuilder().relationId(VorgangAttachedItemTestFactory.ID) .relationVersion(VorgangAttachedItemTestFactory.VERSION).build(); @Nested @@ -242,27 +413,87 @@ class VorgangAttachedItemEventListenerTest { } } + @DisplayName("On revoke create item") @Nested - class TestRevokeCommand { + class TestOnRevokeCreateItem { - @Nested - class TestDeleteItem { + private final Command command = CommandTestFactory.create(); + private final RevokeCommandEvent revokeEvent = new RevokeCommandEvent(command); - @Test - void shouldCallService() { - listener.onRevokeDeleteItem(new RevokeCommandEvent(CommandTestFactory.createBuilder().build())); + @Test + void shouldCallService() { + listener.onRevokeCreateItem(revokeEvent); - verify(service).unmarkAsDeleteByIdAndVersion(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION); - } + verify(service).revokeCreate(command); + } + } - @Test - void shouldCallHandleException() { - doThrow(new RuntimeException()).when(service).unmarkAsDeleteByIdAndVersion(anyString(), anyLong()); + @DisplayName("On revoke delete item") + @Nested + class TestOnRevokeDeleteItem { - listener.onRevokeDeleteItem(new RevokeCommandEvent(CommandTestFactory.createBuilder().build())); + private final Command command = CommandTestFactory.create(); + private final RevokeCommandEvent revokeEvent = new RevokeCommandEvent(command); - verify(publisher).publishEvent(any(CommandRevokeFailedEvent.class)); - } + @Test + void shouldCallService() { + listener.onRevokeDeleteItem(revokeEvent); + + verify(service).revokeDelete(command); + } + } + + @DisplayName("On revoke update item") + @Nested + class TestOnRevokeUpdateItem { + + private final Command command = CommandTestFactory.create(); + private final RevokeCommandEvent revokeEvent = new RevokeCommandEvent(command); + + @BeforeEach + void mock() { + when(commandService.getById(any())).thenReturn(command); + } + + @Test + void shouldGetCommand() { + listener.onRevokeUpdateItem(revokeEvent); + + verify(commandService).getById(CommandTestFactory.ID); + } + + @Test + void shouldCallService() { + listener.onRevokeUpdateItem(revokeEvent); + + verify(service).revokeUpdate(command, CommandTestFactory.PREVIOUS_STATE); + } + } + + @DisplayName("On revoke patch item") + @Nested + class TestOnRevokePatchItem { + + private final Command command = CommandTestFactory.create(); + private final RevokeCommandEvent revokeEvent = new RevokeCommandEvent(command); + + @BeforeEach + void mock() { + when(commandService.getById(any())).thenReturn(CommandTestFactory.create()); + } + + @Test + void shouldGetCommand() { + listener.onRevokePatchItem(revokeEvent); + + verify(commandService).getById(CommandTestFactory.ID); + } + + @Test + void shouldCallService() { + listener.onRevokePatchItem(revokeEvent); + + verify(service).revokePatch(command, CommandTestFactory.PREVIOUS_STATE); } } -} +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java index 79ce07e580ab83de2d02838ad6837fc009116650..cfaf9ff74c00daa0d1405a656fb898ce181a0256 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemITCase.java @@ -29,6 +29,8 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -46,13 +48,16 @@ import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.security.access.AccessDeniedException; import de.ozgcloud.command.Command; -import de.ozgcloud.command.CommandCreatedEvent; +import de.ozgcloud.command.CommandStatus; import de.ozgcloud.common.test.DataITCase; import de.ozgcloud.vorgang.callcontext.CallContextUserTestFactory; import de.ozgcloud.vorgang.callcontext.CurrentUserService; -import de.ozgcloud.vorgang.command.CommandCreatedEventTestFactory; +import de.ozgcloud.vorgang.command.CommandService; import de.ozgcloud.vorgang.command.CommandTestFactory; +import de.ozgcloud.vorgang.command.CreateCommandRequest; +import de.ozgcloud.vorgang.command.CreateCommandRequestTestFactory; import de.ozgcloud.vorgang.command.Order; +import de.ozgcloud.vorgang.command.RevokeCommandEventTestFactory; import de.ozgcloud.vorgang.vorgang.Vorgang; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; import de.ozgcloud.vorgang.vorgang.ZustaendigeStelleTestFactory; @@ -73,23 +78,30 @@ class VorgangAttachedItemITCase { private ApplicationEventPublisher publisher; @Autowired private MongoOperations mongoOperations; + @Autowired + private CommandService commandService; @BeforeEach void prepareDatabase() { + mongoOperations.dropCollection(Vorgang.COLLECTION_NAME); mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME); + mongoOperations.dropCollection(Command.COLLECTION_NAME); } @DisplayName("Create item") @Nested class TestCreateItem { - private Command command = CommandTestFactory.createBuilder().order(Order.CREATE_ATTACHED_ITEM.toString()) - .relationId(VorgangTestFactory.ID).bodyObject(VorgangAttachedItemTestFactory.asMap()).build(); - private CommandCreatedEvent event = CommandCreatedEventTestFactory.create(command); + private final CreateCommandRequest createCommandRequest = CreateCommandRequestTestFactory.createBuilder() + .order(Order.CREATE_ATTACHED_ITEM.name()) + .relationId(VorgangTestFactory.ID) + .bodyObject(VorgangAttachedItemTestFactory.asMap()) + .build(); @Test void shouldPersistInDatabase() { - publisher.publishEvent(event); + commandService.createCommand(createCommandRequest); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { var loadedItems = mongoOperations.findAll(VorgangAttachedItem.class); @@ -106,8 +118,7 @@ class VorgangAttachedItemITCase { @Nested class TestUpdateItem { - private Command command; - private CommandCreatedEvent event; + private CreateCommandRequest createCommandRequest; private VorgangAttachedItem persistedItem; @@ -123,18 +134,16 @@ class VorgangAttachedItemITCase { var bodyObjectMap = new HashMap<>(VorgangAttachedItemTestFactory.asMap()); bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_ITEM, itemMap); - command = CommandTestFactory.createBuilder() + createCommandRequest = CreateCommandRequestTestFactory.createBuilder() .order(Order.UPDATE_ATTACHED_ITEM.name()) .relationId(persistedItem.getId()) .relationVersion(persistedItem.getVersion()) .bodyObject(bodyObjectMap).build(); - - event = CommandCreatedEventTestFactory.create(command); } @Test void shouldIncrementVersion() { - publisher.publishEvent(event); + commandService.createCommand(createCommandRequest); await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { var loaded = mongoOperations.findAll(VorgangAttachedItem.class); @@ -258,4 +267,221 @@ class VorgangAttachedItemITCase { return responseCaptor.getValue(); } } + + @DisplayName("Revoke create item") + @Nested + class TestRevokeCreateItem { + + private VorgangAttachedItem persistedItem; + + @BeforeEach + void mock() { + persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build()); + } + + @Test + void shouldDeleteVorgangAttachedItem() { + publisher.publishEvent(RevokeCommandEventTestFactory.create(createRevokeCommand())); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(findVorgangAttachedItems()).isEmpty()); + } + + private Command createRevokeCommand() { + return CommandTestFactory.createBuilder().order(Order.CREATE_ATTACHED_ITEM.name()) + .relationId(persistedItem.getId()) + .relationVersion(persistedItem.getVersion()) + .bodyObject(VorgangAttachedItemTestFactory.asMap()).build(); + } + } + + @DisplayName("Revoke update item") + @Nested + class TestRevokeUpdateItem { + + private final Map<String, Object> createItem = Map.<String, Object>of("EntryToRecoverByRevokeKey", "EntryToRecoverByRevokeValue", + VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, VorgangAttachedItemTestFactory.ITEM_FIELD_STRING_VALUE); + private VorgangAttachedItem persistedItem; + + private final Map<String, Object> updateItem = Map.<String, Object>of( + VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, "RevertByRevokeValue", + "NewToRemoveByRevokeKey", "NewToRemoveByRevokeValue"); + private Command updatedCommand; + private String vorgangId; + + @BeforeEach + void mock() { + vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).version(0).build()).getId(); + createVorgangAttachedItem(); + + persistedItem = findVorgangAttachedItems().get(0); + + updateVorgangAttchedItem(); + } + + private void createVorgangAttachedItem() { + commandService.createCommand(createAttachedItemCommandRequest()); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(findCommands()).hasSize(1); + var vorgangAttachedItems = findVorgangAttachedItems(); + assertThat(vorgangAttachedItems).hasSize(1); + assertThat(vorgangAttachedItems.get(0).getVersion()).isEqualTo(1); + assertThat(vorgangAttachedItems.get(0).getItem()).isEqualTo(createItem); + }); + } + + private CreateCommandRequest createAttachedItemCommandRequest() { + return CreateCommandRequestTestFactory.createBuilder() + .order(Order.CREATE_ATTACHED_ITEM.name()) + .vorgangId(vorgangId) + .relationId(vorgangId) + .relationVersion(0L) + .bodyObject(createCreateCommandBodyObject(0L, createItem)) + .build(); + } + + private void updateVorgangAttchedItem() { + updatedCommand = commandService.createCommand(createUpdateCommandRequest()); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(findCommands()).hasSize(2); + assertThat(findVorgangAttachedItems()).hasSize(1); + assertThat(findVorgangAttachedItems().get(0).getVersion()).isEqualTo(2); + assertThat(findVorgangAttachedItems().get(0).getItem()).isEqualTo(updateItem); + }); + } + + private CreateCommandRequest createUpdateCommandRequest() { + return CreateCommandRequestTestFactory.createBuilder() + .order(Order.UPDATE_ATTACHED_ITEM.name()) + .vorgangId(vorgangId) + .relationId(persistedItem.getId()) + .relationVersion(persistedItem.getVersion()) + .bodyObject(createCreateCommandBodyObject(1L, updateItem)) + .build(); + } + + private Map<String, Object> createCreateCommandBodyObject(Long relationVersion, Map<String, Object> bodyMap) { + var bodyObjectMap = new HashMap<>(VorgangAttachedItemTestFactory.asMap()); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_ITEM, new HashMap<>(bodyMap)); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_VORGANG_ID, vorgangId); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_VERSION, relationVersion); + return bodyObjectMap; + } + + @Test + void shouldRestoreItemValues() { + commandService.revokeCommand(updatedCommand.getId()); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + var vorgangAttachedItem = findVorgangAttachedItems().get(0); + assertThat(findVorgangAttachedItems()).hasSize(1); + assertThat(vorgangAttachedItem.getVersion()).isEqualTo(3); + var updateCommand = mongoOperations.findById(updatedCommand.getId(), Command.class); + assertThat(updateCommand.getStatus()).isEqualTo(CommandStatus.REVOKED); + assertThat(vorgangAttachedItem.getItem()).containsEntry("EntryToRecoverByRevokeKey", "EntryToRecoverByRevokeValue"); + assertThat(vorgangAttachedItem.getItem()).containsEntry(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, + VorgangAttachedItemTestFactory.ITEM_FIELD_STRING_VALUE); + assertThat(vorgangAttachedItem.getItem()).doesNotContainKey("NewToRemoveByRevokeKey"); + }); + } + } + + @DisplayName("Revoke patch item") + @Nested + class TestRevokePatchItem { + + private final Map<String, Object> createItem = Map.<String, Object>of(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, + VorgangAttachedItemTestFactory.ITEM_FIELD_STRING_VALUE); + private VorgangAttachedItem persistedItem; + + private final Map<String, Object> patchItem = Map.<String, Object>of(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, "UpdatedStringValue"); + private Command patchedCommand; + private String vorgangId; + + @BeforeEach + void mock() { + vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).version(0).build()).getId(); + createVorgangAttachedItem(); + + persistedItem = findVorgangAttachedItems().get(0); + + patchVorgangAttchedItem(); + } + + private void createVorgangAttachedItem() { + commandService.createCommand(createAttachedItemCommandRequest()); + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(findCommands()).hasSize(1); + var vorgangAttachedItems = findVorgangAttachedItems(); + assertThat(vorgangAttachedItems).hasSize(1); + assertThat(vorgangAttachedItems.get(0).getVersion()).isEqualTo(1); + assertThat(vorgangAttachedItems.get(0).getItem()).containsEntry(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, + VorgangAttachedItemTestFactory.ITEM_FIELD_STRING_VALUE); + }); + } + + private CreateCommandRequest createAttachedItemCommandRequest() { + return CreateCommandRequestTestFactory.createBuilder() + .order(Order.CREATE_ATTACHED_ITEM.name()) + .vorgangId(vorgangId) + .relationId(vorgangId) + .relationVersion(0L) + .bodyObject(createCreateCommandBodyObject(0L, createItem)) + .build(); + } + + private void patchVorgangAttchedItem() { + patchedCommand = commandService.createCommand(createPatchCommandRequest()); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + assertThat(findCommands()).hasSize(2); + var vorgangAttachedItems = findVorgangAttachedItems(); + assertThat(vorgangAttachedItems).hasSize(1); + assertThat(vorgangAttachedItems.get(0).getVersion()).isEqualTo(2); + assertThat(vorgangAttachedItems.get(0).getItem()).containsEntry(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, "UpdatedStringValue"); + }); + } + + private CreateCommandRequest createPatchCommandRequest() { + return CreateCommandRequestTestFactory.createBuilder() + .order(Order.PATCH_ATTACHED_ITEM.name()) + .vorgangId(vorgangId) + .relationId(persistedItem.getId()) + .relationVersion(persistedItem.getVersion()) + .bodyObject(createCreateCommandBodyObject(1L, patchItem)) + .build(); + } + + private Map<String, Object> createCreateCommandBodyObject(Long relationVersion, Map<String, Object> bodyMap) { + var bodyObjectMap = new HashMap<>(VorgangAttachedItemTestFactory.asMap()); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_ITEM, new HashMap<>(bodyMap)); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_VORGANG_ID, vorgangId); + bodyObjectMap.put(VorgangAttachedItem.FIELDNAME_VERSION, relationVersion); + return bodyObjectMap; + } + + @Test + void shouldRestoreItemValues() { + commandService.revokeCommand(patchedCommand.getId()); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + var updateCommand = mongoOperations.findById(patchedCommand.getId(), Command.class); + assertThat(updateCommand.getStatus()).isEqualTo(CommandStatus.REVOKED); + assertThat(findVorgangAttachedItems()).hasSize(1); + var vorgangAttachedItem = findVorgangAttachedItems().get(0); + assertThat(vorgangAttachedItem.getVersion()).isEqualTo(3); + assertThat(findVorgangAttachedItems().get(0).getItem()).containsEntry(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, + VorgangAttachedItemTestFactory.ITEM_FIELD_STRING_VALUE); + }); + } + } + + private List<VorgangAttachedItem> findVorgangAttachedItems() { + return mongoOperations.findAll(VorgangAttachedItem.class); + } + + private List<Command> findCommands() { + return mongoOperations.findAll(Command.class); + } } \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapperTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapperTest.java index 93c89a4951f318bff83ecea305ef89a26a2f74f7..db159bbd05feb088276177df9becc3ab69dca57a 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapperTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemMapperTest.java @@ -40,7 +40,7 @@ class VorgangAttachedItemMapperTest { void shouldBeFilled() { var item = mapper.fill(itemAsMap); - assertThat(item).usingRecursiveComparison().isEqualTo(VorgangAttachedItemTestFactory.create()); + assertThat(item).usingRecursiveComparison().ignoringFields(VorgangAttachedItem.FIELDNAME_ID) + .isEqualTo(VorgangAttachedItemTestFactory.create()); } - -} +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java index 8145e0f6cd15c9f4c51d9323b9e2f2fae33ff819..8292b3c695855fa04b2e12cfc06e7cae04b2ca6f 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemRepositoryITCase.java @@ -28,6 +28,7 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.Collections; import java.util.ConcurrentModificationException; +import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -43,6 +44,8 @@ import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; @DataITCase class VorgangAttachedItemRepositoryITCase { + private static final Map<String, Object> UPDATE_ITEM_MAP = Map.of("dontTouch", 600); + @Autowired private VorgangAttachedItemRepository repository; @Autowired @@ -145,19 +148,90 @@ class VorgangAttachedItemRepositoryITCase { } } + @Nested + class TestUpdate { + + @BeforeEach + void init() { + persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build()); + } + + @Test + void shouldUpdate() { + var result = updateItem(); + + assertThat(result.getItem()).containsExactlyEntriesOf(UPDATE_ITEM_MAP); + assertThat(result.getVersion()).isEqualTo(2); + } + + @Test + void shouldNotUpdateDeletedItem() { + persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build()); + + var result = updateItem(); + + assertThat(result.getItem()).isEqualTo(VorgangAttachedItemTestFactory.STRING_MAP); + assertThat(result.getVersion()).isEqualTo(1); + } + + @Test + void shouldFailOnCollision() { + var itemId = persistedItem.getId(); + + assertThrows(ConcurrentModificationException.class, () -> repository.update(itemId, 0, UPDATE_ITEM_MAP)); + } + + VorgangAttachedItem updateItem() { + repository.update(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP); + return mongoOperations.findById(persistedItem.getId(), VorgangAttachedItem.class); + } + + } + + @Nested + class TestForceUpdate { + + @BeforeEach + void init() { + persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build()); + } + + @Test + void shouldUpdate() { + var result = forceUpdateItem(); + + assertThat(result.getItem()).containsExactlyEntriesOf(UPDATE_ITEM_MAP); + assertThat(result.getVersion()).isEqualTo(2); + } + + @Test + void shouldNotUpdateDeletedItem() { + persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build()); + + var result = forceUpdateItem(); + + assertThat(result.getItem()).isEqualTo(VorgangAttachedItemTestFactory.STRING_MAP); + assertThat(result.getVersion()).isEqualTo(1); + } + + VorgangAttachedItem forceUpdateItem() { + repository.forceUpdate(persistedItem.getId(), UPDATE_ITEM_MAP); + return mongoOperations.findById(persistedItem.getId(), VorgangAttachedItem.class); + } + + } + @Nested class TestPatch { @BeforeEach void prepareDatabase() { - mongoOperations.dropCollection(VorgangAttachedItem.COLLECTION_NAME); - persistedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build()); } @Test void shouldUpdateFieldValue() { - repository.patch(persistedItem.getId(), persistedItem.getVersion(), buildPatchMap()); + repository.patch(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP); var loaded = findVorgangAttachedItem(); assertThat(loaded).isNotNull(); @@ -178,7 +252,7 @@ class VorgangAttachedItemRepositoryITCase { @Test void shouldIncreaseVersion() { - repository.patch(persistedItem.getId(), persistedItem.getVersion(), buildPatchMap()); + repository.patch(persistedItem.getId(), persistedItem.getVersion(), UPDATE_ITEM_MAP); var loaded = findVorgangAttachedItem(); assertThat(loaded).isNotNull(); @@ -186,45 +260,51 @@ class VorgangAttachedItemRepositoryITCase { } @Test - void shouldNotUpdateOnWrongVersion() { - repository.patch(persistedItem.getId(), 42, buildPatchMap()); - - var loaded = findVorgangAttachedItem(); + void shouldThrowExceptionWhenVersionMismatch() { + mongoOperations.save(persistedItem); - assertThat(loaded).usingRecursiveComparison().isEqualTo(persistedItem); + var itemId = persistedItem.getId(); + assertThrows(ConcurrentModificationException.class, () -> repository.patch(itemId, 42, UPDATE_ITEM_MAP)); } @Test void shouldNotPatchDeletedItem() { var deletedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build()); - repository.patch(deletedItem.getId(), deletedItem.getVersion(), buildPatchMap()); + repository.patch(deletedItem.getId(), deletedItem.getVersion(), UPDATE_ITEM_MAP); var loaded = mongoOperations.findById(deletedItem.getId(), VorgangAttachedItem.class); assertThat(loaded).usingRecursiveComparison().isEqualTo(deletedItem); } - private Map<String, Object> buildPatchMap() { - return Map.of("dontTouch", 600); - } } @Nested - class TestForceUpdate { + class TestForcePatch { + + @Test + void shouldPatch() { + var expectedItemMap = new HashMap<>(VorgangAttachedItemTestFactory.STRING_MAP); + expectedItemMap.putAll(UPDATE_ITEM_MAP); + var attachedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).build()); + + repository.forcePatch(attachedItem.getId(), UPDATE_ITEM_MAP); + + var result = mongoOperations.findById(attachedItem.getId(), VorgangAttachedItem.class); + assertThat(result.getItem()).containsExactlyEntriesOf(expectedItemMap); + assertThat(result.getVersion()).isEqualTo(2); + } @Test void shouldNotPatchDeletedItem() { var deletedItem = mongoOperations.save(VorgangAttachedItemTestFactory.createBuilder().id(null).version(0).deleted(true).build()); - repository.forcePatch(deletedItem.getId(), buildPatchMap()); + repository.forcePatch(deletedItem.getId(), UPDATE_ITEM_MAP); var loaded = mongoOperations.findById(deletedItem.getId(), VorgangAttachedItem.class); assertThat(loaded).usingRecursiveComparison().isEqualTo(deletedItem); } - private Map<String, Object> buildPatchMap() { - return Map.of("dontTouch", 600); - } } @Nested diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceITCase.java index eccdc065fd841e0119aa7074808c44d9aa23655c..6bafdceabe71af6f52b99993f7a89d309f612924 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceITCase.java @@ -2,6 +2,8 @@ package de.ozgcloud.vorgang.attached_item; import static org.assertj.core.api.Assertions.*; +import jakarta.validation.ConstraintViolationException; + import org.apache.commons.lang3.StringUtils; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -11,7 +13,6 @@ import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.common.test.ITCase; import de.ozgcloud.vorgang.command.CommandTestFactory; -import jakarta.validation.ConstraintViolationException; @ITCase class VorgangAttachedItemServiceITCase { @@ -30,25 +31,21 @@ class VorgangAttachedItemServiceITCase { void shouldDenyMissingClientName() { var item = VorgangAttachedItemTestFactory.createBuilder().client(StringUtils.EMPTY).build(); - assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)) - .isInstanceOf(ConstraintViolationException.class); + assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)).isInstanceOf(ConstraintViolationException.class); } @Test void shouldDenyMissingVorgangId() { var item = VorgangAttachedItemTestFactory.createBuilder().vorgangId(StringUtils.EMPTY).build(); - assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)) - .isInstanceOf(ConstraintViolationException.class); + assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)).isInstanceOf(ConstraintViolationException.class); } @Test void shouldDenyMissingItemName() { var item = VorgangAttachedItemTestFactory.createBuilder().itemName(StringUtils.EMPTY).build(); - assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)) - .isInstanceOf(ConstraintViolationException.class); + assertThatThrownBy(() -> service.create(CommandTestFactory.ID, item)).isInstanceOf(ConstraintViolationException.class); } } - } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java index 126d872284154d990daa7e429e7d866ca4988fae..932e678e5585088726653434c5e55ee0210fc3a9 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemServiceTest.java @@ -36,12 +36,16 @@ import java.util.UUID; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.command.Command; +import de.ozgcloud.command.CommandRevokeFailedEvent; +import de.ozgcloud.command.CommandRevokedEvent; import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; import de.ozgcloud.vorgang.command.CommandTestFactory; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; @@ -108,42 +112,51 @@ class VorgangAttachedItemServiceTest { assertThat(prepared.isDeleted()).isFalse(); } } - } + @DisplayName("Update item") @Nested class TestUpdateItem { + @Captor + private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor; + @Test - void shouldCallRepository() { + void shouldCallDoUpate() { service.update(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create()); - verify(repository).patch(any(), anyLong(), any()); + verify(service).doUpdate(VorgangAttachedItemTestFactory.ID, VERSION, STRING_MAP); } @Test void shouldPublishEvent() { service.update(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create()); - verify(publisher).publishEvent(any(VorgangAttachedItemUpdatedEvent.class)); + verify(publisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue()).extracting(VorgangAttachedItemUpdatedEvent::getSource).isEqualTo(CommandTestFactory.ID); } } + @DisplayName("Force update") @Nested class TestForceUpdateItem { + @Captor + private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor; + @Test void shouldCallRepository() { - service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create()); + service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); - verify(repository).forcePatch(eq(VorgangAttachedItemTestFactory.ID), any()); + verify(repository).forceUpdate(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); } @Test void shouldPublishEvent() { - service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create()); + service.forceUpdate(CommandTestFactory.ID, VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.STRING_MAP); - verify(publisher).publishEvent(any(VorgangAttachedItemUpdatedEvent.class)); + verify(publisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue()).extracting(VorgangAttachedItemUpdatedEvent::getSource).isEqualTo(CommandTestFactory.ID); } } @@ -156,7 +169,6 @@ class VorgangAttachedItemServiceTest { verify(repository).findByIdAndDeleted(PostfachNachrichtTestFactory.ID, VorgangAttachedItemService.NOT_DELETED); } - } @Nested @@ -195,23 +207,24 @@ class VorgangAttachedItemServiceTest { @Nested class TestPatch { - @Test - void shouldCallRepository() { - Map<String, Object> propertyMap = Map.of(VorgangAttachedItemTestFactory.ITEM_FIELD_NAME, - VorgangAttachedItemTestFactory.ITEM_FIELD_INT_VALUE); - var item = VorgangAttachedItemTestFactory.createBuilder().item(propertyMap).build(); - var command = buildCommand(propertyMap); + private final Command command = CommandTestFactory.create(); + + @Captor + private ArgumentCaptor<VorgangAttachedItemUpdatedEvent> eventCaptor; - service.patch(command, item.getItem()); + @Test + void shouldCallDoPatch() { + service.patch(command, VorgangAttachedItemTestFactory.STRING_MAP); - verify(repository).patch(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.VERSION, propertyMap); + verify(service).doPatch(command, CommandTestFactory.RELATION_VERSION, VorgangAttachedItemTestFactory.STRING_MAP); } - private Command buildCommand(Map<String, Object> bodyMap) { - return CommandTestFactory.createBuilder() - .relationId(VorgangAttachedItemTestFactory.ID) - .relationVersion(VorgangAttachedItemTestFactory.VERSION) - .bodyObject(bodyMap).build(); + @Test + void shouldPublishEvent() { + service.update(CommandTestFactory.ID, VorgangAttachedItemTestFactory.create()); + + verify(publisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue()).extracting(VorgangAttachedItemUpdatedEvent::getSource).isEqualTo(CommandTestFactory.ID); } } @@ -230,8 +243,8 @@ class VorgangAttachedItemServiceTest { } @Nested - class TestGetById { + @Test void shouldReturnAttachedItem() { var item = VorgangAttachedItemTestFactory.create(); @@ -297,7 +310,7 @@ class VorgangAttachedItemServiceTest { @Nested @DisplayName("Delete item by vorgangId") - class TestDelete { + class TestDeleteByVorgangId { @Test void shouldCallDeleteMethod() { @@ -307,4 +320,164 @@ class VorgangAttachedItemServiceTest { } } + + @DisplayName("Delete") + @Nested + class TestDelete { + + @Test + void shouldCallRepository() { + service.delete(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.VERSION); + + verify(repository).delete(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.VERSION); + } + } + + @DisplayName("Do patch") + @Nested + class TestDoPatch { + + @Test + void shouldCallRepository() { + service.doPatch(CommandTestFactory.create(), CommandTestFactory.RELATION_VERSION, STRING_MAP); + + verify(repository).patch(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION, STRING_MAP); + } + } + + @DisplayName("Do Update") + @Nested + class TestDoUpdate { + + @Test + void shouldCallRepository() { + service.doUpdate(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.VERSION, STRING_MAP); + + verify(repository).update(VorgangAttachedItemTestFactory.ID, VorgangAttachedItemTestFactory.VERSION, STRING_MAP); + } + } + + @DisplayName("Revoke create") + @Nested + class TestRevokeCreate { + + private final Command command = CommandTestFactory.create(); + + @Test + void shouldCallHandleRevoke() { + service.revokeCreate(command); + + verify(service).handleRevoke(eq(command), any(Runnable.class)); + } + + @Test + void shouldCallDelete() { + service.revokeCreate(command); + + verify(service).delete(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION); + } + } + + @DisplayName("Revoke delete") + @Nested + class TestRevokeDelete { + + private final Command command = CommandTestFactory.create(); + + @Test + void shouldCallHandleRevoke() { + service.revokeDelete(command); + + verify(service).handleRevoke(eq(command), any(Runnable.class)); + } + + @Test + void shouldCallDelete() { + service.revokeDelete(command); + + verify(service).unmarkAsDeleteByIdAndVersion(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION); + } + } + + @DisplayName("Revoke Patch") + @Nested + class TestRevokePatch { + + private final Command command = CommandTestFactory.create(); + + @Test + void shouldCallHandleRevoke() { + service.revokePatch(command, STRING_MAP); + + verify(service).handleRevoke(eq(command), any(Runnable.class)); + } + + @Test + void shouldCallPatch() { + service.revokePatch(command, STRING_MAP); + + verify(service).doPatch(command, CommandTestFactory.RELATION_VERSION + 1, STRING_MAP); + } + } + + @DisplayName("Revoke Update") + @Nested + class TestRevokeUpdate { + + private final Command command = CommandTestFactory.create(); + + @Test + void shouldCallHandleRevoke() { + service.revokeUpdate(command, STRING_MAP); + + verify(service).handleRevoke(eq(command), any(Runnable.class)); + } + + @Test + void shouldCallPatch() { + service.revokeUpdate(command, STRING_MAP); + + verify(service).doUpdate(CommandTestFactory.RELATION_ID, CommandTestFactory.RELATION_VERSION + 1, STRING_MAP); + } + } + + @DisplayName("Handle revoke") + @Nested + class TestHandleRevoke { + + @Captor + private ArgumentCaptor<CommandRevokedEvent> commandRevokedEventCaptor; + @Captor + private ArgumentCaptor<CommandRevokeFailedEvent> commandRevokedFailedEventCaptor; + + @Mock + private Runnable runnable; + + private final Command command = CommandTestFactory.create(); + + @Test + void shouldCallServiceFunction() { + service.handleRevoke(command, runnable); + + verify(runnable).run(); + } + + @Test + void shouldPublishCommandRevokedEvent() { + service.handleRevoke(command, runnable); + + verify(publisher).publishEvent(commandRevokedEventCaptor.capture()); + assertThat(commandRevokedEventCaptor.getValue().getSource()).isEqualTo(command); + } + + @Test + void shouldPublishCommandRevokeFailedEventOnException() { + doThrow(new RuntimeException()).when(runnable).run(); + + service.handleRevoke(command, runnable); + + verify(publisher).publishEvent(commandRevokedFailedEventCaptor.capture()); + assertThat(commandRevokedFailedEventCaptor.getValue().getSource()).isEqualTo(CommandTestFactory.ID); + } + } } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemTestFactory.java index 58bf4f855c2a2d04302354cdc898ca1e5afbde38..b0a8dbbf4dc0a6fe293e7e68d0d4b5b141be81b9 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemTestFactory.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/attached_item/VorgangAttachedItemTestFactory.java @@ -40,25 +40,16 @@ public class VorgangAttachedItemTestFactory { public static final long VERSION = 1L; public static final String CLIENT = LoremIpsum.getInstance().getFirstName() + "Client"; - public static final String ITEM_NAME = "test"; + public static final String ITEM_NAME = "test"; public static final String ITEM_FIELD_NAME = "number"; public static final Integer ITEM_FIELD_INT_VALUE = 42; public static final String ITEM_FIELD_STRING_VALUE = "73"; - public static final String FIELD_NAME_VORGANG_ID = "vorgangId"; - public static final String FIELD_NAME_VORGANG_VERSION = "vorgangVersion"; - public static final String FIELD_NAME_CREATED_AT = "createdAt"; - public static final String CREATED_BY_FIELD_NAME = "createdBy"; - public static final String CREATED_BY_NAME_FIELD_NAME = "createdByName"; - public static final String BETREFF_FIELD_NAME = "betreff"; - public static final String BESCHREIBUNG_FIELD_NAME = "beschreibung"; - public static final String ATTACHMENTS_FIELD_NAME = "attachments"; - public static final FileId ATTACHMENT_ID = FileId.createNew(); static final List<FileId> ATTACHMENTS = List.of(ATTACHMENT_ID, ATTACHMENT_ID); - static final Map<String, Object> STRING_MAP = Map.of(ITEM_FIELD_NAME, ITEM_FIELD_STRING_VALUE); + public static final Map<String, Object> STRING_MAP = Map.of(ITEM_FIELD_NAME, ITEM_FIELD_STRING_VALUE); public static VorgangAttachedItem create() { return createBuilder().build(); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..acd60e5393d455769e639db9f61e24cf23e11f2a --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationITCase.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.*; +import static org.mockito.Mockito.*; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.test.annotation.DirtiesContext; + +import de.ozgcloud.apilib.user.OzgCloudUserId; +import de.ozgcloud.apilib.user.OzgCloudUserProfile; +import de.ozgcloud.apilib.user.OzgCloudUserProfileService; +import de.ozgcloud.command.Command; +import de.ozgcloud.command.CommandStatus; +import de.ozgcloud.common.test.DataITCase; +import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; +import de.ozgcloud.vorgang.callcontext.CallContext; +import de.ozgcloud.vorgang.callcontext.WithMockCustomUser; +import de.ozgcloud.vorgang.command.CommandService; +import de.ozgcloud.vorgang.command.CommandTestFactory; +import de.ozgcloud.vorgang.command.CreateCommandRequest; +import de.ozgcloud.vorgang.vorgang.Vorgang; +import de.ozgcloud.vorgang.vorgang.VorgangHead; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; +import de.ozgcloud.vorgang.vorgang.ZustaendigeStelleTestFactory; + +@SpringBootTest(properties = { + "grpc.server.inProcessName=test", + "grpc.client.vorgang-manager.address=in-process:test", + "grpc.client.ozgcloud-command-manager.address=in-process:test", +}) +@DataITCase +@WithMockCustomUser +@DirtiesContext +class CollaborationITCase { + + @Autowired + private CommandService commandService; + + @Autowired + private MongoOperations mongoOperations; + + @MockBean + private OzgCloudUserProfileService ozgCloudUserProfileService; + @Mock + private OzgCloudUserProfile ozgCloudUserProfile; + + @BeforeEach + void init() { + mongoOperations.dropCollection(Command.class); + mongoOperations.dropCollection(Vorgang.class); + mongoOperations.dropCollection(VorgangAttachedItem.class); + when(ozgCloudUserProfile.getId()).thenReturn(OzgCloudUserId.from(CommandTestFactory.CREATED_BY)); + when(ozgCloudUserProfileService.getById(any())).thenReturn(ozgCloudUserProfile); + } + + @Nested + class TestCreateCollaborationVorgang { + + private static final String FIELD_COLLABORATION_VORGANG_ID = "collaborationVorgangId"; + private static final String TITEL = "Collaboration Vorgang"; + private static final String ANFRAGE = "Anfrage"; + + private String vorgangId; + + @BeforeEach + void init() { + vorgangId = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).version(0L).build()).getId(); + } + + @Test + void shouldSetCollaborationVorgangId() { + var command = commandService.createCommand(buildCreateCollaborationVorgangCommand(vorgangId)); + + waitUntilCommandFinished(command.getId()); + + var collaborationRequests = loadCollaborationRequest(vorgangId); + assertThat(collaborationRequests).hasSize(1).first().extracting(VorgangAttachedItem::getItem, MAP) + .containsKey(FIELD_COLLABORATION_VORGANG_ID); + } + + @Test + void shouldCreateCollaborationVorgang() { + var command = commandService.createCommand(buildCreateCollaborationVorgangCommand(vorgangId)); + + waitUntilCommandFinished(command.getId()); + + var collaborationVorgang = loadCollaborationVorgang(vorgangId); + assertThat(collaborationVorgang.getHeader()).extracting(VorgangHead::getCollaborationLevel) + .isEqualTo(CreateCollaborationVorgangRequestTestFactory.COLLABORATION_LEVEL); + assertThat(collaborationVorgang.getHeader()).extracting(VorgangHead::getOrganisationsEinheitId) + .isEqualTo(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + assertThat(collaborationVorgang.getClientAttributes()).isEmpty(); + } + + private CreateCommandRequest buildCreateCollaborationVorgangCommand(String vorgangId) { + return CreateCommandRequest.builder() + .callContext(CallContext.builder().client("test").build()) + .vorgangId(vorgangId) + .relationId(vorgangId) + .order("CREATE_COLLABORATION_REQUEST") + .bodyObject(Map.of( + "titel", TITEL, + "anfrage", ANFRAGE, + "zustaendigeStelle", ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID + )) + .build(); + } + + private void waitUntilCommandFinished(String commandId) { + await().atMost(60, TimeUnit.SECONDS).until( + () -> mongoOperations.findById(commandId, Command.class), + command -> command.getStatus() == CommandStatus.FINISHED + ); + } + + private Vorgang loadCollaborationVorgang(String sourceVorgangId) { + var collaborationVorgangId = loadCollaborationRequest(sourceVorgangId).getFirst().getItem().get(FIELD_COLLABORATION_VORGANG_ID); + return mongoOperations.findById(collaborationVorgangId, Vorgang.class); + } + + private List<VorgangAttachedItem> loadCollaborationRequest(String vorgangId) { + var query = new Query(new Criteria().andOperator( + Criteria.where(VorgangAttachedItem.FIELDNAME_VORGANG_ID).is(vorgangId), + Criteria.where(VorgangAttachedItem.FIELDNAME_ITEM_NAME).is("CollaborationRequest") + )); + return mongoOperations.find(query, VorgangAttachedItem.class); + } + } + +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationRepositoryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationRepositoryITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..b44c3d827a851c7e9b30c28919fb112647914d81 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationRepositoryITCase.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import de.ozgcloud.common.test.DataITCase; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; + +@DataITCase +class CollaborationRepositoryITCase { + + @Autowired + private CollaborationRepository repository; + + @Test + void shouldSaveVorgang() { + var vorgang = VorgangTestFactory.createBuilder().id(null).build(); + + var savedVorgang = repository.save(vorgang); + + assertThat(savedVorgang.getId()).isNotNull(); + assertThat(savedVorgang).usingRecursiveComparison().ignoringFields("id").isEqualTo(vorgang); + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..01e063f2c45e20dcbd8a8fa3bbc4caa589205784 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CollaborationServiceTest.java @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; + +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.context.ApplicationEventPublisher; + +import de.ozgcloud.command.VorgangCreatedEvent; +import de.ozgcloud.vorgang.servicekonto.ServiceKonto; +import de.ozgcloud.vorgang.servicekonto.ServiceKontoTestFactory; +import de.ozgcloud.vorgang.vorgang.Vorgang; +import de.ozgcloud.vorgang.vorgang.VorgangHead; +import de.ozgcloud.vorgang.vorgang.VorgangHeadTestFactory; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; +import de.ozgcloud.vorgang.vorgang.ZustaendigeStelleTestFactory; + +class CollaborationServiceTest { + + @Spy + @InjectMocks + private CollaborationService service; + + @Mock + private CollaborationRepository collaborationRepository; + @Mock + private ApplicationEventPublisher publisher; + + @Nested + class TestCreateCollaborationVorgang { + + private static final CreateCollaborationVorgangRequest CREATE_COLLABORATION_VORGANG_REQUEST = CreateCollaborationVorgangRequestTestFactory.create(); + private static final String COLLABORATION_VORGANG_ID = "collaboration-vorgang-id"; + private static final Vorgang SAVED_VORGANG = VorgangTestFactory.createBuilder().id(COLLABORATION_VORGANG_ID).build(); + + @Mock + private Vorgang collaborationVorgang; + @Captor + private ArgumentCaptor<VorgangCreatedEvent> eventCaptor; + + @BeforeEach + void init() { + doReturn(collaborationVorgang).when(service).buildCollaborationVorgang(any()); + when(collaborationRepository.save(any())).thenReturn(SAVED_VORGANG); + } + + @Test + void shouldCallBuildCollaborationVorgang() { + createCollaborationVorgang(); + + verify(service).buildCollaborationVorgang(CREATE_COLLABORATION_VORGANG_REQUEST); + } + + @Test + void shouldCallSaveVorgang() { + createCollaborationVorgang(); + + verify(collaborationRepository).save(collaborationVorgang); + } + + @Test + void shouldPublishEvent() { + createCollaborationVorgang(); + + verifyNoInteractions(publisher); + // TODO: aktiviere, wenn VorgangCreatedEvent veröffentlicht wird + // verify(publisher).publishEvent(eventCaptor.capture()); + // assertThat(eventCaptor.getValue().getSource()).isEqualTo(COLLABORATION_VORGANG_ID); + } + + @Test + void shouldReturnVorgang() { + var result = createCollaborationVorgang(); + + assertThat(result).isSameAs(SAVED_VORGANG); + } + + private Vorgang createCollaborationVorgang() { + return service.createCollaborationVorgang(CREATE_COLLABORATION_VORGANG_REQUEST); + } + } + + @Nested + class TestBuildCollaborationVorgang { + + private static final CreateCollaborationVorgangRequest COLLABORATION_REQUEST = CreateCollaborationVorgangRequestTestFactory.createBuilder() + .vorgang(VorgangTestFactory.createBuilder().formEngineName(VorgangTestFactory.FORM_ENGINE_NAME).build()).build(); + + private static final VorgangHead UPDATED_HEAD = VorgangHeadTestFactory.create(); + + @BeforeEach + void init() { + doReturn(UPDATED_HEAD).when(service).getHeader(any()); + } + + @Test + void shouldSetVorgangName() { + var result = buildCollaborationVorgang(); + + assertThat(result.getName()).isEqualTo(VorgangTestFactory.NAME); + } + + @Test + void shouldSetVorgangNummer() { + var result = buildCollaborationVorgang(); + + assertThat(result.getNummer()).isEqualTo(VorgangTestFactory.VORGANG_NUMMER); + } + + @Test + void shouldSetFormEngineName() { + var result = buildCollaborationVorgang(); + + assertThat(result.getFormEngineName()).isEqualTo(VorgangTestFactory.FORM_ENGINE_NAME); + } + + @Test + void shouldSetAktenzeichen() { + var result = buildCollaborationVorgang(); + + assertThat(result.getAktenzeichen()).isEqualTo(VorgangTestFactory.AKTENZEICHEN); + } + + @Test + void shouldCallGetHeader() { + buildCollaborationVorgang(); + + verify(service).getHeader(COLLABORATION_REQUEST); + } + + @Test + void shouldSetHeader() { + var result = buildCollaborationVorgang(); + + assertThat(result.getHeader()).isEqualTo(UPDATED_HEAD); + } + + @Test + void shouldSetEingangs() { + var result = buildCollaborationVorgang(); + + assertThat(result.getEingangs()).containsExactly(VorgangTestFactory.EINGANG); + } + + @Test + void shouldSetCreatedAt() { + var result = buildCollaborationVorgang(); + + assertThat(result.getCreatedAt()).isCloseTo(ZonedDateTime.now(), within(1, ChronoUnit.SECONDS)); + } + + @Test + void shouldSetInCreationFalse() { + var result = buildCollaborationVorgang(); + + assertThat(result.isInCreation()).isFalse(); + } + + @Test + void shouldSetStatusNeu() { + var result = buildCollaborationVorgang(); + + assertThat(result.getStatus()).isEqualTo(Vorgang.Status.NEU); + } + + @Test + void shouldNotSetAssignedTo() { + var result = buildCollaborationVorgang(); + + assertThat(result.getAssignedTo()).isNull(); + } + + @Test + void shouldNotSetClientAttributes() { + var result = buildCollaborationVorgang(); + + assertThat(result.getClientAttributes()).isEmpty(); + } + + @Test + void shouldNotSetForwardings() { + var result = buildCollaborationVorgang(); + + assertThat(result.getForwardings()).isEmpty(); + } + + private Vorgang buildCollaborationVorgang() { + return service.buildCollaborationVorgang(COLLABORATION_REQUEST); + } + } + + @Nested + class TestGetHeader { + + private static final ServiceKonto SERVICE_KONTO = ServiceKontoTestFactory.create(); + + @Test + void shouldSetZustaendigeStelle() { + var result = getHeader(); + + assertThat(result.getOrganisationsEinheitId()).isEqualTo(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID); + } + + @Test + void shouldSetCollaborationLevel() { + var result = getHeader(); + + assertThat(result.getCollaborationLevel()).isEqualTo(CreateCollaborationVorgangRequestTestFactory.COLLABORATION_LEVEL); + } + + @Test + void shouldKeepServiceKonto() { + var result = getHeader(); + + assertThat(result.getServiceKonto()).isSameAs(SERVICE_KONTO); + } + + private VorgangHead getHeader() { + return service.getHeader(CreateCollaborationVorgangRequestTestFactory.createBuilder().vorgang(VorgangTestFactory.createBuilder().header( + VorgangHeadTestFactory.createBuilder().serviceKonto(SERVICE_KONTO).build()).build()).build()); + } + } + +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapperTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..45e76253f7a9e06bd7c221d1a7e4c0a56e46ae32 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestMapperTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class CreateCollaborationVorgangRequestMapperTest { + + private final CreateCollaborationVorgangRequestMapper mapper = Mappers.getMapper(CreateCollaborationVorgangRequestMapper.class); + + @Test + void shouldMapFromGrpc() { + var result = mapper.mapFrom(GrpcCollaborationRequestTestFactory.create()); + + assertThat(result).usingRecursiveComparison().ignoringFields("vorgang").isEqualTo(CreateCollaborationVorgangRequestTestFactory.create()); + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..642dd5385a37c04998ee35664c908cd91a1866f4 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/CreateCollaborationVorgangRequestTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequest.CreateCollaborationVorgangRequestBuilder; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; +import de.ozgcloud.vorgang.vorgang.ZustaendigeStelleTestFactory; + +public class CreateCollaborationVorgangRequestTestFactory { + + public static final int COLLABORATION_LEVEL = 1; + + public static CreateCollaborationVorgangRequest create() { + return createBuilder().build(); + } + + public static CreateCollaborationVorgangRequestBuilder createBuilder() { + return CreateCollaborationVorgangRequest.builder() + .vorgang(VorgangTestFactory.create()) + .zustaendigeStelle(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID) + .collaborationLevel(COLLABORATION_LEVEL); + } + +} diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCollaborationRequestTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCollaborationRequestTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e46337e88b4364406238abb4ae3bfa0cece7ecae --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCollaborationRequestTestFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import de.ozgcloud.vorgang.vorgang.GrpcCollaborationRequest; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; +import de.ozgcloud.vorgang.vorgang.ZustaendigeStelleTestFactory; + +public class GrpcCollaborationRequestTestFactory { + + public static GrpcCollaborationRequest create() { + return createBuilder().build(); + } + + public static GrpcCollaborationRequest.Builder createBuilder() { + return GrpcCollaborationRequest.newBuilder() + .setVorgangId(VorgangTestFactory.ID) + .setZustaendigeStelle(ZustaendigeStelleTestFactory.ORGANISATIONSEINHEIT_ID) + .setCollaborationLevel(CreateCollaborationVorgangRequestTestFactory.COLLABORATION_LEVEL); + } +} diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCreateCollaborationVorgangResponseTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCreateCollaborationVorgangResponseTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..1851256f15a55aeb97fc63b77399d1e71a41ae6d --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/collaboration/GrpcCreateCollaborationVorgangResponseTestFactory.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 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.vorgang.collaboration; + +import java.util.UUID; + +import de.ozgcloud.vorgang.vorgang.GrpcCreateCollaborationVorgangResponse; + +public class GrpcCreateCollaborationVorgangResponseTestFactory { + + public static final String VORGANG_ID = UUID.randomUUID().toString(); + + public static GrpcCreateCollaborationVorgangResponse create() { + return createBuilder().build(); + } + + public static GrpcCreateCollaborationVorgangResponse.Builder createBuilder() { + return GrpcCreateCollaborationVorgangResponse.newBuilder() + .setVorgangId(VORGANG_ID); + } +} diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java index 153af77891fc1df9973ceb7aee9b90251fd32bc7..fe0a42a246cc4018afe82709c0da52cb3a6109cd 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandEventListenerTest.java @@ -95,4 +95,5 @@ class CommandEventListenerTest { verify(commandService).setCommandRevoked(CommandTestFactory.ID); } } + } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandITCase.java index 5a6b4241e64b294b555226694b24e3d72bc97281..cbb89a295b4dde09f94322dc54cad4cf97f7c172 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandITCase.java @@ -127,7 +127,7 @@ class CommandITCase { var response = responseCaptor.getValue(); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { - var command = commandService.findCommand(response.getCommand().getId()); + var command = commandService.findById(response.getCommand().getId()); assertThat(command).isPresent().get().satisfies(cmd -> { assertThat(cmd.getId()).isNotNull(); @@ -309,7 +309,7 @@ class CommandITCase { mongoOperations.dropCollection(Vorgang.class); mongoOperations.save(VorgangTestFactory.createBuilder().status(Status.ANGENOMMEN).build()); - mongoOperations.save(CommandTestFactory.createBuilder() + mongoOperations.save(CommandTestFactory.createBuilder().status(CommandStatus.FINISHED) .relationId(VorgangTestFactory.ID).relationVersion(VorgangTestFactory.VERSION - 1) .previousState(Map.of(Vorgang.MONGODB_FIELDNAME_STATUS, Status.NEU)).build()); } @@ -369,7 +369,7 @@ class CommandITCase { void persistVorgangWithVorgangVerwerfenCommand() { mongoOperations.save(VorgangTestFactory.createBuilder().status(Status.VERWORFEN).build()); - mongoOperations.save(CommandTestFactory.createBuilder().relationId(VorgangTestFactory.ID) + mongoOperations.save(CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).relationId(VorgangTestFactory.ID) .relationVersion(VorgangTestFactory.VERSION - 1).order(Order.VORGANG_VERWERFEN.toString()) .previousState(Map.of(Vorgang.MONGODB_FIELDNAME_STATUS, Status.IN_BEARBEITUNG)) .build()); @@ -380,7 +380,7 @@ class CommandITCase { callServiceRevokeCommand(); await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { - var command = commandService.findCommand(responseCaptor.getValue().getCommand().getId()); + var command = commandService.findById(responseCaptor.getValue().getCommand().getId()); assertThat(command).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.REVOKED); }); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java index cebb7e89bf49c7a6b6e4f4e71df53c4ec6f22020..6f99e9b91bef7f1664e80194b2755173a0c1da37 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRepositoryITCase.java @@ -105,7 +105,7 @@ class CommandRepositoryITCase { repository.finishCommand(command.getId()); - var result = repository.getById(command.getId()); + var result = repository.findById(command.getId()); assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.FINISHED); } @@ -117,7 +117,7 @@ class CommandRepositoryITCase { repository.finishCommand(command.getId()); - var result = repository.getById(command.getId()); + var result = repository.findById(command.getId()); assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(commandStatus); } @@ -127,7 +127,7 @@ class CommandRepositoryITCase { repository.finishCommand(command.getId()); - var result = repository.getById(command.getId()).get(); + var result = repository.findById(command.getId()).get(); assertThat(result.getFinishedAt()).isNotNull(); } @@ -137,7 +137,7 @@ class CommandRepositoryITCase { repository.finishCommand(command.getId(), CommandTestFactory.CREATED_RESOURCE); - var result = repository.getById(command.getId()).get(); + var result = repository.findById(command.getId()).get(); assertThat(result.getStatus()).isEqualTo(CommandStatus.FINISHED); } @@ -147,7 +147,7 @@ class CommandRepositoryITCase { repository.finishCommand(command.getId(), CommandTestFactory.CREATED_RESOURCE); - var result = repository.getById(command.getId()).get(); + var result = repository.findById(command.getId()).get(); assertThat(result.getFinishedAt()).isNotNull(); } @@ -159,7 +159,7 @@ class CommandRepositoryITCase { repository.finishCommand(savedCommand.getId(), CommandTestFactory.CREATED_RESOURCE); - var result = repository.getById(savedCommand.getId()).get(); + var result = repository.findById(savedCommand.getId()).get(); assertThat(result.getCreatedResource()).isEqualTo(CommandTestFactory.CREATED_RESOURCE); } @@ -170,7 +170,7 @@ class CommandRepositoryITCase { repository.finishCommand(savedCommand.getId(), CommandTestFactory.CREATED_RESOURCE); - var result = repository.getById(savedCommand.getId()).get(); + var result = repository.findById(savedCommand.getId()).get(); assertThat(result.getCreatedResource()).isEqualTo(CommandTestFactory.CREATED_RESOURCE); } } @@ -198,7 +198,7 @@ class CommandRepositoryITCase { } private void verifyCommandExists(String id) { - Optional<Command> command = repository.getById(id); + Optional<Command> command = repository.findById(id); assertThat(command).isPresent(); assertThat(command.get().getId()).isEqualTo(id); @@ -218,7 +218,7 @@ class CommandRepositoryITCase { void shouldUpdateCommandStatus(CommandStatus status) { repository.updateCommandStatus(CommandTestFactory.ID, status); - Optional<Command> command = repository.getById(CommandTestFactory.ID); + Optional<Command> command = repository.findById(CommandTestFactory.ID); assertThat(command).isPresent(); assertThat(command.get().getStatus()).isEqualTo(status); @@ -238,7 +238,7 @@ class CommandRepositoryITCase { void shouldUpdateCommandStatus(CommandStatus status) { repository.updateCommandStatusAndVersion(CommandTestFactory.ID, status, 78L); - Optional<Command> command = repository.getById(CommandTestFactory.ID); + Optional<Command> command = repository.findById(CommandTestFactory.ID); assertThat(command).isPresent(); assertThat(command.get().getRelationVersion()).isEqualTo(78L); @@ -246,6 +246,31 @@ class CommandRepositoryITCase { } } + @Nested + class TestUpdateCommandStatusAndReturnPrevious { + + @BeforeEach + void persistVorgangWithCommand() { + repository.save(CommandTestFactory.create()); + } + + @Test + void shouldUpdateStatus() { + repository.updateCommandStatusAndReturnPrevious(CommandTestFactory.ID, CommandStatus.ERROR); + + var command = mongoOperations.findById(CommandTestFactory.ID, Command.class); + assertThat(command.getStatus()).isEqualTo(CommandStatus.ERROR); + } + + @Test + void shouldReturnPreviousCommand() { + Command previousCommand = repository.updateCommandStatusAndReturnPrevious(CommandTestFactory.ID, CommandStatus.ERROR); + + assertThat(previousCommand.getStatus()).isEqualTo(CommandStatus.PENDING); + } + + } + @Nested class TestGetPendingCommands { @@ -297,7 +322,7 @@ class CommandRepositoryITCase { } @Test - // TODO schreibe ein Testcase für Order, Status und Zeit + // TODO schreibe ein Testcase für Order, Status und Zeit void shouldFindCommand() { var command = repository.save(createCommandBuilder() .status(CommandStatus.FINISHED) @@ -330,7 +355,7 @@ class CommandRepositoryITCase { void shouldSavePreviousStateValue() { repository.patch(command.getId(), Map.of(PREVIOUS_STATE_STATUS_FIELD_KEY, PREVIOUS_STATE_STATUS_VALUE)); - var persitedCommand = repository.getById(command.getId()); + var persitedCommand = repository.findById(command.getId()); assertThat(persitedCommand).isPresent().get() .extracting(command -> ((PersistedCommand) command).getPreviousState(), as(InstanceOfAssertFactories.MAP)) @@ -409,7 +434,7 @@ class CommandRepositoryITCase { repository.addSubCommands(commandId, Map.of("addedKey", "addedValue")); - var result = repository.getById(commandId); + var result = repository.findById(commandId); assertThat(result).isPresent().get().extracting(Command::getBodyObject, as(InstanceOfAssertFactories.MAP)) .containsAllEntriesOf(expectedMap); } @@ -417,7 +442,7 @@ class CommandRepositoryITCase { } @Nested - class TestGetNotFailedParentId { + class TestIsCommandFailed { @BeforeEach void init() { @@ -426,28 +451,22 @@ class CommandRepositoryITCase { @Test void shouldReturnParentId() { - var parentId = mongoOperations.save(CommandTestFactory.createBuilder().bodyObject(Map.of( + var commandId = mongoOperations.save(CommandTestFactory.createBuilder().bodyObject(Map.of( PersistedCommand.PROPERTY_COMPLETE_IF_SUBS_COMPLETED, true)).build()).getId(); - var commandId = mongoOperations.save( - CommandTestFactory.createBuilder().id(null).bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, parentId)).build()) - .getId(); - var result = repository.getNotFailedParentId(commandId); + var result = repository.isCommandFailed(commandId); - assertThat(result).contains(parentId); + assertThat(result).isFalse(); } @Test void shouldReturnEmpty() { - var parentId = mongoOperations.save(CommandTestFactory.createBuilder().status(CommandStatus.ERROR).bodyObject(Map.of( + var commandId = mongoOperations.save(CommandTestFactory.createBuilder().status(CommandStatus.ERROR).bodyObject(Map.of( PersistedCommand.PROPERTY_COMPLETE_IF_SUBS_COMPLETED, true)).build()).getId(); - var commandId = mongoOperations.save( - CommandTestFactory.createBuilder().id(null).bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, parentId)).build()) - .getId(); - var result = repository.getNotFailedParentId(commandId); + var result = repository.isCommandFailed(commandId); - assertThat(result).isEmpty(); + assertThat(result).isTrue(); } } @@ -464,7 +483,7 @@ class CommandRepositoryITCase { @Test void shouldReturnParentId() { var commandId = mongoOperations.save( - CommandTestFactory.createBuilder().id(null).bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, PARENT_ID)).build()) + CommandTestFactory.createBuilder().id(null).bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, PARENT_ID)).build()) .getId(); var parentId = repository.getParentId(commandId); @@ -562,8 +581,8 @@ class CommandRepositoryITCase { private static final String PARENT_ID = "parent-id"; - private final PersistedCommandBuilder commandWithParentIdBuilder = CommandTestFactory.createBuilder().id(null).bodyObject(Map.of( - PersistedCommand.PROPERTY_PARENT_ID, PARENT_ID)); + private static final Command COMMAND_WITH_PARENT_ID = CommandTestFactory.createBuilder().id(null).bodyObject(Map.of( + PersistedCommand.PROPERTY_PARENT_ID, PARENT_ID)).build(); @BeforeEach void init() { @@ -571,32 +590,108 @@ class CommandRepositoryITCase { } @Test - void shouldIgnoreErrorStatus() { - mongoOperations.save(commandWithParentIdBuilder.status(CommandStatus.ERROR).build()); + void shouldFindSubCommandIds() { + var commandId = mongoOperations.save(COMMAND_WITH_PARENT_ID).getId(); - var result = repository.findNotFailedSubCommandIds(PARENT_ID); + var result = repository.findSubCommandIds(PARENT_ID); + + assertThat(result).hasSize(1).first().isEqualTo(commandId); + } + + @Test + void shouldReturnEmpty() { + mongoOperations.save(COMMAND_WITH_PARENT_ID); + + var result = repository.findSubCommandIds("other-parent-id"); assertThat(result).isEmpty(); } + } - @DisplayName("should find commands") - @ParameterizedTest(name = "with status {0}") - @EnumSource(value = CommandStatus.class, names = { "ERROR" }, mode = EnumSource.Mode.EXCLUDE) - void shouldFindCommands(CommandStatus status) { - mongoOperations.save(commandWithParentIdBuilder.status(status).build()); + @Nested + class TestSetRevokeStatus { - var result = repository.findNotFailedSubCommandIds(PARENT_ID); + @Test + void shouldSetCanceled() { + var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.NEW).build()); - assertThat(result).hasSize(1); + repository.setRevokeStatus(command.getId()); + + var result = repository.findById(command.getId()); + assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.CANCELED); } @Test - void shouldReturnEmpty() { - mongoOperations.save(commandWithParentIdBuilder.build()); + void shouldSetRevokePendingWhenPending() { + var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).build()); - var result = repository.findNotFailedSubCommandIds("other-parent-id"); + repository.setRevokeStatus(command.getId()); - assertThat(result).isEmpty(); + var result = repository.findById(command.getId()); + assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.REVOKE_PENDING); + } + + @Test + void shouldSetRevokePendingWhenFinished() { + var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.FINISHED).build()); + + repository.setRevokeStatus(command.getId()); + + var result = repository.findById(command.getId()); + assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(CommandStatus.REVOKE_PENDING); + } + + @DisplayName("should not update when") + @ParameterizedTest(name = "status is {0}") + @EnumSource(value = CommandStatus.class, names = { "NEW", "PENDING", "FINISHED" }, mode = EnumSource.Mode.EXCLUDE) + void shouldNotUpdate(CommandStatus status) { + var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(status).build()); + + repository.setRevokeStatus(command.getId()); + + var result = repository.findById(command.getId()); + assertThat(result).isPresent().get().extracting(Command::getStatus).isEqualTo(status); + } + } + + @Nested + class TestSetRevokeStatusIfNotPending { + + @Test + void shouldSetCanceled() { + var command = createCommand(CommandStatus.NEW); + + repository.setRevokeStatusIfNotPending(command.getId()); + + assertThat(getCommandStatus(command)).isEqualTo(CommandStatus.CANCELED); + } + + @Test + void shouldSetRevokePendingWhenFinished() { + var command = createCommand(CommandStatus.FINISHED); + + repository.setRevokeStatusIfNotPending(command.getId()); + + assertThat(getCommandStatus(command)).isEqualTo(CommandStatus.REVOKE_PENDING); + } + + @DisplayName("should not update when") + @ParameterizedTest(name = "status is {0}") + @EnumSource(value = CommandStatus.class, names = { "NEW", "FINISHED" }, mode = EnumSource.Mode.EXCLUDE) + void shouldNotUpdate(CommandStatus status) { + var command = createCommand(status); + + repository.setRevokeStatusIfNotPending(command.getId()); + + assertThat(getCommandStatus(command)).isEqualTo(status); + } + + private Command createCommand(CommandStatus status) { + return mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(status).build()); + } + + private CommandStatus getCommandStatus(Command command) { + return mongoOperations.findById(command.getId(), Command.class).getStatus(); } } } \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRevokedEventTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRevokedEventTestFactory.java index a54027a07cd0861344dfb06b26026d7a2123e208..526d0a592966588a4a52d33b38ccb9bd783b5b93 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRevokedEventTestFactory.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandRevokedEventTestFactory.java @@ -4,6 +4,7 @@ import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandRevokedEvent; public class CommandRevokedEventTestFactory { + public static CommandRevokedEvent create(Command command) { return new CommandRevokedEvent(command); } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceITCase.java index 5a051d3cc4b06c4afecacc3fff00e08e5e0fc554..36429601be30bae6182023940b10249ba1bd6a6f 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceITCase.java @@ -24,10 +24,14 @@ package de.ozgcloud.vorgang.command; import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; +import java.util.Map; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Nested; @@ -75,15 +79,12 @@ class CommandServiceITCase { @Test void persistCommand() { - Command persistedCommand = commandService.saveCommand( + var persistedCommand = commandService.saveCommand( CommandTestFactory.createBuilder() .relationId(CommandTestFactory.RELATION_ID) .relationVersion(CommandTestFactory.RELATION_VERSION).build()); - Optional<Command> commandOpt = commandService.findCommand(persistedCommand.getId()); - assertThat(commandOpt).isPresent(); - - Command command = commandOpt.get(); + var command = commandService.getById(persistedCommand.getId()); assertThat(command.getId()).hasSize(36); assertThat(command.getCreatedAt()).isNotNull(); assertThat(command.getStatus()).isEqualTo(CommandStatus.PENDING); @@ -92,36 +93,37 @@ class CommandServiceITCase { @Test void persistCommandHappyPath() { - CreateCommandRequest request = CreateCommandRequestTestFactory + var request = CreateCommandRequestTestFactory .createBuilder() .relationId(CommandTestFactory.RELATION_ID) .relationVersion(CommandTestFactory.RELATION_VERSION) .build(); - Command persistedCommand = commandService.createCommand(request); - verify(commandService, timeout(60000)).setCommandFinished(eq(persistedCommand.getId()), any()); - - Optional<Command> commandOpt = commandService.findCommand(persistedCommand.getId()); - assertThat(commandOpt).isPresent(); - - Command command = commandOpt.get(); - assertThat(command.getId()).hasSize(36); - assertThat(command.getCreatedAt()).isNotNull(); - assertThat(command.getCreatedBy()).isEqualTo(request.getCallContext().getUser().getId()); - assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED); - assertThat(command.getRelationId()).isEqualTo(CommandTestFactory.RELATION_ID); + var persistedCommand = commandService.createCommand(request); + + await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { + verify(commandService).setCommandFinished(eq(persistedCommand.getId()), any()); + var command = commandService.getById(persistedCommand.getId()); + assertThat(command.getId()).hasSize(36); + assertThat(command.getCreatedAt()).isNotNull(); + assertThat(command.getCreatedBy()).isEqualTo(request.getCallContext().getUser().getId()); + assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED); + assertThat(command.getRelationId()).isEqualTo(CommandTestFactory.RELATION_ID); + }); } } @Nested class TestVorgangCommandFinished { + private Vorgang vorgang; + @BeforeEach void persistVorgang() { mongoOperations.dropCollection(Vorgang.class); mongoOperations.dropCollection(Command.class); - mongoOperations.save(VorgangTestFactory.create()); + vorgang = mongoOperations.save(VorgangTestFactory.create()); mongoOperations.save(CommandTestFactory.createBuilder().relationId(VorgangTestFactory.ID).build()); } @@ -129,7 +131,7 @@ class CommandServiceITCase { void setCommandFinishedHappyPath() { commandService.setCommandFinished(CommandTestFactory.ID); - Command command = mongoOperations.findById(CommandTestFactory.ID, Command.class); + var command = mongoOperations.findById(CommandTestFactory.ID, Command.class); assertThat(command.getId()).hasSize(36); assertThat(command.getCreatedAt()).isNotNull(); @@ -137,6 +139,37 @@ class CommandServiceITCase { assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED); assertThat(command.getRelationId()).isEqualTo(VorgangTestFactory.ID); } + + @Test + void shouldRevokeFinishedCommand() { + var commandId = saveSubCommandWithFailedParent(); + + commandService.setCommandFinished(commandId); + + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + var commandStatus = mongoOperations.findById(commandId, Command.class).getStatus(); + assertThat(commandStatus).isEqualTo(CommandStatus.REVOKED); + }); + } + + private String saveSubCommandWithFailedParent() { + var parentId = mongoOperations.save(createFailedParentCommand()).getId(); + return mongoOperations.save(createSubCommand(parentId)).getId(); + } + + private Command createFailedParentCommand() { + return CommandTestFactory.createBuilder().id(null).status(CommandStatus.ERROR) + .bodyObject(Map.of( + PersistedCommand.PROPERTY_EXECUTION_MODE, SubCommandExecutionMode.PARALLEL.name(), + PersistedCommand.PROPERTY_COMPLETE_IF_SUBS_COMPLETED, true + )).build(); + } + + private Command createSubCommand(String parentId) { + return CommandTestFactory.createBuilder().id(null).vorgangId(vorgang.getId()) + .previousState(Map.of(CommandRepository.MONGODB_STATUS, Vorgang.Status.NEU.name())) + .bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, parentId)).build(); + } } @Nested @@ -161,7 +194,7 @@ class CommandServiceITCase { @Test void shouldReturnCommand1() { - Optional<Command> command = commandService.findCommand(command1.getId()); + Optional<Command> command = commandService.findById(command1.getId()); assertThat(command).isPresent(); assertThat(command.get().getId()).isEqualTo(command1.getId()); @@ -173,7 +206,7 @@ class CommandServiceITCase { @Test void shouldReturnCommand2() { - Optional<Command> command = commandService.findCommand(command2.getId()); + Optional<Command> command = commandService.findById(command2.getId()); assertThat(command).isPresent(); assertThat(command.get().getId()).isEqualTo(command2.getId()); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java index b34413c437df90ab5766b7421d7a527dd81c8319..85714d30f81e92ba77ce0c7f575e89dcc04343fa 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/CommandServiceTest.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.EnumSource; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -48,7 +50,6 @@ import org.springframework.context.ApplicationEventPublisher; import de.ozgcloud.command.Command; import de.ozgcloud.command.CommandExecutedEvent; -import de.ozgcloud.command.CommandFailedEvent; import de.ozgcloud.command.CommandStatus; import de.ozgcloud.command.RevokeCommandEvent; import de.ozgcloud.common.errorhandling.TechnicalException; @@ -57,6 +58,7 @@ import de.ozgcloud.vorgang.callcontext.CallContextUserTestFactory; import de.ozgcloud.vorgang.callcontext.CurrentUserService; import de.ozgcloud.vorgang.callcontext.UserTestFactory; import de.ozgcloud.vorgang.common.errorhandling.NotFoundException; +import de.ozgcloud.vorgang.common.errorhandling.RevokeFailedException; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; class CommandServiceTest { @@ -162,19 +164,19 @@ class CommandServiceTest { @BeforeEach void mockOperationRepository() { - when(repository.getById(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(repository.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); } @Test void shouldCallOperationRepository() { - service.findCommand(commandId); + service.findById(commandId); - verify(repository).getById(any()); + verify(repository).findById(any()); } @Test void shouldReturnCommand() { - Optional<Command> command = service.findCommand(commandId); + Optional<Command> command = service.findById(commandId); assertThat(command).isPresent(); assertThat(command.get().getId()).isEqualTo(commandId); @@ -203,119 +205,304 @@ class CommandServiceTest { class TestSetCommandFinished { @Test - void shouldCallFinishCommand() { - service.setCommandFinished(CommandTestFactory.ID); + void shouldThrowExceptionIfCommandNotFound() { + doThrow(NotFoundException.class).when(service).getById(anyString()); - verify(service).setCommandFinished(CommandTestFactory.ID, null); + assertThrows(NotFoundException.class, () -> service.setCommandFinished(CommandTestFactory.ID)); } - @Test - void shouldCallRepositoryFinishCommand() { - service.setCommandFinished(CommandTestFactory.ID, null); + @Nested + class TestRevokePendingCommand { + + private static final Command REVOKE_PENDING_COMMAND = CommandTestFactory.createBuilder().status(CommandStatus.REVOKE_PENDING).build(); + + @BeforeEach + void init() { + doReturn(true).when(service).shouldRevoke(any()); + doReturn(REVOKE_PENDING_COMMAND).when(service).getById(anyString()); + } + + @Test + void shouldCallIsRevokeCommand() { + setCommandFinished(); + + verify(service).shouldRevoke(REVOKE_PENDING_COMMAND); + } + + @Test + void shouldCallRepository() { + setCommandFinished(); + + verify(repository).setRevokeStatus(CommandTestFactory.ID); + } + + @Test + void shouldCallPublishRevokeCommandEvent() { + setCommandFinished(); + + verify(service).publishRevokeCommandEvent(REVOKE_PENDING_COMMAND); + } + + @Test + void shouldNotCallFinishCommandIfRevokePending() { + setCommandFinished(); + + verify(repository, never()).finishCommand(anyString()); + verify(repository, never()).finishCommand(anyString(), anyString()); + } - verify(repository).finishCommand(CommandTestFactory.ID); + void setCommandFinished() { + service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + } } - @Test - void shouldCallRepositoryFinishCommandWithCreatedResource() { - service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + @Nested + class TestFinishCommand { + + private static final Command FINISHED_COMMAND = CommandTestFactory.createBuilder().status(CommandStatus.FINISHED).build(); + + @BeforeEach + void init() { + doReturn(false).when(service).isParentCommandFailed(any()); + doReturn(FINISHED_COMMAND).when(service).getById(anyString()); + } + + @Test + void shouldCallSetCommandFinish() { + service.setCommandFinished(CommandTestFactory.ID); - verify(repository).finishCommand(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + verify(service).setCommandFinished(CommandTestFactory.ID, null); + } + + @Test + void shouldCallGetCommand() { + setCommandFinished(); + + verify(service).getById(CommandTestFactory.ID); + } + + @Test + void shouldCallRepositoryFinishCommand() { + service.setCommandFinished(CommandTestFactory.ID, null); + + verify(repository).finishCommand(CommandTestFactory.ID); + } + + @Test + void shouldCallRepositoryFinishCommandWithCreatedResource() { + setCommandFinished(); + + verify(repository).finishCommand(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + } + + @Test + void shouldCallGetCompletableParentId() { + setCommandFinished(); + + verify(service).getCompletableParentId(FINISHED_COMMAND); + } + + @Test + void shouldCallPublishCommandExecutedEvent() { + var parentId = "parent-id"; + doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any()); + + setCommandFinished(); + + verify(service).publishCommandExecutedEvent(parentId); + } + + @DisplayName("should not call publishCommandExecutedEvent when no completable parent id") + @Test + void shouldNotPublishWhenNoParentId() { + doReturn(Optional.empty()).when(service).getCompletableParentId(any()); + + setCommandFinished(); + + verify(service, never()).publishCommandExecutedEvent(anyString()); + } + + void setCommandFinished() { + service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + } } + } + + @Nested + class TestShouldRevoke { @Test - void shouldCallGetCompletableParentId() { - service.setCommandFinished(CommandTestFactory.ID); + void shouldReturnTrueWhenRevokePending() { + var command = CommandTestFactory.createBuilder().status(CommandStatus.REVOKE_PENDING).build(); - verify(service).getCompletableParentId(CommandTestFactory.ID); + var result = service.shouldRevoke(command); + + assertThat(result).isTrue(); } @Test - void shouldCallExistsNotFinishedSubCommands() { - var parentId = "parent-id"; - doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any()); + void shouldRevokeTrueWhenParentFailed() { + doReturn(true).when(service).isParentCommandFailed(any()); - service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + var result = service.shouldRevoke(CommandTestFactory.create()); - verify(repository).existsNotFinishedSubCommands(parentId); + assertThat(result).isTrue(); } + } + + @Nested + class TestIsParentCommandFailed { @Test - void shouldCallPublishCommandExecutedEvent() { - var parentId = "parent-id"; - doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any()); + void shouldCallGetParentId() { + var command = CommandTestFactory.create(); - service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + service.isParentCommandFailed(command); - verify(service).publishCommandExecutedEvent(parentId); + verify(service).getParentId(command); } - @DisplayName("should not call publishCommandExecutedEvent when no parent id") @Test - void shouldNotCallPublishCommandExecutedEvent() { - doReturn(Optional.empty()).when(service).getCompletableParentId(any()); + void shouldReturnFalseWhenParentMissing() { + doReturn(Optional.empty()).when(service).getParentId(any()); - service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + var result = isParentCommandFailed(); - verify(service, never()).publishCommandExecutedEvent(anyString()); + assertThat(result).isFalse(); } - @DisplayName("should not call publishCommandExecutedEvent when not all subcommands are finished") - @Test - void shouldNotCallPublishCommandExecutedEvent1() { - var parentId = "parent-id"; - doReturn(Optional.of(parentId)).when(service).getCompletableParentId(any()); - when(repository.existsNotFinishedSubCommands(any())).thenReturn(true); + @Nested + class TestWithParent { + + private static final String PARENT_ID = "parent-id"; + + @BeforeEach + void init() { + doReturn(Optional.of(PARENT_ID)).when(service).getParentId(any()); + } - service.setCommandFinished(CommandTestFactory.ID, CommandTestFactory.CREATED_RESOURCE); + @Test + void shouldCallRepository() { + isParentCommandFailed(); + + verify(repository).isCommandFailed(PARENT_ID); + } + + @Test + void shouldReturnTrueWhenParentNotFailed() { + when(repository.isCommandFailed(any())).thenReturn(true); + + var result = isParentCommandFailed(); + + assertThat(result).isTrue(); + } - verify(service, never()).publishCommandExecutedEvent(anyString()); + @Test + void shouldReturnFalseWhenParentFailed() { + var result = isParentCommandFailed(); + + assertThat(result).isFalse(); + } + } + + boolean isParentCommandFailed() { + return service.isParentCommandFailed(CommandTestFactory.create()); } } @Nested class TestGetCompletableParentId { + private static final Command COMMAND = CommandTestFactory.create(); + @Test - void shouldGetParentId() { - service.getCompletableParentId(CommandTestFactory.ID); + void shouldCallGetParentId() { + service.getCompletableParentId(COMMAND); - verify(repository).getParentId(CommandTestFactory.ID); + verify(service).getParentId(COMMAND); } @Test - void shouldFilterIfParentCompletableBySubCommands() { - var expectedParentId = "parent-id"; - when(repository.getParentId(anyString())).thenReturn(Optional.of(expectedParentId)); + void shouldReturnEmptyWhenNoParentId() { + doReturn(Optional.empty()).when(service).getParentId(any()); + + var result = service.getCompletableParentId(COMMAND); + + assertThat(result).isEmpty(); + } + + @Nested + class TestCompleteIfSubsCompleted { + + private static final String PARENT_ID = "parent-id"; + + @BeforeEach + void init() { + doReturn(Optional.of(PARENT_ID)).when(service).getParentId(any()); + } + + @Test + void shouldCallIsCompleteIfSubsCompleted() { + service.getCompletableParentId(COMMAND); + + verify(repository).isCompleteIfSubsCompleted(PARENT_ID); + } + + @Test + void shouldCallExistsNotFinishedSubCommands() { + when(repository.isCompleteIfSubsCompleted(anyString())).thenReturn(true); + + service.getCompletableParentId(COMMAND); + + verify(repository).existsNotFinishedSubCommands(PARENT_ID); + } - service.getCompletableParentId(CommandTestFactory.ID); + @Test + void shouldReturnParentId() { + when(repository.isCompleteIfSubsCompleted(anyString())).thenReturn(true); + + var result = service.getCompletableParentId(COMMAND); + + assertThat(result).contains(PARENT_ID); + } - verify(repository).isCompleteIfSubsCompleted(expectedParentId); + @Test + void shouldReturnEmptyWhenNotCompletableBySubCommands() { + var result = service.getCompletableParentId(COMMAND); + + assertThat(result).isEmpty(); + } } + } + + @Nested + class TestGetParentId { + @Test - void shouldReturnParentId() { - var expectedParentId = "parent-id"; - doReturn(Optional.of(expectedParentId)).when(repository).getParentId(anyString()); - when(repository.isCompleteIfSubsCompleted(anyString())).thenReturn(true); + void shouldReturnResult() { + var parentId = "parent-id"; + var command = CommandTestFactory.createBuilder().bodyObject(Map.of(PersistedCommand.PROPERTY_PARENT_ID, parentId)).build(); - var result = service.getCompletableParentId(CommandTestFactory.ID); + var result = service.getParentId(command); - assertThat(result).contains(expectedParentId); + assertThat(result).contains(parentId); } @Test - void shouldReturnEmptyWhenNoParentId() { - var result = service.getCompletableParentId(CommandTestFactory.ID); + void shouldReturnEmptyWhenNoBodyObject() { + var command = CommandTestFactory.createBuilder().bodyObject(null).build(); + + var result = service.getParentId(command); assertThat(result).isEmpty(); } @Test - void shouldReturnEmptyWhenNotCompletableBySubCommands() { - doReturn(Optional.of("parent-id")).when(repository).getParentId(anyString()); + void shouldReturnEmptyWhenNoParentId() { + var command = CommandTestFactory.createBuilder().bodyObject(Map.of()).build(); - var result = service.getCompletableParentId(CommandTestFactory.ID); + var result = service.getParentId(command); assertThat(result).isEmpty(); } @@ -331,13 +518,13 @@ class CommandServiceTest { void shouldCallFindCommand() { service.publishCommandExecutedEvent(CommandTestFactory.ID); - verify(service).findCommand(CommandTestFactory.ID); + verify(service).findById(CommandTestFactory.ID); } @Test void shouldCallPublishMethod() { var expectedCommand = CommandTestFactory.create(); - doReturn(Optional.of(expectedCommand)).when(service).findCommand(anyString()); + doReturn(Optional.of(expectedCommand)).when(service).findById(anyString()); service.publishCommandExecutedEvent(CommandTestFactory.ID); @@ -351,111 +538,171 @@ class CommandServiceTest { class TestSetCommandError { private static final String ERROR_MESSAGE = "error message"; - - @Captor - private ArgumentCaptor<CommandFailedEvent> eventCaptor; + private static final String PARENT_ID = "parent-id"; @Test - void shouldCallRepository() { + void shouldCallRepositorySetErrorMessage() { service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); verify(repository).setErrorMessage(CommandTestFactory.ID, ERROR_MESSAGE); } @Test - void shouldCallFindNotFailedSubCommandIds() { + void shouldCallRepositoryGetParentId() { service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); - verify(repository).findNotFailedSubCommandIds(CommandTestFactory.ID); + verify(repository).getParentId(CommandTestFactory.ID); } - @Test - void shouldPublishCommandFailedEvent() { - var notFailedCommandId = "not-failed-command-id"; - when(repository.findNotFailedSubCommandIds(anyString())).thenReturn(Stream.of(notFailedCommandId)); + @Nested + class TestFailParentAndRevokeSubCommands { - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + private static final Command COMMAND_BEFORE_SET_ERROR_STATUS = CommandTestFactory.createBuilder().id(PARENT_ID).build(); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getSource()).isEqualTo(notFailedCommandId); + @BeforeEach + void init() { + when(repository.getParentId(anyString())).thenReturn(Optional.of(PARENT_ID)); + doReturn(COMMAND_BEFORE_SET_ERROR_STATUS).when(service).setErrorStatus(anyString()); + } + + @Test + void shouldCallSetErrorStatus() { + service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + + verify(service).setErrorStatus(PARENT_ID); + } + + @Test + void shouldCallNotErrorStatus() { + service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + + verify(service).notErrorStatus(COMMAND_BEFORE_SET_ERROR_STATUS); + } + + @Test + void shouldCallHandleCommandError() { + service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + + verify(service).handleCommandError(CommandTestFactory.ID, PARENT_ID); + } } - @Test - void shouldFilterOwnId() { - when(repository.findNotFailedSubCommandIds(anyString())).thenReturn(Stream.of(CommandTestFactory.ID)); + @Nested + class TestParentIdMissing { - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + @BeforeEach + void init() { + doReturn(Optional.empty()).when(repository).getParentId(anyString()); + } + + @Test + void shouldNotCallHandleCommandError() { + service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); - verify(publisher, never()).publishEvent(any()); + verify(service, never()).handleCommandError(anyString(), anyString()); + } } - @Test - void shouldNotCallPublishCommandFailedEventIfNoNotFailedSubCommands() { - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + @Nested + class TestParentAlreadyFailed { + + @Mock + private Command failedParentCommand; + + @BeforeEach + void init() { + when(repository.getParentId(anyString())).thenReturn(Optional.of(PARENT_ID)); + doReturn(failedParentCommand).when(service).setErrorStatus(anyString()); + doReturn(false).when(service).notErrorStatus(any()); + } - verify(publisher, never()).publishEvent(any()); + @Test + void shouldNotCallHandleCommandError() { + service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + + verify(service, never()).handleCommandError(anyString(), anyString()); + } } + } + + @Nested + class TestSetErrorStatus { + @Test - void shouldCallGetNotFailedParentId() { - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + void shouldCallRepositoryUpdateStatus() { + when(repository.updateCommandStatusAndReturnPrevious(anyString(), any())).thenReturn(CommandTestFactory.create()); + + service.setErrorStatus(CommandTestFactory.ID); - verify(repository).getNotFailedParentId(CommandTestFactory.ID); + verify(repository).updateCommandStatusAndReturnPrevious(CommandTestFactory.ID, CommandStatus.ERROR); } @Test - void shouldCallPublishCommandFailedEventForParent() { - var parentId = "parent-id"; - when(repository.getNotFailedParentId(anyString())).thenReturn(Optional.of(parentId)); + void shouldReturnCommand() { + var command = CommandTestFactory.create(); + when(repository.updateCommandStatusAndReturnPrevious(anyString(), any())).thenReturn(command); - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); + var result = service.setErrorStatus(CommandTestFactory.ID); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getSource()).isEqualTo(parentId); + assertThat(result).isSameAs(command); } @Test - void shouldNotCallPublishCommandFailedEventForParentIfNoParent() { - service.setCommandError(CommandTestFactory.ID, ERROR_MESSAGE); - - verify(publisher, never()).publishEvent(any()); + void shouldThrowExceptionIfNotFound() { + assertThrows(NotFoundException.class, () -> service.setErrorStatus(CommandTestFactory.ID)); } } @Nested - class TestCreateCommandFailedEventPublisher { + class TestNotErrorStatus { - private static final String EXPECTED_ERROR_MESSAGE = "error message"; + @DisplayName("should return true when") + @ParameterizedTest(name = "command status is {0}") + @EnumSource(value = CommandStatus.class, names = { "ERROR" }, mode = EnumSource.Mode.EXCLUDE) + void shouldReturnTrue(CommandStatus status) { + var command = CommandTestFactory.createBuilder().status(status).build(); - @Captor - private ArgumentCaptor<CommandFailedEvent> eventCaptor; + var result = service.notErrorStatus(command); + + assertThat(result).isTrue(); + } @Test - void shouldPublishCommandFailedEvent() { - publishCommandFailedEvent(); + void shouldReturnFalse() { + var command = CommandTestFactory.createBuilder().status(CommandStatus.ERROR).build(); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue()).isInstanceOf(CommandFailedEvent.class); + var result = service.notErrorStatus(command); + + assertThat(result).isFalse(); } + } + + @Nested + class TestHandleCommandError { + + private static final String PARENT_ID = "parent-id"; + + @Captor + private ArgumentCaptor<String> errorMessageCaptor; @Test - void shouldSetCommandIdInFailedEvent() { - publishCommandFailedEvent(); + void shouldCallPublishCommandFailedEvent() { + handleCommandError(); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getErrorMessage()).isEqualTo(EXPECTED_ERROR_MESSAGE); + verify(service).publishCommandFailedEvent(eq(PARENT_ID), errorMessageCaptor.capture()); + assertThat(errorMessageCaptor.getValue()).contains(PARENT_ID, CommandTestFactory.ID); } @Test - void shouldSetErrorMessageInFailedEvent() { - publishCommandFailedEvent(); + void shouldCallRevokeSubCommands() { + handleCommandError(); - verify(publisher).publishEvent(eventCaptor.capture()); - assertThat(eventCaptor.getValue().getErrorMessage()).isEqualTo(EXPECTED_ERROR_MESSAGE); + verify(service).revokeSubCommands(PARENT_ID); } - private void publishCommandFailedEvent() { - service.createCommandFailedEventPublisher(EXPECTED_ERROR_MESSAGE).accept(CommandTestFactory.ID); + private void handleCommandError() { + service.handleCommandError(CommandTestFactory.ID, PARENT_ID); } } @@ -471,62 +718,126 @@ class CommandServiceTest { } @Nested - class TestRevokeCommandPending { + class TestRevokeCommand { - final String commandId = CommandTestFactory.ID; + private static final Command REVOKE_PENDING_COMMAND = CommandTestFactory.createBuilder().status(CommandStatus.REVOKE_PENDING).build(); @Test - void shouldThrowException() { - assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.setCommandRevokePending(commandId)); + void shouldCallRevokeCommandSilent() { + doReturn(Optional.of(REVOKE_PENDING_COMMAND)).when(service).revokeCommandSilent(anyString()); + + service.revokeCommand(CommandTestFactory.ID); + + verify(service).revokeCommandSilent(CommandTestFactory.ID); } @Test - void shouldUpdateCommandStatus() { - when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create())); + void shouldCallIsRevokePending() { + doReturn(Optional.of(REVOKE_PENDING_COMMAND)).when(service).revokeCommandSilent(anyString()); - service.setCommandRevokePending(commandId); + service.revokeCommand(CommandTestFactory.ID); - verify(repository).updateCommandStatus(anyString(), any(CommandStatus.class)); + verify(service).isRevokePending(REVOKE_PENDING_COMMAND); } @Test - void shouldCallPublishRevokeCommandEvent() { - when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create())); + void shouldReturnCommand() { + doReturn(Optional.of(REVOKE_PENDING_COMMAND)).when(service).revokeCommandSilent(anyString()); + + var result = service.revokeCommand(CommandTestFactory.ID); - service.setCommandRevokePending(commandId); + assertThat(result).isSameAs(REVOKE_PENDING_COMMAND); + } + + @Test + void shouldThrowNotFoundException() { + doReturn(Optional.empty()).when(service).revokeCommandSilent(anyString()); + + assertThrows(NotFoundException.class, () -> service.revokeCommand(CommandTestFactory.ID)); + } + + @Test + void shouldThrowRevokeFailedException() { + doReturn(Optional.of(CommandTestFactory.create())).when(service).revokeCommandSilent(anyString()); + doReturn(false).when(service).isRevokePending(any()); - verify(service).publishRevokeCommandEvent(any(String.class)); + assertThrows(RevokeFailedException.class, () -> service.revokeCommand(CommandTestFactory.ID)); } } @Nested - class TestFiringRevokeCommand { + class TestRevokeCommandSilent { - final String commandId = CommandTestFactory.ID; + private static final Command COMMAND = CommandTestFactory.create(); + + @BeforeEach + void init() { + when(repository.setRevokeStatusIfNotPending(anyString())).thenReturn(Optional.of(COMMAND)); + } @Test - void shoudlFireEvent() { - when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.create())); + void shouldCallRepository() { + service.revokeCommandSilent(CommandTestFactory.ID); + + verify(repository).setRevokeStatusIfNotPending(CommandTestFactory.ID); + } - service.publishRevokeCommandEvent(commandId); + @Test + void shouldCallIsRevokePending() { + service.revokeCommandSilent(CommandTestFactory.ID); - verify(publisher).publishEvent(any(RevokeCommandEvent.class)); + verify(service).isRevokePending(COMMAND); } @Test - void shoudlThrowTechnicalExceptionBecauseCommandNotFound() { - when(repository.getById(commandId)).thenReturn(Optional.empty()); + void shouldCallPublishRevokeCommandEvent() { + doReturn(true).when(service).isRevokePending(any()); - assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.publishRevokeCommandEvent(commandId)); + service.revokeCommandSilent(CommandTestFactory.ID); + + verify(service).publishRevokeCommandEvent(COMMAND); } + } + + @Nested + class TestIsRevokePending { @Test - void shoudlThrowTechnicalExceptionBecauseNoPreviousState() { - when(repository.getById(commandId)).thenReturn(Optional.of(CommandTestFactory.createBuilder().previousState(null).build())); + void shouldReturnTrue() { + var command = CommandTestFactory.createBuilder().status(CommandStatus.REVOKE_PENDING).build(); - assertThatExceptionOfType(TechnicalException.class).isThrownBy(() -> service.publishRevokeCommandEvent(commandId)); + var result = service.isRevokePending(command); + + assertThat(result).isTrue(); } + @DisplayName("should return false when") + @ParameterizedTest(name = "command status is {0}") + @EnumSource(value = CommandStatus.class, names = { "REVOKE_PENDING" }, mode = EnumSource.Mode.EXCLUDE) + void shouldReturnFalse(CommandStatus status) { + var command = CommandTestFactory.createBuilder().status(status).build(); + + var result = service.isRevokePending(command); + + assertThat(result).isFalse(); + } + } + + @Nested + class TestPublishRevokeCommandEvent { + + @Captor + private ArgumentCaptor<RevokeCommandEvent> eventCaptor; + + @Test + void shouldPublishEvent() { + var command = CommandTestFactory.create(); + + service.publishRevokeCommandEvent(command); + + verify(publisher).publishEvent(eventCaptor.capture()); + assertThat(eventCaptor.getValue().getSource()).isSameAs(command); + } } @Nested @@ -615,14 +926,14 @@ class CommandServiceTest { @BeforeEach void init() { doReturn(SUB_COMMAND_VALUES).when(service).buildSubCommandValues(any()); - doReturn(PARENT_COMMAND).when(service).getNotFailedCommand(any()); + doReturn(PARENT_COMMAND).when(service).getPendingCommand(any()); } @Test void shouldCallGetNotFailedCommand() { service.createSubCommands(CreateSubCommandsRequestTestFactory.create()); - verify(service).getNotFailedCommand(CreateSubCommandsRequestTestFactory.PARENT_ID); + verify(service).getPendingCommand(CreateSubCommandsRequestTestFactory.PARENT_ID); } @Test @@ -673,38 +984,33 @@ class CommandServiceTest { } @Nested - class TestGetNotFailedCommand { + class TestGetPendingCommand { @Test void shouldCallFindCommand() { - doReturn(Optional.of(CommandTestFactory.create())).when(service).findCommand(anyString()); + doReturn(Optional.of(CommandTestFactory.create())).when(service).findById(anyString()); - service.getNotFailedCommand(CommandTestFactory.ID); + service.getPendingCommand(CommandTestFactory.ID); - verify(service).findCommand(CommandTestFactory.ID); + verify(service).findById(CommandTestFactory.ID); } - @Test - void shouldThrowExceptionIfCommandNotFound() { - doReturn(Optional.empty()).when(service).findCommand(anyString()); + @DisplayName("should throw exception when") + @ParameterizedTest(name = "command status is {0}") + @EnumSource(value = CommandStatus.class, names = { "PENDING" }, mode = EnumSource.Mode.EXCLUDE) + void shouldThrowExceptionIfCommandFailed(CommandStatus status) { + var command = CommandTestFactory.createBuilder().status(status).build(); + doReturn(command).when(service).getById(anyString()); - assertThrows(NotFoundException.class, () -> service.getNotFailedCommand(CommandTestFactory.ID)); - } - - @Test - void shouldThrowExceptionIfCommandFailed() { - var command = CommandTestFactory.createBuilder().status(CommandStatus.ERROR).build(); - doReturn(Optional.of(command)).when(service).findCommand(anyString()); - - assertThrows(TechnicalException.class, () -> service.getNotFailedCommand(CommandTestFactory.ID)); + assertThrows(TechnicalException.class, () -> service.getPendingCommand(CommandTestFactory.ID)); } @Test void shouldReturnCommand() { var command = CommandTestFactory.create(); - doReturn(Optional.of(command)).when(service).findCommand(anyString()); + doReturn(Optional.of(command)).when(service).findById(anyString()); - var result = service.getNotFailedCommand(CommandTestFactory.ID); + var result = service.getPendingCommand(CommandTestFactory.ID); assertThat(result).isSameAs(command); } @@ -807,4 +1113,5 @@ class CommandServiceTest { assertThat(result).containsAllEntriesOf(BODY_OBJ); } } + } \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java index 3343f674fda73345c563128d368532ee235f60d1..7df40567d8e858171053fe352454a7e67decde01 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceITCase.java @@ -21,7 +21,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.mongodb.core.MongoOperations; -import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.annotation.DirtiesContext; import de.ozgcloud.command.Command; @@ -29,19 +28,20 @@ import de.ozgcloud.command.CommandStatus; import de.ozgcloud.common.test.DataITCase; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory; +import de.ozgcloud.vorgang.callcontext.WithMockCustomUser; import de.ozgcloud.vorgang.clientattribute.ClientAttributeReadPermitted; import de.ozgcloud.vorgang.common.grpc.GrpcObjectMapper; import de.ozgcloud.vorgang.grpc.command.CommandServiceGrpc.CommandServiceBlockingStub; import de.ozgcloud.vorgang.grpc.command.GrpcAddSubCommandsRequest; import de.ozgcloud.vorgang.grpc.command.GrpcCommand; -import de.ozgcloud.vorgang.grpc.command.GrpcCommandsResponse; import de.ozgcloud.vorgang.grpc.command.GrpcCreateCommand; import de.ozgcloud.vorgang.grpc.command.GrpcCreateCommandRequest; import de.ozgcloud.vorgang.grpc.command.GrpcFindCommandsRequest; -import de.ozgcloud.vorgang.grpc.command.GrpcGetCommandRequest; +import de.ozgcloud.vorgang.grpc.command.GrpcRevokeCommandRequest; import de.ozgcloud.vorgang.vorgang.Vorgang; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; import io.grpc.StatusRuntimeException; +import lombok.extern.log4j.Log4j2; import net.devh.boot.grpc.client.inject.GrpcClient; @SpringBootTest(properties = { @@ -50,7 +50,8 @@ import net.devh.boot.grpc.client.inject.GrpcClient; }) @DirtiesContext @DataITCase -@WithMockUser +@WithMockCustomUser +@Log4j2 class GrpcCommandServiceITCase { @GrpcClient("inProcess") @@ -111,6 +112,29 @@ class GrpcCommandServiceITCase { } } + @Nested + class TestRevokeCommand { + + @Test + void shouldHaveSetStatus() { + var vorgang = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()); + var command = mongoOperations.save(CommandTestFactory.createBuilder().id(null).status(CommandStatus.FINISHED).vorgangId(vorgang.getId()) + .previousState(Map.of(Vorgang.MONGODB_FIELDNAME_STATUS, "NEW")).build()); + + var result = serviceBlockingStub.revokeCommand(GrpcRevokeCommandRequest.newBuilder().setId(command.getId()).build()); + + assertThat(result.getCommand().getStatus()).isEqualTo(CommandStatus.REVOKE_PENDING.name()); + } + + @Test + void shouldThrowExceptionIfCommandNotFound() { + var revokeRequest = GrpcRevokeCommandRequest.newBuilder().setId("not-existing-id").build(); + + assertThrows(StatusRuntimeException.class, () -> serviceBlockingStub.revokeCommand(revokeRequest)); + } + + } + @Nested class TestLoadOutdatedOrders { @@ -131,7 +155,6 @@ class GrpcCommandServiceITCase { } @Nested - // TODO Tests überarbeiten: asserts in Test-Methoden verschieben class TestAddSubCommands { private static final String PARENT_TEST_ORDER = "PARENT_TEST_ORDER"; @@ -157,51 +180,39 @@ class GrpcCommandServiceITCase { var grpcCommandsResponse = serviceBlockingStub.addSubCommands(request); - await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> assertCommandIsFinished(parentCommand)); - grpcCommandsResponse.getCommandList().forEach(this::assertCommandIsFinished); + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + var command = loadCommand(parentCommand); + assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED); + }); + grpcCommandsResponse.getCommandList().stream().map(this::loadCommand).forEach(command -> + assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED) + ); } @Test - void shouldSetErrorWhenOneOfParallelsFails() { + void shouldSetErrorStatusForParentCommand() { var request = buildAddSubCommandsRequest(parentCommand.getId(), buildCreateCommand(), buildFailedCreateCommand(), buildCreateCommand()); - var grpcCommandsResponse = serviceBlockingStub.addSubCommands(request); + serviceBlockingStub.addSubCommands(request); - await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> assertCommandIsFailed(parentCommand)); - grpcCommandsResponse.getCommandList().forEach(this::assertCommandIsFailed); - } - - @Test - void shouldSetErrorWhenOneSequentialFails() { - var executedCommands = addSuccessfullySubCommands(); - - var failedCommand = addFailedSubCommands(); - - failedCommand.getCommandList().forEach(this::assertCommandIsFailed); - executedCommands.getCommandList().forEach(this::assertCommandIsFailed); + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + var command = loadCommand(parentCommand); + assertThat(command.getStatus()).isEqualTo(CommandStatus.ERROR); + assertThat(command.getErrorMessage()).isNotBlank(); + }); } @Test - void shouldThrowErrorWhenParentFailedAlready() { - addFailedSubCommands(); - - assertThrows(StatusRuntimeException.class, this::addSuccessfullySubCommands); - } - - private GrpcCommandsResponse addSuccessfullySubCommands() { - var successfullyRequest = buildAddSubCommandsRequest(parentCommand.getId(), buildCreateCommand()); - var executedCommands = serviceBlockingStub.addSubCommands(successfullyRequest); - await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> assertCommandIsFinished(parentCommand)); - return executedCommands; - } - - private GrpcCommandsResponse addFailedSubCommands() { - var failedRequest = buildAddSubCommandsRequest(parentCommand.getId(), buildFailedCreateCommand()); + void shouldRevokeSubCommands() { + var request = buildAddSubCommandsRequest(parentCommand.getId(), buildCreateCommand(), buildFailedCreateCommand(), buildCreateCommand()); - var executedCommands = serviceBlockingStub.addSubCommands(failedRequest); + var grpcCommandsResponse = serviceBlockingStub.addSubCommands(request); - await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> assertCommandIsFailed(parentCommand)); - return executedCommands; + await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { + var revokedCommand = grpcCommandsResponse.getCommandList().stream().map(this::loadCommand) + .filter(command -> command.getStatus() != CommandStatus.ERROR).toList(); + assertThat(revokedCommand).hasSize(2); + }); } private GrpcCommand createParentCommand() { @@ -244,15 +255,13 @@ class GrpcCommandServiceITCase { .build(); } - private void assertCommandIsFinished(GrpcCommand command) { - var commandResponse = serviceBlockingStub.getCommand(GrpcGetCommandRequest.newBuilder().setId(command.getId()).build()); - assertThat(commandResponse.getStatus()).isEqualTo(CommandStatus.FINISHED.name()); + private Command loadCommand(GrpcCommand grpcCommand) { + return mongoOperations.findById(grpcCommand.getId(), Command.class); } - private void assertCommandIsFailed(GrpcCommand command) { - var commandResponse = serviceBlockingStub.getCommand(GrpcGetCommandRequest.newBuilder().setId(command.getId()).build()); - assertThat(commandResponse.getStatus()).isEqualTo(CommandStatus.ERROR.name()); + private void assertCommandIsFinished(GrpcCommand grpcCommand) { + var command = mongoOperations.findById(grpcCommand.getId(), Command.class); + assertThat(command.getStatus()).isEqualTo(CommandStatus.FINISHED); } - } } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java index 3924b31aeb817ab048897bf4de28ddec744b416e..6807b332b138383be4bff92001a809aba224d3ac 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/GrpcCommandServiceTest.java @@ -36,9 +36,6 @@ 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.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -66,7 +63,6 @@ import de.ozgcloud.vorgang.grpc.command.GrpcRevokeCommandRequest; import de.ozgcloud.vorgang.grpc.command.GrpcSetCommandExecutedRequest; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; import io.grpc.stub.StreamObserver; -import lombok.SneakyThrows; class GrpcCommandServiceTest { @@ -106,7 +102,7 @@ class GrpcCommandServiceTest { void mockMapper() { when(createCommandRequestMapper.fromGrpc(any(GrpcCreateCommandRequest.class))).thenReturn(createComandRequest); when(commandService.createCommand(any(CreateCommandRequest.class))).thenReturn(persistedCommand); - when(commandService.findCommand(anyString())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(anyString())).thenReturn(Optional.of(CommandTestFactory.create())); when(commandResponseMapper.toGrpc(any(CommandResponse.class))).thenReturn(response); } @@ -147,29 +143,6 @@ class GrpcCommandServiceTest { } } - @Nested - class TestIsStatusChangeOrder { - - @ParameterizedTest - @EnumSource(mode = Mode.INCLUDE, names = { "VORGANG_ANNEHMEN", "VORGANG_VERWERFEN", "VORGANG_ZURUECKHOLEN", "VORGANG_BEARBEITEN", - "VORGANG_BESCHEIDEN", "VORGANG_ZURUECKSTELLEN", "VORGANG_ABSCHLIESSEN", "VORGANG_WIEDEREROEFFNEN" }) - void shouldReturnTrue(Order order) { - var result = service.isStatusChangeOrder(order.name()); - - assertThat(result).isTrue(); - } - - @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", - "VORGANG_LOESCHEN" }) - void shouldReturnFalse(Order order) { - var result = service.isStatusChangeOrder(order.name()); - - assertThat(result).isFalse(); - } - } - @DisplayName("Get command") @Nested class TestGetCommand { @@ -182,7 +155,7 @@ class GrpcCommandServiceTest { @BeforeEach void init() { - when(commandService.findCommand(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); when(commandMapper.toGrpc(any())).thenReturn(GrpcCommand.newBuilder().setId(CommandTestFactory.ID).build()); } @@ -197,7 +170,7 @@ class GrpcCommandServiceTest { void shouldCallVorgangCommandService() throws Exception { callGetVorgangCommand(); - verify(commandService).findCommand(CommandTestFactory.ID); + verify(commandService).findById(CommandTestFactory.ID); } @Test @@ -226,59 +199,67 @@ class GrpcCommandServiceTest { @Nested class TestRevokeCommand { + private static final GrpcRevokeCommandRequest REVOKE_COMMAND_REQUEST = GrpcRevokeCommandRequest.newBuilder().setId(CommandTestFactory.ID) + .build(); + @Mock private StreamObserver<GrpcCommandResponse> responseObserver; - private final GrpcRevokeCommandRequest request = GrpcRevokeCommandRequest.newBuilder().setId(CommandTestFactory.ID).build(); - @BeforeEach - void mockCommandService() { - Command vorgangCommand = CommandTestFactory.createBuilder().id(CommandTestFactory.ID).build(); + @Mock + private GrpcCommandResponse commandsResponse; - when(commandService.findCommand(any())).thenReturn(Optional.of(vorgangCommand)); + @Captor + private ArgumentCaptor<CommandResponse> responseCaptor; + + private Command updatedCommand; + + private void callRevokeCommand() { + service.revokeCommand(REVOKE_COMMAND_REQUEST, responseObserver); + } + + @BeforeEach + void init() { + updatedCommand = CommandTestFactory.createBuilder().id(CommandTestFactory.ID).build(); } @Test - void shouldCallPolicyService() throws Exception { + void shouldCallPolicyService() { callRevokeCommand(); - verify(policyService).checkPermissionByCommand(anyString()); + verify(policyService).checkPermissionByCommand(CommandTestFactory.ID); } @Test - void shouldCallServiceRevokeCommand() throws Exception { + void shouldCallServiceRevokeCommand() { callRevokeCommand(); - verify(commandService).setCommandRevokePending(request.getId()); + verify(commandService).revokeCommand(CommandTestFactory.ID); } @Test - void shouldCallMapper() throws Exception { + void shouldCallMapper() { + when(commandService.revokeCommand(any())).thenReturn(updatedCommand); + callRevokeCommand(); - verify(commandResponseMapper).toGrpc(any()); + verify(commandResponseMapper).toGrpc(responseCaptor.capture()); + assertThat(responseCaptor.getValue()).extracting(CommandResponse::getCommand).isSameAs(updatedCommand); } - @Nested - class TestProceedRevokeCommand { - - private final PersistedCommand command = CommandTestFactory.create(); - - @BeforeEach - void init() { - when(commandService.findCommand(anyString())).thenReturn(Optional.of(command)); - } + @Test + void shouldCallOnNext() { + when(commandResponseMapper.toGrpc(any())).thenReturn(commandsResponse); - @Test - @SneakyThrows - void shouldLoadCommand() { - callRevokeCommand(); + callRevokeCommand(); - verify(service).getCommand(CommandTestFactory.ID); - } + verify(responseObserver).onNext(commandsResponse); } - private void callRevokeCommand() throws Exception { - service.revokeCommand(request, responseObserver); + @Test + void shouldCloseStream() { + callRevokeCommand(); + + verify(responseObserver).onCompleted(); } } @@ -411,14 +392,14 @@ class GrpcCommandServiceTest { @BeforeEach void init() { - when(commandService.findCommand(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); } @Test void shouldLoadCommand() { service.setCommandExecuted(buildRequest(), responseObserver); - verify(commandService).findCommand(CommandTestFactory.ID); + verify(commandService).findById(CommandTestFactory.ID); } @Test @@ -430,7 +411,7 @@ class GrpcCommandServiceTest { @Test void shouldHandleMissingCommand() { - when(commandService.findCommand(any())).thenReturn(Optional.empty()); + when(commandService.findById(any())).thenReturn(Optional.empty()); service.setCommandExecuted(buildRequest(), responseObserver); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachMailByCommandServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachMailByCommandServiceITCase.java index 6f386a9017ddec6bc2e7dd1c452a997caf4e6379..ca948a0825554fb26818b1bd978bfbf0183c878e 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachMailByCommandServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachMailByCommandServiceITCase.java @@ -26,7 +26,6 @@ package de.ozgcloud.vorgang.command; import static org.assertj.core.api.Assertions.*; import java.util.Map; -import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -36,12 +35,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.security.test.context.support.WithMockUser; -import de.ozgcloud.command.Command; import de.ozgcloud.common.test.DataITCase; -import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItem; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory; -import de.ozgcloud.vorgang.callcontext.UserTestFactory; import de.ozgcloud.vorgang.vorgang.Vorgang; import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; @@ -63,25 +59,6 @@ class PersistPostfachMailByCommandServiceITCase { persistedVorgang = mongoOperations.save(VorgangTestFactory.createWithOrganisationEinheitId("73").toBuilder().id(null).version(0).build()); } - @DisplayName("persist mail") - @Nested - class TestPersistMail { - - @BeforeEach - void initDatabase() { - mongoOperations.dropCollection(Command.COLLECTION_NAME); - } - - @Test - void shouldCreateCommand() { - service.persistNachricht(Optional.of(UserTestFactory.ID), - PostfachNachrichtTestFactory.createBuilder().vorgangId(persistedVorgang.getId()).build()); - - var commands = mongoOperations.findAll(Command.class); - assertThat(commands).hasSize(1); - } - } - @DisplayName("with existing postfach nachricht") @Nested class TestWithExistingPostfachNachricht { diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandServiceTest.java index 3384f06ff55b5146b18eed76d584e7dc8ae078d2..7075a16dbee0496c3b2663dc8111fe3ef1300098 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/PersistPostfachNachrichtByCommandServiceTest.java @@ -24,7 +24,6 @@ package de.ozgcloud.vorgang.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.*; @@ -49,10 +48,8 @@ import de.ozgcloud.nachrichten.postfach.PostfachAddressTestFactory; import de.ozgcloud.nachrichten.postfach.PostfachNachrichtTestFactory; import de.ozgcloud.nachrichten.postfach.osi.MessageAttachmentTestFactory; import de.ozgcloud.nachrichten.postfach.osi.MessageTestFactory; -import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemMapper; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemService; import de.ozgcloud.vorgang.attached_item.VorgangAttachedItemTestFactory; -import de.ozgcloud.vorgang.callcontext.UserTestFactory; import de.ozgcloud.vorgang.files.FileId; import de.ozgcloud.vorgang.files.FileService; import de.ozgcloud.vorgang.files.OzgFile; @@ -64,119 +61,21 @@ class PersistPostfachNachrichtByCommandServiceTest { @InjectMocks private PersistPostfachNachrichtByCommandService service; @Mock - private CommandService commandService; - @Mock private VorgangAttachedItemService attachedItemService; @Mock private FileService fileService; - @DisplayName("Persist mail") + @DisplayName("Persist nachricht") @Nested - class TestPersistMail { - - @Captor - private ArgumentCaptor<CreateCommandRequest> requestCaptor; - - @Test - void shouldCallCommandService() { - callService(); - - verify(commandService).createCommand(notNull()); - } - - @Test - void shouldContainVorgangId() { - var request = callService(); - - assertThat(request.getVorgangId()).isEqualTo(MessageTestFactory.VORGANG_ID); - } - - @Test - void shouldContainRelationInformations() { - var request = callService(); - - assertThat(request.getRelationId()).isEqualTo(MessageTestFactory.VORGANG_ID); - assertThat(request.getRelationVersion()).isNull(); - } + class TestPersistNachricht { @Test - void shouldHaveOrder() { - var request = callService(); - - assertThat(request.getOrder()).isEqualTo(Order.CREATE_ATTACHED_ITEM.name()); - } - - @Test - void shouldHaveCallContext() { - var request = callService(); - - assertThat(request.getCallContext()).isNotNull(); - assertThat(request.getCallContext().getUser().getId()).isEqualTo(UserTestFactory.ID); - } - - @Test - void shouldHaveVorgangAttachedItem() { - var request = callService(); - - assertThat(request.getBodyObject()) - .containsEntry(VorgangAttachedItemMapper.PROPERTY_VORGANG_ID, MessageTestFactory.VORGANG_ID) - .containsEntry(VorgangAttachedItemMapper.PROPERTY_CLIENT, "OzgCloud_NachrichtenManager") - .containsEntry(VorgangAttachedItemMapper.PROPERTY_ITEM_NAME, "PostfachMail") - .containsKey(VorgangAttachedItemMapper.PROPERTY_ITEM); - } - - @Test - void shouldNotAddUser() { - service.persistNachricht(Optional.empty(), PostfachNachrichtTestFactory.create()); - verify(commandService).createCommand(requestCaptor.capture()); - - assertThat(requestCaptor.getValue().getCallContext().getUser()).isNull(); - } - - private CreateCommandRequest callService() { - service.persistNachricht(Optional.of(UserTestFactory.ID), PostfachNachrichtTestFactory.createBuilder().id(null).build()); - verify(commandService).createCommand(requestCaptor.capture()); - - return requestCaptor.getValue(); - } - - @DisplayName("build mail map") - @Nested - class TestBuildMailMap { - - @Test - void shouldContainAllFieldsIgnoringId() { - var map = service.buildNachrichtMap(PostfachNachrichtTestFactory.createBuilder().id(null).build()); - - var mapWithoutId = PostfachNachrichtTestFactory.asMap(); - mapWithoutId.remove("id"); - - assertThat(map).containsAllEntriesOf(mapWithoutId); - } - - @Test - void shouldIgnoreEmptyMessageId() { - assertDoesNotThrow(() -> service.buildNachrichtMap(PostfachNachrichtTestFactory.createBuilder().messageId(null).build())); - } - - @Test - void shouldIgnoreEmptyCreatedBy() { - assertDoesNotThrow(() -> service.buildNachrichtMap(PostfachNachrichtTestFactory.createBuilder().createdBy(null).build())); - } - - @Test - void shouldHandleNullAsPostfachId() { - var postfachMail = PostfachNachrichtTestFactory.createBuilder().postfachId(null).build(); - - assertDoesNotThrow(() -> service.buildNachrichtMap(postfachMail)); - } - - @Test - void shouldProceedWithEmptyPostfachAddress() { - var postfachMail = PostfachNachrichtTestFactory.createBuilder().postfachAddress(null).build(); + void shouldThrowException() { + var postfachNachricht = PostfachNachrichtTestFactory.create(); + var userId = Optional.<String>empty(); - assertDoesNotThrow(() -> service.buildNachrichtMap(postfachMail)); - } + assertThatThrownBy(() -> service.persistNachricht(userId, postfachNachricht)) + .isInstanceOf(UnsupportedOperationException.class); } } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/RevokeCommandEventTestFactory.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/RevokeCommandEventTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..61b93ce3d08e3afe118e21fe0d2017310f2c9714 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/command/RevokeCommandEventTestFactory.java @@ -0,0 +1,11 @@ +package de.ozgcloud.vorgang.command; + +import de.ozgcloud.command.Command; +import de.ozgcloud.command.RevokeCommandEvent; + +public class RevokeCommandEventTestFactory { + + public static RevokeCommandEvent create(Command command) { + return new RevokeCommandEvent(command); + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d2751ccceaf4480dec515641851e051de8eba4f1 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M010_CreateIndexesInVorgangTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2024 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.vorgang.common.migration; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.index.IndexDefinition; +import org.springframework.data.mongodb.core.index.IndexOperations; + +class M010_CreateIndexesInVorgangTest { + + @Spy + @InjectMocks + private M010_CreateIndexesInVorgang migration; + + @Mock + private MongoTemplate template; + @Mock + private IndexOperations indexOperations; + + @Nested + class TestDoMigration { + + @Mock + private IndexDefinition indexDefinition; + + @BeforeEach + void init() { + when(template.indexOps(anyString())).thenReturn(indexOperations); + } + + @Test + void shouldGetIndexOperations() { + migration.doMigration(template); + + verify(template).indexOps(M010_CreateIndexesInVorgang.COLLECTION_NAME); + } + + @Test + void shouldCallBuildCompoundIndexDefinition() { + migration.doMigration(template); + + verify(migration).buildCompoundIndexDefinition(); + } + + @Test + void shouldEnsureComponentIndex() { + doReturn(indexDefinition).when(migration).buildCompoundIndexDefinition(); + + migration.doMigration(template); + + verify(indexOperations).ensureIndex(indexDefinition); + } + + } + + @Nested + class TestBuildCompoundIndex { + + @Test + void shouldSetInCreation() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_IN_CREATION, 1); + } + + @Test + void shouldSetOrganisationseinheitenId() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_ORGANISATIONSEINHEITEN_ID, 1); + } + + @Test + void shouldSetAssignedTo() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_ASSIGNED_TO, 1); + } + + @Test + void shouldSetStatus() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_STATUS, 1); + } + + @Test + void shouldSetCreatedAt() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexKeys()).containsEntry(M010_CreateIndexesInVorgang.FIELD_CREATED_AT, 1); + } + + @Test + void shouldSetName() { + var indexDefinition = migration.buildCompoundIndexDefinition(); + + assertThat(indexDefinition.getIndexOptions()).containsEntry("name", M010_CreateIndexesInVorgang.COMPOUND_INDEX_NAME); + } + } + +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevelITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevelITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..9b5b8b9a499aaf17baf73c073dbf0f75ef7cc694 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/migration/M011_SetDefaultCollaborationLevelITCase.java @@ -0,0 +1,142 @@ +package de.ozgcloud.vorgang.common.migration; + +import static org.assertj.core.api.Assertions.*; + +import org.bson.Document; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Query; + +import com.thedeanda.lorem.LoremIpsum; + +import de.ozgcloud.common.test.DataITCase; + +@DataITCase +class M011_SetDefaultCollaborationLevelITCase { + + private final M011_SetDefaultCollaborationLevel migration = new M011_SetDefaultCollaborationLevel(); + + private final String nameField = "name"; + private final String nameFieldValue = LoremIpsum.getInstance().getWords(1); + + private final String organisationsEinheitField = "organisationsEinheitId"; + private final String organisationsEinheitFieldValue = LoremIpsum.getInstance().getWords(1); + + @Autowired + private MongoTemplate template; + @Autowired + private MigrationDbTestUtils dbTestUtils; + + @DisplayName("Do migration") + @Nested + class TestDoMigration { + + @BeforeEach + void mock() { + dbTestUtils.dropVorgangCollection(); + } + + @DisplayName("on missing collaboration level") + @Nested + class TestOnMissingCollaborationLevel { + + @BeforeEach + void initVorgang() { + dbTestUtils.saveVorgang(createVorgangWithName()); + } + + @Test + void shouldSetDefaultCollaborationLevel() { + migration.doMigration(template); + + assertThat(getVorgangHeader()).containsEntry(M011_SetDefaultCollaborationLevel.COLLABORATION_LEVEL_FIELD, 0); + } + + @Test + void shouldKeepVorgangData() { + migration.doMigration(template); + + assertThat(getVorgang()).containsEntry(nameField, nameFieldValue); + } + + @Test + void shouldKeepHeaderData() { + migration.doMigration(template); + + assertThat(getVorgangHeader()).containsEntry(organisationsEinheitField, + organisationsEinheitFieldValue); + } + } + + @DisplayName("on existing collaboration level") + @Nested + class TestOnExistingCollaborationLevel { + + @BeforeEach + void initVorgang() { + dbTestUtils.saveVorgang(createVorgangWithCollaborationLevel()); + } + + @Test + void shouldKeepCollaborationLevel() { + migration.doMigration(template); + + assertThat(getVorgangHeader()).containsEntry(M011_SetDefaultCollaborationLevel.COLLABORATION_LEVEL_FIELD, 1); + } + + @Test + void shouldKeepVorgangData() { + migration.doMigration(template); + + assertThat(getVorgang()).containsEntry(nameField, nameFieldValue); + } + + @Test + void shouldKeepHeaderData() { + migration.doMigration(template); + + assertThat(getVorgangHeader()).containsEntry(organisationsEinheitField, + organisationsEinheitFieldValue); + } + } + + private Document createVorgangWithCollaborationLevel() { + var vorgang = createVorgangWithName(); + vorgang.append(M011_SetDefaultCollaborationLevel.HEADER_FIELD, createHeaderWithCollaborationLevel()); + return vorgang; + } + + private Document createHeaderWithCollaborationLevel() { + var header = createHeaderWithOrganisationsEinheitId(); + header.append(M011_SetDefaultCollaborationLevel.COLLABORATION_LEVEL_FIELD, 1); + return header; + } + + private Document createVorgangWithName() { + var vorgang = new Document(); + vorgang.append(nameField, nameFieldValue); + vorgang.append(M011_SetDefaultCollaborationLevel.HEADER_FIELD, createHeaderWithOrganisationsEinheitId()); + return vorgang; + } + + private Document createHeaderWithOrganisationsEinheitId() { + var header = new Document(); + header.append(organisationsEinheitField, organisationsEinheitFieldValue); + return header; + } + + private Document getVorgangHeader() { + return (Document) getVorgang().get(M011_SetDefaultCollaborationLevel.HEADER_FIELD); + } + + private Document getVorgang() { + var vorgaenge = dbTestUtils.findVorgang(new Query()); + assertThat(vorgaenge).hasSize(1); + return vorgaenge.get(0); + } + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..c623040a7e824441be13e2bcb82676e0f19e2ec2 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerITCase.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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.vorgang.common.search; + +import static org.assertj.core.api.Assertions.*; +import static org.awaitility.Awaitility.*; + +import java.time.Duration; +import java.util.Optional; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.index.IndexInfo; + +import de.ozgcloud.common.test.DataITCase; +import de.ozgcloud.vorgang.vorgang.Vorgang; + +@SpringBootTest(properties = { + "ozgcloud.mongodbsearch.enabled=true" +}) +@DataITCase +class MongodbFullTextSearchIndexInitializerITCase { + + @Autowired + private MongoOperations mongoOperations; + + @Test + void shouldEnsureIndex() { + await().atMost(Duration.ofSeconds(60)).untilAsserted(() -> + assertThat(mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo()).isNotEmpty()); + + assertThat(getTextIndex()).isPresent(); + } + + private Optional<IndexInfo> getTextIndex() { + return mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo().stream() + .filter(index -> MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME.equals(index.getName())).findFirst(); + } + +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6e93887973173fa3a6e986891adfe6c5fd28e9b9 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/MongodbFullTextSearchIndexInitializerTest.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2024 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.vorgang.common.search; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.index.IndexOperations; +import org.springframework.data.mongodb.core.index.TextIndexDefinition; + +import de.ozgcloud.vorgang.vorgang.Vorgang; + +class MongodbFullTextSearchIndexInitializerTest { + + @Spy + @InjectMocks + private MongodbFullTextSearchIndexInitializer indexInitializer; + + @Mock + private MongoOperations mongoOperations; + + @Nested + class TestInitWildcardTextIndex { + + @Mock + private IndexOperations indexOperations; + @Mock + private TextIndexDefinition textIndexDefinition; + + @BeforeEach + void init() { + when(mongoOperations.indexOps(anyString())).thenReturn(indexOperations); + } + + @Test + void shouldCallEnsureIndex() { + indexInitializer.initWildcardTextIndex(); + + verify(mongoOperations).indexOps(Vorgang.COLLECTION_NAME); + } + + @Test + void shouldCallBuildWildcardTextIndexDefinition() { + indexInitializer.initWildcardTextIndex(); + + verify(indexInitializer).buildWildcardTextIndexDefinition(); + } + + @Test + void shouldCallEnsureIndexWithBuildWildcardTextIndexDefinition() { + doReturn(textIndexDefinition).when(indexInitializer).buildWildcardTextIndexDefinition(); + + indexInitializer.initWildcardTextIndex(); + + verify(indexOperations).ensureIndex(textIndexDefinition); + } + } + + @Nested + class TestBuildWildcardTextIndexDefinition { + + @Test + void shouldSetIndexName() { + var result = indexInitializer.buildWildcardTextIndexDefinition(); + + assertThat(result.getIndexOptions()).containsEntry("name", MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME); + } + + @Test + void shouldSetDefaultLanguage() { + var result = indexInitializer.buildWildcardTextIndexDefinition(); + + assertThat(result.getIndexOptions()).containsEntry("default_language", MongodbFullTextSearchIndexInitializer.TEXT_INDEX_LANGUAGE); + } + + @Test + void shouldCreateWildcardTextIndex() { + var result = indexInitializer.buildWildcardTextIndexDefinition(); + + assertThat(result.getIndexKeys()).containsEntry("$**", "text"); + } + } +} \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchEventListenerTest.java index 3b2a5913798dce1392dc9c53031fa31fa763c8b4..359c21709666e3ec0f2c5c8e07d5317ba351dc79 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchEventListenerTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchEventListenerTest.java @@ -112,7 +112,7 @@ class SearchEventListenerTest { @BeforeEach void init() { - when(commandService.findCommand(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); when(vorgangService.getById(anyString())).thenReturn(vorgang); } diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceITCase.java index e54948ec4f0108debde76cf2a8e8635ebc5b1b87..867f0f4b9f6355f1d4e4b3d1498e4daeb77543eb 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceITCase.java @@ -89,7 +89,7 @@ public class SearchServiceITCase { @BeforeEach void init() { Command cmd = CommandTestFactory.createBuilder().build(); - when(commandService.findCommand(CommandTestFactory.ID)).thenReturn(Optional.of(cmd)); + when(commandService.findById(CommandTestFactory.ID)).thenReturn(Optional.of(cmd)); elasticsearchOperations.indexOps(IndexedVorgang.class).delete(); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java index 65228c43a6fffd553c3520cb7d08ab8a0de3c69a..185953de74bc5b83361db559720fb272c8712fe9 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchServiceTest.java @@ -80,18 +80,6 @@ class SearchServiceTest { } } - @DisplayName("Exists by id") - @Nested - class TestExistsById { - - @Test - void shouldCallRepositoryExistsById() { - searchService.existsById(VorgangTestFactory.ID); - - verify(repository).existsById(VorgangTestFactory.ID); - } - } - @DisplayName("Search") @Nested class TestSearch { diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java new file mode 100644 index 0000000000000000000000000000000000000000..751afdf88ad5545f59f9838787490ddfec73a703 --- /dev/null +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/common/search/SearchVorgangCustomRepositoryMongoITCase.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2024 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.vorgang.common.search; + +import static org.awaitility.Awaitility.*; + +import java.time.Duration; +import java.util.Optional; + +import org.assertj.core.api.Assertions; +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.context.SpringBootTest; +import org.springframework.data.mongodb.core.MongoOperations; +import org.springframework.data.mongodb.core.index.IndexInfo; + +import de.ozgcloud.common.test.DataITCase; +import de.ozgcloud.vorgang.vorgang.AntragstellerTestFactory; +import de.ozgcloud.vorgang.vorgang.EingangTestFactory; +import de.ozgcloud.vorgang.vorgang.FilterCriteria; +import de.ozgcloud.vorgang.vorgang.FindVorgangRequest; +import de.ozgcloud.vorgang.vorgang.Vorgang; +import de.ozgcloud.vorgang.vorgang.VorgangTestFactory; + +@SpringBootTest(properties = { "ozgcloud.mongodbsearch.enabled=true" }) +@DataITCase +public class SearchVorgangCustomRepositoryMongoITCase { + + @Autowired + private SearchVorgangCustomRepositoryMongo repostitory; + + @Autowired + private MongoOperations mongoOperations; + + @Nested + class TestSpecialCharacters { + + @BeforeEach + void init() { + await().atMost(Duration.ofSeconds(10)).until(() -> getTextIndex().isPresent()); + mongoOperations.remove(Vorgang.class).all(); + } + + @DisplayName("should find vorgang when vorgangName") + @ParameterizedTest(name = "contains {0}") + @ValueSource(strings = { "_", "+", "-", "=", "&&", "||", "!", "(", ")", "{", "}", "[", "]", "^", "\"", "~", "*", "?", ":", "\\", "/", "<", + ">", "$", "%", "&", "#", "@" }) + void shouldFindInVorgangName(String character) { + var vorgangName = "AbC%sTest".formatted(character); + mongoOperations.save(VorgangTestFactory.createBuilder().name(vorgangName).build()); + + var vorgangHeaders = repostitory.searchBy(searchRequest(character)); + + Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first() + .extracting("name").isEqualTo(vorgangName); + } + + @Test + void shouldFindInVorgangNummer() { + mongoOperations.save(VorgangTestFactory.create()); + + var vorgangHeaders = repostitory.searchBy(buildSearchRequest("VOR-GANG")); + + Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first() + .extracting("nummer").isEqualTo(VorgangTestFactory.VORGANG_NUMMER); + } + + @Test + void shouldFindInAntragstellerName() { + var antragsteller = AntragstellerTestFactory.createBuilder().nachname("MusterMann").build(); + var eingang = EingangTestFactory.createBuilder().antragsteller(antragsteller).build(); + mongoOperations.save(VorgangTestFactory.createBuilder().clearEingangs().eingang(eingang).build()); + + var vorgangHeaders = repostitory.searchBy(buildSearchRequest("mustermann")); + + Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1); + } + + @Test + void shouldFindInAntragstellerVorname() { + var antragsteller = AntragstellerTestFactory.createBuilder().vorname("Alexander").build(); + var eingang = EingangTestFactory.createBuilder().antragsteller(antragsteller).build(); + mongoOperations.save(VorgangTestFactory.createBuilder().clearEingangs().eingang(eingang).build()); + + var vorgangHeaders = repostitory.searchBy(buildSearchRequest("alexand")); + + Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1); + } + + @Test + void shouldFindInAktenzeichen() { + var foundVorgang = mongoOperations.save(VorgangTestFactory.createBuilder().id(null).aktenzeichen("123-456").build()); + mongoOperations.save(VorgangTestFactory.createBuilder().id(null).build()); + + var vorgangHeaders = repostitory.searchBy(buildSearchRequest("123-")); + + Assertions.assertThat(vorgangHeaders.getContent()).hasSize(1).first().extracting("id").isEqualTo(foundVorgang.getId()); + } + + @Test + void shouldFindInMultipleFields() { + var antragsteller = AntragstellerTestFactory.createBuilder().nachname("Müller").build(); + mongoOperations.save(VorgangTestFactory.createBuilder().id(null).name("Mäuse") + .eingang(EingangTestFactory.createBuilder().antragsteller(antragsteller).build()) + .build(), Vorgang.COLLECTION_NAME); + + var result = repostitory.searchBy(buildSearchRequest("mull mau")); + + Assertions.assertThat(result.getContent()).hasSize(1); + } + + private Optional<IndexInfo> getTextIndex() { + return mongoOperations.indexOps(Vorgang.COLLECTION_NAME).getIndexInfo().stream() + .filter(index -> MongodbFullTextSearchIndexInitializer.TEXT_INDEX_NAME.equals(index.getName())).findFirst(); + } + + private FindVorgangRequest searchRequest(String character) { + return FindVorgangRequest.builder().filterBy(FilterCriteria.builder().build()).searchBy("abc%st".formatted(character)).build(); + } + + private FindVorgangRequest buildSearchRequest(String searchBy) { + return FindVorgangRequest.builder().filterBy(FilterCriteria.builder().build()).searchBy(searchBy).build(); + } + } +} diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerITCase.java index bb751bec059f5f0bb76e50af39caa5d755a928fe..2b3c260fd811ad45c157eb172fdcd4a20ffae1b2 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerITCase.java @@ -45,7 +45,7 @@ class StatusEventListenerITCase { void init() { when(vorgangService.getById(anyString())).thenReturn(VorgangTestFactory.create()); - when(commandService.findCommand(anyString())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(anyString())).thenReturn(Optional.of(CommandTestFactory.create())); } @DisplayName("Test creating status events") diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerTest.java index dca80473467488a16fe29282bb80103c4002acfa..91866958ed434e56b7f297e4a8825085802fbb2b 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/status/StatusEventListenerTest.java @@ -68,7 +68,7 @@ class StatusEventListenerTest { void init() { when(event.getSource()).thenReturn(command); when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); - when(commandService.findCommand(any())).thenReturn(Optional.of(CommandTestFactory.create())); + when(commandService.findById(any())).thenReturn(Optional.of(CommandTestFactory.create())); } @Test @@ -82,7 +82,7 @@ class StatusEventListenerTest { void shouldCallCommandService() { eventListener.abschiessen(event); - verify(commandService).findCommand(any()); + verify(commandService).findById(any()); } @Test @@ -195,7 +195,7 @@ class StatusEventListenerTest { @Test void shouldHandleNoSuchElementException() { - when(commandService.findCommand(any())).thenReturn(Optional.empty()); + when(commandService.findById(any())).thenReturn(Optional.empty()); when(vorgangService.getById(any())).thenReturn(VorgangTestFactory.create()); eventListener.annehmen(CommandCreatedEventTestFactory.create(CommandTestFactory.create())); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceFindQueryITCase.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceFindQueryITCase.java index e5fcd28b7fe4eff5bbd198103ab8a3a676f9faa7..62a1e5ff733572ad745d2b05b0990a36a9292e25 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceFindQueryITCase.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceFindQueryITCase.java @@ -37,8 +37,6 @@ class GrpcVorgangServiceFindQueryITCase { private static final String VORGAENGE_WITH_UNGELESENE_NACHRICHTEN_PATH = "ClientAttribute.OzgCloud_NachrichtenManager.hasNewPostfachNachricht"; - @Autowired - private VorgangHeaderRepository repository; @Autowired private MongoOperations mongoOperations; @@ -72,7 +70,8 @@ class GrpcVorgangServiceFindQueryITCase { GrpcQuery.newBuilder() .addExpressions(GrpcVorgangQueryExpression.newBuilder() .setOperator(GrpcQueryOperator.EXISTS) - .setPath("ClientAttribute.%s.nextWiedervorlageFrist".formatted(CriteriaUtil.DEFAULT_CLIENT)))).build(); + .setPath("ClientAttribute.%s.nextWiedervorlageFrist".formatted(CriteriaUtil.DEFAULT_CLIENT)))) + .build(); var result = vorgangServiceStub.findVorgang(request); @@ -108,8 +107,7 @@ class GrpcVorgangServiceFindQueryITCase { .addExpressions(GrpcVorgangQueryExpression.newBuilder() .setOperator(GrpcQueryOperator.LESS_THEN_OR_EQUAL_TO) .setPath("Vorgang." + Vorgang.MONGODB_FIELDNAME_CREATED_AT) - .setOperandStringValue(VorgangTestFactory.CREATED_AT.toString())) - ) + .setOperandStringValue(VorgangTestFactory.CREATED_AT.toString()))) .build(); var result = vorgangServiceStub.findVorgang(request); diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceTest.java index c47eaab97f6000c602e3d3ccd7aab63fb969392a..3b78df2424dfa62ae1048907e663065de5110e27 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/GrpcVorgangServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.Mockito.*; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -41,6 +42,13 @@ import org.mockito.Mock; import org.mockito.Spy; import org.springframework.data.domain.Page; +import de.ozgcloud.vorgang.collaboration.CollaborationService; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangBadRequestException; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequest; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequestMapper; +import de.ozgcloud.vorgang.collaboration.CreateCollaborationVorgangRequestTestFactory; +import de.ozgcloud.vorgang.collaboration.GrpcCollaborationRequestTestFactory; +import de.ozgcloud.vorgang.collaboration.GrpcCreateCollaborationVorgangResponseTestFactory; import io.grpc.stub.StreamObserver; class GrpcVorgangServiceTest { @@ -73,6 +81,10 @@ class GrpcVorgangServiceTest { private IncomingFileMapper incomingFileMapper; @Mock private IncomingFileGroupMapper incomingFileGroupMapper; + @Mock + private CollaborationService collaborationService; + @Mock + private CreateCollaborationVorgangRequestMapper createCollaborationVorgangRequestMapper; @Nested class TestCreate { @@ -330,4 +342,170 @@ class GrpcVorgangServiceTest { verify(responseObserver).onNext(responseCaptor.capture()); } } + + @Nested + class TestCreateCollaborationVorgangEndpoint { + + @Nested + class TestCollaborationRequest { + + private static final GrpcCollaborationRequest GRPC_COLLABORATION_REQUEST = GrpcCollaborationRequestTestFactory.create(); + private static final GrpcCreateCollaborationVorgangRequest REQUEST = GrpcCreateCollaborationVorgangRequest.newBuilder() + .setCollaborationRequest(GRPC_COLLABORATION_REQUEST).build(); + private static final GrpcCreateCollaborationVorgangResponse GRPC_COLLABORATION_RESPONSE = + GrpcCreateCollaborationVorgangResponseTestFactory.create(); + + @Mock + private StreamObserver<GrpcCreateCollaborationVorgangResponse> responseObserver; + + @Captor + private ArgumentCaptor<GrpcCreateCollaborationVorgangResponse> responseCaptor; + + @BeforeEach + void init() { + doReturn(GRPC_COLLABORATION_RESPONSE).when(service).createCollaborationVorgang(any()); + } + + @Test + void shouldCallCreateCollaborationVorgang() { + createCollaborationVorgang(); + + verify(service).createCollaborationVorgang(GRPC_COLLABORATION_REQUEST); + } + + @Test + void shouldCallOnNext() { + createCollaborationVorgang(); + + verify(responseObserver).onNext(responseCaptor.capture()); + assertThat(responseCaptor.getValue().getVorgangId()).isEqualTo(GrpcCreateCollaborationVorgangResponseTestFactory.VORGANG_ID); + } + + @Test + void shouldCallOnCompleted() { + createCollaborationVorgang(); + + verify(responseObserver).onCompleted(); + } + + private void createCollaborationVorgang() { + service.createCollaborationVorgang(REQUEST, responseObserver); + } + } + + @Nested + class TestEmptyCollaborationRequest { + + @Mock + private StreamObserver<GrpcCreateCollaborationVorgangResponse> responseObserver; + + @Test + void shouldThrowException() { + Assertions.assertThrows(CreateCollaborationVorgangBadRequestException.class, this::createCollaborationVorgang); + } + + private void createCollaborationVorgang() { + service.createCollaborationVorgang(GrpcCreateCollaborationVorgangRequest.newBuilder().build(), responseObserver); + } + } + } + + @Nested + class TestCreateCollaborationVorgang { + + private static final GrpcCollaborationRequest GRPC_COLLABORATION_REQUEST = GrpcCollaborationRequestTestFactory.create(); + private static final CreateCollaborationVorgangRequest CREATE_COLLABORATION_VORGANG_REQUEST = CreateCollaborationVorgangRequestTestFactory.create(); + private static final Vorgang COLLABORATION_VORGANG = VorgangTestFactory.create(); + + @BeforeEach + void init() { + doReturn(CREATE_COLLABORATION_VORGANG_REQUEST).when(service).buildCreateCollaborationVorgangRequest(any()); + when(collaborationService.createCollaborationVorgang(any())).thenReturn(COLLABORATION_VORGANG); + } + + @Test + void shouldCallBuildCreateCollaborationVorgangRequest() { + createCollaborationVorgang(); + + verify(service).buildCreateCollaborationVorgangRequest(GRPC_COLLABORATION_REQUEST); + } + + @Test + void shouldCallCreateCollaborationVorgang() { + createCollaborationVorgang(); + + verify(collaborationService).createCollaborationVorgang(CREATE_COLLABORATION_VORGANG_REQUEST); + } + + @Test + void shouldCallBuildResponse() { + createCollaborationVorgang(); + + verify(service).buildCreateCollaborationVorgangResponse(COLLABORATION_VORGANG); + } + + @Test + void shouldReturnResponse() { + var response = GrpcCreateCollaborationVorgangResponseTestFactory.create(); + doReturn(response).when(service).buildCreateCollaborationVorgangResponse(any()); + + var result = createCollaborationVorgang(); + + assertThat(result).isSameAs(response); + } + + private GrpcCreateCollaborationVorgangResponse createCollaborationVorgang() { + return service.createCollaborationVorgang(GRPC_COLLABORATION_REQUEST); + } + } + + @Nested + class TestBuildCreateCollaborationVorgangRequest { + + private static final GrpcCollaborationRequest GRPC_COLLABORATION_REQUEST = GrpcCollaborationRequestTestFactory.create(); + private static final CreateCollaborationVorgangRequest CREATE_COLLABORATION_VORGANG_REQUEST = CreateCollaborationVorgangRequestTestFactory.create(); + private static final Vorgang VORGANG = VorgangTestFactory.create(); + + @BeforeEach + void init() { + when(createCollaborationVorgangRequestMapper.mapFrom(any())).thenReturn(CREATE_COLLABORATION_VORGANG_REQUEST); + when(vorgangService.getById(anyString())).thenReturn(VORGANG); + } + + @Test + void shouldCallGetVorgang() { + buildCreateCollaborationVorgangRequest(); + + verify(vorgangService).getById(VorgangTestFactory.ID); + } + + @Test + void shouldCallRequestMapper() { + buildCreateCollaborationVorgangRequest(); + + verify(createCollaborationVorgangRequestMapper).mapFrom(GRPC_COLLABORATION_REQUEST); + } + + @Test + void shouldSetVorgang() { + var result = buildCreateCollaborationVorgangRequest(); + + assertThat(result.getVorgang()).isSameAs(VORGANG); + } + + private CreateCollaborationVorgangRequest buildCreateCollaborationVorgangRequest() { + return service.buildCreateCollaborationVorgangRequest(GRPC_COLLABORATION_REQUEST); + } + } + + @Nested + class TestBuildCreateCollaborationVorgangResponse { + + @Test + void shouldSetVorgangId() { + var result = service.buildCreateCollaborationVorgangResponse(VorgangTestFactory.create()); + + assertThat(result.getVorgangId()).isEqualTo(VorgangTestFactory.ID); + } + } } \ No newline at end of file diff --git a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java index 216eb98e9acdd32f73761e69c40c489ffc10a07c..fb8085fe48a868c0c40ff629bf19ae4ec4dcea32 100644 --- a/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java +++ b/vorgang-manager-server/src/test/java/de/ozgcloud/vorgang/vorgang/VorgangServiceTest.java @@ -346,7 +346,7 @@ class VorgangServiceTest { void shouldThrowAccessDeniedException() { when(vorgangAuthorizationService.authorizeByOrganisationseinheitenId(any(), any())).thenReturn(false); - assertThrows(AccessDeniedException.class, () -> service.getById(VorgangTestFactory.ID, FilterCriteriaTestFactory.create())); // NOSONAR + assertThrows(AccessDeniedException.class, () -> service.getById(VorgangTestFactory.ID, FilterCriteriaTestFactory.create())); //NOSONAR } } @@ -381,10 +381,45 @@ class VorgangServiceTest { } } + @Nested + class TestGetOriginalVorgangById { + + private final static Vorgang VORGANG = VorgangTestFactory.create(); + + @BeforeEach + void init() { + doReturn(VORGANG).when(service).loadById(anyString()); + } + + @Test + void shouldCallLoadById() { + service.getOriginalVorgangById(VorgangTestFactory.ID); + + verify(service).loadById(VorgangTestFactory.ID); + } + + @Test + void shouldCallRemoveUnpermittedClientAttributes() { + service.getOriginalVorgangById(VorgangTestFactory.ID); + + verify(service).removeUnpermittedClientAttributes(VORGANG); + } + + @Test + void shouldReturnVorgang() { + var cleanedVorgang = VorgangTestFactory.create(); + doReturn(cleanedVorgang).when(service).removeUnpermittedClientAttributes(any()); + + var result = service.getOriginalVorgangById(VorgangTestFactory.ID); + + assertThat(result).isSameAs(cleanedVorgang); + } + } + @Nested class TestAssignToUser { - private Command command = CommandTestFactory.createBuilder().body(Map.of("assignedTo", "Klaus")).build(); + private final Command command = CommandTestFactory.createBuilder().body(Map.of("assignedTo", "Klaus")).build(); @Test void shouldCallRepository() { @@ -418,8 +453,8 @@ class VorgangServiceTest { @Nested class TestDeleteVorgang { - private Vorgang vorgang = VorgangTestFactory.create(); - private VorgangStub stub = VorgangStubTestFactory.create(); + private final Vorgang vorgang = VorgangTestFactory.create(); + private final VorgangStub stub = VorgangStubTestFactory.create(); @BeforeEach void init() { diff --git a/vorgang-manager-utils/pom.xml b/vorgang-manager-utils/pom.xml index 538d56028531189daa140542a6e5d3a58d70ed6a..6209b590bd46b072824685c59e4f9c9b3ea8391b 100644 --- a/vorgang-manager-utils/pom.xml +++ b/vorgang-manager-utils/pom.xml @@ -30,14 +30,14 @@ <parent> <groupId>de.ozgcloud.common</groupId> <artifactId>ozgcloud-common-dependencies</artifactId> - <version>4.2.0</version> + <version>4.3.1</version> <relativePath/> </parent> <groupId>de.ozgcloud.vorgang</groupId> <artifactId>vorgang-manager-utils</artifactId> <name>OZG-Cloud Vorgang Manager Utils</name> - <version>2.10.0-SNAPSHOT</version> + <version>2.13.0-SNAPSHOT</version> <properties> <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version> diff --git a/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java b/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java index e2d04eb7aa6802d558e320c719f574a1fd27c142..26ce9a4bfd51d6599523467afd695711263b441f 100644 --- a/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java +++ b/vorgang-manager-utils/src/main/java/de/ozgcloud/vorgang/common/grpc/FormDataMappingUtils.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Predicate; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.tuple.Pair; @@ -17,6 +18,11 @@ import de.ozgcloud.vorgang.vorgang.GrpcFormField; class FormDataMappingUtils { + private static final Predicate<Object> IS_VALUE = obj -> obj instanceof String + || obj instanceof Number + || obj instanceof LocalDate + || obj instanceof Boolean; + static final String VALUE_KEY = "value"; static final String LABEL_KEY = "label"; static final String CONTROL_DATA_KEY = "controlData"; @@ -72,13 +78,9 @@ class FormDataMappingUtils { static boolean isValue(Object value) { if (value instanceof Map<?, ?> valueMap) { - return valueMap.containsKey(VALUE_KEY) && - (valueMap.get(VALUE_KEY) instanceof String - || valueMap.get(VALUE_KEY) instanceof Number - || valueMap.get(VALUE_KEY) instanceof LocalDate - || valueMap.get(VALUE_KEY) instanceof Boolean); + return valueMap.containsKey(VALUE_KEY) && IS_VALUE.test(valueMap.get(VALUE_KEY)); } else { - return value instanceof String; + return IS_VALUE.test(value); } } diff --git a/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java b/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java index 3e9143321168141832df7261dc8b3069c5606acb..7694a8c144e0af1fd16d10ad76accb914103e471 100644 --- a/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java +++ b/vorgang-manager-utils/src/test/java/de/ozgcloud/vorgang/common/grpc/GrpcFormDataMapperTest.java @@ -115,6 +115,13 @@ class GrpcFormDataMapperTest { assertThat(grpcField.getLabel()).isEqualTo(FIELD_LABEL); } + + @Test + void shouldHaveNumeric() { + var grpcField = mapper.mapToField(FIELD_NAME, Map.of(VALUE_KEY, 42, LABEL_KEY, FIELD_LABEL)); + + assertThat(grpcField.getValue()).isEqualTo("42"); + } } } @@ -167,7 +174,6 @@ class GrpcFormDataMapperTest { @Test void shouldHaveSubFormField() { - GrpcFormData formData = mapper.mapToFormData(Map.of("key", Map.of("subKey", "value"))); assertThat(formData.getForm(0).getFieldList()).hasSize(1); @@ -175,6 +181,15 @@ class GrpcFormDataMapperTest { assertThat(formData.getForm(0).getField(0).getName()).isEqualTo("subKey"); assertThat(formData.getForm(0).getField(0).getValue()).isEqualTo("value"); } + + @Test + void shouldHaveNumericSubFormField() { + GrpcFormData formData = mapper.mapToFormData(Map.of("key", Map.of("subKey", 42))); + + assertThat(formData.getForm(0).getFieldList()).hasSize(1); + assertThat(formData.getForm(0).getField(0).getValue()).isEqualTo("42"); + } + } @DisplayName("Mapped SubFormField")