diff --git a/Jenkinsfile b/Jenkinsfile index ac7739f9f116a11e1108240bd5d0154242f8a39f..e57f8378b4b14030ce9bec891496876a98e124ea 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,7 +13,7 @@ pipeline { SH_SUCCESS_STATUS_CODE = 0 VORGANG_MANAGER_INTERFACE_VERSION="2.0.0" - FORMCYCLE_INTERFACE_VERSION="2.0.0"//readFormCycleInterfaceVersion() + FORMCYCLE_INTERFACE_VERSION="2.2.0-SNAPSHOT"//readFormCycleInterfaceVersion() } options { @@ -37,7 +37,7 @@ pipeline { steps { script { - echo "Using FormCacle Interface Version: ${FORMCYCLE_INTERFACE_VERSION}" + echo "Using FORMCYCLE Interface Version: ${FORMCYCLE_INTERFACE_VERSION}" cloneVorgangManagerRepo() } @@ -57,7 +57,7 @@ pipeline { steps { configFileProvider([configFile(fileId: 'maven-settings', variable: 'MAVEN_SETTINGS')]) { sh 'mvn --version' - sh 'mvn -f intelliform-adapter/formcycle-adapter/formcycle-adapter-interface -s $MAVEN_SETTINGS -Djava.version=11 install' + sh 'mvn -f eingang-manager/formcycle-adapter/formcycle-adapter-interface -s $MAVEN_SETTINGS -Djava.version=11 install' } } } @@ -147,7 +147,9 @@ Void cloneVorgangManagerRepo() { } dir("vorgang-manager") { - sh 'git checkout tags/${VORGANG_MANAGER_INTERFACE_VERSION} -b branch' + if ( !isSnapshotVersion([VORGANG_MANAGER_INTERFACE_VERSION]) ) { + sh 'git checkout tags/${VORGANG_MANAGER_INTERFACE_VERSION} -b branch' + } } configureGit("vorgang-manager") } @@ -155,13 +157,15 @@ Void cloneVorgangManagerRepo() { Void cloneEingangAdapterRepo() { withCredentials([usernamePassword(credentialsId: 'jenkins-gitea-access-token', passwordVariable: 'TOKEN', usernameVariable: 'USER')]) { - sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/mgm/intelliform-adapter.git' + sh 'git clone https://${USER}:${TOKEN}@git.ozg-sh.de/ozgcloud-app/eingang-manager.git' } - dir("intelliform-adapter") { - sh 'git checkout tags/${FORMCYCLE_INTERFACE_VERSION} -b branch' + dir("eingang-manager") { + if ( !isSnapshotVersion([FORMCYCLE_INTERFACE_VERSION]) ) { + sh 'git checkout tags/${FORMCYCLE_INTERFACE_VERSION} -b branch' + } } - configureGit("intelliform-adapter") + configureGit("eingang-manager") } Void configureGit(String directory) { @@ -177,3 +181,17 @@ Void configureGit(String directory) { String readFormCycleInterfaceVersion() { return sh(returnStdout: true, script: 'xmlstarlet sel -N w="http://maven.apache.org/POM/4.0.0" -t -v "//w:project/w:version" -n pom.xml').trim() } + +Boolean isSnapshotVersion(List versions) { + return matchRegexVersion(versions, SNAPSHOT_REGEX) +} + +Boolean matchRegexVersion(List versions, String regex) { + for (version in versions) { + if ( !(version ==~ regex) ) { + return false + } + } + + return true +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8fd4464c9bf7e61c2c5f83e2cd91c745062deec4..5bcd40c3ef7c003d5bb26bc0522965ad646c654c 100644 --- a/pom.xml +++ b/pom.xml @@ -16,11 +16,12 @@ <!-- Version of FORMCYCLE to built against. --> <xfc.version>7.4.0</xfc.version> - <formcycle-adapter-interface.version>2.0.0</formcycle-adapter-interface.version> - <lombok.version>1.18.26</lombok.version> - <junit-jupiter.version>5.6.0</junit-jupiter.version> - <mockito.version>5.1.1</mockito.version> - <assertj.version>3.24.2</assertj.version> + <formcycle-adapter-interface.version>2.2.0-SNAPSHOT</formcycle-adapter-interface.version> + <lombok.version>1.18.30</lombok.version> + <junit-jupiter.version>5.10.1</junit-jupiter.version> + <mockito.version>5.8.0</mockito.version> + <assertj.version>3.25.0</assertj.version> + <htmlcleaner.version>2.29</htmlcleaner.version> <!-- Maven plugin versions --> <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version> @@ -29,7 +30,6 @@ <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> <fc-server-maven-plugin.version>7.4.0</fc-server-maven-plugin.version> <fc-deploy-plugin-maven-plugin.version>7.0.1</fc-deploy-plugin-maven-plugin.version> - <htmlcleaner.version>2.27</htmlcleaner.version> </properties> <!-- Dependencies required by this plugin. --> @@ -53,6 +53,12 @@ <groupId>de.ozgcloud.eingang</groupId> <artifactId>formcycle-adapter-interface</artifactId> <version>${formcycle-adapter-interface.version}</version> + <exclusions> + <exclusion> + <groupId>com.google.api.grpc</groupId> + <artifactId>proto-google-common-protos</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> @@ -96,8 +102,6 @@ </dependencies> <build> - <finalName>${project.artifactId}</finalName> - <plugins> <!-- Configure the compilation process. At least Java 11 is required. --> <plugin> @@ -166,26 +170,6 @@ </configuration> </plugin> - <!-- Configure how the JAR is created, including manifest entries --> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <configuration> - <archive> - <manifest> - <addDefaultImplementationEntries>true</addDefaultImplementationEntries> - </manifest> - <manifestEntries> - <formcycle-version-requirement>${xfc.version}</formcycle-version-requirement> - <Build-Timestamp>${maven.build.timestamp}</Build-Timestamp> - <Implementation-Title>${project.groupId}:${project.artifactId}</Implementation-Title> - <Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id> - <Implementation-Version>${project.version}</Implementation-Version> - </manifestEntries> - </archive> - </configuration> - </plugin> - <!-- Configure how the final plugin JAR is created. --> <!-- It needs to be a fat JAR with the dependencies required by this plugin. --> <plugin> @@ -206,6 +190,9 @@ </descriptorRefs> <appendAssemblyId>false</appendAssemblyId> <archive> + <manifest> + <addDefaultImplementationEntries>true</addDefaultImplementationEntries> + </manifest> <manifestEntries> <Build-Timestamp>${maven.build.timestamp}</Build-Timestamp> <Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id> diff --git a/src/main/java/de/ozgcloud/formcycle/ExecutionResult.java b/src/main/java/de/ozgcloud/formcycle/ExecutionResult.java index 281ddf1a92ebe4a46b0e31565462c11daf9403f8..6f848b606668e7e63187dff6ef3207eb618c41a9 100644 --- a/src/main/java/de/ozgcloud/formcycle/ExecutionResult.java +++ b/src/main/java/de/ozgcloud/formcycle/ExecutionResult.java @@ -1,20 +1,19 @@ package de.ozgcloud.formcycle; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; +import java.util.List; +import de.ozgcloud.formcycle.errorhandling.Warning; import lombok.Builder; +import lombok.Getter; +import lombok.Singular; @Builder +@Getter public class ExecutionResult { - public static final String VORGANGNUMMER_PROPERTY_KEY = "vorgangnummer"; - private final String vorgangnummer; - public Map<String, String> get() { - return Map.of(VORGANGNUMMER_PROPERTY_KEY, StringUtils.trimToEmpty(vorgangnummer)); - } + @Singular + public List<Warning> warnings; } diff --git a/src/main/java/de/ozgcloud/formcycle/HttpPostRequestBuilder.java b/src/main/java/de/ozgcloud/formcycle/HttpPostRequestBuilder.java index 6ce0d9e6c13b4c722eb9e59bfb0310c4a1d2db29..508171fb362afba401f5e75dccad5a55076a4bd9 100644 --- a/src/main/java/de/ozgcloud/formcycle/HttpPostRequestBuilder.java +++ b/src/main/java/de/ozgcloud/formcycle/HttpPostRequestBuilder.java @@ -39,7 +39,7 @@ class HttpPostRequestBuilder { static final String FORMCYCLE_ENDPOINT = "formData"; static final String FORM_DATA_KEY = "formData"; - static final ContentType PROTOBUF_CONTENT_TYPE = ContentType.create("application/protobuf"); + static final ContentType PROTOBUF_CONTENT_TYPE = ContentType.create("application/x-protobuf"); static final String ATTACHMENT_KEY = "attachments"; static final String REPRESENTATION_KEY = "representations"; static final Pattern URL_WITH_ENDPOINT_PATTERN = Pattern.compile("^((https?://)?[^/]+)(/" + FORMCYCLE_ENDPOINT + ")$"); diff --git a/src/main/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClient.java b/src/main/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClient.java index c21d5586b7a09d71f1ec2a0ecebacb7cf1547f1f..aba16e632765ca91c136bd088716b900c59c399a 100644 --- a/src/main/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClient.java +++ b/src/main/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClient.java @@ -26,18 +26,13 @@ import static java.util.Objects.*; import static org.apache.commons.lang3.StringUtils.*; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Optional; -import org.apache.commons.io.IOUtils; import org.apache.http.HttpHost; -import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.HttpRoutePlanner; @@ -47,12 +42,12 @@ import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.DefaultProxyRoutePlanner; import org.apache.http.protocol.HttpContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import de.ozgcloud.eingang.formcycle.FormCycleConfirmationResponse; import de.ozgcloud.eingang.formcycle.FormCycleFormData; import de.ozgcloud.formcycle.attachment.FormcycleAttachment; +import de.ozgcloud.formcycle.client.OzgCloudResponseHandler; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor @@ -60,10 +55,11 @@ public class OzgCloudFormDataHttpClient { static final int HTTPCLIENT_TIMEOUT = 10 * 60 * 1000; - private static final Logger LOG = LoggerFactory.getLogger(OzgCloudFormDataHttpClient.class); - private final OzgCloudConfig config; private final SystemPropertiesProvider systemPropertiesProvider; + private final PortParser portParser; + private final OzgCloudResponseHandler responseHandler; + private Optional<ProxyConfig> systemProxyConfig; private BasicCredentialsProvider credentialsProvider; @@ -71,15 +67,15 @@ public class OzgCloudFormDataHttpClient { private HttpHost proxyHost; public FormCycleConfirmationResponse send(FormCycleFormData formData, Collection<FormcycleAttachment> attachments, - Collection<FormcycleAttachment> representations) throws IOException { - - try ( - var closeableHttpClient = createCloseableClient(); - var closeableResponse = closeableHttpClient.execute(buildPostRequest(formData, attachments, representations), getContext())) { - return parseResponse(closeableResponse); - } catch (Exception e) { - LOG.info("Used OzgCloudConfig: {}", config); + Collection<FormcycleAttachment> representations) { + + try (var closeableHttpClient = createCloseableClient()) { + return closeableHttpClient.execute(buildPostRequest(formData, attachments, representations), responseHandler, getContext()); + } catch (OzgCloudRequestException e) { + e.setOzgCloudConfig(config); throw e; + } catch (IOException e) { + throw new OzgCloudRequestException("Cannot send data to formcycle adapter in OZG cloud.", config, e); } } @@ -101,7 +97,7 @@ public class OzgCloudFormDataHttpClient { ProxyConfig getProxyConfig() { var proxyConfig = config.getProxyConfig(); - if (isNoneBlank(proxyConfig.getHost())) { + if (nonNull(proxyConfig) && isNoneBlank(proxyConfig.getHost())) { return proxyConfig; } if (isNull(systemProxyConfig)) { @@ -123,7 +119,7 @@ public class OzgCloudFormDataHttpClient { } return ProxyConfig.builder() .host(systemProxyHost) - .port(Utils.parsePort(systemPropertiesProvider.getHttpsProxyPort())) + .port(portParser.parse(systemPropertiesProvider.getHttpsProxyPort())) .user(systemPropertiesProvider.getHttpsProxyUser()) .password(systemPropertiesProvider.getHttpsProxyPassword()) .build(); @@ -136,7 +132,7 @@ public class OzgCloudFormDataHttpClient { } return ProxyConfig.builder() .host(systemProxyHost) - .port(Utils.parsePort(systemPropertiesProvider.getHttpProxyPort())) + .port(portParser.parse(systemPropertiesProvider.getHttpProxyPort())) .user(systemPropertiesProvider.getHttpProxyUser()) .password(systemPropertiesProvider.getHttpProxyPassword()) .build(); @@ -204,37 +200,4 @@ public class OzgCloudFormDataHttpClient { return clientContext; } - FormCycleConfirmationResponse parseResponse(CloseableHttpResponse response) throws IOException { - checkResponseStatus(response); - InputStream responseContent = response.getEntity().getContent(); - return FormCycleConfirmationResponse.parseFrom(responseContent); - } - - private void checkResponseStatus(CloseableHttpResponse response) { - var statusLine = response.getStatusLine(); - if (statusLine.getStatusCode() != HttpStatus.SC_OK) { - throw new UnexpectedStatusCodeException(response); - } - } - - static class UnexpectedStatusCodeException extends RuntimeException { - private static final String MESSAGE = "Unexcepted Status received: %s"; - - UnexpectedStatusCodeException(CloseableHttpResponse response) { - super(buildFullMessage(response)); - } - - private static String buildFullMessage(CloseableHttpResponse response) { - try { - StringBuilder messageBuilder = new StringBuilder(String.format(MESSAGE, response.getStatusLine())).append("\n"); - - IOUtils.readLines(response.getEntity().getContent(), StandardCharsets.UTF_8) - .stream().forEach(line -> messageBuilder.append(line).append("\n")); - - return messageBuilder.toString(); - } catch (IOException e) { - return MESSAGE + "\nError reading body"; - } - } - } } diff --git a/src/main/java/de/ozgcloud/formcycle/OzgCloudPlugin.java b/src/main/java/de/ozgcloud/formcycle/OzgCloudPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..4046d0db6298ac9c0747f1b83ff2216f02b45167 --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/OzgCloudPlugin.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle; + +import static de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory.*; +import static org.apache.commons.lang3.StringUtils.*; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.formcycle.attachment.AttachmentMapper; +import de.ozgcloud.formcycle.client.OzgCloudResponseHandler; +import de.ozgcloud.formcycle.errorhandling.OzgPluginError; +import de.ozgcloud.formcycle.errorhandling.OzgPluginSoftError; +import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; +import de.ozgcloud.formcycle.errorhandling.TechnicalException; +import de.ozgcloud.formcycle.formdata.OzgCloudFormDataMapper; +import de.ozgcloud.formcycle.formdata.PluginFormDataAdapter; +import de.xima.fc.beans.interfaces.IGuiIcon; +import de.xima.fc.entities.Attachment; +import de.xima.fc.exceptions.AbstractAbruptCompletionException; +import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeData; +import de.xima.fc.interfaces.workflow.execution.IFileValueDescriptor; +import de.xima.fc.interfaces.workflow.execution.INormalCompletionResult; +import de.xima.fc.interfaces.workflow.mixin.IElementCategory; +import de.xima.fc.interfaces.workflow.mixin.IKeyValueSummarizableNode; +import de.xima.fc.interfaces.workflow.mixin.ISummaryKeyValueModel; +import de.xima.fc.interfaces.workflow.params.IGetElementPrototypesParams; +import de.xima.fc.interfaces.workflow.params.IGetElementSummaryParams; +import de.xima.fc.interfaces.workflow.params.INodeExecutionParams; +import de.xima.fc.interfaces.workflow.value.IUnionValueDescriptor; +import de.xima.fc.interfaces.workflow.value.IValueBuilder; +import de.xima.fc.interfaces.workflow.value.IValueDescriptor; +import de.xima.fc.interfaces.workflow.value.IValueDescriptorFactory; +import de.xima.fc.mdl.ui.GuiIcon; +import de.xima.fc.workflow.FileValueDescriptor; +import de.xima.fc.workflow.SummaryKeyValueModelBuilder; +import de.xima.fc.workflow.enums.EWorkflowElementCategory; +import de.xima.fc.workflow.mixin.IPluginActionNodeHandler; + +public final class OzgCloudPlugin + implements IPluginActionNodeHandler<OzgPluginWorkflowNodeProperties>, IKeyValueSummarizableNode<OzgPluginWorkflowNodeProperties> { + + static final String VORGANGNUMMER_PROPERTY_KEY = "vorgangnummer"; + + private final transient PortParser portParser = new PortParser(); + private transient OzgPluginPropertiesSupplier ozgPluginPropertiesSupplier; + private transient IPluginInitializeData initializeData; + + @Override + public void initialize(IPluginInitializeData initializeData) { + this.initializeData = initializeData; + this.ozgPluginPropertiesSupplier = new OzgPluginPropertiesSupplier(initializeData, portParser); + } + + @Override + public String getPropertiesViewXhtmlName() { + return "OzgCloudPlugin.xhtml"; + } + + @Override + public String getName() { + return "de.ozgcloud.formcycle.OzgCloudPlugin"; + } + + @Override + public IElementCategory getMainCategory(IGetElementPrototypesParams params) { + // Main category where this node is shown in the drawer panel to the left of the + // designer. + return EWorkflowElementCategory.MAIN_ACTION; + } + + @Override + public IElementCategory getSubCategory(IGetElementPrototypesParams params) { + // Sub category where this node is shown in the drawer panel to the left of the + // designer. + return EWorkflowElementCategory.SUB_ACTION_COMMON; + } + + @Override + public IGuiIcon getPrototypeIcon(IGetElementPrototypesParams params) { + // Optional icon for this workflow node. Shown next to the node's name. + return GuiIcon.valueOf("fa-question"); + } + + @Override + public IFileValueDescriptor getFileValueDescriptor() { + // Implement this method when this node can return files. + // This node can then be selected by other actions that require files. + // If this node does not provide files, remove this method + return new FileValueDescriptor("OzgCloudPlugin.files"); + } + + @Override + public IValueDescriptor<?, ? extends IValueBuilder<?>> getSuccessValueDescriptor(IValueDescriptorFactory factory) { + // Data returned by this node when it is executed + // Users can access this data e.g. via placeholders, for example: + // [%$Action.RESULT.name%] + // [%$Action.RESULT.items.name%] + // [%$Action.RESULT.items.state%] + return factory.recordBuilder().requiredProperty(VORGANGNUMMER_PROPERTY_KEY, factory.string()).build(); + } + + @Override + public IUnionValueDescriptor<String> getErrorValueDescriptor(IValueDescriptorFactory factory) { + // For each error that can occur, additional data can be provided + // Users can access this data e.g. via placeholders, for example: + // [%$Action.ERROR_CODE%] + // [%$Action.ERROR_MESSAGE%] + // [%$Action.ERROR.file%] + // [%$Action.ERROR.requiredPermissions[0]%] + // [%$Action.ERROR.message%] + return factory.unionStringBuilder() // + .add(OzgPluginError.INTERNAL_ERROR.name(), + factory.recordBuilder().requiredProperty(EXCEPTION_ID_KEY, factory.string()).build()) + .add(OzgPluginError.OZGCLOUD_REQUEST_FAILED.name(), + factory.recordBuilder().requiredProperty(EXCEPTION_ID_KEY, factory.string()).build()) + .build(); + } + + @Override + public ISummaryKeyValueModel getElementSummaryKeyValueModel(IGetElementSummaryParams<OzgPluginWorkflowNodeProperties> params) { + // A simple summary table with the most important fields from the properties + // This is shown when the user clicks on the info icon on a node in the designer. + return new SummaryKeyValueModelBuilder().build(); + } + + @Override + public INormalCompletionResult execute(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) throws AbstractAbruptCompletionException { + try { + return buildSuccessResult(createPluginExecutor(params).execute(), params); + } catch (OzgCloudRequestException e) { + throw createExceptionFactory(params).createOzgCloudRequestException(e); + } catch (TechnicalException e) { + throw createExceptionFactory(params).createInternalException(e); + } catch (Exception e) { + throw createExceptionFactory(params).createInternalException("Unexpected error occurred", e); + } + } + + OzgPluginExecutor createPluginExecutor(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + return new OzgPluginExecutor(new OzgCloudFormDataMapper(), createOzgCloudClient(params), new AttachmentMapper(), + createFormDataAdapter(params), createAttachmentSupplier(params)); + } + + OzgCloudFormDataHttpClient createOzgCloudClient(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + return new OzgCloudFormDataHttpClient(buildOzgCloudConfig(params.getData()), new SystemPropertiesProvider(), portParser, + new OzgCloudResponseHandler(new ObjectMapper())); + } + + OzgCloudConfig buildOzgCloudConfig(OzgPluginWorkflowNodeProperties formProperties) { + var ozgCloudConfigBuilder = OzgCloudConfig.builder().eingangsAdapterUrl(getEingangsAdapterUrl(formProperties)); + ozgPluginPropertiesSupplier.getProxyProperties().ifPresent(ozgCloudConfigBuilder::proxyConfig); + return ozgCloudConfigBuilder.build(); + } + + String getEingangsAdapterUrl(OzgPluginWorkflowNodeProperties formProperties) { + var eingangsAdapterUrl = formProperties.isUseCustomEingangsAdapterUrl() + ? formProperties.getEingangsAdapterUrl() + : ozgPluginPropertiesSupplier.getEingangsAdapterUrl(); + + if (isBlank(eingangsAdapterUrl)) { + throw new TechnicalException("Eingangsadapter URL cannot be blank"); + } + return eingangsAdapterUrl; + } + + PluginFormDataAdapter createFormDataAdapter(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + return new PluginFormDataAdapter(params.getWorkflowContext().env(), params.getData().getOrganisationsEinheitId()); + } + + private Supplier<List<Attachment>> createAttachmentSupplier(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + var formRecord = params.getWorkflowContext().env().getFormRecord(); + return () -> Optional.ofNullable(formRecord.getAttachments()).orElse(List.of()); + } + + NodeThrewExceptionFactory createExceptionFactory(INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + return new NodeThrewExceptionFactory(params.throwingException()); + } + + INormalCompletionResult buildSuccessResult(ExecutionResult pluginExecutionResult, INodeExecutionParams<OzgPluginWorkflowNodeProperties> params) { + var resultBuilder = params.normalResult(); + resultBuilder.success(Map.of(VORGANGNUMMER_PROPERTY_KEY, pluginExecutionResult.getVorgangnummer())); + pluginExecutionResult.getWarnings() + .forEach(warning -> resultBuilder.softError(warning.getErrorCode(), warning.getMessage(), warning.getValue(), warning.getCause())); + return resultBuilder.build(); + } + + @Override + public Class<OzgPluginError> getErrorCodeClass() { + // Class of the enumeration with the possible types of errors that may occur. + return OzgPluginError.class; + } + + @Override + public Class<OzgPluginSoftError> getSoftErrorCodeClass() { + // Class of the enumeration with the possible types of soft errors that may occur. + return OzgPluginSoftError.class; + } + + @Override + public IPluginInitializeData getPluginInitializeData() { + return initializeData; + } + +} diff --git a/src/main/java/de/ozgcloud/formcycle/PluginBundleProperties.java b/src/main/java/de/ozgcloud/formcycle/OzgPluginBundleProperties.java similarity index 97% rename from src/main/java/de/ozgcloud/formcycle/PluginBundleProperties.java rename to src/main/java/de/ozgcloud/formcycle/OzgPluginBundleProperties.java index bb3f81373257dfee3f913df329deb7a37276a8ff..a93d7172bdd050a50db828f357627623cce874ce 100644 --- a/src/main/java/de/ozgcloud/formcycle/PluginBundleProperties.java +++ b/src/main/java/de/ozgcloud/formcycle/OzgPluginBundleProperties.java @@ -36,7 +36,7 @@ import de.xima.fc.plugin.config.IBundleProperties; import de.xima.fc.plugin.models.config.BundleConfigGroupItem; import de.xima.fc.plugin.models.config.BundleConfigParam; -public class PluginBundleProperties implements IBundleProperties { +public class OzgPluginBundleProperties implements IBundleProperties { private static final boolean CRYPTIC_VALUE = true; private static final boolean MANDATORY = true; diff --git a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutor.java b/src/main/java/de/ozgcloud/formcycle/OzgPluginExecutor.java similarity index 68% rename from src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutor.java rename to src/main/java/de/ozgcloud/formcycle/OzgPluginExecutor.java index 492b79f0f8ebbdadd1a35472bc7b93317946f33b..7d493ea10dfa343fb4e07770f33f7772c739ba34 100644 --- a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutor.java +++ b/src/main/java/de/ozgcloud/formcycle/OzgPluginExecutor.java @@ -24,7 +24,6 @@ package de.ozgcloud.formcycle; import static java.util.Objects.*; -import java.io.IOException; import java.util.EnumMap; import java.util.HashSet; import java.util.List; @@ -38,45 +37,43 @@ import org.slf4j.LoggerFactory; import de.ozgcloud.formcycle.attachment.AttachmentMapper; import de.ozgcloud.formcycle.attachment.AttachmentType; import de.ozgcloud.formcycle.attachment.FormcycleAttachment; -import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.OzgPluginSoftError; +import de.ozgcloud.formcycle.errorhandling.Warning; import de.ozgcloud.formcycle.formdata.OzgCloudFormDataMapper; import de.ozgcloud.formcycle.formdata.PluginFormDataAdapter; import de.xima.fc.entities.Attachment; -import de.xima.fc.exceptions.AbstractAbruptCompletionException; -import de.xima.fc.exceptions.NodeThrewException; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor -public final class WorkflowElementNodeExecutor { +public final class OzgPluginExecutor { - private static final Logger LOG = LoggerFactory.getLogger(WorkflowElementNodeExecutor.class); + private static final Logger LOG = LoggerFactory.getLogger(OzgPluginExecutor.class); private final OzgCloudFormDataMapper ozgCloudFormDataMapper; private final OzgCloudFormDataHttpClient ozgHttpClient; - private final NodeThrewExceptionFactory exceptionFactory; private final AttachmentMapper attachmentMapper; private final PluginFormDataAdapter pluginFormDataAdapter; private final Supplier<List<Attachment>> attachmentsSupplier; - public ExecutionResult execute() throws AbstractAbruptCompletionException { + public ExecutionResult execute() { LOG.debug("Executing plugin WorkflowElementNodePlugin"); var formData = pluginFormDataAdapter.readFormData(); - var formcycleFormData = ozgCloudFormDataMapper.map(formData); var attachments = getAttachedFiles(formData.getAttachmentUuids()); + var resultBuilder = ExecutionResult.builder(); + if (isNull(attachments.get(AttachmentType.REPRESENTATION))) { + resultBuilder.warning( + buildWarning("Representation is missing. Ensure workflow is configured to attach form view to form data.")); + } - try { - var formCycleConfirmationResponse = ozgHttpClient.send(formcycleFormData, attachments.get(AttachmentType.ATTACHMENT), - attachments.get(AttachmentType.REPRESENTATION)); - LOG.debug("Formcycle adapter response: {}", formCycleConfirmationResponse); - - return ExecutionResult.builder().vorgangnummer(formCycleConfirmationResponse.getVorgangNummer()).build(); + var formcycleFormData = ozgCloudFormDataMapper.map(formData); + var formCycleConfirmationResponse = ozgHttpClient.send(formcycleFormData, attachments.get(AttachmentType.ATTACHMENT), + attachments.get(AttachmentType.REPRESENTATION)); + LOG.debug("Formcycle adapter response: {}", formCycleConfirmationResponse); - } catch (IOException | RuntimeException e) { - throw exceptionFactory.createCannotSendException("Cannot send data to formcycle adapter in OZG cloud.", e); - } + return resultBuilder.vorgangnummer(formCycleConfirmationResponse.getVorgangNummer()).build(); } - Map<AttachmentType, Set<FormcycleAttachment>> getAttachedFiles(Set<String> uploadFieldUuids) throws NodeThrewException { + Map<AttachmentType, Set<FormcycleAttachment>> getAttachedFiles(Set<String> uploadFieldUuids) { var resultMap = new EnumMap<AttachmentType, Set<FormcycleAttachment>>(AttachmentType.class); for (Attachment attachment : attachmentsSupplier.get()) { if (uploadFieldUuids.contains(attachment.getUUID())) { @@ -85,10 +82,10 @@ public final class WorkflowElementNodeExecutor { } resultMap.computeIfAbsent(AttachmentType.REPRESENTATION, e -> new HashSet<>()).add(attachmentMapper.map(attachment)); } - if (isNull(resultMap.get(AttachmentType.REPRESENTATION))) { - LOG.warn("Representation is missing. Activate checkbox 'An den Vorgang anhaengen' in 'Word-Datei befuellen' plugin"); - } return resultMap; } + Warning buildWarning(String message) { + return Warning.builder().errorCode(OzgPluginSoftError.MISSING_REPRESENTATION).message(message).build(); + } } diff --git a/src/main/java/de/ozgcloud/formcycle/PluginPropertiesMapper.java b/src/main/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplier.java similarity index 54% rename from src/main/java/de/ozgcloud/formcycle/PluginPropertiesMapper.java rename to src/main/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplier.java index a721b9770ec8b35e522888bfe83807c9d86c3147..33df4b7e80594fd61e27e2ec4e5600533dd5158c 100644 --- a/src/main/java/de/ozgcloud/formcycle/PluginPropertiesMapper.java +++ b/src/main/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplier.java @@ -26,21 +26,40 @@ package de.ozgcloud.formcycle; import static java.util.Objects.*; import static org.apache.commons.lang3.StringUtils.*; +import java.util.Optional; import java.util.Properties; -import de.ozgcloud.formcycle.errorhandling.UserErrorDispatcher; +import de.ozgcloud.formcycle.errorhandling.TechnicalException; +import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeData; import lombok.RequiredArgsConstructor; @RequiredArgsConstructor -public class PluginPropertiesMapper { +public class OzgPluginPropertiesSupplier { - private final UserErrorDispatcher userErrorDispatcher; + private final IPluginInitializeData pluginData; + private final PortParser portParser; - public String getEingangsAdapterUrl(Properties pluginProperties) { - return pluginProperties.getProperty(OzgCloudConfig.KEY_EINGANGSADAPTER_URL); + public String getEingangsAdapterUrl() { + return pluginData.getProperties().getProperty(OzgCloudConfig.KEY_EINGANGSADAPTER_URL); } - public ProxyConfig mapProxyProperties(Properties pluginProperties) { + public Optional<ProxyConfig> getProxyProperties() { + var pluginProperties = pluginData.getProperties(); + validateProxyConfig(pluginProperties); + var proxyHostProperty = pluginProperties.getProperty(ProxyConfig.KEY_PROXY_HOST); + return isBlank(proxyHostProperty) ? Optional.empty() : Optional.of(buildProxyConfig(pluginProperties)); + } + + void validateProxyConfig(Properties pluginProperties) { + var proxyHostProperty = pluginProperties.getProperty(ProxyConfig.KEY_PROXY_HOST); + var port = getPort(pluginProperties); + if ((isBlank(proxyHostProperty) && nonNull(port)) + || (isNotBlank(proxyHostProperty) && isNull(port))) { + throw new TechnicalException("Proxy configuration is invalid. Both host and port must be set or none of them."); + } + } + + ProxyConfig buildProxyConfig(Properties pluginProperties) { return ProxyConfig.builder() .host(pluginProperties.getProperty(ProxyConfig.KEY_PROXY_HOST)) .port(getPort(pluginProperties)) @@ -51,14 +70,7 @@ public class PluginPropertiesMapper { Integer getPort(Properties pluginProperties) { var portProperty = pluginProperties.getProperty(ProxyConfig.KEY_PROXY_PORT); - if (isBlank(portProperty)) { - return null; - } - var port = Utils.parsePort(portProperty); - if (isNull(port)) { - userErrorDispatcher.sendWrongProxyPortFormatMessage(); - } - return port; + return isBlank(portProperty) ? null : portParser.parse(portProperty); } } diff --git a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeProps.java b/src/main/java/de/ozgcloud/formcycle/OzgPluginWorkflowNodeProperties.java similarity index 96% rename from src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeProps.java rename to src/main/java/de/ozgcloud/formcycle/OzgPluginWorkflowNodeProperties.java index 7773fe8affacf6d7424e3116f32839bf1f2676a6..8ec21b80a0d5c71675739afd10ed4ea9d44d2cf3 100644 --- a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeProps.java +++ b/src/main/java/de/ozgcloud/formcycle/OzgPluginWorkflowNodeProperties.java @@ -54,7 +54,7 @@ import lombok.Setter; withValues = {"true"}, requiredProperties = {"eingangsAdapterUrl"} ) -public final class WorkflowElementNodeProps extends BaseActionProps { +public final class OzgPluginWorkflowNodeProperties extends BaseActionProps { @NotEmpty @Size(max = 20) diff --git a/src/main/java/de/ozgcloud/formcycle/Utils.java b/src/main/java/de/ozgcloud/formcycle/PortParser.java similarity index 77% rename from src/main/java/de/ozgcloud/formcycle/Utils.java rename to src/main/java/de/ozgcloud/formcycle/PortParser.java index 6811cb2fd299332479d1371d9bf244289bc67121..ceb6bf39b8ae2e65fcfee487c7ede366a11d1063 100644 --- a/src/main/java/de/ozgcloud/formcycle/Utils.java +++ b/src/main/java/de/ozgcloud/formcycle/PortParser.java @@ -27,17 +27,18 @@ import static org.apache.commons.lang3.StringUtils.*; import java.util.Optional; import java.util.regex.Pattern; -import lombok.NoArgsConstructor; - -@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE) -public class Utils { +public class PortParser { private static final Pattern PORT_PATTERN = Pattern.compile("\\s*\\d{1,5}\\s*"); - public static Integer parsePort(String portString) { - if (isBlank(portString) || !PORT_PATTERN.matcher(portString).matches()) { + public Integer parse(String portString) { + if (isBlank(portString)) { return null; } - return Optional.of(Integer.parseInt(portString.trim())).filter(port -> port > 0 && port < 65536).orElse(null); + if (!PORT_PATTERN.matcher(portString).matches()) { + throw new IllegalArgumentException("Port must be a number"); + } + return Optional.of(Integer.parseInt(portString.trim())).filter(port -> port > 0 && port < 65536) + .orElseThrow(() -> new IllegalArgumentException("Port must be a number between 1 and 65535.")); } } diff --git a/src/main/java/de/ozgcloud/formcycle/ProxyConfig.java b/src/main/java/de/ozgcloud/formcycle/ProxyConfig.java index 6e2bec1b895cafb8c5d68e1e4fb64ab268acddea..a4c0f4397a633818a6f89dded5fc6cc2bfecdd63 100644 --- a/src/main/java/de/ozgcloud/formcycle/ProxyConfig.java +++ b/src/main/java/de/ozgcloud/formcycle/ProxyConfig.java @@ -31,7 +31,7 @@ import lombok.ToString; @Builder @Getter -@ToString(exclude = "password") +@ToString public class ProxyConfig implements Serializable { private static final long serialVersionUID = 1L; @@ -54,6 +54,8 @@ public class ProxyConfig implements Serializable { private String host; private Integer port; private String user; + + @ToString.Exclude private String password; } diff --git a/src/main/java/de/ozgcloud/formcycle/WorkflowElementEvent.java b/src/main/java/de/ozgcloud/formcycle/WorkflowElementEvent.java deleted file mode 100644 index 52163587766e07f2fe7ac889fbdbce88d917b86c..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/formcycle/WorkflowElementEvent.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das - * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * 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.formcycle; - -import java.util.List; -import java.util.Locale; -import java.util.Map; - -import de.xima.fc.entities.Benutzer; -import de.xima.fc.entities.Mandant; -import de.xima.fc.entities.Projekt; -import de.xima.fc.entities.Vorgang; -import de.xima.fc.workflow.event.AFormRecordEventData; - -/** - * Custom event for the workflow trigger that is initiated by the HTTP servlet action plugin when an HTTP request is - * made. - * @author unspecified - */ -@SuppressWarnings("serial") -public final class WorkflowElementEvent extends AFormRecordEventData { - private final String type; - private final Map<String, List<String>> requestParams; - - /** - * Creates a new event data instance with the given data. - * @param client Client who owns the form record. - * @param user User who initiated the event. - * @param locale Request locale to use for locale-sensitive operations. - * @param project Project to which the form record belongs. - * @param formRecord Form record for which to trigger events. - * @param type Custom type of the event, a workflow trigger reacts only to a given type. - * @param requestParams HTTP parameters of the request that triggered this event. - */ - public WorkflowElementEvent(Mandant client, Benutzer user, Locale locale, Projekt project, Vorgang formRecord, String type, Map<String, List<String>> requestParams) { - super(client, user, locale, project, formRecord); - this.requestParams = requestParams; - this.type = type; - } - - /** - * @return HTTP parameters of the request that triggered this event. - */ - public Map<String, List<String>> getRequestParams() { - return requestParams; - } - - /** - * @return The type of this event. A trigger only reacts to a specific type. - */ - public String getType() { - return type; - } - - @Override - public Boolean isAnonymize() { - return false; - } -} diff --git a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeBean.java b/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeBean.java deleted file mode 100644 index e6cd2a94e44229d209653893e4ed9a435876f386..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodeBean.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das - * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * 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.formcycle; - -import java.util.Properties; -import java.util.concurrent.atomic.AtomicReference; - -import javax.faces.view.ViewScoped; -import javax.inject.Named; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeBeanData; -import de.xima.fc.interfaces.plugin.lifecycle.helper.IPluginFileHelper; -import de.xima.fc.interfaces.plugin.lifecycle.helper.IPluginResourceHelper; -import de.xima.fc.plugin.exception.FCPluginException; -import de.xima.fc.workflow.mixin.INodePropertyPluginBean; - -/** - * Controller bean for the custom UI when editing the properties of the workflow node. - * A bean is not required, often it suffices to add an XHTML page without a bean. This - * class is a stub that that you can expand when you do not a bean, such as when you - * want to enrich your UI with AJAX-enhanced logic. - * @author unspecified - */ -@Named -@ViewScoped -public final class WorkflowElementNodeBean implements INodePropertyPluginBean<WorkflowElementNodeProps> { - - private static final Logger LOG = LoggerFactory.getLogger(WorkflowElementNodeBean.class); - - private transient AtomicReference<IPluginInitializeBeanData> initializeBeanData; - - /** - * A method that could be used the the action for some command button in your UI. - */ - public void controllerAction() { - // Add custom logic here when the button was pressed... - LOG.info("Controller action was called"); - } - - @Override - public IPluginFileHelper getFileHelper() { - return initializeBeanData.get().getFileHelper(); - } - - @Override - public Properties getProperties() { - return initializeBeanData.get().getProperties(); - } - - @Override - public IPluginResourceHelper getResourceHelper() { - return initializeBeanData.get().getResourceHelper(); - } - - @Override - public void initialize(IPluginInitializeBeanData initializeBeanData) throws FCPluginException { - this.initializeBeanData = new AtomicReference<>(initializeBeanData); - } -} diff --git a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodePlugin.java b/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodePlugin.java deleted file mode 100644 index a315fcf08060e2b447bf7809e1f9517bac2c35ee..0000000000000000000000000000000000000000 --- a/src/main/java/de/ozgcloud/formcycle/WorkflowElementNodePlugin.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das - * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * 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.formcycle; - -import static org.apache.commons.lang3.StringUtils.*; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.function.Supplier; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import de.ozgcloud.formcycle.attachment.AttachmentMapper; -import de.ozgcloud.formcycle.errorhandling.EWorkflowElementNodeError; -import de.ozgcloud.formcycle.errorhandling.EWorkflowElementNodeSoftError; -import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; -import de.ozgcloud.formcycle.errorhandling.UserErrorDispatcher; -import de.ozgcloud.formcycle.formdata.OzgCloudFormDataMapper; -import de.ozgcloud.formcycle.formdata.PluginFormDataAdapter; -import de.xima.fc.beans.interfaces.IGuiIcon; -import de.xima.fc.entities.Attachment; -import de.xima.fc.exceptions.AbstractAbruptCompletionException; -import de.xima.fc.exceptions.NodeThrewException; -import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeData; -import de.xima.fc.interfaces.workflow.elements.IElementHelpLocation; -import de.xima.fc.interfaces.workflow.execution.IFileValueDescriptor; -import de.xima.fc.interfaces.workflow.execution.INormalCompletionResult; -import de.xima.fc.interfaces.workflow.execution.IWorkflowExecutionEnvironmentData; -import de.xima.fc.interfaces.workflow.mixin.IElementCategory; -import de.xima.fc.interfaces.workflow.mixin.IKeyValueSummarizableNode; -import de.xima.fc.interfaces.workflow.mixin.ISummaryKeyValueModel; -import de.xima.fc.interfaces.workflow.params.IGetElementPrototypesParams; -import de.xima.fc.interfaces.workflow.params.IGetElementSummaryParams; -import de.xima.fc.interfaces.workflow.params.INodeExecutionParams; -import de.xima.fc.interfaces.workflow.value.IUnionValueDescriptor; -import de.xima.fc.interfaces.workflow.value.IValueBuilder; -import de.xima.fc.interfaces.workflow.value.IValueDescriptor; -import de.xima.fc.interfaces.workflow.value.IValueDescriptorFactory; -import de.xima.fc.mdl.ui.GuiIcon; -import de.xima.fc.workflow.ElementHelpLocation; -import de.xima.fc.workflow.FileValueDescriptor; -import de.xima.fc.workflow.SummaryKeyValueModelBuilder; -import de.xima.fc.workflow.enums.EWorkflowElementCategory; -import de.xima.fc.workflow.mixin.INodePropertyPluginBean; -import de.xima.fc.workflow.mixin.IPluginActionNodeHandler; - -public final class WorkflowElementNodePlugin - implements IPluginActionNodeHandler<WorkflowElementNodeProps>, IKeyValueSummarizableNode<WorkflowElementNodeProps> { - - private static final Logger LOG = LoggerFactory.getLogger(WorkflowElementNodePlugin.class); - - private transient IPluginInitializeData initializeData; - private transient PluginPropertiesMapper pluginPropertiesMapper; - - @Override - public void initialize(IPluginInitializeData initializeData) { - this.initializeData = initializeData; - this.pluginPropertiesMapper = createPropertiesMapper(); - } - - PluginPropertiesMapper createPropertiesMapper() { - return new PluginPropertiesMapper(new UserErrorDispatcher()); - } - - @Override - public String getPropertiesViewXhtmlName() { - return "WorkflowElementNode.xhtml"; - } - - @Override - public String getName() { - return "de.ozgcloud.formcycle.WorkflowElementNodePlugin"; - } - - @Override - public IElementCategory getMainCategory(IGetElementPrototypesParams params) { - // Main category where this node is shown in the drawer panel to the left of the - // designer. - return EWorkflowElementCategory.MAIN_ACTION; - } - - @Override - public IElementCategory getSubCategory(IGetElementPrototypesParams params) { - // Sub category where this node is shown in the drawer panel to the left of the - // designer. - return EWorkflowElementCategory.SUB_ACTION_COMMON; - } - - @Override - public IElementHelpLocation getHelpPageLocation(Locale locale) throws IOException { - // Optional HTML page documenting how to use this workflow node. - // Use ElementHelpLocation#forExternalLink if you host the help page on an external server - // Use ElementHelpLocation#forOfficialHelpPage when you host the help page on the FORMCYCLE wiki - final var path = String.format("WEB-INF/properties/WorkflowElementNodeHelp_%s.html", locale.toLanguageTag()); - return ElementHelpLocation.forHtmlFile(getClass().getClassLoader(), path, StandardCharsets.UTF_8); - } - - @Override - public IGuiIcon getPrototypeIcon(IGetElementPrototypesParams params) { - // Optional icon for this workflow node. Shown next to the node's name. - return GuiIcon.valueOf("fa-question"); - } - - @Override - public IFileValueDescriptor getFileValueDescriptor() { - // Implement this method when this node can return files. - // This node can then be selected by other actions that require files. - // If this node does not provide files, remove this method - return new FileValueDescriptor("WorkflowElementNodePlugin.files"); - } - - @Override - public IValueDescriptor<?, ? extends IValueBuilder<?>> getSuccessValueDescriptor(IValueDescriptorFactory factory) { - // Data returned by this node when it is executed - // Users can access this data e.g. via placeholders, for example: - // [%$Action.RESULT.name%] - // [%$Action.RESULT.items.name%] - // [%$Action.RESULT.items.state%] - return factory.recordBuilder().requiredProperty(ExecutionResult.VORGANGNUMMER_PROPERTY_KEY, factory.string()).build(); - } - - @Override - public IUnionValueDescriptor<String> getErrorValueDescriptor(IValueDescriptorFactory factory) { - // For each error that can occur, additional data can be provided - // Users can access this data e.g. via placeholders, for example: - // [%$Action.ERROR_CODE%] - // [%$Action.ERROR_MESSAGE%] - // [%$Action.ERROR.file%] - // [%$Action.ERROR.requiredPermissions[0]%] - // [%$Action.ERROR.message%] - // TODO das ist dafür gedacht, Fehler an der Oberfläche zu zeigen, was wir eigentlich nicht brauchen man könnte es einfach löschen - return factory.unionStringBuilder() // - .add(EWorkflowElementNodeError.OZGCLOUD_CANNOT_SEND.name(), f -> f.recordBuilder() - .requiredProperty(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, f.string()) - .build() - ) - .add(EWorkflowElementNodeError.PLUGIN_CANNOT_GET_ATTACHMENT.name(), f -> f.recordBuilder() - .requiredProperty(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, f.string()) - .build() - ) - .add(EWorkflowElementNodeError.PLUGIN_CONFIG_ERROR.name(), f -> f.recordBuilder() - .requiredProperty(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, v -> v.string()) - .build()) - .build(); - } - - @Override - public IUnionValueDescriptor<String> getSoftErrorValueDescriptor(IValueDescriptorFactory factory) { - // For each error that can occur, additional data can be provided - // Users can access this data e.g. via placeholders, for example: - // [%$Action.ERROR_CODE%] - // [%$Action.ERROR_MESSAGE%] - // [%$Action.ERROR.file%] - // [%$Action.ERROR.requiredPermissions[0]%] - // [%$Action.ERROR.message%] - return factory.unionStringBuilder() // - .add(EWorkflowElementNodeSoftError.WRONG_HOST_NAME.name(), f -> f.recordBuilder() // - .requiredProperty("expectedHostName", v -> v.string()) // - .build()) // - .add(EWorkflowElementNodeSoftError.USER_DOES_NOT_EXIST.name(), f -> f.string()) // - .add(EWorkflowElementNodeError.PLUGIN_CONFIG_ERROR.name(), f -> f.recordBuilder() - .requiredProperty(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, v -> v.string()) - .build()) - .build(); - } - - @Override - public ISummaryKeyValueModel getElementSummaryKeyValueModel(IGetElementSummaryParams<WorkflowElementNodeProps> params) { - // A simple summary table with the most important fields from the properties - // This is shown when the user clicks on the info icon on a node in the designer. - return new SummaryKeyValueModelBuilder().build(); - } - - @Override - public INormalCompletionResult execute(INodeExecutionParams<WorkflowElementNodeProps> params) throws AbstractAbruptCompletionException { - var workflowExecutionEnvironmentData = params.getWorkflowContext().env(); - var formProperties = params.getData(); - PluginFormDataAdapter pluginFormDataAdapter = new PluginFormDataAdapter(workflowExecutionEnvironmentData, - formProperties.getOrganisationsEinheitId()); - var ozgCloudFormDataMapper = new OzgCloudFormDataMapper(); - var exceptionFactory = new NodeThrewExceptionFactory(params.throwingException()); - var attachmentsAdapter = new AttachmentMapper(exceptionFactory); - var pluginExecutor = new WorkflowElementNodeExecutor(ozgCloudFormDataMapper, createOzgClient(formProperties, exceptionFactory), - exceptionFactory, - attachmentsAdapter, pluginFormDataAdapter, createAttachmentSupplier(workflowExecutionEnvironmentData)); - var executionResult = pluginExecutor.execute(); - return params.normalResult().success(executionResult.get()).build(); - } - - // TODO hier braucht man die Factory eigentlich nicht, es würde reichen einfaches Exception zu werfen - OzgCloudFormDataHttpClient createOzgClient(WorkflowElementNodeProps formProperties, NodeThrewExceptionFactory exceptionFactory) - throws NodeThrewException { - var ozgCloudConfig = buildOzgCloudConfig(formProperties, exceptionFactory); - return new OzgCloudFormDataHttpClient(ozgCloudConfig, new SystemPropertiesProvider()); - } - - OzgCloudConfig buildOzgCloudConfig(WorkflowElementNodeProps formProperties, NodeThrewExceptionFactory exceptionFactory) - throws NodeThrewException { - var eingangsAdapterUrl = getEingangsAdapterUrl(formProperties); - if (isBlank(eingangsAdapterUrl)) { - throw exceptionFactory.createConfigurationException("Eingangsadapter URL cannot be blank"); - } - - return OzgCloudConfig.builder() - .eingangsAdapterUrl(eingangsAdapterUrl) - .proxyConfig(pluginPropertiesMapper.mapProxyProperties(initializeData.getProperties())) - .build(); - } - - String getEingangsAdapterUrl(WorkflowElementNodeProps formProperties) { - if (formProperties.isUseCustomEingangsAdapterUrl()) { - return formProperties.getEingangsAdapterUrl(); - } - return pluginPropertiesMapper.getEingangsAdapterUrl(initializeData.getProperties()); - } - - private Supplier<List<Attachment>> createAttachmentSupplier(IWorkflowExecutionEnvironmentData environmentData) { - return () -> Optional.ofNullable(environmentData.getFormRecord().getAttachments()).orElse(List.of()); - } - - @Override - public Class<EWorkflowElementNodeError> getErrorCodeClass() { - // Class of the enumeration with the possible types of errors that may occur. - return EWorkflowElementNodeError.class; - } - - @Override - public Class<EWorkflowElementNodeSoftError> getSoftErrorCodeClass() { - // Class of the enumeration with the possible types of soft errors that may occur. - return EWorkflowElementNodeSoftError.class; - } - - @Override - public Class<? extends INodePropertyPluginBean<WorkflowElementNodeProps>> getMainPluginBeanClass() { - // List of beans that should be available in the XHTML page. - // Often you do not need a bean, in this case you can just remove this method. - return WorkflowElementNodeBean.class; - } - - @Override - public IPluginInitializeData getPluginInitializeData() { - return initializeData; - } - -} diff --git a/src/main/java/de/ozgcloud/formcycle/attachment/AttachmentMapper.java b/src/main/java/de/ozgcloud/formcycle/attachment/AttachmentMapper.java index ae19865354b23f3757a91d29411320866b848f6c..1f9b3e7fc20ed98c2cb768835ad464ee475ea12d 100644 --- a/src/main/java/de/ozgcloud/formcycle/attachment/AttachmentMapper.java +++ b/src/main/java/de/ozgcloud/formcycle/attachment/AttachmentMapper.java @@ -30,27 +30,22 @@ import java.net.URLConnection; import org.apache.http.entity.ContentType; import org.springframework.util.MimeTypeUtils; -import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.TechnicalException; import de.xima.fc.entities.Attachment; -import de.xima.fc.exceptions.NodeThrewException; -import lombok.RequiredArgsConstructor; -@RequiredArgsConstructor public class AttachmentMapper { static final String CANNOT_READ_ATTACHED_FILE = "Cannot read attached file. It doesn't exist."; static final String FILENAME_IS_NULL = "File name cannot be null."; - private final NodeThrewExceptionFactory exceptionFactory; - - public FormcycleAttachment map(Attachment pluginAttachment) throws NodeThrewException { + public FormcycleAttachment map(Attachment pluginAttachment) { if (isNull(pluginAttachment.getFileEntity().getDaten())) { - throw exceptionFactory.createCannotGetAttachmentException(CANNOT_READ_ATTACHED_FILE); + throw new TechnicalException(CANNOT_READ_ATTACHED_FILE); } return buildAttachment(pluginAttachment); } - FormcycleAttachment buildAttachment(Attachment pluginAttachment) throws NodeThrewException { + FormcycleAttachment buildAttachment(Attachment pluginAttachment) { return FormcycleAttachment.builder() .uuid(pluginAttachment.getUUID()) .fileName(pluginAttachment.getDateiName()) @@ -58,9 +53,9 @@ public class AttachmentMapper { .content(new ByteArrayInputStream(pluginAttachment.getFileEntity().getDaten())).build(); } - private ContentType getContentType(String name) throws NodeThrewException { + ContentType getContentType(String name) { if(isNull(name)) { - throw exceptionFactory.createCannotGetAttachmentException(FILENAME_IS_NULL); + throw new TechnicalException(FILENAME_IS_NULL); } String contentType = requireNonNullElse(URLConnection.guessContentTypeFromName(name), MimeTypeUtils.APPLICATION_OCTET_STREAM_VALUE); return ContentType.create(contentType); diff --git a/src/main/java/de/ozgcloud/formcycle/client/HttpUtils.java b/src/main/java/de/ozgcloud/formcycle/client/HttpUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..9facc53751e94895278c8b405964aff777d7c8df --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/client/HttpUtils.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.client; + +import org.apache.http.HttpStatus; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class HttpUtils { + + public static boolean isError(int statusCode) { + return is400Error(statusCode) || is500Error(statusCode); + } + + public static boolean is400Error(int statusCode) { + return statusCode == HttpStatus.SC_BAD_REQUEST + || statusCode == HttpStatus.SC_UNAUTHORIZED + || statusCode == HttpStatus.SC_PAYMENT_REQUIRED + || statusCode == HttpStatus.SC_FORBIDDEN + || statusCode == HttpStatus.SC_NOT_FOUND + || statusCode == HttpStatus.SC_METHOD_NOT_ALLOWED + || statusCode == HttpStatus.SC_NOT_ACCEPTABLE + || statusCode == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED + || statusCode == HttpStatus.SC_REQUEST_TIMEOUT + || statusCode == HttpStatus.SC_CONFLICT + || statusCode == HttpStatus.SC_GONE + || statusCode == HttpStatus.SC_LENGTH_REQUIRED + || statusCode == HttpStatus.SC_PRECONDITION_FAILED + || statusCode == HttpStatus.SC_REQUEST_TOO_LONG + || statusCode == HttpStatus.SC_REQUEST_URI_TOO_LONG + || statusCode == HttpStatus.SC_UNSUPPORTED_MEDIA_TYPE + || statusCode == HttpStatus.SC_REQUESTED_RANGE_NOT_SATISFIABLE + || statusCode == HttpStatus.SC_EXPECTATION_FAILED + || statusCode == HttpStatus.SC_INSUFFICIENT_SPACE_ON_RESOURCE + || statusCode == HttpStatus.SC_METHOD_FAILURE + || statusCode == HttpStatus.SC_UNPROCESSABLE_ENTITY + || statusCode == HttpStatus.SC_LOCKED + || statusCode == HttpStatus.SC_FAILED_DEPENDENCY + || statusCode == HttpStatus.SC_TOO_MANY_REQUESTS; + } + + public static boolean is500Error(int statusCode) { + return statusCode == HttpStatus.SC_INTERNAL_SERVER_ERROR + || statusCode == HttpStatus.SC_NOT_IMPLEMENTED + || statusCode == HttpStatus.SC_BAD_GATEWAY + || statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE + || statusCode == HttpStatus.SC_GATEWAY_TIMEOUT + || statusCode == HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED + || statusCode == HttpStatus.SC_INSUFFICIENT_STORAGE; + } +} diff --git a/src/main/java/de/ozgcloud/formcycle/client/OzgCloudErrorDto.java b/src/main/java/de/ozgcloud/formcycle/client/OzgCloudErrorDto.java new file mode 100644 index 0000000000000000000000000000000000000000..8e5b545ef71d75168ffa85d8902711ce70bac37d --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/client/OzgCloudErrorDto.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.client; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@JsonIgnoreProperties(ignoreUnknown = true) +public class OzgCloudErrorDto { + + private String message; + private String exceptionId; + +} diff --git a/src/main/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandler.java b/src/main/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..bef4b00047cd42f55a25420c164bd3818f8ce0ba --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandler.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.client; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.ResponseHandler; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.eingang.formcycle.FormCycleConfirmationResponse; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class OzgCloudResponseHandler implements ResponseHandler<FormCycleConfirmationResponse> { + + static final String MESSAGE = "Unexpected Status received: %s"; + + private final ObjectMapper objectMapper; + + @Override + public FormCycleConfirmationResponse handleResponse(HttpResponse response) throws IOException { + checkResponseStatus(response); + return buildResponse(response); + } + + FormCycleConfirmationResponse buildResponse(HttpResponse response) throws IOException { + return FormCycleConfirmationResponse.parseFrom(response.getEntity().getContent()); + } + + void checkResponseStatus(HttpResponse response) { + var statusCode = response.getStatusLine().getStatusCode(); + if (HttpUtils.isError(statusCode)) { + var receivedError = getOzgCloudError(response.getEntity()); + throw new OzgCloudRequestException(receivedError.getMessage(), receivedError.getExceptionId()); + } + if (statusCode != HttpStatus.SC_OK) { + throw new OzgCloudRequestException(buildFullMessage(response)); + } + } + + OzgCloudErrorDto getOzgCloudError(HttpEntity entity) { + try { + return objectMapper.readValue(entity.getContent(), OzgCloudErrorDto.class); + } catch (IOException e) { + throw new OzgCloudRequestException("Cannot read error message.", e); + } + } + + String buildFullMessage(HttpResponse response) { + StringBuilder messageBuilder = new StringBuilder(String.format(MESSAGE, response.getStatusLine())); + try { + IOUtils.readLines(response.getEntity().getContent(), StandardCharsets.UTF_8).forEach(line -> messageBuilder.append("\n").append(line)); + } catch (IOException e) { + messageBuilder.append("\nError reading body"); + } + return messageBuilder.toString(); + } +} diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/ExceptionUtil.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/ExceptionUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..7135603c2485b5888826f3b1b774d88bc3fda74c --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/ExceptionUtil.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.errorhandling; + +import java.util.UUID; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ExceptionUtil { + + private static final String TEMPLATE_MESSAGE_WITH_EXCEPTION_ID = "%s (ExceptionId: %s)"; + + public static String formatMessageWithExceptionId(String message) { + return formatMessageWithExceptionId(message, createExceptionId()); + } + + public static String formatMessageWithExceptionId(String message, String exceptionId) { + return String.format(TEMPLATE_MESSAGE_WITH_EXCEPTION_ID, message, exceptionId); + } + + public static String createExceptionId() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactory.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactory.java index 97e6f5053665915e04020c5961c7af14f7791be9..a5eb26eccb8b77997b412b2155c16764b22211d0 100644 --- a/src/main/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactory.java +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactory.java @@ -1,8 +1,8 @@ package de.ozgcloud.formcycle.errorhandling; -import static de.ozgcloud.formcycle.errorhandling.EWorkflowElementNodeError.*; +import static de.ozgcloud.formcycle.errorhandling.OzgPluginError.*; -import com.alibaba.fastjson.JSONObject; +import java.util.Map; import de.xima.fc.exceptions.NodeThrewException; import de.xima.fc.interfaces.workflow.params.INodeThrewExceptionBuilder; @@ -11,25 +11,27 @@ import lombok.RequiredArgsConstructor; @RequiredArgsConstructor public class NodeThrewExceptionFactory { - public static final String ERROR_MESSAGE_KEY = "message"; + public static final String EXCEPTION_ID_KEY = "exceptionId"; private final INodeThrewExceptionBuilder exceptionBuilder; - public NodeThrewException createCannotSendException(String message, Exception cause) { - final var data = new JSONObject(); - data.put(ERROR_MESSAGE_KEY, message); - return exceptionBuilder.error(OZGCLOUD_CANNOT_SEND.name(), data).message(message).cause(cause).build(); + public NodeThrewException createInternalException(TechnicalException e) { + return exceptionBuilder.error(INTERNAL_ERROR.name(), createDataMap(e.getExceptionId())).message(e.getMessage()).cause(e) + .build(); } - public NodeThrewException createCannotGetAttachmentException(String message) { - final var data = new JSONObject(); - data.put(ERROR_MESSAGE_KEY, message); - return exceptionBuilder.error(PLUGIN_CANNOT_GET_ATTACHMENT.name(), data).message(message).build(); + public NodeThrewException createInternalException(String message, Exception e) { + return exceptionBuilder.error(INTERNAL_ERROR.name(), createDataMap(ExceptionUtil.createExceptionId())) + .message(ExceptionUtil.formatMessageWithExceptionId(message)).cause(e).build(); } - public NodeThrewException createConfigurationException(String message) { - final var data = new JSONObject(); - data.put(ERROR_MESSAGE_KEY, message); - return exceptionBuilder.error(PLUGIN_CONFIG_ERROR.name(), data).message(message).build(); + public NodeThrewException createOzgCloudRequestException(OzgCloudRequestException e) { + return exceptionBuilder.error(OZGCLOUD_REQUEST_FAILED.name(), createDataMap(e.getExceptionId())).message(e.getMessageWithConfig()).cause(e) + .build(); } + + Map<String, String> createDataMap(String exceptionId) { + return Map.of(EXCEPTION_ID_KEY, exceptionId); + } + } diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgCloudRequestException.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgCloudRequestException.java new file mode 100644 index 0000000000000000000000000000000000000000..21b60e93617325fe15e7ef5bc5b8213d6364e07a --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgCloudRequestException.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.errorhandling; + +import static java.util.Objects.*; + +import de.ozgcloud.formcycle.OzgCloudConfig; +import lombok.Setter; + +@Setter +public class OzgCloudRequestException extends TechnicalException { + + private OzgCloudConfig ozgCloudConfig; // NOSONAR + + public OzgCloudRequestException(String message) { + super(message); + } + + public OzgCloudRequestException(String message, Throwable cause) { + super(message, cause); + } + + public OzgCloudRequestException(String message, String exceptionId) { + super(message, exceptionId); + } + + public OzgCloudRequestException(String msg, OzgCloudConfig ozgCloudConfig, Throwable cause) { + super(msg, cause); + this.ozgCloudConfig = ozgCloudConfig; + } + + public String getMessageWithConfig() { + var messageBuilder = new StringBuilder(super.getMessage()); + if (isNull(ozgCloudConfig)) { + return messageBuilder.toString(); + } + messageBuilder.append("\nPlugin configuration:\n") + .append("\t").append(OzgCloudConfig.KEY_EINGANGSADAPTER_URL).append(": ").append(ozgCloudConfig.getEingangsAdapterUrl()); + if (nonNull(ozgCloudConfig.getProxyConfig())) { + messageBuilder.append("\n\t").append(ozgCloudConfig.getProxyConfig()); + } + return messageBuilder.toString(); + } +} diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeError.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginError.java similarity index 89% rename from src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeError.java rename to src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginError.java index 9c8eb36f290e517cc72377d91073a5130bc3bb93..4763c2ad9f1ff686720f13adc2768077ab1e9e6a 100644 --- a/src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeError.java +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginError.java @@ -22,6 +22,6 @@ */ package de.ozgcloud.formcycle.errorhandling; -public enum EWorkflowElementNodeError { - OZGCLOUD_CANNOT_SEND, PLUGIN_CANNOT_GET_ATTACHMENT, PLUGIN_CONFIG_ERROR +public enum OzgPluginError { + INTERNAL_ERROR, OZGCLOUD_REQUEST_FAILED } diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeSoftError.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginSoftError.java similarity index 84% rename from src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeSoftError.java rename to src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginSoftError.java index d83bbe21fe781c045228fe74a5265c7000dea089..6fe907a27509f4a8f7d7b18939edb47cb5dd3d09 100644 --- a/src/main/java/de/ozgcloud/formcycle/errorhandling/EWorkflowElementNodeSoftError.java +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/OzgPluginSoftError.java @@ -29,10 +29,9 @@ package de.ozgcloud.formcycle.errorhandling; * practice to define meaningful errors. They are shown in the workflow designer. * @author unspecified */ -public enum EWorkflowElementNodeSoftError { - /** No action was performed because an URL with the wrong host name was entered */ - WRONG_HOST_NAME, +public enum OzgPluginSoftError { + + /** Indicates that representation of a formular was not attached to form data */ + MISSING_REPRESENTATION - /** No action was performed because the user does not exist. */ - USER_DOES_NOT_EXIST; } diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/UserErrorDispatcher.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/TechnicalException.java similarity index 56% rename from src/main/java/de/ozgcloud/formcycle/errorhandling/UserErrorDispatcher.java rename to src/main/java/de/ozgcloud/formcycle/errorhandling/TechnicalException.java index f71e3c7be6031d638eead686c4136f54e249cb76..033d4f1620d33464546e96dcd591a5866d96f3bc 100644 --- a/src/main/java/de/ozgcloud/formcycle/errorhandling/UserErrorDispatcher.java +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/TechnicalException.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur * Zentrales IT-Management * @@ -23,23 +23,32 @@ package de.ozgcloud.formcycle.errorhandling; -import java.util.Optional; +import lombok.Getter; -import javax.faces.context.FacesContext; +@Getter +public class TechnicalException extends RuntimeException { -import org.omnifaces.util.Messages; -import de.ozgcloud.formcycle.ProxyConfig; + private final String exceptionId; -public class UserErrorDispatcher { + public TechnicalException(String message) { + super(message); + exceptionId = ExceptionUtil.createExceptionId(); + } + + public TechnicalException(String message, Throwable cause) { + super(message, cause); + exceptionId = ExceptionUtil.createExceptionId(); + } - static final String PORT_FORMAT_ERROR_MESSAGE = "\"{0}\" hat ein falsches Format"; - static final String PORT_FORMAT_ERROR_DESCRIPTION = "Port muss eine Zahl zwischen 1 und 65535 sein"; + public TechnicalException(String message, String exceptionId) { + super(message); + this.exceptionId = exceptionId; + } - public void sendWrongProxyPortFormatMessage() { - Optional.ofNullable(FacesContext.getCurrentInstance()) - .ifPresent(ctx -> Messages.create(PORT_FORMAT_ERROR_MESSAGE, ProxyConfig.KEY_PROXY_PORT).detail(PORT_FORMAT_ERROR_DESCRIPTION).error() - .add()); + @Override + public String getMessage() { + return ExceptionUtil.formatMessageWithExceptionId(super.getMessage(), exceptionId); } } diff --git a/src/main/java/de/ozgcloud/formcycle/errorhandling/Warning.java b/src/main/java/de/ozgcloud/formcycle/errorhandling/Warning.java new file mode 100644 index 0000000000000000000000000000000000000000..42be81a725587f9eb8f0ee4e5e0960418af74dc8 --- /dev/null +++ b/src/main/java/de/ozgcloud/formcycle/errorhandling/Warning.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.errorhandling; + +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class Warning { + + private OzgPluginSoftError errorCode; + private String message; + private Object value; + private Exception cause; + + public String getErrorCode() { + return errorCode.name(); + } +} diff --git a/src/main/resources/WEB-INF/properties/WorkflowElementNodeHelp_de.html b/src/main/resources/WEB-INF/properties/WorkflowElementNodeHelp_de.html deleted file mode 100644 index 75333f4d8bd53a97c5118da2ab14472c8b2e2fd5..0000000000000000000000000000000000000000 --- a/src/main/resources/WEB-INF/properties/WorkflowElementNodeHelp_de.html +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> -<meta charset="utf-8"> -<title>Hilfeseite: WorkflowElementNode</title> -<meta name="author" content="unspecified"> -<meta name="description" content="Hilfeseite WorkflowElementNode"> -<meta name="viewport" content="width=device-width, initial-scale=1"> -</head> - -<body> -<body> -<body> - <main> - <h1>Plugin WorkflowElementNode</h1> - <h2>Einführung</h2> - <section>TODO</section> - <h2>Konfiguration</h2> - <section>TODO</section> - <h2>Verwendung</h2> - <section>TODO</section> - </main> - - <footer> - <p>by unspecified</p> - </footer> -</body> - -</html> diff --git a/src/main/resources/WEB-INF/properties/WorkflowElementTriggerHelp_de.html b/src/main/resources/WEB-INF/properties/WorkflowElementTriggerHelp_de.html deleted file mode 100644 index 7685599bd5a0ee907e3b6554875597ea1e8c3ef8..0000000000000000000000000000000000000000 --- a/src/main/resources/WEB-INF/properties/WorkflowElementTriggerHelp_de.html +++ /dev/null @@ -1,30 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> -<meta charset="utf-8"> -<title>Hilfeseite: WorkflowElementTrigger</title> -<meta name="author" content="unspecified"> -<meta name="description" content="Hilfeseite für WorkflowElementTrigger"> -<meta name="viewport" content="width=device-width, initial-scale=1"> -</head> - -<body> -<body> -<body> - <main> - <h1>Plugin WorkflowElementTrigger</h1> - <h2>Einführung</h2> - <section>TODO</section> - <h2>Konfiguration</h2> - <section>TODO</section> - <h2>Verwendung</h2> - <section>TODO</section> - </main> - - <footer> - <p>by unspecified</p> - </footer> -</body> - -</html> diff --git a/src/main/resources/WEB-INF/properties/i18n_de.properties b/src/main/resources/WEB-INF/properties/i18n_de.properties index 1131798a70782701fe542fec1926bcb95bce330a..010bc7487784e9d87f009b2b17b7fcc8a7933ec0 100644 --- a/src/main/resources/WEB-INF/properties/i18n_de.properties +++ b/src/main/resources/WEB-INF/properties/i18n_de.properties @@ -1,41 +1,20 @@ +de.ozgcloud.formcycle.OzgCloudPlugin.name = OZG-Cloud Plugin +de.ozgcloud.formcycle.OzgCloudPlugin.desc = Plugin zum Senden von Formulardaten an die OZG-Cloud +de.ozgcloud.formcycle.OzgCloudPlugin.sublabel = Sendet Formulardaten an die OZG-Cloud -de.ozgcloud.formcycle.WorkflowElementNodePlugin.desc = Das ist ein Plugin, welches zeigt, wie man eine Node zum Workflow hinzuf\u00FCgt. -de.ozgcloud.formcycle.WorkflowElementNodePlugin.errVal.OZGCLOUD_CANNOT_SEND.message = Weitere Information zum Fehler -de.ozgcloud.formcycle.WorkflowElementNodePlugin.errcode.OZGCLOUD_CANNOT_SEND = Die Formulardaten konnten nicht gesendet werden. -de.ozgcloud.formcycle.WorkflowElementNodePlugin.name = OZG-Cloud Plugin # Nachrichten fuer die UI der Aktion -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.count = Anzahl der zur\u00FCckgelieferten Datens\u00E4tze (Sachprozesse) -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.items[i].active = Ob der Sachprozesse aktiv ist -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.items[i].name = Name des Sachprozesses -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.items[i].state.APPROVED = Wenn der Sachprozess akzeptiert wurde -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.items[i].state.PENDING = Wenn der Sachprozesse in Bearbeitung ist -de.ozgcloud.formcycle.WorkflowElementNodePlugin.retval.success.items[i].state.REJECTED = Wenn der Sachprozesse abgelehnt wurde -de.ozgcloud.formcycle.WorkflowElementNodePlugin.searchtext = wundervoll plugin aktion -de.ozgcloud.formcycle.WorkflowElementNodePlugin.softErrVal.WRONG_HOST_NAME.expectedHostName = Der erwartete Hostname, der benutzt werden sollte. -de.ozgcloud.formcycle.WorkflowElementNodePlugin.softerrcode.USER_DOES_NOT_EXIST = Wenn es keinen Nutzer gibt. -de.ozgcloud.formcycle.WorkflowElementNodePlugin.softerrcode.WRONG_HOST_NAME = Wenn der Hostname nicht unterst\u00FCtzt wird. -de.ozgcloud.formcycle.WorkflowElementNodePlugin.sublabel = Sendet Formulardaten an die OZG-Cloud -de.ozgcloud.formcycle.WorkflowElementNodePlugin.title = Sendet Formulardaten an die OZG-Cloud -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.data.requestParameters['key'][i] = i-ter Wert des HTTP-Parameters mit dem Namen 'key'. Ein HTTP-Parameter kann mehrfach angegeben werden und damit mehrere Werte haben. -# Nachrichten fuer die UI des Triggers -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.data.type = Typ der vom HTTP-Request \u00FCbermittelt wurde -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.desc = Das ist ein Plugin, welches zeigt, wie man ein Ereignis zum Workflow hinzuf\u00FCgt. -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.name = WorkflowElementTrigger -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.searchtext = wundervoll plugin ereignis -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.sublabel = Ein tolles Ereignis zum Ausprobieren -de.ozgcloud.formcycle.plugin.WorkflowElementTriggerPlugin.title = Tats\u00E4chlich ist dieses Ereignis so toll, dass Sie dieses nie wieder entfernen wollen! -de.ozgcloud.formcycle.plugin.HttpServletActionPlugin.name = HTTP-Request f\u00FCr Ereignis +de.ozgcloud.formcycle.OzgCloudPlugin.retval.success.vorgangnummer = Dem Vorgang zugewiesene Nummer in OZG-Cloud -WorkflowElementNode.desc = Dieses Plugin sendet Formulardaten an die OZG-Cloud -WorkflowElementNode.name = OZG-Cloud Plugin +# Errors +de.ozgcloud.formcycle.OzgCloudPlugin.errcode.INTERNAL_ERROR = Fehlercode, wenn ein interner Fehler aufgetreten ist. +de.ozgcloud.formcycle.OzgCloudPlugin.errVal.INTERNAL_ERROR.exceptionId = ID, um Fehler im System nachverfolgen zu k�nnen. +de.ozgcloud.formcycle.OzgCloudPlugin.errcode.OZGCLOUD_REQUEST_FAILED = Fehlercode, wenn die Formulardaten an OZG-Cloud nicht gesendet werden konnten. +de.ozgcloud.formcycle.OzgCloudPlugin.errVal.OZGCLOUD_REQUEST_FAILED.exceptionId = ID, um Fehler im System nachverfolgen zu k�nnen. -WorkflowElementNodeProps.section_settings = Einstellungen -WorkflowElementNodeProps.eingangsAdapterUrl = URL f�r die OZG-Cloud -WorkflowElementNodeProps.useCustomEingangsAdapterUrl = Nutze formularspezifische Eingangsadapter-URL -WorkflowElementNodeProps.organisationsEinheitId = ID der zust�ndigen Organisationseinheit +# Soft Errors +de.ozgcloud.formcycle.OzgCloudPlugin.softerrcode.MISSING_REPRESENTATION = Weicher Fehlercode, wenn Formular Representation nicht angeh�ngt werden konnte. -WorkflowElementTrigger.desc = Dieses Ereignis tritt ein, wenn ein HTTP-Request an das Servlet-Plugin gestellt wird. -WorkflowElementTrigger.name = Mein Plugin f\u00FCr einen Workflow-Trigger - -WorkflowElementTriggerProps.section_settings = Einstellungen -WorkflowElementTriggerProps.type = Request-Typ +OzgPluginWorkflowNodeProperties.section_settings = Einstellungen +OzgPluginWorkflowNodeProperties.eingangsAdapterUrl = URL f�r die OZG-Cloud +OzgPluginWorkflowNodeProperties.useCustomEingangsAdapterUrl = Nutze formularspezifische Eingangsadapter-URL +OzgPluginWorkflowNodeProperties.organisationsEinheitId = ID der zust�ndigen Organisationseinheit \ No newline at end of file diff --git a/src/main/resources/WEB-INF/ui/WorkflowElementNode.xhtml b/src/main/resources/WEB-INF/ui/OzgCloudPlugin.xhtml similarity index 83% rename from src/main/resources/WEB-INF/ui/WorkflowElementNode.xhtml rename to src/main/resources/WEB-INF/ui/OzgCloudPlugin.xhtml index 99fa3fa6574bf751f528e90ebf2896c76db72033..eb5e4711cc1c3ece83d4527582f38e7986556556 100644 --- a/src/main/resources/WEB-INF/ui/WorkflowElementNode.xhtml +++ b/src/main/resources/WEB-INF/ui/OzgCloudPlugin.xhtml @@ -14,14 +14,14 @@ <xi:namingContainer id="WorkflowElementNode"> <xi:newWorkflowActionBase id="base" value="#{model}" - legend="#{msg['WorkflowElementNode.name']}" /> + legend="#{msg['de.ozgcloud.formcycle.OzgCloudPlugin.name']}"/> - <p:fieldset legend="#{msg['WorkflowElementNodeProps.section_settings']}" + <p:fieldset legend="#{msg['OzgPluginWorkflowNodeProperties.section_settings']}" styleClass="fc-fieldset"> <xi:inputText id="organisationsEinheitId" value="#{model.organisationsEinheitId}" forceIndicateRequired="true" - label="#{msg['WorkflowElementNodeProps.organisationsEinheitId']}"> + label="#{msg['OzgPluginWorkflowNodeProperties.organisationsEinheitId']}"> <p:ajax event="change" partialSubmit="true" listener="#{elementPropertiesBean.storeCurrent}" process="@this" update=":flowchartForm:flowchart" global="false" /> @@ -30,7 +30,7 @@ <xi:selectBooleanCheckbox id="chkRememberMe" value="#{model.useCustomEingangsAdapterUrl}" - label="#{msg['WorkflowElementNodeProps.useCustomEingangsAdapterUrl']}"> + label="#{msg['OzgPluginWorkflowNodeProperties.useCustomEingangsAdapterUrl']}"> <p:ajax type="change" process="@this" update="wfPropertiesForm:WorkflowElementNode:panelId"/> <p:ajax event="change" partialSubmit="true" listener="#{elementPropertiesBean.storeCurrent}" process="@this" @@ -41,7 +41,7 @@ <xi:inputText id="eingangsAdapterCustomUrl-id" value="#{model.eingangsAdapterUrl}" forceIndicateRequired="true" - label="#{msg['WorkflowElementNodeProps.eingangsAdapterUrl']}"> + label="#{msg['OzgPluginWorkflowNodeProperties.eingangsAdapterUrl']}"> <p:ajax event="change" partialSubmit="true" listener="#{elementPropertiesBean.storeCurrent}" process="@this" update=":flowchartForm:flowchart" global="false"/> diff --git a/src/main/resources/WEB-INF/ui/WorkflowElementTrigger.xhtml b/src/main/resources/WEB-INF/ui/WorkflowElementTrigger.xhtml deleted file mode 100644 index 40d2b3531dfb93a411bd276c13e0d55250a636ea..0000000000000000000000000000000000000000 --- a/src/main/resources/WEB-INF/ui/WorkflowElementTrigger.xhtml +++ /dev/null @@ -1,20 +0,0 @@ -<ui:composition xmlns="http://www.w3.org/1999/xhtml" - xmlns:h="http://xmlns.jcp.org/jsf/html" - xmlns:f="http://xmlns.jcp.org/jsf/core" - xmlns:ui="http://xmlns.jcp.org/jsf/facelets" - xmlns:o="http://omnifaces.org/ui" xmlns:p="http://primefaces.org/ui" - xmlns:xi="http://www.xima.de/taglib/xfc"> - - <xi:validateBean value="#{model}" showMessageFor="@violating" - method="validateActual" /> - - <p:importConstants type="de.xima.fc.mdl.enums.ETextbausteinKategorie" - var="ETextbausteinKategorie" /> - - <xi:namingContainer id="WorkflowElementTrigger"> - - <xi:newWorkflowActionBase id="base" value="#{model}" - legend="#{msg['WorkflowElementTrigger.name']}" /> - - </xi:namingContainer> -</ui:composition> diff --git a/src/test/java/de/ozgcloud/formcycle/ExecutionResultTest.java b/src/test/java/de/ozgcloud/formcycle/ExecutionResultTest.java deleted file mode 100644 index c64aa66e5b4824976b2920fa95c3cc3f408d53d1..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/formcycle/ExecutionResultTest.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.ozgcloud.formcycle; - -import static de.ozgcloud.formcycle.ExecutionResult.*; - -import org.apache.commons.lang3.StringUtils; -import org.assertj.core.api.Assertions; -import org.assertj.core.data.MapEntry; -import org.junit.jupiter.api.Test; - -class ExecutionResultTest { - - @Test - void shouldCreateResultMap() { - var vorgangnummer = "1"; - var executionResult = ExecutionResult.builder().vorgangnummer(vorgangnummer).build(); - - var resultPropertyMap = executionResult.get(); - - Assertions.assertThat(resultPropertyMap).containsOnly(MapEntry.entry(VORGANGNUMMER_PROPERTY_KEY, vorgangnummer)); - } - - @Test - void shouldAddEmptyValues() { - var executionResult = ExecutionResult.builder().build(); - - var resultPropertyMap = executionResult.get(); - - Assertions.assertThat(resultPropertyMap).containsOnly(MapEntry.entry(VORGANGNUMMER_PROPERTY_KEY, StringUtils.EMPTY)); - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/ExecutionResultTestFactory.java b/src/test/java/de/ozgcloud/formcycle/ExecutionResultTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4c73c7208f97a338be1fd022f18477dc98eecfba --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/ExecutionResultTestFactory.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle; + +import java.util.List; + +import de.ozgcloud.formcycle.ExecutionResult.ExecutionResultBuilder; +import de.ozgcloud.formcycle.errorhandling.Warning; + +public class ExecutionResultTestFactory { + + public static final String VORGANGNUMMER = "123456789"; + public static final Warning WARNING = WarningTestFactory.create(); + + public static ExecutionResult create() { + return createBuilder().build(); + } + + public static ExecutionResultBuilder createBuilder() { + return ExecutionResult.builder() + .vorgangnummer(VORGANGNUMMER) + .warnings(List.of(WARNING)); + } +} diff --git a/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientITCase.java b/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientITCase.java index e2257a3df39c111d59eaec0e835f1ac0b09c0bf7..cb05ea0fb1d1bb4407054c94dfa238447deda5b0 100644 --- a/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientITCase.java +++ b/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientITCase.java @@ -24,7 +24,6 @@ package de.ozgcloud.formcycle; import static org.assertj.core.api.Assertions.*; -import java.io.IOException; import java.time.ZonedDateTime; import java.util.Collections; @@ -33,25 +32,33 @@ import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.fasterxml.jackson.databind.ObjectMapper; + import de.ozgcloud.eingang.formcycle.FormCycleAttachmentGroup; import de.ozgcloud.eingang.formcycle.FormCycleConfirmationResponse; import de.ozgcloud.eingang.formcycle.FormCycleFormData; import de.ozgcloud.eingang.formcycle.FormCycleFormHeader; import de.ozgcloud.eingang.formcycle.FormCycleServiceKonto; +import de.ozgcloud.formcycle.client.OzgCloudResponseHandler; import de.ozgcloud.vorgang.vorgang.GrpcFormData; +import lombok.SneakyThrows; class OzgCloudFormDataHttpClientITCase { private static final Logger LOG = LoggerFactory.getLogger(OzgCloudFormDataHttpClientITCase.class); + private final OzgCloudResponseHandler responseHandler = new OzgCloudResponseHandler(new ObjectMapper()); + private final PortParser portParser = new PortParser(); + @Test @Disabled("Test sollte nur manuell ausgeführt werden, da die Daten in ein Dev System eingeliefert werden") - void testReal() throws IOException { + @SneakyThrows + void testReal() { String url = "https://fuerth-formcycle.dev.by.ozg-cloud.de/formData"; - OzgCloudFormDataHttpClient client = new OzgCloudFormDataHttpClient(OzgCloudConfig.builder() - .eingangsAdapterUrl(url).build(), new SystemPropertiesProvider()); + OzgCloudFormDataHttpClient client = new OzgCloudFormDataHttpClient(OzgCloudConfig.builder().eingangsAdapterUrl(url).build(), + new SystemPropertiesProvider(), portParser, responseHandler); FormCycleConfirmationResponse response = client.send(createTestFormData(), Collections.emptyList(), Collections.emptyList()); diff --git a/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientTest.java b/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientTest.java index 55aa9a9d98d755b9091fea21288e29752b0faa0b..c6c77805cb86e26c9250a20c9f9c2dfded9a278f 100644 --- a/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientTest.java +++ b/src/test/java/de/ozgcloud/formcycle/OzgCloudFormDataHttpClientTest.java @@ -24,19 +24,17 @@ package de.ozgcloud.formcycle; import static de.ozgcloud.formcycle.OzgCloudFormDataHttpClient.*; 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.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.lang.reflect.Field; import java.util.List; import java.util.Optional; -import org.apache.http.HttpEntity; -import org.apache.http.HttpStatus; -import org.apache.http.StatusLine; -import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.HttpHost; +import org.apache.http.client.methods.HttpPost; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.HttpRoutePlanner; import org.apache.http.impl.client.BasicCredentialsProvider; @@ -51,18 +49,24 @@ 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.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.springframework.util.ReflectionUtils; -import de.ozgcloud.eingang.formcycle.FormCycleConfirmationResponse; import de.ozgcloud.eingang.formcycle.FormCycleFormData; -import de.ozgcloud.formcycle.OzgCloudFormDataHttpClient.UnexpectedStatusCodeException; import de.ozgcloud.formcycle.attachment.FormcycleAttachment; import de.ozgcloud.formcycle.attachment.FormcycleAttachmentTestFactory; +import de.ozgcloud.formcycle.client.OzgCloudResponseHandler; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; import lombok.SneakyThrows; class OzgCloudFormDataHttpClientTest { + @Spy + @InjectMocks + private OzgCloudFormDataHttpClient client; + @Mock private SystemPropertiesProvider systemPropertiesProvider; @Mock @@ -70,37 +74,31 @@ class OzgCloudFormDataHttpClientTest { @Mock private CloseableHttpClient httpClient; @Mock - private CloseableHttpResponse closeableHttpResponse; + private OzgCloudConfig ozgCloudConfig; + @Mock + private PortParser portParser; + @Mock + private OzgCloudResponseHandler responseHandler; private List<FormcycleAttachment> attachments; private List<FormcycleAttachment> representations; - private OzgCloudFormDataHttpClient client; - - @BeforeEach - void setup() { - client = createOzgClient(OzgCloudConfigTestFactory.proxyConfig); - } - - private OzgCloudFormDataHttpClient createOzgClient(ProxyConfig proxyConfig) { - return spy(new OzgCloudFormDataHttpClient(OzgCloudConfigTestFactory.createBuilder() - .proxyConfig(proxyConfig).build(), systemPropertiesProvider)); - } - @Nested class TestSendData { + @Mock + private HttpPost postRequest; + @Mock + private HttpContext httpContext; + @SneakyThrows @BeforeEach void setup() { - when(formData.toByteArray()).thenReturn(new byte[] {}); attachments = List.of(FormcycleAttachmentTestFactory.create()); representations = List.of(FormcycleAttachmentTestFactory.create()); - - when(httpClient.execute(any(), any(HttpContext.class))).thenReturn(closeableHttpResponse); - doReturn(httpClient).when(client).createCloseableClient(); - doReturn(FormCycleConfirmationResponseTestFactory.create()).when(client).parseResponse(any()); + doReturn(postRequest).when(client).buildPostRequest(any(), any(), any()); + doReturn(httpContext).when(client).getContext(); } @SneakyThrows @@ -114,57 +112,39 @@ class OzgCloudFormDataHttpClientTest { @SneakyThrows @Test void shouldSendData() { - client.send(formData, attachments, representations); - - verify(client).createCloseableClient(); - verify(httpClient).execute(any(), any(HttpContext.class)); - } + when(httpClient.execute(any(), any(OzgCloudResponseHandler.class), any())).thenReturn(FormCycleConfirmationResponseTestFactory.create()); - @SneakyThrows - @Test - void shouldCallParseRequest() { - client.send(formData, attachments, representations); + var response = client.send(formData, attachments, representations); - verify(client).parseResponse(any()); + assertThat(response).isEqualTo(FormCycleConfirmationResponseTestFactory.create()); } - } - @Nested - class TestParseREsponse { - @Mock - private CloseableHttpResponse httpResponse; - @Mock - private HttpEntity entityMock; - @Mock - private StatusLine statusLineMock; + @Nested + class TestThrowException { - private FormCycleConfirmationResponse response = FormCycleConfirmationResponseTestFactory.create(); + @Mock + private IOException ioException; - @BeforeEach - @SneakyThrows - void init() { - when(httpResponse.getEntity()).thenReturn(entityMock); - when(httpResponse.getStatusLine()).thenReturn(statusLineMock); - when(statusLineMock.getStatusCode()).thenReturn(HttpStatus.SC_OK); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - response.writeTo(out); - when(entityMock.getContent()).thenReturn(new ByteArrayInputStream(out.toByteArray())); - } + private OzgCloudRequestException exception = spy(new OzgCloudRequestException("message")); - @Test - @SneakyThrows - void shouldParseResponse() { - var result = client.parseResponse(httpResponse); + @SneakyThrows + @Test + void shouldAddOzgCloudConfig() { + when(httpClient.execute(any(), any(OzgCloudResponseHandler.class), any(HttpContext.class))).thenThrow(exception); - assertThat(result).isEqualTo(response); - } + assertThrows(OzgCloudRequestException.class, () -> client.send(formData, attachments, representations)); + verify(exception).setOzgCloudConfig(ozgCloudConfig); + } - @Test - void shouldThrowException() { - when(statusLineMock.getStatusCode()).thenReturn(HttpStatus.SC_SERVICE_UNAVAILABLE); + @SneakyThrows + @Test + void shouldWrapIOException() { + when(httpClient.execute(any(), any(OzgCloudResponseHandler.class), any(HttpContext.class))).thenThrow(ioException); - assertThatThrownBy(()->client.parseResponse(httpResponse)).isInstanceOf(UnexpectedStatusCodeException.class); + var thrownException = assertThrows(OzgCloudRequestException.class, () -> client.send(formData, attachments, representations)); + assertThat(thrownException.getCause()).isEqualTo(ioException); + assertThat(thrownException).extracting("ozgCloudConfig").isEqualTo(ozgCloudConfig); + } } } @@ -227,9 +207,14 @@ class OzgCloudFormDataHttpClientTest { @Mock private HttpClientBuilder httpClientBuilder; + @Mock + private ProxyConfig proxyConfig; @Test void shouldCallConfigureProxy() { + doReturn(proxyConfig).when(client).getProxyConfig(); + doNothing().when(client).configureProxy(any()); + client.addProxy(httpClientBuilder); verify(client).configureProxy(httpClientBuilder); @@ -241,11 +226,21 @@ class OzgCloudFormDataHttpClientTest { @Mock private HttpClientBuilder httpClientBuilder; + @Mock + private HttpHost proxyHost; + @Mock + private BasicCredentialsProvider credentialsProvider; + @Mock + private HttpRoutePlanner routePlanner; + + @BeforeEach + void setup() { + doReturn(routePlanner).when(client).createHttpRouterPlanner(); + doReturn(credentialsProvider).when(client).getCredentialsProvider(); + } @Test void shouldCallSetRouterPlanner() { - var routePlanner = mock(HttpRoutePlanner.class); - doReturn(routePlanner).when(client).createHttpRouterPlanner(); client.configureProxy(httpClientBuilder); @@ -254,8 +249,6 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldCallSetCredentialProvider() { - var credentialsProvider = mock(BasicCredentialsProvider.class); - doReturn(credentialsProvider).when(client).getCredentialsProvider(); client.configureProxy(httpClientBuilder); @@ -268,6 +261,8 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldReturnFromOzgCloudConfig() { + when(client.getProxyConfig()).thenReturn(OzgCloudConfigTestFactory.proxyConfig); + var proxyConfig = client.getProxyConfig(); assertThat(proxyConfig).isEqualTo(OzgCloudConfigTestFactory.proxyConfig); @@ -275,8 +270,6 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldCallLoadSystemProxyConfig() { - client = createOzgClient(ProxyConfigTestFactory.createEmpty()); - client.getProxyConfig(); verify(client).loadSystemProxyConfig(); @@ -284,7 +277,7 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldReturnNullWhenNoProxy() { - client = createOzgClient(ProxyConfigTestFactory.createEmpty()); + // client = createOzgClient(ProxyConfigTestFactory.createEmpty()); doReturn(Optional.empty()).when(client).loadSystemProxyConfig(); var proxyConfig = client.getProxyConfig(); @@ -356,7 +349,7 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldSetProxyPort() { - when(systemPropertiesProvider.getHttpsProxyPort()).thenReturn(PropertiesTestFactory.PROXY_PORT); + when(portParser.parse(any())).thenReturn(ProxyConfigTestFactory.PROXY_PORT); var proxyConfig = client.loadHttpsProxyConfig(); @@ -411,7 +404,7 @@ class OzgCloudFormDataHttpClientTest { @Test void shouldSetProxyPort() { - when(systemPropertiesProvider.getHttpProxyPort()).thenReturn(PropertiesTestFactory.PROXY_PORT); + when(portParser.parse(any())).thenReturn(ProxyConfigTestFactory.PROXY_PORT); var proxyConfig = client.loadHttpProxyConfig(); @@ -464,9 +457,14 @@ class OzgCloudFormDataHttpClientTest { @Nested class TestCreateContext { + @Mock + private BasicCredentialsProvider credentialsProvider; + @Mock + private ProxyConfig proxyConfig; + @Test void shouldSetCredentialsProvider() { - var credentialsProvider = mock(BasicCredentialsProvider.class); + doReturn(proxyConfig).when(client).getProxyConfig(); doReturn(credentialsProvider).when(client).getCredentialsProvider(); var result = client.createContext(); diff --git a/src/test/java/de/ozgcloud/formcycle/OzgCloudPluginTest.java b/src/test/java/de/ozgcloud/formcycle/OzgCloudPluginTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c3c732df72ba55fec01ef18424249e53444f33c3 --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/OzgCloudPluginTest.java @@ -0,0 +1,274 @@ +package de.ozgcloud.formcycle; + +import static de.ozgcloud.formcycle.ExecutionResultTestFactory.*; +import static de.ozgcloud.formcycle.OzgCloudPlugin.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; +import de.ozgcloud.formcycle.errorhandling.TechnicalException; +import de.xima.fc.exceptions.NodeThrewException; +import de.xima.fc.interfaces.workflow.execution.INormalCompletionResult; +import de.xima.fc.interfaces.workflow.params.INodeExecutionParams; +import de.xima.fc.interfaces.workflow.params.INormalCompletionResultBuilder; +import de.xima.fc.interfaces.workflow.value.IRecordValueDescriptorBuilder; +import de.xima.fc.interfaces.workflow.value.IStringValueDescriptor; +import de.xima.fc.interfaces.workflow.value.IValueDescriptor; +import de.xima.fc.interfaces.workflow.value.IValueDescriptorFactory; +import lombok.SneakyThrows; + +class OzgCloudPluginTest { + + @Spy + @InjectMocks + private OzgCloudPlugin plugin; + + @Mock + private OzgPluginPropertiesSupplier propertiesMapper; + @Mock + private INodeExecutionParams<OzgPluginWorkflowNodeProperties> invocationParameters; + + @Nested + @DisplayName("Success value descriptor") + class TestSuccessValueDescriptor { + + @Mock + private IValueDescriptorFactory descriptorFactory; + @Mock + private IRecordValueDescriptorBuilder descriptorBuilder; + @Mock + private IStringValueDescriptor stringValueDescriptor; + + @BeforeEach + void setup() { + when(descriptorFactory.recordBuilder()).thenReturn(descriptorBuilder); + when(descriptorBuilder.requiredProperty(any(), any(IValueDescriptor.class))).thenReturn(descriptorBuilder); + } + + @Test + @DisplayName("should has vorgangnummer property") + void shouldAddVorgangnummerProperty() { + when(descriptorFactory.string()).thenReturn(stringValueDescriptor); + + plugin.getSuccessValueDescriptor(descriptorFactory); + + verify(descriptorBuilder).requiredProperty(VORGANGNUMMER_PROPERTY_KEY, stringValueDescriptor); + } + } + + @Nested + class TestCreateOzgClient { + + @Mock + private OzgPluginWorkflowNodeProperties formProperties; + @Mock + private NodeThrewExceptionFactory exceptionFactory; + + @Test + @SneakyThrows + void shouldCallBuildOzgCloudConfig() { + when(invocationParameters.getData()).thenReturn(formProperties); + doReturn("").when(plugin).getEingangsAdapterUrl(any()); + + plugin.createOzgCloudClient(invocationParameters); + + verify(plugin).buildOzgCloudConfig(formProperties); + } + + @Nested + class TestEingangsAdapterUrl { + + private static final String CUSTOM_URL = "http://custom.url"; + + @Test + void shouldReturnCustomUrl() { + when(formProperties.getEingangsAdapterUrl()).thenReturn(CUSTOM_URL); + when(formProperties.isUseCustomEingangsAdapterUrl()).thenReturn(true); + + var eingangsAdapterUrl = plugin.getEingangsAdapterUrl(formProperties); + + assertThat(eingangsAdapterUrl).isEqualTo(CUSTOM_URL); + } + + @Test + void shouldReturnDefaultUrl() { + when(formProperties.isUseCustomEingangsAdapterUrl()).thenReturn(false); + when(propertiesMapper.getEingangsAdapterUrl()).thenReturn(PropertiesTestFactory.EINGANGSADAPTER_URL); + + var eingangsAdapterUrl = plugin.getEingangsAdapterUrl(formProperties); + + assertThat(eingangsAdapterUrl).isEqualTo(PropertiesTestFactory.EINGANGSADAPTER_URL); + } + + @Test + void shouldThrowExceptionIfNoUrl() { + when(formProperties.isUseCustomEingangsAdapterUrl()).thenReturn(false); + + assertThrows(TechnicalException.class, () -> plugin.getEingangsAdapterUrl(formProperties)); + } + } + + @Nested + class TestBuildOzgClientConfig { + + @Mock + private Properties properties; + + @Test + void shouldSetEingangsAdapterUrl() { + doReturn(PropertiesTestFactory.EINGANGSADAPTER_URL).when(plugin).getEingangsAdapterUrl(any()); + + var ozgClientConfig = buildOzgCloudConfig(); + + assertThat(ozgClientConfig.getEingangsAdapterUrl()).isEqualTo(PropertiesTestFactory.EINGANGSADAPTER_URL); + } + + @SneakyThrows + @Test + void shouldSetProxyConfig() { + var expectedProxyConfig = ProxyConfigTestFactory.create(); + when(propertiesMapper.getEingangsAdapterUrl()).thenReturn(PropertiesTestFactory.EINGANGSADAPTER_URL); + when(propertiesMapper.getProxyProperties()).thenReturn(Optional.of(expectedProxyConfig)); + + var ozgClientConfig = buildOzgCloudConfig(); + + assertThat(ozgClientConfig.getProxyConfig()).isEqualTo(expectedProxyConfig); + } + + @SneakyThrows + private OzgCloudConfig buildOzgCloudConfig() { + return plugin.buildOzgCloudConfig(formProperties); + } + } + } + + @Nested + class TestBuildSuccessResult { + + @Mock + private INormalCompletionResultBuilder resultBuilder; + @Captor + private ArgumentCaptor<Map<String, String>> resultCaptor; + + @BeforeEach + public void setup() { + when(invocationParameters.normalResult()).thenReturn(resultBuilder); + when(resultBuilder.success(resultCaptor.capture())).thenReturn(resultBuilder); + when(resultBuilder.softError(any(), any(), any(), any())).thenReturn(resultBuilder); + } + + @Test + void shouldSetVorgangnummer() { + plugin.buildSuccessResult(create(), invocationParameters); + + assertThat(resultCaptor.getValue()).containsExactly(Map.entry(VORGANGNUMMER_PROPERTY_KEY, VORGANGNUMMER)); + } + + @Test + void shouldSetWarnings() { + plugin.buildSuccessResult(create(), invocationParameters); + + verify(resultBuilder).softError(WARNING.getErrorCode(), WARNING.getMessage(), WARNING.getValue(), WARNING.getCause()); + } + } + + @Nested + class TestExecute { + + @Mock + private OzgPluginExecutor pluginExecutor; + @Mock + private ExecutionResult executionResult; + @Mock + private INormalCompletionResult pluginCompletionResult; + + @BeforeEach + void setup() { + doReturn(pluginExecutor).when(plugin).createPluginExecutor(any()); + } + + @Test + void shouldCallBuildSuccessResult() { + when(pluginExecutor.execute()).thenReturn(executionResult); + doReturn(pluginCompletionResult).when(plugin).buildSuccessResult(any(), any()); + + executePlugin(); + + verify(plugin).buildSuccessResult(executionResult, invocationParameters); + } + + @Nested + class TestCreateNodeThrewException { + + @Mock + private NodeThrewExceptionFactory exceptionFactory; + @Mock + private OzgCloudRequestException ozgCloudRequestException; + @Mock + private TechnicalException technicalException; + @Mock + private RuntimeException unexpectedException; + @Mock + private NodeThrewException nodeThrewException; + + @BeforeEach + void setup() { + doReturn(exceptionFactory).when(plugin).createExceptionFactory(any()); + } + + @Test + void shouldCallCreateOzgCloudRequestException() { + when(pluginExecutor.execute()).thenThrow(ozgCloudRequestException); + when(exceptionFactory.createOzgCloudRequestException(any())).thenReturn(nodeThrewException); + + executePlugin(); + + verify(exceptionFactory).createOzgCloudRequestException(ozgCloudRequestException); + } + + @Test + void shouldCallCreateInternalException() { + when(pluginExecutor.execute()).thenThrow(technicalException); + when(exceptionFactory.createInternalException(any())).thenReturn(nodeThrewException); + + executePlugin(); + + verify(exceptionFactory).createInternalException(technicalException); + } + + @Test + void shouldCallCreateInternalExceptionWhenUnexpectedThrown() { + when(pluginExecutor.execute()).thenThrow(unexpectedException); + when(exceptionFactory.createInternalException(any(), any())).thenReturn(nodeThrewException); + + executePlugin(); + + verify(exceptionFactory).createInternalException(any(), eq(unexpectedException)); + } + } + + void executePlugin() { + try { + plugin.execute(invocationParameters); + } catch (Exception e) { + } + } + + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutorTest.java b/src/test/java/de/ozgcloud/formcycle/OzgPluginExecutorTest.java similarity index 91% rename from src/test/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutorTest.java rename to src/test/java/de/ozgcloud/formcycle/OzgPluginExecutorTest.java index ef68358ce6aa9bb6d4e8c06ea831ad57f22997bb..74b6ec69fc7a0f818a94ca3937729f33d7bc9832 100644 --- a/src/test/java/de/ozgcloud/formcycle/WorkflowElementNodeExecutorTest.java +++ b/src/test/java/de/ozgcloud/formcycle/OzgPluginExecutorTest.java @@ -26,7 +26,6 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.*; -import java.io.IOException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -50,17 +49,18 @@ import de.ozgcloud.formcycle.attachment.AttachmentType; import de.ozgcloud.formcycle.attachment.FormcycleAttachment; import de.ozgcloud.formcycle.attachment.FormcycleAttachmentTestFactory; import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.OzgPluginSoftError; import de.ozgcloud.formcycle.formdata.FormData; import de.ozgcloud.formcycle.formdata.OzgCloudFormDataMapper; import de.ozgcloud.formcycle.formdata.PluginFormDataAdapter; import de.xima.fc.entities.Attachment; import lombok.SneakyThrows; -class WorkflowElementNodeExecutorTest { +class OzgPluginExecutorTest { @Spy @InjectMocks - private WorkflowElementNodeExecutor executor; + private OzgPluginExecutor executor; @Mock private OzgCloudFormDataMapper ozgCloudFormDataMapper; @@ -119,7 +119,7 @@ class WorkflowElementNodeExecutorTest { void shouldCreateSuccessResult() { var executionResult = executor.execute(); - assertThat(executionResult.get()).containsEntry(ExecutionResult.VORGANGNUMMER_PROPERTY_KEY, VORGANGS_NUMMER); + assertThat(executionResult.getVorgangnummer()).isEqualTo(VORGANGS_NUMMER); } } @@ -180,29 +180,6 @@ class WorkflowElementNodeExecutorTest { } } - @Nested - @DisplayName("By error") - class TestException { - - @Mock - private FormData formData; - - @SneakyThrows - @Test - void shouldThrowException() { - when(attachmentSupplier.get()).thenReturn(List.of()); - when(pluginFormDataAdapter.readFormData()).thenReturn(formData); - when(ozgHttpClient.send(any(), any(), any())).thenThrow(IOException.class); - - try { - executor.execute(); - } catch (NullPointerException e) { - } - - verify(exceptionFactory).createCannotSendException(any(), any()); - } - } - @Nested @DisplayName("Attachment's handling") class TestGetAttachments { @@ -267,4 +244,23 @@ class WorkflowElementNodeExecutorTest { } } + @Nested + class TestBuildWarning { + + private final String MESSAGE = "message"; + + @Test + void shouldSetErrorCode() { + var warning = executor.buildWarning(MESSAGE); + + assertThat(warning.getErrorCode()).isEqualTo(OzgPluginSoftError.MISSING_REPRESENTATION.toString()); + } + + @Test + void shouldSetMessage() { + var warning = executor.buildWarning(MESSAGE); + + assertThat(warning.getMessage()).isEqualTo(MESSAGE); + } + } } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplierTest.java b/src/test/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplierTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a30ce39e069ca5170290fa3dd8b4888c725b9921 --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/OzgPluginPropertiesSupplierTest.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle; + +import static de.ozgcloud.formcycle.PropertiesTestFactory.*; +import static java.util.Objects.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.Properties; + +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.CsvSource; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; + +import de.ozgcloud.formcycle.errorhandling.TechnicalException; +import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeData; + +class OzgPluginPropertiesSupplierTest { + @Spy + @InjectMocks + private OzgPluginPropertiesSupplier mapper; + + @Mock + private IPluginInitializeData pluginData; + @Mock + private PortParser portParser; + + private Properties properties = PropertiesTestFactory.create(); + + @Nested + class TestMapProxyProperty { + + @Mock + private ProxyConfig proxyConfig; + + @Test + void shouldCallValidateProxyConfig() { + doReturn(properties).when(pluginData).getProperties(); + + mapper.getProxyProperties(); + + verify(mapper).validateProxyConfig(properties); + } + + @Test + void shouldReturnProxyConfig() { + doReturn(properties).when(pluginData).getProperties(); + doNothing().when(mapper).validateProxyConfig(any()); + doReturn(proxyConfig).when(mapper).buildProxyConfig(any()); + + var result = mapper.getProxyProperties(); + + assertThat(result).isPresent().get().isEqualTo(proxyConfig); + } + + @Test + void shouldReturnEmptyWhenNoProxy() { + doReturn(PropertiesTestFactory.createEmpty()).when(pluginData).getProperties(); + + var result = mapper.getProxyProperties(); + + assertThat(result).isEmpty(); + } + } + + @Nested + class TestBuildProxyConfig { + + @Test + void shouldSetHost() { + var result = mapper.buildProxyConfig(properties); + + assertThat(result.getHost()).isEqualTo(PROXY_HOST); + } + + @Test + void shouldSetPort() { + doReturn(1).when(mapper).getPort(any()); + + var result = mapper.buildProxyConfig(properties); + + assertThat(result.getPort()).isEqualTo(1); + } + + @Test + void shouldSetUser() { + var result = mapper.buildProxyConfig(properties); + + assertThat(result.getUser()).isEqualTo(PROXY_USER); + } + + @Test + void shouldSetPassword() { + var result = mapper.buildProxyConfig(properties); + + assertThat(result.getPassword()).isEqualTo(PROXY_PASSWORD); + } + + } + + @Nested + class TestValidateProxyConfig { + + @DisplayName(value = "should fail when") + @ParameterizedTest(name = "host: \"{0}\" and port: \"{1}\"") + @CsvSource({ "host, ", ", 1", "'',1", "host,' '" }) + void shouldFailWhen(String host, String port) { + var properties = createProperties(host, port); + + assertThrows(TechnicalException.class, () -> mapper.validateProxyConfig(properties)); + } + + @Test + void shouldValidate() { + assertDoesNotThrow(() -> mapper.validateProxyConfig(properties)); + } + + private Properties createProperties(String host, String port) { + var properties = new Properties(); + if (nonNull(host)) { + properties.setProperty(ProxyConfig.KEY_PROXY_HOST, host); + } + if (nonNull(port)) { + properties.setProperty(ProxyConfig.KEY_PROXY_PORT, port); + } + return properties; + } + } + + @Nested + class TestGetPort { + + @Test + void shouldReturnNull() { + var result = mapper.getPort(PropertiesTestFactory.createEmpty()); + + assertThat(result).isNull(); + } + + @Test + void shouldCallParsePort() { + mapper.getPort(properties); + + verify(portParser).parse(PROXY_PORT); + } + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/PluginPropertiesMapperTest.java b/src/test/java/de/ozgcloud/formcycle/PluginPropertiesMapperTest.java deleted file mode 100644 index 1955324effb56cfd9e987eb9eb8d90bceed0dd05..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/formcycle/PluginPropertiesMapperTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2023 Das Land Schleswig-Holstein vertreten durch das - * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * 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.formcycle; - -import static de.ozgcloud.formcycle.PropertiesTestFactory.*; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Properties; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.Mock; - -import de.ozgcloud.formcycle.errorhandling.UserErrorDispatcher; - -class PluginPropertiesMapperTest { - - private PluginPropertiesMapper mapper; - - @Mock - private UserErrorDispatcher userErrorDispatcher; - - private Properties properties = PropertiesTestFactory.create(); - - @BeforeEach - void setUp() { - mapper = spy(new PluginPropertiesMapper(userErrorDispatcher)); - } - - @Nested - class TestMapProxyProperty { - - @Test - void shouldSetHost() { - var result = mapper.mapProxyProperties(properties); - - assertThat(result.getHost()).isEqualTo(PROXY_HOST); - } - - @Test - void shouldSetPort(){ - doReturn(1).when(mapper).getPort(any()); - - var result = mapper.mapProxyProperties(properties); - - assertThat(result.getPort()).isEqualTo(1); - } - - @Test - void shouldSetUser() { - var result = mapper.mapProxyProperties(properties); - - assertThat(result.getUser()).isEqualTo(PROXY_USER); - } - - @Test - void shouldSetPassword() { - var result = mapper.mapProxyProperties(properties); - - assertThat(result.getPassword()).isEqualTo(PROXY_PASSWORD); - } - - } - - @Nested - class TestGetPort { - - @DisplayName("should send user error message when") - @ParameterizedTest(name = "port property is {0}") - @ValueSource(strings = {"0", "65536", "abs"}) - void shouldSendUserErrorMessage(String portProperty) { - mapper.getPort(createPortProperty(portProperty)); - - verify(userErrorDispatcher).sendWrongProxyPortFormatMessage(); - } - - @Test - void shouldReturnPort() { - var result = mapper.getPort(create()); - - assertThat(result).isEqualTo(1); - } - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/UtilsTest.java b/src/test/java/de/ozgcloud/formcycle/PortParserTest.java similarity index 66% rename from src/test/java/de/ozgcloud/formcycle/UtilsTest.java rename to src/test/java/de/ozgcloud/formcycle/PortParserTest.java index 96b8741289c6f23532e25337d4c0cd7cae36963e..58f006b371b2d06f8811de2aa073610c7f115fdc 100644 --- a/src/test/java/de/ozgcloud/formcycle/UtilsTest.java +++ b/src/test/java/de/ozgcloud/formcycle/PortParserTest.java @@ -24,27 +24,39 @@ package de.ozgcloud.formcycle; import static org.assertj.core.api.Assertions.*; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.DisplayName; 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.NullAndEmptySource; import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.InjectMocks; -class UtilsTest { +class PortParserTest { - @DisplayName("should return null when") + @InjectMocks + private PortParser portParser; + + @DisplayName("should throw exception when") @ParameterizedTest(name = "port property is \"{0}\"") - @NullSource - @ValueSource(strings = {"", "0", "65536", "abs"}) + @ValueSource(strings = { "-1", "0", "65536", "abs" }) void shouldReturnNull(String portProperty) { - var result = Utils.parsePort(portProperty); + Assertions.assertThrows(IllegalArgumentException.class, () -> portParser.parse(portProperty)); + } + + @DisplayName("should return null when") + @ParameterizedTest(name = "port property is \"{0}\"") + @NullAndEmptySource + @ValueSource(strings = { " " }) + void shouldReturnNullWhenPortPropertyIsBlank(String portProperty) { + var port = portParser.parse(portProperty); - assertThat(result).isNull(); + assertThat(port).isNull(); } @Test void shouldReturnPort() { - var result = Utils.parsePort("1"); + var result = portParser.parse("1"); assertThat(result).isEqualTo(1); } diff --git a/src/test/java/de/ozgcloud/formcycle/PropertiesTestFactory.java b/src/test/java/de/ozgcloud/formcycle/PropertiesTestFactory.java index aa6caf0cfa281471f03ae788aca0dddae5ea1fe5..8d634384f73584631cc423392835df23c334f3cd 100644 --- a/src/test/java/de/ozgcloud/formcycle/PropertiesTestFactory.java +++ b/src/test/java/de/ozgcloud/formcycle/PropertiesTestFactory.java @@ -36,6 +36,7 @@ public class PropertiesTestFactory { public static Properties createEmpty() { return new Properties(); } + public static Properties create() { var properties = createEmpty(); properties.setProperty(OzgCloudConfig.KEY_EINGANGSADAPTER_URL, EINGANGSADAPTER_URL); diff --git a/src/test/java/de/ozgcloud/formcycle/WarningTestFactory.java b/src/test/java/de/ozgcloud/formcycle/WarningTestFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..410198d7d7c002f6e7044551a436051e41ffcdda --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/WarningTestFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle; + +import de.ozgcloud.formcycle.errorhandling.OzgPluginSoftError; +import de.ozgcloud.formcycle.errorhandling.Warning; +import de.ozgcloud.formcycle.errorhandling.Warning.WarningBuilder; + +public class WarningTestFactory { + + public static final OzgPluginSoftError ERROR_CODE = OzgPluginSoftError.MISSING_REPRESENTATION; + public static final String MESSAGE = "Missing representation"; + public static final String VALUE = "value"; + public static final Exception CAUSE = new Exception("cause"); + + public static Warning create() { + return createBuilder().build(); + } + + public static WarningBuilder createBuilder() { + return Warning.builder() + .errorCode(ERROR_CODE) + .message(MESSAGE) + .value(VALUE) + .cause(CAUSE); + } +} diff --git a/src/test/java/de/ozgcloud/formcycle/WorkflowElementNodePluginTest.java b/src/test/java/de/ozgcloud/formcycle/WorkflowElementNodePluginTest.java deleted file mode 100644 index d851698ee47a3867d9c03a9e51bc1a0fc4ec1ba0..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/formcycle/WorkflowElementNodePluginTest.java +++ /dev/null @@ -1,195 +0,0 @@ -package de.ozgcloud.formcycle; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Properties; - -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.InjectMocks; -import org.mockito.Mock; -import org.mockito.Spy; -import org.springframework.util.ReflectionUtils; - -import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; -import de.xima.fc.interfaces.plugin.lifecycle.IPluginInitializeData; -import de.xima.fc.interfaces.workflow.value.IRecordValueDescriptorBuilder; -import de.xima.fc.interfaces.workflow.value.IStringValueDescriptor; -import de.xima.fc.interfaces.workflow.value.IValueDescriptor; -import de.xima.fc.interfaces.workflow.value.IValueDescriptorFactory; -import lombok.SneakyThrows; - -class WorkflowElementNodePluginTest { - - @Spy - @InjectMocks - private WorkflowElementNodePlugin plugin; - - @Mock - private IPluginInitializeData initializeData; - @Mock - private PluginPropertiesMapper propertiesMapper; - - @Nested - class TestInitialization { - - @Test - void shouldCallCreatePropertiesMapper() { - plugin.initialize(initializeData); - - verify(plugin).createPropertiesMapper(); - } - - @Test - void shouldSetInitializeData() { - plugin.initialize(initializeData); - - assertThat(getInitializedData()).isEqualTo(initializeData); - } - - private IPluginInitializeData getInitializedData() { - var initializeDataField = ReflectionUtils.findField(WorkflowElementNodePlugin.class, "initializeData"); - initializeDataField.setAccessible(true); - return (IPluginInitializeData) ReflectionUtils.getField(initializeDataField, plugin); - } - - } - - @Nested - @DisplayName("Success value descriptor") - class TestSuccessValueDescriptor { - - @Mock - private IValueDescriptorFactory descriptorFactory; - @Mock - private IRecordValueDescriptorBuilder descriptorBuilder; - @Mock - private IStringValueDescriptor stringValueDescriptor; - - @BeforeEach - void setup() { - when(descriptorFactory.recordBuilder()).thenReturn(descriptorBuilder); - when(descriptorBuilder.requiredProperty(any(), any(IValueDescriptor.class))).thenReturn(descriptorBuilder); - } - - @Test - @DisplayName("should has vorgangnummer property") - void shouldAddVorgangnummerProperty() { - when(descriptorFactory.string()).thenReturn(stringValueDescriptor); - - plugin.getSuccessValueDescriptor(descriptorFactory); - - verify(descriptorBuilder).requiredProperty(ExecutionResult.VORGANGNUMMER_PROPERTY_KEY, stringValueDescriptor); - } - } - - @Nested - class TestCreateOzgClient { - - @Mock - private WorkflowElementNodeProps formProperties; - @Mock - private NodeThrewExceptionFactory exceptionFactory; - - private OzgCloudConfig ozgCloudConfig = OzgCloudConfigTestFactory.create(); - - @Test - @SneakyThrows - void shouldSetOzgCloudConfig() { - doReturn(ozgCloudConfig).when(plugin).buildOzgCloudConfig(any(), any()); - - var ozgClient = plugin.createOzgClient(formProperties, exceptionFactory); - - assertThat(getOzgCloudConfig(ozgClient)).isEqualTo(ozgCloudConfig); - } - - @Nested - class TestEingangsAdapterUrl { - - private static final String CUSTOM_URL = "http://custom.url"; - - @Test - void shouldReturnCustomUrl() { - when(formProperties.getEingangsAdapterUrl()).thenReturn(CUSTOM_URL); - when(formProperties.isUseCustomEingangsAdapterUrl()).thenReturn(true); - - var eingangsAdapterUrl = plugin.getEingangsAdapterUrl(formProperties); - - assertThat(eingangsAdapterUrl).isEqualTo(CUSTOM_URL); - } - - @Test - void shouldReturnDefaultUrl() { - when(formProperties.isUseCustomEingangsAdapterUrl()).thenReturn(false); - when(propertiesMapper.getEingangsAdapterUrl(any())).thenReturn(PropertiesTestFactory.EINGANGSADAPTER_URL); - - var eingangsAdapterUrl = plugin.getEingangsAdapterUrl(formProperties); - - assertThat(eingangsAdapterUrl).isEqualTo(PropertiesTestFactory.EINGANGSADAPTER_URL); - } - - } - - @Nested - class TestBuildOzgClientConfig { - - @Mock - private Properties properties; - - @Test - void shouldSetEingangsAdapterUrl() { - doReturn(PropertiesTestFactory.EINGANGSADAPTER_URL).when(plugin).getEingangsAdapterUrl(any()); - - var ozgClientConfig = buildOzgCloudConfig(); - - assertThat(ozgClientConfig.getEingangsAdapterUrl()).isEqualTo(PropertiesTestFactory.EINGANGSADAPTER_URL); - } - - @Test - void shouldSetProxyConfig() { - var expectedProxyConfig = ProxyConfigTestFactory.create(); - when(propertiesMapper.getEingangsAdapterUrl(any())).thenReturn(PropertiesTestFactory.EINGANGSADAPTER_URL); - when(propertiesMapper.mapProxyProperties(any())).thenReturn(expectedProxyConfig); - - var ozgClientConfig = buildOzgCloudConfig(); - - assertThat(ozgClientConfig.getProxyConfig()).isEqualTo(expectedProxyConfig); - } - - @Test - void shouldCallMapProxyProperties() { - doReturn(PropertiesTestFactory.EINGANGSADAPTER_URL).when(plugin).getEingangsAdapterUrl(any()); - when(initializeData.getProperties()).thenReturn(properties); - - buildOzgCloudConfig(); - - verify(propertiesMapper).mapProxyProperties(properties); - } - - @Test - void shouldCallCreateException() { - try { - buildOzgCloudConfig(); - } catch (NullPointerException e) { - // ignore exception - } - - verify(exceptionFactory).createConfigurationException(anyString()); - } - - @SneakyThrows - private OzgCloudConfig buildOzgCloudConfig() { - return plugin.buildOzgCloudConfig(formProperties, exceptionFactory); - } - } - - private OzgCloudConfig getOzgCloudConfig(OzgCloudFormDataHttpClient ozgClient) { - var ozgCloudConfigField = ReflectionUtils.findField(OzgCloudFormDataHttpClient.class, "config"); - ozgCloudConfigField.setAccessible(true); - return (OzgCloudConfig) ReflectionUtils.getField(ozgCloudConfigField, ozgClient); - } - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/attachment/AttachmentMapperTest.java b/src/test/java/de/ozgcloud/formcycle/attachment/AttachmentMapperTest.java index e13d9a5c510615b091e47e48a15eca0816031b62..a0601fb17eb151986ee61626d36d6055936e9389 100644 --- a/src/test/java/de/ozgcloud/formcycle/attachment/AttachmentMapperTest.java +++ b/src/test/java/de/ozgcloud/formcycle/attachment/AttachmentMapperTest.java @@ -24,8 +24,10 @@ package de.ozgcloud.formcycle.attachment; import static de.ozgcloud.formcycle.attachment.FormcycleAttachmentTestFactory.*; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; +import org.apache.http.entity.ContentType; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -33,8 +35,11 @@ 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.mockito.stubbing.Stubber; import de.ozgcloud.formcycle.errorhandling.NodeThrewExceptionFactory; +import de.ozgcloud.formcycle.errorhandling.TechnicalException; import de.xima.fc.entities.Attachment; import de.xima.fc.entities.AttachmentDatei; import de.xima.fc.exceptions.NodeThrewException; @@ -42,20 +47,48 @@ import lombok.SneakyThrows; class AttachmentMapperTest { + @Spy @InjectMocks private AttachmentMapper mapper; - @Mock - private NodeThrewExceptionFactory exceptionFactory; @Mock private Attachment attachment; @Nested - @DisplayName("Set fields") - class TestSetFields { + class TestMap { + + @Mock + private AttachmentDatei attachmentDatei; + + @BeforeEach + void setup() { + when(attachment.getFileEntity()).thenReturn(attachmentDatei); + } + + @Test + void shouldThrowException() { + assertThrows(TechnicalException.class, () -> mapper.map(attachment)); + } + + @Test + void shouldReturnResult() { + var expectedFormcycleAttachment = FormcycleAttachmentTestFactory.create(); + doReturn(expectedFormcycleAttachment).when(mapper).buildAttachment(any()); + when(attachmentDatei.getDaten()).thenReturn(CONTENT); + + var result = mapper.map(attachment); + + assertThat(result).isEqualTo(expectedFormcycleAttachment); + } + } + + @Nested + class TestBuildAttachment { @Mock private AttachmentDatei attachmentDatei; + @Mock + private ContentType contentType; @BeforeEach void setup() { @@ -89,9 +122,11 @@ class AttachmentMapperTest { @Test void shouldCreateAttachmentWithContentType() { + doReturn(contentType).when(mapper).getContentType(any()); + var ozgAttachment = buildAttachments(); - assertThat(ozgAttachment.getContentType().getMimeType()).isEqualTo(FormcycleAttachmentTestFactory.CONTENT_TYPE.getMimeType()); + assertThat(ozgAttachment.getContentType()).isEqualTo(contentType); } @SneakyThrows @@ -101,33 +136,23 @@ class AttachmentMapperTest { } @Nested - class TestExceptions { - - @Mock - private AttachmentDatei attachmentDatei; + class TestGetContentType { @BeforeEach void setup() { - when(attachment.getFileEntity()).thenReturn(attachmentDatei); - when(exceptionFactory.createCannotGetAttachmentException(any())).thenReturn(mock(NodeThrewException.class)); +// when(exceptionFactory.createMissingRepresentationException(any())).thenReturn(mock(NodeThrewException.class)); } @Test - @DisplayName("should throw when can not read attached data") void shouldThrowException() { - Assertions.assertThrows(NodeThrewException.class, () -> mapper.map(attachment)); - - verify(exceptionFactory).createCannotGetAttachmentException(AttachmentMapper.CANNOT_READ_ATTACHED_FILE); + assertThrows(TechnicalException.class, () -> mapper.getContentType(null)); } @Test - @DisplayName("should throw exception when file name is null") - void shouldThrowExceptionName() { - when(attachmentDatei.getDaten()).thenReturn(new byte[]{}); - - Assertions.assertThrows(NodeThrewException.class, () -> mapper.map(attachment)); + void shouldReturnResult() { + var result = mapper.getContentType(FILE_NAME); - verify(exceptionFactory).createCannotGetAttachmentException(AttachmentMapper.FILENAME_IS_NULL); + assertThat(result.getMimeType()).isEqualTo(CONTENT_TYPE.getMimeType()); } } } \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/attachment/FormcycleAttachmentTestFactory.java b/src/test/java/de/ozgcloud/formcycle/attachment/FormcycleAttachmentTestFactory.java index 5b16895bb7c629b3e87870f5c3141c1854cb0532..7dbb6461e0503ac7598d1079bc118a214ac2bfdc 100644 --- a/src/test/java/de/ozgcloud/formcycle/attachment/FormcycleAttachmentTestFactory.java +++ b/src/test/java/de/ozgcloud/formcycle/attachment/FormcycleAttachmentTestFactory.java @@ -19,7 +19,7 @@ public class FormcycleAttachmentTestFactory { return createBuilder().build(); } - private static FormcycleAttachmentBuilder createBuilder() { + static FormcycleAttachmentBuilder createBuilder() { return FormcycleAttachment.builder() .uuid(UUID) .fileName(FILE_NAME) diff --git a/src/test/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandlerTest.java b/src/test/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3d8dc0e3bcc61660b7f69bbba6cd4171b2b1ff62 --- /dev/null +++ b/src/test/java/de/ozgcloud/formcycle/client/OzgCloudResponseHandlerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 Das Land Schleswig-Holstein vertreten durch das + * Ministerium für Energiewende, Klimaschutz, Umwelt und Natur + * Zentrales IT-Management + * + * 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.formcycle.client; + +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.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +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 com.fasterxml.jackson.databind.ObjectMapper; + +import de.ozgcloud.eingang.formcycle.FormCycleConfirmationResponse; +import de.ozgcloud.formcycle.errorhandling.OzgCloudRequestException; +import lombok.SneakyThrows; + +class OzgCloudResponseHandlerTest { + + @Spy + @InjectMocks + private OzgCloudResponseHandler responseHandler; + + @Mock + private ObjectMapper objectMapper; + + @Mock + private HttpResponse httpResponse; + + @Nested + class TestHandleResponse { + + @Mock + private HttpEntity httpEntity; + + @SneakyThrows + @BeforeEach + void setUp() { + doNothing().when(responseHandler).checkResponseStatus(any()); + } + + @SneakyThrows + @Test + void shouldCheckResponseStatus() { + when(httpResponse.getEntity()).thenReturn(httpEntity); + when(httpEntity.getContent()).thenReturn(InputStream.nullInputStream()); + + responseHandler.handleResponse(httpResponse); + + verify(responseHandler).checkResponseStatus(httpResponse); + } + + @SneakyThrows + @Test + void shouldReturnVorgangNummer() { + var expectedConformationResponse = FormCycleConfirmationResponse.newBuilder().setVorgangNummer("123").build(); + doReturn(expectedConformationResponse).when(responseHandler).buildResponse(httpResponse); + + var response = responseHandler.handleResponse(httpResponse); + + assertThat(response).isEqualTo(expectedConformationResponse); + } + + } + + @Nested + class TestCheckResponseStatus { + + @Mock + private StatusLine statusLine; + + private final OzgCloudErrorDto errorDto = new OzgCloudErrorDto(); + private final String message = "message"; + private final String exceptionId = "exception-id"; + + @BeforeEach + void setUp() { + errorDto.setMessage(message); + errorDto.setExceptionId(exceptionId); + } + + @Test + void shouldPassWhenOk() { + when(statusLine.getStatusCode()).thenReturn(HttpStatus.SC_OK); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + + assertDoesNotThrow(() -> responseHandler.checkResponseStatus(httpResponse)); + } + + @Test + void shouldThrowExceptionWhenError() { + when(statusLine.getStatusCode()).thenReturn(HttpStatus.SC_BAD_REQUEST); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + doReturn(errorDto).when(responseHandler).getOzgCloudError(any()); + + var ozgCloudRequestException = assertThrows(OzgCloudRequestException.class, () -> responseHandler.checkResponseStatus(httpResponse)); + + assertThat(ozgCloudRequestException.getMessage()).startsWith(message); + assertThat(ozgCloudRequestException.getExceptionId()).isEqualTo(exceptionId); + } + + @Test + void shouldThrowExceptionWhenNotOk() { + when(statusLine.getStatusCode()).thenReturn(HttpStatus.SC_NO_CONTENT); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + doReturn(message).when(responseHandler).buildFullMessage(any()); + + var ozgCloudRequestException = assertThrows(OzgCloudRequestException.class, () -> responseHandler.checkResponseStatus(httpResponse)); + + assertThat(ozgCloudRequestException.getMessage()).startsWith(message); + } + } + + @Nested + class TestBuildFullMessage { + + @Mock + private HttpEntity httpEntity; + + @Mock + private StatusLine statusLine; + + private String messageBody = "message-body"; + + @SneakyThrows + @BeforeEach + void setUp() { + when(statusLine.toString()).thenReturn(String.valueOf(HttpStatus.SC_BAD_REQUEST)); + when(httpResponse.getStatusLine()).thenReturn(statusLine); + var inputStream = IOUtils.toInputStream(messageBody, StandardCharsets.UTF_8); + when(httpEntity.getContent()).thenReturn(inputStream); + when(httpResponse.getEntity()).thenReturn(httpEntity); + } + + @Test + void shouldBuildMessage() { + var expectedMessage = String.format(OzgCloudResponseHandler.MESSAGE, HttpStatus.SC_BAD_REQUEST) + "\n" + messageBody; + + var message = responseHandler.buildFullMessage(httpResponse); + + assertThat(message).isEqualTo(expectedMessage); + } + + } +} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactoryTest.java b/src/test/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactoryTest.java index fc9ed294f733899cb4353c9db23a573d37193429..2235fd882268800d9730c51e993af1dd17fb9f7e 100644 --- a/src/test/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactoryTest.java +++ b/src/test/java/de/ozgcloud/formcycle/errorhandling/NodeThrewExceptionFactoryTest.java @@ -3,7 +3,7 @@ package de.ozgcloud.formcycle.errorhandling; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.io.IOException; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -15,13 +15,13 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Spy; -import com.alibaba.fastjson.JSONObject; - import de.ozgcloud.formcycle.attachment.FormcycleAttachment; import de.xima.fc.interfaces.workflow.params.INodeThrewExceptionBuilder; class NodeThrewExceptionFactoryTest { + private static final String EXCEPTION_ID = "exception-id"; + @Spy @InjectMocks private NodeThrewExceptionFactory exceptionFactory; @@ -30,74 +30,187 @@ class NodeThrewExceptionFactoryTest { private INodeThrewExceptionBuilder exceptionBuilder; @Captor - private ArgumentCaptor<String> errorKeyCaptor; + private ArgumentCaptor<String> stringCaptor; @Captor - private ArgumentCaptor<JSONObject> errorDataCaptor; + private ArgumentCaptor<Map<String, String>> errorDataCaptor; @Captor - private ArgumentCaptor<IOException> errorCauseCaptor; + private ArgumentCaptor<Exception> errorCauseCaptor; @Mock private FormcycleAttachment expectedAttachments; @BeforeEach void setup() { - when(exceptionBuilder.error(errorKeyCaptor.capture(), errorDataCaptor.capture())).thenReturn(exceptionBuilder); - when(exceptionBuilder.message(anyString())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(any(), any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.cause(any())).thenReturn(exceptionBuilder); } + @DisplayName("Create internal exception") @Nested - @DisplayName("When Can Not Send exception will throw") - class TestCanNotSend { + class TestCreateInternalException { - @BeforeEach - void setup() { - when(exceptionBuilder.cause(errorCauseCaptor.capture())).thenReturn(exceptionBuilder); - } + @DisplayName("form TechnicalException") + @Nested + class TestFromTechnicalException { + @Mock + private TechnicalException exception; - @Test - void shouldAddErrorCode() { - exceptionFactory.createCannotSendException("", mock(IOException.class)); + @BeforeEach + void setup() { + when(exception.getExceptionId()).thenReturn(EXCEPTION_ID); + } - assertThat(errorKeyCaptor.getValue()).isEqualTo(EWorkflowElementNodeError.OZGCLOUD_CANNOT_SEND.name()); - } + @Test + void shouldAddErrorCode() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(stringCaptor.capture(), any())).thenReturn(exceptionBuilder); - @Test - void shouldAddExceptionMessage() { - var message = "error"; + exceptionFactory.createInternalException(exception); + + assertThat(stringCaptor.getValue()).isEqualTo(OzgPluginError.INTERNAL_ERROR.name()); + } + + @Test + void shouldAddExceptionId() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(any(), errorDataCaptor.capture())).thenReturn(exceptionBuilder); - exceptionFactory.createCannotSendException(message, mock(IOException.class)); + exceptionFactory.createInternalException(exception); - assertThat(errorDataCaptor.getValue()).containsEntry(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, message); + var errorData = errorDataCaptor.getValue(); + assertThat(errorData).containsEntry(NodeThrewExceptionFactory.EXCEPTION_ID_KEY, EXCEPTION_ID); + } + + @Test + void shouldAddExceptionMessage() { + var errorMessage = "error message"; + when(exception.getMessage()).thenReturn(errorMessage); + when(exceptionBuilder.message(stringCaptor.capture())).thenReturn(exceptionBuilder); + + exceptionFactory.createInternalException(exception); + + assertThat(stringCaptor.getValue()).isEqualTo(errorMessage); + } + + @Test + void shouldAddCause() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.cause(errorCauseCaptor.capture())).thenReturn(exceptionBuilder); + + exceptionFactory.createInternalException(exception); + + assertThat(errorCauseCaptor.getValue()).isEqualTo(exception); + } } - @Test - void shouldAddCause() { - var causeException = mock(IOException.class); + @DisplayName("from message and exception") + @Nested + class TestFromException { + + private static final String EXCEPTION_MESSAGE = "exception message"; + @Mock + private Exception exception; + + @Test + void shouldAddErrorCode() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(stringCaptor.capture(), any())).thenReturn(exceptionBuilder); + + createException(); + + assertThat(stringCaptor.getValue()).isEqualTo(OzgPluginError.INTERNAL_ERROR.name()); + } + + @Test + void shouldAddExceptionId() { + doReturn(Map.of(NodeThrewExceptionFactory.EXCEPTION_ID_KEY, EXCEPTION_ID)).when(exceptionFactory).createDataMap(any()); + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(any(), errorDataCaptor.capture())).thenReturn(exceptionBuilder); - exceptionFactory.createCannotSendException("", causeException); + createException(); - assertThat(errorCauseCaptor.getValue()).isEqualTo(causeException); + var errorData = errorDataCaptor.getValue(); + assertThat(errorData).containsEntry(NodeThrewExceptionFactory.EXCEPTION_ID_KEY, EXCEPTION_ID); + } + + @Test + void shouldAddExceptionMessage() { + when(exceptionBuilder.message(stringCaptor.capture())).thenReturn(exceptionBuilder); + + createException(); + + assertThat(stringCaptor.getValue()).contains(EXCEPTION_MESSAGE, "(ExceptionId:"); + } + + @Test + void shouldAddCause() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.cause(errorCauseCaptor.capture())).thenReturn(exceptionBuilder); + + createException(); + + assertThat(errorCauseCaptor.getValue()).isEqualTo(exception); + } + + private void createException() { + exceptionFactory.createInternalException(EXCEPTION_MESSAGE, exception); + } } } @Nested - @DisplayName("When error by reading of attachments") - class TestCanNotGetAttachments { + class TestFromOzgCloudRequestException { + @Mock + private OzgCloudRequestException exception; + + @BeforeEach + void setup() { + when(exception.getExceptionId()).thenReturn(EXCEPTION_ID); + } @Test void shouldAddErrorCode() { - exceptionFactory.createCannotGetAttachmentException(""); + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(stringCaptor.capture(), any())).thenReturn(exceptionBuilder); + + createException(); - assertThat(errorKeyCaptor.getValue()).isEqualTo(EWorkflowElementNodeError.PLUGIN_CANNOT_GET_ATTACHMENT.name()); + assertThat(stringCaptor.getValue()).isEqualTo(OzgPluginError.OZGCLOUD_REQUEST_FAILED.name()); + } + + @Test + void shouldAddExceptionId() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.error(any(), errorDataCaptor.capture())).thenReturn(exceptionBuilder); + + createException(); + + var errorData = errorDataCaptor.getValue(); + assertThat(errorData).containsEntry(NodeThrewExceptionFactory.EXCEPTION_ID_KEY, EXCEPTION_ID); } @Test void shouldAddExceptionMessage() { - var message = "error"; + var errorMessage = "error message"; + when(exception.getMessageWithConfig()).thenReturn(errorMessage); + when(exceptionBuilder.message(stringCaptor.capture())).thenReturn(exceptionBuilder); + + createException(); + + assertThat(stringCaptor.getValue()).isEqualTo(errorMessage); + } + + @Test + void shouldAddCause() { + when(exceptionBuilder.message(any())).thenReturn(exceptionBuilder); + when(exceptionBuilder.cause(errorCauseCaptor.capture())).thenReturn(exceptionBuilder); - exceptionFactory.createCannotGetAttachmentException(message); + createException(); - assertThat(errorDataCaptor.getValue()).containsEntry(NodeThrewExceptionFactory.ERROR_MESSAGE_KEY, message); + assertThat(errorCauseCaptor.getValue()).isEqualTo(exception); } + private void createException() { + exceptionFactory.createOzgCloudRequestException(exception); + } } } \ No newline at end of file