diff --git a/jenkinsfiles/test.Jenkinsfile b/jenkinsfiles/test.Jenkinsfile index 783068c56fec24b890daeb35b759a66a54808987..2d06afdbe0a951a9609593d9ac8a34bd0ecbd601 100644 --- a/jenkinsfiles/test.Jenkinsfile +++ b/jenkinsfiles/test.Jenkinsfile @@ -8,7 +8,7 @@ pipeline { string(name: 'BRANCH', defaultValue: "main", description: 'Set branch') string(name: 'CREDENTIALS_ID', defaultValue: "xplanbox.lat-lon.de", description: 'Set id of Jenkins credentials to login to environment (BASIC AUTH)') string(name: 'SERVICES_API_KEY', defaultValue: "xplanbox", description: 'Set ApiKey to access /config of XPlanDienste') - string(name: 'ENDPOINT_VALIDATOR_API', defaultValue: "https://xplanbox.lat-lon.de/xvalidator/api/v1", description: 'Set endpoint of XPlanValidatorAPI') + string(name: 'BASE_URL_VALIDATOR_API', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanValidatorAPI') string(name: 'BASE_URL_DIENSTE', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanDienste') string(name: 'BASE_URL_INSPIRE_PLU', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of INSPIRE PLU') string(name: 'BASE_URL_MANAGER_API', defaultValue: "https://xplanbox.lat-lon.de", description: 'Set base URL of XPlanManagerAPI') @@ -44,7 +44,7 @@ pipeline { usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') ]) { sh 'mvn test -pl :xplan-tests-soapui -Psystem-tests -DtestFileName=xplan-validator-api-soapui-project.xml \ - -Dendpoint=${ENDPOINT_VALIDATOR_API} -Dusername=$Username -Dpassword=$Password' + -DbaseUrlValidatorApi=${BASE_URL_VALIDATOR_API} -Dusername=$Username -Dpassword=$Password' } } } @@ -79,7 +79,9 @@ pipeline { withCredentials([ usernamePassword(credentialsId:"${CREDENTIALS_ID}", passwordVariable: 'Password', usernameVariable: 'Username') ]) { - sh 'mvn integration-test -pl :xplan-tests-selenium -Psystem-tests -DbaseUrlValidatorWeb=${BASE_URL_VALIDATOR_WEB} -Dusername=$Username -Dpassword=$Password' + withEnv(['SKIP_RABBIT_TESTS=true']) { + sh 'mvn integration-test -pl :xplan-tests-selenium -Psystem-tests -DbaseUrlValidatorWeb=${BASE_URL_VALIDATOR_WEB} -Dusername=$Username -Dpassword=$Password' + } } } } diff --git a/pom.xml b/pom.xml index 47c1a68d7dc69dd5c490ad85e2085108c80a75f3..e7e52fef6a268777bfc712630a16d2b9572a5b5a 100644 --- a/pom.xml +++ b/pom.xml @@ -759,7 +759,7 @@ <plugin> <groupId>io.fabric8</groupId> <artifactId>docker-maven-plugin</artifactId> - <version>0.44.0</version> + <version>0.45.0</version> </plugin> <plugin> <groupId>org.codehaus.mojo</groupId> @@ -1095,6 +1095,11 @@ <artifactId>xplan-core-validator-events</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>xplan-validator-storage</artifactId> + <version>${project.version}</version> + </dependency> <dependency> <groupId>${project.groupId}</groupId> <artifactId>xplan-core-schemas</artifactId> @@ -1150,6 +1155,12 @@ <version>1.1.1</version> <scope>test</scope> </dependency> + <dependency> + <groupId>io.findify</groupId> + <artifactId>s3mock_2.13</artifactId> + <version>0.2.6</version> + <scope>test</scope> + </dependency> <!-- pdf report --> <dependency> <groupId>com.github.librepdf</groupId> diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/openapi/OpenApiFilter.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/openapi/OpenApiFilter.java index 9161f5a09b16541ba6427f7c1627c13a0dc72a16..1ca5b88e400ccbb2f3ccd15a9de20c7d60d666c9 100644 --- a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/openapi/OpenApiFilter.java +++ b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/openapi/OpenApiFilter.java @@ -88,7 +88,7 @@ public class OpenApiFilter extends AbstractSpecFilter { paths.putAll(filteredPathItems); } - private String createNewKey(String path) { + protected String createNewKey(String path) { Pattern pattern = Pattern.compile("\\/(xvalidator|xmanager|xdokumente)\\/api\\/v[\\d_\\-\\.]*(\\/|)"); Matcher matcher = pattern.matcher(path); return matcher.replaceFirst("/"); diff --git a/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v1/model/AbstractLink.java b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v1/model/AbstractLink.java new file mode 100644 index 0000000000000000000000000000000000000000..29caeb95b4b3649535d7bb87ddeaeaf166c1cd77 --- /dev/null +++ b/xplan-core/xplan-core-api/src/main/java/de/latlon/xplanbox/api/commons/v1/model/AbstractLink.java @@ -0,0 +1,163 @@ +/*- + * #%L + * xplan-manager-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.commons.v1.model; + +import java.net.URI; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; + +/** + * Datatype for Link. A AbstractLink to a resource related to the resource such as + * XPlanWerkWMS or the resource itself. + * + * @since 4.0 + */ +@Schema(description = "Link to a resource related to the resource such as XPlanWerkWMS or the resource itself") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", + date = "2020-08-28T13:42:47.160+02:00[Europe/Berlin]") +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class AbstractLink { + + private @Valid URI href; + + private @Valid String type; + + private @Valid String hreflang; + + private @Valid String title; + + private @Valid Integer length; + + public AbstractLink href(URI href) { + this.href = href; + return this; + } + + @Schema(example = "https://xplanbox.lat-lon.de/xmanager/api/v1/plan/123", required = true) + @JsonProperty("href") + @NotNull + public URI getHref() { + return href; + } + + public void setHref(URI href) { + this.href = href; + } + + public AbstractLink type(String type) { + this.type = type; + return this; + } + + @Schema(example = "application/json") + @JsonProperty("type") + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public AbstractLink hreflang(String hreflang) { + this.hreflang = hreflang; + return this; + } + + @Schema(example = "en") + @JsonProperty("hreflang") + public String getHreflang() { + return hreflang; + } + + public void setHreflang(String hreflang) { + this.hreflang = hreflang; + } + + public AbstractLink title(String title) { + this.title = title; + return this; + } + + @Schema(example = "Othmarschen 3, Hamburg") + @JsonProperty("title") + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public AbstractLink length(Integer length) { + this.length = length; + return this; + } + + @Schema + @JsonProperty("length") + public Integer getLength() { + return length; + } + + public void setLength(Integer length) { + this.length = length; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AbstractLink link = (AbstractLink) o; + return Objects.equals(this.href, link.href) && Objects.equals(this.type, link.type) + && Objects.equals(this.hreflang, link.hreflang) && Objects.equals(this.title, link.title) + && Objects.equals(this.length, link.length); + } + + @Override + public int hashCode() { + return Objects.hash(href, type, hreflang, title, length); + } + + /** + * Convert the given object to string with each line indented by 4 spaces (except the + * first line). + */ + protected String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} diff --git a/xplan-core/xplan-core-commons/pom.xml b/xplan-core/xplan-core-commons/pom.xml index 44119c28e04b3a39a9fd70afb2c316728989cbf4..afda0793b6e51a3b2b7fd4360b9d3dd79b10c1ca 100644 --- a/xplan-core/xplan-core-commons/pom.xml +++ b/xplan-core/xplan-core-commons/pom.xml @@ -120,6 +120,10 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-java-sdk-s3</artifactId> + </dependency> </dependencies> </project> \ No newline at end of file diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Metadata.java b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Metadata.java similarity index 97% rename from xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Metadata.java rename to xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Metadata.java index 74d7a5dcf314ce4e3fbe6e07f3caec5ccfb4e907..df5eccda75f95b3b759811bff99e56668cf10380 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Metadata.java +++ b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Metadata.java @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplan.manager.storage.s3; +package de.latlon.xplan.commons.s3; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Object.java b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Object.java similarity index 96% rename from xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Object.java rename to xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Object.java index b9cb4d69d6b8f9a3fcf34b78e07fb6bc3adffe54..5ae595abbcc95e8df23d43096719ff3023dff201 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Object.java +++ b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Object.java @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplan.manager.storage.s3; +package de.latlon.xplan.commons.s3; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Storage.java b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Storage.java similarity index 82% rename from xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Storage.java rename to xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Storage.java index 65cee688603bc1ab2a19cfaf161813c593f7ed85..e16095e7c988a50430d7809ceb4955eafe98e48f 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3Storage.java +++ b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/S3Storage.java @@ -18,29 +18,33 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplan.manager.storage.s3; +package de.latlon.xplan.commons.s3; + +import static com.amazonaws.services.s3.model.BucketLifecycleConfiguration.ENABLED; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Path; +import java.util.List; import com.amazonaws.AmazonServiceException; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.AmazonS3Exception; import com.amazonaws.services.s3.model.Bucket; +import com.amazonaws.services.s3.model.BucketLifecycleConfiguration; import com.amazonaws.services.s3.model.ObjectListing; import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectResult; import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectSummary; +import com.amazonaws.services.s3.model.lifecycle.LifecycleFilter; import de.latlon.xplan.commons.archive.ArchiveEntry; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; -import java.util.List; - /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> * @since 7.0 @@ -58,13 +62,24 @@ public class S3Storage { this.bucketName = bucketName; } + public void setBucketExpirationDate(String id, int expirationInDays) throws StorageException { + createBucketIfNotExists(); + BucketLifecycleConfiguration bucketLifecycleConfig = new BucketLifecycleConfiguration(); + BucketLifecycleConfiguration.Rule rule = new BucketLifecycleConfiguration.Rule().withId(id) + .withExpirationInDays(expirationInDays) + .withStatus(ENABLED) + .withFilter(new LifecycleFilter()); + bucketLifecycleConfig.withRules(rule); + client.setBucketLifecycleConfiguration(bucketName, bucketLifecycleConfig); + } + /** * @param key of the object to return * @return the S3Object with the passed key, never <code>null</code> * @throws StorageException if an error occurred requesting the object or an object * with the passed key was not found */ - public de.latlon.xplan.manager.storage.s3.S3Object getObject(String key) throws StorageException { + public de.latlon.xplan.commons.s3.S3Object getObject(String key) throws StorageException { S3Object object = null; try { LOG.info("Get object with key {} from bucket {}.", key, bucketName); @@ -74,7 +89,7 @@ public class S3Storage { objectMetadata.getContentLength()); ByteArrayOutputStream bos = new ByteArrayOutputStream(); object.getObjectContent().transferTo(bos); - return new de.latlon.xplan.manager.storage.s3.S3Object(s3Metadata, bos.toByteArray()); + return new de.latlon.xplan.commons.s3.S3Object(s3Metadata, bos.toByteArray()); } catch (AmazonServiceException | IOException e) { throw new StorageException("Could not get object with key " + key + " from bucket " + bucketName + ".", e); @@ -107,8 +122,9 @@ public class S3Storage { return objectsToDelete.getObjectSummaries(); } - protected String insertObject(int planId, String entryName, XPlanArchiveContentAccess archive) + public String insertObject(int planId, String entryName, XPlanArchiveContentAccess archive) throws StorageException { + createBucketIfNotExists(); String key = createKey(planId, entryName); try { LOG.info("Insert object with key {} in bucket {}.", key, bucketName); @@ -127,17 +143,19 @@ public class S3Storage { } } - protected void insertObject(String key, Path file) throws StorageException { + public PutObjectResult insertObject(String key, Path file) throws StorageException { + createBucketIfNotExists(); try { LOG.info("Insert object with key {} in bucket {}.", key, bucketName); - client.putObject(bucketName, key, file.toFile()); + return client.putObject(bucketName, key, file.toFile()); } catch (AmazonServiceException e) { throw new StorageException("Could not insert object with key " + key + " in bucket " + bucketName + ".", e); } } - public void insertObject(de.latlon.xplan.manager.storage.s3.S3Object object) throws StorageException { + public void insertObject(de.latlon.xplan.commons.s3.S3Object object) throws StorageException { + createBucketIfNotExists(); String key = object.getS3Metadata().getKey(); try { LOG.info("Insert object with key {} in bucket {}.", key, bucketName); diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/StorageException.java b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/StorageException.java similarity index 89% rename from xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/StorageException.java rename to xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/StorageException.java index ff5dc2d2021feb8742de6bcbed27830ef3220960..198f42ef45bf8fdc6cee34d942397e2fc5fee930 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/StorageException.java +++ b/xplan-core/xplan-core-commons/src/main/java/de/latlon/xplan/commons/s3/StorageException.java @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplan.manager.wmsconfig.raster.storage; +package de.latlon.xplan.commons.s3; import com.amazonaws.AmazonServiceException; @@ -35,6 +35,11 @@ public class StorageException extends Exception { this.statusCode = ((AmazonServiceException) e).getStatusCode(); } + public StorageException(String message, int statusCode) { + super(message); + this.statusCode = statusCode; + } + public int getStatusCode() { return statusCode; } diff --git a/xplan-core/xplan-core-manager/pom.xml b/xplan-core/xplan-core-manager/pom.xml index 08053849600421735dfed56bf65b892de08b87ae..2870fd8959fc072ca69ed48cf8264cb326a572a7 100644 --- a/xplan-core/xplan-core-manager/pom.xml +++ b/xplan-core/xplan-core-manager/pom.xml @@ -164,7 +164,6 @@ <dependency> <groupId>io.findify</groupId> <artifactId>s3mock_2.13</artifactId> - <version>0.2.6</version> <scope>test</scope> </dependency> <dependency> diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/DocumentStorage.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/DocumentStorage.java index 56806e16b0110a67d4a2c21ba8403df42546e743..906d8149351c87217e9cd4093901698c773338cd 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/DocumentStorage.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/DocumentStorage.java @@ -22,7 +22,7 @@ package de.latlon.xplan.manager.document; import de.latlon.xplan.commons.archive.XPlanArchive; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import java.nio.file.Path; import java.util.List; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/XPlanDocumentManager.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/XPlanDocumentManager.java index 8fde067de2b0dd909b2b8b84a7816f4afbe98c15..1764ae100f5744991ce8a2614a5c6457a8cf9c37 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/XPlanDocumentManager.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/XPlanDocumentManager.java @@ -25,7 +25,7 @@ import de.latlon.xplan.commons.reference.ExternalReference; import de.latlon.xplan.commons.reference.ExternalReferenceInfo; import de.latlon.xplan.manager.edit.EditedArtefacts; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationEventPublisher; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/S3DocumentStorage.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/S3DocumentStorage.java index 17ea9de039e3b7ddc7ae34031925923b3402eb9b..318db1a9ce3c387014e19af1eca92b48935d0051 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/S3DocumentStorage.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/S3DocumentStorage.java @@ -24,9 +24,9 @@ import com.amazonaws.services.s3.AmazonS3; import de.latlon.xplan.commons.archive.XPlanArchive; import de.latlon.xplan.manager.document.DocumentStorage; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.storage.s3.S3Object; -import de.latlon.xplan.manager.storage.s3.S3Storage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; import java.nio.file.Path; import java.util.ArrayList; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/config/AmazonS3DocumentStorageContext.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/config/AmazonS3DocumentStorageContext.java index d6d362679c962406b0563a8772b5c72bb6ea2ea5..313ac427276b808e92b71551b0bd9e848f12fc3b 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/config/AmazonS3DocumentStorageContext.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/document/s3/config/AmazonS3DocumentStorageContext.java @@ -42,7 +42,7 @@ public class AmazonS3DocumentStorageContext { @Bean public S3DocumentStorage documentStorage(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { return new S3DocumentStorage(s3Client, bucketName); } diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageCleanUpManager.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageCleanUpManager.java index d342cd6f91baf84e433a3cb5d987c3fab97b074d..2b0278093e5a8cea54dd78af4f78157ac39cb3a1 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageCleanUpManager.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageCleanUpManager.java @@ -20,7 +20,7 @@ */ package de.latlon.xplan.manager.storage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageEvent.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageEvent.java index 688e8e0e5db5d471d05c30b2e6ff6ce48663d38c..e81d45b641eae96d09087c84a3ec6216e69601c1 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageEvent.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/StorageEvent.java @@ -20,7 +20,7 @@ */ package de.latlon.xplan.manager.storage; -import de.latlon.xplan.manager.storage.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Object; import java.nio.file.Path; import java.util.ArrayList; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/filesystem/FilesystemStorageCleanUpManager.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/filesystem/FilesystemStorageCleanUpManager.java index cf12833a2d024faac7426be63629f3f8791f7ca7..2898356c89fae6fa4fdc6dcafdd40214b2334221 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/filesystem/FilesystemStorageCleanUpManager.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/filesystem/FilesystemStorageCleanUpManager.java @@ -23,7 +23,7 @@ package de.latlon.xplan.manager.storage.filesystem; import de.latlon.xplan.manager.storage.StorageCleanUpManager; import de.latlon.xplan.manager.storage.StorageEvent; import de.latlon.xplan.manager.wmsconfig.raster.storage.FileSystemStorage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManager.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManager.java index bf213e712e4df1a3b4e8aefa0a4a3a6d64d9d758..73673267d2b0bc64e52e6b210cc460185ad57229 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManager.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManager.java @@ -22,9 +22,11 @@ package de.latlon.xplan.manager.storage.s3; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.model.S3ObjectSummary; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; import de.latlon.xplan.manager.storage.StorageCleanUpManager; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import java.util.List; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3Context.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3Context.java index b91778c0f36c7d8fed2b91adef356c177c14c947..8087bbe55638f8d71c9c52f7d204bb69543e6003 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3Context.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3Context.java @@ -27,7 +27,7 @@ import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import de.latlon.xplan.manager.storage.StorageCleanUpManager; -import de.latlon.xplan.manager.storage.s3.S3Storage; +import de.latlon.xplan.commons.s3.S3Storage; import de.latlon.xplan.manager.storage.s3.S3StorageCleanUpManager; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -49,8 +49,8 @@ public class AmazonS3Context { @Bean(destroyMethod = "shutdown") public AmazonS3 s3Client(AWSCredentials credentials, - @Value("${s3.region:#{environment.XPLAN_S3_REGION}}") String signingRegion, - @Value("${s3.endpoint.url:#{environment.XPLAN_S3_ENDPOINT}}") String endpointUrl) { + @Value("${xplanbox.s3.region:#{environment.XPLAN_S3_REGION}}") String signingRegion, + @Value("${xplanbox.s3.endpoint.url:#{environment.XPLAN_S3_ENDPOINT}}") String endpointUrl) { AmazonS3 client; // TODO refactoring if/else to @ConditionalOnExpression with SpringBoot into 2 // SpringBeans @@ -72,20 +72,21 @@ public class AmazonS3Context { } @Bean - public AWSCredentials credentials(@Value("${s3.accessKeyId:#{environment.XPLAN_S3_ACCESS_KEY}}") String accessKeyId, - @Value("${s3.secretKey:#{environment.XPLAN_S3_SECRET_ACCESS_KEY}}") String secretKey) { + public AWSCredentials credentials( + @Value("${xplanbox.s3.accessKeyId:#{environment.XPLAN_S3_ACCESS_KEY}}") String accessKeyId, + @Value("${xplanbox.s3.secretKey:#{environment.XPLAN_S3_SECRET_ACCESS_KEY}}") String secretKey) { return new BasicAWSCredentials(accessKeyId, secretKey); } @Bean public StorageCleanUpManager storageCleanUpManager(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { return new S3StorageCleanUpManager(s3Client, bucketName); } @Bean public S3Storage rollbackStorage(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { return new S3Storage(s3Client, bucketName); } diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/listener/S3TransactionListener.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/listener/S3TransactionListener.java index d4fc106de3ac5c742eb1cb2dfe5a76b58f4a5e5a..c5b02d943d59ef0939aa2c80a58cb61f0d3b2698 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/listener/S3TransactionListener.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/storage/s3/listener/S3TransactionListener.java @@ -21,9 +21,9 @@ package de.latlon.xplan.manager.storage.s3.listener; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.storage.s3.S3Object; -import de.latlon.xplan.manager.storage.s3.S3Storage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanDeleteService.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanDeleteService.java index 06b8f29388d3010ed65a7070c8f8f2bc5cd1b005..93282c6cd889dce6c7726f7b92af74560cc19c62 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanDeleteService.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanDeleteService.java @@ -23,7 +23,7 @@ package de.latlon.xplan.manager.transaction.service; import de.latlon.xplan.manager.database.XPlanManagerDao; import de.latlon.xplan.manager.storage.StorageCleanUpManager; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.springframework.context.ApplicationEventPublisher; import jakarta.transaction.Transactional; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanEditService.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanEditService.java index 76a21df86eadf44709e514f278e3fc64eb48004e..1b86828c46a2472ccd60564a21128688fd843475 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanEditService.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanEditService.java @@ -26,7 +26,7 @@ import de.latlon.xplan.manager.document.XPlanDocumentManager; import de.latlon.xplan.manager.edit.EditedArtefacts; import de.latlon.xplan.manager.web.shared.PlanStatus; import de.latlon.xplan.manager.web.shared.XPlan; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.deegree.feature.FeatureCollection; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanInsertService.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanInsertService.java index e2c8846c35553d6a1071e2e591d1b438684e1336..41cc2a7f5cb80043878471fd5d860eae0fa7dd1f 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanInsertService.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/transaction/service/XPlanInsertService.java @@ -29,7 +29,7 @@ import de.latlon.xplan.manager.database.XPlanManagerDao; import de.latlon.xplan.manager.document.XPlanDocumentManager; import de.latlon.xplan.manager.transaction.PlanImportData; import de.latlon.xplan.manager.web.shared.PlanStatus; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/XPlanRasterManager.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/XPlanRasterManager.java index 9b50e576fc1bbc54ee36e73a14248bf4347a83d0..2683ed09064efb8bdae1b121578c25db3e16c597 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/XPlanRasterManager.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/XPlanRasterManager.java @@ -28,7 +28,7 @@ import de.latlon.xplan.manager.storage.StorageEvent; import de.latlon.xplan.manager.web.shared.PlanStatus; import de.latlon.xplan.manager.wmsconfig.raster.config.RasterConfigManager; import de.latlon.xplan.manager.wmsconfig.raster.storage.RasterStorage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplan.manager.workspace.WorkspaceException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/RasterStorage.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/RasterStorage.java index 73d108de24013365de392ebce6cfd0b1dc32a24d..420216fc4521610b6c43046e41c83300bdedfd51 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/RasterStorage.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/RasterStorage.java @@ -21,6 +21,7 @@ package de.latlon.xplan.manager.wmsconfig.raster.storage; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplan.manager.storage.StorageEvent; import java.io.IOException; diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorage.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorage.java index 1a2e939b7a42ae99761c00b194a37db25d107c2d..2414e71ccff2f9fe4a64dd29704c231dccd1c2d1 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorage.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorage.java @@ -23,10 +23,10 @@ package de.latlon.xplan.manager.wmsconfig.raster.storage.s3; import com.amazonaws.services.s3.AmazonS3; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.storage.s3.S3Object; -import de.latlon.xplan.manager.storage.s3.S3Storage; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; import de.latlon.xplan.manager.wmsconfig.raster.storage.RasterStorage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** @@ -45,7 +45,6 @@ public class S3RasterStorage extends S3Storage implements RasterStorage { @SuppressFBWarnings(value = "PATH_TRAVERSAL_IN") public String addRasterFile(int planId, String referenceEntryName, String georefEntryName, XPlanArchiveContentAccess archive, StorageEvent storageEvent) throws StorageException { - createBucketIfNotExists(); String objectKey = insertObject(planId, referenceEntryName, archive); storageEvent.addInsertedKey(objectKey); if (georefEntryName != null) { diff --git a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/config/AmazonS3RasterStorageContext.java b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/config/AmazonS3RasterStorageContext.java index a10f3c74031520c3aff8850bb6ee3b4416932fee..0305d17b6556790876a45480e6e198ae217e63c4 100644 --- a/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/config/AmazonS3RasterStorageContext.java +++ b/xplan-core/xplan-core-manager/src/main/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/config/AmazonS3RasterStorageContext.java @@ -47,7 +47,7 @@ public class AmazonS3RasterStorageContext { @Bean public S3RasterStorage rasterStorage(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { return new S3RasterStorage(s3Client, bucketName); } diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/document/s3/S3DocumentStorageIT.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/document/s3/S3DocumentStorageIT.java index b697547fa85abbc7111c17f190b6666c801e799d..498a1d7f98a9f8acad85b75e4cec604109a2ec0f 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/document/s3/S3DocumentStorageIT.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/document/s3/S3DocumentStorageIT.java @@ -25,7 +25,7 @@ import de.latlon.xplan.commons.archive.XPlanArchiveCreator; import de.latlon.xplan.manager.document.s3.config.AmazonS3DocumentStorageContext; import de.latlon.xplan.manager.storage.StorageEvent; import de.latlon.xplan.manager.storage.s3.config.AmazonS3TestContext; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManagerTest.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManagerTest.java index 95714db597d3b729e3c0e238e98dee4c0b5f583a..07ced67fa9007fb2f39e21df1cb507eaaa7f3de1 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManagerTest.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageCleanUpManagerTest.java @@ -27,7 +27,7 @@ import com.amazonaws.services.s3.model.S3Object; import com.amazonaws.services.s3.model.S3ObjectInputStream; import com.amazonaws.services.s3.model.S3ObjectSummary; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -74,8 +74,8 @@ public class S3StorageCleanUpManagerTest { verify(client).listObjects(BUCKET_NAME, "1_"); verify(client).deleteObject(BUCKET_NAME, "1_test.png"); - ArgumentCaptor<de.latlon.xplan.manager.storage.s3.S3Object> argument = ArgumentCaptor - .forClass(de.latlon.xplan.manager.storage.s3.S3Object.class); + ArgumentCaptor<de.latlon.xplan.commons.s3.S3Object> argument = ArgumentCaptor + .forClass(de.latlon.xplan.commons.s3.S3Object.class); verify(storageEvent).addDeletedKey(argument.capture()); assertThat(argument.getValue().getS3Metadata().getKey(), is("1_test.png")); } diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageTestManual.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageTestManual.java index 8f9cdd86363fb766003ed92f918e267d139e535d..453f98b547f0469d5e7772a94189a142fa68156b 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageTestManual.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/S3StorageTestManual.java @@ -22,9 +22,12 @@ package de.latlon.xplan.manager.storage.s3; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; import de.latlon.xplan.commons.archive.XPlanArchiveCreator; +import de.latlon.xplan.commons.s3.S3Metadata; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; import de.latlon.xplan.manager.storage.s3.config.AmazonS3TestContext; import de.latlon.xplan.manager.storage.s3.config.S3StorageTestContext; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3TestContext.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3TestContext.java index c69428f6459e34a10dd5f5780edb6f6d9f25ada5..e989e049de122e7ade29d3b09ba26c7366b1cb69 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3TestContext.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/AmazonS3TestContext.java @@ -8,12 +8,12 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% @@ -66,8 +66,10 @@ public class AmazonS3TestContext { @Bean @Primary @Profile("mock") - public AmazonS3 s3TestClient(@Value("${s3.region}") String signingRegion, - @Value("${s3.bucketName}") String bucketName, @Value("${s3.endpoint.url}") String url) { + public AmazonS3 s3TestClient(@Value("${xplanbox.s3.region}") String signingRegion, + @Value("${xplanbox.s3.bucketName}") String bucketName, @Value("${xplanbox.s3.endpoint.url}") String url) { + + System.out.println("region: " + signingRegion + ", bucket: " + bucketName + ", url: " + url); AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(url + ":" + port, signingRegion); AmazonS3 client = AmazonS3ClientBuilder.standard() @@ -84,7 +86,6 @@ public class AmazonS3TestContext { s3TestClient.shutdown(); if (s3Mock != null) { s3Mock.stop(); - s3Mock.shutdown(); } } diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/S3StorageTestContext.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/S3StorageTestContext.java index 1a8b23ca61ab835e7936c0c2231224a3c815a030..f65645921dc4856a3fb4722a369bcc339b799610 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/S3StorageTestContext.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/storage/s3/config/S3StorageTestContext.java @@ -21,7 +21,7 @@ package de.latlon.xplan.manager.storage.s3.config; import com.amazonaws.services.s3.AmazonS3; -import de.latlon.xplan.manager.storage.s3.S3Storage; +import de.latlon.xplan.commons.s3.S3Storage; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -34,7 +34,7 @@ public class S3StorageTestContext { @Bean public S3Storage rollbackStorage(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { return new S3Storage(s3Client, bucketName); } diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTest.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTest.java index 5bc5da6cc0bc02e6a5524fb79dd226a012d52a1d..1f8e76df1186bdc1e9850d567eeadad327431b0b 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTest.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTest.java @@ -20,36 +20,25 @@ */ package de.latlon.xplan.manager.wmsconfig.raster.storage.s3; +import static org.hamcrest.CoreMatchers.hasItems; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +import java.io.InputStream; +import java.util.Collections; +import java.util.List; + import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.model.ObjectListing; -import com.amazonaws.services.s3.model.ObjectMetadata; -import com.amazonaws.services.s3.model.S3Object; -import com.amazonaws.services.s3.model.S3ObjectInputStream; -import com.amazonaws.services.s3.model.S3ObjectSummary; +import com.amazonaws.services.s3.model.*; import de.latlon.xplan.commons.archive.ArchiveEntry; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; import org.junit.Test; import org.mockito.ArgumentCaptor; -import java.io.IOException; -import java.io.InputStream; -import java.util.Collections; -import java.util.List; - -import static org.hamcrest.CoreMatchers.hasItems; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ @@ -58,7 +47,7 @@ public class S3RasterStorageTest { private static final String BUCKET_NAME = "xplanbox"; @Test - public void testAddRasterFile() throws IOException, StorageException { + public void testAddRasterFile() throws StorageException { AmazonS3 client = spy(AmazonS3.class); S3RasterStorage s3RasterStorage = new S3RasterStorage(client, BUCKET_NAME); XPlanArchiveContentAccess archive = mockArchive(); @@ -67,7 +56,7 @@ public class S3RasterStorageTest { String key = s3RasterStorage.addRasterFile(1, "test.png", "test.png.aux.xml", archive, storageEvent); ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); assertThat(key, is("1_test.png")); - verify(client).doesBucketExistV2(eq(BUCKET_NAME)); + verify(client, times(2)).doesBucketExistV2(eq(BUCKET_NAME)); verify(client, times(2)).putObject(eq(BUCKET_NAME), captor.capture(), nullable(InputStream.class), any(ObjectMetadata.class)); assertThat(captor.getAllValues(), hasItems("1_test.png", "1_test.png.aux.xml")); @@ -99,8 +88,8 @@ public class S3RasterStorageTest { s3RasterStorage.deleteRasterFile(1, "test.png", storageEvent); verify(client).deleteObject(BUCKET_NAME, "1_test.png"); - ArgumentCaptor<de.latlon.xplan.manager.storage.s3.S3Object> argument = ArgumentCaptor - .forClass(de.latlon.xplan.manager.storage.s3.S3Object.class); + ArgumentCaptor<de.latlon.xplan.commons.s3.S3Object> argument = ArgumentCaptor + .forClass(de.latlon.xplan.commons.s3.S3Object.class); verify(storageEvent).addDeletedKey(argument.capture()); assertThat(argument.getValue().getS3Metadata().getKey(), is("1_test.png")); } diff --git a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTestManual.java b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTestManual.java index 90706d7dc0beb7f33d5e980c0d83492baed52511..fb0fb651cb5fbc5cad10061a470deac4ae43899e 100644 --- a/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTestManual.java +++ b/xplan-core/xplan-core-manager/src/test/java/de/latlon/xplan/manager/wmsconfig/raster/storage/s3/S3RasterStorageTestManual.java @@ -23,9 +23,9 @@ package de.latlon.xplan.manager.wmsconfig.raster.storage.s3; import de.latlon.xplan.commons.archive.XPlanArchiveContentAccess; import de.latlon.xplan.commons.archive.XPlanArchiveCreator; import de.latlon.xplan.manager.storage.StorageEvent; -import de.latlon.xplan.manager.storage.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Object; import de.latlon.xplan.manager.storage.s3.config.AmazonS3TestContext; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplan.manager.wmsconfig.raster.storage.s3.config.AmazonS3RasterStorageContext; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/xplan-core/xplan-core-manager/src/test/resources/s3Mock.properties b/xplan-core/xplan-core-manager/src/test/resources/s3Mock.properties index 3edcb5508b1553a9f5e29b5367824dbbef4b8cf3..419aaa99b2e994a43c482d2d97f2638111f7ac30 100644 --- a/xplan-core/xplan-core-manager/src/test/resources/s3Mock.properties +++ b/xplan-core/xplan-core-manager/src/test/resources/s3Mock.properties @@ -18,8 +18,8 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # #L% ### -s3.accessKeyId= -s3.secretKey= -s3.bucketName=latlonxpb -s3.region=eu-central-1 -s3.endpoint.url=http://localhost +xplanbox.s3.accessKeyId= +xplanbox.s3.secretKey= +xplanbox.s3.bucketName=latlonxpb +xplanbox.s3.region=eu-central-1 +xplanbox.s3.endpoint.url=http://localhost diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestNotifier.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/EventSender.java similarity index 87% rename from xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestNotifier.java rename to xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/EventSender.java index e88f76163befb75cf05426a16cc39840270f17c2..5feb9fe096a4e816d141d39dbb84dbc15c476174 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestNotifier.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/EventSender.java @@ -8,12 +8,12 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% @@ -22,10 +22,12 @@ package de.latlon.core.validator.events; /** * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> - * @since 7.2 + * @since 8.0 */ -public interface ValidationRequestNotifier { +public interface EventSender { - void sendEvent(ValidationRequestedEvent event); + void sendEvent(XPlanboxInternalEvent e); + + void sendPublicEvent(XPlanboxPublicEvent e); } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitConfig.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitConfig.java index 1c15bcc22f9c11739d7465af91ecd44cbcb15fbd..a89b1713b631aed418a11e62828293c57aaf6746 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitConfig.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitConfig.java @@ -8,25 +8,30 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.core.validator.events; -import org.springframework.amqp.core.Binding; -import org.springframework.amqp.core.BindingBuilder; -import org.springframework.amqp.core.Queue; -import org.springframework.amqp.core.TopicExchange; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; /** @@ -34,33 +39,47 @@ import org.springframework.context.annotation.Profile; * @since 7.2 */ @Configuration -@Profile("rabbit") +@Import({ ValidationTaskQueueConfig.class }) +@Profile("!test") public class RabbitConfig { - @Bean - RabbitSettings rabbitSettings() { - return new RabbitSettings(); - } + @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Qualifier + @interface InternalFanout { - @Bean - Queue queue(RabbitSettings rabbitSettings) { - return new Queue(rabbitSettings.getInternalQueueName(), true); } - @Bean - TopicExchange exchange(RabbitSettings rabbitSettings) { - return new TopicExchange(rabbitSettings.getInternalExchangeName()); + @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) + @Retention(RetentionPolicy.RUNTIME) + @Qualifier + @interface PublicFanout { + } @Bean - Binding binding(Queue queue, TopicExchange exchange) { - return BindingBuilder.bind(queue).to(exchange).with("foo.bar.#"); + @ConditionalOnMissingBean(RabbitSettings.class) + RabbitSettings rabbitSettings() { + return new RabbitSettings(); } @Bean + @ConditionalOnMissingBean(Jackson2JsonMessageConverter.class) public Jackson2JsonMessageConverter jsonMessageConverter() { Jackson2JsonMessageConverter jsonConverter = new Jackson2JsonMessageConverter(); return jsonConverter; } + @Bean + @InternalFanout + public FanoutExchange internalFanoutExchange(RabbitSettings rabbitSettings) { + return new FanoutExchange(rabbitSettings.getInternalFanoutName()); + } + + @Bean + @PublicFanout + public FanoutExchange publicFanoutExchange(RabbitSettings rabbitSettings) { + return new FanoutExchange(rabbitSettings.getPublicFanoutName()); + } + } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEmitterConfig.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEmitterConfig.java index b6440f7f6b207e5aea3390e75f4d9adf4e15353b..d511e5bc048a3de49eef54b5c42d2ba99663eb56 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEmitterConfig.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEmitterConfig.java @@ -8,57 +8,42 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.core.validator.events; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.core.FanoutExchange; import org.springframework.amqp.rabbit.core.RabbitTemplate; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; -@Configuration -@Import({ RabbitConfig.class }) +import de.latlon.core.validator.events.RabbitConfig.InternalFanout; +import de.latlon.core.validator.events.RabbitConfig.PublicFanout; + /** * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> * @since 7.2 */ +@Configuration +@Import({ RabbitConfig.class }) +@Profile("!test") public class RabbitEmitterConfig { @Bean - @Profile("rabbit") - public RabbitTemplate rabbitTemplate(Jackson2JsonMessageConverter msgConverter, ConnectionFactory connectionFactory, + EventSender validationRequestNotifier(RabbitTemplate rabbitTemplate, + @InternalFanout FanoutExchange internalFanoutExchange, @PublicFanout FanoutExchange publicFanoutExchange, RabbitSettings rabbitSettings) { - RabbitTemplate template = new RabbitTemplate(); - template.setMessageConverter(msgConverter); - template.setConnectionFactory(connectionFactory); - template.setExchange(rabbitSettings.getInternalExchangeName()); - - return template; - } - - @Bean - @Profile("rabbit") - ValidationRequestNotifier validationRequestNotifier(RabbitTemplate rabbitTemplate) { - return new RabbitValidationRequestNotifier(rabbitTemplate); - } - - @Bean - @Profile("!rabbit") - ValidationRequestNotifier noOpValidationRequestNotifier(RabbitTemplate rabbitTemplate) { - return (ValidationRequestedEvent event) -> { - }; + return new RabbitEventSender(rabbitTemplate, internalFanoutExchange, publicFanoutExchange, rabbitSettings); } } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEventSender.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEventSender.java new file mode 100644 index 0000000000000000000000000000000000000000..82ca44ac29430cc8305d60c856a82b6530345741 --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitEventSender.java @@ -0,0 +1,81 @@ +/*- + * #%L + * xplan-core-validator-events - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.core.validator.events; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.amqp.rabbit.core.RabbitTemplate; + +/** + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 7.2 + */ +class RabbitEventSender implements EventSender { + + private static final Logger LOG = getLogger(RabbitEventSender.class); + + private final RabbitTemplate rabbitTemplate; + + private final String publicFanoutExchangeName; + + private final String internalFanoutExchangeName; + + private final Map<Class<?>, String> taskClass2QueueName = new HashMap<>(); + + public RabbitEventSender(RabbitTemplate rabbitTemplate, FanoutExchange internalFanoutExchange, + FanoutExchange publicFanoutExchange, RabbitSettings rabbitSettings) { + this.internalFanoutExchangeName = internalFanoutExchange.getName(); + this.publicFanoutExchangeName = publicFanoutExchange.getName(); + this.rabbitTemplate = rabbitTemplate; + + taskClass2QueueName.put(ValidationRequestedEvent.class, rabbitSettings.getInternalValidationWorkQueueName()); + } + + public void sendEvent(XPlanboxInternalEvent event) { + if (event instanceof XPlanboxInternalTaskEvent) { + String workQueueName = taskClass2QueueName.get(event.getClass()); + if (workQueueName == null) { + LOG.error("Ignoring task: no queue configured to send event to (" + event + ")"); + } + else { + LOG.info("Sending task event: " + event); + rabbitTemplate.convertAndSend(workQueueName, event); + } + } + else { + LOG.info("Sending fanout event: " + event); + rabbitTemplate.convertAndSend(internalFanoutExchangeName, "fanout." + event.getClass().getSimpleName(), + event); + } + } + + @Override + public void sendPublicEvent(XPlanboxPublicEvent event) { + LOG.info("Sending fanout public event: " + event); + rabbitTemplate.convertAndSend(publicFanoutExchangeName, "xplanbox", event); + } + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitReceiverConfig.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitReceiverConfig.java index 3ca256f4804e5ca4c9075f6fd32a716f1f420f87..0fb4ed1f400614480f88357946a87ebc89dcf774 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitReceiverConfig.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitReceiverConfig.java @@ -8,58 +8,49 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.core.validator.events; -import org.springframework.amqp.rabbit.connection.ConnectionFactory; -import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; -import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter; -import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.core.AnonymousQueue; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.FanoutExchange; +import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Profile; +import de.latlon.core.validator.events.RabbitConfig.InternalFanout; + /** * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> * @since 7.2 */ @Configuration @Import({ RabbitConfig.class }) +@Profile("!test") public class RabbitReceiverConfig { @Bean - @Profile("rabbit") - SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, - MessageListenerAdapter listenerAdapter, RabbitSettings rabbitSettings) { - SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); - container.setConnectionFactory(connectionFactory); - container.setQueueNames(rabbitSettings.getInternalQueueName()); - container.setMessageListener(listenerAdapter); - return container; - } - - @Bean - @Profile("rabbit") - MessageListenerAdapter listenerAdapter(ValidationMessageReceiver receiver, - Jackson2JsonMessageConverter msgConverter) { - MessageListenerAdapter listenerAdapter = new MessageListenerAdapter(receiver, "receiveMessage"); - listenerAdapter.setMessageConverter(msgConverter); - return listenerAdapter; + @InternalFanout + public Queue internalFanoutQueue() { + return new AnonymousQueue(); } @Bean - ValidationMessageReceiver validationMessageReceiver() { - return new ValidationMessageReceiver(); + @InternalFanout + public Binding internalFanoutBinding(@InternalFanout FanoutExchange fanout, @InternalFanout Queue fanoutQueue) { + return BindingBuilder.bind(fanoutQueue).to(fanout); } } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitSettings.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitSettings.java index cc9e9582188de44d236f65abf4e4bf0115365176..06f34440aafd66233e17ad7b1e4555aa7923f05a 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitSettings.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitSettings.java @@ -8,12 +8,12 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% @@ -28,19 +28,37 @@ import org.springframework.beans.factory.annotation.Value; */ class RabbitSettings { - private static final String topicExchangeName = "-internal-exchange"; + private static final String suffixInternalFanoutName = "-internal-fanout"; - private static final String queueName = "-internal-queue"; + private static final String suffixPublicFanoutName = "-public-fanout"; + + private static final String suffixTopicExchangeName = "-internal-exchange"; + + private static final String suffixValidationWorkQueueName = "-internal-validation-queue"; @Value("${xplanbox.rabbitmq.internal.prefix}") private String internalPrefix; - public String getInternalQueueName() { - return internalPrefix + queueName; + @Value("${xplanbox.rabbitmq.public.fanout}") + private String publicFanoutName; + + public String getInternalValidationWorkQueueName() { + return internalPrefix + suffixValidationWorkQueueName; } public String getInternalExchangeName() { - return internalPrefix + topicExchangeName; + return internalPrefix + suffixTopicExchangeName; + } + + public String getInternalFanoutName() { + return internalPrefix + suffixInternalFanoutName; + } + + public String getPublicFanoutName() { + if (publicFanoutName != null && !publicFanoutName.isBlank()) { + return publicFanoutName; + } + return internalPrefix + suffixPublicFanoutName; } } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationFinishedEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationFinishedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..b4cb999c16b497506263850be50aa512353f14da --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationFinishedEvent.java @@ -0,0 +1,62 @@ +/*- + * #%L + * xplan-core-validator-events - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.core.validator.events; + +/** + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public class ValidationFinishedEvent extends XPlanboxInternalFanoutEvent { + + private String uuid; + + private ValidationFinishedStatus validationFinishedStatus; + + public enum ValidationFinishedStatus { + + SUCCEEDED, FAILED + + } + + public ValidationFinishedEvent() { + + } + + public ValidationFinishedEvent(String uuid, ValidationFinishedStatus validationFinishedStatus) { + this.uuid = uuid; + this.validationFinishedStatus = validationFinishedStatus; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + uuid + ", validationFinishedStatus: " + validationFinishedStatus + + ")"; + } + + public String getUuid() { + return uuid; + } + + public ValidationFinishedStatus getValidationFinishedStatus() { + return validationFinishedStatus; + } + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestedEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestedEvent.java index 2caec823e6fc1f277c5a97911fc0bc530c15190b..0a5a90c09d22fd11c033923095e9ab1c5a55f2a6 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestedEvent.java +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationRequestedEvent.java @@ -8,12 +8,12 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% @@ -26,25 +26,89 @@ import de.latlon.xplan.validator.web.shared.ValidationSettings; * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> * @since 7.2 */ -public class ValidationRequestedEvent { +public class ValidationRequestedEvent extends XPlanboxInternalTaskEvent { + + public enum MediaType { + + ZIP("application/zip", ".zip"), PDF("application/pdf", ".pdf"), JSON("application/json", ".json"), + XML("application/xml", ".xml"); + + private final String mimeType; + + private final String fileExtension; + + MediaType(String mimeType, String fileExtension) { + this.mimeType = mimeType; + this.fileExtension = fileExtension; + } + + public String getFileExtension() { + return fileExtension; + } + + public String getMimeType() { + return mimeType; + } + + } + + public enum OriginFile { + + ZIP, GML + + } private ValidationSettings settings; + private String uuid; + + private MediaType requestedMediaType; + + private String xFileName; + + private OriginFile originFile; + public ValidationRequestedEvent() { } - public ValidationRequestedEvent(ValidationSettings settings) { + public ValidationRequestedEvent(ValidationSettings settings, String xFileName, MediaType requestedMediaType, + OriginFile originFile) { + this(null, settings, xFileName, requestedMediaType, originFile); + } + + public ValidationRequestedEvent(String uuid, ValidationSettings settings, String xFileName, + MediaType requestedMediaType, OriginFile originFile) { + this.uuid = uuid; this.settings = settings; + this.xFileName = xFileName; + this.requestedMediaType = requestedMediaType; + this.originFile = originFile; + } + + public OriginFile getOriginFile() { + return originFile; + } + + public MediaType getRequestedMediaType() { + return requestedMediaType; } public ValidationSettings getSettings() { return settings; } + public String getUuid() { + return uuid; + } + + public String getxFileName() { + return xFileName; + } + @Override public String toString() { - return settings.toString(); + return "settings: " + settings + ", uuid: " + uuid; } } diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationTaskQueueConfig.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationTaskQueueConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..4546be531332de7fb2e9637cf4292b1fb0f28baa --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationTaskQueueConfig.java @@ -0,0 +1,13 @@ +package de.latlon.core.validator.events; + +import org.springframework.amqp.core.Queue; +import org.springframework.context.annotation.Bean; + +public class ValidationTaskQueueConfig { + + @Bean + public Queue validationTaskQueue(RabbitSettings rabbitSettings) { + return new Queue(rabbitSettings.getInternalValidationWorkQueueName(), true); + } + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..b162e1b17eb82dee295db3e5e8d572f22b21503c --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalEvent.java @@ -0,0 +1,11 @@ +package de.latlon.core.validator.events; + +/** + * Marker interface for internal events. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public sealed interface XPlanboxInternalEvent permits XPlanboxInternalFanoutEvent, XPlanboxInternalTaskEvent { + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalFanoutEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalFanoutEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..a05b1dc6a3fad64e4cde530fe1a910b7b5ace718 --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalFanoutEvent.java @@ -0,0 +1,11 @@ +package de.latlon.core.validator.events; + +/** + * Base class for internal fanout events. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public non-sealed class XPlanboxInternalFanoutEvent implements XPlanboxInternalEvent { + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalTaskEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalTaskEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..001926cde69f8cb84c6baedf53d71026a806b404 --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxInternalTaskEvent.java @@ -0,0 +1,11 @@ +package de.latlon.core.validator.events; + +/** + * Base class for internal task events. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public non-sealed class XPlanboxInternalTaskEvent implements XPlanboxInternalEvent { + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxPublicEvent.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxPublicEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..bed8dedce39e61ba8f597cea2ed9f89015aa54b5 --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/XPlanboxPublicEvent.java @@ -0,0 +1,11 @@ +package de.latlon.core.validator.events; + +/** + * Marker interface for public events. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public interface XPlanboxPublicEvent { + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1Event.java b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1Event.java new file mode 100644 index 0000000000000000000000000000000000000000..04584a233d210d57d77682c2e51f104a0c376571 --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1Event.java @@ -0,0 +1,78 @@ +package de.latlon.core.validator.events.v1; + +import java.util.Objects; + +import de.latlon.core.validator.events.XPlanboxPublicEvent; + +/** + * V1 public event. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public class XPlanboxPublicV1Event implements XPlanboxPublicEvent { + + public static enum EventType { + + VALIDATION_START, VALIDATION_FINISHED + + } + + private String apiVersion = "1.0"; + + private EventType eventType; + + private String uuid; + + private String message; + + public XPlanboxPublicV1Event() { + + } + + public XPlanboxPublicV1Event(EventType eventType, String uuid, String message) { + this.eventType = eventType; + this.uuid = uuid; + this.message = message; + } + + public String getApiVersion() { + return apiVersion; + } + + public EventType getEventType() { + return eventType; + } + + public String getMessage() { + return message; + } + + public String getUuid() { + return uuid; + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, eventType, message, uuid); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + XPlanboxPublicV1Event other = (XPlanboxPublicV1Event) obj; + return Objects.equals(apiVersion, other.apiVersion) && eventType == other.eventType + && Objects.equals(message, other.message) && Objects.equals(uuid, other.uuid); + } + + @Override + public String toString() { + return "XPlanboxPublicV1Event(" + eventType + ", " + uuid + ", " + message + ")"; + } + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitValidationRequestNotifier.java b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationFinishedEventTest.java similarity index 57% rename from xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitValidationRequestNotifier.java rename to xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationFinishedEventTest.java index 192a545be9a09231a84a04b350242f3e78f8aa55..aa0e0a6818c1b61bc9b84a196ec51f1ff25f93ce 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/RabbitValidationRequestNotifier.java +++ b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationFinishedEventTest.java @@ -20,28 +20,25 @@ */ package de.latlon.core.validator.events; -import static org.slf4j.LoggerFactory.getLogger; +import static de.latlon.core.validator.events.ValidationFinishedEvent.ValidationFinishedStatus.SUCCEEDED; +import static org.assertj.core.api.Assertions.assertThat; -import org.slf4j.Logger; -import org.springframework.amqp.rabbit.core.RabbitTemplate; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; -/** - * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> - * @since 7.2 - */ -class RabbitValidationRequestNotifier implements ValidationRequestNotifier { +class ValidationFinishedEventTest { - private static final Logger LOG = getLogger(RabbitValidationRequestNotifier.class); + @Test + void jsonSerializeAndDeserialize() throws Exception { + ValidationFinishedEvent event = new ValidationFinishedEvent("my-uuid", SUCCEEDED); - private RabbitTemplate rabbitTemplate; + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(event); - public RabbitValidationRequestNotifier(RabbitTemplate rabbitTemplate) { - this.rabbitTemplate = rabbitTemplate; - } + ValidationFinishedEvent valueFromJson = mapper.readValue(json, ValidationFinishedEvent.class); + + assertThat(valueFromJson).usingRecursiveComparison().isEqualTo(event); - public void sendEvent(ValidationRequestedEvent event) { - LOG.info("Sending message: " + event); - rabbitTemplate.convertAndSend("foo.bar.baz", event); } } diff --git a/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationRequestedEventTest.java b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationRequestedEventTest.java index cb7db7d09d76f662bcd5ea2485557a761cb8f201..f00c4d20ee52e0b0df3d0a4a379030c0b7672998 100644 --- a/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationRequestedEventTest.java +++ b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/ValidationRequestedEventTest.java @@ -29,6 +29,8 @@ import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; +import de.latlon.core.validator.events.ValidationRequestedEvent.MediaType; +import de.latlon.core.validator.events.ValidationRequestedEvent.OriginFile; import de.latlon.xplan.validator.web.shared.ValidationOption; import de.latlon.xplan.validator.web.shared.ValidationSettings; import de.latlon.xplan.validator.web.shared.ValidationType; @@ -42,7 +44,8 @@ class ValidationRequestedEventTest { List<ValidationType> validationTypes = Arrays.asList(ValidationType.GEOMETRIC, ValidationType.SYNTACTIC, ValidationType.SEMANTIC); ValidationSettings settings = new ValidationSettings("validationName", validationTypes, extendedOptions); - ValidationRequestedEvent event = new ValidationRequestedEvent(settings); + ValidationRequestedEvent event = new ValidationRequestedEvent(settings, "xFileName", MediaType.PDF, + OriginFile.GML); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(event); diff --git a/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1EventTest.java b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1EventTest.java new file mode 100644 index 0000000000000000000000000000000000000000..585943e68deab7cc615d3ff84cfd4e21a5680f8a --- /dev/null +++ b/xplan-core/xplan-core-validator-events/src/test/java/de/latlon/core/validator/events/v1/XPlanboxPublicV1EventTest.java @@ -0,0 +1,39 @@ +package de.latlon.core.validator.events.v1; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.latlon.core.validator.events.v1.XPlanboxPublicV1Event.EventType; + +class XPlanboxPublicV1EventTest { + + @Test + void jsonSerializeValidationStart() throws Exception { + XPlanboxPublicV1Event event = new XPlanboxPublicV1Event(EventType.VALIDATION_START, "uuid1", + "start validation"); + + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(event); + + String expected = "{\"apiVersion\":\"1.0\",\"eventType\":\"VALIDATION_START\",\"uuid\":\"uuid1\",\"message\":\"start validation\"}"; + + assertThat(json).isEqualTo(expected); + } + + @Test + void jsonSerializeValidationFinished() throws Exception { + XPlanboxPublicV1Event event = new XPlanboxPublicV1Event(EventType.VALIDATION_FINISHED, "uuid1", + "end validation"); + + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(event); + + String expected = "{\"apiVersion\":\"1.0\",\"eventType\":\"VALIDATION_FINISHED\",\"uuid\":\"uuid1\",\"message\":\"end validation\"}"; + + assertThat(json).isEqualTo(expected); + } + +} diff --git a/xplan-docker/xplan-docker-volume-init/setupVolumes.sh b/xplan-docker/xplan-docker-volume-init/setupVolumes.sh index 8951de68c448238d2eebfa06dd610d158234d08a..77a9ef20b4b027c434179d3c6712a321624e834f 100755 --- a/xplan-docker/xplan-docker-volume-init/setupVolumes.sh +++ b/xplan-docker/xplan-docker-volume-init/setupVolumes.sh @@ -90,8 +90,6 @@ XPLAN_SERVICES_METADATA_URL="${XPLAN_SERVICES_METADATA_URL}" XPLAN_INIT_INSPIREPLU="${XPLAN_INIT_INSPIREPLU:-disabled}" -XPLAN_S3_PUBLIC_URL="${XPLAN_S3_PUBLIC_URL}" - ############################# # Update content of volumes # ############################# diff --git a/xplan-documentation/xplan-betriebshandbuch/src/main/asciidoc/s3storage.adoc b/xplan-documentation/xplan-betriebshandbuch/src/main/asciidoc/s3storage.adoc index 01a8f16d377ceca7f21569a41f3500fe2f70e0c6..504fe7be2bf9a894fcb793ed93bc8c671d395a3a 100644 --- a/xplan-documentation/xplan-betriebshandbuch/src/main/asciidoc/s3storage.adoc +++ b/xplan-documentation/xplan-betriebshandbuch/src/main/asciidoc/s3storage.adoc @@ -42,13 +42,11 @@ XPLAN_S3_SECRET_ACCESS_KEY=MY_SECRET_KEY <2> XPLAN_S3_BUCKET_NAME=mydocumentbucket <3> XPLAN_S3_REGION=eu-central-1 <4> XPLAN_S3_ENDPOINT=s3.eu-central-1.amazonaws.com <5> -XPLAN_S3_PUBLIC_URL=http://mydocumentbucket.s3.eu-central-1.amazonaws.com <6> ---- <1> Hier muss der S3 Accesskey gesetzt werden <2> Hier muss der S3 Secretkey gesetzt werden <3> Hier muss der Name des S3 Bucket für die Begleitdokumente gesetzt werden, hier als Beispiel `mydocumentbucket` <4> Hier muss die S3 Region gesetzt werden <5> Hier muss die S3 Endpoint URL gesetzt werden -<6> Hier kann die öffentliche S3 Endpoint URL gesetzt werden (optional), setzt sich aus (3), (4) und (5) zusammen. NOTE: Für die Aktivierung des S3 Objektspeichers für die Ablage von Begleitdokumenten muss zusätzlich auch die Umgebungsvariable `spring_profiles_active=s3doc` gesetzt werden. diff --git a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/config/S3DocumentContext.java b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/config/S3DocumentContext.java index d73f9fc46dd935c55db19be141a77774be2d500e..cc43ddb716a46aa080415ac88d138a103b09af87 100644 --- a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/config/S3DocumentContext.java +++ b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/config/S3DocumentContext.java @@ -21,7 +21,7 @@ package de.latlon.xplanbox.api.dokumente.config; import com.amazonaws.services.s3.AmazonS3; -import de.latlon.xplan.manager.storage.s3.S3Storage; +import de.latlon.xplan.commons.s3.S3Storage; import de.latlon.xplan.manager.storage.s3.config.AmazonS3Context; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Value; @@ -45,7 +45,7 @@ public class S3DocumentContext { @Bean public S3Storage documentStorage(AmazonS3 s3Client, - @Value("${s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { + @Value("${xplanbox.s3.bucketName:#{environment.XPLAN_S3_BUCKET_NAME}}") String bucketName) { LOG.info("Instantiate S3Storage to manage documents"); return new S3Storage(s3Client, bucketName); } diff --git a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/handler/DocumentHandler.java b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/handler/DocumentHandler.java index 03a8024602b621015c6695709e68d28155590fb9..12adba4616ba6068baa393482785e8ea165dc6c7 100644 --- a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/handler/DocumentHandler.java +++ b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/handler/DocumentHandler.java @@ -21,8 +21,7 @@ package de.latlon.xplanbox.api.dokumente.handler; import de.latlon.xplan.core.manager.db.repository.PlanRepository; -import de.latlon.xplan.manager.database.XPlanDao; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplanbox.api.commons.exception.InvalidPlanId; import de.latlon.xplanbox.api.commons.exception.InvalidPlanIdSyntax; import de.latlon.xplanbox.api.dokumente.exception.InvalidDocument; diff --git a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/DocumentService.java b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/DocumentService.java index 83ad260f0d5a962fc5e6ab729f9ecd0a46f37aa9..1b762bfc9e11cac8fbab5acaf31dc6ae7642244f 100644 --- a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/DocumentService.java +++ b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/DocumentService.java @@ -20,7 +20,7 @@ */ package de.latlon.xplanbox.api.dokumente.service; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplanbox.api.dokumente.exception.InvalidDocument; import de.latlon.xplanbox.api.dokumente.v1.model.Document; diff --git a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/s3/S3DocumentService.java b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/s3/S3DocumentService.java index 1b7f89cddcadf6e66c57b1d74c0c1da9bfe09209..ee34897a6cb4167eb23212a8685ae1edc7166d96 100644 --- a/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/s3/S3DocumentService.java +++ b/xplan-dokumente/xplan-dokumente-api/src/main/java/de/latlon/xplanbox/api/dokumente/service/s3/S3DocumentService.java @@ -21,10 +21,10 @@ package de.latlon.xplanbox.api.dokumente.service.s3; import com.amazonaws.services.s3.model.S3ObjectSummary; -import de.latlon.xplan.manager.storage.s3.S3Metadata; -import de.latlon.xplan.manager.storage.s3.S3Object; -import de.latlon.xplan.manager.storage.s3.S3Storage; -import de.latlon.xplan.manager.wmsconfig.raster.storage.StorageException; +import de.latlon.xplan.commons.s3.S3Metadata; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; import de.latlon.xplanbox.api.dokumente.exception.InvalidDocument; import de.latlon.xplanbox.api.dokumente.service.DocumentHeader; import de.latlon.xplanbox.api.dokumente.service.DocumentHeaderWithStream; diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/PlanInfoBuilder.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/PlanInfoBuilder.java index 729ded25dc02bd9c4812bd7009ebc3742ec62492..2d9957d1e23d414bcbfbe6cff1cf3f6c121910b1 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/PlanInfoBuilder.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/PlanInfoBuilder.java @@ -20,6 +20,18 @@ */ package de.latlon.xplanbox.api.manager; +import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.ALTERNATE; +import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.PLANWERKWMS; +import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.SELF; +import static org.slf4j.LoggerFactory.getLogger; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + import de.latlon.xplan.manager.web.shared.PlanStatus; import de.latlon.xplan.manager.web.shared.XPlan; import de.latlon.xplan.validator.web.shared.XPlanEnvelope; @@ -35,18 +47,6 @@ import de.latlon.xplanbox.api.manager.v1.model.PlanStatusEnum; import org.apache.http.client.utils.URIBuilder; import org.slf4j.Logger; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.ALTERNATE; -import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.PLANWERKWMS; -import static de.latlon.xplanbox.api.manager.v1.model.Link.RelEnum.SELF; -import static org.slf4j.LoggerFactory.getLogger; - /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ @@ -120,11 +120,14 @@ public class PlanInfoBuilder { List<Link> links = new ArrayList<>(); URI selfRef = createSelfRef(); if (selfRef != null) { - Link selfLink = new Link().href(selfRef).rel(SELF).type(selfMediaType).title(xPlan.getName()); + Link selfLink = (Link) new Link().rel(SELF).href(selfRef).type(selfMediaType).title(xPlan.getName()); links.add(selfLink); alternateMediaTypes.forEach(mediaType -> { - Link alternateLink = new Link().href(selfRef).rel(ALTERNATE).type(mediaType).title(xPlan.getName()); + Link alternateLink = (Link) new Link().rel(ALTERNATE) + .href(selfRef) + .type(mediaType) + .title(xPlan.getName()); links.add(alternateLink); }); } @@ -170,7 +173,7 @@ public class PlanInfoBuilder { pathSegments.add(xPlan.getName().replace("/", "")); uriBuilder.setPathSegments(pathSegments); URI planwerkWmsRef = uriBuilder.build(); - Link planwerkWmsLink = new Link().href(planwerkWmsRef).rel(PLANWERKWMS).title(xPlan.getName()); + Link planwerkWmsLink = (Link) new Link().rel(PLANWERKWMS).href(planwerkWmsRef).title(xPlan.getName()); return planwerkWmsLink; } catch (URISyntaxException e) { diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/model/Link.java b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/model/Link.java index f787e7788bb4c7d440cdda1b988937fa88c88db0..82645548560ae54db9bbf54a35399e04113b704a 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/model/Link.java +++ b/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/v1/model/Link.java @@ -20,18 +20,17 @@ */ package de.latlon.xplanbox.api.manager.v1.model; +import java.util.Objects; + import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; +import de.latlon.xplanbox.api.commons.v1.model.AbstractLink; import io.swagger.v3.oas.annotations.media.Schema; - import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; -import java.net.URI; -import java.util.Objects; /** * Datatype for Link. A Link to a resource related to the resource such as XPlanWerkWMS or @@ -44,14 +43,11 @@ import java.util.Objects; date = "2020-08-28T13:42:47.160+02:00[Europe/Berlin]") @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) -public class Link { - - private @Valid URI href; +public class Link extends AbstractLink { public enum RelEnum { - SELF(String.valueOf("self")), ALTERNATE(String.valueOf("alternate")), - PLANWERKWMS(String.valueOf("planwerkwms")); + SELF("self"), ALTERNATE("alternate"), PLANWERKWMS("planwerkwms"); private String value; @@ -83,36 +79,6 @@ public class Link { private @Valid RelEnum rel; - private @Valid String type; - - private @Valid String hreflang; - - private @Valid String title; - - private @Valid Integer length; - - /** - * - **/ - public Link href(URI href) { - this.href = href; - return this; - } - - @Schema(example = "https://xplanbox.lat-lon.de/xmanager/api/v1/plan/123", required = true) - @JsonProperty("href") - @NotNull - public URI getHref() { - return href; - } - - public void setHref(URI href) { - this.href = href; - } - - /** - * - **/ public Link rel(RelEnum rel) { this.rel = rel; return this; @@ -128,95 +94,21 @@ public class Link { this.rel = rel; } - /** - * - **/ - public Link type(String type) { - this.type = type; - return this; - } - - @Schema(example = "application/json") - @JsonProperty("type") - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - /** - * - **/ - public Link hreflang(String hreflang) { - this.hreflang = hreflang; - return this; - } - - @Schema(example = "en") - @JsonProperty("hreflang") - public String getHreflang() { - return hreflang; - } - - public void setHreflang(String hreflang) { - this.hreflang = hreflang; - } - - /** - * - **/ - public Link title(String title) { - this.title = title; - return this; - } - - @Schema(example = "Othmarschen 3, Hamburg") - @JsonProperty("title") - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - /** - * - **/ - public Link length(Integer length) { - this.length = length; - return this; - } - - @Schema - @JsonProperty("length") - public Integer getLength() { - return length; - } - - public void setLength(Integer length) { - this.length = length; - } - @Override - public boolean equals(java.lang.Object o) { - if (this == o) { + public boolean equals(Object o) { + if (this == o) return true; - } - if (o == null || getClass() != o.getClass()) { + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) return false; - } Link link = (Link) o; - return Objects.equals(this.href, link.href) && Objects.equals(this.rel, link.rel) - && Objects.equals(this.type, link.type) && Objects.equals(this.hreflang, link.hreflang) - && Objects.equals(this.title, link.title) && Objects.equals(this.length, link.length); + return rel == link.rel; } @Override public int hashCode() { - return Objects.hash(href, rel, type, hreflang, title, length); + return Objects.hash(super.hashCode(), rel); } @Override @@ -224,25 +116,14 @@ public class Link { StringBuilder sb = new StringBuilder(); sb.append("class Link {\n"); - sb.append(" href: ").append(toIndentedString(href)).append("\n"); + sb.append(" href: ").append(toIndentedString(getHref())).append("\n"); sb.append(" rel: ").append(toIndentedString(rel)).append("\n"); - sb.append(" type: ").append(toIndentedString(type)).append("\n"); - sb.append(" hreflang: ").append(toIndentedString(hreflang)).append("\n"); - sb.append(" title: ").append(toIndentedString(title)).append("\n"); - sb.append(" length: ").append(toIndentedString(length)).append("\n"); + sb.append(" type: ").append(toIndentedString(getType())).append("\n"); + sb.append(" hreflang: ").append(toIndentedString(getHreflang())).append("\n"); + sb.append(" title: ").append(toIndentedString(getTitle())).append("\n"); + sb.append(" length: ").append(toIndentedString(getLength())).append("\n"); sb.append("}"); return sb.toString(); } - /** - * Convert the given object to string with each line indented by 4 spaces (except the - * first line). - */ - private String toIndentedString(java.lang.Object o) { - if (o == null) { - return "null"; - } - return o.toString().replace("\n", "\n "); - } - } diff --git a/xplan-manager/xplan-manager-api/src/main/resources/application.properties b/xplan-manager/xplan-manager-api/src/main/resources/application.properties index fb6b50101fe2ac2a1d8ff91331738e7b4b965b50..9492e9ccff5d6737956887f9c2a8f01634ef6375 100644 --- a/xplan-manager/xplan-manager-api/src/main/resources/application.properties +++ b/xplan-manager/xplan-manager-api/src/main/resources/application.properties @@ -35,3 +35,16 @@ spring.rabbitmq.port=${XPLAN_RABBIT_PORT:5672} spring.rabbitmq.username=${XPLAN_RABBIT_USER:guest} xplanbox.rabbitmq.internal.prefix=${XPLAN_RABBIT_INTERNAL_PREFIX:xplanbox} +xplanbox.rabbitmq.fanout.topics=none +xplanbox.rabbitmq.public.fanout=${XPLAN_RABBIT_PUBLIC_FANOUT:} +xplanbox.rabbitmq.task.topics=none + +xplanbox.s3.accessKeyId=${XPLAN_S3_ACCESS_KEY} +xplanbox.s3.endpoint.url=${XPLAN_S3_ENDPOINT} +xplanbox.s3.region=${XPLAN_S3_REGION} +xplanbox.s3.secretKey=${XPLAN_S3_SECRET_ACCESS_KEY} + +xplanbox.validation.fsdirectory=${XPLAN_FS_DIRECTORY:/tmp/validation} +xplanbox.validation.profiles=${XPLAN_VALIDATOR_PROFILES:} +xplanbox.validation.s3.bucketName=${XPLAN_VALIDATION_S3_BUCKET_NAME:validation} +xplanbox.validation.s3.bucketPublicUrl=${XPLAN_VALIDATION_S3_BUCKET_PUBLIC_URL} diff --git a/xplan-manager/xplan-manager-web/pom.xml b/xplan-manager/xplan-manager-web/pom.xml index 7e82215a77f12b2e110551444d761f6362ffb4a6..c84776ad06ea7c3eee903ef12f4a1b26c6b56e67 100644 --- a/xplan-manager/xplan-manager-web/pom.xml +++ b/xplan-manager/xplan-manager-web/pom.xml @@ -174,11 +174,6 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> - <dependency> - <groupId>com.fasterxml.jackson.core</groupId> - <artifactId>jackson-databind</artifactId> - <type>jar</type> - </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> diff --git a/xplan-tests/xplan-tests-selenium/README.md b/xplan-tests/xplan-tests-selenium/README.md index ef4181fb20b971cb0766646ae2cf6bc5ae1b42a5..186d16d28a41179c0e66cc74172ebb60ac16596f 100644 --- a/xplan-tests/xplan-tests-selenium/README.md +++ b/xplan-tests/xplan-tests-selenium/README.md @@ -10,7 +10,7 @@ mvn clean integration-test -Psystem-tests -DbaseUrlValidatorWeb=https://xplanbox ## Ausführung im Docker Container -Die Selenium Tests können in einem Docker Container ausgeführt werden +Die Tests können in einem Docker Container ausgeführt werden ``` docker run --env XPLAN_VALIDATOR_WEB_BASE_URL=... xplanbox/xplan-tests-selenium @@ -21,6 +21,13 @@ docker run --env XPLAN_VALIDATOR_WEB_BASE_URL=... xplanbox/xplan-tests-selenium - `XPLAN_VALIDATOR_WEB_BASE_URL` - `XPLAN_VALIDATOR_WEB_USERNAME` - `XPLAN_VALIDATOR_WEB_PASSWORD` +- `SKIP_RABBIT_TESTS`: wenn `true` oder `1` werden die Tests ignoriert, für die eine Verbindung zum RabbitMQ Server benötigt wird. +- `XPLAN_RABBIT_HOST`: der Host vom RabbitMQ Server (Default: `localhost`) +- `XPLAN_RABBIT_PORT`: der Port vom RabbitMQ Server (Default: `5672`) +- `XPLAN_RABBIT_USER`: der Benutzername für die Verbindung zum RabbitMQ Server (Default: `guest`) +- `XPLAN_RABBIT_PASSWORD`: das Passwort für die Verbindung zum RabbitMQ Server (Default: `guest`) +- `XPLAN_RABBIT_PUBLIC_FANOUT`: der Name vom Fanout Channel (Default: `xplanbox-public-fanout`) +- `XPLAN_VALIDATORAPI_URL_PUBLIC`: die Url von der validator-api Anwendung (Default: `http://localhost:8085/xplan-validator-api`) Der Report im PDF Format kann zu einem S3 Bucket hochgeladen werden, dafür müssen folgende Umgebungsvariablen gesetzt werden: diff --git a/xplan-tests/xplan-tests-selenium/pom.xml b/xplan-tests/xplan-tests-selenium/pom.xml index ab2bf8ab350e0f42e046887e12c6a14b92f494cc..2846de24b4bcaf1a2281a0bdb6fb6bb7cd559fd5 100644 --- a/xplan-tests/xplan-tests-selenium/pom.xml +++ b/xplan-tests/xplan-tests-selenium/pom.xml @@ -1,4 +1,6 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>xplan-tests-selenium</artifactId> @@ -47,6 +49,15 @@ <artifactId>assertj-core</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>io.github.openfeign</groupId> + <artifactId>feign-jackson</artifactId> + <version>13.3</version> + </dependency> + <dependency> + <groupId>com.rabbitmq</groupId> + <artifactId>amqp-client</artifactId> + </dependency> </dependencies> <build> @@ -64,10 +75,10 @@ <profile> <id>system-tests</id> <properties> - <baseUrlValidatorWeb /> + <baseUrlValidatorWeb/> <maven.test.skip>false</maven.test.skip> - <password /> - <username /> + <password/> + <username/> </properties> <build> <plugins> @@ -111,11 +122,15 @@ <files> <file> <source>../pom.xml</source> - <destName>xplan-tests-pom.xml</destName> + <destName> + xplan-tests-pom.xml + </destName> </file> <file> <source>../../pom.xml</source> - <destName>xplanbox-pom.xml</destName> + <destName> + xplanbox-pom.xml + </destName> </file> </files> </inline> diff --git a/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh b/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh index fa2e91a8340ade8f2d04e268bf9c28f037dda03e..3528bccf1a0b24011ef9fee3e1d0462d1fb712bc 100755 --- a/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh +++ b/xplan-tests/xplan-tests-selenium/runAllSeleniumTests.sh @@ -38,6 +38,7 @@ function waitForRightVersion() { echo "Waiting for services with git revision $GIT_REVISION:" waitForRightVersion $XPLAN_VALIDATOR_WEB_BASE_URL/version.txt +waitForRightVersion $XPLAN_VALIDATORAPI_URL_PUBLIC/xvalidator/version.txt # start virtual buffer Xvfb :0 -screen 0 1024x768x24 & diff --git a/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidationPublicEventsIT.java b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidationPublicEventsIT.java new file mode 100644 index 0000000000000000000000000000000000000000..f75326822d0995683c4244b33a49bdeef104fed6 --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidationPublicEventsIT.java @@ -0,0 +1,219 @@ +package de.latlon.xplanbox.tests.validator.rabbit; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable; + +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; +import com.rabbitmq.client.DeliverCallback; + +import de.latlon.xplanbox.tests.validator.rabbit.ValidatorApiV2.StatusResponse; +import de.latlon.xplanbox.tests.validator.rabbit.ValidatorApiV2.ValidateResponse; +import feign.Client; +import feign.Feign; +import feign.RequestTemplate; +import feign.codec.EncodeException; +import feign.codec.Encoder; +import feign.jackson.JacksonDecoder; + +@DisabledIfEnvironmentVariable(named = "SKIP_RABBIT_TESTS", matches = "(1|true)") +class ValidationPublicEventsIT { + + private List<String> receivedPublicEvents = Collections.synchronizedList(new ArrayList<>()); + + record TestConfig(String apiBaseUrl, String rabbitHost, int rabbitPort, String rabbitUsername, + String rabbitPassword, String publicFanoutQueueName) { + } + + static TestConfig getTestConfig() { + String apiBaseUrl = getEnv("XPLAN_VALIDATORAPI_URL_PUBLIC", "http://localhost:8085/xplan-validator-api"); + String rabbitHost = getEnv("XPLAN_RABBIT_HOST", "localhost"); + int rabbitPort = Integer.parseInt(getEnv("XPLAN_RABBIT_PORT", "5672"), 10); + String rabbitUsername = getEnv("XPLAN_RABBIT_USER", "guest"); + String rabbitPassword = getEnv("XPLAN_RABBIT_PASSWORD", "guest"); + String publicFanoutQueueName = getEnv("XPLAN_RABBIT_PUBLIC_FANOUT", "xplanbox-public-fanout"); + return new TestConfig(apiBaseUrl, rabbitHost, rabbitPort, rabbitUsername, rabbitPassword, + publicFanoutQueueName); + } + + private static String getEnv(String key, String defaultValue) { + String value = System.getenv(key); + if (value == null || value.isBlank()) { + value = System.getProperty(key, defaultValue); + } + return value; + } + + private TestConfig testConfig = getTestConfig(); + + @Test + void validateAndVerifyPublicEvents() throws Exception { + ValidatorApiV2 v2Api = Feign.builder() // + .client(new Client.Default(getSSLSocketFactory(), getHostnameVerifier())) + // .logger(new Logger.ErrorLogger()) // + // .logLevel(Level.FULL) // + .encoder(new SimpleBodyFileEncoder()) // + .decoder(new JacksonDecoder()) // + .target(ValidatorApiV2.class, testConfig.apiBaseUrl); + + startRabbitPublicEventsReception(); + + Path f = Paths.get(getClass().getResource("/BPlan001_5-4.zip").toURI()); + ValidateResponse r = v2Api.validate(f); + + assertThat(r.uuid()).isNotBlank(); + assertThat(r.statusLink()).isNotNull(); + + // verify status (probably not yet finished but who knows...) + StatusResponse statusResponse = v2Api.status(r.uuid()); + assertThat(statusResponse.status()).isIn("REQUESTED", "FINISHED"); // .... + + // awaiting 2 events for this uuid + List<String> expectedEvents = List.of( // + "{\"apiVersion\":\"1.0\",\"eventType\":\"VALIDATION_START\",\"uuid\":\"" + r.uuid() + + "\",\"message\":\"validation started\"}", // + "{\"apiVersion\":\"1.0\",\"eventType\":\"VALIDATION_FINISHED\",\"uuid\":\"" + r.uuid() + + "\",\"message\":\"validation finished\"}"); + waitingIfNeeded(10, () -> assertThat(receivedEventsFor(r.uuid())).isEqualTo(expectedEvents)); + + // verify status + statusResponse = v2Api.status(r.uuid()); + assertThat(statusResponse.status()).isEqualTo("FINISHED"); // now surely finished + + System.out.println(receivedPublicEvents); + } + + List<String> receivedEventsFor(String uuid) { + return receivedPublicEvents.stream() // + .filter((it) -> it.contains(uuid)) // + .collect(Collectors.toList()); + } + + private void startRabbitPublicEventsReception() throws Exception { + ConnectionFactory factory = new ConnectionFactory(); + factory.setHost(testConfig.rabbitHost); + factory.setPort(testConfig.rabbitPort); + factory.setUsername(testConfig.rabbitUsername); + factory.setPassword(testConfig.rabbitPassword); + Connection connection = factory.newConnection(); + Channel channel = connection.createChannel(); + + String queueName = channel.queueDeclare().getQueue(); + channel.queueBind(queueName, testConfig.publicFanoutQueueName(), ""); + + DeliverCallback deliverCallback = (consumerTag, delivery) -> { + String message = new String(delivery.getBody(), "UTF-8"); + receivedPublicEvents.add(message); + }; + channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { + }); + } + + // SSLSocketFactory + // Install the all-trusting trust manager + public static SSLSocketFactory getSSLSocketFactory() { + try { + SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(null, getTrustManager(), new SecureRandom()); + return sslContext.getSocketFactory(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + // TrustManager + // trust manager that does not validate certificate chains + private static TrustManager[] getTrustManager() { + TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) { + + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) { + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[] {}; + } + } }; + return trustAllCerts; + } + + // HostnameVerifier + public static HostnameVerifier getHostnameVerifier() { + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String s, SSLSession sslSession) { + return true; + } + }; + return hostnameVerifier; + } + + // encode @Param("bodyFile") to request body bytes + public static class SimpleBodyFileEncoder implements Encoder { + + public void encode(Object object, Type type, RequestTemplate template) throws EncodeException { + @SuppressWarnings("unchecked") + Map<String, ?> params = (Map<String, ?>) object; + Path file = (Path) params.get("bodyFile"); + byte[] bytes; + try { + bytes = Files.readAllBytes(file); + } + catch (IOException e) { + throw new RuntimeException(e); + } + template.body(bytes, StandardCharsets.UTF_8); + } + + } + + private void waitingIfNeeded(int maxWaitInSeconds, Callable<?> callable) throws Exception { + long maxWaitTime = System.currentTimeMillis() + 1_000 * maxWaitInSeconds; + while (true) { + try { + callable.call(); + return; + } + catch (Throwable t) { + if (System.currentTimeMillis() > maxWaitTime) { + throw t; + } + Thread.sleep(1_000); + } + } + + } + +} diff --git a/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidatorApiV2.java b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidatorApiV2.java new file mode 100644 index 0000000000000000000000000000000000000000..1f4cc988fb8e1277a60c7ba89658084befe4f7cd --- /dev/null +++ b/xplan-tests/xplan-tests-selenium/src/test/java/de/latlon/xplanbox/tests/validator/rabbit/ValidatorApiV2.java @@ -0,0 +1,33 @@ +package de.latlon.xplanbox.tests.validator.rabbit; + +import java.nio.file.Path; +import java.util.List; + +import feign.Headers; +import feign.Param; +import feign.RequestLine; + +/** + * Simple interface for the API methods currently tested. All paths relative to + * "/xplan-validator-api" + */ +public interface ValidatorApiV2 { + + public record StatusResponse(String status, List<Link> links) { + } + + public record Link(String expirationDate, String href, String type, String hreflang, String title, String length, + String rel) { + } + + public record ValidateResponse(String uuid, Link statusLink) { + } + + @RequestLine("GET /api/v2/status/{uuid}") + StatusResponse status(@Param("uuid") String uuid); + + @RequestLine("POST /api/v2/validate") + @Headers("Content-Type: application/zip") + ValidateResponse validate(@Param("bodyFile") Path f); + +} \ No newline at end of file diff --git a/xplan-tests/xplan-tests-soapui/README.md b/xplan-tests/xplan-tests-soapui/README.md index b9686b6860cb5ec61a3e0f8bf66f967b0adb5c9d..b79f364781abcbad961e526878061fa472ee8cc8 100644 --- a/xplan-tests/xplan-tests-soapui/README.md +++ b/xplan-tests/xplan-tests-soapui/README.md @@ -8,7 +8,7 @@ Details zu den einzelnen SoapUI TestSuites sind in der [README](src/main/resourc ``` mvn clean test -Psystem-tests -DtestFileName=xplan-validator-api-soapui-project.xml \ - -Dendpoint=https://xplanbox.lat-lon.de/xvalidator/api/v1 -Dusername=xplanbox -Dpassword='PWD' + -DbaseUrlValidatorApi=https://xplanbox.lat-lon.de -Dusername=xplanbox -Dpassword='PWD' ``` ### xplan-manager-api-soapui-project diff --git a/xplan-tests/xplan-tests-soapui/pom.xml b/xplan-tests/xplan-tests-soapui/pom.xml index 19c0007390fb748da4fa0d4e71a4513d61a36bd0..97b8c1a8e68264038f3d498191e4b44a531ba676 100644 --- a/xplan-tests/xplan-tests-soapui/pom.xml +++ b/xplan-tests/xplan-tests-soapui/pom.xml @@ -68,11 +68,11 @@ <baseUrlMapProxy /> <usernameMapProxy /> <passwordMapProxy /> + <baseUrlValidatorApi /> <username /> <password /> <apiKey /> <jdbcUrl /> - <endpoint /> </properties> <build> <plugins> @@ -104,9 +104,6 @@ <junitReport>true</junitReport> <exportAll>true</exportAll> <testFailIgnore>true</testFailIgnore> - <endpoint>${endpoint}</endpoint> - <username>${username}</username> - <password>${password}</password> <projectProperties> <projectProperty>projectDir=${basedir}/src/main/resources</projectProperty> <value>baseUrlInspirePlu=${baseUrlInspirePlu}</value> @@ -127,6 +124,7 @@ <value>baseUrlMapProxy=${baseUrlMapProxy}</value> <value>usernameMapProxy=${usernameMapProxy}</value> <value>passwordMapProxy=${passwordMapProxy}</value> + <value>baseUrlValidatorApi=${baseUrlValidatorApi}</value> <value>username=${username}</value> <value>password=${password}</value> <value>apiKey=${apiKey}</value> diff --git a/xplan-tests/xplan-tests-soapui/runAllSoapUiTests.sh b/xplan-tests/xplan-tests-soapui/runAllSoapUiTests.sh index ae69ef7ebd7d1659649f62fa25ca1036f00376a2..6b6121f1918bebef79c30300a0965156c5c6b93a 100755 --- a/xplan-tests/xplan-tests-soapui/runAllSoapUiTests.sh +++ b/xplan-tests/xplan-tests-soapui/runAllSoapUiTests.sh @@ -69,7 +69,7 @@ mvn test -Psystem-tests -DtestFileName=xplan-manager-api-soapui-project.xml \ -DjdbcUrl=$JDBC_URL mvn test -Psystem-tests -DtestFileName=xplan-validator-api-soapui-project.xml \ - -Dendpoint=$XPLAN_VALIDATOR_API_BASE_URL/xvalidator/api/v1 -Dusername=$XPLAN_VALIDATOR_API_USERNAME -Dpassword=$XPLAN_VALIDATOR_API_PASSWORD + -DbaseUrlValidatorApi=$XPLAN_VALIDATOR_API_BASE_URL -Dusername=$XPLAN_VALIDATOR_API_USERNAME -Dpassword=$XPLAN_VALIDATOR_API_PASSWORD if [ -z ${XPLAN_DOKUMENTE_API_BASE_URL+x} ]; then diff --git a/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-validator-api-soapui-project.xml b/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-validator-api-soapui-project.xml index 7657184eeef32ecc3b80d828b0ca2d7f5da0245c..21fc65c9bf21cac35c26bcebe925c43141a075a5 100644 --- a/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-validator-api-soapui-project.xml +++ b/xplan-tests/xplan-tests-soapui/src/main/resources/xplan-validator-api-soapui-project.xml @@ -24,6 +24,7 @@ <con:settings/> <con:definitionCache type="TEXT" rootPart=""/> <con:endpoints> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:endpoint>https://xplanbox.lat-lon.de/xvalidator/api/v1</con:endpoint> </con:endpoints> @@ -52,16 +53,28 @@ OpenAPI document</con:description> <con:params/> <con:element>html</con:element> </con:representation> + <con:representation type="FAULT"> + <con:mediaType>text/html;charset=utf-8</con:mediaType> + <con:status>404</con:status> + <con:params/> + <con:element>html</con:element> + </con:representation> + <con:representation type="FAULT"> + <con:mediaType>application/json</con:mediaType> + <con:status>404</con:status> + <con:params/> + <con:element xmlns:v1="http://localhost/xplan-validator-api/xvalidator/api/v1/">v1:Fault</con:element> + </con:representation> <con:request name="Request 1" id="17005e5b-eacc-4780-bec7-796702b4e0f4" mediaType="application/json"> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/</con:originalUri> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:preemptive>false</con:preemptive> @@ -102,12 +115,12 @@ Show system and application configuration</con:description> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/info</con:originalUri> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:authType>No Authorization</con:authType> @@ -678,12 +691,12 @@ Validate XPlanGML or XPlanArchive</con:description> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:authType>No Authorization</con:authType> @@ -720,7 +733,7 @@ Validate XPlanGML or XPlanArchive</con:description> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="f3ba4686-3a8b-4dc9-8e96-9bc351221c5a" name="Valid HTTP Status Codes"> @@ -846,8 +859,8 @@ Validate XPlanGML or XPlanArchive</con:description> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:preemptive>false</con:preemptive> @@ -871,7 +884,7 @@ Validate XPlanGML or XPlanArchive</con:description> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/info</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="0cc46e02-cf98-4922-b9a0-70051141ecfd" name="Valid HTTP Status Codes"> @@ -908,8 +921,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:authType>No Authorization</con:authType> @@ -932,7 +945,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -941,8 +954,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -980,7 +993,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1025,8 +1038,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1064,7 +1077,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1109,8 +1122,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1149,7 +1162,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1194,8 +1207,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1234,7 +1247,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1288,8 +1301,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1328,7 +1341,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1373,8 +1386,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1413,7 +1426,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1458,8 +1471,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1498,7 +1511,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1570,8 +1583,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1614,7 +1627,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1659,8 +1672,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1699,7 +1712,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1744,8 +1757,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1783,7 +1796,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1828,8 +1841,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1867,7 +1880,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1876,8 +1889,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -1916,7 +1929,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/json" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -1961,8 +1974,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2000,7 +2013,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2027,8 +2040,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2066,7 +2079,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="text/xml" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2093,8 +2106,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2132,7 +2145,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/pdf" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2148,8 +2161,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2187,7 +2200,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/zip" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2196,8 +2209,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2235,7 +2248,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2289,8 +2302,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2328,7 +2341,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2382,8 +2395,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2421,7 +2434,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2484,8 +2497,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2523,7 +2536,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2586,8 +2599,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2625,7 +2638,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2688,8 +2701,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2727,7 +2740,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2772,8 +2785,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2811,7 +2824,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2856,8 +2869,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2895,7 +2908,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2904,8 +2917,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2944,7 +2957,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/pdf" xmlns="http://eviware.com/soapui/config"/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -2953,8 +2966,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -2993,7 +3006,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3002,8 +3015,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3041,7 +3054,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3095,8 +3108,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3136,7 +3149,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3208,8 +3221,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3249,7 +3262,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3312,8 +3325,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3351,7 +3364,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3367,8 +3380,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3406,7 +3419,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3469,8 +3482,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3509,7 +3522,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3581,8 +3594,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3620,7 +3633,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3701,8 +3714,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3741,7 +3754,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3804,8 +3817,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3844,7 +3857,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3916,8 +3929,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -3955,7 +3968,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="7d16e282-0f06-406d-9b34-d11a52136ce3" name="Valid HTTP Status Codes"> @@ -3964,8 +3977,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -4012,7 +4025,7 @@ assert json.version != null</scriptText> <con:settings> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/info</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="79bf32cf-1071-4e79-ba39-ea58115c33a0" name="Valid HTTP Status Codes"> @@ -4066,8 +4079,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>No Authorization</con:selectedAuthProfile> <con:authType>No Authorization</con:authType> @@ -4106,7 +4119,7 @@ assert json.version != null</scriptText> <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> </con:settings> <con:encoding>UTF-8</con:encoding> - <con:endpoint>http://localhost:8085/xplan-validator-api/xvalidator/api/v1</con:endpoint> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> <con:request/> <con:originalUri>http://localhost/xplan-validator-api/xvalidator/api/v1/validate</con:originalUri> <con:assertion type="Valid HTTP Status Codes" id="5f4dcada-c13f-4671-84c1-8a94197dfc67" name="Valid HTTP Status Codes"> @@ -4151,8 +4164,8 @@ assert json.version != null</scriptText> </con:configuration> </con:assertion> <con:credentials> - <con:username xsi:nil="true"/> - <con:password xsi:nil="true"/> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> <con:domain xsi:nil="true"/> <con:selectedAuthProfile>Basic</con:selectedAuthProfile> <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> @@ -4203,11 +4216,968 @@ assert json.version != null</scriptText> </con:property> </con:properties> </con:testSuite> + <con:testSuite id="fa8cd647-1575-4886-b599-51ecc10029ea" name="XPlanValidatorAPIv2 TestSuite"> + <con:description>Tests für XPlanValidatorAPI version 2.</con:description> + <con:settings/> + <con:runType>SEQUENTIAL</con:runType> + <con:testCase id="8ba01cec-aea7-4882-b1b0-8d9e4647f2ef" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/ TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/"-Pfad.</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET XX X.X XX openAPI" id="f77d6853-198d-4e77-ad18-72cbf741adde"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="c41918a7-8619-4507-bb66-c291e018afa5" name="GET XX X.X XX openAPI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="f3ba4686-3a8b-4dc9-8e96-9bc351221c5a" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="41304562-7812-4f95-87b1-17cf5354b417" name="openapi"> + <con:configuration> + <path>$.openapi</path> + <content>3.0.1</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fb838b6f-0eaa-45a1-8a43-f87364021182" name="path /"> + <con:configuration> + <path>$.paths./</path> + <content>{"get":{"summary":"OpenAPI document","description":"API documentation","operationId":"openApi","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object"}}}},"406":{"description":"Requested format is not available"}}}}</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="fb838b6f-0eaa-45a1-8a43-f87364021182" name="path /validate X-Filename parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'X-Filename')]</path> + <content>[{"name":"X-Filename","in":"header","description":"Name of the file to be uploaded","schema":{"pattern":"^[A-Za-z0-9.()_\\-]*$","type":"string"},"example":"File names such as xplan.gml, xplan.xml, xplan.zip"}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate name parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'name')]</path> + <content>[{"name":"name","in":"query","description":"Name of the validation","schema":{"pattern":"^[A-Za-z0-9.()_\\-]*$","type":"string"},"example":"xplan-1Pruefbericht_Torstrasse_10_report-4223"}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate skipSemantisch parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'skipSemantisch')]</path> + <content>[{"name":"skipSemantisch","in":"query","description":"skip semantische Validierung","schema":{"type":"boolean","default":false}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate skipGeometrisch parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'skipGeometrisch')]</path> + <content>[{"name":"skipGeometrisch","in":"query","description":"skip geometrische Validierung","schema":{"type":"boolean","default":false}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate skipFlaechenschluss parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'skipFlaechenschluss')]</path> + <content>[{"name":"skipFlaechenschluss","in":"query","description":"skip Flaechenschluss Ueberpruefung","schema":{"type":"boolean","default":false}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate skipGeltungsbereich parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'skipGeltungsbereich')]</path> + <content>[{"name":"skipGeltungsbereich","in":"query","description":"skip Geltungsbereich Ueberpruefung","schema":{"type":"boolean","default":false}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate skipLaufrichtung parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'skipLaufrichtung')]</path> + <content>[{"name":"skipLaufrichtung","in":"query","description":"skip Laufrichtung Ueberpruefung","schema":{"type":"boolean","default":false}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="path /validate profiles parameter"> + <con:configuration> + <path>$.paths./validate.post.parameters.[?(@.name == 'profiles')]</path> + <content>[{"name":"profiles","in":"query","description":"Names of profiles which shall be additionally used for validation","explode":false,"schema":{"type":"array","items":{"type":"string"}}}]</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="schemas ValidationReport externalReferences items type" disabled="true"> + <con:configuration> + <path>$.components.schemas.ValidationReport.properties.externalReferences.items.type</path> + <content>string</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="634b1a88-6df2-49f0-9e23-8babb820f690" name="schemas ValidationReport externalReferencesResult items ref" disabled="true"> + <con:configuration> + <path>$.components.schemas.ValidationReport.properties.externalReferencesResult.items.$ref</path> + <content>#/components/schemas/ExternalReferenceResult</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="21d4a0ad-da22-4876-8ecc-ce7ebc245f39" name="schemas DocumentSummary" disabled="true"> + <con:configuration> + <path>$.components.schemas.DocumentSummary</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="3a711a3a-80f6-4f4b-92d2-412784d41ae8" name="path /status/{uuid}"> + <con:configuration> + <path>$.paths./status/{uuid}</path> + <content>{"get":{"tags":["status"],"summary":"Status of a validation","description":"Returns the status of a validation","operationId":"status","parameters":[{"name":"uuid","in":"path","description":"UUID of the validation","required":true,"schema":{"type":"string"},"example":"0a0cedbc-bf3f-4f1f-bdaf-ea0e52075540"}],"responses":{"200":{"description":"ValidationReport","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationReceipt"}}}},"404":{"description":"Invalid uuid, no validation with the passed uuid found"},"406":{"description":"Requested format is not available"}}}}</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET XX X.X XX openAPI invalidAcceptHeaderExpectError" id="a7ea3bf5-a395-42f4-8ebf-ef27b6e766c3"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="c41918a7-8619-4507-bb66-c291e018afa5" name="GET XX X.X XX openAPI invalidAcceptHeaderExpectError" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="invalid" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="f3ba4686-3a8b-4dc9-8e96-9bc351221c5a" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>406</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:properties/> + </con:testCase> + <con:testCase id="ea696cef-8d32-45ec-9cbf-65b2c190cfb7" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/info TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/info"-Pfad.</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET XX X.X XX showConfig" id="2b3bc64c-6c0f-4af1-87d0-b486e93aeccc"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="70d479ab-c344-411e-a79f-167f3f70c0a9" name="GET XX X.X XX showConfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/info</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="0cc46e02-cf98-4922-b9a0-70051141ecfd" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="04006561-4031-47bd-9cd2-fb1215076b2d" name="version"> + <con:configuration> + <path>$.version</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="GroovyScriptAssertion" id="81fc7cf2-d170-45a8-ba3f-26d3c82a741a" name="versionNotNull"> + <con:configuration> + <scriptText>import groovy.json.JsonSlurper + + json = new JsonSlurper().parseText(messageExchange.response.responseContent) + assert !(json.isEmpty()) + + assert json.version != null</scriptText> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="d1a2a299-a358-4dcb-b245-b5ce405449b0" name="supportedXPlanGmlVersions"> + <con:configuration> + <path>$.supportedXPlanGmlVersions</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET XX X.X XX showConfig invalidAcceptHeaderExpectError" id="3f3d2e0c-ee50-454a-968d-322e846f120a"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="70d479ab-c344-411e-a79f-167f3f70c0a9" name="GET XX X.X XX showConfig invalidAcceptHeaderExpectError" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="invalid" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/info</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="0cc46e02-cf98-4922-b9a0-70051141ecfd" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>406</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:properties/> + </con:testCase> + <con:testCase id="6bb3bcfc-8b50-4786-ab61-966e4b29dfb6" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/validate TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/validate"-Pfad.</con:description> + <con:settings/> + <con:testStep type="httprequest" name="POST BP 6.0.2 XX validatePlan" id="12aa68bf-5824-4401-bde6-18535f69548a"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP 6.0.2 XX validatePlan" postQueryString="false" mediaType="application/octet-stream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="a14750a4-b6ee-40eb-b83b-6905d92e26ed" name="uuid"> + <con:configuration> + <path>$.uuid</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="cca268ea-ed94-467c-9fee-33c8215bf396" name="statusLink"> + <con:configuration> + <path>$.statusLink</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="cca268ea-ed94-467c-9fee-33c8215bf396" name="href"> + <con:configuration> + <path>$.statusLink.href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/BP_6.0.2_valide.zip</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="transfer" name="POST BP 6.0.2 XX validatePlan Property Transfer" id="ad6e933c-44dc-4400-bd4e-61d7493afc36"> + <con:settings/> + <con:config xsi:type="con:PropertyTransfersStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" ignoreEmpty="false" transferToAll="false" entitize="false" transferChildNodes="false"> + <con:name>hrefStatus</con:name> + <con:sourceType>Response</con:sourceType> + <con:sourceStep>POST BP 6.0.2 XX validatePlan</con:sourceStep> + <con:sourcePath>$.statusLink.href</con:sourcePath> + <con:targetType>hrefStatus</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:type>JSONPATH</con:type> + <con:targetTransferType>JSONPATH</con:targetTransferType> + <con:upgraded>true</con:upgraded> + </con:transfers> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="POST BP 6.0.2 XX validatePlan mismatchingContentTypeAndFileType" id="4a261e97-6577-4e32-b3ea-536c2a239621"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP 6.0.2 XX validatePlan mismatchingContentTypeAndFileType" postQueryString="false" mediaType="text/xml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="8f708cd5-ffca-4b46-bf7e-080df45e77ba" name="uuid"> + <con:configuration> + <path>$.uuid</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="8390a98f-4263-4974-9789-a23a7a134a41" name="statusLink"> + <con:configuration> + <path>$.statusLink</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="b53b1dc3-03fc-469b-8934-81e34c1e7043" name="href"> + <con:configuration> + <path>$.statusLink.href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>text/xml</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/BP_6.0.2_valide.zip</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="transfer" name="POST BP 6.0.2 XX validatePlan mismatchingContentTypeAndFileType Property Transfer" id="deb991ae-2746-43e4-95ec-854455d33c8d"> + <con:settings/> + <con:config xsi:type="con:PropertyTransfersStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" ignoreEmpty="false" transferToAll="false" entitize="false" transferChildNodes="false"> + <con:name>hrefStatus2</con:name> + <con:sourceType>Response</con:sourceType> + <con:sourceStep>POST BP 6.0.2 XX validatePlan mismatchingContentTypeAndFileType</con:sourceStep> + <con:sourcePath>$.statusLink.href</con:sourcePath> + <con:targetType>hrefStatus2</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:type>JSONPATH</con:type> + <con:targetTransferType>JSONPATH</con:targetTransferType> + <con:upgraded>true</con:upgraded> + </con:transfers> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="POST BP 6.0.2 XX validatePlan invalidQueryParamExpectError" id="244e9447-3e31-4d44-b521-25bafd92536c"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP 6.0.2 XX validatePlan invalidQueryParamExpectError" postQueryString="false" mediaType="application/octet-stream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>400</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/BP_6.0.2_valide.zip</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters> + <con:parameter> + <con:name>name</con:name> + <con:value>//</con:value> + <con:style>QUERY</con:style> + </con:parameter> + </con:parameters> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="POST BP 6.0.2 XX validatePlan invalidAcceptHeaderExpectError" id="d6ddd986-409b-45e3-b69f-13f2b9d18210"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP 6.0.2 XX validatePlan invalidAcceptHeaderExpectError" postQueryString="false" mediaType="application/octet-stream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="invalid" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>406</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/BP_6.0.2_valide.zip</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="POST BP 6.0 XX validatePlan unsupportedMediaTypeZipExpectError" id="4e24cbc3-ecdc-48b1-bcd0-eb709b803334"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP 6.0 XX validatePlan unsupportedMediaTypeZipExpectError" postQueryString="false" mediaType="application/octet-stream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>415</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/Blankenese29_Test_60_InvalidContent.zip</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="POST BP X.X XX validatePlan unsupportedMediaTypeOdtExpectError" id="59528b98-9523-40ef-8183-6e100065c202"> + <con:settings/> + <con:config method="POST" xsi:type="con:HttpRequest" id="aa5ee7a4-a529-48ac-ad3a-eb3064f75a0f" name="POST BP X.X XX validatePlan unsupportedMediaTypeOdtExpectError" postQueryString="false" mediaType="application/octet-stream" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/validate</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="047b0265-8e8e-45c2-b4d6-1c67ce5c4a7c" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>415</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:attachment> + <con:name>BP_6.0.2_valide.zip</con:name> + <con:contentType>application/octet-stream</con:contentType> + <con:contentId>BP_6.0.2_valide.zip</con:contentId> + <con:url>${projectDir}/xplan-validator-api/plans/invalidContentType.odt</con:url> + <con:id>292d515d-99cd-4aac-99f1-c05d1d4a5851</con:id> + </con:attachment> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:properties/> + </con:testCase> + <con:testCase id="1ac50e96-08c6-4f7f-b28c-b0c00453a683" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="/status/{uuid} TestCase" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Tests für den "/status/{uuid}"-Pfad.</con:description> + <con:settings/> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveStatus" id="f6b97694-bf15-4a53-8b89-d854f9bd8680"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveStatus" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#TestSuite#hrefStatus}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="ae2299b3-55a5-4171-b298-0c0fbf353fbb" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="9c15ee46-6bc7-4cb2-bca6-6439ca6fdd4f" name="status exists"> + <con:configuration> + <path>$.status</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="c47301d5-d89e-4a17-8345-4329bf534b29" name="status" disabled="true"> + <con:configuration> + <path>$.status</path> + <content>FINISHED</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="9c15ee46-6bc7-4cb2-bca6-6439ca6fdd4f" name="links"> + <con:configuration> + <path>$.links</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="79d9689a-c545-4733-bf8d-4b8da83f53a3" name="link[0]" disabled="true"> + <con:configuration> + <path>$.links[0].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>(.*).json|(.*).pdf</regEx> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath RegEx Match" id="79d9689a-c545-4733-bf8d-4b8da83f53a3" name="link[1]" disabled="true"> + <con:configuration> + <path>$.links[1].href</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + <regEx>(.*).json|(.*).pdf</regEx> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="groovy" name="pollStatus" id="119b8ff3-609c-44dc-895b-0399ac3383e3"> + <con:settings/> + <con:config> + <script>import groovy.json.JsonSlurper + +def testStepName = "GET BP 6.0.2 XX retrieveStatus" +def response = testRunner.testCase.getTestStepByName(testStepName).getPropertyValue("Response") +def json = new JsonSlurper().parseText(response) + +if( context.loopIndex == null ) + context.loopIndex = 0 + +if( ++context.loopIndex < 20 && json.status != "FINISHED" ){ + sleep(1000) + testRunner.gotoStepByName(testStepName) +} else if (context.loopIndex == 20) { + context.loopIndex = null + assert(false) +} else { + context.loopIndex = null +}</script> + </con:config> + </con:testStep> + <con:testStep type="groovy" name="transferUrls" id="1504923d-a693-40d9-a8c9-8ec60e856c80"> + <con:settings/> + <con:config> + <script>import groovy.json.JsonSlurper + +def response = testRunner.testCase.getTestStepByName("GET BP 6.0.2 XX retrieveStatus").getPropertyValue("Response") +def json = new JsonSlurper().parseText(response) + +def hrefJson = json.links.find { it.type == "application/json" }.href +def hrefPdf = json.links.find { it.type == "application/pdf" }.href + +testRunner.testCase.testSuite.setPropertyValue( "reportJson", hrefJson ) +testRunner.testCase.testSuite.setPropertyValue( "reportPdf", hrefPdf )</script> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveStatus mismatchingContentTypeAndFileType" id="3bef223a-0426-4f62-8d72-85dc566f33fb"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveStatus mismatchingContentTypeAndFileType" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#TestSuite#hrefStatus2}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="ae2299b3-55a5-4171-b298-0c0fbf353fbb" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Existence Match" id="c2e1881a-b3ea-42ae-9576-ccd831867f6a" name="status exists"> + <con:configuration> + <path>$.status</path> + <content>true</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="bd70dfd2-7b9c-4737-baca-a3278a5b6fd4" name="status" disabled="true"> + <con:configuration> + <path>$.status</path> + <content>FAILED</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="1c25a6b8-17f5-48ea-9e07-eb18352886d7" name="errorMsg" disabled="true"> + <con:configuration> + <path>$.errorMsg</path> + <content>Could not read attached file as XPlanGML</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:assertion type="JsonPath Match" id="095b0bb8-5f84-4622-bce5-36deb4f29499" name="links"> + <con:configuration> + <path>$.links</path> + <content>null</content> + <allowWildcards>false</allowWildcards> + <ignoreNamspaceDifferences>false</ignoreNamspaceDifferences> + <ignoreComments>false</ignoreComments> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="groovy" name="pollStatus2" id="8ca0b5da-0bd9-44d8-aa10-5ee9285b7b34"> + <con:settings/> + <con:config> + <script>import groovy.json.JsonSlurper + +def testStepName = "GET BP 6.0.2 XX retrieveStatus mismatchingContentTypeAndFileType" +def response = testRunner.testCase.getTestStepByName(testStepName).getPropertyValue("Response") +def json = new JsonSlurper().parseText(response) + +if( context.loopIndex == null ) + context.loopIndex = 0 + +if( ++context.loopIndex < 20 && json.status != "FAILED" ){ + sleep(1000) + testRunner.gotoStepByName(testStepName) +} else if (context.loopIndex == 20) { + context.loopIndex = null + assert(false) +} else { + context.loopIndex = null +}</script> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveReportJson" id="0255adc3-8e64-4a6a-8127-9e26fc88d5c4" disabled="true"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveReportJson" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#TestSuite#reportJson}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="84472359-c38c-468b-b28a-f03bbd016181" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveReportPdf" id="383e53c7-3a23-4fe2-9481-3c823846806b" disabled="true"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveReportPdf" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#TestSuite#reportPdf}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="84472359-c38c-468b-b28a-f03bbd016181" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>200</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveStatus invalidUuidExpectError" id="fe250f45-cf4c-404c-a39d-25b1b4df0012"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveStatus invalidUuidExpectError" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><xml-fragment/></con:setting> + </con:settings> + <con:endpoint>${#Project#baseUrlValidatorApi}/xplan-validator-api/api/v2/status/invalid</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="ae2299b3-55a5-4171-b298-0c0fbf353fbb" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>404</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:testStep type="httprequest" name="GET BP 6.0.2 XX retrieveStatus invalidAcceptHeaderExpectError" id="dbca65e9-2ba6-4e07-8cd4-20f2bfe20b01"> + <con:settings/> + <con:config method="GET" xsi:type="con:HttpRequest" id="80a79ed8-fcfd-4604-9220-f7c650063d0c" name="GET BP 6.0.2 XX retrieveStatus invalidAcceptHeaderExpectError" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:settings> + <con:setting id="com.eviware.soapui.impl.wsdl.WsdlRequest@request-headers"><entry key="Accept" value="application/xml" xmlns="http://eviware.com/soapui/config"/></con:setting> + </con:settings> + <con:endpoint>${#TestSuite#hrefStatus}</con:endpoint> + <con:request/> + <con:assertion type="Valid HTTP Status Codes" id="ae2299b3-55a5-4171-b298-0c0fbf353fbb" name="Valid HTTP Status Codes"> + <con:configuration> + <codes>406</codes> + </con:configuration> + </con:assertion> + <con:credentials> + <con:username>${#Project#username}</con:username> + <con:password>${#Project#password}</con:password> + <con:selectedAuthProfile>Basic</con:selectedAuthProfile> + <con:addedBasicAuthenticationTypes>Basic</con:addedBasicAuthenticationTypes> + <con:preemptive>true</con:preemptive> + <con:authType>Preemptive</con:authType> + </con:credentials> + <con:jmsConfig JMSDeliveryMode="PERSISTENT"/> + <con:jmsPropertyConfig/> + <con:parameters/> + </con:config> + </con:testStep> + <con:properties/> + </con:testCase> + <con:testCase id="9fd4d5d4-1361-4668-adc4-d026000695b0" failOnError="false" failTestCaseOnErrors="true" keepSession="false" maxResults="0" name="Cleanup Properties" searchProperties="true" timeout="0" wsrmEnabled="false" wsrmVersion="1.0" wsrmAckTo="" amfAuthorisation="false" amfEndpoint="" amfLogin="" amfPassword=""> + <con:description>Gesetzte Properties werden wieder geleert.</con:description> + <con:settings/> + <con:testStep type="transfer" name="Property Transfer" id="50852410-a76f-4519-9e59-556d1486eb93"> + <con:settings/> + <con:config xsi:type="con:PropertyTransfersStep" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" ignoreEmpty="false" transferToAll="false" entitize="false" transferChildNodes="false"> + <con:name>hrefStatus</con:name> + <con:sourceType>empty</con:sourceType> + <con:sourceStep>#TestCase#</con:sourceStep> + <con:targetType>hrefStatus</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:upgraded>true</con:upgraded> + </con:transfers> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" ignoreEmpty="false" transferToAll="false" entitize="false" transferChildNodes="false"> + <con:name>reportJson</con:name> + <con:sourceType>empty</con:sourceType> + <con:sourceStep>#TestCase#</con:sourceStep> + <con:targetType>reportJson</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:upgraded>true</con:upgraded> + </con:transfers> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" ignoreEmpty="false" transferToAll="false" entitize="false" transferChildNodes="false"> + <con:name>reportPdf</con:name> + <con:sourceType>empty</con:sourceType> + <con:sourceStep>#TestCase#</con:sourceStep> + <con:targetType>reportPdf</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:upgraded>true</con:upgraded> + </con:transfers> + <con:transfers setNullOnMissingSource="true" transferTextContent="true" failOnError="true" disabled="false" entitize="false" ignoreEmpty="false" transferChildNodes="false" transferToAll="false" useXQuery="false"> + <con:name>hrefStatus2</con:name> + <con:sourceType>empty</con:sourceType> + <con:sourceStep>#TestCase#</con:sourceStep> + <con:sourcePath xsi:nil="true"/> + <con:targetType>hrefStatus2</con:targetType> + <con:targetStep>#TestSuite#</con:targetStep> + <con:targetPath xsi:nil="true"/> + <con:type>XPATH</con:type> + <con:targetTransferType>XPATH</con:targetTransferType> + <con:upgraded>true</con:upgraded> + </con:transfers> + </con:config> + </con:testStep> + <con:properties> + <con:property> + <con:name>empty</con:name> + <con:value/> + </con:property> + </con:properties> + </con:testCase> + <con:properties> + <con:property> + <con:name>hrefStatus</con:name> + <con:value/> + </con:property> + <con:property> + <con:name>hrefStatus2</con:name> + <con:value/> + </con:property> + <con:property> + <con:name>reportJson</con:name> + <con:value/> + </con:property> + <con:property> + <con:name>reportPdf</con:name> + <con:value/> + </con:property> + </con:properties> + </con:testSuite> + <con:endpointStrategy xsi:type="con:DefaultEndpointStrategy" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <con:endpoint mode="COMPLEMENT" username="${#Project#username}" password="${#Project#password}">${#Project#baseUrlValidatorApi}/xplan-validator-api/xvalidator/api/v1</con:endpoint> + </con:endpointStrategy> <con:properties> <con:property> <con:name>null</con:name> <con:value/> </con:property> + <con:property> + <con:name>baseUrlValidatorApi</con:name> + <con:value>http://localhost:8085</con:value> + </con:property> + <con:property> + <con:name>username</con:name> + <con:value/> + </con:property> + <con:property> + <con:name>password</con:name> + <con:value/> + </con:property> </con:properties> <con:wssContainer/> <con:oAuth2ProfileContainer/> diff --git a/xplan-validator/pom.xml b/xplan-validator/pom.xml index 2e04209b7f93e615085b7fee5307abe5096e2d88..2815d249ca5b331f0fb35da6b5f7793069719044 100644 --- a/xplan-validator/pom.xml +++ b/xplan-validator/pom.xml @@ -13,6 +13,8 @@ <modules> <module>xplan-validator-api</module> <module>xplan-validator-config</module> + <module>xplan-validator-executor</module> + <module>xplan-validator-storage</module> <module>xplan-validator-web</module> </modules> diff --git a/xplan-validator/xplan-validator-api/Dockerfile b/xplan-validator/xplan-validator-api/Dockerfile index 1b148be282719620e5cf24a21b9bc8f116ffdbd3..355030cf78a7f1a00aebb2a0a2d300f947a22fed 100644 --- a/xplan-validator/xplan-validator-api/Dockerfile +++ b/xplan-validator/xplan-validator-api/Dockerfile @@ -30,12 +30,11 @@ COPY --from=builder $JMX_EXPORTER_DIR $JMX_EXPORTER_DIR ENV JAVA_ADDITIONAL_ARG_JAVA17_EXPORTS="--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED" \ JAVA_ADDITIONAL_ARG_JMX_EXPORTER='-javaagent:$JMX_EXPORTER_DIR/jmx_prometheus_javaagent-1.0.1.jar=12345:$JMX_EXPORTER_DIR/jmx-exporter.config.yaml' - # set environment variables ENV DEEGREE_WORKSPACE_ROOT=/xplanbox/deegree \ - JAVA_ADDITIONAL_ARG_APP="-Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl -Djts.overlay=ng -Duser.timezone=Europe/Berlin" \ - XPLANBOX_CONFIG="/xplanbox/xplan-validator-config/" + JAVA_ADDITIONAL_ARG_APP="-Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl -Duser.timezone=Europe/Berlin" \ + XPLANBOX_CONFIG="/xplanbox/xplan-validator-config/" COPY ${WAR_FILE} /xplanbox/app.war COPY run.sh /xplanbox/ diff --git a/xplan-validator/xplan-validator-api/README.md b/xplan-validator/xplan-validator-api/README.md index 9ebfe953d76f8adc65108d8abee57ddd72451e2c..c2510183f3b24c66e8c9d8fc294da7690d6750cd 100644 --- a/xplan-validator/xplan-validator-api/README.md +++ b/xplan-validator/xplan-validator-api/README.md @@ -1,9 +1,29 @@ -## xPlanValidatorAPI +# xPlanValidatorAPI OpenAPI: #host#/xplan-validator-api/xvalidator/api/v1/ +## Development + +The application can be started locally with + +``` +mvn spring-boot:run -Dspring-boot.run.profiles=dev +``` + +(RabbitMQ required on localhost:5672 with credentials guest/guest) + +The application starts on port 8085: + +- v1 API: http://localhost:8085/xplan-validator-api/xvalidator/api/v1 + +- v2 API: http://localhost:8085/xplan-validator-api/api/v2 + ## Examples with curl +``` +export XPLAN_VALIDATOR_API_HOST=http://localhost:8085 +``` + ### validation ``` diff --git a/xplan-validator/xplan-validator-api/pom.xml b/xplan-validator/xplan-validator-api/pom.xml index de6d8d55463c55516531705b05dc14ad654c3d4d..16a78ab6a9a1075d7f1edab1a7900335f63fcc49 100755 --- a/xplan-validator/xplan-validator-api/pom.xml +++ b/xplan-validator/xplan-validator-api/pom.xml @@ -153,6 +153,10 @@ <groupId>de.latlon.product.xplanbox</groupId> <artifactId>xplan-core-api</artifactId> </dependency> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-validator-storage</artifactId> + </dependency> <dependency> <groupId>de.latlon.product.xplanbox</groupId> <artifactId>xplan-core-security</artifactId> @@ -329,6 +333,10 @@ <groupId>de.latlon.product.xplanbox</groupId> <artifactId>xplan-core-validator-events</artifactId> </dependency> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-java-sdk-s3</artifactId> + </dependency> </dependencies> <profiles> @@ -336,7 +344,7 @@ <id>docker</id> <properties> <docker-image.skip>false</docker-image.skip> - <docker-contextTarFile.expectedSizeInMat10pct>85</docker-contextTarFile.expectedSizeInMat10pct> + <docker-contextTarFile.expectedSizeInMat10pct>88</docker-contextTarFile.expectedSizeInMat10pct> </properties> <dependencies> <dependency> <!-- to copy jmx exporter stuff from docker image --> diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/SpringBootApp.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/SpringBootApp.java index c8388b2511925bdad417b50c2e4b07d755aaa26f..07c72e020861ebea6d983f77cb5e48a47c14ff43 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/SpringBootApp.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/SpringBootApp.java @@ -8,32 +8,39 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.xplanbox.api.validator; +import de.latlon.xplanbox.api.validator.api.v1.ApiV1Config; +import de.latlon.xplanbox.api.validator.api.v2.ApiV2Config; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletException; - +import jakarta.ws.rs.core.Context; +import org.glassfish.jersey.servlet.ServletContainer; +import org.glassfish.jersey.servlet.ServletProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.autoconfigure.security.servlet.ManagementWebSecurityAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication -@ComponentScan(basePackages = { "de.latlon.xplanbox.api.validator.config" }) +@ComponentScan(basePackages = { "de.latlon.xplanbox.api.validator.config", + "de.latlon.xplanbox.validator.storage.config", "de.latlon.xplanbox.validator.executor" }) @EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class }) public class SpringBootApp extends SpringBootServletInitializer { @@ -51,10 +58,27 @@ public class SpringBootApp extends SpringBootServletInitializer { return application.sources(SpringBootApp.class); } - @Override - public void onStartup(ServletContext servletContext) throws ServletException { - // TODO Auto-generated method stub + @Bean + public ServletRegistrationBean v1config(@Context ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration) { + ServletRegistrationBean v1config = new ServletRegistrationBean( + new ServletContainer(new ApiV1Config(servletContext, validatorApiConfiguration)), + "/xvalidator/api/v1/*"); + v1config.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, ApiV1Config.class.getName()); + v1config.setName(ApiV1Config.class.getName()); + v1config.setLoadOnStartup(1); + return v1config; + } + @Bean + public ServletRegistrationBean v2config(@Context ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration) { + ServletRegistrationBean v2config = new ServletRegistrationBean( + new ServletContainer(new ApiV2Config(servletContext, validatorApiConfiguration)), "/api/v2/*"); + v2config.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, ApiV2Config.class.getName()); + v2config.setName(ApiV2Config.class.getName()); + v2config.setLoadOnStartup(2); + return v2config; } } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/JerseyConfig.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/AbstractApiConfig.java similarity index 70% rename from xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/JerseyConfig.java rename to xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/AbstractApiConfig.java index 5010b71060e96e7a4764170c79f86b420946813a..9e6668e3f716e4019ff78c834bef09694989cf83 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/JerseyConfig.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/AbstractApiConfig.java @@ -8,17 +8,19 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplanbox.api.validator.config; +package de.latlon.xplanbox.api.validator.api; + +import java.util.Collections; import de.latlon.xplanbox.api.commons.ObjectMapperContextResolver; import de.latlon.xplanbox.api.commons.converter.StringListConverterProvider; @@ -26,11 +28,9 @@ import de.latlon.xplanbox.api.commons.exception.ConstraintViolationExceptionMapp import de.latlon.xplanbox.api.commons.exception.UnsupportedContentTypeExceptionMapper; import de.latlon.xplanbox.api.commons.exception.ValidatorExceptionMapper; import de.latlon.xplanbox.api.commons.exception.XPlanApiExceptionMapper; -import de.latlon.xplanbox.api.commons.openapi.OpenApiFilter; -import de.latlon.xplanbox.api.validator.v1.DefaultApi; -import de.latlon.xplanbox.api.validator.v1.InfoApi; -import de.latlon.xplanbox.api.validator.v1.ValidateApi; -import io.swagger.v3.oas.integration.SwaggerConfiguration; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import de.latlon.xplanbox.api.validator.exception.ValidationExecutionExceptionMapper; +import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Contact; @@ -38,20 +38,10 @@ import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.tags.Tag; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.server.ServerProperties; -import org.slf4j.Logger; -import org.springframework.context.annotation.Configuration; - import jakarta.servlet.ServletContext; -import jakarta.ws.rs.ApplicationPath; import jakarta.ws.rs.core.Context; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static org.slf4j.LoggerFactory.getLogger; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.server.ServerProperties; /** * Application configuration for XPlanValidator REST API. Example mapping for proxy @@ -62,56 +52,53 @@ import static org.slf4j.LoggerFactory.getLogger; * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ -@ApplicationPath("/xvalidator/api/v1") -@Configuration -public class JerseyConfig extends ResourceConfig { - - private static final Logger LOG = getLogger(JerseyConfig.class); - - private static final String APP_PATH = "xvalidator/api/v1"; +public abstract class AbstractApiConfig extends ResourceConfig { - public JerseyConfig(@Context ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration) { + public AbstractApiConfig(@Context ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration) { property(ServerProperties.WADL_FEATURE_DISABLE, true); register(new ObjectMapperContextResolver()); - register(InfoApi.class); - register(ValidateApi.class); + BaseOpenApiResource openApiResource = createDefaultApi(servletContext, validatorApiConfiguration); + register(openApiResource); + register(ConstraintViolationExceptionMapper.class); register(UnsupportedContentTypeExceptionMapper.class); + register(ValidationExecutionExceptionMapper.class); register(ValidatorExceptionMapper.class); register(XPlanApiExceptionMapper.class); register(StringListConverterProvider.class); + } + + protected abstract BaseOpenApiResource createDefaultApi(ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration); + protected OpenAPI createOpenAPI(ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration, + String apiPath) { OpenAPI openApi = new OpenAPI(); - openApi.setInfo(new Info().title("XPlanValidatorAPI") - .version("1.3.0") - .description("XPlanValidator REST API") - .termsOfService(getTermsOfService(validatorApiConfiguration)) - .license(new License().name("Apache 2.0").url("http://www.apache.org/licenses/LICENSE-2.0.html"))); + addInfo(openApi, validatorApiConfiguration); addContact(openApi, validatorApiConfiguration); - openApi.servers(servers(servletContext, validatorApiConfiguration)); - Tag tag = createTag(validatorApiConfiguration); - openApi.tags(Collections.singletonList(tag)); - - DefaultApi openApiResource = new DefaultApi(); - SwaggerConfiguration oasConfig = new SwaggerConfiguration().openAPI(openApi) - .filterClass(OpenApiFilter.class.getCanonicalName()) - .prettyPrint(true) - .resourcePackages(Stream.of("de.latlon.xplanbox.api.validator.v1").collect(Collectors.toSet())); - - openApiResource.setOpenApiConfiguration(oasConfig); - register(openApiResource); - LOG.info("XPlanApiValidator successfully initialized"); + addServers(openApi, servletContext, validatorApiConfiguration, apiPath); + addTag(openApi, validatorApiConfiguration); + return openApi; } - private Tag createTag(ValidatorApiConfiguration validatorApiConfiguration) { + private void addTag(OpenAPI openApi, ValidatorApiConfiguration validatorApiConfiguration) { Tag tag = new Tag().name("validate").description("Validate XPlanGML documents"); if (validatorApiConfiguration != null && validatorApiConfiguration.getDocumentationUrl() != null) { tag.externalDocs(new ExternalDocumentation().description("xPlanBox") .url(validatorApiConfiguration.getDocumentationUrl())); } - return tag; + openApi.tags(Collections.singletonList(tag)); + } + + private void addInfo(OpenAPI openApi, ValidatorApiConfiguration validatorApiConfiguration) { + openApi.setInfo(new Info().title("XPlanValidatorAPI") + .version("1.3.0") + .description("XPlanValidator REST API") + .termsOfService(getTermsOfService(validatorApiConfiguration)) + .license(new License().name("Apache 2.0").url("http://www.apache.org/licenses/LICENSE-2.0.html"))); } private void addContact(OpenAPI openApi, ValidatorApiConfiguration validatorApiConfiguration) { @@ -121,13 +108,15 @@ public class JerseyConfig extends ResourceConfig { } } - private List<Server> servers(ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration) { - String serverUrl = getServerUrl(servletContext, validatorApiConfiguration); + private void addServers(OpenAPI openApi, ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration, String apiPath) { + String serverUrl = getServerUrl(servletContext, validatorApiConfiguration, apiPath); Server server = new Server().url(serverUrl); - return Collections.singletonList(server); + openApi.servers(Collections.singletonList(server)); } - private String getServerUrl(ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration) { + private String getServerUrl(ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration, + String apiPath) { StringBuilder serverUrl = new StringBuilder(); if (validatorApiConfiguration != null && validatorApiConfiguration.getApiUrl() != null) { String apiEndpoint = validatorApiConfiguration.getApiUrl().toString(); @@ -138,7 +127,7 @@ public class JerseyConfig extends ResourceConfig { } if (!serverUrl.toString().endsWith("/")) serverUrl.append("/"); - serverUrl.append(APP_PATH); + serverUrl.append(apiPath); return serverUrl.toString(); } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v1/ApiV1Config.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v1/ApiV1Config.java new file mode 100644 index 0000000000000000000000000000000000000000..989a1a3b2bd65be3b127cc337d618efb16701bf6 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v1/ApiV1Config.java @@ -0,0 +1,78 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.api.v1; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Set; + +import de.latlon.xplanbox.api.commons.openapi.OpenApiFilter; +import de.latlon.xplanbox.api.validator.api.AbstractApiConfig; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import de.latlon.xplanbox.api.validator.v1.DefaultApi; +import de.latlon.xplanbox.api.validator.v1.InfoApi; +import de.latlon.xplanbox.api.validator.v1.ValidateApi; +import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; +import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.OpenAPI; +import jakarta.servlet.ServletContext; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Context; +import org.slf4j.Logger; + +/** + * Application configuration for XPlanValidator REST API. Example mapping for proxy + * mapping: http://xplanbox.lat-lon.de/xvalidator/api/v1/ -> + * http://host:8080/xplan-api-validator/xvalidator/api/v1/ Public address: + * http://xplanbox.lat-lon.de/xvalidator/ Internal address: + * http://host:8080/xplan-api-validator/xvalidator/ + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + */ +@ApplicationPath("/xvalidator/api/v1") +public class ApiV1Config extends AbstractApiConfig { + + private static final Logger LOG = getLogger(ApiV1Config.class); + + private static final String APP_PATH = "xvalidator/api/v1"; + + public ApiV1Config(@Context ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration) { + super(servletContext, validatorApiConfiguration); + + register(InfoApi.class); + register(ValidateApi.class); + + LOG.info("XPlanApiValidator v1 successfully initialized"); + } + + public BaseOpenApiResource createDefaultApi(ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration) { + DefaultApi openApiResourceV1 = new DefaultApi(); + OpenAPI v1OpenApi = createOpenAPI(servletContext, validatorApiConfiguration, APP_PATH); + SwaggerConfiguration oasConfigV1 = new SwaggerConfiguration().openAPI(v1OpenApi) + .filterClass(OpenApiFilter.class.getCanonicalName()) + .prettyPrint(true) + .resourcePackages(Set.of("de.latlon.xplanbox.api.validator.v1")); + openApiResourceV1.setOpenApiConfiguration(oasConfigV1); + return openApiResourceV1; + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Config.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Config.java new file mode 100644 index 0000000000000000000000000000000000000000..97e57359f2cd33b8e927619602c0d4235bdba9c8 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Config.java @@ -0,0 +1,81 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.api.v2; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Set; + +import de.latlon.xplanbox.api.validator.api.AbstractApiConfig; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import de.latlon.xplanbox.api.validator.api.v2.ApiV2Filter; +import de.latlon.xplanbox.api.validator.v2.DefaultApi2; +import de.latlon.xplanbox.api.validator.v2.InfoApi2; +import de.latlon.xplanbox.api.validator.v2.StatusApi; +import de.latlon.xplanbox.api.validator.v2.ValidateApi2; +import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; +import io.swagger.v3.oas.integration.SwaggerConfiguration; +import io.swagger.v3.oas.models.OpenAPI; +import jakarta.servlet.ServletContext; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Context; +import org.slf4j.Logger; + +/** + * Application configuration for XPlanValidator REST API. Example mapping for proxy + * mapping: http://xplanbox.lat-lon.de/xvalidator/api/v1/ -> + * http://host:8080/xplan-api-validator/xvalidator/api/v1/ Public address: + * http://xplanbox.lat-lon.de/xvalidator/ Internal address: + * http://host:8080/xplan-api-validator/xvalidator/ + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + */ +@ApplicationPath("/api/v2") +public class ApiV2Config extends AbstractApiConfig { + + private static final Logger LOG = getLogger(ApiV2Config.class); + + private static final String APP_PATH = "api/v2"; + + public ApiV2Config(@Context ServletContext servletContext, ValidatorApiConfiguration validatorApiConfiguration) { + super(servletContext, validatorApiConfiguration); + + register(InfoApi2.class); + register(ValidateApi2.class); + register(StatusApi.class); + + LOG.info("XPlanApiValidator v2 successfully initialized"); + } + + @Override + protected BaseOpenApiResource createDefaultApi(ServletContext servletContext, + ValidatorApiConfiguration validatorApiConfiguration) { + DefaultApi2 openApiResourceV2 = new DefaultApi2(); + OpenAPI v2OpenApi = createOpenAPI(servletContext, validatorApiConfiguration, APP_PATH); + SwaggerConfiguration oasConfigV2 = new SwaggerConfiguration().openAPI(v2OpenApi) + .filterClass(ApiV2Filter.class.getCanonicalName()) + .prettyPrint(true) + .resourcePackages(Set.of("de.latlon.xplanbox.api.validator.v2")); + openApiResourceV2.setOpenApiConfiguration(oasConfigV2); + return openApiResourceV2; + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Filter.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Filter.java new file mode 100644 index 0000000000000000000000000000000000000000..1311ea46cacf65b444633270c834acdc536c4236 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/api/v2/ApiV2Filter.java @@ -0,0 +1,22 @@ +package de.latlon.xplanbox.api.validator.api.v2; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.latlon.xplanbox.api.commons.openapi.OpenApiFilter; + +/** + * Filter to keep only v2 Api elements. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +public class ApiV2Filter extends OpenApiFilter { + + protected String createNewKey(String path) { + Pattern pattern = Pattern.compile("\\/api\\/v[\\d_\\-\\.]*(\\/|)"); + Matcher matcher = pattern.matcher(path); + return matcher.replaceFirst("/"); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/ApplicationContext.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/ApplicationContext.java index 5478f45abcb1d5037ce3b219f2c6451584d526f4..a416556599ec75a1c816e009e38fdc8a82910cc3 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/ApplicationContext.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/ApplicationContext.java @@ -8,18 +8,27 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.xplanbox.api.validator.config; +import static java.nio.file.Files.createTempDirectory; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + import de.latlon.xplan.commons.configuration.PropertiesLoader; import de.latlon.xplan.commons.configuration.SystemPropertyPropertiesLoader; import de.latlon.xplan.manager.web.shared.ConfigurationException; @@ -39,31 +48,26 @@ import de.latlon.xplan.validator.semantic.profile.SemanticProfilesCreator; import de.latlon.xplan.validator.semantic.xquery.XQuerySemanticValidator; import de.latlon.xplan.validator.syntactic.SyntacticValidator; import de.latlon.xplan.validator.syntactic.SyntacticValidatorImpl; -import de.latlon.xplan.validator.wms.config.ValidatorWmsContext; import de.latlon.xplanbox.api.commons.handler.SystemConfigHandler; import de.latlon.xplanbox.security.config.SecurityContext; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.config.AmazonS3Context; +import de.latlon.xplanbox.validator.storage.filesystem.FileSystemValidationExecutionStorage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Profile; import org.springframework.core.io.ResourceLoader; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static java.nio.file.Files.createTempDirectory; - /** * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ @Configuration @ComponentScan(basePackages = { "de.latlon.xplanbox.api.validator.handler", "de.latlon.xplanbox.api.validator.v1" }) -@Import({ SecurityContext.class, ValidatorWmsContext.class }) +@Import({ SecurityContext.class, AmazonS3Context.class }) public class ApplicationContext { @Autowired @@ -111,7 +115,7 @@ public class ApplicationContext { @Bean public SemanticProfiles semanticProfiles(ValidatorConfiguration validatorConfiguration, PropertiesLoader validatorPropertiesLoader, - @Value("#{environment.XPLAN_VALIDATOR_PROFILES}") String activatedProfiles) throws ConfigurationException { + @Value("${xplanbox.validation.profiles}") String activatedProfiles) throws ConfigurationException { List<String> activatedProfilesList = activatedProfiles != null ? Arrays.asList(activatedProfiles.split(",")) : Collections.emptyList(); SemanticProfilesCreator semanticProfilesCreator = new SemanticProfilesCreator(validatorConfiguration, @@ -155,4 +159,11 @@ public class ApplicationContext { return new ReportWriter(); } + @Bean + @Profile("!s3execution") + public ValidationExecutionStorage validationExecutionStorage( + @Value("${xplanbox.validation.fsdirectory}") Optional<Path> fsExecutionDir) throws IOException { + return new FileSystemValidationExecutionStorage(fsExecutionDir); + } + } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/RabbitConfiguration.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/RabbitConfiguration.java index 2e1cdec46bed65c173791978e160c269e6a85c66..b0bbd5fb885d9d9ebb6f02f5365a9fc9892b0824 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/RabbitConfiguration.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/config/RabbitConfiguration.java @@ -24,9 +24,10 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import de.latlon.core.validator.events.RabbitEmitterConfig; +import de.latlon.core.validator.events.RabbitReceiverConfig; @Configuration -@Import({ RabbitEmitterConfig.class }) +@Import({ RabbitEmitterConfig.class, RabbitReceiverConfig.class }) public class RabbitConfiguration { } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/InvalidValidationUuid.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/InvalidValidationUuid.java new file mode 100644 index 0000000000000000000000000000000000000000..2d4702c56b74b8e41872c0acf447d947dc1850d9 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/InvalidValidationUuid.java @@ -0,0 +1,44 @@ +/*- + * #%L + * xplan-core-api - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.exception; + +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; + +import de.latlon.xplanbox.api.commons.exception.XPlanApiException; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class InvalidValidationUuid extends XPlanApiException { + + private static final String EXCEPTION_MESSAGE = "Validation with uuid %s is not available!"; + + public InvalidValidationUuid(String uuid) { + super(String.format(EXCEPTION_MESSAGE, uuid)); + } + + @Override + public int getStatusCode() { + return NOT_FOUND.getStatusCode(); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationExecutionExceptionMapper.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationExecutionExceptionMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..8bbc0ca70fe915a86a2de6ca591bff42fa5265d5 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationExecutionExceptionMapper.java @@ -0,0 +1,44 @@ +/*- + * #%L + * xplan-core-api - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.exception; + +import de.latlon.xplanbox.validator.storage.ValidationExecutionException; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.ext.ExceptionMapper; +import jakarta.ws.rs.ext.Provider; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +@Provider +public class ValidationExecutionExceptionMapper implements ExceptionMapper<ValidationExecutionException> { + + @Override + public Response toResponse(ValidationExecutionException exception) { + return Response.status(mapStatusCode(exception)).entity(exception.getMessage()).build(); + } + + private int mapStatusCode(ValidationExecutionException exception) { + return exception.getErrorType().getStatusCode(); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationTimeout.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationTimeout.java new file mode 100644 index 0000000000000000000000000000000000000000..10176675844726087f42d70d6e4670446d577513 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/exception/ValidationTimeout.java @@ -0,0 +1,44 @@ +/*- + * #%L + * xplan-core-api - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.exception; + +import static jakarta.ws.rs.core.Response.Status.REQUEST_TIMEOUT; + +import de.latlon.xplanbox.api.commons.exception.XPlanApiException; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class ValidationTimeout extends XPlanApiException { + + private static final String EXCEPTION_MESSAGE = "Timeout of validation %s"; + + public ValidationTimeout(String uuid) { + super(String.format(EXCEPTION_MESSAGE, uuid)); + } + + @Override + public int getStatusCode() { + return REQUEST_TIMEOUT.getStatusCode(); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapper.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..402320200c8cd60addea10b0ed3ccab662d82ce8 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapper.java @@ -0,0 +1,102 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.handler; + +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationFinishedEvent; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.xplanbox.api.validator.exception.ValidationTimeout; +import jakarta.inject.Singleton; +import org.joda.time.Instant; +import org.slf4j.Logger; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +@Component +@Singleton +public class AsyncValidationWrapper { + + private static final Logger LOG = getLogger(AsyncValidationWrapper.class); + + private final EventSender validationRequestNotifier; + + private final Map<String, Boolean> waitingUuids = Collections.synchronizedMap(new HashMap<>()); + + protected AsyncValidationWrapper(EventSender validationRequestNotifier) { + this.validationRequestNotifier = validationRequestNotifier; + } + + public boolean validate(ValidationRequestedEvent event) throws ValidationTimeout { + validationRequestNotifier.sendEvent(event); + + String uuid = event.getUuid(); + LOG.info("Event sent. Waiting for validation of {}", uuid); + waitingUuids.put(uuid, null); + + long maxWaitDate = Long.MAX_VALUE; // TODO: which value would be correct? + boolean waitFinished = false; + while (!waitFinished) { + synchronized (waitingUuids) { + try { + waitingUuids.wait(); + } + catch (InterruptedException e) { + // ignoring + } + } + + if (waitingUuids.get(uuid) != null) { + LOG.info("Finished waiting for validation of {}", uuid); + waitFinished = true; + } + else if (Instant.now().isAfter(maxWaitDate)) { + throw new ValidationTimeout(uuid); + } + else { + LOG.info("Still waiting for validation of {}", uuid); + } + } + + return waitingUuids.remove(uuid); + } + + @RabbitListener(queues = "#{internalFanoutQueue.name}") + public void someValidationFinished(ValidationFinishedEvent t) { + String uuid = t.getUuid(); + if (waitingUuids.containsKey(uuid)) { + LOG.info("Notifying waiting threads for finished validation of {}", uuid); + waitingUuids.put(uuid, Boolean.TRUE); + synchronized (waitingUuids) { + waitingUuids.notifyAll(); + } + } + else { + LOG.info("No waiting threads for {}. Ignoring", uuid); + } + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/DefaultApi.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/DefaultApi.java index f2f2f28f69348a399fcbb1796395ad44649a3f3b..f1a30ced83004425825b449342bb8464e1840a98 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/DefaultApi.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/DefaultApi.java @@ -20,10 +20,12 @@ */ package de.latlon.xplanbox.api.validator.v1; +import static io.swagger.v3.oas.integration.api.OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.responses.ApiResponse; - import jakarta.servlet.ServletConfig; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @@ -34,15 +36,15 @@ import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.UriInfo; -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; - /** * Controller class for handling access to the default resource returning the OpenAPI * document. * * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> * @since 4.0 + * @deprecated since 8.0 use API V2 instead */ +@Deprecated @Path("/") @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2020-08-27T12:32:04.497+02:00[Europe/Berlin]") @@ -63,4 +65,12 @@ public class DefaultApi extends BaseOpenApiResource { return super.getOpenApi(headers, this.config, this.app, uriInfo, APPLICATION_JSON); } + @Override + protected String getContextId(ServletConfig config) { + String contextId = super.getContextId(config); + if (OPENAPI_CONTEXT_ID_DEFAULT.equals(contextId)) + return contextId + getClass().getSimpleName(); + return contextId; + } + } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/InfoApi.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/InfoApi.java index 733b6a05bb71786923ce45d9c9fdc215d1505824..01882062c06aeda0583737ef56c4da3e16fe9a82 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/InfoApi.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/InfoApi.java @@ -40,7 +40,9 @@ import java.io.IOException; * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> * @since 4.0 + * @deprecated since 8.0 use API V2 instead */ +@Deprecated @Path("/info") @jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2020-08-27T12:32:04.497+02:00[Europe/Berlin]") diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/ValidateApi.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/ValidateApi.java index 0c88a02c1d6a6bea5cbd7bed34994e936d9dce01..f89dec702834760b1673b59f33e7b0492c40fc27 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/ValidateApi.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v1/ValidateApi.java @@ -8,31 +8,45 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.xplanbox.api.validator.v1; -import de.latlon.core.validator.events.ValidationRequestNotifier; +import static de.latlon.xplan.commons.util.ContentTypeChecker.checkContentTypesOfXPlanArchiveOrGml; +import static de.latlon.xplan.commons.util.TextPatternConstants.SIMPLE_NAME_PATTERN; +import static de.latlon.xplanbox.api.commons.ValidatorConverter.createValidationSettings; +import static de.latlon.xplanbox.api.commons.ValidatorConverter.detectOrCreateValidationName; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_PDF; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_PDF_TYPE; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP_TYPE; +import static io.swagger.v3.oas.annotations.enums.Explode.FALSE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_XML; +import static jakarta.ws.rs.core.MediaType.APPLICATION_XML_TYPE; +import static jakarta.ws.rs.core.MediaType.TEXT_XML; +import static jakarta.ws.rs.core.MediaType.TEXT_XML_TYPE; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.databind.ObjectMapper; import de.latlon.core.validator.events.ValidationRequestedEvent; -import de.latlon.xplan.commons.archive.XPlanArchive; -import de.latlon.xplan.commons.util.UnsupportedContentTypeException; -import de.latlon.xplan.validator.ValidatorException; -import de.latlon.xplan.validator.report.ValidatorReport; import de.latlon.xplan.validator.web.shared.ValidationSettings; -import de.latlon.xplanbox.api.commons.ValidationReportBuilder; -import de.latlon.xplanbox.api.commons.exception.InvalidXPlanGmlOrArchive; -import de.latlon.xplanbox.api.commons.exception.UnsupportedHeaderValue; -import de.latlon.xplanbox.api.commons.exception.UnsupportedParameterValue; +import de.latlon.xplanbox.api.commons.ObjectMapperContextResolver; import de.latlon.xplanbox.api.commons.v1.model.ValidationReport; -import de.latlon.xplanbox.api.validator.handler.ValidationHandler; +import de.latlon.xplanbox.api.validator.handler.AsyncValidationWrapper; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -40,9 +54,6 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; import io.swagger.v3.oas.annotations.responses.ApiResponse; -import org.apache.commons.io.FileUtils; -import org.springframework.beans.factory.annotation.Autowired; - import jakarta.validation.Valid; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.DefaultValue; @@ -56,28 +67,7 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Request; import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Variant; -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.util.List; - -import static de.latlon.xplan.commons.util.ContentTypeChecker.checkContentTypesOfXPlanArchiveOrGml; -import static de.latlon.xplan.commons.util.TextPatternConstants.SIMPLE_NAME_PATTERN; -import static de.latlon.xplanbox.api.commons.ValidatorConverter.createValidationSettings; -import static de.latlon.xplanbox.api.commons.ValidatorConverter.detectOrCreateValidationName; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_PDF; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_PDF_TYPE; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP_TYPE; -import static io.swagger.v3.oas.annotations.enums.Explode.FALSE; -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; -import static jakarta.ws.rs.core.MediaType.APPLICATION_XML; -import static jakarta.ws.rs.core.MediaType.APPLICATION_XML_TYPE; -import static jakarta.ws.rs.core.MediaType.TEXT_XML; -import static jakarta.ws.rs.core.MediaType.TEXT_XML_TYPE; +import org.springframework.beans.factory.annotation.Autowired; /** * Controller class for handling access to the validate resource. @@ -85,17 +75,17 @@ import static jakarta.ws.rs.core.MediaType.TEXT_XML_TYPE; * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> * @since 4.0 + * @deprecated since 8.0 use API V2 instead */ +@Deprecated @Path("/validate") -@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", - date = "2020-08-26T09:59:16.298+02:00[Europe/Berlin]") public class ValidateApi { @Autowired - private ValidationHandler validationHandler; + ValidationExecutionStorage validationExecutionStorage; @Autowired - private ValidationRequestNotifier validationRequestNotifier; + AsyncValidationWrapper validationHandler; @POST @Consumes({ "text/xml", "application/gml+xml" }) @@ -157,14 +147,13 @@ public class ValidateApi { @QueryParam("profiles") @Parameter( description = "Names of profiles which shall be additionaly used for validation", explode = FALSE) List<String> profiles) - throws IOException, ValidatorException, URISyntaxException, InvalidXPlanGmlOrArchive, - UnsupportedContentTypeException, UnsupportedParameterValue, UnsupportedHeaderValue { + throws Exception { checkContentTypesOfXPlanArchiveOrGml(body.toPath()); String validationName = detectOrCreateValidationName(xFilename, name); - XPlanArchive archive = validationHandler.createArchiveFromGml(body, validationName); - return validate(request, xFilename, validationName, skipSemantisch, skipGeometrisch, skipFlaechenschluss, - skipGeltungsbereich, skipLaufrichtung, profiles, archive); + return doValidate(body, request, xFilename, validationName, skipSemantisch, skipGeometrisch, + skipFlaechenschluss, skipGeltungsbereich, skipLaufrichtung, profiles, + ValidationRequestedEvent.OriginFile.GML); } @POST @@ -182,47 +171,55 @@ public class ValidateApi { @QueryParam("profiles") @Parameter( description = "Names of profiles which shall be additionaly used for validation", explode = FALSE) List<String> profiles) - throws IOException, ValidatorException, URISyntaxException, InvalidXPlanGmlOrArchive, - UnsupportedContentTypeException, UnsupportedParameterValue, UnsupportedHeaderValue { - checkContentTypesOfXPlanArchiveOrGml(body.toPath()); - String validationName = detectOrCreateValidationName(xFilename, name); - XPlanArchive archive = validationHandler.createArchiveFromZip(body, validationName); + throws Exception { - return validate(request, xFilename, validationName, skipSemantisch, skipGeometrisch, skipFlaechenschluss, - skipGeltungsbereich, skipLaufrichtung, profiles, archive); + return doValidate(body, request, xFilename, name, skipSemantisch, skipGeometrisch, skipFlaechenschluss, + skipGeltungsbereich, skipLaufrichtung, profiles, ValidationRequestedEvent.OriginFile.ZIP); } - private Response validate(Request request, String xFileName, String validationName, Boolean skipSemantisch, + private Response doValidate(File body, Request request, String xFilename, String name, Boolean skipSemantisch, Boolean skipGeometrisch, Boolean skipFlaechenschluss, Boolean skipGeltungsbereich, Boolean skipLaufrichtung, - List<String> profiles, XPlanArchive archive) throws ValidatorException, IOException { + List<String> profiles, ValidationRequestedEvent.OriginFile originFile) throws Exception { + checkContentTypesOfXPlanArchiveOrGml(body.toPath()); + + String uuid = validationExecutionStorage.addPlanToValidate(body.toPath()); + String validationName = detectOrCreateValidationName(xFilename, name); MediaType mediaType = detectRequestedMediaType(request); + final ValidationRequestedEvent.MediaType requestedMediaType = detectValidationEventMediaType(mediaType); ValidationSettings settings = createValidationSettings(validationName, skipGeometrisch, skipSemantisch, skipFlaechenschluss, skipGeltungsbereich, skipLaufrichtung, profiles); - validationRequestNotifier.sendEvent(new ValidationRequestedEvent(settings)); + ValidationRequestedEvent event = new ValidationRequestedEvent(uuid, settings, xFilename, requestedMediaType, + originFile); - ValidatorReport validatorReport = validationHandler.validate(archive, xFileName, settings); - if (APPLICATION_ZIP_TYPE.equals(mediaType)) { - java.nio.file.Path report = validationHandler.zipReports(validatorReport); - return Response.ok(FileUtils.readFileToByteArray(report.toFile())) - .type(APPLICATION_ZIP) - .header("Content-Disposition", "attachment; filename=\"" + validationName + ".zip\"") - .build(); - } - if (APPLICATION_PDF_TYPE.equals(mediaType)) { - java.nio.file.Path report = validationHandler.writePdfReport(validatorReport); - return Response.ok(Files.readAllBytes(report)) - .type(APPLICATION_PDF) - .header("Content-Disposition", "attachment; filename=\"" + validationName + ".pdf\"") + return validate(event, mediaType); + } + + private Response validate(ValidationRequestedEvent validationRequestedEvent, MediaType mediaType) throws Exception { + + ValidationSettings settings = validationRequestedEvent.getSettings(); + validationHandler.validate(validationRequestedEvent); + + de.latlon.core.validator.events.ValidationRequestedEvent.MediaType requestedMediaType = validationRequestedEvent + .getRequestedMediaType(); + ValidationExecutionStorage.ReportType reportType = ValidationExecutionStorage.ReportType + .byFileExtension(requestedMediaType.getFileExtension()); + byte[] report = validationExecutionStorage.retrieveReport(validationRequestedEvent.getUuid(), reportType); + String filename = settings.getValidationName() + requestedMediaType.getFileExtension(); + // xml not directly supported anymore => parse and convert + if (APPLICATION_XML_TYPE.equals(mediaType) || TEXT_XML_TYPE.equals(mediaType)) { + ValidationReport validationReport = createReportFileFrom(report); + return Response.ok(validationReport) // + .type(mediaType) // + .header("Content-Disposition", "attachment; filename=\"" + filename + "\"") // .build(); } - URI wmsUrl = validationHandler.addToWms(archive); - ValidationReport validationReport = new ValidationReportBuilder().validatorReport(validatorReport) - .filename(xFileName) - .wmsUrl(wmsUrl) + + return Response.ok(report) // + .type(mediaType) // + .header("Content-Disposition", "attachment; filename=\"" + filename + "\"") // .build(); - return Response.ok(validationReport).build(); } private MediaType detectRequestedMediaType(Request request) { @@ -234,4 +231,17 @@ public class ValidateApi { return selectVariant.getMediaType(); } + private static ValidationRequestedEvent.MediaType detectValidationEventMediaType(MediaType mediaType) { + if (APPLICATION_ZIP_TYPE.equals(mediaType)) + return ValidationRequestedEvent.MediaType.ZIP; + if (APPLICATION_PDF_TYPE.equals(mediaType)) + return ValidationRequestedEvent.MediaType.PDF; + return ValidationRequestedEvent.MediaType.JSON; + } + + private ValidationReport createReportFileFrom(byte[] reportAsJsonInByte) throws IOException { + ObjectMapper mapper = new ObjectMapperContextResolver().getContext(ValidationReport.class); + return mapper.readValue(reportAsJsonInByte, ValidationReport.class); + } + } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2.java new file mode 100644 index 0000000000000000000000000000000000000000..b1961c26c8b9bd890070ebbf3af1274e48b3cd1a --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2.java @@ -0,0 +1,72 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static io.swagger.v3.oas.integration.api.OpenApiContext.OPENAPI_CONTEXT_ID_DEFAULT; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import io.swagger.v3.jaxrs2.integration.resources.BaseOpenApiResource; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.ServletConfig; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; + +/** + * Controller class for handling access to the default resource returning the OpenAPI + * document. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +@Path("/") +public class DefaultApi2 extends BaseOpenApiResource { + + @Context + private ServletConfig config; + + @Context + private Application app; + + @GET + @Produces({ "application/json" }) + @Operation(summary = "OpenAPI document", description = "API documentation", + responses = { @ApiResponse(responseCode = "200", description = "successful operation"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response openApi(@Context HttpHeaders headers, @Context UriInfo uriInfo) throws Exception { + return super.getOpenApi(headers, this.config, this.app, uriInfo, APPLICATION_JSON); + } + + @Override + protected String getContextId(ServletConfig config) { + String contextId = super.getContextId(config); + if (OPENAPI_CONTEXT_ID_DEFAULT.equals(contextId)) + return contextId + getClass().getSimpleName(); + return contextId; + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/InfoApi2.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/InfoApi2.java new file mode 100644 index 0000000000000000000000000000000000000000..07511f5bb3f084ddca1465e070cd2eee45f7f0fe --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/InfoApi2.java @@ -0,0 +1,62 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import de.latlon.xplanbox.api.commons.v1.model.SystemConfig; +import de.latlon.xplanbox.api.validator.handler.ConfigHandler; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.springframework.beans.factory.annotation.Autowired; + +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import java.io.IOException; + +/** + * Controller class for handling access to the application info resource. + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> + * @since 4.0 + */ +@Path("/info") +public class InfoApi2 { + + @Autowired + private ConfigHandler configHandler; + + @GET + @Produces({ "application/json" }) + @Operation(summary = "Show system and application configuration", + description = "Returns the system and application configuration", + responses = { + @ApiResponse(responseCode = "200", description = "successful operation", + content = @Content(schema = @Schema(implementation = SystemConfig.class))), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response showConfig() throws IOException { + return Response.ok().entity(configHandler.describeSystem()).build(); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/StatusApi.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/StatusApi.java new file mode 100644 index 0000000000000000000000000000000000000000..0c2a0065fd854ae4cd9741cf70422f43442f1771 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/StatusApi.java @@ -0,0 +1,83 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.net.URISyntaxException; + +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.api.validator.exception.InvalidValidationUuid; +import de.latlon.xplanbox.api.validator.v2.model.StatusNotification; +import de.latlon.xplanbox.api.validator.v2.model.ValidationReceipt; +import de.latlon.xplanbox.validator.storage.Status; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.PathParam; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Response; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @since 8.0 + */ +@Path("/status") +public class StatusApi { + + @Autowired + private ValidationExecutionStorage validationExecutionStorage; + + @GET + @Produces({ "application/json" }) + @Path("/{uuid}") + @Operation(summary = "Status of a validation", description = "Returns the status of a validation", + tags = { "status" }, + responses = { + @ApiResponse(responseCode = "200", description = "ValidationReport", + content = { @Content(mediaType = APPLICATION_JSON, + schema = @Schema(implementation = ValidationReceipt.class)) }), + @ApiResponse(responseCode = "404", + description = "Invalid uuid, no validation with the passed uuid found"), + @ApiResponse(responseCode = "406", description = "Requested format is not available") }) + public Response status( + @PathParam("uuid") @Parameter(description = "UUID of the validation", + example = "0a0cedbc-bf3f-4f1f-bdaf-ea0e52075540") String uuid) + throws InvalidValidationUuid, StorageException, URISyntaxException { + try { + Status status = validationExecutionStorage.retrieveStatus(uuid); + return Response.ok(StatusNotification.fromStatus(status)).build(); + } + catch (StorageException e) { + if (e.getStatusCode() == 404) { + throw new InvalidValidationUuid(uuid); + } + throw e; + } + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2.java new file mode 100644 index 0000000000000000000000000000000000000000..0b60497c9ee0a93071935552bedef320cc371d80 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2.java @@ -0,0 +1,197 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static de.latlon.xplan.commons.util.ContentTypeChecker.checkContentTypesOfXPlanArchiveOrGml; +import static de.latlon.xplan.commons.util.TextPatternConstants.SIMPLE_NAME_PATTERN; +import static de.latlon.xplanbox.api.commons.ValidatorConverter.createValidationSettings; +import static de.latlon.xplanbox.api.commons.ValidatorConverter.detectOrCreateValidationName; +import static de.latlon.xplanbox.api.validator.v2.model.Link.RelEnum.STATUS; +import static io.swagger.v3.oas.annotations.enums.Explode.FALSE; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplan.commons.util.UnsupportedContentTypeException; +import de.latlon.xplan.validator.ValidatorException; +import de.latlon.xplan.validator.web.shared.ValidationSettings; +import de.latlon.xplanbox.api.commons.exception.InvalidXPlanGmlOrArchive; +import de.latlon.xplanbox.api.commons.exception.UnsupportedHeaderValue; +import de.latlon.xplanbox.api.commons.exception.UnsupportedParameterValue; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import de.latlon.xplanbox.api.validator.v2.model.Link; +import de.latlon.xplanbox.api.validator.v2.model.ValidationReceipt; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import io.swagger.v3.oas.annotations.Hidden; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import jakarta.servlet.ServletContext; +import jakarta.validation.Valid; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.DefaultValue; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Controller class for handling access to the validate resource. + * + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +@Path("/validate") +public class ValidateApi2 { + + @Autowired + private ValidationExecutionStorage validationExecutionStorage; + + @Autowired + private EventSender validationRequestNotifier; + + @Autowired + private ValidatorApiConfiguration validatorApiConfiguration; + + @Context + private ServletContext servletContext; + + @POST + @Consumes({ "text/xml", "application/gml+xml" }) + @Produces({ "application/json" }) + @Operation(summary = "Validate XPlanGML or XPlanArchive", description = "Validates XPlanGML or XPlanArchive file", + tags = { "validate" }, + responses = { + @ApiResponse(responseCode = "200", description = "ValidationReport", + content = { @Content(mediaType = APPLICATION_JSON, + schema = @Schema(implementation = ValidationReceipt.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid input"), + @ApiResponse(responseCode = "406", description = "Requested format is not available"), + @ApiResponse(responseCode = "415", + description = "Unsupported media type or content - only xml/gml, zip are accepted; all zip files entries must also match the supported content types for XPlanArchives") }, + requestBody = @RequestBody(content = { + @Content(mediaType = "application/octet-stream", + schema = @Schema(type = "string", format = "binary", + description = "XPlanGML or XPlanArchive (application/zip) file to upload")), + @Content(mediaType = "application/zip", + schema = @Schema(type = "string", format = "binary", + description = "XPlanGML or XPlanArchive (application/zip) file to upload")), + @Content(mediaType = "application/x-zip", + schema = @Schema(type = "string", format = "binary", + description = "XPlanGML or XPlanArchive (application/zip) file to upload")), + @Content(mediaType = "application/x-zip-compressed", + schema = @Schema(type = "string", format = "binary", + description = "XPlanGML or XPlanArchive (application/zip) file to upload")), + @Content(mediaType = "text/xml", + schema = @Schema(type = "string", format = "binary", description = "XPlanGML to upload")), + @Content(mediaType = "application/gml+xml", schema = @Schema(type = "string", format = "binary", + description = "XPlanGML to upload")) })) + public Response validate(@Valid File body, + @HeaderParam("X-Filename") @Parameter(description = "Name of the file to be uploaded", + example = "File names such as xplan.gml, xplan.xml, xplan.zip", + schema = @Schema(pattern = SIMPLE_NAME_PATTERN)) String xFilename, + @QueryParam("name") @Parameter(description = "Name of the validation", + schema = @Schema(pattern = SIMPLE_NAME_PATTERN), + example = "xplan-1Pruefbericht_Torstrasse_10_report-4223") String name, + @QueryParam("skipSemantisch") @DefaultValue("false") @Parameter( + description = "skip semantische Validierung") Boolean skipSemantisch, + @QueryParam("skipGeometrisch") @DefaultValue("false") @Parameter( + description = "skip geometrische Validierung") Boolean skipGeometrisch, + @QueryParam("skipFlaechenschluss") @DefaultValue("false") @Parameter( + description = "skip Flaechenschluss Ueberpruefung") Boolean skipFlaechenschluss, + @QueryParam("skipGeltungsbereich") @DefaultValue("false") @Parameter( + description = "skip Geltungsbereich Ueberpruefung") Boolean skipGeltungsbereich, + @QueryParam("skipLaufrichtung") @DefaultValue("false") @Parameter( + description = "skip Laufrichtung Ueberpruefung") Boolean skipLaufrichtung, + @QueryParam("profiles") @Parameter( + description = "Names of profiles which shall be additionally used for validation", + explode = FALSE) List<String> profiles) + throws IOException, ValidatorException, URISyntaxException, InvalidXPlanGmlOrArchive, + UnsupportedContentTypeException, UnsupportedParameterValue, UnsupportedHeaderValue, StorageException { + return doValidate(body, xFilename, name, skipSemantisch, skipGeometrisch, skipFlaechenschluss, + skipGeltungsbereich, skipLaufrichtung, profiles, ValidationRequestedEvent.OriginFile.GML); + } + + @POST + @Consumes({ "application/octet-stream", "application/zip", "application/x-zip", "application/x-zip-compressed" }) + @Produces({ "application/json" }) + @Hidden + public Response validateZip(@Valid File body, + @HeaderParam("X-Filename") @Parameter(schema = @Schema(pattern = SIMPLE_NAME_PATTERN)) String xFilename, + @QueryParam("name") @Parameter(schema = @Schema(pattern = SIMPLE_NAME_PATTERN)) String name, + @QueryParam("skipSemantisch") @DefaultValue("false") Boolean skipSemantisch, + @QueryParam("skipGeometrisch") @DefaultValue("false") Boolean skipGeometrisch, + @QueryParam("skipFlaechenschluss") @DefaultValue("false") Boolean skipFlaechenschluss, + @QueryParam("skipGeltungsbereich") @DefaultValue("false") Boolean skipGeltungsbereich, + @QueryParam("skipLaufrichtung") @DefaultValue("false") Boolean skipLaufrichtung, + @QueryParam("profiles") @Parameter( + description = "Names of profiles which shall be additionally used for validation", + explode = FALSE) List<String> profiles) + throws IOException, UnsupportedContentTypeException, UnsupportedParameterValue, UnsupportedHeaderValue, + StorageException { + return doValidate(body, xFilename, name, skipSemantisch, skipGeometrisch, skipFlaechenschluss, + skipGeltungsbereich, skipLaufrichtung, profiles, ValidationRequestedEvent.OriginFile.ZIP); + } + + private Response doValidate(File body, String xFilename, String name, Boolean skipSemantisch, + Boolean skipGeometrisch, Boolean skipFlaechenschluss, Boolean skipGeltungsbereich, Boolean skipLaufrichtung, + List<String> profiles, ValidationRequestedEvent.OriginFile originFile) throws IOException, + UnsupportedContentTypeException, StorageException, UnsupportedParameterValue, UnsupportedHeaderValue { + checkContentTypesOfXPlanArchiveOrGml(body.toPath()); + + String uuid = validationExecutionStorage.addPlanToValidate(body.toPath()); + + String validationName = detectOrCreateValidationName(xFilename, name); + ValidationSettings settings = createValidationSettings(validationName, skipGeometrisch, skipSemantisch, + skipFlaechenschluss, skipGeltungsbereich, skipLaufrichtung, profiles); + + validationRequestNotifier.sendEvent(new ValidationRequestedEvent(uuid, settings, xFilename, + ValidationRequestedEvent.MediaType.JSON, originFile)); + + URI linkToStatus = createLinkToStatus(uuid); + Link statusLink = (Link) new Link().rel(STATUS).href(linkToStatus).type("application/json"); + ValidationReceipt receipt = new ValidationReceipt(uuid, statusLink); + + return Response.ok(receipt).build(); + } + + private URI createLinkToStatus(String uuid) { + URI apiUrl = validatorApiConfiguration.getApiUrl(); + if (apiUrl == null) + return null; + return apiUrl.resolve(servletContext.getContextPath() + "/api/v2/status/" + uuid); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/Link.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/Link.java new file mode 100644 index 0000000000000000000000000000000000000000..6cfb26d0aa8684298a80cc7969d6bee7b3083844 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/Link.java @@ -0,0 +1,144 @@ +/*- + * #%L + * xplan-manager-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2.model; + +import java.util.Date; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import de.latlon.xplanbox.api.commons.v1.model.AbstractLink; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; + +/** + * Datatype for Link. A Link to a resource related to the resource such as XPlanWerkWMS or + * the resource itself. + * + * @since 4.0 + */ +@Schema(description = "Link to a resource related to the resource such as XPlanWerkWMS or the resource itself") +@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", + date = "2020-08-28T13:42:47.160+02:00[Europe/Berlin]") +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class Link extends AbstractLink { + + public enum RelEnum { + + STATUS("status"), REPORT("report"); + + private String value; + + RelEnum(String v) { + value = v; + } + + public String value() { + return value; + } + + @Override + @JsonValue + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static RelEnum fromValue(String value) { + for (RelEnum b : RelEnum.values()) { + if (b.value.equals(value)) { + return b; + } + } + throw new IllegalArgumentException("Unexpected value '" + value + "'"); + } + + } + + private @Valid RelEnum rel; + + private Date expirationTime; + + public Link rel(RelEnum rel) { + this.rel = rel; + return this; + } + + @Schema(example = "self") + @JsonProperty("rel") + public RelEnum getRel() { + return rel; + } + + public void setRel(RelEnum rel) { + this.rel = rel; + } + + public Date getExpirationTime() { + return expirationTime; + } + + public void setExpirationTime(Date expirationTime) { + this.expirationTime = expirationTime; + } + + public Link expirationTime(Date expirationTime) { + this.expirationTime = expirationTime; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + Link link = (Link) o; + return rel == link.rel; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), rel); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class Link {\n"); + sb.append(" href: ").append(toIndentedString(getHref())).append("\n"); + sb.append(" rel: ").append(toIndentedString(rel)).append("\n"); + sb.append(" type: ").append(toIndentedString(getType())).append("\n"); + sb.append(" hreflang: ").append(toIndentedString(getHreflang())).append("\n"); + sb.append(" title: ").append(toIndentedString(getTitle())).append("\n"); + sb.append(" length: ").append(toIndentedString(getLength())).append("\n"); + sb.append("}"); + return sb.toString(); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusEnum.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..5783ac23d532581f996bc51f43603d359b3d6f6e --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusEnum.java @@ -0,0 +1,21 @@ +package de.latlon.xplanbox.api.validator.v2.model; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public enum StatusEnum { + + // Validation requested + REQUESTED, + + // Validation started + STARTED, + + // Validation failed + FAILED, + + // Validation finished + FINISHED + +} diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusNotification.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusNotification.java new file mode 100644 index 0000000000000000000000000000000000000000..3ed5fc155b1147b318b4b74192b8c0dfb34c7ad1 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/StatusNotification.java @@ -0,0 +1,94 @@ +package de.latlon.xplanbox.api.validator.v2.model; + +import static de.latlon.xplanbox.api.validator.v2.model.Link.RelEnum.REPORT; +import static de.latlon.xplanbox.api.validator.v2.model.StatusEnum.REQUESTED; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Date; +import java.util.List; + +import de.latlon.xplanbox.validator.storage.Status; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class StatusNotification { + + private StatusEnum status; + + private String errorMsg; + + private List<Link> links; + + private Date expirationTime; + + public StatusNotification() { + this.status = REQUESTED; + } + + public StatusEnum getStatus() { + return status; + } + + public void setStatus(StatusEnum status) { + this.status = status; + } + + public StatusNotification status(StatusEnum status) { + this.status = status; + return this; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public StatusNotification errorMsg(String errorMsg) { + this.errorMsg = errorMsg; + return this; + } + + public List<Link> getLinks() { + return links; + } + + public void setLinks(List<Link> links) { + this.links = links; + } + + public StatusNotification links(List<Link> links) { + this.links = links; + return this; + } + + public StatusNotification links(Link... links) { + this.links = List.of(links); + return this; + } + + public static StatusNotification fromStatus(Status status) throws URISyntaxException { + StatusNotification statusNotification = new StatusNotification() + .status(StatusEnum.valueOf(status.getStatus().name())) + .errorMsg(status.getErrorMsg()); + if (status.getLinkToJsonReport() != null) + statusNotification.links( + (Link) new Link().rel(REPORT) + .expirationTime(status.getExpirationTime()) + .type("application/json") + .title("Validierungsreport") + .href(new URI(status.getLinkToJsonReport())), + (Link) new Link().rel(REPORT) + .expirationTime(status.getExpirationTime()) + .type("application/pdf") + .title("Validierungsreport") + .href(new URI(status.getLinkToPdfReport()))); + return statusNotification; + } + +} diff --git a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationMessageReceiver.java b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/ValidationReceipt.java similarity index 57% rename from xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationMessageReceiver.java rename to xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/ValidationReceipt.java index 156fd518053fa25fef984caea754f0ad49520410..e714f31277e6ff832903c73fcb6db6dba1c27140 100644 --- a/xplan-core/xplan-core-validator-events/src/main/java/de/latlon/core/validator/events/ValidationMessageReceiver.java +++ b/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/v2/model/ValidationReceipt.java @@ -1,6 +1,6 @@ /*- * #%L - * xplan-core-validator-events - Modul zur Gruppierung der Kernmodule + * xplan-core-validator - XPlan Validator Core Komponente * %% * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH * %% @@ -8,44 +8,41 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.core.validator.events; - -import java.util.function.Consumer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +package de.latlon.xplanbox.api.validator.v2.model; /** + * A receipt for the submission of a validation request. + * * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> - * @since 7.2 + * @since 8.0 */ -public class ValidationMessageReceiver { +public class ValidationReceipt { - private static final Logger LOG = LoggerFactory.getLogger(ValidationMessageReceiver.class); + private final String uuid; - private Consumer<ValidationRequestedEvent> listener; + private final Link statusLink; + + public ValidationReceipt(String uuid, Link statusLink) { + this.uuid = uuid; + this.statusLink = statusLink; + } - void receiveMessage(ValidationRequestedEvent msg) { - if (listener != null) { - listener.accept(msg); - } - else { - LOG.warn("No listener registered"); - } + public String getUuid() { + return uuid; } - public void setListener(Consumer<ValidationRequestedEvent> listener) { - this.listener = listener; + public Link getStatusLink() { + return statusLink; } } diff --git a/xplan-validator/xplan-validator-api/src/main/resources/application-dev.properties b/xplan-validator/xplan-validator-api/src/main/resources/application-dev.properties new file mode 100644 index 0000000000000000000000000000000000000000..7675c93694bf1e9e1b4a68127fc14c1974311c42 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/main/resources/application-dev.properties @@ -0,0 +1,23 @@ +### +# #%L +# xplan-api-validator - Modul zur Gruppierung der REST-API +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### + +## for local development +server.port=8085 \ No newline at end of file diff --git a/xplan-validator/xplan-validator-api/src/main/resources/application.properties b/xplan-validator/xplan-validator-api/src/main/resources/application.properties index d22f6272514b062643ebfd6b24a1859b97d736b5..e7648d4e7cab1700ca7962041aadb4666656b167 100644 --- a/xplan-validator/xplan-validator-api/src/main/resources/application.properties +++ b/xplan-validator/xplan-validator-api/src/main/resources/application.properties @@ -33,3 +33,16 @@ spring.rabbitmq.port=${XPLAN_RABBIT_PORT:5672} spring.rabbitmq.username=${XPLAN_RABBIT_USER:guest} xplanbox.rabbitmq.internal.prefix=${XPLAN_RABBIT_INTERNAL_PREFIX:xplanbox} +xplanbox.rabbitmq.fanout.topics=none +xplanbox.rabbitmq.public.fanout=${XPLAN_RABBIT_PUBLIC_FANOUT:} +xplanbox.rabbitmq.task.topics=none + +xplanbox.s3.accessKeyId=${XPLAN_S3_ACCESS_KEY} +xplanbox.s3.endpoint.url=${XPLAN_S3_ENDPOINT} +xplanbox.s3.region=${XPLAN_S3_REGION} +xplanbox.s3.secretKey=${XPLAN_S3_SECRET_ACCESS_KEY} + +xplanbox.validation.fsdirectory=${XPLAN_FS_DIRECTORY:/tmp/validation} +xplanbox.validation.profiles=${XPLAN_VALIDATOR_PROFILES:} +xplanbox.validation.s3.bucketName=${XPLAN_VALIDATION_S3_BUCKET_NAME:validation} +xplanbox.validation.s3.bucketPublicUrl=${XPLAN_VALIDATION_S3_BUCKET_PUBLIC_URL} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/FakeAsyncValidationWrapper.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/FakeAsyncValidationWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..b374418c6583a9f196de25c817943022917b8d56 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/FakeAsyncValidationWrapper.java @@ -0,0 +1,51 @@ +package de.latlon.xplanbox.api.validator.config; + +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; + +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.xplanbox.api.validator.handler.AsyncValidationWrapper; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType; + +public class FakeAsyncValidationWrapper extends AsyncValidationWrapper { + + @Autowired + private ValidationExecutionStorage validationExecutionStorage; + + private Path reportForNextValidateRequest; + + private ValidationRequestedEvent lastValidatedEvent; + + FakeAsyncValidationWrapper() { + super(null); + } + + @Override + public boolean validate(ValidationRequestedEvent event) { + lastValidatedEvent = event; + + try { + Map<ReportType, Path> reports = new HashMap<>(); + reports.put(ReportType.JSON, reportForNextValidateRequest); + validationExecutionStorage.saveValidationResult(event.getUuid(), reports); + } + catch (Exception e) { + throw new RuntimeException(e); + } + + return true; + } + + public void saveReportForNextValidateRequest(boolean successfull, Path validationResult) { + reportForNextValidateRequest = validationResult; + } + + public ValidationRequestedEvent getLastValidatedEvent() { + return lastValidatedEvent; + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/TestContext.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/TestContext.java index c5e52297cb3e559dc585dc476628b75219dc4e5e..b06bb65e2bba01d8ec8eb499b2e6be2f58035703 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/TestContext.java +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/config/TestContext.java @@ -26,7 +26,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.slf4j.LoggerFactory.getLogger; -import jakarta.annotation.PostConstruct; +import java.io.IOException; +import java.util.Optional; import org.mockito.Mockito; import org.slf4j.Logger; @@ -35,12 +36,15 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; -import de.latlon.core.validator.events.ValidationRequestNotifier; +import de.latlon.core.validator.events.EventSender; import de.latlon.xplan.commons.archive.SemanticValidableXPlanArchive; import de.latlon.xplan.validator.semantic.configuration.metadata.RulesMetadata; import de.latlon.xplan.validator.semantic.profile.SemanticProfileValidator; import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; import de.latlon.xplan.validator.semantic.report.SemanticValidatorResult; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.filesystem.FileSystemValidationExecutionStorage; +import jakarta.annotation.PostConstruct; /** * Indented to register the JAX-RS resources within Spring Application Context. TODO @@ -69,8 +73,18 @@ public class TestContext { } @Bean - public ValidationRequestNotifier validationRequestNotifier() { - return Mockito.mock(ValidationRequestNotifier.class); + public EventSender validationRequestNotifier() { + return Mockito.mock(EventSender.class); + } + + @Bean + public FakeAsyncValidationWrapper asyncValidationWrapper() { + return new FakeAsyncValidationWrapper(); + } + + @Bean + public ValidationExecutionStorage validationExecutionStorage() throws IOException { + return new FileSystemValidationExecutionStorage(Optional.empty()); } private static SemanticProfileValidator createValidator(RulesMetadata profile) { diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapperTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4833ea1cb011ec1b5a68223cff8105946e5efcd5 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/AsyncValidationWrapperTest.java @@ -0,0 +1,136 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.handler; + +import static de.latlon.core.validator.events.ValidationFinishedEvent.ValidationFinishedStatus.SUCCEEDED; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationFinishedEvent; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class AsyncValidationWrapperTest { + + private List<String> logs = Collections.synchronizedList(new ArrayList<>()); + + @BeforeEach + private void cleanup() { + logs.clear(); + } + + @Test + void simpleCase() throws Exception { + EventSender eventSender = Mockito.mock(EventSender.class); + AsyncValidationWrapper wrapper = new AsyncValidationWrapper(eventSender); + + startAsyncValidation(wrapper, someEventWithUuid("uuid1"), "event1"); + verifyNewLog("event1: before validate"); + + // send validation finished event + wrapper.someValidationFinished(new ValidationFinishedEvent("uuid1", SUCCEEDED)); + verifyNewLog("event1: after validate"); + } + + @Test + void moreComplicated() throws Exception { + EventSender eventSender = Mockito.mock(EventSender.class); + AsyncValidationWrapper wrapper = new AsyncValidationWrapper(eventSender); + + startAsyncValidation(wrapper, someEventWithUuid("uuid1"), "event1"); + verifyNewLog("event1: before validate"); + + startAsyncValidation(wrapper, someEventWithUuid("uuid2"), "event2"); + verifyNewLog("event2: before validate"); + + startAsyncValidation(wrapper, someEventWithUuid("uuid3"), "event3"); + verifyNewLog("event3: before validate"); + + // send validation finished event for something else + wrapper.someValidationFinished(new ValidationFinishedEvent("uuidOther", SUCCEEDED)); + + // send validation finished event for event 2 + wrapper.someValidationFinished(new ValidationFinishedEvent("uuid2", SUCCEEDED)); + verifyNewLog("event2: after validate"); + + // send validation finished events for something else + wrapper.someValidationFinished(new ValidationFinishedEvent("uuidOther2", SUCCEEDED)); + wrapper.someValidationFinished(new ValidationFinishedEvent("uuid2", SUCCEEDED)); + + // send validation finished event for event 1 + wrapper.someValidationFinished(new ValidationFinishedEvent("uuid1", SUCCEEDED)); + verifyNewLog("event1: after validate"); + + // send validation finished event for event 1 + wrapper.someValidationFinished(new ValidationFinishedEvent("uuid3", SUCCEEDED)); + verifyNewLog("event3: after validate"); + } + + private void startAsyncValidation(AsyncValidationWrapper wrapper, ValidationRequestedEvent event, + String msgPrefix) { + new Thread("Thread " + msgPrefix) { + public void run() { + try { + logs.add(msgPrefix + ": before validate"); + wrapper.validate(event); + } + catch (Exception e) { + logs.add(msgPrefix + ": exception " + e.getMessage()); + } + logs.add(msgPrefix + ": after validate"); + }; + }.start(); + } + + private void verifyNewLog(String expectedNewMessage) throws Exception { + waitingIfNeeded(() -> assertThat(logs).containsExactly(expectedNewMessage)); + logs.clear(); + } + + private void waitingIfNeeded(Callable<?> callable) throws Exception { + long maxWaitTime = System.currentTimeMillis() + 5_000; + while (true) { + try { + callable.call(); + return; + } + catch (Throwable t) { + if (System.currentTimeMillis() > maxWaitTime) { + throw t; + } + Thread.sleep(1_000); + } + } + + } + + private ValidationRequestedEvent someEventWithUuid(String uuid) { + return new ValidationRequestedEvent(uuid, null, null, null, null); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ConfigHandlerTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ConfigHandlerTest.java index 961f69c7d7b18dad6fe818860662848869dd9572..d9700cee0070dd8f622ce72c3ee027bda5ddda97 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ConfigHandlerTest.java +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ConfigHandlerTest.java @@ -33,12 +33,13 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; /** * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> */ @ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { ApplicationContext.class }) +@ContextConfiguration(classes = { ApplicationContext.class, TestContext.class }) @ActiveProfiles("test") class ConfigHandlerTest { diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/DefaultApiTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/DefaultApiTest.java index 8b1df4bd5d32990b3f776af42d0c6a7e23260c1c..7212406d6219d08ca3eed2b3da63893236bd9133 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/DefaultApiTest.java +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/DefaultApiTest.java @@ -27,10 +27,14 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; +import de.latlon.xplanbox.api.validator.api.v1.ApiV1Config; +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; import jakarta.servlet.ServletContext; import jakarta.ws.rs.core.Application; import jakarta.ws.rs.core.Response; - import org.apache.http.HttpHeaders; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; @@ -42,11 +46,6 @@ import org.mockito.Mockito; import org.springframework.boot.test.json.BasicJsonTester; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; -import de.latlon.xplanbox.api.validator.config.ApplicationContext; -import de.latlon.xplanbox.api.validator.config.JerseyConfig; -import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; - /** * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> */ @@ -73,12 +72,13 @@ class DefaultApiTest extends JerseyTest { try { ValidatorApiConfiguration validatorConfig = new ValidatorApiConfiguration( new DefaultPropertiesLoader(getClass())); - resourceConfig = new JerseyConfig(mockServletContext, validatorConfig); + resourceConfig = new ApiV1Config(mockServletContext, validatorConfig); } catch (Exception e) { throw new RuntimeException(e); } - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); resourceConfig.property("contextConfig", context); return resourceConfig; } diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiJerseyTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiJerseyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4af7b8b3778a5a4321de4144a27a832d727e2ea8 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiJerseyTest.java @@ -0,0 +1,236 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v1; + +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_X_ZIP; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_X_ZIP_COMPRESSED; +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM; +import static jakarta.ws.rs.core.MediaType.APPLICATION_XML; +import static jakarta.ws.rs.core.MediaType.TEXT_XML; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.http.HttpHeaders; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import de.latlon.core.validator.events.ValidationRequestedEvent.OriginFile; +import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; +import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; +import de.latlon.xplanbox.api.validator.api.v1.ApiV1Config; +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.FakeAsyncValidationWrapper; +import de.latlon.xplanbox.api.validator.config.TestContext; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import jakarta.servlet.ServletContext; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; + +/** + * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { TestContext.class }) +public class ValidateApiJerseyTest extends JerseyTest { + + @TempDir + public static Path tempFolder; + + @BeforeAll + static void setupFakedWorkspace() throws IOException { + Path workspace = tempFolder.resolve("xplan-webservices-validator-wms-memory-workspace"); + Files.createDirectories(workspace); + System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); + } + + @Autowired + private SemanticProfiles semanticProfiles; + + private FakeAsyncValidationWrapper fakeAsyncValidationWrapper; + + @Override + protected Application configure() { + ResourceConfig resourceConfig; + ServletContext mockServletContext = Mockito.mock(ServletContext.class); + Mockito.when(mockServletContext.getContextPath()).thenReturn(""); + + try { + ValidatorApiConfiguration validatorConfig = new ValidatorApiConfiguration( + new DefaultPropertiesLoader(getClass())); + resourceConfig = new ApiV1Config(mockServletContext, validatorConfig); + } + catch (Exception e) { + throw new RuntimeException(e); + } + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); + fakeAsyncValidationWrapper = context.getBean(FakeAsyncValidationWrapper.class); + resourceConfig.property("contextConfig", context); + return resourceConfig; + } + + @Test + void verifyThat_Response_ContainsCorrectStatusCodeAndMediaType() throws IOException, URISyntaxException { + final String data = Files.readString(Paths.get(ValidateApiJerseyTest.class.getResource("/xplan.gml").toURI())); + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, + Paths.get(getClass().getResource("validation-report1.json").toURI())); + + final Response response = target("/validate").request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, TEXT_XML)); + + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getOriginFile()).isEqualTo(OriginFile.GML); + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); + } + + @Test + void verifyThat_validationOctetStream_Response_ContainsXmlEncoding() throws URISyntaxException, IOException { + final byte[] data = Files.readAllBytes(Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI())); + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, someJsonReport()); + + final Response response = target("/validate").request() + .accept(APPLICATION_XML) + .post(Entity.entity(data, APPLICATION_OCTET_STREAM)); + + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getOriginFile()).isEqualTo(OriginFile.ZIP); + + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); + assertThat(response.readEntity(String.class)).contains("valid"); + } + + @Test + void verifyThat_validationZip_Response_ContainsXmlEncoding() throws Exception { + final byte[] data = Files.readAllBytes(Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI())); + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, someJsonReport()); + + final Response response = target("/validate").request() + .accept(APPLICATION_XML) + .post(Entity.entity(data, APPLICATION_ZIP)); + + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getOriginFile()).isEqualTo(OriginFile.ZIP); + + assertThat(response.getStatus()).isEqualTo(200); + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); + assertThat(response.readEntity(String.class)).contains("valid"); + } + + @Test + void verifyThat_validationXZip_Response_ContainsXmlEncoding() throws Exception { + final byte[] data = Files.readAllBytes(Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI())); + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, someJsonReport()); + + final Response response = target("/validate").request() + .accept(APPLICATION_XML) + .post(Entity.entity(data, APPLICATION_X_ZIP)); + + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getOriginFile()).isEqualTo(OriginFile.ZIP); + + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); + assertThat(response.readEntity(String.class)).contains("valid"); + } + + @Test + void verifyThat_validationXZipCompressed_Response_ContainsXmlEncoding() throws Exception { + final byte[] data = Files.readAllBytes(Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI())); + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, someJsonReport()); + + final Response response = target("/validate").request() + .accept(APPLICATION_XML) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getOriginFile()).isEqualTo(OriginFile.ZIP); + + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); + assertThat(response.readEntity(String.class)).contains("valid"); + } + + @Test + void verifyThat_validationXZipCompressedWithProfilesCommaseparated_Response_ContainsJsonEncoding() + throws Exception { + + fakeAsyncValidationWrapper.saveReportForNextValidateRequest(true, someJsonReport()); + + final byte[] data = Files + .readAllBytes(Paths.get(ValidateApiJerseyTest.class.getResource("/bplan_valid_41.zip").toURI())); + String profileId0 = semanticProfiles.getProfileValidators().get(0).getId(); + String profileId1 = semanticProfiles.getProfileValidators().get(1).getId(); + final Response response = target("/validate").queryParam("profiles", profileId0 + "," + profileId1) + .request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); + assertThat(fakeAsyncValidationWrapper.getLastValidatedEvent().getSettings().getProfiles()) + .containsExactlyInAnyOrder(profileId0, profileId1); + } + + @Test + void verifyThat_validationWithInvalidXFileName_Response_IsStatusCode400() throws URISyntaxException, IOException { + final byte[] data = Files + .readAllBytes(Paths.get(ValidateApiJerseyTest.class.getResource("/bplan_valid_41.zip").toURI())); + final Response response = target("/validate").request() + .header("X-Filename", "invalid.filename with blanks") + .accept(APPLICATION_JSON) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + void verifyThat_validationWithInvalidName_Response_IsStatusCode400() throws Exception { + final byte[] data = Files + .readAllBytes(Paths.get(ValidateApiJerseyTest.class.getResource("/bplan_valid_41.zip").toURI())); + final Response response = target("/validate").queryParam("name", "invalid.name with blanks") + .request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); + } + + Path someJsonReport() throws URISyntaxException { + return Paths.get(getClass().getResource("validation-report1.json").toURI()); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiTest.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiTest.java index 72557f9ab8febb51ca68870f03d7d43f23b0b985..39945640cd029222ea95064de69302e78983afc8 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiTest.java +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v1/ValidateApiTest.java @@ -8,231 +8,82 @@ * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ package de.latlon.xplanbox.api.validator.v1; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_X_ZIP; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_X_ZIP_COMPRESSED; -import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_ZIP; -import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; -import static jakarta.ws.rs.core.MediaType.APPLICATION_OCTET_STREAM; -import static jakarta.ws.rs.core.MediaType.APPLICATION_XML; -import static jakarta.ws.rs.core.MediaType.TEXT_XML; import static org.assertj.core.api.Assertions.assertThat; import java.io.IOException; -import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; -import jakarta.servlet.ServletContext; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.Application; -import jakarta.ws.rs.core.Response; - -import org.apache.http.HttpHeaders; -import org.glassfish.jersey.server.ResourceConfig; -import org.glassfish.jersey.test.JerseyTest; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; -import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; -import de.latlon.xplanbox.api.validator.config.ApplicationContext; -import de.latlon.xplanbox.api.validator.config.JerseyConfig; -import de.latlon.xplanbox.api.validator.config.TestContext; -import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; + +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.xplanbox.api.validator.handler.AsyncValidationWrapper; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import jakarta.ws.rs.core.Request; /** - * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { TestContext.class }) -public class ValidateApiTest extends JerseyTest { +class ValidateApiTest { @TempDir - public static Path tempFolder; - - @BeforeAll - static void setupFakedWorkspace() throws IOException { - Path workspace = tempFolder.resolve("xplan-webservices-validator-wms-memory-workspace"); - Files.createDirectories(workspace); - System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); - } - - @Autowired - private SemanticProfiles semanticProfiles; - - @Override - protected Application configure() { - ResourceConfig resourceConfig; - ServletContext mockServletContext = Mockito.mock(ServletContext.class); - Mockito.when(mockServletContext.getContextPath()).thenReturn(""); - - try { - ValidatorApiConfiguration validatorConfig = new ValidatorApiConfiguration( - new DefaultPropertiesLoader(getClass())); - resourceConfig = new JerseyConfig(mockServletContext, validatorConfig); - } - catch (Exception e) { - throw new RuntimeException(e); - } - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, - TestContext.class); - resourceConfig.property("contextConfig", context); - return resourceConfig; - } + Path tmpDir; @Test - void verifyThat_Response_ContainsCorrectStatusCodeAndMediaType() throws IOException, URISyntaxException { - final String data = new String( - Files.readAllBytes(Paths.get(ValidateApiTest.class.getResource("/xplan.gml").toURI()))); - final Response response = target("/validate").request() - .accept(APPLICATION_JSON) - .post(Entity.entity(data, TEXT_XML)); - - assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); - } - - @Test - void verifyThat_validationOctetStream_Response_ContainsXmlEncoding() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").request() - .accept(APPLICATION_XML) - .post(Entity.entity(data, APPLICATION_OCTET_STREAM)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); - assertThat(response.readEntity(String.class)).contains("valid"); - } + void verifyThatProfilesAreSentToValidator_noProfile() throws Exception { + List<String> profilesInEvent = callValidateAndExtractProfilesInValidationRequestEvent(null); + assertThat(profilesInEvent).isEmpty(); - @Test - void verifyThat_validationZip_Response_ContainsXmlEncoding() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").request() - .accept(APPLICATION_XML) - .post(Entity.entity(data, APPLICATION_ZIP)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); - assertThat(response.readEntity(String.class)).contains("valid"); - } + profilesInEvent = callValidateAndExtractProfilesInValidationRequestEvent(Collections.emptyList()); + assertThat(profilesInEvent).isEmpty(); - @Test - void verifyThat_validationXZip_Response_ContainsXmlEncoding() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").request() - .accept(APPLICATION_XML) - .post(Entity.entity(data, APPLICATION_X_ZIP)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); - assertThat(response.readEntity(String.class)).contains("valid"); } @Test - void verifyThat_validationXZipCompressed_Response_ContainsXmlEncoding() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").request() - .accept(APPLICATION_XML) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_XML); - assertThat(response.readEntity(String.class)).contains("valid"); + void verifyThatProfilesAreSentToValidator_oneProfile() throws Exception { + List<String> profilesInEvent = callValidateAndExtractProfilesInValidationRequestEvent(List.of("profile1")); + assertThat(profilesInEvent).containsExactlyInAnyOrder("profile1"); } @Test - void verifyThat_validationXZipCompressedWithProfile_Response_ContainsJsonEncoding() - throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate") - .queryParam("profiles", semanticProfiles.getProfileValidators().get(0).getId()) - .request() - .accept(APPLICATION_JSON) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); - String actual = response.readEntity(String.class); - assertThat(actual).contains("profil"); + void verifyThatProfilesAreSentToValidator_twoProfiles() throws Exception { + List<String> profilesInEvent = callValidateAndExtractProfilesInValidationRequestEvent( + List.of("profile1", "profile2")); + assertThat(profilesInEvent).containsExactlyInAnyOrder("profile1", "profile2"); } - @Test - void verifyThat_validationXZipCompressedWithProfiles_Response_ContainsJsonEncoding() - throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate") - .queryParam("profiles", semanticProfiles.getProfileValidators().get(0).getId()) - .queryParam("profiles", semanticProfiles.getProfileValidators().get(1).getId()) - .request() - .accept(APPLICATION_JSON) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); - String actual = response.readEntity(String.class); - assertThat(actual).contains("profil"); - } + private List<String> callValidateAndExtractProfilesInValidationRequestEvent(List profiles) throws Exception { + ValidateApi validateApi = new ValidateApi(); + validateApi.validationExecutionStorage = Mockito.mock(ValidationExecutionStorage.class); + validateApi.validationHandler = Mockito.mock(AsyncValidationWrapper.class); - @Test - void verifyThat_validationXZipCompressedWithProfilesCommaseparated_Response_ContainsJsonEncoding() - throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate") - .queryParam("profiles", - semanticProfiles.getProfileValidators().get(0).getId() + "," - + semanticProfiles.getProfileValidators().get(1).getId()) - .request() - .accept(APPLICATION_JSON) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); - String actual = response.readEntity(String.class); - assertThat(actual).contains("profil"); - } + Path tmpFile = tmpDir.resolve("my-file.xml"); + Files.writeString(tmpFile, ""); + Request request = Mockito.mock(Request.class); + validateApi.validate(request, tmpFile.toFile(), null, null, false, false, false, false, false, profiles); - @Test - void verifyThat_validationWithInvalidXFileName_Response_IsStatusCode400() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").request() - .header("X-Filename", "invalid.filename with blanks") - .accept(APPLICATION_JSON) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); - } + ArgumentCaptor<ValidationRequestedEvent> argumentCaptor = ArgumentCaptor + .forClass(ValidationRequestedEvent.class); + Mockito.verify(validateApi.validationHandler).validate(argumentCaptor.capture()); - @Test - void verifyThat_validationWithInvalidName_Response_IsStatusCode400() throws URISyntaxException, IOException { - final byte[] data = Files - .readAllBytes(Paths.get(ValidateApiTest.class.getResource("/bplan_valid_41.zip").toURI())); - final Response response = target("/validate").queryParam("name", "invalid.name with blanks") - .request() - .accept(APPLICATION_JSON) - .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); - - assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); + ValidationRequestedEvent event = argumentCaptor.getValue(); + return event.getSettings().getProfiles(); } } diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2Test.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..bdacc0f9d1f90e0edbc7eae27ab217420229ac25 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/DefaultApi2Test.java @@ -0,0 +1,139 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; +import de.latlon.xplanbox.api.validator.api.v2.ApiV2Config; +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import de.latlon.xplanbox.api.validator.v1.DefaultApi; +import jakarta.servlet.ServletContext; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; +import org.apache.http.HttpHeaders; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @since 8.0 + */ +class DefaultApi2Test extends JerseyTest { + + @TempDir + static Path tempFolder; + + @BeforeAll + static void setupFakedWorkspace() throws IOException { + Path workspace = tempFolder.resolve("xplan-webservices-validator-wms-memory-workspace"); + Files.createDirectories(workspace); + System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); + } + + @Override + protected Application configure() { + enable(TestProperties.LOG_TRAFFIC); + + ResourceConfig resourceConfig; + ServletContext mockServletContext = Mockito.mock(ServletContext.class); + Mockito.when(mockServletContext.getContextPath()).thenReturn(""); + + try { + ValidatorApiConfiguration validatorConfig = new ValidatorApiConfiguration( + new DefaultPropertiesLoader(DefaultApi.class)); + resourceConfig = new ApiV2Config(mockServletContext, validatorConfig); + } + catch (Exception e) { + throw new RuntimeException(e); + } + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); + resourceConfig.property("contextConfig", context); + return resourceConfig; + } + + @Test + void verifyThat_Response_ContainsCorrectStatusCodeAndMediaType() { + final Response response = target("/").request(APPLICATION_JSON).get(); + + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); + } + + @Test + void verifyThat_Response_ContainsOpenApiDocument() { + final String response = target("/").request(APPLICATION_JSON).get(String.class); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response)).extractingJsonPathStringValue("$.openapi").isEqualTo("3.0.1"); + } + + @Test + void verifyThat_Response_ContainsLicence() { + final String response = target("/").request(APPLICATION_JSON).get(String.class); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response)).extractingJsonPathStringValue("$.info.license.name").isEqualTo("Apache 2.0"); + } + + @Test + void verifyThat_Response_Paths() { + final String response = target("/").request(APPLICATION_JSON).get(String.class); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response)).extractingJsonPathMapValue("$.paths") + .containsOnlyKeys("/", "/info", "/status/{uuid}", "/validate"); + } + + @Test + void verifyThat_Response_ContainsTermsOfService() { + final String response = target("/").request(APPLICATION_JSON).get(String.class); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response)).extractingJsonPathStringValue("$.info.termsOfService") + .isEqualTo("http://xplanbox/api-validator/terms"); + } + + @Test + void verifyThat_Response_ContainsServersUrl() { + final String response = target("/").request(APPLICATION_JSON).get(String.class); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response)).extractingJsonPathStringValue("$.servers[0].url") + .isEqualTo("http://xplanbox-api-validator/api/v2"); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/InfoApi2Test.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/InfoApi2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..e3443994b7ce8a10acde84de97a30ca80a26ec1b --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/InfoApi2Test.java @@ -0,0 +1,92 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; +import org.apache.http.HttpHeaders; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.boot.test.json.BasicJsonTester; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @since 8.0 + */ +class InfoApi2Test extends JerseyTest { + + @TempDir + public static Path tempFolder; + + @BeforeAll + static void setupFakedWorkspace() throws IOException { + Path workspace = tempFolder.resolve("xplan-validator-wms-memory-workspace"); + Files.createDirectories(workspace); + System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); + } + + @Override + protected Application configure() { + enable(TestProperties.LOG_TRAFFIC); + final ResourceConfig resourceConfig = new ResourceConfig(InfoApi2.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); + resourceConfig.property("contextConfig", context); + return resourceConfig; + } + + @Test + void verifyThat_Response_ContainsCorrectStatusCodeAndMediaType() { + final Response response = target("/info").request(APPLICATION_JSON).get(); + + assertEquals(Response.Status.OK.getStatusCode(), response.getStatus()); + assertEquals(APPLICATION_JSON, response.getHeaderString(HttpHeaders.CONTENT_TYPE)); + + BasicJsonTester json = new BasicJsonTester(getClass()); + assertThat(json.from(response.readEntity(String.class))).extractingJsonPathStringValue("$.rulesMetadata.source") + .startsWith("https://gitlab.opencode.de/xleitstelle/xplanung/validierungsregeln/standard/-/tree/"); + + } + + @Test + void verifyThat_Response_ContainsSupportedXplanGmlVersionsAndProfiles() { + final String response = target("/info").request(APPLICATION_JSON).get(String.class); + + assertThat(response).contains("supportedXPlanGmlVersions"); + assertThat(response).contains("profiles"); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/StatusApi2Test.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/StatusApi2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..02f0e284339ebf6dbef507f753c2e28d141e582c --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/StatusApi2Test.java @@ -0,0 +1,80 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; + +import de.latlon.xplanbox.api.commons.exception.XPlanApiExceptionMapper; +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @since 8.0 + */ +class StatusApi2Test extends JerseyTest { + + @TempDir + public static Path tempFolder; + + @BeforeAll + static void setupFakedWorkspace() throws IOException { + Path workspace = tempFolder.resolve("xplan-validator-wms-memory-workspace"); + Files.createDirectories(workspace); + System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); + } + + @Override + protected Application configure() { + enable(TestProperties.LOG_TRAFFIC); + final ResourceConfig resourceConfig = new ResourceConfig(StatusApi.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); + resourceConfig.property("contextConfig", context); + resourceConfig.register(XPlanApiExceptionMapper.class); + return resourceConfig; + } + + @Test + @Disabled("TODO: fix implementation") + void verify_404_forInvalidUuid() { + final Response response = target("/status/" + UUID.randomUUID()).request(APPLICATION_JSON).get(); + + assertEquals(Response.Status.NOT_FOUND.getStatusCode(), response.getStatus()); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2Test.java b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2Test.java new file mode 100644 index 0000000000000000000000000000000000000000..c5b4250347def6635ffde497eca48d22eeaad814 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/v2/ValidateApi2Test.java @@ -0,0 +1,147 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.api.validator.v2; + +import static de.latlon.xplanbox.api.commons.XPlanBoxMediaType.APPLICATION_X_ZIP_COMPRESSED; +import static jakarta.ws.rs.core.MediaType.APPLICATION_JSON; +import static jakarta.ws.rs.core.MediaType.TEXT_XML; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import de.latlon.xplan.commons.configuration.DefaultPropertiesLoader; +import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; +import de.latlon.xplanbox.api.validator.api.v2.ApiV2Config; +import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.api.validator.config.TestContext; +import de.latlon.xplanbox.api.validator.config.ValidatorApiConfiguration; +import jakarta.servlet.ServletContext; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Application; +import jakarta.ws.rs.core.Response; +import org.apache.http.HttpHeaders; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz</a> + * @since 8.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { TestContext.class }) +public class ValidateApi2Test extends JerseyTest { + + @TempDir + public static Path tempFolder; + + @BeforeAll + static void setupFakedWorkspace() throws IOException { + Path workspace = tempFolder.resolve("xplan-webservices-validator-wms-memory-workspace"); + Files.createDirectories(workspace); + System.setProperty("DEEGREE_WORKSPACE_ROOT", workspace.getParent().toString()); + } + + @Autowired + private SemanticProfiles semanticProfiles; + + @Override + protected Application configure() { + ResourceConfig resourceConfig; + ServletContext mockServletContext = Mockito.mock(ServletContext.class); + Mockito.when(mockServletContext.getContextPath()).thenReturn(""); + + try { + ValidatorApiConfiguration validatorConfig = new ValidatorApiConfiguration( + new DefaultPropertiesLoader(getClass())); + resourceConfig = new ApiV2Config(mockServletContext, validatorConfig); + } + catch (Exception e) { + throw new RuntimeException(e); + } + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationContext.class, + TestContext.class); + resourceConfig.property("contextConfig", context); + return resourceConfig; + } + + @Test + void verifyThat_Response_ContainsCorrectStatusCodeAndMediaType() throws IOException, URISyntaxException { + final String data = new String( + Files.readAllBytes(Paths.get(ValidateApi2Test.class.getResource("/xplan.gml").toURI()))); + final Response response = target("/validate").request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, TEXT_XML)); + + assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); + assertThat(response.readEntity(String.class)).contains("uuid"); + } + + @Test + void verifyThat_validationXZipCompressed_Response_ContainsJsonEncoding() throws URISyntaxException, IOException { + final byte[] data = Files + .readAllBytes(Paths.get(ValidateApi2Test.class.getResource("/bplan_valid_41.zip").toURI())); + final Response response = target("/validate").request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(APPLICATION_JSON); + assertThat(response.readEntity(String.class)).contains("uuid"); + } + + @Test + void verifyThat_validationWithInvalidName_Response_IsStatusCode400() throws URISyntaxException, IOException { + final byte[] data = Files.readAllBytes(Paths.get(ValidateApi2Test.class.getResource("/xplan.gml").toURI())); + final Response response = target("/validate").queryParam("name", "invalid.name with blanks") + .request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, TEXT_XML)); + + assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); + } + + @Test + void verifyThat_validationXZipCompressedWithInvalidName_Response_IsStatusCode400() + throws URISyntaxException, IOException { + final byte[] data = Files + .readAllBytes(Paths.get(ValidateApi2Test.class.getResource("/bplan_valid_41.zip").toURI())); + final Response response = target("/validate").queryParam("name", "invalid.name with blanks") + .request() + .accept(APPLICATION_JSON) + .post(Entity.entity(data, APPLICATION_X_ZIP_COMPRESSED)); + + assertThat(response.getStatus()).isEqualTo(Response.Status.BAD_REQUEST.getStatusCode()); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report1.json b/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report1.json new file mode 100644 index 0000000000000000000000000000000000000000..3a90816e2e8e7bc2dff0edaa986235796ed3c126 --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report1.json @@ -0,0 +1 @@ +{"documentSummary":[{"name":"Nr. 108 Holstener Weg","type":"BP_Plan"}],"version":"XPLAN_41","filename":null,"name":"8147f2f6-5e53-4fc7-a795-75a6ea4b3190","bbox":{"minX":7.373668092967802,"minY":52.33234200586314,"maxX":7.377600099759094,"maxY":52.33376312995529,"crs":"EPSG:4326"},"date":1721376349423,"valid":false,"status":"Die Validierung wurde ausgeführt.","externalReferences":[],"externalReferencesResult":[],"rulesMetadata":{"version":"unbekannt","source":"unbekannt"},"validationResult":{"semantisch":{"valid":true,"rules":[]},"geometrisch":{"valid":false,"errors":["XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW)."],"warnings":[]},"syntaktisch":{"valid":true,"messages":[]}}} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report2.xml b/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report2.xml new file mode 100644 index 0000000000000000000000000000000000000000..073a1c07d850493ad6d48a8a4c971a6232f4cf1e --- /dev/null +++ b/xplan-validator/xplan-validator-api/src/test/resources/de/latlon/xplanbox/api/validator/v1/validation-report2.xml @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?><validationReport><version>XPLAN_41</version><name>858202ca-3d84-4174-be76-be04af280e36</name><documentSummary><name>Nr. 108 Holstener Weg</name><type>BP_Plan</type></documentSummary><bbox><minX>7.373668092967802</minX><minY>52.33234200586314</minY><maxX>7.377600099759094</maxX><maxY>52.33376312995529</maxY><crs>EPSG:4326</crs></bbox><date>2024-07-19T10:26:13.799+02:00</date><valid>false</valid><status>Die Validierung wurde ausgeführt.</status><wmsUrl>https://latlon.xplanbox.develop.diplanung.de/xplan-validator-wms/services/wms?PLANWERK_MANAGERID=4688&SERVICE=WMS&REQUEST=GetCapabilities</wmsUrl><rulesMetadata><version>1.1.9</version><source>https://gitlab.opencode.de/xleitstelle/xplanung/validierungsregeln/standard/-/tree/v1.1.9</source></rulesMetadata><validationResult><geometrisch><valid>false</valid><errors>XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW).</errors></geometrisch><semantisch><valid>true</valid><rules><isValid>true</isValid><message>Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType</message><name>2.1.2.1</name></rules><rules><isValid>true</isValid><message>Angabe eines Standard CRS</message><name>2.1.3.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussbedingung</message><name>2.2.1.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Text-Abschnitte</message><name>3.1.1.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Begründungs-Abschnitte</message><name>3.1.1.2</name></rules><rules><isValid>true</isValid><message>Relation auf Präsentationsobjekte</message><name>3.1.2.1</name></rules><rules><isValid>true</isValid><message>Relation auf Fachobjekte</message><name>3.1.2.2</name></rules><rules><isValid>true</isValid><message>Relation auf Basis-Rasterplan</message><name>3.1.2.3</name></rules><rules><isValid>true</isValid><message>Relationen auf Text-Abschnitte</message><name>3.1.3.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Begründungs-Abschnitte</message><name>3.1.3.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>3.1.3.3</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Präsentationsobjekte</message><name>3.1.3.4</name></rules><rules><isValid>true</isValid><message>Spezifikation des Textinhalts</message><name>3.2.1.1</name></rules><rules><isValid>true</isValid><message>Spezifikation des Textinhalts</message><name>3.2.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der verschiedenen Höhenangaben</message><name>3.2.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist</message><name>3.2.3.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug</message><name>3.2.3.3</name></rules><rules><isValid>true</isValid><message>Verweis auf Dokumente.</message><name>3.2.4.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel).</message><name>3.2.5.1</name></rules><rules><isValid>true</isValid><message>Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten</message><name>3.3.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute art und index</message><name>3.3.1.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>3.3.1.3</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Punktgeometrie.</message><name>3.3.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Liniengeometrie.</message><name>3.3.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Flächengeometrie.</message><name>3.3.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>4.1.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltBPlan</message><name>4.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>4.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>4.1.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchFlaeche</message><name>4.1.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchMassnahme</message><name>4.1.3.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme</message><name>4.1.3.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche</message><name>4.1.3.4</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>4.1.3.5</name></rules><rules><isValid>true</isValid><message>BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>4.1.3.6</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchABE</message><name>4.1.3.7</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>4.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>4.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>4.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>4.1.5.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekte gehören nie zum Flächenschluss.</message><name>4.1.6.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss</message><name>4.1.7.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>4.1.8.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>4.1.9.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ</message><name>4.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GF</message><name>4.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ und GF</message><name>4.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BMZ</message><name>4.2.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BM</message><name>4.2.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BMZ und BM</message><name>4.2.6</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GRZ</message><name>4.2.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GR</message><name>4.2.8</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GRZ und GR</message><name>4.2.9</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zu Z</message><name>4.2.10</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zu ZU</message><name>4.2.11</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur Dachneigung</message><name>4.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute dachform und detaillierteDachform</message><name>4.3.2</name></rules><rules><isValid>true</isValid><message>Relation abweichungText</message><name>4.5.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung</message><name>4.5.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung</message><name>4.5.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute bauweise und abweichendeBauweise</message><name>4.5.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung</message><name>4.5.1.5</name></rules><rules><isValid>true</isValid><message>Relation baugrenze</message><name>4.5.2.1</name></rules><rules><isValid>true</isValid><message>Relation baulinie</message><name>4.5.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute typ und sonstTyp</message><name>4.5.10.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.5.13.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.5.13.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.5.13.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.5.14.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.5.14.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation eigentümer</message><name>4.5.14.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.5.14.4</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.5.15.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.6.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.7.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.7.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.7.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.7.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)</message><name>4.7.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.7.1.6</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.7.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.7.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.7.2.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.8.1.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.8.2.3</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>4.8.2.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.8.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.8.3.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi</message><name>4.8.3.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.8.3.6</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.5.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.5.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.5.3</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.9.5.4</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.9.6.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.10.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.11.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.11.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.11.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.11.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi</message><name>4.11.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.11.1.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>4.11.1.7</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation begrenzungslinie</message><name>4.12.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation begrenzungslinie</message><name>4.12.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.12.2.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.12.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.13.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.13.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.14.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.14.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute startWinkel und endWinkel</message><name>4.14.3.1</name></rules><rules><isValid>true</isValid><message>Kein flächenhafter Raumbezug</message><name>4.14.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.14.4.1</name></rules><rules><isValid>true</isValid><message>Notwendige Spezifikation einer Textlichen Darstellung</message><name>4.14.6.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Relation hoehenangabe</message><name>4.14.7.1</name></rules><rules><isValid>true</isValid><message>Kein flächenhafter Raumbezug</message><name>4.14.7.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>5.1.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute planArt und sonstPlanArt</message><name>5.1.1.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltFPlan</message><name>5.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>5.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>5.1.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchFlaeche</message><name>5.1.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPE</message><name>5.1.3.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>5.1.3.3</name></rules><rules><isValid>true</isValid><message>FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>5.1.3.4</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>5.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>5.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>5.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>5.1.5.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekte gehören nie zum Flächenschluss</message><name>5.1.6.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss</message><name>5.1.7.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>5.1.8.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>5.1.9.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung</message><name>5.3.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung</message><name>5.3.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung</message><name>5.3.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ</message><name>5.3.1.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.4.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.4.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.4.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.4.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)</message><name>5.4.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.4.1.6</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs</message><name>5.4.1.7</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.4.1.8</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.4.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung</message><name>5.4.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.4.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs</message><name>5.4.2.4</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.4.2.5</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.5.1.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.5.2.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.5.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.5.3.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)</message><name>5.5.3.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.5.3.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.5.3.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2</message><name>5.6.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>5.6.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2</message><name>5.6.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>5.6.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.7.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.7.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.7.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.7.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)</message><name>5.7.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.7.1.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.7.1.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.8.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.8.1.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.8.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.9.1.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.9.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.9.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.9.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.10.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.10.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)</message><name>5.10.2.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.3.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.10.4.1</name></rules><rules><isValid>true</isValid><message>Notwendige Spezifikation einer Textlichen Darstellung</message><name>5.10.6.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>6.1.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltSoPlan</message><name>6.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>6.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>6.1.2.3</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>6.1.3.1</name></rules><rules><isValid>true</isValid><message>SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>6.1.3.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>6.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>6.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>6.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>6.1.5.2</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>6.1.6.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>6.1.7.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.2.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.2.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.4.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung</message><name>6.2.5.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung</message><name>6.2.5.2</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>6.2.5.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.6.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.7.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>6.2.7.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.8.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.3.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.3.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute gebietsArt und sonstGebietsArt</message><name>6.4.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.4.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz von typ und sonstTyp</message><name>6.5.1.1</name></rules></semantisch><syntaktisch><valid>true</valid></syntaktisch></validationResult></validationReport> \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/Dockerfile b/xplan-validator/xplan-validator-executor/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..8d209e513072ebc85bcd54d7eee00baea7a42478 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/Dockerfile @@ -0,0 +1,44 @@ +ARG XPLANBOX_VERSION=latest +ARG XPLANBOX_IMAGE_NAME_PREFIX=xplanbox + +FROM ${XPLANBOX_IMAGE_NAME_PREFIX}/xplan-docker-tomcat:$XPLANBOX_VERSION as builder + +FROM eclipse-temurin:17.0.11_9-jre-alpine +ARG BUILD_DATE=? +ARG DOCKER_IMAGE_NAME=? +ARG GIT_REVISION=? +ARG JAR_FILE=target/*.jar +ARG XPLANBOX_VERSION=latest + +# see https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys +LABEL "org.opencontainers.image.created"="$BUILD_DATE" \ + "org.opencontainers.image.description"="ozgxplanung xPlanBox component" \ + "org.opencontainers.image.licenses"="GNU Affero General Public License & others" \ + "org.opencontainers.image.ref.name"="$DOCKER_IMAGE_NAME" \ + "org.opencontainers.image.revision"="$GIT_REVISION" \ + "org.opencontainers.image.title"="ozgxplanung - $DOCKER_IMAGE_NAME" \ + "org.opencontainers.image.url"="https://gitlab.opencode.de/diplanung/ozgxplanung" \ + "org.opencontainers.image.vendor"="lat/lon GmbH" \ + "org.opencontainers.image.version"="$XPLANBOX_VERSION" + +ENV TZ=Europe/Berlin + +# copy jmx exporter from xplan-docker-tomcat until we switch to actuator based prometheus publishing +ENV JMX_EXPORTER_DIR=/xplanbox/prometheus +RUN mkdir -p $JMX_EXPORTER_DIR +COPY --from=builder $JMX_EXPORTER_DIR $JMX_EXPORTER_DIR + +ENV JAVA_ADDITIONAL_ARG_JAVA17_EXPORTS="--add-exports=java.desktop/com.sun.imageio.spi=ALL-UNNAMED" \ + JAVA_ADDITIONAL_ARG_JMX_EXPORTER='-javaagent:$JMX_EXPORTER_DIR/jmx_prometheus_javaagent-1.0.1.jar=12345:$JMX_EXPORTER_DIR/jmx-exporter.config.yaml' + +# set environment variables +ENV DEEGREE_WORKSPACE_ROOT=/xplanbox/deegree \ + JAVA_ADDITIONAL_ARG_APP="-Djavax.xml.transform.TransformerFactory=net.sf.saxon.TransformerFactoryImpl -Djts.overlay=ng -Duser.timezone=Europe/Berlin" \ + XPLANBOX_CONFIG="/xplanbox/xplan-validator-config/" + +COPY ${JAR_FILE} /xplanbox/app.jar +COPY run.sh /xplanbox/ + +USER 1001 + +ENTRYPOINT ["/bin/sh", "/xplanbox/run.sh"] diff --git a/xplan-validator/xplan-validator-executor/README.md b/xplan-validator/xplan-validator-executor/README.md new file mode 100644 index 0000000000000000000000000000000000000000..98c9a27570149f96e59a316d32e68bddf0549a8b --- /dev/null +++ b/xplan-validator/xplan-validator-executor/README.md @@ -0,0 +1,11 @@ +# xPlanValidatorExecutor + +## Development + +The application can be started locally with + +``` +mvn spring-boot:run -Dspring-boot.run.profiles=dev +``` + +(RabbitMQ required on localhost:5672 with credentials guest/guest) diff --git a/xplan-validator/xplan-validator-executor/pom.xml b/xplan-validator/xplan-validator-executor/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..666341e8a35b4b50f249ee86a955521ffa2d2d68 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/pom.xml @@ -0,0 +1,217 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>xplan-validator-executor</artifactId> + + <parent> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-validator</artifactId> + <version>8.0-SNAPSHOT</version> + </parent> + + <build> + <plugins> + <plugin> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>repackage</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> <!-- redeclare here to run *after* spring-boot repackage --> + <groupId>io.fabric8</groupId> + <artifactId>docker-maven-plugin</artifactId> + <executions> + <execution> + <id>context-sources</id> + <goals> + <goal>source</goal> + </goals> + <configuration> + <skip>${docker-image.skip-sources}</skip> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <addDefaultImplementationEntries>true</addDefaultImplementationEntries> + </manifest> + </archive> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <!-- xPlanBox --> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-core-validator-events</artifactId> + </dependency> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-validator-storage</artifactId> + </dependency> + <!-- XPlanung --> + <dependency> + <groupId>de.xleitstelle.xplanung</groupId> + <artifactId>regeln</artifactId> + </dependency> + <!-- spring --> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-web</artifactId> + <exclusions> + <exclusion> + <groupId>org.apache.tomcat.embed</groupId> + <artifactId>tomcat-embed-el</artifactId> + </exclusion> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>jul-to-slf4j</artifactId> + </exclusion> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>log4j-over-slf4j</artifactId> + </exclusion> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-to-slf4j</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + </exclusion> + <exclusion> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-amqp</artifactId> + <exclusions> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-to-slf4j</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + </exclusion> + <exclusion> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-core</artifactId> + </exclusion> + <exclusion> + <groupId>org.yaml</groupId> + <artifactId>snakeyaml</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-actuator</artifactId> + </dependency> + <!-- logging --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jcl-over-slf4j</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>jul-to-slf4j</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j2-impl</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-layout-template-json</artifactId> + </dependency> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-core-validator</artifactId> + </dependency> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-core-api</artifactId> + </dependency> + <dependency> + <groupId>jakarta.inject</groupId> + <artifactId>jakarta.inject-api</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-xml</artifactId> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework.boot</groupId> + <artifactId>spring-boot-starter-test</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + + <profiles> + <profile> + <id>docker</id> + <properties> + <docker-image.skip>false</docker-image.skip> + <docker-contextTarFile.expectedSizeInMat10pct>75</docker-contextTarFile.expectedSizeInMat10pct> + </properties> + <dependencies> + <dependency> <!-- to copy jmx exporter stuff from docker image --> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-docker-tomcat</artifactId> + <version>${project.version}</version> + <type>pom</type> + </dependency> + <!-- Profiles --> + <dependency> + <groupId>de.xleitstelle.xplanung</groupId> + <artifactId>regeln-berlin</artifactId> + </dependency> + <dependency> + <groupId>de.xleitstelle.xplanung</groupId> + <artifactId>regeln-brandenburg</artifactId> + </dependency> + </dependencies> + </profile> + </profiles> + +</project> \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/run.sh b/xplan-validator/xplan-validator-executor/run.sh new file mode 100755 index 0000000000000000000000000000000000000000..d12d67ccd8497bb2b4bc582a7eb5b75e5160e792 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/run.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +ALL_ADDITIONAL_ARGS=$(env | grep ^JAVA_ADDITIONAL_ | sed 's/^JAVA_ADDITIONAL_[[:alnum:]_]*=//' | tr '\n' ' ') + +if [ -n "${ALL_ADDITIONAL_ARGS}" ]; then + JAVA_OPTS=$(eval "echo $ALL_ADDITIONAL_ARGS") + echo "xPlanBox JAVA_OPTS set: $JAVA_OPTS" +fi + +exec java $JAVA_OPTS -jar /xplanbox/app.jar diff --git a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/PlanValidator.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/PlanValidator.java new file mode 100644 index 0000000000000000000000000000000000000000..3e676f94fca1585a7f9547c247b8ec40879be680 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/PlanValidator.java @@ -0,0 +1,155 @@ +package de.latlon.xplanbox.validator.executor; + +import static de.latlon.xplanbox.validator.storage.ErrorType.INTERNAL_ERROR; +import static de.latlon.xplanbox.validator.storage.ErrorType.INVALID_REQUEST; +import static de.latlon.xplanbox.validator.storage.ErrorType.fromStatusCode; + +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.core.validator.events.v1.XPlanboxPublicV1Event; +import de.latlon.core.validator.events.v1.XPlanboxPublicV1Event.EventType; +import de.latlon.xplan.commons.archive.XPlanArchive; +import de.latlon.xplan.validator.ValidatorException; +import de.latlon.xplan.validator.report.ValidatorReport; +import de.latlon.xplanbox.api.commons.ObjectMapperContextResolver; +import de.latlon.xplanbox.api.commons.ValidationReportBuilder; +import de.latlon.xplanbox.api.commons.exception.XPlanApiException; +import de.latlon.xplanbox.api.commons.v1.model.ValidationReport; +import de.latlon.xplanbox.validator.executor.handler.ValidationHandler; +import de.latlon.xplanbox.validator.storage.StatusType; +import de.latlon.xplanbox.validator.storage.StoredValidationReport; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType; + +/** + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +@Component +public class PlanValidator { + + private static final Logger LOG = LoggerFactory.getLogger(PlanValidator.class); + + private final ValidationExecutionStorage validationExecutionStorage; + + private final ValidationHandler validationHandler; + + private final EventSender eventSender; + + public PlanValidator(ValidationExecutionStorage validationExecutionStorage, ValidationHandler validationHandler, + EventSender eventSender) { + this.validationExecutionStorage = validationExecutionStorage; + this.validationHandler = validationHandler; + this.eventSender = eventSender; + } + + public void validate(ValidationRequestedEvent event) throws Exception { + validationExecutionStorage.changeStatus(event.getUuid(), StatusType.STARTED); + eventSender.sendPublicEvent( + new XPlanboxPublicV1Event(EventType.VALIDATION_START, event.getUuid(), "validation started")); + + try { + doValidate(event); + } + finally { + eventSender.sendPublicEvent( + new XPlanboxPublicV1Event(EventType.VALIDATION_FINISHED, event.getUuid(), "validation finished")); + } + } + + private void doValidate(ValidationRequestedEvent event) throws Exception { + Map<ReportType, Path> reports = new HashMap<>(); + try { + validationExecutionStorage.changeStatus(event.getUuid(), StatusType.STARTED); + Path tmpPath = Files.createTempFile(event.getUuid(), ".rcv"); + if (Files.exists(tmpPath)) + Files.delete(tmpPath); + validationExecutionStorage.writePlanToValidate(event.getUuid(), tmpPath); + + final XPlanArchive archive; + if (event.getOriginFile() == ValidationRequestedEvent.OriginFile.GML) { + archive = validationHandler.createArchiveFromGml(tmpPath.toFile(), + event.getSettings().getValidationName()); + } + else { + archive = validationHandler.createArchiveFromZip(tmpPath.toFile(), + event.getSettings().getValidationName()); + } + + LOG.info("Validating {}", tmpPath); + ValidatorReport validatorReport = validationHandler.validate(archive, event.getxFileName(), + event.getSettings()); + // TODO: delete tmpPath + + ValidationReport validationReport = createValidationReport(event, archive, validatorReport); + + reports.put(ReportType.JSON, createJsonReportFile(validationReport)); + reports.put(ReportType.PDF, createPdfReportFile(validatorReport)); + reports.put(ReportType.ZIP, createZipReportFile(validatorReport)); + + StoredValidationReport storedValidationReport = validationExecutionStorage + .saveValidationResult(event.getUuid(), reports); + LOG.info("Validation Report: " + validatorReport); + validationExecutionStorage.changeStatus(event.getUuid(), StatusType.FINISHED, storedValidationReport); + validationExecutionStorage.cleanupAfterValidation(event.getUuid()); + } + catch (XPlanApiException e) { + validationExecutionStorage.changeStatus(event.getUuid(), e.getMessage(), fromStatusCode(e.getStatusCode())); + LOG.info("Validation with uuid " + event.getUuid() + " failed: " + e.getMessage()); + throw e; + } + catch (ValidatorException e) { + validationExecutionStorage.changeStatus(event.getUuid(), e.getMessage(), INVALID_REQUEST); + LOG.info("Validation with uuid " + event.getUuid() + " failed: " + e.getMessage()); + throw e; + } + catch (Exception e) { + validationExecutionStorage.changeStatus(event.getUuid(), e.getMessage(), INTERNAL_ERROR); + LOG.info("Validation with uuid " + event.getUuid() + " failed: " + e.getMessage()); + throw e; + } + finally { + for (Path path : reports.values()) { + Files.deleteIfExists(path); + } + } + } + + private ValidationReport createValidationReport(ValidationRequestedEvent event, final XPlanArchive archive, + ValidatorReport validatorReport) { + URI wmsUrl = validationHandler.addToWms(archive); + return new ValidationReportBuilder().validatorReport(validatorReport) // + .filename(event.getxFileName()) // + .wmsUrl(wmsUrl) // + .build(); + } + + private Path createJsonReportFile(ValidationReport validationReport) throws IOException { + ObjectMapper mapper = new ObjectMapperContextResolver().getContext(ValidationReport.class); + Path reportFile = Files.createTempFile("", ".json"); + mapper.writeValue(reportFile.toFile(), validationReport); + return reportFile; + } + + private Path createPdfReportFile(ValidatorReport validatorReport) throws IOException { + return validationHandler.writePdfReport(validatorReport); + } + + private Path createZipReportFile(ValidatorReport validatorReport) throws IOException { + return validationHandler.zipReports(validatorReport); + } + +} diff --git a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/SpringBootApp.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/SpringBootApp.java new file mode 100644 index 0000000000000000000000000000000000000000..57497839cd1658dbe4d81973b5b846e9c6f118b5 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/SpringBootApp.java @@ -0,0 +1,54 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.executor; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +@SpringBootApplication +@ComponentScan(basePackages = { "de.latlon.xplanbox.validator.executor" }) +@EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class }) +public class SpringBootApp extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(SpringBootApp.class, args); + } + + /** + * Runs the application as deployable WAR. + * @param application Spring application builder + * @return Spring application builder with this application configured + */ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + return application.sources(SpringBootApp.class); + } + +} diff --git a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ApplicationContext.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ApplicationContext.java new file mode 100644 index 0000000000000000000000000000000000000000..c223b5d3e53a0cc6bea6a793a77c9a7d1ea1d7bf --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ApplicationContext.java @@ -0,0 +1,162 @@ +/*- + * #%L + * xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.executor.config; + +import static java.nio.file.Files.createTempDirectory; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import de.latlon.xplan.commons.configuration.PropertiesLoader; +import de.latlon.xplan.commons.configuration.SystemPropertyPropertiesLoader; +import de.latlon.xplan.manager.web.shared.ConfigurationException; +import de.latlon.xplan.validator.XPlanValidator; +import de.latlon.xplan.validator.configuration.ValidatorConfiguration; +import de.latlon.xplan.validator.configuration.ValidatorConfigurationParser; +import de.latlon.xplan.validator.geometric.GeometricValidator; +import de.latlon.xplan.validator.geometric.GeometricValidatorImpl; +import de.latlon.xplan.validator.report.ReportArchiveGenerator; +import de.latlon.xplan.validator.report.ReportWriter; +import de.latlon.xplan.validator.semantic.SemanticValidator; +import de.latlon.xplan.validator.semantic.configuration.SemanticRulesConfiguration; +import de.latlon.xplan.validator.semantic.configuration.SemanticRulesMainConfiguration; +import de.latlon.xplan.validator.semantic.configuration.xquery.XQuerySemanticValidatorConfigurationRetriever; +import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; +import de.latlon.xplan.validator.semantic.profile.SemanticProfilesCreator; +import de.latlon.xplan.validator.semantic.xquery.XQuerySemanticValidator; +import de.latlon.xplan.validator.syntactic.SyntacticValidator; +import de.latlon.xplan.validator.syntactic.SyntacticValidatorImpl; +import de.latlon.xplan.validator.wms.config.ValidatorWmsContext; +import de.latlon.xplanbox.api.commons.handler.SystemConfigHandler; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.config.AmazonS3Context; +import de.latlon.xplanbox.validator.storage.filesystem.FileSystemValidationExecutionStorage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Profile; +import org.springframework.core.io.ResourceLoader; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +@Configuration +@Import({ ValidatorWmsContext.class, AmazonS3Context.class }) +public class ApplicationContext { + + @Autowired + private ResourceLoader resourceLoader; + + @Bean + public SystemConfigHandler systemConfigHandler(XQuerySemanticValidatorConfigurationRetriever configurationRetriever, + SemanticProfiles semanticProfiles) { + return new SystemConfigHandler(configurationRetriever, semanticProfiles.getProfileMetadata()); + } + + @Bean + public Path uploadFolder() throws IOException { + return createTempDirectory("xplan-validator"); + } + + @Bean + public SyntacticValidator syntacticValidator() { + return new SyntacticValidatorImpl(); + } + + @Bean + public GeometricValidator geometricValidator() { + return new GeometricValidatorImpl(); + } + + @Bean + public SemanticValidator semanticValidator(XQuerySemanticValidatorConfigurationRetriever configurationRetriever) + throws ConfigurationException { + return new XQuerySemanticValidator(configurationRetriever); + } + + @Bean + public XQuerySemanticValidatorConfigurationRetriever xQuerySemanticValidatorConfigurationRetriever( + SemanticRulesConfiguration semanticRulesConfiguration) { + return new XQuerySemanticValidatorConfigurationRetriever(semanticRulesConfiguration); + } + + @Bean + public SemanticRulesConfiguration semanticRulesConfiguration(ValidatorConfiguration validatorConfiguration) { + Path validationRulesDirectory = validatorConfiguration.getValidationRulesDirectory(); + return new SemanticRulesMainConfiguration(validationRulesDirectory); + } + + @Bean + public SemanticProfiles semanticProfiles(ValidatorConfiguration validatorConfiguration, + PropertiesLoader validatorPropertiesLoader, + @Value("${xplanbox.validation.profiles}") String activatedProfiles) throws ConfigurationException { + List<String> activatedProfilesList = activatedProfiles != null ? Arrays.asList(activatedProfiles.split(",")) + : Collections.emptyList(); + SemanticProfilesCreator semanticProfilesCreator = new SemanticProfilesCreator(validatorConfiguration, + validatorPropertiesLoader, resourceLoader); + return semanticProfilesCreator.createSemanticProfiles(activatedProfilesList); + } + + @Bean + public PropertiesLoader validatorPropertiesLoader() { + return new SystemPropertyPropertiesLoader(ValidatorConfiguration.class); + } + + @Bean + public ValidatorConfiguration validatorConfiguration(PropertiesLoader validatorPropertiesLoader) + throws IOException, ConfigurationException { + ValidatorConfigurationParser validatorConfigurationParser = new ValidatorConfigurationParser(); + return validatorConfigurationParser.parse(validatorPropertiesLoader); + } + + @Bean + public XPlanValidator xplanValidator(GeometricValidator geometricValidator, SyntacticValidator syntacticValidator, + SemanticValidator semanticValidator, SemanticProfiles semanticProfiles, + ReportArchiveGenerator reportArchiveGenerator) { + return new XPlanValidator(geometricValidator, syntacticValidator, semanticValidator, + semanticProfiles.getProfileValidators(), reportArchiveGenerator); + } + + @Bean + public ReportArchiveGenerator reportArchiveGenerator(ValidatorConfiguration validatorConfiguration) { + return new ReportArchiveGenerator(validatorConfiguration); + } + + @Bean + public ReportWriter reportWriter() { + return new ReportWriter(); + } + + @Bean + @Profile("!s3execution") + public ValidationExecutionStorage validationExecutionStorage( + @Value("${xplanbox.validation.fsdirectory}") Optional<Path> fsExecutionDir) throws IOException { + return new FileSystemValidationExecutionStorage(fsExecutionDir); + } + +} diff --git a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/messagingrabbitmq/Receiver.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ExecutorRabbitConfiguration.java similarity index 56% rename from xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/messagingrabbitmq/Receiver.java rename to xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ExecutorRabbitConfiguration.java index 11f147b3f1f32240087fa9ea1e3d4e3df1a0433b..6741c53ed1af39fcf1384c3efe1875c2357bc0fb 100644 --- a/xplan-manager/xplan-manager-api/src/main/java/de/latlon/xplanbox/api/manager/messagingrabbitmq/Receiver.java +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/config/ExecutorRabbitConfiguration.java @@ -18,30 +18,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplanbox.api.manager.messagingrabbitmq; +package de.latlon.xplanbox.validator.executor.config; -import static org.slf4j.LoggerFactory.getLogger; +import de.latlon.core.validator.events.RabbitEmitterConfig; +import de.latlon.core.validator.events.RabbitReceiverConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; -import java.util.function.Consumer; - -import org.slf4j.Logger; -import org.springframework.stereotype.Component; - -import de.latlon.core.validator.events.ValidationMessageReceiver; -import de.latlon.core.validator.events.ValidationRequestedEvent; - -@Component -public class Receiver implements Consumer<ValidationRequestedEvent> { - - private static final Logger LOG = getLogger(Receiver.class); - - Receiver(ValidationMessageReceiver msgReceiver) { - msgReceiver.setListener(this); - } - - @Override - public void accept(ValidationRequestedEvent e) { - LOG.info("Received event: " + e); - } +@Configuration +@Import({ RabbitReceiverConfig.class, RabbitEmitterConfig.class }) +public class ExecutorRabbitConfiguration { } diff --git a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/ValidationHandler.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java similarity index 99% rename from xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/ValidationHandler.java rename to xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java index 2d01d0a4711483fa52cad412e4dd7702f96b980d..f7b49c38b9061202ca88d74eea3abebd6430d01e 100644 --- a/xplan-validator/xplan-validator-api/src/main/java/de/latlon/xplanbox/api/validator/handler/ValidationHandler.java +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandler.java @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplanbox.api.validator.handler; +package de.latlon.xplanbox.validator.executor.handler; import de.latlon.xplan.commons.archive.XPlanArchive; import de.latlon.xplan.commons.archive.XPlanArchiveCreator; diff --git a/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/messagingrabbitmq/Receiver.java b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/messagingrabbitmq/Receiver.java new file mode 100644 index 0000000000000000000000000000000000000000..f1f00e258837d5ced01b635a230feb846009612d --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/java/de/latlon/xplanbox/validator/executor/messagingrabbitmq/Receiver.java @@ -0,0 +1,71 @@ +/*- + * #%L + * xplan-manager-api - Software zur Verwaltung von XPlanGML Daten + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.executor.messagingrabbitmq; + +import static de.latlon.core.validator.events.ValidationFinishedEvent.ValidationFinishedStatus.FAILED; +import static de.latlon.core.validator.events.ValidationFinishedEvent.ValidationFinishedStatus.SUCCEEDED; +import static org.slf4j.LoggerFactory.getLogger; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationFinishedEvent; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.xplanbox.validator.executor.PlanValidator; +import org.slf4j.Logger; +import org.springframework.amqp.rabbit.annotation.RabbitHandler; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +@Component +@RabbitListener(queues = "#{validationTaskQueue.name}", concurrency = "1") +public class Receiver { + + private static final Logger LOG = getLogger(Receiver.class); + + @Autowired + private PlanValidator validator; + + @Autowired + private EventSender eventSender; + + @RabbitHandler + public void accept(ValidationRequestedEvent event) { + LOG.info("Received event: " + event); + + try { + validator.validate(event); + + ValidationFinishedEvent finishedEvent = new ValidationFinishedEvent(event.getUuid(), SUCCEEDED); + eventSender.sendEvent(finishedEvent); + } + catch (Exception e) { + LOG.error("Failed to process event. Discarding it", e); + + ValidationFinishedEvent finishedEvent = new ValidationFinishedEvent(event.getUuid(), FAILED); + eventSender.sendEvent(finishedEvent); + } + } + +} diff --git a/xplan-validator/xplan-validator-executor/src/main/resources/application-dev.properties b/xplan-validator/xplan-validator-executor/src/main/resources/application-dev.properties new file mode 100644 index 0000000000000000000000000000000000000000..526d01eb7e58f7e2ee944c05cd17d80268b94df0 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/resources/application-dev.properties @@ -0,0 +1,23 @@ +### +# #%L +# xplan-api-validator-executor - Modul zur Gruppierung der REST-API +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### + +## for local development +server.port=8094 \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/main/resources/application.properties b/xplan-validator/xplan-validator-executor/src/main/resources/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..61cd9f495550daddc2506379a95c9628dc761a51 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/resources/application.properties @@ -0,0 +1,50 @@ +### +# #%L +# xplan-api-validator - Modul zur Gruppierung der REST-API +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### +server.servlet.context-path=/xplan-validator-executor + +logging.level.root=warn +logging.level.de.latlon=debug +logging.level.org.springframework.web=debug +logging.level.org.hibernate=error + +management.endpoint.health.probes.enabled=true + +spring.rabbitmq.host=${XPLAN_RABBIT_HOST:localhost} +spring.rabbitmq.password=${XPLAN_RABBIT_PASSWORD:guest} +spring.rabbitmq.port=${XPLAN_RABBIT_PORT:5672} +spring.rabbitmq.username=${XPLAN_RABBIT_USER:guest} +spring.rabbitmq.listener.prefetch=1 + +xplanbox.rabbitmq.internal.prefix=${XPLAN_RABBIT_INTERNAL_PREFIX:xplanbox} +xplanbox.rabbitmq.public.fanout=${XPLAN_RABBIT_PUBLIC_FANOUT:} + +xplanbox.rabbitmq.fanout.topics=none +xplanbox.rabbitmq.task.topics=task.# + +xplanbox.s3.accessKeyId=${XPLAN_S3_ACCESS_KEY} +xplanbox.s3.endpoint.url=${XPLAN_S3_ENDPOINT} +xplanbox.s3.region=${XPLAN_S3_REGION} +xplanbox.s3.secretKey=${XPLAN_S3_SECRET_ACCESS_KEY} + +xplanbox.validation.fsdirectory=${XPLAN_FS_DIRECTORY:/tmp/validation} +xplanbox.validation.profiles=${XPLAN_VALIDATOR_PROFILES:} +xplanbox.validation.s3.bucketName=${XPLAN_VALIDATION_S3_BUCKET_NAME:validation} +xplanbox.validation.s3.bucketPublicUrl=${XPLAN_VALIDATION_S3_BUCKET_PUBLIC_URL} diff --git a/xplan-validator/xplan-validator-executor/src/main/resources/log4j2.yaml b/xplan-validator/xplan-validator-executor/src/main/resources/log4j2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fa1df7f8bf1fdc9cf4ae1e690a7fc1d23ed3c689 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/main/resources/log4j2.yaml @@ -0,0 +1,82 @@ +### +# #%L +# xplan-validator-api - Software zur Verwaltung von XPlanGML Daten +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### +Configuration: + name: XPlanValidatorExecutor + status: warn + Properties: + Property: + name: appenderToUse + value: stdout_${env:LOG4J_LAYOUT:-plain} + + Appenders: + Console: + - name: stdout_plain + target: SYSTEM_OUT + PatternLayout: + Pattern: "%d %p %C{1.} [%t] %m%n" + - name: stdout_json + target: SYSTEM_OUT + JsonTemplateLayout: + eventTemplateUri: classpath:de/latlon/xplan/commons/minimalist-format.json + + Loggers: + Root: + level: info + AppenderRef: + - ref: ${appenderToUse} + level: info + Logger: + - name: org.deegree + level: info + - name: de.latlon.xplan + level: info + - name: de.latlon.xplanbox.api + level: info + - name: de.latlon.xplanbox.api.validator.config + level: info + - name: org.springframework + level: warn + - name: org.glassfish + level: warn + - name: org.glassfish.jersey + level: warn + - name: org.apache + level: warn + - name: net.sf.jasperreports + level: warn + - name: com.ctc.wstx + level: warn + - name: com.sun.xml + level: warn + - name: org.reflections + level: fatal + - name: io.swagger.v3 + level: warn + - name: org.deegree.geometry.standard.AbstractDefaultGeometry + level: fatal + - name: de.latlon.xplan.validator.geometric.XPlanGeometryInspector + level: fatal + - name: de.latlon.xplan.validator.semantic.configuration.xquery.XQuerySemanticValidatorConfigurationRetriever + level: warn + - name: bind + level: error + - name: logger + level: error diff --git a/xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/PlanValidatorTest.java b/xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/PlanValidatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..45a735d2aae92fc71a8a1d4472444d1b3bd8f0c2 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/PlanValidatorTest.java @@ -0,0 +1,196 @@ +/*- + * #%L + * xplan-core-api - Modul zur Gruppierung der Kernmodule + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.executor; + +import static de.latlon.xplan.validator.web.shared.ValidationType.GEOMETRIC; +import static de.latlon.xplan.validator.web.shared.ValidationType.SEMANTIC; +import static de.latlon.xplan.validator.web.shared.ValidationType.SYNTACTIC; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.json.JSONObject; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.latlon.core.validator.events.EventSender; +import de.latlon.core.validator.events.ValidationRequestedEvent; +import de.latlon.core.validator.events.ValidationRequestedEvent.MediaType; +import de.latlon.core.validator.events.ValidationRequestedEvent.OriginFile; +import de.latlon.core.validator.events.v1.XPlanboxPublicV1Event; +import de.latlon.core.validator.events.v1.XPlanboxPublicV1Event.EventType; +import de.latlon.xplan.commons.archive.SemanticValidableXPlanArchive; +import de.latlon.xplan.validator.semantic.configuration.metadata.RulesMetadata; +import de.latlon.xplan.validator.semantic.profile.SemanticProfileValidator; +import de.latlon.xplan.validator.semantic.profile.SemanticProfiles; +import de.latlon.xplan.validator.semantic.report.SemanticValidatorResult; +import de.latlon.xplan.validator.web.shared.ValidationSettings; +import de.latlon.xplan.validator.web.shared.ValidationType; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType; + +/** + * @author <a href="mailto:guillemot@lat-lon.de">Marc Guillemot</a> + * @since 8.0 + */ +@SpringBootTest(classes = { SpringBootApp.class }) +class PlanValidatorTest { + + @TestConfiguration + static class TestConfig { + + @Primary + @Bean + public SemanticProfiles fakeSemanticProfiles() { + RulesMetadata profile1 = new RulesMetadata("id1", "test1", "description1", "0.1", "unbekannt"); + RulesMetadata profile2 = new RulesMetadata("id2", "test2", "description2", "0.2", "lokal"); + return new SemanticProfiles().add(profile1, createValidator(profile1)) + .add(profile2, createValidator(profile2)); + } + + private static SemanticProfileValidator createValidator(RulesMetadata profile) { + SemanticProfileValidator semanticProfileValidator = mock(SemanticProfileValidator.class); + when(semanticProfileValidator.getId()).thenReturn(profile.getId()); + SemanticValidatorResult result = mock(SemanticValidatorResult.class); + when(result.getRulesMetadata()).thenReturn(profile); + when(semanticProfileValidator.validateSemantic(any(SemanticValidableXPlanArchive.class), anyList())) + .thenReturn(result); + return semanticProfileValidator; + } + + } + + @MockBean + EventSender eventSender; + + @Captor + ArgumentCaptor<XPlanboxPublicV1Event> publicEventCaptor; + + @Autowired + PlanValidator validator; + + @Autowired + ValidationExecutionStorage validationExecutionStorage; + + @Autowired + ObjectMapper jsonMapper; + + @Autowired + private SemanticProfiles semanticProfiles; + + @Test + // from old + // ValidateApiTest.verifyThat_Response_ContainsCorrectStatusCodeAndMediaType + void validateGmlJsonReport() throws Exception { + + Path pathToPlan = Paths.get(getClass().getResource("/xplan.gml").toURI()); + String uuid = validationExecutionStorage.addPlanToValidate(pathToPlan); + + List<ValidationType> validationTypes = List.of(SYNTACTIC, SEMANTIC, GEOMETRIC); + ValidationSettings settings = new ValidationSettings("edfd613e-b85d-4ea6-9b97-bb33712b1ba6", validationTypes, + null); + ValidationRequestedEvent event = new ValidationRequestedEvent(uuid, settings, + null /* filename */, MediaType.JSON, OriginFile.GML); + + validator.validate(event); + + verifyExpectedReport(uuid, ReportType.JSON, "report1.expected.json"); + + XPlanboxPublicV1Event expectedStartEvent = new XPlanboxPublicV1Event(EventType.VALIDATION_START, uuid, + "validation started"); + XPlanboxPublicV1Event expectedFinishedEvent = new XPlanboxPublicV1Event(EventType.VALIDATION_FINISHED, uuid, + "validation finished"); + Mockito.verify(eventSender, times(2)).sendPublicEvent(publicEventCaptor.capture()); + assertThat(publicEventCaptor.getAllValues()).containsExactly(expectedStartEvent, expectedFinishedEvent); + } + + @Test + // from old + // ValidateApiTest.verifyThat_validationXZipCompressedWithProfile_Response_ContainsJsonEncoding() + void valideZipWithOneProfile() throws Exception { + Path pathToPlan = Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI()); + String uuid = validationExecutionStorage.addPlanToValidate(pathToPlan); + + List<ValidationType> validationTypes = List.of(SYNTACTIC, SEMANTIC, GEOMETRIC); + List<String> profiles = List.of(semanticProfiles.getProfileValidators().get(0).getId()); + ValidationSettings settings = new ValidationSettings("8dc3163c-a361-49b8-9c53-3733f7d61274", validationTypes, + profiles, null /* extendedOptions */); + ValidationRequestedEvent event = new ValidationRequestedEvent(uuid, settings, null, MediaType.JSON, + OriginFile.ZIP); + + validator.validate(event); + + verifyExpectedReport(uuid, ReportType.JSON, "report6.expected.json"); + } + + @Test + // from old + // ValidateApiTest.verifyThat_validationXZipCompressedWithProfiles_Response_ContainsJsonEncoding() + void valideZipWithTwoProfiles() throws Exception { + Path pathToPlan = Paths.get(getClass().getResource("/bplan_valid_41.zip").toURI()); + String uuid = validationExecutionStorage.addPlanToValidate(pathToPlan); + + List<ValidationType> validationTypes = List.of(SYNTACTIC, SEMANTIC, GEOMETRIC); + List<String> profiles = List.of(semanticProfiles.getProfileValidators().get(0).getId()); + ValidationSettings settings = new ValidationSettings("6463c72f-4a7d-40d0-a129-3de95fdd0eb8", validationTypes, + profiles, null /* extendedOptions */); + ValidationRequestedEvent event = new ValidationRequestedEvent(uuid, settings, null, MediaType.JSON, + OriginFile.ZIP); + + validator.validate(event); + + verifyExpectedReport(uuid, ReportType.JSON, "report7.expected.json"); + } + + private void verifyExpectedReport(String uuid, ReportType reportType, String expectedReportFile) throws Exception { + String jsonReport = new String(validationExecutionStorage.retrieveReport(uuid, reportType), + StandardCharsets.UTF_8); + + String expectedReport = Files.readString(Paths.get(getClass().getResource(expectedReportFile).toURI())); + assertThat(sanitizeDates(jsonReport)).isEqualTo(sanitizeDates(expectedReport)); + } + + private String sanitizeDates(String s) throws Exception { + s = s.replaceAll("\"date\":\"[^\"]+\"", "\"date\":\"-fixed-for-comparison-\""); + JSONObject json = new JSONObject(s); + return json.toString(4); + } + +} diff --git a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ValidationHandlerTest.java b/xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandlerTest.java similarity index 91% rename from xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ValidationHandlerTest.java rename to xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandlerTest.java index 3132d2cc73f4ef3be7ea448aa1d215bd301a4bac..5287428ec0bf455dcf079ec6ac7ee578d9066e01 100644 --- a/xplan-validator/xplan-validator-api/src/test/java/de/latlon/xplanbox/api/validator/handler/ValidationHandlerTest.java +++ b/xplan-validator/xplan-validator-executor/src/test/java/de/latlon/xplanbox/validator/executor/handler/ValidationHandlerTest.java @@ -18,7 +18,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ -package de.latlon.xplanbox.api.validator.handler; +package de.latlon.xplanbox.validator.executor.handler; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -36,32 +36,31 @@ import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.io.TempDir; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - +import de.latlon.core.validator.events.EventSender; import de.latlon.xplan.commons.archive.XPlanArchive; import de.latlon.xplan.validator.ValidatorException; import de.latlon.xplan.validator.report.ValidatorReport; import de.latlon.xplan.validator.web.shared.ValidationSettings; import de.latlon.xplanbox.api.commons.exception.InvalidXPlanGmlOrArchive; -import de.latlon.xplanbox.api.validator.config.ApplicationContext; +import de.latlon.xplanbox.validator.executor.SpringBootApp; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; /** * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { ApplicationContext.class }) -@ActiveProfiles("test") +@SpringBootTest(classes = { SpringBootApp.class }) public class ValidationHandlerTest { + @MockBean + EventSender eventSender; + @TempDir public static Path tempFolder; diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/bplan_valid_41.zip b/xplan-validator/xplan-validator-executor/src/test/resources/bplan_valid_41.zip new file mode 100644 index 0000000000000000000000000000000000000000..3b382162672e420c133b227c2c5b8c6ebad7d6aa Binary files /dev/null and b/xplan-validator/xplan-validator-executor/src/test/resources/bplan_valid_41.zip differ diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/config/application.properties b/xplan-validator/xplan-validator-executor/src/test/resources/config/application.properties new file mode 100644 index 0000000000000000000000000000000000000000..bf0a025176235439bf659c4d273190ae75d59c06 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/config/application.properties @@ -0,0 +1,26 @@ +### +# #%L +# xplan-api-validator - Modul zur Gruppierung der REST-API +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### + +# properties overwritten for unit/integration tests + +spring.profiles.active=test + +spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report1.expected.json b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report1.expected.json new file mode 100644 index 0000000000000000000000000000000000000000..98e35f8addf7aa7909484b1ae31726a476e11ead --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report1.expected.json @@ -0,0 +1,1697 @@ +{ + "date": "-fixed-for-comparison-", + "valid": false, + "externalReferences": [], + "externalReferencesResult": [], + "rulesMetadata": { + "source": "https:\/\/gitlab.opencode.de\/xleitstelle\/xplanung\/validierungsregeln\/standard\/-\/tree\/v1.1.9", + "version": "1.1.9" + }, + "filename": null, + "validationResult": { + "syntaktisch": { + "valid": true, + "messages": [] + }, + "semantisch": { + "valid": true, + "rules": [ + { + "isValid": true, + "name": "2.1.2.1", + "warnedFeatures": [], + "message": "Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "2.1.3.1", + "warnedFeatures": [], + "message": "Angabe eines Standard CRS", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "2.2.1.1", + "warnedFeatures": [], + "message": "Flächenschlussbedingung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.1.1", + "warnedFeatures": [], + "message": "Relationen auf Text-Abschnitte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.1.2", + "warnedFeatures": [], + "message": "Relationen auf Begründungs-Abschnitte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.2.1", + "warnedFeatures": [], + "message": "Relation auf Präsentationsobjekte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.2.2", + "warnedFeatures": [], + "message": "Relation auf Fachobjekte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.2.3", + "warnedFeatures": [], + "message": "Relation auf Basis-Rasterplan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.3.1", + "warnedFeatures": [], + "message": "Relationen auf Text-Abschnitte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.3.2", + "warnedFeatures": [], + "message": "Relationen auf Begründungs-Abschnitte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.3.3", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Plan-Bereiche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.1.3.4", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Präsentationsobjekte", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.1.1", + "warnedFeatures": [], + "message": "Spezifikation des Textinhalts", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.2.1", + "warnedFeatures": [], + "message": "Spezifikation des Textinhalts", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.3.1", + "warnedFeatures": [], + "message": "Konsistenz der verschiedenen Höhenangaben", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.3.2", + "warnedFeatures": [], + "message": "Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.3.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.4.1", + "warnedFeatures": [], + "message": "Verweis auf Dokumente.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.2.5.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel).", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.1.1", + "warnedFeatures": [], + "message": "Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.1.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute art und index", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.1.3", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Plan-Bereiche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.2.1", + "warnedFeatures": [], + "message": "Einschränkung des Raumbezugs auf Punktgeometrie.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.3.1", + "warnedFeatures": [], + "message": "Einschränkung des Raumbezugs auf Liniengeometrie.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "3.3.4.1", + "warnedFeatures": [], + "message": "Einschränkung des Raumbezugs auf Flächengeometrie.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.1.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation bereich", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.2.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation inhaltBPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.2.2", + "warnedFeatures": [], + "message": "Einschränkung der Relation rasterAenderung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.2.3", + "warnedFeatures": [], + "message": "Einschränkung der Relation gehoertZuPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.2", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchMassnahme", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.3", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.4", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.5", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Plan-Bereiche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.6", + "warnedFeatures": [], + "message": "BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.3.7", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchABE", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.4.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.4.2", + "warnedFeatures": [], + "message": "Einschränkung auf Flächengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.5.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.5.2", + "warnedFeatures": [], + "message": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.6.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekte gehören nie zum Flächenschluss.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.7.1", + "warnedFeatures": [], + "message": "Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.8.1", + "warnedFeatures": [], + "message": "Einschränkung auf Liniengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.1.9.1", + "warnedFeatures": [], + "message": "Einschränkung auf Punktgeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GFZ", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.2", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GF", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GFZ und GF", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.4", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur BMZ", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.5", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur BM", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.6", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur BMZ und BM", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.7", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GRZ", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.8", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GR", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.9", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GRZ und GR", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.10", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zu Z", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.2.11", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zu ZU", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.3.1", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur Dachneigung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.3.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute dachform und detaillierteDachform", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.1.1", + "warnedFeatures": [], + "message": "Relation abweichungText", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.1.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.1.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute bauweise und abweichendeBauweise", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.1.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.2.1", + "warnedFeatures": [], + "message": "Relation baugrenze", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.2.2", + "warnedFeatures": [], + "message": "Relation baulinie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.10.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute typ und sonstTyp", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.13.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.13.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.13.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.14.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.14.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.14.3", + "warnedFeatures": [], + "message": "Einschränkung der Relation eigentümer", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.14.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.5.15.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.6.3.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.1.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.2.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.7.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.1.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.2.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.2.4", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.8.3.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute massnahme und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.2.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.2.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.3.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute massnahme und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.3.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.3.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.5.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute massnahme und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.5.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.5.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.5.4", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.9.6.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.10.2.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.11.1.7", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.12.1.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation begrenzungslinie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.12.2.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation begrenzungslinie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.12.2.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.12.3.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.13.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.13.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.2.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.3.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute startWinkel und endWinkel", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.3.2", + "warnedFeatures": [], + "message": "Kein flächenhafter Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.4.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.6.1", + "warnedFeatures": [], + "message": "Notwendige Spezifikation einer Textlichen Darstellung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.7.1", + "warnedFeatures": [], + "message": "Verwendung der Relation hoehenangabe", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "4.14.7.2", + "warnedFeatures": [], + "message": "Kein flächenhafter Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.1.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation bereich", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.1.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute planArt und sonstPlanArt", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.2.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation inhaltFPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.2.2", + "warnedFeatures": [], + "message": "Einschränkung der Relation rasterAenderung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.2.3", + "warnedFeatures": [], + "message": "Einschränkung der Relation gehoertZuPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.3.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchFlaeche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.3.2", + "warnedFeatures": [], + "message": "Einschränkung der Relation wirdAusgeglichenDurchSPE", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.3.3", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Plan-Bereiche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.3.4", + "warnedFeatures": [], + "message": "FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.4.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.4.2", + "warnedFeatures": [], + "message": "Einschränkung auf Flächengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.5.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.5.2", + "warnedFeatures": [], + "message": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.6.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekte gehören nie zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.7.1", + "warnedFeatures": [], + "message": "Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.8.1", + "warnedFeatures": [], + "message": "Einschränkung auf Liniengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.1.9.1", + "warnedFeatures": [], + "message": "Einschränkung auf Punktgeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.3.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.3.1.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.3.1.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.3.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Angaben zur GFZ", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.7", + "warnedFeatures": [], + "message": "Einschränkung des Raumbezugs", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.1.8", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.2.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.2.4", + "warnedFeatures": [], + "message": "Einschränkung des Raumbezugs", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.4.2.5", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.1.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.2.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.5.3.7", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.6.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.6.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.6.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.6.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.3", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.5", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.6", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.7.1.7", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.8.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.8.1.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.8.1.3", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.9.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.9.1.2", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.9.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.9.2.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.1.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.2.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.2.2", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.2.3", + "warnedFeatures": [], + "message": "Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.2.4", + "warnedFeatures": [], + "message": "Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.3.1", + "warnedFeatures": [], + "message": "Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.4.1", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "5.10.6.1", + "warnedFeatures": [], + "message": "Notwendige Spezifikation einer Textlichen Darstellung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.1.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation bereich", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.2.1", + "warnedFeatures": [], + "message": "Einschränkung der Relation inhaltSoPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.2.2", + "warnedFeatures": [], + "message": "Einschränkung der Relation rasterAenderung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.2.3", + "warnedFeatures": [], + "message": "Einschränkung der Relation gehoertZuPlan", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.3.1", + "warnedFeatures": [], + "message": "Rückwärts-Referenzen auf Plan-Bereiche", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.3.2", + "warnedFeatures": [], + "message": "SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.4.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.4.2", + "warnedFeatures": [], + "message": "Einschränkung auf Flächengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.5.1", + "warnedFeatures": [], + "message": "Nur Flächenobjekte der Basisebene gehören zum Flächenschluss", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.5.2", + "warnedFeatures": [], + "message": "Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.6.1", + "warnedFeatures": [], + "message": "Einschränkung auf Liniengeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.1.7.1", + "warnedFeatures": [], + "message": "Einschränkung auf Punktgeometrie", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.1.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.2.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.3.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.4.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.5.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.5.2", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.5.3", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.6.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.7.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.7.2", + "warnedFeatures": [], + "message": "Flächenschlussobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.2.8.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.1.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.2.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.2.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.3.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.3.3.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.4.1.1", + "warnedFeatures": [], + "message": "Konsistenz der Attribute gebietsArt und sonstGebietsArt", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.4.1.2", + "warnedFeatures": [], + "message": "Überlagerungsobjekt bei flächenhaftem Raumbezug", + "erroredFeatures": [] + }, + { + "isValid": true, + "name": "6.5.1.1", + "warnedFeatures": [], + "message": "Konsistenz von typ und sonstTyp", + "erroredFeatures": [] + } + ] + }, + "geometrisch": { + "valid": false, + "warnings": [], + "errors": [ + "XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW)." + ] + } + }, + "bbox": { + "minY": 52.33234200586314, + "minX": 7.373668092967802, + "crs": "EPSG:4326", + "maxY": 52.33376312995529, + "maxX": 7.377600099759094 + }, + "name": "edfd613e-b85d-4ea6-9b97-bb33712b1ba6", + "version": "XPLAN_41", + "documentSummary": [ + { + "name": "Nr. 108 Holstener Weg", + "type": "BP_Plan" + } + ], + "status": "Die Validierung wurde ausgeführt." +} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.xml b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec4936d5d66f23cf8a5f795a4a0dbe1f947400aa --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report2.expected.xml @@ -0,0 +1 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?><validationReport><version>XPLAN_41</version><name>996c971b-d70d-439e-add8-3a17681022c8</name><documentSummary><name>Nr. 108 Holstener Weg</name><type>BP_Plan</type></documentSummary><bbox><minX>7.373668092967802</minX><minY>52.33234200586314</minY><maxX>7.377600099759094</maxX><maxY>52.33376312995529</maxY><crs>EPSG:4326</crs></bbox><date>2024-07-26T10:16:16.136+02:00</date><valid>false</valid><status>Die Validierung wurde ausgeführt.</status><rulesMetadata><version>1.1.9</version><source>https://gitlab.opencode.de/xleitstelle/xplanung/validierungsregeln/standard/-/tree/v1.1.9</source></rulesMetadata><validationResult><geometrisch><valid>false</valid><errors>XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW).</errors></geometrisch><semantisch><valid>true</valid><rules><isValid>true</isValid><message>Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType</message><name>2.1.2.1</name></rules><rules><isValid>true</isValid><message>Angabe eines Standard CRS</message><name>2.1.3.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussbedingung</message><name>2.2.1.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Text-Abschnitte</message><name>3.1.1.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Begründungs-Abschnitte</message><name>3.1.1.2</name></rules><rules><isValid>true</isValid><message>Relation auf Präsentationsobjekte</message><name>3.1.2.1</name></rules><rules><isValid>true</isValid><message>Relation auf Fachobjekte</message><name>3.1.2.2</name></rules><rules><isValid>true</isValid><message>Relation auf Basis-Rasterplan</message><name>3.1.2.3</name></rules><rules><isValid>true</isValid><message>Relationen auf Text-Abschnitte</message><name>3.1.3.1</name></rules><rules><isValid>true</isValid><message>Relationen auf Begründungs-Abschnitte</message><name>3.1.3.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>3.1.3.3</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Präsentationsobjekte</message><name>3.1.3.4</name></rules><rules><isValid>true</isValid><message>Spezifikation des Textinhalts</message><name>3.2.1.1</name></rules><rules><isValid>true</isValid><message>Spezifikation des Textinhalts</message><name>3.2.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der verschiedenen Höhenangaben</message><name>3.2.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist</message><name>3.2.3.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug</message><name>3.2.3.3</name></rules><rules><isValid>true</isValid><message>Verweis auf Dokumente.</message><name>3.2.4.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel).</message><name>3.2.5.1</name></rules><rules><isValid>true</isValid><message>Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten</message><name>3.3.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute art und index</message><name>3.3.1.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>3.3.1.3</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Punktgeometrie.</message><name>3.3.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Liniengeometrie.</message><name>3.3.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs auf Flächengeometrie.</message><name>3.3.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>4.1.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltBPlan</message><name>4.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>4.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>4.1.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchFlaeche</message><name>4.1.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchMassnahme</message><name>4.1.3.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme</message><name>4.1.3.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche</message><name>4.1.3.4</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>4.1.3.5</name></rules><rules><isValid>true</isValid><message>BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>4.1.3.6</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchABE</message><name>4.1.3.7</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>4.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>4.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>4.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>4.1.5.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekte gehören nie zum Flächenschluss.</message><name>4.1.6.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss</message><name>4.1.7.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>4.1.8.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>4.1.9.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ</message><name>4.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GF</message><name>4.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ und GF</message><name>4.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BMZ</message><name>4.2.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BM</message><name>4.2.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur BMZ und BM</message><name>4.2.6</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GRZ</message><name>4.2.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GR</message><name>4.2.8</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GRZ und GR</message><name>4.2.9</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zu Z</message><name>4.2.10</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zu ZU</message><name>4.2.11</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur Dachneigung</message><name>4.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute dachform und detaillierteDachform</message><name>4.3.2</name></rules><rules><isValid>true</isValid><message>Relation abweichungText</message><name>4.5.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung</message><name>4.5.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung</message><name>4.5.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute bauweise und abweichendeBauweise</message><name>4.5.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung</message><name>4.5.1.5</name></rules><rules><isValid>true</isValid><message>Relation baugrenze</message><name>4.5.2.1</name></rules><rules><isValid>true</isValid><message>Relation baulinie</message><name>4.5.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute typ und sonstTyp</message><name>4.5.10.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.5.13.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.5.13.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.5.13.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.5.14.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.5.14.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation eigentümer</message><name>4.5.14.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.5.14.4</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.5.15.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.6.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.7.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.7.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.7.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.7.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)</message><name>4.7.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.7.1.6</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.7.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.7.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.7.2.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.8.1.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.8.2.3</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>4.8.2.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.8.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.8.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.8.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.8.3.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi</message><name>4.8.3.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.8.3.6</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme und weitereMassnahme1</message><name>4.9.5.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1</message><name>4.9.5.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>4.9.5.3</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.9.5.4</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.9.6.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.10.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.11.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>4.11.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>4.11.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>4.11.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi</message><name>4.11.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.11.1.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>4.11.1.7</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation begrenzungslinie</message><name>4.12.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation begrenzungslinie</message><name>4.12.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>4.12.2.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.12.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.13.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>4.13.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.14.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>4.14.2.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute startWinkel und endWinkel</message><name>4.14.3.1</name></rules><rules><isValid>true</isValid><message>Kein flächenhafter Raumbezug</message><name>4.14.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>4.14.4.1</name></rules><rules><isValid>true</isValid><message>Notwendige Spezifikation einer Textlichen Darstellung</message><name>4.14.6.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Relation hoehenangabe</message><name>4.14.7.1</name></rules><rules><isValid>true</isValid><message>Kein flächenhafter Raumbezug</message><name>4.14.7.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>5.1.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute planArt und sonstPlanArt</message><name>5.1.1.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltFPlan</message><name>5.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>5.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>5.1.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchFlaeche</message><name>5.1.3.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation wirdAusgeglichenDurchSPE</message><name>5.1.3.2</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>5.1.3.3</name></rules><rules><isValid>true</isValid><message>FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>5.1.3.4</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>5.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>5.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>5.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>5.1.5.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekte gehören nie zum Flächenschluss</message><name>5.1.6.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss</message><name>5.1.7.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>5.1.8.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>5.1.9.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung</message><name>5.3.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung</message><name>5.3.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung</message><name>5.3.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Angaben zur GFZ</message><name>5.3.1.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.4.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.4.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.4.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.4.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)</message><name>5.4.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.4.1.6</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs</message><name>5.4.1.7</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.4.1.8</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.4.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung</message><name>5.4.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.4.2.3</name></rules><rules><isValid>true</isValid><message>Einschränkung des Raumbezugs</message><name>5.4.2.4</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.4.2.5</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.5.1.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.5.2.3</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.5.3.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.5.3.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.5.3.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.5.3.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)</message><name>5.5.3.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.5.3.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.5.3.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2</message><name>5.6.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>5.6.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2</message><name>5.6.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen</message><name>5.6.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.7.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.7.1.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen</message><name>5.7.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.7.1.4</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)</message><name>5.7.1.5</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.7.1.6</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.7.1.7</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.8.1.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung</message><name>5.8.1.2</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.8.1.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.9.1.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>5.9.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung</message><name>5.9.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.9.2.2</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.1.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.2.1</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen</message><name>5.10.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung</message><name>5.10.2.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)</message><name>5.10.2.4</name></rules><rules><isValid>true</isValid><message>Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen</message><name>5.10.3.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>5.10.4.1</name></rules><rules><isValid>true</isValid><message>Notwendige Spezifikation einer Textlichen Darstellung</message><name>5.10.6.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation bereich</message><name>6.1.1.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation inhaltSoPlan</message><name>6.1.2.1</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation rasterAenderung</message><name>6.1.2.2</name></rules><rules><isValid>true</isValid><message>Einschränkung der Relation gehoertZuPlan</message><name>6.1.2.3</name></rules><rules><isValid>true</isValid><message>Rückwärts-Referenzen auf Plan-Bereiche</message><name>6.1.3.1</name></rules><rules><isValid>true</isValid><message>SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.</message><name>6.1.3.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>6.1.4.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Flächengeometrie</message><name>6.1.4.2</name></rules><rules><isValid>true</isValid><message>Nur Flächenobjekte der Basisebene gehören zum Flächenschluss</message><name>6.1.5.1</name></rules><rules><isValid>true</isValid><message>Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug</message><name>6.1.5.2</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Liniengeometrie</message><name>6.1.6.1</name></rules><rules><isValid>true</isValid><message>Einschränkung auf Punktgeometrie</message><name>6.1.7.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.2.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.2.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.3.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.4.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung</message><name>6.2.5.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung</message><name>6.2.5.2</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>6.2.5.3</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.6.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.7.1</name></rules><rules><isValid>true</isValid><message>Flächenschlussobjekt bei flächenhaftem Raumbezug</message><name>6.2.7.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.2.8.1</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.2.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.2.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung</message><name>6.3.3.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.3.3.2</name></rules><rules><isValid>true</isValid><message>Konsistenz der Attribute gebietsArt und sonstGebietsArt</message><name>6.4.1.1</name></rules><rules><isValid>true</isValid><message>Überlagerungsobjekt bei flächenhaftem Raumbezug</message><name>6.4.1.2</name></rules><rules><isValid>true</isValid><message>Konsistenz von typ und sonstTyp</message><name>6.5.1.1</name></rules></semantisch><syntaktisch><valid>true</valid></syntaktisch></validationResult></validationReport> \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report6.expected.json b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report6.expected.json new file mode 100644 index 0000000000000000000000000000000000000000..847c3dbb47e6f1b435a2fe77c7317b9904488158 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report6.expected.json @@ -0,0 +1 @@ +{"documentSummary":[{"name":"Nr. 108 Holstener Weg","type":"BP_Plan"}],"version":"XPLAN_41","filename":null,"name":"8dc3163c-a361-49b8-9c53-3733f7d61274","bbox":{"minX":7.373668092967802,"minY":52.33234200586314,"maxX":7.377600099759094,"maxY":52.33376312995529,"crs":"EPSG:4326"},"date":"2024-07-26T10:16:30.191+0200","valid":false,"status":"Die Validierung wurde ausgeführt.","externalReferences":[],"externalReferencesResult":[],"rulesMetadata":{"version":"1.1.9","source":"https://gitlab.opencode.de/xleitstelle/xplanung/validierungsregeln/standard/-/tree/v1.1.9"},"validationResult":{"semantisch":{"valid":true,"rules":[{"name":"2.1.2.1","isValid":true,"message":"Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType","warnedFeatures":[],"erroredFeatures":[]},{"name":"2.1.3.1","isValid":true,"message":"Angabe eines Standard CRS","warnedFeatures":[],"erroredFeatures":[]},{"name":"2.2.1.1","isValid":true,"message":"Flächenschlussbedingung","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.1.1","isValid":true,"message":"Relationen auf Text-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.1.2","isValid":true,"message":"Relationen auf Begründungs-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.1","isValid":true,"message":"Relation auf Präsentationsobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.2","isValid":true,"message":"Relation auf Fachobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.3","isValid":true,"message":"Relation auf Basis-Rasterplan","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.1","isValid":true,"message":"Relationen auf Text-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.2","isValid":true,"message":"Relationen auf Begründungs-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.4","isValid":true,"message":"Rückwärts-Referenzen auf Präsentationsobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.1.1","isValid":true,"message":"Spezifikation des Textinhalts","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.2.1","isValid":true,"message":"Spezifikation des Textinhalts","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.1","isValid":true,"message":"Konsistenz der verschiedenen Höhenangaben","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.2","isValid":true,"message":"Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.3","isValid":true,"message":"Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.4.1","isValid":true,"message":"Verweis auf Dokumente.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.5.1","isValid":true,"message":"Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel).","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.1","isValid":true,"message":"Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.2","isValid":true,"message":"Konsistenz der Attribute art und index","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.2.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Punktgeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.3.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Liniengeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.4.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Flächengeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltBPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.1","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.2","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchMassnahme","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.3","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.4","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.5","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.6","isValid":true,"message":"BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.7","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchABE","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.6.1","isValid":true,"message":"Überlagerungsobjekte gehören nie zum Flächenschluss.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.7.1","isValid":true,"message":"Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.8.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.9.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.1","isValid":true,"message":"Konsistenz der Angaben zur GFZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.2","isValid":true,"message":"Konsistenz der Angaben zur GF","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.3","isValid":true,"message":"Konsistenz der Angaben zur GFZ und GF","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.4","isValid":true,"message":"Konsistenz der Angaben zur BMZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.5","isValid":true,"message":"Konsistenz der Angaben zur BM","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.6","isValid":true,"message":"Konsistenz der Angaben zur BMZ und BM","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.7","isValid":true,"message":"Konsistenz der Angaben zur GRZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.8","isValid":true,"message":"Konsistenz der Angaben zur GR","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.9","isValid":true,"message":"Konsistenz der Angaben zur GRZ und GR","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.10","isValid":true,"message":"Konsistenz der Angaben zu Z","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.11","isValid":true,"message":"Konsistenz der Angaben zu ZU","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.3.1","isValid":true,"message":"Konsistenz der Angaben zur Dachneigung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.3.2","isValid":true,"message":"Konsistenz der Attribute dachform und detaillierteDachform","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.1","isValid":true,"message":"Relation abweichungText","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.2","isValid":true,"message":"Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.3","isValid":true,"message":"Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.4","isValid":true,"message":"Konsistenz der Attribute bauweise und abweichendeBauweise","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.5","isValid":true,"message":"Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.2.1","isValid":true,"message":"Relation baugrenze","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.2.2","isValid":true,"message":"Relation baulinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.10.1","isValid":true,"message":"Konsistenz der Attribute typ und sonstTyp","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.3","isValid":true,"message":"Einschränkung der Relation eigentümer","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.4","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.15.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.6.3.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.4","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.4","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.6.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.10.2.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.1.1","isValid":true,"message":"Einschränkung der Relation begrenzungslinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.2.1","isValid":true,"message":"Einschränkung der Relation begrenzungslinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.2.2","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.3.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.13.1.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.13.2.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.2.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.3.1","isValid":true,"message":"Konsistenz der Attribute startWinkel und endWinkel","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.3.2","isValid":true,"message":"Kein flächenhafter Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.4.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.6.1","isValid":true,"message":"Notwendige Spezifikation einer Textlichen Darstellung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.7.1","isValid":true,"message":"Verwendung der Relation hoehenangabe","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.7.2","isValid":true,"message":"Kein flächenhafter Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.1.2","isValid":true,"message":"Konsistenz der Attribute planArt und sonstPlanArt","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltFPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.1","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.2","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPE","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.4","isValid":true,"message":"FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.6.1","isValid":true,"message":"Überlagerungsobjekte gehören nie zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.7.1","isValid":true,"message":"Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.8.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.9.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.1","isValid":true,"message":"Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.2","isValid":true,"message":"Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.3","isValid":true,"message":"Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.4","isValid":true,"message":"Konsistenz der Angaben zur GFZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.7","isValid":true,"message":"Einschränkung des Raumbezugs","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.8","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.4","isValid":true,"message":"Einschränkung des Raumbezugs","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.5","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.1.1","isValid":true,"message":"Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.2.1","isValid":true,"message":"Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.5","isValid":true,"message":"Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.1","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.2","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.3","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.1.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.1.2","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.2.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.3","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.4","isValid":true,"message":"Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.4.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.6.1","isValid":true,"message":"Notwendige Spezifikation einer Textlichen Darstellung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltSoPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.3.1","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.3.2","isValid":true,"message":"SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.6.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.7.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.1.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.2.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.3.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.4.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.2","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.3","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.6.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.7.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.7.2","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.8.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.1.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.2.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.3.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.3.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.4.1.1","isValid":true,"message":"Konsistenz der Attribute gebietsArt und sonstGebietsArt","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.4.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.5.1.1","isValid":true,"message":"Konsistenz von typ und sonstTyp","warnedFeatures":[],"erroredFeatures":[]}]},"geometrisch":{"valid":false,"errors":["XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW)."],"warnings":[]},"syntaktisch":{"valid":true,"messages":[]},"profile":[{"name":"test1","description":"description1","ergebnis":{"valid":false,"rules":[]}}]}} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report7.expected.json b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report7.expected.json new file mode 100644 index 0000000000000000000000000000000000000000..0e22c169b55d21988a33d8a19a77ba7f8def5a47 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/de/latlon/xplanbox/validator/executor/report7.expected.json @@ -0,0 +1 @@ +{"documentSummary":[{"name":"Nr. 108 Holstener Weg","type":"BP_Plan"}],"version":"XPLAN_41","filename":null,"name":"6463c72f-4a7d-40d0-a129-3de95fdd0eb8","bbox":{"minX":7.373668092967802,"minY":52.33234200586314,"maxX":7.377600099759094,"maxY":52.33376312995529,"crs":"EPSG:4326"},"date":"2024-07-26T10:16:25.697+0200","valid":false,"status":"Die Validierung wurde ausgeführt.","externalReferences":[],"externalReferencesResult":[],"rulesMetadata":{"version":"1.1.9","source":"https://gitlab.opencode.de/xleitstelle/xplanung/validierungsregeln/standard/-/tree/v1.1.9"},"validationResult":{"semantisch":{"valid":true,"rules":[{"name":"2.1.2.1","isValid":true,"message":"Verwendung vorgegebenen URNs für das uom-Attribut von GML-MeasureType","warnedFeatures":[],"erroredFeatures":[]},{"name":"2.1.3.1","isValid":true,"message":"Angabe eines Standard CRS","warnedFeatures":[],"erroredFeatures":[]},{"name":"2.2.1.1","isValid":true,"message":"Flächenschlussbedingung","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.1.1","isValid":true,"message":"Relationen auf Text-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.1.2","isValid":true,"message":"Relationen auf Begründungs-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.1","isValid":true,"message":"Relation auf Präsentationsobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.2","isValid":true,"message":"Relation auf Fachobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.2.3","isValid":true,"message":"Relation auf Basis-Rasterplan","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.1","isValid":true,"message":"Relationen auf Text-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.2","isValid":true,"message":"Relationen auf Begründungs-Abschnitte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.1.3.4","isValid":true,"message":"Rückwärts-Referenzen auf Präsentationsobjekte","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.1.1","isValid":true,"message":"Spezifikation des Textinhalts","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.2.1","isValid":true,"message":"Spezifikation des Textinhalts","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.1","isValid":true,"message":"Konsistenz der verschiedenen Höhenangaben","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.2","isValid":true,"message":"Verwendung von Höhenangaben, die sich auf eine auf Bezugshöhe beziehen, die auf Planebene definiert ist","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.3.3","isValid":true,"message":"Konsistenz der Attribute hoehenbezug und abweichenderHoehenbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.4.1","isValid":true,"message":"Verweis auf Dokumente.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.2.5.1","isValid":true,"message":"Konsistenz der Attribute ags (Amtlicher Gemeindeschlüssel) und rs (Regionalschlüssel).","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.1","isValid":true,"message":"Spezifikation des Fachobjekt-Attributs bei nicht-freien Präsentationsobjekten","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.2","isValid":true,"message":"Konsistenz der Attribute art und index","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.1.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.2.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Punktgeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.3.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Liniengeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"3.3.4.1","isValid":true,"message":"Einschränkung des Raumbezugs auf Flächengeometrie.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltBPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.1","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.2","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchMassnahme","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.3","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPEMassnahme","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.4","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPEFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.5","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.6","isValid":true,"message":"BPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.3.7","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchABE","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.6.1","isValid":true,"message":"Überlagerungsobjekte gehören nie zum Flächenschluss.","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.7.1","isValid":true,"message":"Flächenschlussobjekte auf Ebene 0 gehören immer zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.8.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.1.9.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.1","isValid":true,"message":"Konsistenz der Angaben zur GFZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.2","isValid":true,"message":"Konsistenz der Angaben zur GF","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.3","isValid":true,"message":"Konsistenz der Angaben zur GFZ und GF","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.4","isValid":true,"message":"Konsistenz der Angaben zur BMZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.5","isValid":true,"message":"Konsistenz der Angaben zur BM","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.6","isValid":true,"message":"Konsistenz der Angaben zur BMZ und BM","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.7","isValid":true,"message":"Konsistenz der Angaben zur GRZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.8","isValid":true,"message":"Konsistenz der Angaben zur GR","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.9","isValid":true,"message":"Konsistenz der Angaben zur GRZ und GR","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.10","isValid":true,"message":"Konsistenz der Angaben zu Z","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.2.11","isValid":true,"message":"Konsistenz der Angaben zu ZU","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.3.1","isValid":true,"message":"Konsistenz der Angaben zur Dachneigung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.3.2","isValid":true,"message":"Konsistenz der Attribute dachform und detaillierteDachform","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.1","isValid":true,"message":"Relation abweichungText","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.2","isValid":true,"message":"Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.3","isValid":true,"message":"Konsistenz der Attribute besondereArtDerBaulNutzung und sondernutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.4","isValid":true,"message":"Konsistenz der Attribute bauweise und abweichendeBauweise","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.1.5","isValid":true,"message":"Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sondernutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.2.1","isValid":true,"message":"Relation baugrenze","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.2.2","isValid":true,"message":"Relation baulinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.10.1","isValid":true,"message":"Konsistenz der Attribute typ und sonstTyp","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.13.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.3","isValid":true,"message":"Einschränkung der Relation eigentümer","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.14.4","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.5.15.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.6.3.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4)","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.7.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.1.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.2.4","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.8.3.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.2.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.1","isValid":true,"message":"Konsistenz der Attribute massnahme und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.2","isValid":true,"message":"Konsistenz der Attribute weitereMassnahme2und weitereMassnahme1","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.5.4","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.9.6.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.10.2.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.11.1.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.1.1","isValid":true,"message":"Einschränkung der Relation begrenzungslinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.2.1","isValid":true,"message":"Einschränkung der Relation begrenzungslinie","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.2.2","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.12.3.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.13.1.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.13.2.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.2.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.3.1","isValid":true,"message":"Konsistenz der Attribute startWinkel und endWinkel","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.3.2","isValid":true,"message":"Kein flächenhafter Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.4.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.6.1","isValid":true,"message":"Notwendige Spezifikation einer Textlichen Darstellung","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.7.1","isValid":true,"message":"Verwendung der Relation hoehenangabe","warnedFeatures":[],"erroredFeatures":[]},{"name":"4.14.7.2","isValid":true,"message":"Kein flächenhafter Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.1.2","isValid":true,"message":"Konsistenz der Attribute planArt und sonstPlanArt","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltFPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.1","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchFlaeche","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.2","isValid":true,"message":"Einschränkung der Relation wirdAusgeglichenDurchSPE","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.3","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.3.4","isValid":true,"message":"FPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.6.1","isValid":true,"message":"Überlagerungsobjekte gehören nie zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.7.1","isValid":true,"message":"Flächenschlussobjekte der Ebene 0 gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.8.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.1.9.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.1","isValid":true,"message":"Konsistenz der Attribute allgArtDerBaulNutzung und besondereArtDerBaulNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.2","isValid":true,"message":"Konsistenz der Attribute besondereArtDerBaulNutzung und sonderNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.3","isValid":true,"message":"Konsistenz der Attribute detaillierteArtDer BaulNutzung, allgArtDerBaulNutzung, besondereArtDerBaulNutzung und sonderNutzung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.3.1.4","isValid":true,"message":"Konsistenz der Angaben zur GFZ","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.7","isValid":true,"message":"Einschränkung des Raumbezugs","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.1.8","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation einer einzigen detaillierter Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.4","isValid":true,"message":"Einschränkung des Raumbezugs","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.4.2.5","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.1.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.2.3","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.5","isValid":true,"message":"Konsistenz der Attribute weitereBesondZweckbestimmungi und weitereZweckbestimmungi (i = 1, 2, 3, 4, 5)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.5.3.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.1.1","isValid":true,"message":"Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.2.1","isValid":true,"message":"Konsistenz der Attribute massnahme, weitereMassnahme1 und weitereMassnahme2","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.6.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Maßnahmen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.3","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer detaillierter Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.4","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.5","isValid":true,"message":"Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2, 3)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.6","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.7.1.7","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.1","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.2","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung bzw. besondere Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.8.1.3","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.1.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.1.2","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.2.1","isValid":true,"message":"Konsistenz der Attribute für detaillierte Zweckbestimmung und Zweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.9.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.1.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.2","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer besonderer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.3","isValid":true,"message":"Konsistenz der Attribute zweckbestimmung und besondereZweckbestimmung","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.2.4","isValid":true,"message":"Konsistenz der Attribute weitereZweckbestimmungi und weitereBesondZweckbestimmungi (i = 1, 2)","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.3.1","isValid":true,"message":"Verwendung der Attribute zur Spezifikation mehrerer Zweckbestimmungen","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.4.1","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"5.10.6.1","isValid":true,"message":"Notwendige Spezifikation einer Textlichen Darstellung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.1.1","isValid":true,"message":"Einschränkung der Relation bereich","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.1","isValid":true,"message":"Einschränkung der Relation inhaltSoPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.2","isValid":true,"message":"Einschränkung der Relation rasterAenderung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.2.3","isValid":true,"message":"Einschränkung der Relation gehoertZuPlan","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.3.1","isValid":true,"message":"Rückwärts-Referenzen auf Plan-Bereiche","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.3.2","isValid":true,"message":"SOPlan-Inhalte dürfen nicht gleichzeitig als originärer Planinhalt und nachrichtliche Übernahme in den Plan integriert werden.","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.4.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.4.2","isValid":true,"message":"Einschränkung auf Flächengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.5.1","isValid":true,"message":"Nur Flächenobjekte der Basisebene gehören zum Flächenschluss","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.5.2","isValid":true,"message":"Angabe des Attributs flaechenschluss bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.6.1","isValid":true,"message":"Einschränkung auf Liniengeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.1.7.1","isValid":true,"message":"Einschränkung auf Punktgeometrie","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.1.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.2.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.3.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.4.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und besondereArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.2","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung, besondereArtDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.5.3","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.6.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.7.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.7.2","isValid":true,"message":"Flächenschlussobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.2.8.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.1.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.2.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.2.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.3.1","isValid":true,"message":"Konsistenz der Attribute artDerFestlegung und detailArtDerFestlegung","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.3.3.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.4.1.1","isValid":true,"message":"Konsistenz der Attribute gebietsArt und sonstGebietsArt","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.4.1.2","isValid":true,"message":"Überlagerungsobjekt bei flächenhaftem Raumbezug","warnedFeatures":[],"erroredFeatures":[]},{"name":"6.5.1.1","isValid":true,"message":"Konsistenz von typ und sonstTyp","warnedFeatures":[],"erroredFeatures":[]}]},"geometrisch":{"valid":false,"errors":["XPlanAuszug (Zeile 1, Spalte 1): 2.2.2.1: äußerer Ring verwendet falsche Laufrichtung (CW)."],"warnings":[]},"syntaktisch":{"valid":true,"messages":[]},"profile":[{"name":"test1","description":"description1","ergebnis":{"valid":false,"rules":[]}}]}} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-executor/src/test/resources/xplan.gml b/xplan-validator/xplan-validator-executor/src/test/resources/xplan.gml new file mode 100644 index 0000000000000000000000000000000000000000..07d99bfb192a2f599ad75961058121ac7912d1d1 --- /dev/null +++ b/xplan-validator/xplan-validator-executor/src/test/resources/xplan.gml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- + #%L + xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + %% + Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + #L% + --> + +<xplan:XPlanAuszug xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xplan="http://www.xplanung.de/xplangml/4/1" + gml:id="GML_ad5adb8f-d05d-45d2-8197-29b26f4dd142"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <gml:featureMember> + <xplan:BP_Bereich gml:id="GML_b3cca7aa-8908-4182-bf9f-1d96e623a1c7"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <xplan:nummer>1683</xplan:nummer> + <xplan:name>610-531-01/37.1</xplan:name> + <xplan:gehoertZuPlan xlink:href="#GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3"/> + </xplan:BP_Bereich> + </gml:featureMember> + <gml:featureMember> + <xplan:BP_Plan gml:id="GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <xplan:name>Nr. 108 Holstener Weg</xplan:name> + <xplan:nummer>108</xplan:nummer> + <xplan:beschreibung>(§ 13 BauGB)</xplan:beschreibung> + <xplan:technHerstellDatum>2019-01-01</xplan:technHerstellDatum> + <xplan:genehmigungsDatum>2019-07-15</xplan:genehmigungsDatum> + <xplan:erstellungsMassstab>10000</xplan:erstellungsMassstab> + <xplan:xPlanGMLVersion>4.1</xplan:xPlanGMLVersion> + <xplan:raeumlicherGeltungsbereich> + <gml:Polygon srsName="EPSG:25832" gml:id="GML_8ddef72c-e5d0-4633-9514-fc27286aa5bd"> + <gml:exterior> + <gml:LinearRing> + <gml:posList srsDimension="2">389187.29 5799400.487 389192.226 5799400.102 389213.987 + 5799398.409 389228.148 5799397.301 389235.230180782 5799396.76051955 389267.224 + 5799394.319 389280.11 5799393.347 389313.709 5799390.721 389320.31 5799390.205 + 389322.384189298 5799390.04297999 389325.29 5799389.816 389335.943 5799388.983 + 389338.767 5799388.73 389359.381 5799386.879 389383.682 5799382.558 389399.504 + 5799378.591 389428.454 5799370.13900001 389431.729 5799369.183 389436.708 5799367.117 + 389442.462 5799364.731 389444.099 5799364.052 389444.497 5799363.887 389448.589 + 5799362.19 389450.365 5799361.454 389451.908 5799360.814 389435.775757406 + 5799350.39333417 389420.573 5799340.573 389415.206 5799337.114 389418.749 5799302.879 + 389419.677 5799293.894 389420.484 5799286.098 389422.311 5799268.414 389401.481518475 + 5799266.40521146 389396.274 5799265.903 389370.273 5799263.3 389356.361 5799262.039 + 389355.362 5799262.017 389341.745 5799262.042 389337.745 5799262.048 389323.108 + 5799262.075 389317.236 5799262.088 389312.912346494 5799261.28406564 389303.866 + 5799259.602 389281.835 5799255.493 389278.769 5799254.92 389271.16 5799253.497 + 389269.442 5799253.17600001 389256.587 5799250.788 389248.612 5799249.308 389247.613 + 5799249.119 389243.177 5799248.293 389187.29 5799400.487 + </gml:posList> + </gml:LinearRing> + </gml:exterior> + </gml:Polygon> + </xplan:raeumlicherGeltungsbereich> + <xplan:gemeinde> + <xplan:XP_Gemeinde> + <xplan:ags>03454045</xplan:ags> + <xplan:gemeindeName>Salzbergen</xplan:gemeindeName> + </xplan:XP_Gemeinde> + </xplan:gemeinde> + <xplan:planArt>9999</xplan:planArt> + <xplan:rechtsstand>4000</xplan:rechtsstand> + <xplan:auslegungsStartDatum>2019-04-01</xplan:auslegungsStartDatum> + <xplan:auslegungsEndDatum>2019-05-03</xplan:auslegungsEndDatum> + <xplan:satzungsbeschlussDatum>2019-08-25</xplan:satzungsbeschlussDatum> + <xplan:inkrafttretensDatum>2019-08-25</xplan:inkrafttretensDatum> + <xplan:bereich xlink:href="#GML_b3cca7aa-8908-4182-bf9f-1d96e623a1c7"/> + </xplan:BP_Plan> + </gml:featureMember> +</xplan:XPlanAuszug> diff --git a/xplan-validator/xplan-validator-storage/pom.xml b/xplan-validator/xplan-validator-storage/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..bd940607d27eecd6ceb6234a9c4b7b3e56f8aafe --- /dev/null +++ b/xplan-validator/xplan-validator-storage/pom.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <artifactId>xplan-validator-storage</artifactId> + + <parent> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-validator</artifactId> + <version>8.0-SNAPSHOT</version> + </parent> + + <dependencies> + <!-- xPlanBox --> + <dependency> + <groupId>de.latlon.product.xplanbox</groupId> + <artifactId>xplan-core-commons</artifactId> + </dependency> + <!-- spring --> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-beans</artifactId> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-yaml</artifactId> + </dependency> + <!-- aws --> + <dependency> + <groupId>com.amazonaws</groupId> + <artifactId>aws-java-sdk-s3</artifactId> + </dependency> + <!-- test --> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-test</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>io.findify</groupId> + <artifactId>s3mock_2.13</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>jakarta.annotation</groupId> + <artifactId>jakarta.annotation-api</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + +</project> \ No newline at end of file diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ErrorType.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ErrorType.java new file mode 100644 index 0000000000000000000000000000000000000000..cdc73c021cd418eefbcef4040f8c9a3882948b61 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ErrorType.java @@ -0,0 +1,34 @@ +package de.latlon.xplanbox.validator.storage; + +import java.util.Arrays; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public enum ErrorType { + + INVALID_REQUEST(400), + + INVALID_CONTENT(422), + + INTERNAL_ERROR(500); + + private final int statusCode; + + ErrorType(int statusCode) { + this.statusCode = statusCode; + } + + public int getStatusCode() { + return statusCode; + } + + public static ErrorType fromStatusCode(int statusCode) { + return Arrays.stream(values()) + .filter(errorType -> errorType.statusCode == statusCode) + .findFirst() + .orElse(INTERNAL_ERROR); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/Status.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/Status.java new file mode 100644 index 0000000000000000000000000000000000000000..83fd1fcc453a75c8ce7a21ebb9d21fec960e6d25 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/Status.java @@ -0,0 +1,77 @@ +package de.latlon.xplanbox.validator.storage; + +import static de.latlon.xplanbox.validator.storage.StatusType.REQUESTED; + +import java.util.Date; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class Status { + + private StatusType status; + + private String errorMsg; + + private ErrorType errorType; + + private String linkToJsonReport; + + private String linkToPdfReport; + + private Date expirationTime; + + public Status() { + this.status = REQUESTED; + } + + public StatusType getStatus() { + return status; + } + + public void setStatus(StatusType status) { + this.status = status; + } + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public ErrorType getErrorType() { + return errorType; + } + + public void setErrorType(ErrorType errorType) { + this.errorType = errorType; + } + + public String getLinkToJsonReport() { + return linkToJsonReport; + } + + public void setLinkToJsonReport(String linkToJsonReport) { + this.linkToJsonReport = linkToJsonReport; + } + + public void setReportLinkExpirationTime(Date expirationTime) { + this.expirationTime = expirationTime; + } + + public Date getExpirationTime() { + return expirationTime; + } + + public void setReportLinkPdf(String linkToPdfReport) { + this.linkToPdfReport = linkToPdfReport; + } + + public String getLinkToPdfReport() { + return linkToPdfReport; + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StatusType.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StatusType.java new file mode 100644 index 0000000000000000000000000000000000000000..6f54a92f5acac57cceda6359af77e092907df0a9 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StatusType.java @@ -0,0 +1,21 @@ +package de.latlon.xplanbox.validator.storage; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public enum StatusType { + + // Validation requested + REQUESTED, + + // Validation started + STARTED, + + // Validation failed + FAILED, + + // Validation finished + FINISHED + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StoredValidationReport.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StoredValidationReport.java new file mode 100644 index 0000000000000000000000000000000000000000..866e4b58c93f0fb5e0d7078c45ebb09163a5010b --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/StoredValidationReport.java @@ -0,0 +1,46 @@ +package de.latlon.xplanbox.validator.storage; + +import java.util.Date; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class StoredValidationReport { + + private final String linkToJsonReport; + + private final String linkToPdfReport; + + private final Date expirationTime; + + public StoredValidationReport(String linkToJsonReport, String linkToPdfReport) { + this(linkToJsonReport, linkToPdfReport, null); + } + + public StoredValidationReport(String linkToJsonReport, String linkToPdfReport, Date expirationTime) { + this.linkToJsonReport = linkToJsonReport; + this.linkToPdfReport = linkToPdfReport; + this.expirationTime = expirationTime; + } + + /** + * @return the url the file can be accessed, never <code>null</code> + */ + public String getLinkToJsonReport() { + return linkToJsonReport; + } + + public String getLinkToPdfReport() { + return linkToPdfReport; + } + + /** + * @return the date the objet expires, may be <code>null</code> if the file does not + * expire + */ + public Date getExpirationTime() { + return expirationTime; + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationDetails.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationDetails.java new file mode 100644 index 0000000000000000000000000000000000000000..467de70a37d6f3df5c96f35a5222fe67deba666c --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationDetails.java @@ -0,0 +1,29 @@ +package de.latlon.xplanbox.validator.storage; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class ValidationDetails { + + @JsonProperty("attachments") + private List<String> attachments = new ArrayList<>(); + + public List<String> getAttachments() { + return attachments; + } + + public void setAttachments(List<String> attachments) { + this.attachments = attachments; + } + + public void addAttachment(String attachments) { + getAttachments().add(attachments); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionException.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionException.java new file mode 100644 index 0000000000000000000000000000000000000000..43d1560087bb3bf58f649e401528e50f1a688652 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionException.java @@ -0,0 +1,20 @@ +package de.latlon.xplanbox.validator.storage; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class ValidationExecutionException extends Exception { + + private final ErrorType errorType; + + public ValidationExecutionException(String msg, ErrorType errorType) { + super(msg); + this.errorType = errorType; + } + + public ErrorType getErrorType() { + return errorType; + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionStorage.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..3d771f09af6ee5518d63f6feddab1f79a5b9c891 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/ValidationExecutionStorage.java @@ -0,0 +1,238 @@ +package de.latlon.xplanbox.validator.storage; + +import static de.latlon.xplanbox.validator.storage.StatusType.FAILED; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.latlon.xplan.commons.archive.XPlanArchiveCreator; +import de.latlon.xplan.commons.s3.StorageException; +import org.apache.tika.Tika; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public abstract class ValidationExecutionStorage { + + public static final String APPLICATION_YAML = "application/yaml"; + + public enum FileType { + + PLAN(""), DETAILS("_details.yaml"), STATUS("_status.yaml"); + + private final String suffix; + + FileType(String suffix) { + this.suffix = suffix; + } + + public String getSuffix() { + return suffix; + } + + } + + public enum ReportType { + + JSON(".json"), PDF(".pdf"), ZIP(".zip"); + + private final String fileExtension; + + ReportType(String fileExtension) { + this.fileExtension = fileExtension; + } + + public static ReportType byFileExtension(String fileExtension) { + Optional<ReportType> reportType = Arrays.stream(values()) + .filter(rt -> rt.fileExtension.equals(fileExtension)) + .findFirst(); + return reportType.orElseThrow( + () -> new IllegalArgumentException("Could not find ReportType with fileExtension" + fileExtension)); + } + + public String getFileExtension() { + return fileExtension; + } + + } + + static public class Key { + + private String fileName; + + Key(String uuid, FileType fileType) { + this.fileName = uuid + fileType.getSuffix(); + } + + public Key(String uuid, ReportType reportType) { + this.fileName = uuid + "_report" + reportType.getFileExtension(); + } + + public String toFileName() { + return fileName; + } + + public static Key details(String uuid) { + return new Key(uuid, FileType.DETAILS); + } + + public static Key status(String uuid) { + return new Key(uuid, FileType.STATUS); + } + + public static Key plan(String uuid) { + return new Key(uuid, FileType.PLAN); + } + + public static Key report(String uuid, ReportType reportType) { + return new Key(uuid, reportType); + } + + } + + public String addPlanToValidate(Path plan) throws IOException, StorageException { + String uuid = UUID.randomUUID().toString(); + ValidationDetails validationDetails = createValidationDetails(plan); + byte[] serializedValidationDetails = serialize(validationDetails); + addToStore(Key.details(uuid), APPLICATION_YAML, serializedValidationDetails); + addToStore(Key.status(uuid), APPLICATION_YAML, serialize(new Status())); + addToStore(Key.plan(uuid), plan); + return uuid; + } + + public void cleanupAfterValidation(String uuid) throws StorageException { + removeFromStore(Key.details(uuid)); + removeFromStore(Key.plan(uuid)); + } + + public Status retrieveStatus(String uuid) throws StorageException { + byte[] content = retrieveContent(Key.status(uuid)); + return deserialize(content); + } + + public void changeStatus(String uuid, StatusType statusType) throws StorageException { + changeStatus(uuid, statusType, null); + } + + public void changeStatus(String uuid, String errorMsg, ErrorType errorType) throws StorageException { + Status status = retrieveStatus(uuid); + status.setStatus(FAILED); + status.setErrorMsg(errorMsg); + status.setErrorType(errorType); + addToStore(Key.status(uuid), APPLICATION_YAML, serialize(status)); + } + + public void changeStatus(String uuid, StatusType statusType, StoredValidationReport storedValidationReport) + throws StorageException { + Status status = retrieveStatus(uuid); + status.setStatus(statusType); + if (storedValidationReport != null) { + status.setLinkToJsonReport(storedValidationReport.getLinkToJsonReport()); + status.setReportLinkPdf(storedValidationReport.getLinkToPdfReport()); + status.setReportLinkExpirationTime(storedValidationReport.getExpirationTime()); + } + addToStore(Key.status(uuid), APPLICATION_YAML, serialize(status)); + } + + public byte[] retrieveReport(String uuid, ReportType reportType) + throws StorageException, ValidationExecutionException { + Status status = retrieveStatus(uuid); + if (status.getStatus() == FAILED) + throw new ValidationExecutionException(status.getErrorMsg(), status.getErrorType()); + return retrieveContent(Key.report(uuid, reportType)); + } + + /** + * @param key of the file to return, never <code>null</code> + * @return the content of the file, never <code>null</code> + * @throws StorageException if an error occured retrieving the file or the file does + * not exist + */ + protected abstract byte[] retrieveContent(Key key) throws StorageException; + + protected abstract void addToStore(Key key, String contentType, byte[] file) throws StorageException; + + protected abstract void addToStore(Key key, Path file) throws IOException, StorageException; + + protected abstract void removeFromStore(Key key) throws StorageException; + + /** + * ugly name to improve + * @throws IOException + */ + public abstract void writePlanToValidate(String uuid, Path toPath) throws IOException, StorageException; + + /** + * ugly name to improve + * @return + * @throws IOException + */ + public abstract StoredValidationReport saveValidationResult(String uuid, Map<ReportType, Path> reports) + throws IOException, StorageException; + + private ValidationDetails createValidationDetails(Path plan) throws IOException { + ValidationDetails validationDetails = new ValidationDetails(); + if (isZipFile(plan)) { + try (ZipFile zipFile = new ZipFile(plan.toFile())) { + Enumeration<? extends ZipEntry> entries = zipFile.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + validationDetails.addAttachment(entry.getName()); + } + } + } + else { + validationDetails.addAttachment(XPlanArchiveCreator.MAIN_FILE); + } + return validationDetails; + } + + private boolean isZipFile(Path path) throws IOException { + String contentType = new Tika().detect(path); + if ("application/zip".equals(contentType)) + return true; + return false; + } + + private byte[] serialize(ValidationDetails validationDetails) throws StorageException { + try { + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.writeValueAsBytes(validationDetails); + } + catch (JsonProcessingException e) { + throw new StorageException("Could not serialize ValidationDetails " + validationDetails, e); + } + } + + private byte[] serialize(Status statusNotification) throws StorageException { + try { + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.writeValueAsBytes(statusNotification); + } + catch (JsonProcessingException e) { + throw new StorageException("Could not serialize Status " + statusNotification, e); + } + } + + private Status deserialize(byte[] content) throws StorageException { + try { + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.readValue(content, Status.class); + } + catch (IOException e) { + throw new StorageException("Could not deserialize Status", e); + } + } + +} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/config/AmazonS3Context.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/config/AmazonS3Context.java new file mode 100644 index 0000000000000000000000000000000000000000..e8880b26da90b5d075edb1126c235b2578afec8f --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/config/AmazonS3Context.java @@ -0,0 +1,92 @@ +/*- + * #%L + * xplan-core-manager - XPlan Manager Core Komponente + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.storage.config; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.s3.S3PlanValidationExecutionStorage; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; + +/** + * Spring configuration for using AWS S3 as a storage. + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> + * @since 7.0 + */ +@Configuration +@Profile("s3execution") +public class AmazonS3Context { + + @Bean(destroyMethod = "shutdown") + public AmazonS3 s3Client(AWSCredentials credentials, @Value("${xplanbox.s3.region}") String signingRegion, + @Value("${xplanbox.s3.endpoint.url}") String endpointUrl) { + AmazonS3 client; + // TODO refactoring if/else to @ConditionalOnExpression with SpringBoot into 2 + // SpringBeans + if (endpointUrl == null || endpointUrl.isEmpty()) { + client = AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withRegion(signingRegion) + .build(); + } + else { + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(endpointUrl, + signingRegion); + client = AmazonS3ClientBuilder.standard() + .withCredentials(new AWSStaticCredentialsProvider(credentials)) + .withEndpointConfiguration(endpoint) + .build(); + } + return client; + } + + @Bean + public AWSCredentials credentials(@Value("${xplanbox.s3.accessKeyId}") String accessKeyId, + @Value("${xplanbox.s3.secretKey}") String secretKey) { + return new BasicAWSCredentials(accessKeyId, secretKey); + } + + @Bean + public ValidationExecutionStorage validationExecutionStorage(S3Storage s3Storage, + @Value("${xplanbox.validation.s3.bucketPublicUrl}") String s3PublicUrl) { + return new S3PlanValidationExecutionStorage(s3Storage, s3PublicUrl); + } + + @Bean + public S3Storage s3Storage(AmazonS3 s3Client, @Value("${xplanbox.validation.s3.bucketName}") String bucketName) + throws StorageException { + S3Storage s3Storage = new S3Storage(s3Client, bucketName); + s3Storage.setBucketExpirationDate("deleteAfter30d", 30); + return s3Storage; + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorage.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..54517fa6776c80086dae81c23f8ef22188dfbc46 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorage.java @@ -0,0 +1,89 @@ +package de.latlon.xplanbox.validator.storage.filesystem; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; + +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.StoredValidationReport; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class FileSystemValidationExecutionStorage extends ValidationExecutionStorage { + + private final Path storageDir; + + public FileSystemValidationExecutionStorage(Optional<Path> storageDir) throws IOException { + if (!storageDir.isPresent()) + this.storageDir = Files.createTempDirectory("FileSystemPlanStorage"); + else + this.storageDir = storageDir.get(); + if (!Files.exists(this.storageDir)) + Files.createDirectories(this.storageDir); + } + + @Override + protected byte[] retrieveContent(Key key) throws StorageException { + try { + Path source = storageDir.resolve(key.toFileName()); + if (!Files.exists(source)) + throw new StorageException("File " + source + " does not exist.", 404); + return Files.readAllBytes(source); + } + catch (IOException e) { + throw new StorageException("Could not find file " + key.toFileName() + " from " + storageDir, e); + } + } + + @Override + protected void addToStore(Key key, String contentType, byte[] content) throws StorageException { + Path target = storageDir.resolve(key.toFileName()); + try { + Files.write(target, content); + } + catch (IOException e) { + throw new StorageException("Could not write file " + key.toFileName() + " to " + storageDir, e); + } + } + + @Override + protected void addToStore(Key key, Path file) throws IOException { + Path target = storageDir.resolve(key.toFileName()); + Files.copy(file, target); + } + + @Override + protected void removeFromStore(Key key) throws StorageException { + try { + Path fileToDelete = storageDir.resolve(key.toFileName()); + Files.delete(fileToDelete); + } + catch (IOException e) { + throw new StorageException("Could not delete file " + key.toFileName(), e); + } + } + + @Override + public void writePlanToValidate(String uuid, Path toPath) throws IOException { + Path origin = storageDir.resolve(Key.plan(uuid).toFileName()); + Files.copy(origin, toPath); + } + + @Override + public StoredValidationReport saveValidationResult(String uuid, Map<ReportType, Path> reports) throws IOException { + + for (Entry<ReportType, Path> entry : reports.entrySet()) { + Key key = Key.report(uuid, entry.getKey()); + addToStore(key, entry.getValue()); + } + return new StoredValidationReport(Key.report(uuid, ReportType.JSON).toFileName(), + Key.report(uuid, ReportType.PDF).toFileName()); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/s3/S3PlanValidationExecutionStorage.java b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/s3/S3PlanValidationExecutionStorage.java new file mode 100644 index 0000000000000000000000000000000000000000..897e2458e14efbbbb5d1ffd881c57e99030c3ce5 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/main/java/de/latlon/xplanbox/validator/storage/s3/S3PlanValidationExecutionStorage.java @@ -0,0 +1,84 @@ +package de.latlon.xplanbox.validator.storage.s3; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Map.Entry; + +import com.amazonaws.services.s3.model.PutObjectResult; +import com.amazonaws.services.s3.model.S3ObjectSummary; +import de.latlon.xplan.commons.s3.S3Metadata; +import de.latlon.xplan.commons.s3.S3Object; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.StoredValidationReport; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class S3PlanValidationExecutionStorage extends ValidationExecutionStorage { + + private final S3Storage s3Storage; + + private final String s3PublicUrl; + + public S3PlanValidationExecutionStorage(S3Storage s3Storage, String s3PublicUrl) { + this.s3Storage = s3Storage; + this.s3PublicUrl = s3PublicUrl; + } + + @Override + protected byte[] retrieveContent(Key key) throws StorageException { + S3Object object = s3Storage.getObject(key.toFileName()); + return object.getContent(); + } + + @Override + protected void addToStore(Key key, String contentType, byte[] content) throws StorageException { + S3Metadata s3Metadata = new S3Metadata(key.toFileName(), contentType, content.length); + S3Object s3Object = new S3Object(s3Metadata, content); + s3Storage.insertObject(s3Object); + } + + @Override + protected void addToStore(Key key, Path file) throws StorageException { + s3Storage.insertObject(key.toFileName(), file); + } + + @Override + protected void removeFromStore(Key key) { + S3ObjectSummary object = new S3ObjectSummary(); + object.setKey(key.toFileName()); + s3Storage.deleteObject(object); + } + + @Override + public void writePlanToValidate(String uuid, Path toPath) throws StorageException { + Key key = Key.plan(uuid); + S3Object object = s3Storage.getObject(key.toFileName()); + try { + Files.copy(new ByteArrayInputStream(object.getContent()), toPath); + } + catch (IOException e) { + throw new StorageException("Could not write plan to file" + toPath, e); + } + } + + @Override + public StoredValidationReport saveValidationResult(String uuid, Map<ReportType, Path> reports) + throws StorageException { + PutObjectResult lastPutObjectResult = null; + for (Entry<ReportType, Path> entry : reports.entrySet()) { + String fileName = Key.report(uuid, entry.getKey()).toFileName(); + lastPutObjectResult = s3Storage.insertObject(fileName, entry.getValue()); + } + String reportLinkToJson = s3PublicUrl + "/" + Key.report(uuid, ReportType.JSON).toFileName(); + String reportLinkToPdf = s3PublicUrl + "/" + Key.report(uuid, ReportType.PDF).toFileName(); + return new StoredValidationReport(reportLinkToJson, reportLinkToPdf, lastPutObjectResult.getExpirationTime()); + } + +} \ No newline at end of file diff --git a/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorageStorageTest.java b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorageStorageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..44c27343f12cf838bb9aa20f0f679c21d8ecd447 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/filesystem/FileSystemValidationExecutionStorageStorageTest.java @@ -0,0 +1,75 @@ +package de.latlon.xplanbox.validator.storage.filesystem; + +import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.FileType.DETAILS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.ValidationDetails; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +public class FileSystemValidationExecutionStorageStorageTest { + + @TempDir + static Path tempFolder; + + @Test + void testAddPlan_gml() throws IOException, URISyntaxException, StorageException { + Path targetFolder = tempFolder.resolve("testGml"); + Files.createDirectory(targetFolder); + FileSystemValidationExecutionStorage fileSystemPlanStorage = new FileSystemValidationExecutionStorage( + Optional.of(targetFolder)); + URL xplanGml = getClass().getResource("/xplan.gml"); + String key = fileSystemPlanStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + + assertThat(Files.exists(targetFolder.resolve(key))); + assertThat(Files.exists(targetFolder.resolve(key + DETAILS.getSuffix()))); + + ValidationDetails validationDetails = deserializeDetails(targetFolder, key); + assertThat(validationDetails.getAttachments()).containsExactlyInAnyOrder("xplan.gml"); + } + + @Test + void testAddPlan_zip() throws IOException, URISyntaxException, StorageException { + Path targetFolder = tempFolder.resolve("testZip"); + Files.createDirectory(targetFolder); + FileSystemValidationExecutionStorage fileSystemPlanStorage = new FileSystemValidationExecutionStorage( + Optional.of(targetFolder)); + URL xplanGml = getClass().getResource("/BPlan002_5-3.zip"); + String key = fileSystemPlanStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + + assertTrue(Files.exists(targetFolder.resolve(key))); + assertTrue(Files.exists(targetFolder.resolve(key + DETAILS.getSuffix()))); + + ValidationDetails validationDetails = deserializeDetails(targetFolder, key); + assertThat(validationDetails.getAttachments()).containsExactlyInAnyOrder("BPlan002_5-3.pgw", "BPlan002_5-3.png", + "xplan.gml"); + } + + private ValidationDetails deserializeDetails(Path targetFolder, String key) throws IOException { + Path details = targetFolder.resolve(key + DETAILS.getSuffix()); + byte[] detailsContent = Files.readAllBytes(details); + return deserialize(detailsContent); + } + + private ValidationDetails deserialize(byte[] detailsContent) throws IOException { + ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + return mapper.readValue(detailsContent, ValidationDetails.class); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/AmazonS3TestContext.java b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/AmazonS3TestContext.java new file mode 100644 index 0000000000000000000000000000000000000000..5e224f92b524577fd5b3dc5d3c1b1f61ea033180 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/AmazonS3TestContext.java @@ -0,0 +1,88 @@ +/*- + * #%L + * xplan-core-manager - XPlan Manager Core Komponente + * %% + * Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * #L% + */ +package de.latlon.xplanbox.validator.storage.s3; + +import com.amazonaws.auth.AWSStaticCredentialsProvider; +import com.amazonaws.auth.AnonymousAWSCredentials; +import com.amazonaws.client.builder.AwsClientBuilder; +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import io.findify.s3mock.S3Mock; +import jakarta.annotation.PreDestroy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Profile; +import org.springframework.test.util.TestSocketUtils; + +/** + * Spring Configuration to enable usage of mock objects for integration tests. + * + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @author <a href="mailto:friebe@lat-lon.de">Torsten Friebe</a> + */ +@Configuration +public class AmazonS3TestContext { + + @Autowired(required = false) + private S3Mock s3Mock; + + @Autowired + private AmazonS3 s3TestClient; + + private final int port = TestSocketUtils.findAvailableTcpPort(); + + @Bean + @Profile("mock") + public S3Mock s3Mock() { + S3Mock s3Mock = new S3Mock.Builder().withPort(port).withInMemoryBackend().build(); + s3Mock.start(); + return s3Mock; + } + + @Bean + @Primary + @Profile("mock") + public AmazonS3 s3TestClient(@Value("${xplanbox.s3.region}") String signingRegion, + @Value("${xplanbox.validation.s3.bucketName}") String bucketName, + @Value("${xplanbox.s3.endpoint.url}") String url) { + AwsClientBuilder.EndpointConfiguration endpoint = new AwsClientBuilder.EndpointConfiguration(url + ":" + port, + signingRegion); + AmazonS3 client = AmazonS3ClientBuilder.standard() + .withPathStyleAccessEnabled(true) + .withEndpointConfiguration(endpoint) + .withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())) + .build(); + client.createBucket(bucketName); + return client; + } + + @PreDestroy + public void shutdown() { + s3TestClient.shutdown(); + if (s3Mock != null) { + s3Mock.stop(); + } + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageIT.java b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageIT.java new file mode 100644 index 0000000000000000000000000000000000000000..d9ea4d9c86d5b695c6d3c8ba1a39a875341c6284 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageIT.java @@ -0,0 +1,87 @@ +package de.latlon.xplanbox.validator.storage.s3; + +import static de.latlon.xplanbox.validator.storage.ErrorType.INTERNAL_ERROR; +import static de.latlon.xplanbox.validator.storage.StatusType.FINISHED; +import static de.latlon.xplanbox.validator.storage.StatusType.REQUESTED; +import static de.latlon.xplanbox.validator.storage.StatusType.STARTED; +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; + +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.ErrorType; +import de.latlon.xplanbox.validator.storage.Status; +import de.latlon.xplanbox.validator.storage.StoredValidationReport; +import de.latlon.xplanbox.validator.storage.ValidationExecutionException; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage; +import de.latlon.xplanbox.validator.storage.config.AmazonS3Context; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { AmazonS3Context.class, AmazonS3TestContext.class }) +@ActiveProfiles({ "s3execution", "mock" }) +@TestPropertySource("classpath:s3Mock.properties") +public class S3ValidationExecutionStorageIT { + + @Autowired + private S3PlanValidationExecutionStorage validationExecutionStorage; + + @Test + void testStatus() throws StorageException, URISyntaxException, IOException { + URL xplanGml = getClass().getResource("/xplan.gml"); + + String uuid = validationExecutionStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + Status statusRequested = validationExecutionStorage.retrieveStatus(uuid); + assertThat(statusRequested.getStatus()).isEqualTo(REQUESTED); + + validationExecutionStorage.changeStatus(uuid, STARTED); + Status statusStarted = validationExecutionStorage.retrieveStatus(uuid); + assertThat(statusStarted.getStatus()).isEqualTo(STARTED); + + StoredValidationReport validationReportDetails = new StoredValidationReport("http://test.org/test.json", + "http://test.org/test.pdf"); + validationExecutionStorage.changeStatus(uuid, FINISHED, validationReportDetails); + Status statusFinished = validationExecutionStorage.retrieveStatus(uuid); + assertThat(statusFinished.getStatus()).isEqualTo(FINISHED); + assertThat(statusFinished.getLinkToJsonReport()).isEqualTo(validationReportDetails.getLinkToJsonReport()); + assertThat(statusFinished.getLinkToPdfReport()).isEqualTo(validationReportDetails.getLinkToPdfReport()); + assertThat(statusFinished.getExpirationTime()).isEqualTo(validationReportDetails.getExpirationTime()); + } + + @Test + void testAddAndCleanup() throws StorageException, URISyntaxException, IOException { + URL xplanGml = getClass().getResource("/xplan.gml"); + + String uuid = validationExecutionStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + validationExecutionStorage.cleanupAfterValidation(uuid); + } + + @Test + void testChangeStatus_toError() throws URISyntaxException, StorageException, IOException { + URL xplanGml = getClass().getResource("/xplan.gml"); + String uuid = validationExecutionStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + + String errorMsg = "Error test"; + validationExecutionStorage.changeStatus(uuid, errorMsg, INTERNAL_ERROR); + + ValidationExecutionException exception = Assertions.assertThrows(ValidationExecutionException.class, + () -> validationExecutionStorage.retrieveReport(uuid, ValidationExecutionStorage.ReportType.JSON)); + assertThat(exception.getErrorType()).isEqualTo(ErrorType.fromStatusCode(INTERNAL_ERROR.getStatusCode())); + assertThat(exception.getMessage()).isEqualTo(errorMsg); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageTest.java b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f56ca231d4f7b6b63ebb163c1fe321641a9ee700 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/java/de/latlon/xplanbox/validator/storage/s3/S3ValidationExecutionStorageTest.java @@ -0,0 +1,111 @@ +package de.latlon.xplanbox.validator.storage.s3; + +import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.FileType.DETAILS; +import static de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.FileType.STATUS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import com.amazonaws.services.s3.AmazonS3; +import com.amazonaws.services.s3.model.ObjectMetadata; +import com.amazonaws.services.s3.model.PutObjectResult; +import de.latlon.xplan.commons.s3.S3Storage; +import de.latlon.xplan.commons.s3.StorageException; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.Key; +import de.latlon.xplanbox.validator.storage.ValidationExecutionStorage.ReportType; + +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; + +/** + * @author <a href="mailto:goltz@lat-lon.de">Lyn Goltz </a> + * @since 8.0 + */ +class S3ValidationExecutionStorageTest { + + private static final String BUCKET_NAME = "xplanbox"; + + private static final String S3_PUBLIC_URL = "http://xplanbox.s3.eu-central-1.amazonaws.com"; + + @Test + void testAddPlanToValidate_gml() throws IOException, URISyntaxException, StorageException { + AmazonS3 client = spy(AmazonS3.class); + S3Storage s3Storage = new S3Storage(client, BUCKET_NAME); + S3PlanValidationExecutionStorage validationExecutionStorage = new S3PlanValidationExecutionStorage(s3Storage, + S3_PUBLIC_URL); + URL xplanGml = getClass().getResource("/xplan.gml"); + String key = validationExecutionStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + + ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class); + + verify(client, times(3)).doesBucketExistV2(eq(BUCKET_NAME)); + verify(client, times(2)).putObject(eq(BUCKET_NAME), keyCaptor.capture(), any(InputStream.class), + any(ObjectMetadata.class)); + verify(client).putObject(eq(BUCKET_NAME), keyCaptor.capture(), any(File.class)); + + assertThat(keyCaptor.getAllValues()).containsExactlyInAnyOrder(key, key + DETAILS.getSuffix(), + key + STATUS.getSuffix()); + } + + @Test + void testAddPlanToValidate_zip() throws IOException, URISyntaxException, StorageException { + AmazonS3 client = spy(AmazonS3.class); + S3Storage s3Storage = new S3Storage(client, BUCKET_NAME); + S3PlanValidationExecutionStorage validationExecutionStorage = new S3PlanValidationExecutionStorage(s3Storage, + S3_PUBLIC_URL); + URL xplanGml = getClass().getResource("/BPlan002_5-3.zip"); + String key = validationExecutionStorage.addPlanToValidate(Paths.get(xplanGml.toURI())); + + ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class); + + verify(client, times(3)).doesBucketExistV2(eq(BUCKET_NAME)); + verify(client, times(2)).putObject(eq(BUCKET_NAME), keyCaptor.capture(), any(InputStream.class), + any(ObjectMetadata.class)); + verify(client).putObject(eq(BUCKET_NAME), keyCaptor.capture(), any(File.class)); + + assertThat(keyCaptor.getAllValues()).containsExactlyInAnyOrder(key, key + DETAILS.getSuffix(), + key + STATUS.getSuffix()); + } + + @Test + void testSaveValidationResult() throws URISyntaxException, StorageException { + AmazonS3 client = spy(AmazonS3.class); + when(client.putObject(anyString(), anyString(), any(File.class))).thenReturn(mock(PutObjectResult.class)); + S3Storage s3Storage = new S3Storage(client, BUCKET_NAME); + S3PlanValidationExecutionStorage validationExecutionStorage = new S3PlanValidationExecutionStorage(s3Storage, + S3_PUBLIC_URL); + URL xplanGml = getClass().getResource("/BPlan002_5-3.zip"); + String uuid = UUID.randomUUID().toString(); + + Map<ReportType, Path> reports = new HashMap<>(); + reports.put(ReportType.JSON, Paths.get(xplanGml.toURI())); + reports.put(ReportType.PDF, Paths.get(xplanGml.toURI())); + validationExecutionStorage.saveValidationResult(uuid, reports); + + ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class); + + verify(client, times(2)).doesBucketExistV2(eq(BUCKET_NAME)); + verify(client, times(2)).putObject(eq(BUCKET_NAME), keyCaptor.capture(), any(File.class)); + + assertThat(keyCaptor.getAllValues()).containsExactlyInAnyOrder(Key.report(uuid, ReportType.PDF).toFileName(), + Key.report(uuid, ReportType.JSON).toFileName()); + } + +} diff --git a/xplan-validator/xplan-validator-storage/src/test/resources/BPlan002_5-3.zip b/xplan-validator/xplan-validator-storage/src/test/resources/BPlan002_5-3.zip new file mode 100644 index 0000000000000000000000000000000000000000..e68a0a9a42a7f9247e54484a768f2e49ec2bf261 Binary files /dev/null and b/xplan-validator/xplan-validator-storage/src/test/resources/BPlan002_5-3.zip differ diff --git a/xplan-validator/xplan-validator-storage/src/test/resources/s3Mock.properties b/xplan-validator/xplan-validator-storage/src/test/resources/s3Mock.properties new file mode 100644 index 0000000000000000000000000000000000000000..d858dfb28a5afe3c5f3da6f4c3d380b6cf532de1 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/resources/s3Mock.properties @@ -0,0 +1,25 @@ +### +# #%L +# xplan-manager-core - XPlan Manager Core Komponente +# %% +# Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH +# %% +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# #L% +### +xplanbox.s3.accessKeyId= +xplanbox.s3.secretKey= +xplanbox.s3.region=eu-central-1 +xplanbox.s3.endpoint.url=http://localhost +xplanbox.validation.s3.bucketName=latlonxpbexecution diff --git a/xplan-validator/xplan-validator-storage/src/test/resources/xplan.gml b/xplan-validator/xplan-validator-storage/src/test/resources/xplan.gml new file mode 100644 index 0000000000000000000000000000000000000000..07d99bfb192a2f599ad75961058121ac7912d1d1 --- /dev/null +++ b/xplan-validator/xplan-validator-storage/src/test/resources/xplan.gml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!-- + #%L + xplan-validator-api - Software zur Verwaltung von XPlanGML Daten + %% + Copyright (C) 2008 - 2024 Freie und Hansestadt Hamburg, developed by lat/lon gesellschaft für raumbezogene Informationssysteme mbH + %% + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + #L% + --> + +<xplan:XPlanAuszug xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:xplan="http://www.xplanung.de/xplangml/4/1" + gml:id="GML_ad5adb8f-d05d-45d2-8197-29b26f4dd142"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <gml:featureMember> + <xplan:BP_Bereich gml:id="GML_b3cca7aa-8908-4182-bf9f-1d96e623a1c7"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <xplan:nummer>1683</xplan:nummer> + <xplan:name>610-531-01/37.1</xplan:name> + <xplan:gehoertZuPlan xlink:href="#GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3"/> + </xplan:BP_Bereich> + </gml:featureMember> + <gml:featureMember> + <xplan:BP_Plan gml:id="GML_3a07cab4-ba41-43cf-a2b2-098a099ab0c3"> + <gml:boundedBy> + <gml:Envelope srsName="EPSG:25832"> + <gml:lowerCorner>389187.29 5799248.293</gml:lowerCorner> + <gml:upperCorner>389451.908 5799400.487</gml:upperCorner> + </gml:Envelope> + </gml:boundedBy> + <xplan:name>Nr. 108 Holstener Weg</xplan:name> + <xplan:nummer>108</xplan:nummer> + <xplan:beschreibung>(§ 13 BauGB)</xplan:beschreibung> + <xplan:technHerstellDatum>2019-01-01</xplan:technHerstellDatum> + <xplan:genehmigungsDatum>2019-07-15</xplan:genehmigungsDatum> + <xplan:erstellungsMassstab>10000</xplan:erstellungsMassstab> + <xplan:xPlanGMLVersion>4.1</xplan:xPlanGMLVersion> + <xplan:raeumlicherGeltungsbereich> + <gml:Polygon srsName="EPSG:25832" gml:id="GML_8ddef72c-e5d0-4633-9514-fc27286aa5bd"> + <gml:exterior> + <gml:LinearRing> + <gml:posList srsDimension="2">389187.29 5799400.487 389192.226 5799400.102 389213.987 + 5799398.409 389228.148 5799397.301 389235.230180782 5799396.76051955 389267.224 + 5799394.319 389280.11 5799393.347 389313.709 5799390.721 389320.31 5799390.205 + 389322.384189298 5799390.04297999 389325.29 5799389.816 389335.943 5799388.983 + 389338.767 5799388.73 389359.381 5799386.879 389383.682 5799382.558 389399.504 + 5799378.591 389428.454 5799370.13900001 389431.729 5799369.183 389436.708 5799367.117 + 389442.462 5799364.731 389444.099 5799364.052 389444.497 5799363.887 389448.589 + 5799362.19 389450.365 5799361.454 389451.908 5799360.814 389435.775757406 + 5799350.39333417 389420.573 5799340.573 389415.206 5799337.114 389418.749 5799302.879 + 389419.677 5799293.894 389420.484 5799286.098 389422.311 5799268.414 389401.481518475 + 5799266.40521146 389396.274 5799265.903 389370.273 5799263.3 389356.361 5799262.039 + 389355.362 5799262.017 389341.745 5799262.042 389337.745 5799262.048 389323.108 + 5799262.075 389317.236 5799262.088 389312.912346494 5799261.28406564 389303.866 + 5799259.602 389281.835 5799255.493 389278.769 5799254.92 389271.16 5799253.497 + 389269.442 5799253.17600001 389256.587 5799250.788 389248.612 5799249.308 389247.613 + 5799249.119 389243.177 5799248.293 389187.29 5799400.487 + </gml:posList> + </gml:LinearRing> + </gml:exterior> + </gml:Polygon> + </xplan:raeumlicherGeltungsbereich> + <xplan:gemeinde> + <xplan:XP_Gemeinde> + <xplan:ags>03454045</xplan:ags> + <xplan:gemeindeName>Salzbergen</xplan:gemeindeName> + </xplan:XP_Gemeinde> + </xplan:gemeinde> + <xplan:planArt>9999</xplan:planArt> + <xplan:rechtsstand>4000</xplan:rechtsstand> + <xplan:auslegungsStartDatum>2019-04-01</xplan:auslegungsStartDatum> + <xplan:auslegungsEndDatum>2019-05-03</xplan:auslegungsEndDatum> + <xplan:satzungsbeschlussDatum>2019-08-25</xplan:satzungsbeschlussDatum> + <xplan:inkrafttretensDatum>2019-08-25</xplan:inkrafttretensDatum> + <xplan:bereich xlink:href="#GML_b3cca7aa-8908-4182-bf9f-1d96e623a1c7"/> + </xplan:BP_Plan> + </gml:featureMember> +</xplan:XPlanAuszug> diff --git a/xplan-webservices/xplan-webservices-validator-wms/src/test/resources/de/latlon/xplan/validator/wms/libs.expected.txt b/xplan-webservices/xplan-webservices-validator-wms/src/test/resources/de/latlon/xplan/validator/wms/libs.expected.txt index 33c42d2c13dccb9c89a88f01938b1d2b6e6e3eb2..1341834ed613b9ec9fdd2596589fae65901a73f4 100644 --- a/xplan-webservices/xplan-webservices-validator-wms/src/test/resources/de/latlon/xplan/validator/wms/libs.expected.txt +++ b/xplan-webservices/xplan-webservices-validator-wms/src/test/resources/de/latlon/xplan/validator/wms/libs.expected.txt @@ -3,6 +3,9 @@ angus-activation-2.0.2.jar antlr-runtime-3.5.3.jar apache-mime4j-core-0.8.6.jar asm-9.6.jar +aws-java-sdk-core-1.12.765.jar +aws-java-sdk-kms-1.12.765.jar +aws-java-sdk-s3-1.12.765.jar axiom-api-1.4.0.jar axiom-impl-1.4.0.jar batik-anim-1.17.jar @@ -36,6 +39,7 @@ commons-fileupload2-core-2.0.0-M2.jar commons-fileupload2-jakarta-servlet6-2.0.0-M2.jar commons-io-2.16.1.jar commons-lang-2.6.jar +commons-logging-1.1.3.jar commons-math-2.2.jar commons-pool2-2.12.0.jar com.sun.xml.bind-jaxb-core-4.0.5.jar @@ -117,6 +121,7 @@ istack-commons-runtime-4.1.2.jar jackson-annotations-2.17.2.jar jackson-core-2.17.2.jar jackson-databind-2.17.2.jar +jackson-dataformat-cbor-2.17.2.jar jackson-dataformat-yaml-2.17.2.jar jai-codec-1.1.3.jar jai-core-1.1.3.jar @@ -140,6 +145,8 @@ jaxb-runtime-4.0.5.jar jaxen-2.0.0.jar jcl-over-slf4j-2.0.13.jar jgridshift-core-1.3.1.jar +jmespath-java-1.12.765.jar +joda-time-2.12.7.jar jogl-1.1.2.jar json-path-2.9.0.jar json-smart-2.5.1.jar